diff options
author | Florian Pritz <bluewind@xinu.at> | 2018-03-22 11:32:53 +0100 |
---|---|---|
committer | Florian Pritz <bluewind@xinu.at> | 2018-03-22 11:32:53 +0100 |
commit | 343a8b8ee33f9a181e662fc0e3a3979dd9b52dd4 (patch) | |
tree | 2be5d6faa22089f8d1b0c1d7e14f283550130370 | |
parent | e4803632f41cc3f09af6a88511b1d6359be3d325 (diff) | |
parent | 33414d7869aa55aaccd45cdb82268d454cb79863 (diff) | |
download | cgit-343a8b8ee33f9a181e662fc0e3a3979dd9b52dd4.tar.gz cgit-343a8b8ee33f9a181e662fc0e3a3979dd9b52dd4.tar.xz |
Merge branch 'master' of https://git.zx2c4.com/cgit into local
-rw-r--r-- | .gitmodules | 2 | ||||
-rw-r--r-- | .mailmap | 4 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | cache.c | 8 | ||||
-rw-r--r-- | cache.h | 2 | ||||
-rw-r--r-- | cgit.c | 35 | ||||
-rw-r--r-- | cgit.css | 34 | ||||
-rw-r--r-- | cgit.h | 2 | ||||
-rw-r--r-- | cgit.mk | 3 | ||||
-rw-r--r-- | cgitrc.5.txt | 9 | ||||
-rw-r--r-- | cmd.c | 12 | ||||
-rw-r--r-- | filter.c | 13 | ||||
-rwxr-xr-x | filters/syntax-highlighting.py | 6 | ||||
-rwxr-xr-x | filters/syntax-highlighting.sh | 2 | ||||
m--------- | git | 0 | ||||
-rw-r--r-- | html.c | 32 | ||||
-rw-r--r-- | html.h | 2 | ||||
-rw-r--r-- | parsing.c | 3 | ||||
-rw-r--r-- | scan-tree.c | 5 | ||||
-rw-r--r-- | shared.c | 12 | ||||
-rwxr-xr-x | tests/t0109-gitconfig.sh | 10 | ||||
-rw-r--r-- | ui-atom.c | 2 | ||||
-rw-r--r-- | ui-blame.c | 298 | ||||
-rw-r--r-- | ui-blame.h | 6 | ||||
-rw-r--r-- | ui-blob.c | 15 | ||||
-rw-r--r-- | ui-clone.c | 3 | ||||
-rw-r--r-- | ui-commit.c | 8 | ||||
-rw-r--r-- | ui-diff.c | 20 | ||||
-rw-r--r-- | ui-log.c | 17 | ||||
-rw-r--r-- | ui-patch.c | 6 | ||||
-rw-r--r-- | ui-plain.c | 4 | ||||
-rw-r--r-- | ui-repolist.c | 40 | ||||
-rw-r--r-- | ui-shared.c | 76 | ||||
-rw-r--r-- | ui-shared.h | 7 | ||||
-rw-r--r-- | ui-snapshot.c | 4 | ||||
-rw-r--r-- | ui-tag.c | 4 | ||||
-rw-r--r-- | ui-tree.c | 63 |
37 files changed, 579 insertions, 194 deletions
diff --git a/.gitmodules b/.gitmodules index 1daea94..5c6ecb4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "git"] - url = git://git.kernel.org/pub/scm/git/git.git + url = https://git.kernel.org/pub/scm/git/git.git path = git @@ -5,6 +5,6 @@ Lars Hjemli <hjemli@gmail.com> <larsh@hal-2004.(none)> Lars Hjemli <hjemli@gmail.com> <larsh@hatman.(none)> Lars Hjemli <hjemli@gmail.com> <larsh@slackbox.hjemli.net> Lars Hjemli <hjemli@gmail.com> <larsh@slaptop.hjemli.net> -Lukas Fleischer <cgit@cryptocrack.de> <cgit@crytocrack.de> -Lukas Fleischer <cgit@cryptocrack.de> <info@cryptocrack.de> +Lukas Fleischer <lfleischer@lfos.de> <cgit@cryptocrack.de> +Lukas Fleischer <lfleischer@lfos.de> <info@cryptocrack.de> Stefan Bühler <source@stbuehler.de> <lighttpd@stbuehler.de> @@ -14,7 +14,7 @@ htmldir = $(docdir) pdfdir = $(docdir) mandir = $(prefix)/share/man SHA1_HEADER = <openssl/sha.h> -GIT_VER = 2.10.2 +GIT_VER = 2.16.0 GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.gz INSTALL = install COPYTREE = cp -r @@ -134,7 +134,7 @@ doc-pdf: $(DOC_PDF) a2x -f manpage $< $(DOC_HTML): %.html : %.txt - a2x -f xhtml --stylesheet=cgit-doc.css $< + a2x -f xhtml --stylesheet=cgit-doc.css --xsltproc-opts="--param generate.consistent.ids 1" $< $(DOC_PDF): %.pdf : %.txt a2x -f pdf cgitrc.5.txt @@ -224,6 +224,12 @@ static int fill_slot(struct cache_slot *slot) /* Generate cache content */ slot->fn(); + /* Make sure any buffered data is flushed to the file */ + if (fflush(stdout)) { + close(tmp); + return errno; + } + /* update stat info */ if (fstat(slot->lock_fd, &slot->cache_st)) { close(tmp); @@ -312,7 +318,7 @@ static int process_slot(struct cache_slot *slot) /* If the cache slot does not exist (or its key doesn't match the * current key), lets try to create a new cache slot for this * request. If this fails (for whatever reason), lets just generate - * the content without caching it and fool the caller to belive + * the content without caching it and fool the caller to believe * everything worked out (but print a warning on stdout). */ @@ -19,7 +19,7 @@ typedef void (*cache_fill_fn)(void); * fn content generator function for this key * * Return value - * 0 indicates success, everyting else is an error + * 0 indicates success, everything else is an error */ extern int cache_process(int size, const char *path, const char *key, int ttl, cache_fill_fn fn); @@ -167,6 +167,8 @@ static void config_cb(const char *name, const char *value) ctx.cfg.enable_index_links = atoi(value); else if (!strcmp(name, "enable-index-owner")) ctx.cfg.enable_index_owner = atoi(value); + else if (!strcmp(name, "enable-blame")) + ctx.cfg.enable_blame = atoi(value); else if (!strcmp(name, "enable-commit-graph")) ctx.cfg.enable_commit_graph = atoi(value); else if (!strcmp(name, "enable-log-filecount")) @@ -476,7 +478,7 @@ static char *guess_defbranch(void) const char *ref, *refname; struct object_id oid; - ref = resolve_ref_unsafe("HEAD", 0, oid.hash, NULL); + ref = resolve_ref_unsafe("HEAD", 0, &oid, NULL); if (!ref || !skip_prefix(ref, "refs/heads/", &refname)) return "master"; return xstrdup(refname); @@ -559,12 +561,8 @@ static void print_no_repo_clone_urls(const char *url) html("</a></td></tr>\n"); } -static int prepare_repo_cmd(void) +static void prepare_repo_env(int *nongit) { - struct object_id oid; - int nongit = 0; - int rc; - /* The path to the git repository. */ setenv("GIT_DIR", ctx.repo->path, 1); @@ -577,8 +575,13 @@ static int prepare_repo_cmd(void) /* Setup the git directory and initialize the notes system. Both of these * load local configuration from the git repository, so we do them both while * the HOME variables are unset. */ - setup_git_directory_gently(&nongit); + setup_git_directory_gently(nongit); init_display_notes(NULL); +} +static int prepare_repo_cmd(int nongit) +{ + struct object_id oid; + int rc; if (nongit) { const char *name = ctx.repo->name; @@ -698,6 +701,7 @@ static inline void authenticate_cookie(void) static void process_request(void) { struct cgit_cmd *cmd; + int nongit = 0; /* If we're not yet authenticated, no matter what page we're on, * display the authentication body from the auth_filter. This should @@ -713,6 +717,9 @@ static void process_request(void) return; } + if (ctx.repo) + prepare_repo_env(&nongit); + cmd = cgit_get_cmd(); if (!cmd) { ctx.page.title = "cgit error"; @@ -726,19 +733,19 @@ static void process_request(void) return; } - /* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual" - * in-project path limit to be made available at ctx.qry.vpath. - * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL). - */ - ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL; - if (cmd->want_repo && !ctx.repo) { cgit_print_error_page(400, "Bad request", "No repository selected"); return; } - if (ctx.repo && prepare_repo_cmd()) + /* If cmd->want_vpath is set, assume ctx.qry.path contains a "virtual" + * in-project path limit to be made available at ctx.qry.vpath. + * Otherwise, no path limit is in effect (ctx.qry.vpath = NULL). + */ + ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL; + + if (ctx.repo && prepare_repo_cmd(nongit)) return; cmd->fn(); @@ -300,6 +300,7 @@ div#cgit table.blob { border-top: solid 1px black; } +div#cgit table.blob td.hashes, div#cgit table.blob td.lines { margin: 0; padding: 0 0 0 0.5em; vertical-align: top; @@ -329,6 +330,39 @@ div#cgit table.ssdiff td.lineno a:hover { color: black; } +div#cgit table.blame td.hashes, +div#cgit table.blame td.lines, +div#cgit table.blame td.linenumbers { + padding: 0; +} + +div#cgit table.blame td.hashes div.alt, +div#cgit table.blame td.lines div.alt { + padding: 0 0.5em 0 0.5em; +} + +div#cgit table.blame td.linenumbers div.alt { + padding: 0 0.5em 0 0; +} + +div#cgit table.blame div.alt:nth-child(even) { + background: #eee; +} + +div#cgit table.blame div.alt:nth-child(odd) { + background: white; +} + +div#cgit table.blame td.lines > div { + position: relative; +} + +div#cgit table.blame td.lines > div > pre { + padding: 0 0 0 0.5em; + position: absolute; + top: 0; +} + div#cgit table.bin-blob { margin-top: 0.5em; border: solid 1px black; @@ -71,7 +71,6 @@ struct cgit_exec_filter { char *cmd; char **argv; int old_stdout; - int pipe_fh[2]; int pid; }; @@ -228,6 +227,7 @@ struct cgit_config { int enable_http_clone; int enable_index_links; int enable_index_owner; + int enable_blame; int enable_commit_graph; int enable_log_filecount; int enable_log_linecount; @@ -54,7 +54,7 @@ endif endif # Add -ldl to linker flags on systems that commonly use GNU libc. -ifneq (,$(filter $(uname_S),Linux GNU/kFreeBSD)) +ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD)) CGIT_LIBS += -ldl endif @@ -77,6 +77,7 @@ CGIT_OBJ_NAMES += parsing.o CGIT_OBJ_NAMES += scan-tree.o CGIT_OBJ_NAMES += shared.o CGIT_OBJ_NAMES += ui-atom.o +CGIT_OBJ_NAMES += ui-blame.o CGIT_OBJ_NAMES += ui-blob.o CGIT_OBJ_NAMES += ui-clone.o CGIT_OBJ_NAMES += ui-commit.o diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 9fcf445..4da166c 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -141,6 +141,11 @@ embedded:: suitable for embedding in other html pages. Default value: none. See also: "noheader". +enable-blame:: + Flag which, when set to "1", will allow cgit to provide a "blame" page + for files, and will make it generate links to that page in appropriate + places. Default value: "0". + enable-commit-graph:: Flag which, when set to "1", will make cgit print an ASCII-art commit history graph to the left of the commit messages in the repository @@ -799,6 +804,10 @@ enable-http-clone=1 enable-index-links=1 +# Enable blame page and create links to it from tree page +enable-blame=1 + + # Enable ASCII art commit history graph on the log pages enable-commit-graph=1 @@ -1,6 +1,6 @@ /* cmd.c: the cgit command dispatcher * - * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -11,6 +11,7 @@ #include "cache.h" #include "ui-shared.h" #include "ui-atom.h" +#include "ui-blame.h" #include "ui-blob.h" #include "ui-clone.h" #include "ui-commit.h" @@ -63,6 +64,14 @@ static void about_fn(void) cgit_print_site_readme(); } +static void blame_fn(void) +{ + if (ctx.cfg.enable_blame) + cgit_print_blame(); + else + cgit_print_error_page(403, "Forbidden", "Blame is disabled"); +} + static void blob_fn(void) { cgit_print_blob(ctx.qry.sha1, ctx.qry.path, ctx.qry.head, 0); @@ -164,6 +173,7 @@ struct cgit_cmd *cgit_get_cmd(void) def_cmd(HEAD, 1, 0, 1), def_cmd(atom, 1, 0, 0), def_cmd(about, 0, 0, 0), + def_cmd(blame, 1, 1, 0), def_cmd(blob, 1, 0, 0), def_cmd(commit, 1, 1, 0), def_cmd(diff, 1, 1, 0), @@ -42,6 +42,7 @@ void cgit_cleanup_filters(void) static int open_exec_filter(struct cgit_filter *base, va_list ap) { struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; + int pipe_fh[2]; int i; for (i = 0; i < filter->base.argument_count; i++) @@ -49,19 +50,19 @@ static int open_exec_filter(struct cgit_filter *base, va_list ap) filter->old_stdout = chk_positive(dup(STDOUT_FILENO), "Unable to duplicate STDOUT"); - chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); + chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess"); filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); if (filter->pid == 0) { - close(filter->pipe_fh[1]); - chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), + close(pipe_fh[1]); + chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO), "Unable to use pipe as STDIN"); execvp(filter->cmd, filter->argv); die_errno("Unable to exec subprocess %s", filter->cmd); } - close(filter->pipe_fh[0]); - chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), + close(pipe_fh[0]); + chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO), "Unable to use pipe as STDOUT"); - close(filter->pipe_fh[1]); + close(pipe_fh[1]); return 0; } diff --git a/filters/syntax-highlighting.py b/filters/syntax-highlighting.py index 1ca4108..e912594 100755 --- a/filters/syntax-highlighting.py +++ b/filters/syntax-highlighting.py @@ -30,11 +30,11 @@ from pygments.lexers import guess_lexer_for_filename from pygments.formatters import HtmlFormatter -sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8') -sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') +sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace') +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') data = sys.stdin.read() filename = sys.argv[1] -formatter = HtmlFormatter(style='pastie') +formatter = HtmlFormatter(style='pastie', nobackground=True) try: lexer = guess_lexer_for_filename(filename, data) diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh index 4fa7928..840bc34 100755 --- a/filters/syntax-highlighting.sh +++ b/filters/syntax-highlighting.sh @@ -1,6 +1,6 @@ #!/bin/sh # This script can be used to implement syntax highlighting in the cgit -# tree-view by refering to this file with the source-filter or repo.source- +# tree-view by referring to this file with the source-filter or repo.source- # filter options in cgitrc. # # This script requires a shell supporting the ${var##pattern} syntax. diff --git a/git b/git -Subproject 6406bdc0b95715a087fdeeb0f6adf3deb80a25b +Subproject 2512f15446149235156528dafbe75930c712b29 @@ -124,29 +124,20 @@ void html_vtxtf(const char *format, va_list ap) void html_txt(const char *txt) { - const char *t = txt; - while (t && *t) { - int c = *t; - if (c == '<' || c == '>' || c == '&') { - html_raw(txt, t - txt); - if (c == '>') - html(">"); - else if (c == '<') - html("<"); - else if (c == '&') - html("&"); - txt = t + 1; - } - t++; - } - if (t != txt) - html(txt); + if (txt) + html_ntxt(txt, strlen(txt)); } -void html_ntxt(int len, const char *txt) +ssize_t html_ntxt(const char *txt, size_t len) { const char *t = txt; - while (t && *t && len--) { + ssize_t slen; + + if (len > SSIZE_MAX) + return -1; + + slen = (ssize_t) len; + while (t && *t && slen--) { int c = *t; if (c == '<' || c == '>' || c == '&') { html_raw(txt, t - txt); @@ -162,8 +153,7 @@ void html_ntxt(int len, const char *txt) } if (t != txt) html_raw(txt, t - txt); - if (len < 0) - html("..."); + return slen; } void html_attrf(const char *fmt, ...) @@ -19,7 +19,7 @@ __attribute__((format (printf,1,2))) extern void html_attrf(const char *format,...); extern void html_txt(const char *txt); -extern void html_ntxt(int len, const char *txt); +extern ssize_t html_ntxt(const char *txt, size_t len); extern void html_attr(const char *txt); extern void html_url_path(const char *txt); extern void html_url_arg(const char *txt); @@ -20,10 +20,10 @@ void cgit_parse_url(const char *url) char *c, *cmd, *p; struct cgit_repo *repo; - ctx.repo = NULL; if (!url || url[0] == '\0') return; + ctx.qry.page = NULL; ctx.repo = cgit_get_repoinfo(url); if (ctx.repo) { ctx.qry.repo = ctx.repo->url; @@ -53,7 +53,6 @@ void cgit_parse_url(const char *url) } if (cmd[1]) ctx.qry.page = xstrdup(cmd + 1); - return; } } diff --git a/scan-tree.c b/scan-tree.c index 08f3f1d..6a2f65a 100644 --- a/scan-tree.c +++ b/scan-tree.c @@ -10,6 +10,7 @@ #include "scan-tree.h" #include "configfile.h" #include "html.h" +#include <config.h> /* return 1 if path contains a objects/ directory and a HEAD file */ static int is_git_dir(const char *path) @@ -48,7 +49,7 @@ out: static struct cgit_repo *repo; static repo_config_fn config_fn; -static void repo_config(const char *name, const char *value) +static void scan_tree_repo_config(const char *name, const char *value) { config_fn(repo, name, value); } @@ -178,7 +179,7 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) strbuf_addstr(path, "cgitrc"); if (!stat(path->buf, &st)) - parse_configfile(path->buf, &repo_config); + parse_configfile(path->buf, &scan_tree_repo_config); strbuf_release(&rel); } @@ -160,7 +160,7 @@ static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_ ref = xmalloc(sizeof (struct refinfo)); ref->refname = xstrdup(refname); - ref->object = parse_object(oid->hash); + ref->object = parse_object(oid); switch (ref->object->type) { case OBJ_TAG: ref->tag = cgit_parse_tag((struct tag *)ref->object); @@ -346,13 +346,13 @@ void cgit_diff_tree(const struct object_id *old_oid, opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; opt.rename_limit = ctx.cfg.renamelimit; - DIFF_OPT_SET(&opt, RECURSIVE); + opt.flags.recursive = 1; if (ignorews) DIFF_XDL_SET(&opt, IGNORE_WHITESPACE); opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; if (prefix) { - item.match = prefix; + item.match = xstrdup(prefix); item.len = strlen(prefix); opt.pathspec.nr = 1; opt.pathspec.items = &item; @@ -360,11 +360,13 @@ void cgit_diff_tree(const struct object_id *old_oid, diff_setup_done(&opt); if (old_oid && !is_null_oid(old_oid)) - diff_tree_sha1(old_oid->hash, new_oid->hash, "", &opt); + diff_tree_oid(old_oid, new_oid, "", &opt); else - diff_root_tree_sha1(new_oid->hash, "", &opt); + diff_root_tree_oid(new_oid, "", &opt); diffcore_std(&opt); diff_flush(&opt); + + free(item.match); } void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix) diff --git a/tests/t0109-gitconfig.sh b/tests/t0109-gitconfig.sh index 5a84258..3ba6684 100755 --- a/tests/t0109-gitconfig.sh +++ b/tests/t0109-gitconfig.sh @@ -10,16 +10,16 @@ test -n "$(which strace 2>/dev/null)" || { } test_no_home_access () { - non_existant_path="/path/to/some/place/that/does/not/possibly/exist" - while test -d "$non_existant_path"; do - non_existant_path="$non_existant_path/$(date +%N)" + non_existent_path="/path/to/some/place/that/does/not/possibly/exist" + while test -d "$non_existent_path"; do + non_existent_path="$non_existent_path/$(date +%N)" done && strace \ - -E HOME="$non_existant_path" \ + -E HOME="$non_existent_path" \ -E CGIT_CONFIG="$PWD/cgitrc" \ -E QUERY_STRING="url=$1" \ -e access -f -o strace.out cgit && - test_must_fail grep "$non_existant_path" strace.out + test_must_fail grep "$non_existent_path" strace.out } test_no_home_access_success() { @@ -63,7 +63,7 @@ static void add_entry(struct commit *commit, const char *host) html_attr(pageurl); if (ctx.cfg.virtual_root) delim = '?'; - htmlf("%cid=%s", delim, hex); + html_attrf("%cid=%s", delim, hex); html("'/>\n"); free(pageurl); } diff --git a/ui-blame.c b/ui-blame.c new file mode 100644 index 0000000..17e2d60 --- /dev/null +++ b/ui-blame.c @@ -0,0 +1,298 @@ +/* ui-blame.c: functions for blame output + * + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-blame.h" +#include "html.h" +#include "ui-shared.h" +#include "argv-array.h" +#include "blame.h" + + +static char *emit_suspect_detail(struct blame_origin *suspect) +{ + struct commitinfo *info; + struct strbuf detail = STRBUF_INIT; + + info = cgit_parse_commit(suspect->commit); + + strbuf_addf(&detail, "author %s", info->author); + if (!ctx.cfg.noplainemail) + strbuf_addf(&detail, " %s", info->author_email); + strbuf_addf(&detail, " %s\n", + show_date(info->author_date, info->author_tz, + cgit_date_mode(DATE_ISO8601))); + + strbuf_addf(&detail, "committer %s", info->committer); + if (!ctx.cfg.noplainemail) + strbuf_addf(&detail, " %s", info->committer_email); + strbuf_addf(&detail, " %s\n\n", + show_date(info->committer_date, info->committer_tz, + cgit_date_mode(DATE_ISO8601))); + + strbuf_addstr(&detail, info->subject); + + cgit_free_commitinfo(info); + return strbuf_detach(&detail, NULL); +} + +static void emit_blame_entry_hash(struct blame_entry *ent) +{ + struct blame_origin *suspect = ent->suspect; + struct object_id *oid = &suspect->commit->object.oid; + unsigned long line = 0; + + char *detail = emit_suspect_detail(suspect); + html("<span class='sha1'>"); + cgit_commit_link(find_unique_abbrev(oid->hash, DEFAULT_ABBREV), detail, + NULL, ctx.qry.head, oid_to_hex(oid), suspect->path); + html("</span>"); + free(detail); + + while (line++ < ent->num_lines) + html("\n"); +} + +static void emit_blame_entry_linenumber(struct blame_entry *ent) +{ + const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n"; + + unsigned long lineno = ent->lno; + while (lineno < ent->lno + ent->num_lines) + htmlf(numberfmt, ++lineno); +} + +static void emit_blame_entry_line_background(struct blame_scoreboard *sb, + struct blame_entry *ent) +{ + unsigned long line; + size_t len, maxlen = 2; + const char* pos, *endpos; + + for (line = ent->lno; line < ent->lno + ent->num_lines; line++) { + html("\n"); + pos = blame_nth_line(sb, line); + endpos = blame_nth_line(sb, line + 1); + len = 0; + while (pos < endpos) { + len++; + if (*pos++ == '\t') + len = (len + 7) & ~7; + } + if (len > maxlen) + maxlen = len; + } + + for (len = 0; len < maxlen - 1; len++) + html(" "); +} + +struct walk_tree_context { + char *curr_rev; + int match_baselen; + int state; +}; + +static void print_object(const unsigned char *sha1, const char *path, + const char *basename, const char *rev) +{ + enum object_type type; + char *buf; + unsigned long size; + struct argv_array rev_argv = ARGV_ARRAY_INIT; + struct rev_info revs; + struct blame_scoreboard sb; + struct blame_origin *o; + struct blame_entry *ent = NULL; + + type = sha1_object_info(sha1, &size); + if (type == OBJ_BAD) { + cgit_print_error_page(404, "Not found", "Bad object name: %s", + sha1_to_hex(sha1)); + return; + } + + buf = read_sha1_file(sha1, &type, &size); + if (!buf) { + cgit_print_error_page(500, "Internal server error", + "Error reading object %s", sha1_to_hex(sha1)); + return; + } + + argv_array_push(&rev_argv, "blame"); + argv_array_push(&rev_argv, rev); + init_revisions(&revs, NULL); + revs.diffopt.flags.allow_textconv = 1; + setup_revisions(rev_argv.argc, rev_argv.argv, &revs, NULL); + init_scoreboard(&sb); + sb.revs = &revs; + setup_scoreboard(&sb, path, &o); + o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o); + prio_queue_put(&sb.commits, o->commit); + blame_origin_decref(o); + sb.ent = NULL; + sb.path = path; + assign_blame(&sb, 0); + blame_sort_final(&sb); + blame_coalesce(&sb); + + cgit_set_title_from_path(path); + + cgit_print_layout_start(); + htmlf("blob: %s (", sha1_to_hex(sha1)); + cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); + html(") ("); + cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path); + html(")\n"); + + if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) { + htmlf("<div class='error'>blob size (%ldKB)" + " exceeds display size limit (%dKB).</div>", + size / 1024, ctx.cfg.max_blob_size); + return; + } + + html("<table class='blame blob'>\n<tr>\n"); + + /* Commit hashes */ + html("<td class='hashes'>"); + for (ent = sb.ent; ent; ent = ent->next) { + html("<div class='alt'><pre>"); + emit_blame_entry_hash(ent); + html("</pre></div>"); + } + html("</td>\n"); + + /* Line numbers */ + if (ctx.cfg.enable_tree_linenumbers) { + html("<td class='linenumbers'>"); + for (ent = sb.ent; ent; ent = ent->next) { + html("<div class='alt'><pre>"); + emit_blame_entry_linenumber(ent); + html("</pre></div>"); + } + html("</td>\n"); + } + + html("<td class='lines'><div>"); + + /* Colored bars behind lines */ + html("<div>"); + for (ent = sb.ent; ent; ) { + struct blame_entry *e = ent->next; + html("<div class='alt'><pre>"); + emit_blame_entry_line_background(&sb, ent); + html("</pre></div>"); + free(ent); + ent = e; + } + html("</div>"); + + free((void *)sb.final_buf); + + /* Lines */ + html("<pre><code>"); + if (ctx.repo->source_filter) { + char *filter_arg = xstrdup(basename); + cgit_open_filter(ctx.repo->source_filter, filter_arg); + html_raw(buf, size); + cgit_close_filter(ctx.repo->source_filter); + free(filter_arg); + } else { + html_txt(buf); + } + html("</code></pre>"); + + html("</div></td>\n"); + + html("</tr>\n</table>\n"); + + cgit_print_layout_end(); +} + +static int walk_tree(const unsigned char *sha1, struct strbuf *base, + const char *pathname, unsigned mode, int stage, + void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + + if (base->len == walk_tree_ctx->match_baselen) { + if (S_ISREG(mode)) { + struct strbuf buffer = STRBUF_INIT; + strbuf_addbuf(&buffer, base); + strbuf_addstr(&buffer, pathname); + print_object(sha1, buffer.buf, pathname, + walk_tree_ctx->curr_rev); + strbuf_release(&buffer); + walk_tree_ctx->state = 1; + } else if (S_ISDIR(mode)) { + walk_tree_ctx->state = 2; + } + } else if (base->len < INT_MAX + && (int)base->len > walk_tree_ctx->match_baselen) { + walk_tree_ctx->state = 2; + } else if (S_ISDIR(mode)) { + return READ_TREE_RECURSIVE; + } + return 0; +} + +static int basedir_len(const char *path) +{ + char *p = strrchr(path, '/'); + if (p) + return p - path + 1; + return 0; +} + +void cgit_print_blame(void) +{ + const char *rev = ctx.qry.sha1; + struct object_id oid; + struct commit *commit; + struct pathspec_item path_items = { + .match = ctx.qry.path, + .len = ctx.qry.path ? strlen(ctx.qry.path) : 0 + }; + struct pathspec paths = { + .nr = 1, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .state = 0 + }; + + if (!rev) + rev = ctx.qry.head; + + if (get_oid(rev, &oid)) { + cgit_print_error_page(404, "Not found", + "Invalid revision name: %s", rev); + return; + } + commit = lookup_commit_reference(&oid); + if (!commit || parse_commit(commit)) { + cgit_print_error_page(404, "Not found", + "Invalid commit reference: %s", rev); + return; + } + + walk_tree_ctx.curr_rev = xstrdup(rev); + walk_tree_ctx.match_baselen = (path_items.match) ? + basedir_len(path_items.match) : -1; + + read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, + &walk_tree_ctx); + if (!walk_tree_ctx.state) + cgit_print_error_page(404, "Not found", "Not found"); + else if (walk_tree_ctx.state == 2) + cgit_print_error_page(404, "No blame for folders", + "Blame is not available for folders."); + + free(walk_tree_ctx.curr_rev); +} diff --git a/ui-blame.h b/ui-blame.h new file mode 100644 index 0000000..5b97e03 --- /dev/null +++ b/ui-blame.h @@ -0,0 +1,6 @@ +#ifndef UI_BLAME_H +#define UI_BLAME_H + +extern void cgit_print_blame(void); + +#endif /* UI_BLAME_H */ @@ -38,7 +38,7 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only) struct object_id oid; unsigned long size; struct pathspec_item path_items = { - .match = path, + .match = xstrdup(path), .len = strlen(path) }; struct pathspec paths = { @@ -53,10 +53,13 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only) }; if (get_oid(ref, &oid)) - return 0; + goto done; if (sha1_object_info(oid.hash, &size) != OBJ_COMMIT) - return 0; - read_tree_recursive(lookup_commit_reference(oid.hash)->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); + goto done; + read_tree_recursive(lookup_commit_reference(&oid)->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); + +done: + free(path_items.match); return walk_tree_ctx.found_path; } @@ -86,7 +89,7 @@ int cgit_print_file(char *path, const char *head, int file_only) return -1; type = sha1_object_info(oid.hash, &size); if (type == OBJ_COMMIT) { - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.found_path) return -1; @@ -142,7 +145,7 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl type = sha1_object_info(oid.hash, &size); if ((!hex) && type == OBJ_COMMIT && path) { - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); type = sha1_object_info(oid.hash, &size); } @@ -11,13 +11,14 @@ #include "ui-clone.h" #include "html.h" #include "ui-shared.h" +#include "packfile.h" static int print_ref_info(const char *refname, const struct object_id *oid, int flags, void *cb_data) { struct object *obj; - if (!(obj = parse_object(oid->hash))) + if (!(obj = parse_object(oid))) return 0; htmlf("%s\t%s\n", oid_to_hex(oid), refname); diff --git a/ui-commit.c b/ui-commit.c index db69d54..abf58f6 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -31,7 +31,7 @@ void cgit_print_commit(char *hex, const char *prefix) "Bad object id: %s", hex); return; } - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (!commit) { cgit_print_error_page(404, "Not found", "Bad commit reference: %s", hex); @@ -39,9 +39,9 @@ void cgit_print_commit(char *hex, const char *prefix) } info = cgit_parse_commit(commit); - format_display_notes(oid.hash, ¬es, PAGE_ENCODING, 0); + format_display_notes(&oid, ¬es, PAGE_ENCODING, 0); - load_ref_decorations(DECORATE_FULL_REFS); + load_ref_decorations(NULL, DECORATE_FULL_REFS); cgit_print_layout_start(); cgit_print_diff_ctrls(); @@ -87,7 +87,7 @@ void cgit_print_commit(char *hex, const char *prefix) free(tmp); html("</td></tr>\n"); for (p = commit->parents; p; p = p->next) { - parent = lookup_commit_reference(p->item->object.oid.hash); + parent = lookup_commit_reference(&p->item->object.oid); if (!parent) { html("<tr><td colspan='3'>"); cgit_print_error("Error reading parent commit"); @@ -385,7 +385,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix, int show_ctrls, int raw) { struct commit *commit, *commit2; - const unsigned char *old_tree_sha1, *new_tree_sha1; + const struct object_id *old_tree_oid, *new_tree_oid; diff_type difftype; /* @@ -407,13 +407,13 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, "Bad object name: %s", new_rev); return; } - commit = lookup_commit_reference(new_rev_oid->hash); + commit = lookup_commit_reference(new_rev_oid); if (!commit || parse_commit(commit)) { cgit_print_error_page(404, "Not found", "Bad commit: %s", oid_to_hex(new_rev_oid)); return; } - new_tree_sha1 = commit->tree->object.oid.hash; + new_tree_oid = &commit->tree->object.oid; if (old_rev) { if (get_oid(old_rev, old_rev_oid)) { @@ -428,15 +428,15 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, } if (!is_null_oid(old_rev_oid)) { - commit2 = lookup_commit_reference(old_rev_oid->hash); + commit2 = lookup_commit_reference(old_rev_oid); if (!commit2 || parse_commit(commit2)) { cgit_print_error_page(404, "Not found", "Bad commit: %s", oid_to_hex(old_rev_oid)); return; } - old_tree_sha1 = commit2->tree->object.oid.hash; + old_tree_oid = &commit2->tree->object.oid; } else { - old_tree_sha1 = NULL; + old_tree_oid = NULL; } if (raw) { @@ -444,16 +444,16 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, diff_setup(&diffopt); diffopt.output_format = DIFF_FORMAT_PATCH; - DIFF_OPT_SET(&diffopt, RECURSIVE); + diffopt.flags.recursive = 1; diff_setup_done(&diffopt); ctx.page.mimetype = "text/plain"; cgit_print_http_headers(); - if (old_tree_sha1) { - diff_tree_sha1(old_tree_sha1, new_tree_sha1, "", + if (old_tree_oid) { + diff_tree_oid(old_tree_oid, new_tree_oid, "", &diffopt); } else { - diff_root_tree_sha1(new_tree_sha1, "", &diffopt); + diff_root_tree_oid(new_tree_oid, "", &diffopt); } diffcore_std(&diffopt); diff_flush(&diffopt); @@ -119,8 +119,7 @@ static int show_commit(struct commit *commit, struct rev_info *revs) struct commit_list *parents = commit->parents; struct commit *parent; int found = 0, saved_fmt; - unsigned saved_flags = revs->diffopt.flags; - + struct diff_flags saved_flags = revs->diffopt.flags; /* Always show if we're not in "follow" mode with a single file. */ if (!ctx.qry.follow) @@ -149,10 +148,10 @@ static int show_commit(struct commit *commit, struct rev_info *revs) add_lines = 0; rem_lines = 0; - DIFF_OPT_SET(&revs->diffopt, RECURSIVE); - diff_tree_sha1(parent->tree->object.oid.hash, - commit->tree->object.oid.hash, - "", &revs->diffopt); + revs->diffopt.flags.recursive = 1; + diff_tree_oid(&parent->tree->object.oid, + &commit->tree->object.oid, + "", &revs->diffopt); diffcore_std(&revs->diffopt); found = !diff_queue_is_empty(); @@ -273,7 +272,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs) strbuf_addstr(&msgbuf, info->msg); strbuf_addch(&msgbuf, '\n'); } - format_display_notes(commit->object.oid.hash, + format_display_notes(&commit->object.oid, &msgbuf, PAGE_ENCODING, 0); strbuf_addch(&msgbuf, '\n'); strbuf_ltrim(&msgbuf); @@ -434,9 +433,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern rev.ignore_missing = 1; rev.simplify_history = 1; setup_revisions(rev_argv.argc, rev_argv.argv, &rev, NULL); - load_ref_decorations(DECORATE_FULL_REFS); + load_ref_decorations(NULL, DECORATE_FULL_REFS); rev.show_decorations = 1; - rev.grep_filter.regflags |= REG_ICASE; + rev.grep_filter.ignore_case = 1; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = ctx.cfg.renamelimit; @@ -33,7 +33,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, "Bad object id: %s", new_rev); return; } - commit = lookup_commit_reference(new_rev_oid.hash); + commit = lookup_commit_reference(&new_rev_oid); if (!commit) { cgit_print_error_page(404, "Not found", "Bad commit reference: %s", new_rev); @@ -46,7 +46,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, "Bad object id: %s", old_rev); return; } - if (!lookup_commit_reference(old_rev_oid.hash)) { + if (!lookup_commit_reference(&old_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad commit reference: %s", old_rev); return; @@ -92,6 +92,4 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, log_tree_commit(&rev, commit); printf("-- \ncgit %s\n\n", cgit_version); } - - fflush(stdout); } @@ -135,7 +135,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base, struct walk_tree_context *walk_tree_ctx = cbdata; if (base->len == walk_tree_ctx->match_baselen) { - if (S_ISREG(mode)) { + if (S_ISREG(mode) || S_ISLNK(mode)) { if (print_object(sha1, pathname)) walk_tree_ctx->match = 1; } else if (S_ISDIR(mode)) { @@ -185,7 +185,7 @@ void cgit_print_plain(void) cgit_print_error_page(404, "Not found", "Not found"); return; } - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (!commit || parse_commit(commit)) { cgit_print_error_page(404, "Not found", "Not found"); return; diff --git a/ui-repolist.c b/ui-repolist.c index b57ea60..af52f9b 100644 --- a/ui-repolist.c +++ b/ui-repolist.c @@ -184,27 +184,6 @@ static int cmp(const char *s1, const char *s2) return 0; } -static int sort_section(const void *a, const void *b) -{ - const struct cgit_repo *r1 = a; - const struct cgit_repo *r2 = b; - int result; - time_t t; - - result = cmp(r1->section, r2->section); - if (!result) { - if (!strcmp(ctx.cfg.repository_sort, "age")) { - // get_repo_modtime caches the value in r->mtime, so we don't - // have to worry about inefficiencies here. - if (get_repo_modtime(r1, &t) && get_repo_modtime(r2, &t)) - result = r2->mtime - r1->mtime; - } - if (!result) - result = cmp(r1->name, r2->name); - } - return result; -} - static int sort_name(const void *a, const void *b) { const struct cgit_repo *r1 = a; @@ -241,6 +220,22 @@ static int sort_idle(const void *a, const void *b) return t2 - t1; } +static int sort_section(const void *a, const void *b) +{ + const struct cgit_repo *r1 = a; + const struct cgit_repo *r2 = b; + int result; + + result = cmp(r1->section, r2->section); + if (!result) { + if (!strcmp(ctx.cfg.repository_sort, "age")) + result = sort_idle(r1, r2); + if (!result) + result = cmp(r1->name, r2->name); + } + return result; +} + struct sortcolumn { const char *name; int (*fn)(const void *a, const void *b); @@ -334,7 +329,8 @@ void cgit_print_repolist(void) repourl = cgit_repourl(ctx.repo->url); html_link_open(repourl, NULL, NULL); free(repourl); - html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); + if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0) + html("..."); html_link_close(); html("</td><td>"); if (ctx.cfg.enable_index_owner) { diff --git a/ui-shared.c b/ui-shared.c index 2e4fcd9..9d8f66b 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1,6 +1,6 @@ /* ui-shared.c: common web output functions * - * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -304,6 +304,12 @@ void cgit_plain_link(const char *name, const char *title, const char *class, reporevlink("plain", name, title, class, head, rev, path); } +void cgit_blame_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + reporevlink("blame", name, title, class, head, rev, path); +} + void cgit_log_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path, int ofs, const char *grep, const char *pattern, int showmsg, @@ -347,18 +353,11 @@ void cgit_log_link(const char *name, const char *title, const char *class, html("</a>"); } -void cgit_commit_link(char *name, const char *title, const char *class, +void cgit_commit_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path) { char *delim; - if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { - name[ctx.cfg.max_msg_len] = '\0'; - name[ctx.cfg.max_msg_len - 1] = '.'; - name[ctx.cfg.max_msg_len - 2] = '.'; - name[ctx.cfg.max_msg_len - 3] = '.'; - } - delim = repolink(title, class, "commit", head, path); if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) { html(delim); @@ -387,9 +386,13 @@ void cgit_commit_link(char *name, const char *title, const char *class, html("follow=1"); } html("'>"); - if (name[0] != '\0') - html_txt(name); - else + if (name[0] != '\0') { + if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { + html_ntxt(name, ctx.cfg.max_msg_len - 3); + html("..."); + } else + html_txt(name); + } else html_txt("(no commit message)"); html("</a>"); } @@ -481,6 +484,10 @@ static void cgit_self_link(char *name, const char *title, const char *class) cgit_plain_link(name, title, class, ctx.qry.head, ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL, ctx.qry.path); + else if (!strcmp(ctx.qry.page, "blame")) + cgit_blame_link(name, title, class, ctx.qry.head, + ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL, + ctx.qry.path); else if (!strcmp(ctx.qry.page, "log")) cgit_log_link(name, title, class, ctx.qry.head, ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL, @@ -986,8 +993,12 @@ void cgit_print_pageheader(void) cgit_log_link("log", NULL, hc("log"), ctx.qry.head, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, ctx.qry.follow); - cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, - ctx.qry.sha1, ctx.qry.vpath); + if (ctx.qry.page && !strcmp(ctx.qry.page, "blame")) + cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head, + ctx.qry.sha1, ctx.qry.vpath); + else + cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, + ctx.qry.sha1, ctx.qry.vpath); cgit_commit_link("commit", NULL, hc("commit"), ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath); cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head, @@ -1016,7 +1027,7 @@ void cgit_print_pageheader(void) html_option("committer", "committer", ctx.qry.grep); html_option("range", "range", ctx.qry.grep); html("</select>\n"); - html("<input class='txt' type='text' size='10' name='q' value='"); + html("<input class='txt' type='search' size='10' name='q' value='"); html_attr(ctx.qry.search); html("'/>\n"); html("<input type='submit' value='search'/>\n"); @@ -1031,7 +1042,7 @@ void cgit_print_pageheader(void) html("<form method='get' action='"); html_attr(currenturl); html("'>\n"); - html("<input type='text' name='q' size='10' value='"); + html("<input type='search' name='q' size='10' value='"); html_attr(ctx.qry.search); html("'/>\n"); html("<input type='submit' value='search'/>\n"); @@ -1039,7 +1050,7 @@ void cgit_print_pageheader(void) free(currenturl); } html("</td></tr></table>\n"); - if (ctx.env.authenticated && ctx.qry.vpath) { + if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) { html("<div class='path'>"); html("path: "); cgit_print_path_crumbs(ctx.qry.vpath); @@ -1111,3 +1122,34 @@ void cgit_print_snapshot_links(const char *repo, const char *head, } strbuf_release(&filename); } + +void cgit_set_title_from_path(const char *path) +{ + size_t path_len, path_index, path_last_end; + char *new_title; + + if (!path) + return; + + path_len = strlen(path); + new_title = xmalloc(path_len + 3 + strlen(ctx.page.title) + 1); + new_title[0] = '\0'; + + for (path_index = path_len, path_last_end = path_len; path_index-- > 0;) { + if (path[path_index] == '/') { + if (path_index == path_len - 1) { + path_last_end = path_index - 1; + continue; + } + strncat(new_title, &path[path_index + 1], path_last_end - path_index - 1); + strcat(new_title, "\\"); + path_last_end = path_index; + } + } + if (path_last_end) + strncat(new_title, path, path_last_end); + + strcat(new_title, " - "); + strcat(new_title, ctx.page.title); + ctx.page.title = new_title; +} diff --git a/ui-shared.h b/ui-shared.h index 87799f1..b760a17 100644 --- a/ui-shared.h +++ b/ui-shared.h @@ -26,11 +26,14 @@ extern void cgit_tree_link(const char *name, const char *title, extern void cgit_plain_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path); +extern void cgit_blame_link(const char *name, const char *title, + const char *class, const char *head, + const char *rev, const char *path); extern void cgit_log_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path, int ofs, const char *grep, const char *pattern, int showmsg, int follow); -extern void cgit_commit_link(char *name, const char *title, +extern void cgit_commit_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path); extern void cgit_patch_link(const char *name, const char *title, @@ -77,4 +80,6 @@ extern void cgit_print_snapshot_links(const char *repo, const char *head, const char *hex, int snapshots); extern void cgit_add_hidden_formfields(int incl_head, int incl_search, const char *page); + +extern void cgit_set_title_from_path(const char *path); #endif /* UI_SHARED_H */ diff --git a/ui-snapshot.c b/ui-snapshot.c index 08c6e80..b2d95f7 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c @@ -37,7 +37,7 @@ static int write_archive_type(const char *format, const char *hex, const char *p /* argv_array guarantees a trailing NULL entry. */ memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1)); - result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0); + result = write_archive(argv.argc, nargv, NULL, NULL, 0); argv_array_clear(&argv); free(nargv); return result; @@ -116,7 +116,7 @@ static int make_snapshot(const struct cgit_snapshot_format *format, "Bad object id: %s", hex); return 1; } - if (!lookup_commit_reference(oid.hash)) { + if (!lookup_commit_reference(&oid)) { cgit_print_error_page(400, "Bad request", "Not a commit reference: %s", hex); return 1; @@ -54,7 +54,7 @@ void cgit_print_tag(char *revname) "Bad tag reference: %s", revname); goto cleanup; } - obj = parse_object(oid.hash); + obj = parse_object(&oid); if (!obj) { cgit_print_error_page(500, "Internal server error", "Bad object id: %s", oid_to_hex(&oid)); @@ -64,7 +64,7 @@ void cgit_print_tag(char *revname) struct tag *tag; struct taginfo *info; - tag = lookup_tag(oid.hash); + tag = lookup_tag(&oid); if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { cgit_print_error_page(500, "Internal server error", "Bad tag object: %s", revname); @@ -1,6 +1,6 @@ /* ui-tree.c: functions for tree output * - * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com> * * Licensed under GNU General Public License v2 * (see COPYING for full license text) @@ -84,37 +84,6 @@ static void print_binary_buffer(char *buf, unsigned long size) html("</table>\n"); } -static void set_title_from_path(const char *path) -{ - size_t path_len, path_index, path_last_end; - char *new_title; - - if (!path) - return; - - path_len = strlen(path); - new_title = xmalloc(path_len + 3 + strlen(ctx.page.title) + 1); - new_title[0] = '\0'; - - for (path_index = path_len, path_last_end = path_len; path_index-- > 0;) { - if (path[path_index] == '/') { - if (path_index == path_len - 1) { - path_last_end = path_index - 1; - continue; - } - strncat(new_title, &path[path_index + 1], path_last_end - path_index - 1); - strcat(new_title, "\\"); - path_last_end = path_index; - } - } - if (path_last_end) - strncat(new_title, path, path_last_end); - - strcat(new_title, " - "); - strcat(new_title, ctx.page.title); - ctx.page.title = new_title; -} - static void print_object(const unsigned char *sha1, char *path, const char *basename, const char *rev) { enum object_type type; @@ -135,12 +104,17 @@ static void print_object(const unsigned char *sha1, char *path, const char *base return; } - set_title_from_path(path); + cgit_set_title_from_path(path); cgit_print_layout_start(); htmlf("blob: %s (", sha1_to_hex(sha1)); cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); + if (ctx.cfg.enable_blame) { + html(") ("); + cgit_blame_link("blame", NULL, NULL, ctx.qry.head, + rev, path); + } html(")\n"); if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) { @@ -157,7 +131,7 @@ static void print_object(const unsigned char *sha1, char *path, const char *base struct single_tree_ctx { struct strbuf *path; - unsigned char sha1[GIT_SHA1_RAWSZ]; + struct object_id oid; char *name; size_t count; }; @@ -177,7 +151,7 @@ static int single_tree_cb(const unsigned char *sha1, struct strbuf *base, } ctx->name = xstrdup(pathname); - hashcpy(ctx->sha1, sha1); + hashcpy(ctx->oid.hash, sha1); strbuf_addf(ctx->path, "/%s", pathname); return 0; } @@ -195,13 +169,13 @@ static void write_tree_link(const unsigned char *sha1, char *name, .nr = 0 }; - hashcpy(tree_ctx.sha1, sha1); + hashcpy(tree_ctx.oid.hash, sha1); while (tree_ctx.count == 1) { cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev, fullpath->buf); - tree = lookup_tree(tree_ctx.sha1); + tree = lookup_tree(&tree_ctx.oid); if (!tree) return; @@ -275,6 +249,9 @@ static int ls_item(const unsigned char *sha1, struct strbuf *base, if (!S_ISGITLINK(mode)) cgit_plain_link("plain", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); + if (!S_ISDIR(mode) && ctx.cfg.enable_blame) + cgit_blame_link("blame", NULL, "button", ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf); html("</td></tr>\n"); free(name); strbuf_release(&fullpath); @@ -300,17 +277,17 @@ static void ls_tail(void) cgit_print_layout_end(); } -static void ls_tree(const unsigned char *sha1, char *path, struct walk_tree_context *walk_tree_ctx) +static void ls_tree(const struct object_id *oid, char *path, struct walk_tree_context *walk_tree_ctx) { struct tree *tree; struct pathspec paths = { .nr = 0 }; - tree = parse_tree_indirect(sha1); + tree = parse_tree_indirect(oid); if (!tree) { cgit_print_error_page(404, "Not found", - "Not a tree object: %s", sha1_to_hex(sha1)); + "Not a tree object: %s", sha1_to_hex(oid->hash)); return; } @@ -335,7 +312,7 @@ static int walk_tree(const unsigned char *sha1, struct strbuf *base, if (S_ISDIR(mode)) { walk_tree_ctx->state = 1; - set_title_from_path(buffer.buf); + cgit_set_title_from_path(buffer.buf); strbuf_release(&buffer); ls_head(); return READ_TREE_RECURSIVE; @@ -380,7 +357,7 @@ void cgit_print_tree(const char *rev, char *path) "Invalid revision name: %s", rev); return; } - commit = lookup_commit_reference(oid.hash); + commit = lookup_commit_reference(&oid); if (!commit || parse_commit(commit)) { cgit_print_error_page(404, "Not found", "Invalid commit reference: %s", rev); @@ -390,7 +367,7 @@ void cgit_print_tree(const char *rev, char *path) walk_tree_ctx.curr_rev = xstrdup(rev); if (path == NULL) { - ls_tree(commit->tree->object.oid.hash, NULL, &walk_tree_ctx); + ls_tree(&commit->tree->object.oid, NULL, &walk_tree_ctx); goto cleanup; } |