diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | cache.c | 45 | ||||
-rw-r--r-- | cgit.c | 23 | ||||
-rw-r--r-- | cgit.css | 4 | ||||
-rw-r--r-- | cgit.h | 34 | ||||
-rw-r--r-- | cgit.js | 68 | ||||
-rw-r--r-- | cgitrc.5.txt | 21 | ||||
-rwxr-xr-x | filters/html-converters/md2html | 6 | ||||
m--------- | git | 0 | ||||
-rw-r--r-- | html.c | 2 | ||||
-rw-r--r-- | parsing.c | 2 | ||||
-rw-r--r-- | robots.txt | 1 | ||||
-rw-r--r-- | scan-tree.c | 2 | ||||
-rw-r--r-- | shared.c | 29 | ||||
-rw-r--r-- | ui-atom.c | 36 | ||||
-rw-r--r-- | ui-blame.c | 32 | ||||
-rw-r--r-- | ui-blob.c | 30 | ||||
-rw-r--r-- | ui-commit.c | 5 | ||||
-rw-r--r-- | ui-diff.c | 14 | ||||
-rw-r--r-- | ui-log.c | 21 | ||||
-rw-r--r-- | ui-patch.c | 6 | ||||
-rw-r--r-- | ui-plain.c | 13 | ||||
-rw-r--r-- | ui-repolist.c | 2 | ||||
-rw-r--r-- | ui-shared.c | 70 | ||||
-rw-r--r-- | ui-shared.h | 2 | ||||
-rw-r--r-- | ui-snapshot.c | 14 | ||||
-rw-r--r-- | ui-stats.c | 5 | ||||
-rw-r--r-- | ui-tag.c | 2 | ||||
-rw-r--r-- | ui-tree.c | 55 |
29 files changed, 358 insertions, 189 deletions
@@ -14,7 +14,7 @@ htmldir = $(docdir) pdfdir = $(docdir) mandir = $(prefix)/share/man SHA1_HEADER = <openssl/sha.h> -GIT_VER = 2.30.0 +GIT_VER = 2.45.0 GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz INSTALL = install COPYTREE = cp -r @@ -87,6 +87,7 @@ install: all $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css + $(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png $(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico $(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt @@ -85,40 +85,45 @@ static int close_slot(struct cache_slot *slot) /* Print the content of the active cache slot (but skip the key). */ static int print_slot(struct cache_slot *slot) { + off_t off; #ifdef HAVE_LINUX_SENDFILE - off_t start_off; - int ret; + off_t size; +#endif + + off = slot->keylen + 1; - start_off = slot->keylen + 1; +#ifdef HAVE_LINUX_SENDFILE + size = slot->cache_st.st_size; do { - ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off, - slot->cache_st.st_size - start_off); + ssize_t ret; + ret = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off); if (ret < 0) { if (errno == EAGAIN || errno == EINTR) continue; + /* Fall back to read/write on EINVAL or ENOSYS */ + if (errno == EINVAL || errno == ENOSYS) + break; return errno; } - return 0; + if (off == size) + return 0; } while (1); -#else - ssize_t i, j; +#endif - i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); - if (i != slot->keylen + 1) + if (lseek(slot->cache_fd, off, SEEK_SET) != off) return errno; do { - i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); - if (i > 0) - j = xwrite(STDOUT_FILENO, slot->buf, i); - } while (i > 0 && j == i); - - if (i < 0 || j != i) - return errno; - else - return 0; -#endif + ssize_t ret; + ret = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); + if (ret < 0) + return errno; + if (ret == 0) + return 0; + if (write_in_full(STDOUT_FILENO, slot->buf, ret) < 0) + return errno; + } while (1); } /* Check if the slot has expired */ @@ -142,7 +142,9 @@ static void config_cb(const char *name, const char *value) else if (!strcmp(name, "root-readme")) ctx.cfg.root_readme = xstrdup(value); else if (!strcmp(name, "css")) - ctx.cfg.css = xstrdup(value); + string_list_append(&ctx.cfg.css, xstrdup(value)); + else if (!strcmp(name, "js")) + string_list_append(&ctx.cfg.js, xstrdup(value)); else if (!strcmp(name, "favicon")) ctx.cfg.favicon = xstrdup(value); else if (!strcmp(name, "footer")) @@ -237,9 +239,11 @@ static void config_cb(const char *name, const char *value) ctx.cfg.max_repodesc_len = atoi(value); else if (!strcmp(name, "max-blob-size")) ctx.cfg.max_blob_size = atoi(value); - else if (!strcmp(name, "max-repo-count")) + else if (!strcmp(name, "max-repo-count")) { ctx.cfg.max_repo_count = atoi(value); - else if (!strcmp(name, "max-commit-count")) + if (ctx.cfg.max_repo_count <= 0) + ctx.cfg.max_repo_count = INT_MAX; + } else if (!strcmp(name, "max-commit-count")) ctx.cfg.max_commit_count = atoi(value); else if (!strcmp(name, "project-list")) ctx.cfg.project_list = xstrdup(expand_macros(value)); @@ -376,7 +380,6 @@ static void prepare_context(void) ctx.cfg.case_sensitive_sort = 1; ctx.cfg.branch_sort = 0; ctx.cfg.commit_sort = 0; - ctx.cfg.css = "/cgit.css"; ctx.cfg.logo = "/cgit.png"; ctx.cfg.favicon = "/favicon.ico"; ctx.cfg.local_time = 0; @@ -428,7 +431,7 @@ static void prepare_context(void) ctx.page.modified = time(NULL); ctx.page.expires = ctx.page.modified; ctx.page.etag = NULL; - string_list_init(&ctx.cfg.mimetypes, 1); + string_list_init_dup(&ctx.cfg.mimetypes); if (ctx.env.script_name) ctx.cfg.script_name = xstrdup(ctx.env.script_name); if (ctx.env.query_string) @@ -507,9 +510,11 @@ static inline void parse_readme(const char *readme, char **filename, char **ref, /* Check if the readme is tracked in the git repo. */ colon = strchr(readme, ':'); if (colon && strlen(colon) > 1) { - /* If it starts with a colon, we want to use - * the default branch */ - if (colon == readme && repo->defbranch) + /* If it starts with a colon, we want to use head given + * from query or the default branch */ + if (colon == readme && ctx.qry.head) + *ref = xstrdup(ctx.qry.head); + else if (colon == readme && repo->defbranch) *ref = xstrdup(repo->defbranch); else *ref = xstrndup(readme, colon - readme); @@ -626,7 +631,7 @@ static int prepare_repo_cmd(int nongit) return 1; } - if (get_oid(ctx.qry.head, &oid)) { + if (repo_get_oid(the_repository, ctx.qry.head, &oid)) { char *old_head = ctx.qry.head; ctx.qry.head = xstrdup(ctx.repo->defbranch); cgit_print_error_page(404, "Not found", @@ -363,6 +363,10 @@ div#cgit table.blame td.lines > div > pre { top: 0; } +div#cgit table.blame .oid { + font-size: 100%; +} + div#cgit table.bin-blob { margin-top: 0.5em; border: solid 1px black; @@ -1,30 +1,35 @@ #ifndef CGIT_H #define CGIT_H +#include <stdbool.h> #include <git-compat-util.h> -#include <stdbool.h> -#include <cache.h> +#include <archive.h> +#include <commit.h> +#include <diffcore.h> +#include <diff.h> +#include <environment.h> +#include <graph.h> #include <grep.h> +#include <hex.h> +#include <log-tree.h> +#include <notes.h> #include <object.h> +#include <object-name.h> #include <object-store.h> -#include <tree.h> -#include <commit.h> -#include <tag.h> -#include <diff.h> -#include <diffcore.h> -#include <strvec.h> +#include <path.h> #include <refs.h> #include <revision.h> -#include <log-tree.h> -#include <archive.h> +#include <setup.h> #include <string-list.h> +#include <strvec.h> +#include <tag.h> +#include <tree.h> +#include <utf8.h> +#include <wrapper.h> #include <xdiff-interface.h> #include <xdiff/xdiff.h> -#include <utf8.h> -#include <notes.h> -#include <graph.h> /* Add isgraph(x) to Git's sane ctype support (see git-compat-util.h) */ #undef isgraph @@ -195,7 +200,6 @@ struct cgit_config { char *cache_root; char *clone_prefix; char *clone_url; - char *css; char *favicon; char *footer; char *head_include; @@ -206,6 +210,7 @@ struct cgit_config { char *module_link; char *project_list; struct string_list readme; + struct string_list css; char *robots; char *root_title; char *root_desc; @@ -264,6 +269,7 @@ struct cgit_config { int branch_sort; int commit_sort; struct string_list mimetypes; + struct string_list js; struct cgit_filter *about_filter; struct cgit_filter *commit_filter; struct cgit_filter *source_filter; @@ -0,0 +1,68 @@ +/* cgit.js: javacript functions for cgit + * + * Copyright (C) 2006-2018 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +(function () { + +/* This follows the logic and suffixes used in ui-shared.c */ + +var age_classes = [ "age-mins", "age-hours", "age-days", "age-weeks", "age-months", "age-years" ]; +var age_suffix = [ "min.", "hours", "days", "weeks", "months", "years", "years" ]; +var age_next = [ 60, 3600, 24 * 3600, 7 * 24 * 3600, 30 * 24 * 3600, 365 * 24 * 3600, 365 * 24 * 3600 ]; +var age_limit = [ 7200, 24 * 7200, 7 * 24 * 7200, 30 * 24 * 7200, 365 * 25 * 7200, 365 * 25 * 7200 ]; +var update_next = [ 10, 5 * 60, 1800, 24 * 3600, 24 * 3600, 24 * 3600, 24 * 3600 ]; + +function render_age(e, age) { + var t, n; + + for (n = 0; n < age_classes.length; n++) + if (age < age_limit[n]) + break; + + t = Math.round(age / age_next[n]) + " " + age_suffix[n]; + + if (e.textContent != t) { + e.textContent = t; + if (n == age_classes.length) + n--; + if (e.className != age_classes[n]) + e.className = age_classes[n]; + } +} + +function aging() { + var n, next = 24 * 3600, + now_ut = Math.round((new Date().getTime() / 1000)); + + for (n = 0; n < age_classes.length; n++) { + var m, elems = document.getElementsByClassName(age_classes[n]); + + if (elems.length && update_next[n] < next) + next = update_next[n]; + + for (m = 0; m < elems.length; m++) { + var age = now_ut - elems[m].getAttribute("data-ut"); + + render_age(elems[m], age); + } + } + + /* + * We only need to come back when the age might have changed. + * Eg, if everything is counted in hours already, once per + * 5 minutes is accurate enough. + */ + + window.setTimeout(aging, next * 1000); +} + +document.addEventListener("DOMContentLoaded", function() { + /* we can do the aging on DOM content load since no layout dependency */ + aging(); +}, false); + +})(); diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 33a6a8c..6f3e952 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -126,7 +126,8 @@ commit-sort:: css:: Url which specifies the css document to include in all cgit pages. - Default value: "/cgit.css". + Default value: "/cgit.css". May be given multiple times, each + css URL path is added in the head section of the document in turn. email-filter:: Specifies a command which will be invoked to format names and email @@ -238,6 +239,11 @@ include:: Name of a configfile to include before the rest of the current config- file is parsed. Default value: none. See also: "MACRO EXPANSION". +js:: + Url which specifies the javascript script document to include in all cgit + pages. Default value: "/cgit.js". Setting this to an empty string will + disable generation of the link to this file in the head section. + local-time:: Flag which, if set to "1", makes cgit print commit and tag times in the servers timezone. Default value: "0". @@ -269,7 +275,8 @@ max-message-length:: max-repo-count:: Specifies the number of entries to list per page on the repository - index page. Default value: "50". + index page. The value "0" shows all repositories without limitation. + Default value: "50". max-repodesc-length:: Specifies the maximum number of repo description characters to display @@ -579,11 +586,11 @@ repo.readme:: verbatim as the "About" page for this repo. You may also specify a git refspec by head or by hash by prepending the refspec followed by a colon. For example, "master:docs/readme.mkd". If the value begins - with a colon, i.e. ":docs/readme.rst", the default branch of the - repository will be used. Sharing any file will expose that entire - directory tree to the "/about/PATH" endpoints, so be sure that there - are no non-public files located in the same directory as the readme - file. Default value: <readme>. + with a colon, i.e. ":docs/readme.rst", the head giving in query or + the default branch of the repository will be used. Sharing any file + will expose that entire directory tree to the "/about/PATH" endpoints, + so be sure that there are no non-public files located in the same + directory as the readme file. Default value: <readme>. repo.section:: Override the current section name for this repository. Default value: diff --git a/filters/html-converters/md2html b/filters/html-converters/md2html index f505cb2..59f43a8 100755 --- a/filters/html-converters/md2html +++ b/filters/html-converters/md2html @@ -86,11 +86,7 @@ div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div# margin: 15px 0; } .markdown-body hr { - background: transparent url("/dirty-shade.png") repeat-x 0 0; - border: 0 none; - color: #ccc; - height: 4px; - padding: 0; + border: 2px solid #ccc; } .markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child { margin-top: 0; diff --git a/git b/git -Subproject 71ca53e8125e36efbda17293c50027d31681a41 +Subproject 786a3e4b8d754d2b14b1208b98eeb0a554ef19a @@ -59,7 +59,7 @@ char *fmt(const char *format, ...) va_start(args, format); len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args); va_end(args); - if (len > sizeof(buf[bufidx])) { + if (len >= sizeof(buf[bufidx])) { fprintf(stderr, "[html.c] string truncated: %s\n", format); exit(1); } @@ -198,7 +198,7 @@ struct taginfo *cgit_parse_tag(struct tag *tag) const char *p; struct taginfo *ret = NULL; - data = read_object_file(&tag->object.oid, &type, &size); + data = repo_read_object_file(the_repository, &tag->object.oid, &type, &size); if (!data || type != OBJ_TAG) goto cleanup; @@ -1,3 +1,4 @@ User-agent: * Disallow: /*/snapshot/* +Disallow: /*/blame/* Allow: / diff --git a/scan-tree.c b/scan-tree.c index 6a2f65a..84da86e 100644 --- a/scan-tree.c +++ b/scan-tree.c @@ -54,7 +54,7 @@ static void scan_tree_repo_config(const char *name, const char *value) config_fn(repo, name, value); } -static int gitconfig_config(const char *key, const char *value, void *cb) +static int gitconfig_config(const char *key, const char *value, const struct config_context *, void *cb) { const char *name; @@ -241,7 +241,7 @@ static int load_mmfile(mmfile_t *file, const struct object_id *oid) file->ptr = (char *)""; file->size = 0; } else { - file->ptr = read_object_file(oid, &type, + file->ptr = repo_read_object_file(the_repository, oid, &type, (unsigned long *)&file->size); } return 1; @@ -341,10 +341,9 @@ void cgit_diff_tree(const struct object_id *old_oid, filepair_fn fn, const char *prefix, int ignorews) { struct diff_options opt; - struct pathspec_item item; + struct pathspec_item *item; - memset(&item, 0, sizeof(item)); - diff_setup(&opt); + repo_diff_setup(the_repository, &opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; opt.rename_limit = ctx.cfg.renamelimit; @@ -354,10 +353,11 @@ void cgit_diff_tree(const struct object_id *old_oid, opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; if (prefix) { - item.match = xstrdup(prefix); - item.len = strlen(prefix); + item = xcalloc(1, sizeof(*item)); + item->match = xstrdup(prefix); + item->len = strlen(prefix); opt.pathspec.nr = 1; - opt.pathspec.items = &item; + opt.pathspec.items = item; } diff_setup_done(&opt); @@ -367,8 +367,6 @@ void cgit_diff_tree(const struct object_id *old_oid, 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) @@ -541,7 +539,9 @@ char *expand_macros(const char *txt) char *get_mimetype_for_filename(const char *filename) { - char *ext, *mimetype, *token, line[1024], *saveptr; + char *ext, *mimetype, line[1024]; + struct string_list list = STRING_LIST_INIT_NODUP; + int i; FILE *file; struct string_list_item *mime; @@ -566,13 +566,16 @@ char *get_mimetype_for_filename(const char *filename) while (fgets(line, sizeof(line), file)) { if (!line[0] || line[0] == '#') continue; - mimetype = strtok_r(line, " \t\r\n", &saveptr); - while ((token = strtok_r(NULL, " \t\r\n", &saveptr))) { - if (!strcasecmp(ext, token)) { + string_list_split_in_place(&list, line, " \t\r\n", -1); + string_list_remove_empty_items(&list, 0); + mimetype = list.items[0].string; + for (i = 1; i < list.nr; i++) { + if (!strcasecmp(ext, list.items[i].string)) { fclose(file); return xstrdup(mimetype); } } + string_list_clear(&list, 0); } fclose(file); return NULL; @@ -67,17 +67,12 @@ static void add_entry(struct commit *commit, const char *host) html("'/>\n"); free(pageurl); } - htmlf("<id>%s</id>\n", hex); + html("<id>"); + html_txtf("urn:%s:%s", the_hash_algo->name, hex); + html("</id>\n"); html("<content type='text'>\n"); html_txt(info->msg); html("</content>\n"); - html("<content type='xhtml'>\n"); - html("<div xmlns='http://www.w3.org/1999/xhtml'>\n"); - html("<pre>\n"); - html_txt(info->msg); - html("</pre>\n"); - html("</div>\n"); - html("</content>\n"); html("</entry>\n"); cgit_free_commitinfo(info); } @@ -90,6 +85,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count) struct commit *commit; struct rev_info rev; int argc = 2; + bool first = true; if (ctx.qry.show_all) argv[1] = "--all"; @@ -101,7 +97,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count) argv[argc++] = path; } - init_revisions(&rev, NULL); + repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.verbose_header = 1; @@ -130,18 +126,30 @@ void cgit_print_atom(char *tip, const char *path, int max_count) html_txt(ctx.repo->desc); html("</subtitle>\n"); if (host) { + char *fullurl = cgit_currentfullurl(); char *repourl = cgit_repourl(ctx.repo->url); + html("<id>"); + html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl); + html("</id>\n"); + html("<link rel='self' href='"); + html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl); + html("'/>\n"); html("<link rel='alternate' type='text/html' href='"); - html(cgit_httpscheme()); - html_attr(host); - html_attr(repourl); + html_attrf("%s%s%s", cgit_httpscheme(), host, repourl); html("'/>\n"); + free(fullurl); free(repourl); } while ((commit = get_revision(&rev)) != NULL) { + if (first) { + html("<updated>"); + html_txt(show_date(commit->date, 0, + date_mode_from_type(DATE_ISO8601_STRICT))); + html("</updated>\n"); + first = false; + } add_entry(commit, host); - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } html("</feed>\n"); @@ -49,11 +49,20 @@ static void emit_blame_entry_hash(struct blame_entry *ent) char *detail = emit_suspect_detail(suspect); html("<span class='oid'>"); - cgit_commit_link(find_unique_abbrev(oid, DEFAULT_ABBREV), detail, + cgit_commit_link(repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV), detail, NULL, ctx.qry.head, oid_to_hex(oid), suspect->path); html("</span>"); free(detail); + if (!repo_parse_commit(the_repository, suspect->commit) && suspect->commit->parents) { + struct commit *parent = suspect->commit->parents->item; + + html(" "); + cgit_blame_link("^", "Blame the previous revision", NULL, + ctx.qry.head, oid_to_hex(&parent->object.oid), + suspect->path); + } + while (line++ < ent->num_lines) html("\n"); } @@ -117,7 +126,7 @@ static void print_object(const struct object_id *oid, const char *path, return; } - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) { cgit_print_error_page(500, "Internal server error", "Error reading object %s", oid_to_hex(oid)); @@ -126,7 +135,7 @@ static void print_object(const struct object_id *oid, const char *path, strvec_push(&rev_argv, "blame"); strvec_push(&rev_argv, rev); - init_revisions(&revs, NULL); + repo_init_revisions(the_repository, &revs, NULL); revs.diffopt.flags.allow_textconv = 1; setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL); init_scoreboard(&sb); @@ -152,6 +161,10 @@ static void print_object(const struct object_id *oid, const char *path, cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path); html(")\n"); + if (buffer_is_binary(buf, size)) { + html("<div class='error'>blob is binary.</div>"); + goto cleanup; + } 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>", @@ -221,8 +234,7 @@ cleanup: } static int walk_tree(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, - void *cbdata) + const char *pathname, unsigned mode, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; @@ -275,13 +287,13 @@ void cgit_print_blame(void) if (!rev) rev = ctx.qry.head; - if (get_oid(rev, &oid)) { + if (repo_get_oid(the_repository, rev, &oid)) { cgit_print_error_page(404, "Not found", "Invalid revision name: %s", rev); return; } commit = lookup_commit_reference(the_repository, &oid); - if (!commit || parse_commit(commit)) { + if (!commit || repo_parse_commit(the_repository, commit)) { cgit_print_error_page(404, "Not found", "Invalid commit reference: %s", rev); return; @@ -291,10 +303,8 @@ void cgit_print_blame(void) walk_tree_ctx.match_baselen = (path_items.match) ? basedir_len(path_items.match) : -1; - read_tree_recursive(the_repository, - repo_get_commit_tree(the_repository, commit), - "", 0, 0, - &paths, walk_tree, &walk_tree_ctx); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &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) @@ -19,7 +19,7 @@ struct walk_tree_context { }; static int walk_tree(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *cbdata) + const char *pathname, unsigned mode, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; @@ -52,13 +52,13 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only) .file_only = file_only }; - if (get_oid(ref, &oid)) + if (repo_get_oid(the_repository, ref, &oid)) goto done; if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT) goto done; - read_tree_recursive(the_repository, - repo_get_commit_tree(the_repository, lookup_commit_reference(the_repository, &oid)), - "", 0, 0, &paths, walk_tree, &walk_tree_ctx); + read_tree(the_repository, + repo_get_commit_tree(the_repository, lookup_commit_reference(the_repository, &oid)), + &paths, walk_tree, &walk_tree_ctx); done: free(path_items.match); @@ -87,22 +87,20 @@ int cgit_print_file(char *path, const char *head, int file_only) .file_only = file_only }; - if (get_oid(head, &oid)) + if (repo_get_oid(the_repository, head, &oid)) return -1; type = oid_object_info(the_repository, &oid, &size); if (type == OBJ_COMMIT) { commit = lookup_commit_reference(the_repository, &oid); - read_tree_recursive(the_repository, - repo_get_commit_tree(the_repository, commit), - "", 0, 0, &paths, walk_tree, - &walk_tree_ctx); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.found_path) return -1; type = oid_object_info(the_repository, &oid, &size); } if (type == OBJ_BAD) return -1; - buf = read_object_file(&oid, &type, &size); + buf = repo_read_object_file(the_repository, &oid, &type, &size); if (!buf) return -1; buf[size] = '\0'; @@ -140,7 +138,7 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl return; } } else { - if (get_oid(head, &oid)) { + if (repo_get_oid(the_repository, head, &oid)) { cgit_print_error_page(404, "Not found", "Bad ref: %s", head); return; @@ -151,10 +149,8 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl if ((!hex) && type == OBJ_COMMIT && path) { commit = lookup_commit_reference(the_repository, &oid); - read_tree_recursive(the_repository, - repo_get_commit_tree(the_repository, commit), - "", 0, 0, &paths, walk_tree, - &walk_tree_ctx); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); type = oid_object_info(the_repository, &oid, &size); } @@ -164,7 +160,7 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl return; } - buf = read_object_file(&oid, &type, &size); + buf = repo_read_object_file(the_repository, &oid, &type, &size); if (!buf) { cgit_print_error_page(500, "Internal server error", "Error reading object %s", hex); diff --git a/ui-commit.c b/ui-commit.c index 948118c..30672d0 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -26,7 +26,7 @@ void cgit_print_commit(char *hex, const char *prefix) if (!hex) hex = ctx.qry.head; - if (get_oid(hex, &oid)) { + if (repo_get_oid(the_repository, hex, &oid)) { cgit_print_error_page(400, "Bad request", "Bad object id: %s", hex); return; @@ -39,10 +39,11 @@ void cgit_print_commit(char *hex, const char *prefix) } info = cgit_parse_commit(commit); - format_display_notes(&oid, ¬es, PAGE_ENCODING, 0); + format_display_notes(&oid, ¬es, PAGE_ENCODING, 1); load_ref_decorations(NULL, DECORATE_FULL_REFS); + ctx.page.title = fmtalloc("%s - %s", info->subject, ctx.page.title); cgit_print_layout_start(); cgit_print_diff_ctrls(); html("<table summary='commit info' class='commit-info'>\n"); @@ -258,8 +258,8 @@ static void header(const struct object_id *oid1, char *path1, int mode1, htmlf("<br/>deleted file mode %.6o", mode1); if (!subproject) { - abbrev1 = xstrdup(find_unique_abbrev(oid1, DEFAULT_ABBREV)); - abbrev2 = xstrdup(find_unique_abbrev(oid2, DEFAULT_ABBREV)); + abbrev1 = xstrdup(repo_find_unique_abbrev(the_repository, oid1, DEFAULT_ABBREV)); + abbrev2 = xstrdup(repo_find_unique_abbrev(the_repository, oid2, DEFAULT_ABBREV)); htmlf("<br/>index %s..%s", abbrev1, abbrev2); free(abbrev1); free(abbrev2); @@ -402,13 +402,13 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, if (!new_rev) new_rev = ctx.qry.head; - if (get_oid(new_rev, new_rev_oid)) { + if (repo_get_oid(the_repository, new_rev, new_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad object name: %s", new_rev); return; } commit = lookup_commit_reference(the_repository, new_rev_oid); - if (!commit || parse_commit(commit)) { + if (!commit || repo_parse_commit(the_repository, commit)) { cgit_print_error_page(404, "Not found", "Bad commit: %s", oid_to_hex(new_rev_oid)); return; @@ -416,7 +416,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, new_tree_oid = get_commit_tree_oid(commit); if (old_rev) { - if (get_oid(old_rev, old_rev_oid)) { + if (repo_get_oid(the_repository, old_rev, old_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad object name: %s", old_rev); return; @@ -429,7 +429,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, if (!is_null_oid(old_rev_oid)) { commit2 = lookup_commit_reference(the_repository, old_rev_oid); - if (!commit2 || parse_commit(commit2)) { + if (!commit2 || repo_parse_commit(the_repository, commit2)) { cgit_print_error_page(404, "Not found", "Bad commit: %s", oid_to_hex(old_rev_oid)); return; @@ -442,7 +442,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, if (raw) { struct diff_options diffopt; - diff_setup(&diffopt); + repo_diff_setup(the_repository, &diffopt); diffopt.output_format = DIFF_FORMAT_PATCH; diffopt.flags.recursive = 1; diff_setup_done(&diffopt); @@ -65,8 +65,9 @@ void show_commit_decorations(struct commit *commit) return; html("<span class='decoration'>"); while (deco) { - struct object_id peeled; + struct object_id oid_tag, peeled; int is_annotated = 0; + strlcpy(buf, prettify_refname(deco->name), sizeof(buf)); switch(deco->type) { case DECORATION_NONE: @@ -79,8 +80,8 @@ void show_commit_decorations(struct commit *commit) ctx.qry.showmsg, 0); break; case DECORATION_REF_TAG: - if (!peel_ref(deco->name, &peeled)) - is_annotated = !oidcmp(&commit->object.oid, &peeled); + if (!read_ref(deco->name, &oid_tag) && !peel_iterated_oid(&oid_tag, &peeled)) + is_annotated = !oideq(&oid_tag, &peeled); cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf); break; case DECORATION_REF_REMOTE: @@ -145,7 +146,7 @@ static int show_commit(struct commit *commit, struct rev_info *revs) /* When we get here we have precisely one parent. */ parent = parents->item; /* If we can't parse the commit, let print_commit() report an error. */ - if (parse_commit(parent)) + if (repo_parse_commit(the_repository, parent)) return 1; files = 0; @@ -158,7 +159,7 @@ static int show_commit(struct commit *commit, struct rev_info *revs) "", &revs->diffopt); diffcore_std(&revs->diffopt); - found = !diff_queue_is_empty(); + found = !diff_queue_is_empty(&revs->diffopt); saved_fmt = revs->diffopt.output_format; revs->diffopt.output_format = DIFF_FORMAT_CALLBACK; revs->diffopt.format_callback = cgit_diff_tree_cb; @@ -329,7 +330,7 @@ static const char *disambiguate_ref(const char *ref, int *must_free_result) struct strbuf longref = STRBUF_INIT; strbuf_addf(&longref, "refs/heads/%s", ref); - if (get_oid(longref.buf, &oid) == 0) { + if (repo_get_oid(the_repository, longref.buf, &oid) == 0) { *must_free_result = 1; return strbuf_detach(&longref, NULL); } @@ -429,7 +430,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern if (path) strvec_push(&rev_argv, path); - init_revisions(&rev, NULL); + repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.verbose_header = 1; @@ -488,8 +489,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) { if (show_commit(commit, &rev)) i++; - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } @@ -510,8 +510,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern i++; print_commit(commit, &rev); } - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } if (pager) { @@ -31,7 +31,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, if (!new_rev) new_rev = ctx.qry.head; - if (get_oid(new_rev, &new_rev_oid)) { + if (repo_get_oid(the_repository, new_rev, &new_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad object id: %s", new_rev); return; @@ -44,7 +44,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, } if (old_rev) { - if (get_oid(old_rev, &old_rev_oid)) { + if (repo_get_oid(the_repository, old_rev, &old_rev_oid)) { cgit_print_error_page(404, "Not found", "Bad object id: %s", old_rev); return; @@ -78,7 +78,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, "%s%n%n%w(0)%b"; } - init_revisions(&rev, NULL); + repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.verbose_header = 1; rev.diff = 1; @@ -28,7 +28,7 @@ static int print_object(const struct object_id *oid, const char *path) return 0; } - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) { cgit_print_error_page(404, "Not found", "Not found"); return 0; @@ -130,7 +130,7 @@ static void print_dir_tail(void) } static int walk_tree(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *cbdata) + const char *pathname, unsigned mode, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; @@ -181,12 +181,12 @@ void cgit_print_plain(void) if (!rev) rev = ctx.qry.head; - if (get_oid(rev, &oid)) { + if (repo_get_oid(the_repository, rev, &oid)) { cgit_print_error_page(404, "Not found", "Not found"); return; } commit = lookup_commit_reference(the_repository, &oid); - if (!commit || parse_commit(commit)) { + if (!commit || repo_parse_commit(the_repository, commit)) { cgit_print_error_page(404, "Not found", "Not found"); return; } @@ -198,9 +198,8 @@ void cgit_print_plain(void) } else walk_tree_ctx.match_baselen = basedir_len(path_items.match); - read_tree_recursive(the_repository, - repo_get_commit_tree(the_repository, commit), - "", 0, 0, &paths, walk_tree, &walk_tree_ctx); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.match) cgit_print_error_page(404, "Not found", "Not found"); else if (walk_tree_ctx.match == 2) diff --git a/ui-repolist.c b/ui-repolist.c index 529a203..d12e3dd 100644 --- a/ui-repolist.c +++ b/ui-repolist.c @@ -321,7 +321,7 @@ void cgit_print_repolist(void) } htmlf("<tr><td class='%s'>", !sorted && section ? "sublevel-repo" : "toplevel-repo"); - cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); + cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); html("</td><td>"); repourl = cgit_repourl(ctx.repo->url); html_link_open(repourl, NULL, NULL); diff --git a/ui-shared.c b/ui-shared.c index acd8ab5..d5b5b20 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -662,18 +662,18 @@ void cgit_submodule_link(const char *class, char *path, const char *rev) path[len - 1] = tail; } -const struct date_mode *cgit_date_mode(enum date_mode_type type) +const struct date_mode cgit_date_mode(enum date_mode_type type) { static struct date_mode mode; mode.type = type; mode.local = ctx.cfg.local_time; - return &mode; + return mode; } static void print_rel_date(time_t t, int tz, double value, const char *class, const char *suffix) { - htmlf("<span class='%s' title='", class); + htmlf("<span class='%s' data-ut='%" PRIu64 "' title='", class, (uint64_t)t); html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601))); htmlf("'>%.0f %s</span>", value, suffix); } @@ -768,6 +768,38 @@ static void print_rel_vcs_link(const char *url) html(" Git repository'/>\n"); } +static int emit_css_link(struct string_list_item *s, void *arg) +{ + /* Do not emit anything if css= is specified. */ + if (s && *s->string == '\0') + return 0; + + html("<link rel='stylesheet' type='text/css' href='"); + if (s) + html_attr(s->string); + else + html_attr((const char *)arg); + html("'/>\n"); + + return 0; +} + +static int emit_js_link(struct string_list_item *s, void *arg) +{ + /* Do not emit anything if js= is specified. */ + if (s && *s->string == '\0') + return 0; + + html("<script type='text/javascript' src='"); + if (s) + html_attr(s->string); + else + html_attr((const char *)arg); + html("'></script>\n"); + + return 0; +} + void cgit_print_docstart(void) { char *host = cgit_hosturl(); @@ -787,9 +819,17 @@ void cgit_print_docstart(void) htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); if (ctx.cfg.robots && *ctx.cfg.robots) htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots); - html("<link rel='stylesheet' type='text/css' href='"); - html_attr(ctx.cfg.css); - html("'/>\n"); + + if (ctx.cfg.css.items) + for_each_string_list(&ctx.cfg.css, emit_css_link, NULL); + else + emit_css_link(NULL, "/cgit.css"); + + if (ctx.cfg.js.items) + for_each_string_list(&ctx.cfg.js, emit_js_link, NULL); + else + emit_js_link(NULL, "/cgit.js"); + if (ctx.cfg.favicon) { html("<link rel='shortcut icon' href='"); html_attr(ctx.cfg.favicon); @@ -995,7 +1035,7 @@ static void print_header(void) if (ctx.repo) { cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1); html(" : "); - cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); + cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); if (ctx.env.authenticated) { html("</td><td class='form'>"); html("<form method='get'>\n"); @@ -1016,7 +1056,13 @@ static void print_header(void) if (ctx.repo) { html_txt(ctx.repo->desc); html("</td><td class='sub right'>"); - html_txt(ctx.repo->owner); + if (ctx.repo->owner_filter) { + cgit_open_filter(ctx.repo->owner_filter); + html_txt(ctx.repo->owner); + cgit_close_filter(ctx.repo->owner_filter); + } else { + html_txt(ctx.repo->owner); + } } else { if (ctx.cfg.root_desc) html_txt(ctx.cfg.root_desc); @@ -1142,11 +1188,11 @@ void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base, * name starts with {v,V}[0-9] and the prettify mapping is injective, * i.e. each stripped tag can be inverted without ambiguities. */ - if (get_oid(fmt("refs/tags/%s", ref), &oid) == 0 && + if (repo_get_oid(the_repository, fmt("refs/tags/%s", ref), &oid) == 0 && (ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) && - ((get_oid(fmt("refs/tags/%s", ref + 1), &oid) == 0) + - (get_oid(fmt("refs/tags/v%s", ref + 1), &oid) == 0) + - (get_oid(fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1)) + ((repo_get_oid(the_repository, fmt("refs/tags/%s", ref + 1), &oid) == 0) + + (repo_get_oid(the_repository, fmt("refs/tags/v%s", ref + 1), &oid) == 0) + + (repo_get_oid(the_repository, fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1)) ref++; strbuf_addf(filename, "%s-%s", base, ref); diff --git a/ui-shared.h b/ui-shared.h index 6964873..f12fa99 100644 --- a/ui-shared.h +++ b/ui-shared.h @@ -65,7 +65,7 @@ __attribute__((format (printf,1,2))) extern void cgit_print_error(const char *fmt, ...); __attribute__((format (printf,1,0))) extern void cgit_vprint_error(const char *fmt, va_list ap); -extern const struct date_mode *cgit_date_mode(enum date_mode_type type); +extern const struct date_mode cgit_date_mode(enum date_mode_type type); extern void cgit_print_age(time_t t, int tz, time_t max_relative); extern void cgit_print_http_headers(void); extern void cgit_redirect(const char *url, bool permanent); diff --git a/ui-snapshot.c b/ui-snapshot.c index 18361a6..9f629a9 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c @@ -117,7 +117,7 @@ const struct object_id *cgit_snapshot_get_sig(const char *ref, struct notes_tree *tree; struct object_id oid; - if (get_oid(ref, &oid)) + if (repo_get_oid(the_repository, ref, &oid)) return NULL; tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]]; @@ -156,7 +156,7 @@ static int make_snapshot(const struct cgit_snapshot_format *format, { struct object_id oid; - if (get_oid(hex, &oid)) { + if (repo_get_oid(the_repository, hex, &oid)) { cgit_print_error_page(404, "Not found", "Bad object id: %s", hex); return 1; @@ -190,7 +190,7 @@ static int write_sig(const struct cgit_snapshot_format *format, return 0; } - buf = read_object_file(note, &type, &size); + buf = repo_read_object_file(the_repository, note, &type, &size); if (!buf) { cgit_print_error_page(404, "Not found", "Not found"); return 0; @@ -230,7 +230,7 @@ static const char *get_ref_from_filename(const struct cgit_repo *repo, strbuf_addstr(&snapshot, filename); strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix)); - if (get_oid(snapshot.buf, &oid) == 0) + if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0) goto out; reponame = cgit_snapshot_prefix(repo); @@ -242,15 +242,15 @@ static const char *get_ref_from_filename(const struct cgit_repo *repo, strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0); } - if (get_oid(snapshot.buf, &oid) == 0) + if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0) goto out; strbuf_insert(&snapshot, 0, "v", 1); - if (get_oid(snapshot.buf, &oid) == 0) + if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0) goto out; strbuf_splice(&snapshot, 0, 1, "V", 1); - if (get_oid(snapshot.buf, &oid) == 0) + if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0) goto out; result = 0; @@ -230,7 +230,7 @@ static struct string_list collect_stats(const struct cgit_period *period) argv[4] = ctx.qry.path; argc += 2; } - init_revisions(&rev, NULL); + repo_init_revisions(the_repository, &rev, NULL); rev.abbrev = DEFAULT_ABBREV; rev.commit_format = CMIT_FMT_DEFAULT; rev.max_parents = 1; @@ -241,8 +241,7 @@ static struct string_list collect_stats(const struct cgit_period *period) memset(&authors, 0, sizeof(authors)); while ((commit = get_revision(&rev)) != NULL) { add_commit(&authors, commit, period); - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } return authors; @@ -48,7 +48,7 @@ void cgit_print_tag(char *revname) revname = ctx.qry.head; strbuf_addf(&fullref, "refs/tags/%s", revname); - if (get_oid(fullref.buf, &oid)) { + if (repo_get_oid(the_repository, fullref.buf, &oid)) { cgit_print_error_page(404, "Not found", "Bad tag reference: %s", revname); goto cleanup; @@ -89,6 +89,7 @@ static void print_object(const struct object_id *oid, const char *path, const ch enum object_type type; char *buf; unsigned long size; + bool is_binary; type = oid_object_info(the_repository, oid, &size); if (type == OBJ_BAD) { @@ -97,12 +98,13 @@ static void print_object(const struct object_id *oid, const char *path, const ch return; } - buf = read_object_file(oid, &type, &size); + buf = repo_read_object_file(the_repository, oid, &type, &size); if (!buf) { cgit_print_error_page(500, "Internal server error", "Error reading object %s", oid_to_hex(oid)); return; } + is_binary = buffer_is_binary(buf, size); cgit_set_title_from_path(path); @@ -110,7 +112,7 @@ static void print_object(const struct object_id *oid, const char *path, const ch htmlf("blob: %s (", oid_to_hex(oid)); cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); - if (ctx.repo->enable_blame) { + if (ctx.repo->enable_blame && !is_binary) { html(") ("); cgit_blame_link("blame", NULL, NULL, ctx.qry.head, rev, path); @@ -123,7 +125,7 @@ static void print_object(const struct object_id *oid, const char *path, const ch return; } - if (buffer_is_binary(buf, size)) + if (is_binary) print_binary_buffer(buf, size); else print_text_buffer(basename, buf, size); @@ -139,8 +141,7 @@ struct single_tree_ctx { }; static int single_tree_cb(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, - void *cbdata) + const char *pathname, unsigned mode, void *cbdata) { struct single_tree_ctx *ctx = cbdata; @@ -185,8 +186,7 @@ static void write_tree_link(const struct object_id *oid, char *name, tree_ctx.name = NULL; tree_ctx.count = 0; - read_tree_recursive(the_repository, tree, "", 0, 1, - &paths, single_tree_cb, &tree_ctx); + read_tree(the_repository, tree, &paths, single_tree_cb, &tree_ctx); if (tree_ctx.count != 1) break; @@ -199,14 +199,16 @@ static void write_tree_link(const struct object_id *oid, char *name, } static int ls_item(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *cbdata) + const char *pathname, unsigned mode, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; char *name; struct strbuf fullpath = STRBUF_INIT; + struct strbuf linkpath = STRBUF_INIT; struct strbuf class = STRBUF_INIT; enum object_type type; unsigned long size = 0; + char *buf; name = xstrdup(pathname); strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", @@ -218,8 +220,7 @@ static int ls_item(const struct object_id *oid, struct strbuf *base, htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", name, oid_to_hex(oid)); - free(name); - return 0; + goto cleanup; } } @@ -239,6 +240,21 @@ static int ls_item(const struct object_id *oid, struct strbuf *base, cgit_tree_link(name, NULL, class.buf, ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); } + if (S_ISLNK(mode)) { + html(" -> "); + buf = repo_read_object_file(the_repository, oid, &type, &size); + if (!buf) { + htmlf("Error reading object: %s", oid_to_hex(oid)); + goto cleanup; + } + strbuf_addbuf(&linkpath, &fullpath); + strbuf_addf(&linkpath, "/../%s", buf); + strbuf_normalize_path(&linkpath); + cgit_tree_link(buf, NULL, class.buf, ctx.qry.head, + walk_tree_ctx->curr_rev, linkpath.buf); + free(buf); + strbuf_release(&linkpath); + } htmlf("</td><td class='ls-size'>%li</td>", size); html("<td>"); @@ -255,6 +271,8 @@ static int ls_item(const struct object_id *oid, struct strbuf *base, cgit_blame_link("blame", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); html("</td></tr>\n"); + +cleanup: free(name); strbuf_release(&fullpath); strbuf_release(&class); @@ -294,14 +312,13 @@ static void ls_tree(const struct object_id *oid, const char *path, struct walk_t } ls_head(); - read_tree_recursive(the_repository, tree, "", 0, 1, - &paths, ls_item, walk_tree_ctx); + read_tree(the_repository, tree, &paths, ls_item, walk_tree_ctx); ls_tail(); } static int walk_tree(const struct object_id *oid, struct strbuf *base, - const char *pathname, unsigned mode, int stage, void *cbdata) + const char *pathname, unsigned mode, void *cbdata) { struct walk_tree_context *walk_tree_ctx = cbdata; @@ -326,7 +343,7 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base, return 0; } } - ls_item(oid, base, pathname, mode, stage, walk_tree_ctx); + ls_item(oid, base, pathname, mode, walk_tree_ctx); return 0; } @@ -355,13 +372,13 @@ void cgit_print_tree(const char *rev, char *path) if (!rev) rev = ctx.qry.head; - if (get_oid(rev, &oid)) { + if (repo_get_oid(the_repository, rev, &oid)) { cgit_print_error_page(404, "Not found", "Invalid revision name: %s", rev); return; } commit = lookup_commit_reference(the_repository, &oid); - if (!commit || parse_commit(commit)) { + if (!commit || repo_parse_commit(the_repository, commit)) { cgit_print_error_page(404, "Not found", "Invalid commit reference: %s", rev); return; @@ -374,10 +391,8 @@ void cgit_print_tree(const char *rev, char *path) goto cleanup; } - read_tree_recursive(the_repository, - repo_get_commit_tree(the_repository, commit), - "", 0, 0, - &paths, walk_tree, &walk_tree_ctx); + read_tree(the_repository, repo_get_commit_tree(the_repository, commit), + &paths, walk_tree, &walk_tree_ctx); if (walk_tree_ctx.state == 1) ls_tail(); else if (walk_tree_ctx.state == 2) |