| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Makefile | 75 | ||||
| -rw-r--r-- | cache.c | 21 | ||||
| -rw-r--r-- | cache.h | 23 | ||||
| -rw-r--r-- | cgit.c | 389 | ||||
| -rw-r--r-- | cgit.h | 227 | ||||
| -rw-r--r-- | cmd.c | 112 | ||||
| -rw-r--r-- | cmd.h | 15 | ||||
| -rw-r--r-- | configfile.c | 87 | ||||
| -rw-r--r-- | configfile.h | 8 | ||||
| -rw-r--r-- | html.c | 95 | ||||
| -rw-r--r-- | html.h | 20 | ||||
| -rw-r--r-- | parsing.c | 146 | ||||
| -rw-r--r-- | shared.c | 257 | ||||
| -rw-r--r-- | ui-blob.c | 16 | ||||
| -rw-r--r-- | ui-blob.h | 6 | ||||
| -rw-r--r-- | ui-commit.c | 26 | ||||
| -rw-r--r-- | ui-commit.h | 6 | ||||
| -rw-r--r-- | ui-diff.c | 9 | ||||
| -rw-r--r-- | ui-diff.h | 7 | ||||
| -rw-r--r-- | ui-log.c | 32 | ||||
| -rw-r--r-- | ui-log.h | 7 | ||||
| -rw-r--r-- | ui-patch.c | 10 | ||||
| -rw-r--r-- | ui-patch.h | 6 | ||||
| -rw-r--r-- | ui-refs.c | 175 | ||||
| -rw-r--r-- | ui-refs.h | 8 | ||||
| -rw-r--r-- | ui-repolist.c | 54 | ||||
| -rw-r--r-- | ui-repolist.h | 6 | ||||
| -rw-r--r-- | ui-shared.c | 237 | ||||
| -rw-r--r-- | ui-shared.h | 36 | ||||
| -rw-r--r-- | ui-snapshot.c | 121 | ||||
| -rw-r--r-- | ui-snapshot.h | 8 | ||||
| -rw-r--r-- | ui-summary.c | 189 | ||||
| -rw-r--r-- | ui-summary.h | 6 | ||||
| -rw-r--r-- | ui-tag.c | 3 | ||||
| -rw-r--r-- | ui-tag.h | 6 | ||||
| -rw-r--r-- | ui-tree.c | 26 | ||||
| -rw-r--r-- | ui-tree.h | 6 |
38 files changed, 1110 insertions, 1372 deletions
@@ -3,4 +3,3 @@ cgit cgit.conf VERSION *.o -*.d @@ -12,61 +12,12 @@ GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 # -include cgit.conf -# -# Define a way to invoke make in subdirs quietly, shamelessly ripped -# from git.git -# -QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir -QUIET_SUBDIR1 = - -ifneq ($(findstring $(MAKEFLAGS),w),w) -PRINT_DIR = --no-print-directory -else # "make -w" -NO_SUBDIR = : -endif - -ifndef V - QUIET_CC = @echo ' ' CC $@; - QUIET_MM = @echo ' ' MM $@; - QUIET_SUBDIR0 = +@subdir= - QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ - $(MAKE) $(PRINT_DIR) -C $$subdir -endif - -# -# Define a pattern rule for automatic dependency building -# -%.d: %.c - $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@ - -# -# Define a pattern rule for silent object building -# -%.o: %.c - $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $< - EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto -OBJECTS = -OBJECTS += cache.o -OBJECTS += cgit.o -OBJECTS += cmd.o -OBJECTS += configfile.o -OBJECTS += html.o -OBJECTS += parsing.o -OBJECTS += shared.o -OBJECTS += ui-blob.o -OBJECTS += ui-commit.o -OBJECTS += ui-diff.o -OBJECTS += ui-log.o -OBJECTS += ui-patch.o -OBJECTS += ui-refs.o -OBJECTS += ui-repolist.o -OBJECTS += ui-shared.o -OBJECTS += ui-snapshot.o -OBJECTS += ui-summary.o -OBJECTS += ui-tag.o -OBJECTS += ui-tree.o +OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ + ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ + ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o ui-patch.o + ifdef NEEDS_LIBICONV EXTLIBS += -liconv @@ -90,25 +41,21 @@ CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' -cgit: $(OBJECTS) - $(QUIET_CC)$(CC) $(CFLAGS) -o cgit $(OBJECTS) $(EXTLIBS) - -$(OBJECTS): git/xdiff/lib.a git/libgit.a - -cgit.o: VERSION +cgit: cgit.c $(OBJECTS) + $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) --include $(OBJECTS:.o=.d) +$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION git/xdiff/lib.a: | git git/libgit.a: | git git: - $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a - $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.a + cd git && $(MAKE) xdiff/lib.a + cd git && $(MAKE) libgit.a test: all - $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all + $(MAKE) -C tests install: all mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH) @@ -122,7 +69,7 @@ uninstall: rm -f $(CGIT_SCRIPT_PATH)/cgit.png clean: - rm -f cgit VERSION *.o *.d + rm -f cgit VERSION *.o cd git && $(MAKE) clean distclean: clean @@ -7,7 +7,6 @@ */ #include "cgit.h" -#include "cache.h" const int NOLOCK = -1; @@ -45,23 +44,23 @@ int cache_create_dirs() { char *path; - path = fmt("%s", ctx.cfg.cache_root); + path = fmt("%s", cgit_cache_root); if (mkdir(path, S_IRWXU) && errno!=EEXIST) return 0; - if (!ctx.repo) + if (!cgit_repo) return 0; - path = fmt("%s/%s", ctx.cfg.cache_root, - cache_safe_filename(ctx.repo->url)); + path = fmt("%s/%s", cgit_cache_root, + cache_safe_filename(cgit_repo->url)); if (mkdir(path, S_IRWXU) && errno!=EEXIST) return 0; - if (ctx.qry.page) { - path = fmt("%s/%s/%s", ctx.cfg.cache_root, - cache_safe_filename(ctx.repo->url), - ctx.qry.page); + if (cgit_query_page) { + path = fmt("%s/%s/%s", cgit_cache_root, + cache_safe_filename(cgit_repo->url), + cgit_query_page); if (mkdir(path, S_IRWXU) && errno!=EEXIST) return 0; } @@ -75,7 +74,7 @@ int cache_refill_overdue(const char *lockfile) if (stat(lockfile, &st)) return 0; else - return (time(NULL) - st.st_mtime > ctx.cfg.cache_max_create_time); + return (time(NULL) - st.st_mtime > cgit_cache_max_create_time); } int cache_lock(struct cacheitem *item) @@ -84,7 +83,7 @@ int cache_lock(struct cacheitem *item) char *lockfile = xstrdup(fmt("%s.lock", item->name)); top: - if (++i > ctx.cfg.max_lock_attempts) + if (++i > cgit_max_lock_attempts) die("cache_lock: unable to lock %s: %s", item->name, strerror(errno)); diff --git a/cache.h b/cache.h deleted file mode 100644 index 4dcbea3..0000000 --- a/cache.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Since git has it's own cache.h which we include, - * lets test on CGIT_CACHE_H to avoid confusion - */ - -#ifndef CGIT_CACHE_H -#define CGIT_CACHE_H - -struct cacheitem { - char *name; - struct stat st; - int ttl; - int fd; -}; - -extern char *cache_safe_filename(const char *unsafe); -extern int cache_lock(struct cacheitem *item); -extern int cache_unlock(struct cacheitem *item); -extern int cache_cancel_lock(struct cacheitem *item); -extern int cache_exist(struct cacheitem *item); -extern int cache_expired(struct cacheitem *item); - -#endif /* CGIT_CACHE_H */ @@ -7,199 +7,40 @@ */ #include "cgit.h" -#include "cache.h" -#include "cmd.h" -#include "configfile.h" -#include "html.h" -#include "ui-shared.h" - -const char *cgit_version = CGIT_VERSION; - -void config_cb(const char *name, const char *value) -{ - if (!strcmp(name, "root-title")) - ctx.cfg.root_title = xstrdup(value); - else if (!strcmp(name, "css")) - ctx.cfg.css = xstrdup(value); - else if (!strcmp(name, "logo")) - ctx.cfg.logo = xstrdup(value); - else if (!strcmp(name, "index-header")) - ctx.cfg.index_header = xstrdup(value); - else if (!strcmp(name, "index-info")) - ctx.cfg.index_info = xstrdup(value); - else if (!strcmp(name, "logo-link")) - ctx.cfg.logo_link = xstrdup(value); - else if (!strcmp(name, "module-link")) - ctx.cfg.module_link = xstrdup(value); - else if (!strcmp(name, "virtual-root")) { - ctx.cfg.virtual_root = trim_end(value, '/'); - if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) - ctx.cfg.virtual_root = ""; - } else if (!strcmp(name, "nocache")) - ctx.cfg.nocache = atoi(value); - else if (!strcmp(name, "snapshots")) - ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); - else if (!strcmp(name, "enable-index-links")) - ctx.cfg.enable_index_links = atoi(value); - else if (!strcmp(name, "enable-log-filecount")) - ctx.cfg.enable_log_filecount = atoi(value); - else if (!strcmp(name, "enable-log-linecount")) - ctx.cfg.enable_log_linecount = atoi(value); - else if (!strcmp(name, "cache-root")) - ctx.cfg.cache_root = xstrdup(value); - else if (!strcmp(name, "cache-root-ttl")) - ctx.cfg.cache_root_ttl = atoi(value); - else if (!strcmp(name, "cache-repo-ttl")) - ctx.cfg.cache_repo_ttl = atoi(value); - else if (!strcmp(name, "cache-static-ttl")) - ctx.cfg.cache_static_ttl = atoi(value); - else if (!strcmp(name, "cache-dynamic-ttl")) - ctx.cfg.cache_dynamic_ttl = atoi(value); - else if (!strcmp(name, "max-message-length")) - ctx.cfg.max_msg_len = atoi(value); - else if (!strcmp(name, "max-repodesc-length")) - ctx.cfg.max_repodesc_len = atoi(value); - else if (!strcmp(name, "max-commit-count")) - ctx.cfg.max_commit_count = atoi(value); - else if (!strcmp(name, "summary-log")) - ctx.cfg.summary_log = atoi(value); - else if (!strcmp(name, "summary-branches")) - ctx.cfg.summary_branches = atoi(value); - else if (!strcmp(name, "summary-tags")) - ctx.cfg.summary_tags = atoi(value); - else if (!strcmp(name, "agefile")) - ctx.cfg.agefile = xstrdup(value); - else if (!strcmp(name, "renamelimit")) - ctx.cfg.renamelimit = atoi(value); - else if (!strcmp(name, "robots")) - ctx.cfg.robots = xstrdup(value); - else if (!strcmp(name, "clone-prefix")) - ctx.cfg.clone_prefix = xstrdup(value); - else if (!strcmp(name, "repo.group")) - ctx.cfg.repo_group = xstrdup(value); - else if (!strcmp(name, "repo.url")) - ctx.repo = cgit_add_repo(value); - else if (!strcmp(name, "repo.name")) - ctx.repo->name = xstrdup(value); - else if (ctx.repo && !strcmp(name, "repo.path")) - ctx.repo->path = trim_end(value, '/'); - else if (ctx.repo && !strcmp(name, "repo.clone-url")) - ctx.repo->clone_url = xstrdup(value); - else if (ctx.repo && !strcmp(name, "repo.desc")) - ctx.repo->desc = xstrdup(value); - else if (ctx.repo && !strcmp(name, "repo.owner")) - ctx.repo->owner = xstrdup(value); - else if (ctx.repo && !strcmp(name, "repo.defbranch")) - ctx.repo->defbranch = xstrdup(value); - else if (ctx.repo && !strcmp(name, "repo.snapshots")) - ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ - else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) - ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); - else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) - ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); - else if (ctx.repo && !strcmp(name, "repo.module-link")) - ctx.repo->module_link= xstrdup(value); - else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { - if (*value == '/') - ctx.repo->readme = xstrdup(value); - else - ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); - } else if (!strcmp(name, "include")) - parse_configfile(value, config_cb); -} - -static void querystring_cb(const char *name, const char *value) -{ - if (!strcmp(name,"r")) { - ctx.qry.repo = xstrdup(value); - ctx.repo = cgit_get_repoinfo(value); - } else if (!strcmp(name, "p")) { - ctx.qry.page = xstrdup(value); - } else if (!strcmp(name, "url")) { - cgit_parse_url(value); - } else if (!strcmp(name, "qt")) { - ctx.qry.grep = xstrdup(value); - } else if (!strcmp(name, "q")) { - ctx.qry.search = xstrdup(value); - } else if (!strcmp(name, "h")) { - ctx.qry.head = xstrdup(value); - ctx.qry.has_symref = 1; - } else if (!strcmp(name, "id")) { - ctx.qry.sha1 = xstrdup(value); - ctx.qry.has_sha1 = 1; - } else if (!strcmp(name, "id2")) { - ctx.qry.sha2 = xstrdup(value); - ctx.qry.has_sha1 = 1; - } else if (!strcmp(name, "ofs")) { - ctx.qry.ofs = atoi(value); - } else if (!strcmp(name, "path")) { - ctx.qry.path = trim_end(value, '/'); - } else if (!strcmp(name, "name")) { - ctx.qry.name = xstrdup(value); - } -} - -static void prepare_context(struct cgit_context *ctx) -{ - memset(ctx, 0, sizeof(ctx)); - ctx->cfg.agefile = "info/web/last-modified"; - ctx->cfg.cache_dynamic_ttl = 5; - ctx->cfg.cache_max_create_time = 5; - ctx->cfg.cache_repo_ttl = 5; - ctx->cfg.cache_root = CGIT_CACHE_ROOT; - ctx->cfg.cache_root_ttl = 5; - ctx->cfg.cache_static_ttl = -1; - ctx->cfg.css = "/cgit.css"; - ctx->cfg.logo = "/git-logo.png"; - ctx->cfg.max_commit_count = 50; - ctx->cfg.max_lock_attempts = 5; - ctx->cfg.max_msg_len = 60; - ctx->cfg.max_repodesc_len = 60; - ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; - ctx->cfg.renamelimit = -1; - ctx->cfg.robots = "index, nofollow"; - ctx->cfg.root_title = "Git repository browser"; - ctx->cfg.script_name = CGIT_SCRIPT_NAME; - ctx->page.mimetype = "text/html"; - ctx->page.charset = PAGE_ENCODING; - ctx->page.filename = NULL; -} static int cgit_prepare_cache(struct cacheitem *item) { - if (!ctx.repo && ctx.qry.repo) { - ctx.page.title = fmt("%s - %s", ctx.cfg.root_title, - "Bad request"); - cgit_print_http_headers(&ctx); - cgit_print_docstart(&ctx); - cgit_print_pageheader(&ctx); - cgit_print_error(fmt("Unknown repo: %s", ctx.qry.repo)); + if (!cgit_repo && cgit_query_repo) { + char *title = fmt("%s - %s", cgit_root_title, "Bad request"); + cgit_print_docstart(title, item); + cgit_print_pageheader(title, 0); + cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo)); cgit_print_docend(); return 0; } - if (!ctx.repo) { - item->name = xstrdup(fmt("%s/index.html", ctx.cfg.cache_root)); - item->ttl = ctx.cfg.cache_root_ttl; + if (!cgit_repo) { + item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); + item->ttl = cgit_cache_root_ttl; return 1; } - if (!ctx.qry.page) { - item->name = xstrdup(fmt("%s/%s/index.%s.html", ctx.cfg.cache_root, - cache_safe_filename(ctx.repo->url), - cache_safe_filename(ctx.qry.raw))); - item->ttl = ctx.cfg.cache_repo_ttl; + if (!cgit_cmd) { + item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root, + cache_safe_filename(cgit_repo->url), + cache_safe_filename(cgit_querystring))); + item->ttl = cgit_cache_repo_ttl; } else { - item->name = xstrdup(fmt("%s/%s/%s/%s.html", ctx.cfg.cache_root, - cache_safe_filename(ctx.repo->url), - ctx.qry.page, - cache_safe_filename(ctx.qry.raw))); - if (ctx.qry.has_symref) - item->ttl = ctx.cfg.cache_dynamic_ttl; - else if (ctx.qry.has_sha1) - item->ttl = ctx.cfg.cache_static_ttl; + item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, + cache_safe_filename(cgit_repo->url), + cgit_query_page, + cache_safe_filename(cgit_querystring))); + if (cgit_query_has_symref) + item->ttl = cgit_cache_dynamic_ttl; + else if (cgit_query_has_sha1) + item->ttl = cgit_cache_static_ttl; else - item->ttl = ctx.cfg.cache_repo_ttl; + item->ttl = cgit_cache_repo_ttl; } return 1; } @@ -223,7 +64,7 @@ int find_current_ref(const char *refname, const unsigned char *sha1, return info->match; } -char *find_default_branch(struct cgit_repo *repo) +char *find_default_branch(struct repoinfo *repo) { struct refmatch info; @@ -237,98 +78,113 @@ char *find_default_branch(struct cgit_repo *repo) return info.first_ref; } -static int prepare_repo_cmd(struct cgit_context *ctx) +static void cgit_print_repo_page(struct cacheitem *item) { - char *tmp; + char *title, *tmp; + int show_search; unsigned char sha1[20]; - int nongit = 0; - setenv("GIT_DIR", ctx->repo->path, 1); - setup_git_directory_gently(&nongit); - if (nongit) { - ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, - "config error"); - tmp = fmt("Not a git repository: '%s'", ctx->repo->path); - ctx->repo = NULL; - cgit_print_http_headers(ctx); - cgit_print_docstart(ctx); - cgit_print_pageheader(ctx); - cgit_print_error(tmp); + if (chdir(cgit_repo->path)) { + title = fmt("%s - %s", cgit_root_title, "Bad request"); + cgit_print_docstart(title, item); + cgit_print_pageheader(title, 0); + cgit_print_error(fmt("Unable to scan repository: %s", + strerror(errno))); cgit_print_docend(); - return 1; + return; } - ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); - if (!ctx->qry.head) { - ctx->qry.head = xstrdup(find_default_branch(ctx->repo)); - ctx->repo->defbranch = ctx->qry.head; + title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); + show_search = 0; + setenv("GIT_DIR", cgit_repo->path, 1); + + if (!cgit_query_head) { + cgit_query_head = xstrdup(find_default_branch(cgit_repo)); + cgit_repo->defbranch = cgit_query_head; } - if (!ctx->qry.head) { - cgit_print_http_headers(ctx); - cgit_print_docstart(ctx); - cgit_print_pageheader(ctx); + if (!cgit_query_head) { + cgit_print_docstart(title, item); + cgit_print_pageheader(title, 0); cgit_print_error("Repository seems to be empty"); cgit_print_docend(); - return 1; + return; } - if (get_sha1(ctx->qry.head, sha1)) { - tmp = xstrdup(ctx->qry.head); - ctx->qry.head = ctx->repo->defbranch; - cgit_print_http_headers(ctx); - cgit_print_docstart(ctx); - cgit_print_pageheader(ctx); + if (get_sha1(cgit_query_head, sha1)) { + tmp = xstrdup(cgit_query_head); + cgit_query_head = cgit_repo->defbranch; + cgit_print_docstart(title, item); + cgit_print_pageheader(title, 0); cgit_print_error(fmt("Invalid branch: %s", tmp)); cgit_print_docend(); - return 1; + return; } - return 0; -} - -static void process_request(struct cgit_context *ctx) -{ - struct cgit_cmd *cmd; - cmd = cgit_get_cmd(ctx); - if (!cmd) { - ctx->page.title = "cgit error"; - ctx->repo = NULL; - cgit_print_http_headers(ctx); - cgit_print_docstart(ctx); - cgit_print_pageheader(ctx); - cgit_print_error("Invalid request"); - cgit_print_docend(); + if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { + cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1, + cgit_repobasename(cgit_repo->url), + cgit_query_path, + cgit_repo->snapshots ); return; } - if (cmd->want_repo && prepare_repo_cmd(ctx)) + if (cgit_cmd == CMD_PATCH) { + cgit_print_patch(cgit_query_sha1, item); return; - - if (cmd->want_layout) { - cgit_print_http_headers(ctx); - cgit_print_docstart(ctx); - cgit_print_pageheader(ctx); } - cmd->fn(ctx); + if (cgit_cmd == CMD_BLOB) { + cgit_print_blob(item, cgit_query_sha1, cgit_query_path); + return; + } - if (cmd->want_layout) + show_search = (cgit_cmd == CMD_LOG); + cgit_print_docstart(title, item); + if (!cgit_cmd) { + cgit_print_pageheader("summary", show_search); + cgit_print_summary(); cgit_print_docend(); -} + return; + } -static long ttl_seconds(long ttl) -{ - if (ttl<0) - return 60 * 60 * 24 * 365; - else - return ttl * 60; + cgit_print_pageheader(cgit_query_page, show_search); + + switch(cgit_cmd) { + case CMD_LOG: + cgit_print_log(cgit_query_sha1, cgit_query_ofs, + cgit_max_commit_count, cgit_query_grep, cgit_query_search, + cgit_query_path, 1); + break; + case CMD_TREE: + cgit_print_tree(cgit_query_sha1, cgit_query_path); + break; + case CMD_COMMIT: + cgit_print_commit(cgit_query_sha1); + break; + case CMD_REFS: + cgit_print_refs(); + break; + case CMD_TAG: + cgit_print_tag(cgit_query_sha1); + break; + case CMD_DIFF: + cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path); + break; + default: + cgit_print_error("Invalid request"); + } + cgit_print_docend(); } static void cgit_fill_cache(struct cacheitem *item, int use_cache) { + static char buf[PATH_MAX]; int stdout2; + getcwd(buf, sizeof(buf)); + item->st.st_mtime = time(NULL); + if (use_cache) { stdout2 = chk_positive(dup(STDOUT_FILENO), "Preserving STDOUT"); @@ -336,9 +192,10 @@ static void cgit_fill_cache(struct cacheitem *item, int use_cache) chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); } - ctx.page.modified = time(NULL); - ctx.page.expires = ctx.page.modified + ttl_seconds(item->ttl); - process_request(&ctx); + if (cgit_repo) + cgit_print_repo_page(item); + else + cgit_print_repolist(item); if (use_cache) { chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); @@ -346,6 +203,8 @@ static void cgit_fill_cache(struct cacheitem *item, int use_cache) "Restoring original STDOUT"); chk_zero(close(stdout2), "Closing temporary STDOUT"); } + + chdir(buf); } static void cgit_check_cache(struct cacheitem *item) @@ -353,7 +212,7 @@ static void cgit_check_cache(struct cacheitem *item) int i = 0; top: - if (++i > ctx.cfg.max_lock_attempts) { + if (++i > cgit_max_lock_attempts) { die("cgit_refresh_cache: unable to lock %s: %s", item->name, strerror(errno)); } @@ -399,30 +258,30 @@ static void cgit_parse_args(int argc, const char **argv) for (i = 1; i < argc; i++) { if (!strncmp(argv[i], "--cache=", 8)) { - ctx.cfg.cache_root = xstrdup(argv[i]+8); + cgit_cache_root = xstrdup(argv[i]+8); } if (!strcmp(argv[i], "--nocache")) { - ctx.cfg.nocache = 1; + cgit_nocache = 1; } if (!strncmp(argv[i], "--query=", 8)) { - ctx.qry.raw = xstrdup(argv[i]+8); + cgit_querystring = xstrdup(argv[i]+8); } if (!strncmp(argv[i], "--repo=", 7)) { - ctx.qry.repo = xstrdup(argv[i]+7); + cgit_query_repo = xstrdup(argv[i]+7); } if (!strncmp(argv[i], "--page=", 7)) { - ctx.qry.page = xstrdup(argv[i]+7); + cgit_query_page = xstrdup(argv[i]+7); } if (!strncmp(argv[i], "--head=", 7)) { - ctx.qry.head = xstrdup(argv[i]+7); - ctx.qry.has_symref = 1; + cgit_query_head = xstrdup(argv[i]+7); + cgit_query_has_symref = 1; } if (!strncmp(argv[i], "--sha1=", 7)) { - ctx.qry.sha1 = xstrdup(argv[i]+7); - ctx.qry.has_sha1 = 1; + cgit_query_sha1 = xstrdup(argv[i]+7); + cgit_query_has_sha1 = 1; } if (!strncmp(argv[i], "--ofs=", 6)) { - ctx.qry.ofs = atoi(argv[i]+6); + cgit_query_ofs = atoi(argv[i]+6); } } } @@ -432,24 +291,24 @@ int main(int argc, const char **argv) struct cacheitem item; const char *cgit_config_env = getenv("CGIT_CONFIG"); - prepare_context(&ctx); + htmlfd = STDOUT_FILENO; item.st.st_mtime = time(NULL); cgit_repolist.length = 0; cgit_repolist.count = 0; cgit_repolist.repos = NULL; - parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, - config_cb); - ctx.repo = NULL; + cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG, + cgit_global_config_cb); + cgit_repo = NULL; if (getenv("SCRIPT_NAME")) - ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); + cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); if (getenv("QUERY_STRING")) - ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); + cgit_querystring = xstrdup(getenv("QUERY_STRING")); cgit_parse_args(argc, argv); - http_parse_querystring(ctx.qry.raw, querystring_cb); + cgit_parse_query(cgit_querystring, cgit_querystring_cb); if (!cgit_prepare_cache(&item)) return 0; - if (ctx.cfg.nocache) { + if (cgit_nocache) { cgit_fill_cache(&item, 0); } else { cgit_check_cache(&item); @@ -20,6 +20,19 @@ /* + * The valid cgit repo-commands + */ +#define CMD_LOG 1 +#define CMD_COMMIT 2 +#define CMD_DIFF 3 +#define CMD_TREE 4 +#define CMD_BLOB 5 +#define CMD_SNAPSHOT 6 +#define CMD_TAG 7 +#define CMD_REFS 8 +#define CMD_PATCH 9 + +/* * Dateformats used on misc. pages */ #define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" @@ -46,7 +59,14 @@ typedef void (*configfn)(const char *name, const char *value); typedef void (*filepair_fn)(struct diff_filepair *pair); typedef void (*linediff_fn)(char *line, int len); -struct cgit_repo { +struct cacheitem { + char *name; + struct stat st; + int ttl; + int fd; +}; + +struct repoinfo { char *url; char *name; char *path; @@ -62,10 +82,10 @@ struct cgit_repo { int enable_log_linecount; }; -struct cgit_repolist { +struct repolist { int length; int count; - struct cgit_repo *repos; + struct repoinfo *repos; }; struct commitinfo { @@ -103,94 +123,74 @@ struct reflist { int count; }; -struct cgit_query { - int has_symref; - int has_sha1; - char *raw; - char *repo; - char *page; - char *search; - char *grep; - char *head; - char *sha1; - char *sha2; - char *path; - char *name; - int ofs; -}; +extern const char *cgit_version; -struct cgit_config { - char *agefile; - char *cache_root; - char *clone_prefix; - char *css; - char *index_header; - char *index_info; - char *logo; - char *logo_link; - char *module_link; - char *repo_group; - char *robots; - char *root_title; - char *script_name; - char *virtual_root; - int cache_dynamic_ttl; - int cache_max_create_time; - int cache_repo_ttl; - int cache_root_ttl; - int cache_static_ttl; - int enable_index_links; - int enable_log_filecount; - int enable_log_linecount; - int max_commit_count; - int max_lock_attempts; - int max_msg_len; - int max_repodesc_len; - int nocache; - int renamelimit; - int snapshots; - int summary_branches; - int summary_log; - int summary_tags; -}; +extern struct repolist cgit_repolist; +extern struct repoinfo *cgit_repo; +extern int cgit_cmd; -struct cgit_page { - time_t modified; - time_t expires; - char *mimetype; - char *charset; - char *filename; - char *title; -}; +extern char *cgit_root_title; +extern char *cgit_css; +extern char *cgit_logo; +extern char *cgit_index_header; +extern char *cgit_index_info; +extern char *cgit_logo_link; +extern char *cgit_module_link; +extern char *cgit_agefile; +extern char *cgit_virtual_root; +extern char *cgit_script_name; +extern char *cgit_cache_root; +extern char *cgit_repo_group; +extern char *cgit_robots; +extern char *cgit_clone_prefix; -struct cgit_context { - struct cgit_query qry; - struct cgit_config cfg; - struct cgit_repo *repo; - struct cgit_page page; -}; +extern int cgit_nocache; +extern int cgit_snapshots; +extern int cgit_enable_index_links; +extern int cgit_enable_log_filecount; +extern int cgit_enable_log_linecount; +extern int cgit_max_lock_attempts; +extern int cgit_cache_root_ttl; +extern int cgit_cache_repo_ttl; +extern int cgit_cache_dynamic_ttl; +extern int cgit_cache_static_ttl; +extern int cgit_cache_max_create_time; +extern int cgit_summary_log; +extern int cgit_summary_tags; +extern int cgit_summary_branches; -struct cgit_snapshot_format { - const char *suffix; - const char *mimetype; - write_archive_fn_t write_func; - int bit; -}; +extern int cgit_max_msg_len; +extern int cgit_max_repodesc_len; +extern int cgit_max_commit_count; -extern const char *cgit_version; +extern int cgit_query_has_symref; +extern int cgit_query_has_sha1; + +extern char *cgit_querystring; +extern char *cgit_query_repo; +extern char *cgit_query_page; +extern char *cgit_query_search; +extern char *cgit_query_grep; +extern char *cgit_query_head; +extern char *cgit_query_sha1; +extern char *cgit_query_sha2; +extern char *cgit_query_path; +extern char *cgit_query_name; +extern int cgit_query_ofs; -extern struct cgit_repolist cgit_repolist; -extern struct cgit_context ctx; -extern const struct cgit_snapshot_format cgit_snapshot_formats[]; +extern int htmlfd; -extern struct cgit_repo *cgit_add_repo(const char *url); -extern struct cgit_repo *cgit_get_repoinfo(const char *url); +extern int cgit_get_cmd_index(const char *cmd); +extern struct repoinfo *cgit_get_repoinfo(const char *url); +extern void cgit_global_config_cb(const char *name, const char *value); extern void cgit_repo_config_cb(const char *name, const char *value); +extern void cgit_querystring_cb(const char *name, const char *value); extern int chk_zero(int result, char *msg); extern int chk_positive(int result, char *msg); extern int chk_non_negative(int result, char *msg); +extern int hextoint(char c); extern char *trim_end(const char *str, char c); extern char *strlpart(char *txt, int maxlen); extern char *strrpart(char *txt, int maxlen); @@ -213,12 +213,83 @@ extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); extern char *fmt(const char *format,...); +extern void html(const char *txt); +extern void htmlf(const char *format,...); +extern void html_txt(char *txt); +extern void html_ntxt(int len, char *txt); +extern void html_attr(char *txt); +extern void html_hidden(char *name, char *value); +extern void html_option(char *value, char *text, char *selected_value); +extern void html_link_open(char *url, char *title, char *class); +extern void html_link_close(void); +extern void html_filemode(unsigned short mode); +extern int html_include(const char *filename); + +extern int cgit_read_config(const char *filename, configfn fn); +extern int cgit_parse_query(char *txt, configfn fn); extern struct commitinfo *cgit_parse_commit(struct commit *commit); extern struct taginfo *cgit_parse_tag(struct tag *tag); extern void cgit_parse_url(const char *url); +extern char *cache_safe_filename(const char *unsafe); +extern int cache_lock(struct cacheitem *item); +extern int cache_unlock(struct cacheitem *item); +extern int cache_cancel_lock(struct cacheitem *item); +extern int cache_exist(struct cacheitem *item); +extern int cache_expired(struct cacheitem *item); + +extern char *cgit_repourl(const char *reponame); +extern char *cgit_fileurl(const char *reponame, const char *pagename, + const char *filename, const char *query); +extern char *cgit_pageurl(const char *reponame, const char *pagename, + const char *query); + extern const char *cgit_repobasename(const char *reponame); +extern void cgit_tree_link(char *name, char *title, char *class, char *head, + char *rev, char *path); +extern void cgit_log_link(char *name, char *title, char *class, char *head, + char *rev, char *path, int ofs, char *grep, + char *pattern); +extern void cgit_commit_link(char *name, char *title, char *class, char *head, + char *rev); +extern void cgit_refs_link(char *name, char *title, char *class, char *head, + char *rev, char *path); +extern void cgit_snapshot_link(char *name, char *title, char *class, + char *head, char *rev, char *archivename); +extern void cgit_diff_link(char *name, char *title, char *class, char *head, + char *new_rev, char *old_rev, char *path); + +extern void cgit_object_link(struct object *obj); + +extern void cgit_print_error(char *msg); +extern void cgit_print_date(time_t secs, char *format); +extern void cgit_print_age(time_t t, time_t max_relative, char *format); +extern void cgit_print_docstart(char *title, struct cacheitem *item); +extern void cgit_print_docend(); +extern void cgit_print_pageheader(char *title, int show_search); +extern void cgit_print_snapshot_start(const char *mimetype, + const char *filename, + struct cacheitem *item); +extern void cgit_print_branches(int maxcount); +extern void cgit_print_tags(int maxcount); + +extern void cgit_print_repolist(struct cacheitem *item); +extern void cgit_print_summary(); +extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, + char *pattern, char *path, int pager); +extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); +extern void cgit_print_tree(const char *rev, char *path); +extern void cgit_print_commit(char *hex); +extern void cgit_print_refs(); +extern void cgit_print_tag(char *revname); +extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); +extern void cgit_print_patch(char *hex, struct cacheitem *item); +extern void cgit_print_snapshot(struct cacheitem *item, const char *head, + const char *hex, const char *prefix, + const char *filename, int snapshot); +extern void cgit_print_snapshot_links(const char *repo, const char *head, + const char *hex, int snapshots); extern int cgit_parse_snapshots_mask(const char *str); #endif /* CGIT_H */ @@ -1,112 +0,0 @@ -/* cmd.c: the cgit command dispatcher - * - * Copyright (C) 2008 Lars Hjemli - * - * Licensed under GNU General Public License v2 - * (see COPYING for full license text) - */ - -#include "cgit.h" -#include "cmd.h" -#include "ui-blob.h" -#include "ui-commit.h" -#include "ui-diff.h" -#include "ui-log.h" -#include "ui-patch.h" -#include "ui-refs.h" -#include "ui-repolist.h" -#include "ui-snapshot.h" -#include "ui-summary.h" -#include "ui-tag.h" -#include "ui-tree.h" - -static void blob_fn(struct cgit_context *ctx) -{ - cgit_print_blob(ctx->qry.sha1, ctx->qry.path); -} - -static void commit_fn(struct cgit_context *ctx) -{ - cgit_print_commit(ctx->qry.sha1); -} - -static void diff_fn(struct cgit_context *ctx) -{ - cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); -} - -static void repolist_fn(struct cgit_context *ctx) -{ - cgit_print_repolist(); -} - -static void log_fn(struct cgit_context *ctx) -{ - cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, - ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); -} - -static void patch_fn(struct cgit_context *ctx) -{ - cgit_print_patch(ctx->qry.sha1); -} - -static void refs_fn(struct cgit_context *ctx) -{ - cgit_print_refs(); -} - -static void snapshot_fn(struct cgit_context *ctx) -{ - cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, - cgit_repobasename(ctx->repo->url), ctx->qry.path, - ctx->repo->snapshots); -} - -static void summary_fn(struct cgit_context *ctx) -{ - cgit_print_summary(); -} - -static void tag_fn(struct cgit_context *ctx) -{ - cgit_print_tag(ctx->qry.sha1); -} - -static void tree_fn(struct cgit_context *ctx) -{ - cgit_print_tree(ctx->qry.sha1, ctx->qry.path); -} - -#define def_cmd(name, want_repo, want_layout) \ - {#name, name##_fn, want_repo, want_layout} - -struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) -{ - static struct cgit_cmd cmds[] = { - def_cmd(blob, 1, 0), - def_cmd(commit, 1, 1), - def_cmd(diff, 1, 1), - def_cmd(log, 1, 1), - def_cmd(patch, 1, 0), - def_cmd(refs, 1, 1), - def_cmd(repolist, 0, 0), - def_cmd(snapshot, 1, 0), - def_cmd(summary, 1, 1), - def_cmd(tag, 1, 1), - def_cmd(tree, 1, 1), - }; - int i; - - if (ctx->qry.page == NULL) { - if (ctx->repo) - ctx->qry.page = "summary"; - else - ctx->qry.page = "repolist"; - } - - for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) - if (!strcmp(ctx->qry.page, cmds[i].name)) - return &cmds[i]; - return NULL; -} @@ -1,15 +0,0 @@ -#ifndef CMD_H -#define CMD_H - -typedef void (*cgit_cmd_fn)(struct cgit_context *ctx); - -struct cgit_cmd { - const char *name; - cgit_cmd_fn fn; - unsigned int want_repo:1, - want_layout:1; -}; - -extern struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx); - -#endif /* CMD_H */ diff --git a/configfile.c b/configfile.c deleted file mode 100644 index 4908058..0000000 --- a/configfile.c +++ /dev/null @@ -1,87 +0,0 @@ -/* configfile.c: parsing of config files - * - * Copyright (C) 2008 Lars Hjemli - * - * Licensed under GNU General Public License v2 - * (see COPYING for full license text) - */ - -#include <ctype.h> -#include <stdio.h> -#include "configfile.h" - -int next_char(FILE *f) -{ - int c = fgetc(f); - if (c=='\r') { - c = fgetc(f); - if (c!='\n') { - ungetc(c, f); - c = '\r'; - } - } - return c; -} - -void skip_line(FILE *f) -{ - int c; - - while((c=next_char(f)) && c!='\n' && c!=EOF) - ; -} - -int read_config_line(FILE *f, char *line, const char **value, int bufsize) -{ - int i = 0, isname = 0; - - *value = NULL; - while(i<bufsize-1) { - int c = next_char(f); - if (!isname && (c=='#' || c==';')) { - skip_line(f); - continue; - } - if (!isname && isspace(c)) - continue; - - if (c=='=' && !*value) { - line[i] = 0; - *value = &line[i+1]; - } else if (c=='\n' && !isname) { - i = 0; - continue; - } else if (c=='\n' || c==EOF) { - line[i] = 0; - break; - } else { - line[i]=c; - } - isname = 1; - i++; - } - line[i+1] = 0; - return i; -} - -int parse_configfile(const char *filename, configfile_value_fn fn) -{ - static int nesting; - int len; - char line[256]; - const char *value; - FILE *f; - - /* cancel deeply nested include-commands */ - if (nesting > 8) - return -1; - if (!(f = fopen(filename, "r"))) - return -1; - nesting++; - while((len = read_config_line(f, line, &value, sizeof(line))) > 0) - fn(line, value); - nesting--; - fclose(f); - return 0; -} - diff --git a/configfile.h b/configfile.h deleted file mode 100644 index 04235e5..0000000 --- a/configfile.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef CONFIGFILE_H -#define CONFIGFILE_H - -typedef void (*configfile_value_fn)(const char *name, const char *value); - -extern int parse_configfile(const char *filename, configfile_value_fn fn); - -#endif /* CONFIGFILE_H */ @@ -6,13 +6,7 @@ * (see COPYING for full license text) */ -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdarg.h> -#include <string.h> - -int htmlfd = STDOUT_FILENO; +#include "cgit.h" char *fmt(const char *format, ...) { @@ -27,10 +21,8 @@ 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])) { - fprintf(stderr, "[html.c] string truncated: %s\n", format); - exit(1); - } + if (len>sizeof(buf[bufidx])) + die("[html.c] string truncated: %s", format); return buf[bufidx]; } @@ -158,10 +150,25 @@ void html_link_close(void) void html_fileperm(unsigned short mode) { - htmlf("%c%c%c", (mode & 4 ? 'r' : '-'), + htmlf("%c%c%c", (mode & 4 ? 'r' : '-'), (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-')); } +void html_filemode(unsigned short mode) +{ + if (S_ISDIR(mode)) + html("d"); + else if (S_ISLNK(mode)) + html("l"); + else if (S_ISGITLINK(mode)) + html("m"); + else + html("-"); + html_fileperm(mode >> 6); + html_fileperm(mode >> 3); + html_fileperm(mode); +} + int html_include(const char *filename) { FILE *f; @@ -175,67 +182,3 @@ int html_include(const char *filename) fclose(f); return 0; } - -int hextoint(char c) -{ - if (c >= 'a' && c <= 'f') - return 10 + c - 'a'; - else if (c >= 'A' && c <= 'F') - return 10 + c - 'A'; - else if (c >= '0' && c <= '9') - return c - '0'; - else - return -1; -} - -char *convert_query_hexchar(char *txt) -{ - int d1, d2; - if (strlen(txt) < 3) { - *txt = '\0'; - return txt-1; - } - d1 = hextoint(*(txt+1)); - d2 = hextoint(*(txt+2)); - if (d1<0 || d2<0) { - strcpy(txt, txt+3); - return txt-1; - } else { - *txt = d1 * 16 + d2; - strcpy(txt+1, txt+3); - return txt; - } -} - -int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value)) -{ - char *t, *value = NULL, c; - - if (!txt) - return 0; - - t = txt = strdup(txt); - if (t == NULL) { - printf("Out of memory\n"); - exit(1); - } - while((c=*t) != '\0') { - if (c=='=') { - *t = '\0'; - value = t+1; - } else if (c=='+') { - *t = ' '; - } else if (c=='%') { - t = convert_query_hexchar(t); - } else if (c=='&') { - *t = '\0'; - (*fn)(txt, value); - txt = t+1; - value = NULL; - } - t++; - } - if (t!=txt) - (*fn)(txt, value); - return 0; -} @@ -1,20 +0,0 @@ -#ifndef HTML_H -#define HTML_H - -extern int htmlfd; - -extern void html(const char *txt); -extern void htmlf(const char *format,...); -extern void html_txt(char *txt); -extern void html_ntxt(int len, char *txt); -extern void html_attr(char *txt); -extern void html_hidden(char *name, char *value); -extern void html_option(char *value, char *text, char *selected_value); -extern void html_link_open(char *url, char *title, char *class); -extern void html_link_close(void); -extern void html_fileperm(unsigned short mode); -extern int html_include(const char *filename); - -extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value)); - -#endif /* HTML_H */ @@ -8,6 +8,130 @@ #include "cgit.h" +int next_char(FILE *f) +{ + int c = fgetc(f); + if (c=='\r') { + c = fgetc(f); + if (c!='\n') { + ungetc(c, f); + c = '\r'; + } + } + return c; +} + +void skip_line(FILE *f) +{ + int c; + + while((c=next_char(f)) && c!='\n' && c!=EOF) + ; +} + +int read_config_line(FILE *f, char *line, const char **value, int bufsize) +{ + int i = 0, isname = 0; + + *value = NULL; + while(i<bufsize-1) { + int c = next_char(f); + if (!isname && (c=='#' || c==';')) { + skip_line(f); + continue; + } + if (!isname && isspace(c)) + continue; + + if (c=='=' && !*value) { + line[i] = 0; + *value = &line[i+1]; + } else if (c=='\n' && !isname) { + i = 0; + continue; + } else if (c=='\n' || c==EOF) { + line[i] = 0; + break; + } else { + line[i]=c; + } + isname = 1; + i++; + } + line[i+1] = 0; + return i; +} + +int cgit_read_config(const char *filename, configfn fn) +{ + static int nesting; + int len; + char line[256]; + const char *value; + FILE *f; + + /* cancel deeply nested include-commands */ + if (nesting > 8) + return -1; + if (!(f = fopen(filename, "r"))) + return -1; + nesting++; + while((len = read_config_line(f, line, &value, sizeof(line))) > 0) + (*fn)(line, value); + nesting--; + fclose(f); + return 0; +} + +char *convert_query_hexchar(char *txt) +{ + int d1, d2; + if (strlen(txt) < 3) { + *txt = '\0'; + return txt-1; + } + d1 = hextoint(*(txt+1)); + d2 = hextoint(*(txt+2)); + if (d1<0 || d2<0) { + strcpy(txt, txt+3); + return txt-1; + } else { + *txt = d1 * 16 + d2; + strcpy(txt+1, txt+3); + return txt; + } +} + +int cgit_parse_query(char *txt, configfn fn) +{ + char *t, *value = NULL, c; + + if (!txt) + return 0; + + t = txt = xstrdup(txt); + + while((c=*t) != '\0') { + if (c=='=') { + *t = '\0'; + value = t+1; + } else if (c=='+') { + *t = ' '; + } else if (c=='%') { + t = convert_query_hexchar(t); + } else if (c=='&') { + *t = '\0'; + (*fn)(txt, value); + txt = t+1; + value = NULL; + } + t++; + } + if (t!=txt) + (*fn)(txt, value); + return 0; +} + /* * url syntax: [repo ['/' cmd [ '/' path]]] * repo: any valid repo url, may contain '/' @@ -19,35 +143,35 @@ void cgit_parse_url(const char *url) { char *cmd, *p; - ctx.repo = NULL; + cgit_repo = NULL; if (!url || url[0] == '\0') return; - ctx.repo = cgit_get_repoinfo(url); - if (ctx.repo) { - ctx.qry.repo = ctx.repo->url; + cgit_repo = cgit_get_repoinfo(url); + if (cgit_repo) { + cgit_query_repo = cgit_repo->url; return; } cmd = strchr(url, '/'); - while (!ctx.repo && cmd) { + while (!cgit_repo && cmd) { cmd[0] = '\0'; - ctx.repo = cgit_get_repoinfo(url); - if (ctx.repo == NULL) { + cgit_repo = cgit_get_repoinfo(url); + if (cgit_repo == NULL) { cmd[0] = '/'; cmd = strchr(cmd + 1, '/'); continue; } - ctx.qry.repo = ctx.repo->url; + cgit_query_repo = cgit_repo->url; p = strchr(cmd + 1, '/'); if (p) { p[0] = '\0'; if (p[1]) - ctx.qry.path = trim_end(p + 1, '/'); + cgit_query_path = trim_end(p + 1, '/'); } - if (cmd[1]) - ctx.qry.page = xstrdup(cmd + 1); + cgit_cmd = cgit_get_cmd_index(cmd + 1); + cgit_query_page = xstrdup(cmd + 1); return; } } @@ -8,10 +8,77 @@ #include "cgit.h" -struct cgit_repolist cgit_repolist; -struct cgit_context ctx; +struct repolist cgit_repolist; +struct repoinfo *cgit_repo; int cgit_cmd; +const char *cgit_version = CGIT_VERSION; + +char *cgit_root_title = "Git repository browser"; +char *cgit_css = "/cgit.css"; +char *cgit_logo = "/git-logo.png"; +char *cgit_index_header = NULL; +char *cgit_index_info = NULL; +char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; +char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; +char *cgit_agefile = "info/web/last-modified"; +char *cgit_virtual_root = NULL; +char *cgit_script_name = CGIT_SCRIPT_NAME; +char *cgit_cache_root = CGIT_CACHE_ROOT; +char *cgit_repo_group = NULL; +char *cgit_robots = "index, nofollow"; +char *cgit_clone_prefix = NULL; + +int cgit_nocache = 0; +int cgit_snapshots = 0; +int cgit_enable_index_links = 0; +int cgit_enable_log_filecount = 0; +int cgit_enable_log_linecount = 0; +int cgit_max_lock_attempts = 5; +int cgit_cache_root_ttl = 5; +int cgit_cache_repo_ttl = 5; +int cgit_cache_dynamic_ttl = 5; +int cgit_cache_static_ttl = -1; +int cgit_cache_max_create_time = 5; +int cgit_summary_log = 0; +int cgit_summary_tags = 0; +int cgit_summary_branches = 0; +int cgit_renamelimit = -1; + +int cgit_max_msg_len = 60; +int cgit_max_repodesc_len = 60; +int cgit_max_commit_count = 50; + +int cgit_query_has_symref = 0; +int cgit_query_has_sha1 = 0; + +char *cgit_querystring = NULL; +char *cgit_query_repo = NULL; +char *cgit_query_page = NULL; +char *cgit_query_head = NULL; +char *cgit_query_search = NULL; +char *cgit_query_grep = NULL; +char *cgit_query_sha1 = NULL; +char *cgit_query_sha2 = NULL; +char *cgit_query_path = NULL; +char *cgit_query_name = NULL; +int cgit_query_ofs = 0; + +int htmlfd = 0; + + +int cgit_get_cmd_index(const char *cmd) +{ + static char *cmds[] = {"log", "commit", "diff", "tree", "blob", + "snapshot", "tag", "refs", "patch", NULL}; + int i; + + for(i = 0; cmds[i]; i++) + if (!strcmp(cmd, cmds[i])) + return i + 1; + return 0; +} + int chk_zero(int result, char *msg) { if (result != 0) @@ -33,9 +100,9 @@ int chk_non_negative(int result, char *msg) return result; } -struct cgit_repo *cgit_add_repo(const char *url) +struct repoinfo *add_repo(const char *url) { - struct cgit_repo *ret; + struct repoinfo *ret; if (++cgit_repolist.count > cgit_repolist.length) { if (cgit_repolist.length == 0) @@ -44,7 +111,7 @@ struct cgit_repo *cgit_add_repo(const char *url) cgit_repolist.length *= 2; cgit_repolist.repos = xrealloc(cgit_repolist.repos, cgit_repolist.length * - sizeof(struct cgit_repo)); + sizeof(struct repoinfo)); } ret = &cgit_repolist.repos[cgit_repolist.count-1]; @@ -53,20 +120,20 @@ struct cgit_repo *cgit_add_repo(const char *url) ret->path = NULL; ret->desc = "[no description]"; ret->owner = NULL; - ret->group = ctx.cfg.repo_group; + ret->group = cgit_repo_group; ret->defbranch = "master"; - ret->snapshots = ctx.cfg.snapshots; - ret->enable_log_filecount = ctx.cfg.enable_log_filecount; - ret->enable_log_linecount = ctx.cfg.enable_log_linecount; - ret->module_link = ctx.cfg.module_link; + ret->snapshots = cgit_snapshots; + ret->enable_log_filecount = cgit_enable_log_filecount; + ret->enable_log_linecount = cgit_enable_log_linecount; + ret->module_link = cgit_module_link; ret->readme = NULL; return ret; } -struct cgit_repo *cgit_get_repoinfo(const char *url) +struct repoinfo *cgit_get_repoinfo(const char *url) { int i; - struct cgit_repo *repo; + struct repoinfo *repo; for (i=0; i<cgit_repolist.count; i++) { repo = &cgit_repolist.repos[i]; @@ -76,6 +143,131 @@ struct cgit_repo *cgit_get_repoinfo(const char *url) return NULL; } +void cgit_global_config_cb(const char *name, const char *value) +{ + if (!strcmp(name, "root-title")) + cgit_root_title = xstrdup(value); + else if (!strcmp(name, "css")) + cgit_css = xstrdup(value); + else if (!strcmp(name, "logo")) + cgit_logo = xstrdup(value); + else if (!strcmp(name, "index-header")) + cgit_index_header = xstrdup(value); + else if (!strcmp(name, "index-info")) + cgit_index_info = xstrdup(value); + else if (!strcmp(name, "logo-link")) + cgit_logo_link = xstrdup(value); + else if (!strcmp(name, "module-link")) + cgit_module_link = xstrdup(value); + else if (!strcmp(name, "virtual-root")) { + cgit_virtual_root = trim_end(value, '/'); + if (!cgit_virtual_root && (!strcmp(value, "/"))) + cgit_virtual_root = ""; + } else if (!strcmp(name, "nocache")) + cgit_nocache = atoi(value); + else if (!strcmp(name, "snapshots")) + cgit_snapshots = cgit_parse_snapshots_mask(value); + else if (!strcmp(name, "enable-index-links")) + cgit_enable_index_links = atoi(value); + else if (!strcmp(name, "enable-log-filecount")) + cgit_enable_log_filecount = atoi(value); + else if (!strcmp(name, "enable-log-linecount")) + cgit_enable_log_linecount = atoi(value); + else if (!strcmp(name, "cache-root")) + cgit_cache_root = xstrdup(value); + else if (!strcmp(name, "cache-root-ttl")) + cgit_cache_root_ttl = atoi(value); + else if (!strcmp(name, "cache-repo-ttl")) + cgit_cache_repo_ttl = atoi(value); + else if (!strcmp(name, "cache-static-ttl")) + cgit_cache_static_ttl = atoi(value); + else if (!strcmp(name, "cache-dynamic-ttl")) + cgit_cache_dynamic_ttl = atoi(value); + else if (!strcmp(name, "max-message-length")) + cgit_max_msg_len = atoi(value); + else if (!strcmp(name, "max-repodesc-length")) + cgit_max_repodesc_len = atoi(value); + else if (!strcmp(name, "max-commit-count")) + cgit_max_commit_count = atoi(value); + else if (!strcmp(name, "summary-log")) + cgit_summary_log = atoi(value); + else if (!strcmp(name, "summary-branches")) + cgit_summary_branches = atoi(value); + else if (!strcmp(name, "summary-tags")) + cgit_summary_tags = atoi(value); + else if (!strcmp(name, "agefile")) + cgit_agefile = xstrdup(value); + else if (!strcmp(name, "renamelimit")) + cgit_renamelimit = atoi(value); + else if (!strcmp(name, "robots")) + cgit_robots = xstrdup(value); + else if (!strcmp(name, "clone-prefix")) + cgit_clone_prefix = xstrdup(value); + else if (!strcmp(name, "repo.group")) + cgit_repo_group = xstrdup(value); + else if (!strcmp(name, "repo.url")) + cgit_repo = add_repo(value); + else if (!strcmp(name, "repo.name")) + cgit_repo->name = xstrdup(value); + else if (cgit_repo && !strcmp(name, "repo.path")) + cgit_repo->path = trim_end(value, '/'); + else if (cgit_repo && !strcmp(name, "repo.clone-url")) + cgit_repo->clone_url = xstrdup(value); + else if (cgit_repo && !strcmp(name, "repo.desc")) + cgit_repo->desc = xstrdup(value); + else if (cgit_repo && !strcmp(name, "repo.owner")) + cgit_repo->owner = xstrdup(value); + else if (cgit_repo && !strcmp(name, "repo.defbranch")) + cgit_repo->defbranch = xstrdup(value); + else if (cgit_repo && !strcmp(name, "repo.snapshots")) + cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ + else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) + cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); + else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) + cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); + else if (cgit_repo && !strcmp(name, "repo.module-link")) + cgit_repo->module_link= xstrdup(value); + else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { + if (*value == '/') + cgit_repo->readme = xstrdup(value); + else + cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); + } else if (!strcmp(name, "include")) + cgit_read_config(value, cgit_global_config_cb); +} + +void cgit_querystring_cb(const char *name, const char *value) +{ + if (!strcmp(name,"r")) { + cgit_query_repo = xstrdup(value); + cgit_repo = cgit_get_repoinfo(value); + } else if (!strcmp(name, "p")) { + cgit_query_page = xstrdup(value); + cgit_cmd = cgit_get_cmd_index(value); + } else if (!strcmp(name, "url")) { + cgit_parse_url(value); + } else if (!strcmp(name, "qt")) { + cgit_query_grep = xstrdup(value); + } else if (!strcmp(name, "q")) { + cgit_query_search = xstrdup(value); + } else if (!strcmp(name, "h")) { + cgit_query_head = xstrdup(value); + cgit_query_has_symref = 1; + } else if (!strcmp(name, "id")) { + cgit_query_sha1 = xstrdup(value); + cgit_query_has_sha1 = 1; + } else if (!strcmp(name, "id2")) { + cgit_query_sha2 = xstrdup(value); + cgit_query_has_sha1 = 1; + } else if (!strcmp(name, "ofs")) { + cgit_query_ofs = atoi(value); + } else if (!strcmp(name, "path")) { + cgit_query_path = trim_end(value, '/'); + } else if (!strcmp(name, "name")) { + cgit_query_name = xstrdup(value); + } +} + void *cgit_free_commitinfo(struct commitinfo *info) { free(info->author); @@ -89,6 +281,18 @@ void *cgit_free_commitinfo(struct commitinfo *info) return NULL; } +int hextoint(char c) +{ + if (c >= 'a' && c <= 'f') + return 10 + c - 'a'; + else if (c >= 'A' && c <= 'F') + return 10 + c - 'A'; + else if (c >= '0' && c <= '9') + return c - '0'; + else + return -1; +} + char *trim_end(const char *str, char c) { int len; @@ -287,7 +491,7 @@ void cgit_diff_tree(const unsigned char *old_sha1, diff_setup(&opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; - opt.rename_limit = ctx.cfg.renamelimit; + opt.rename_limit = cgit_renamelimit; DIFF_OPT_SET(&opt, RECURSIVE); opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; @@ -315,30 +519,3 @@ void cgit_diff_commit(struct commit *commit, filepair_fn fn) old_sha1 = commit->parents->item->object.sha1; cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); } - -int cgit_parse_snapshots_mask(const char *str) -{ - const struct cgit_snapshot_format *f; - static const char *delim = " \t,:/|;"; - int tl, sl, rv = 0; - - /* favor legacy setting */ - if(atoi(str)) - return 1; - for(;;) { - str += strspn(str,delim); - tl = strcspn(str,delim); - if (!tl) - break; - for (f = cgit_snapshot_formats; f->suffix; f++) { - sl = strlen(f->suffix); - if((tl == sl && !strncmp(f->suffix, str, tl)) || - (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { - rv |= f->bit; - break; - } - } - str += tl; - } - return rv; -} @@ -1,16 +1,6 @@ -/* ui-blob.c: show blob content - * - * Copyright (C) 2008 Lars Hjemli - * - * Licensed under GNU General Public License v2 - * (see COPYING for full license text) - */ - #include "cgit.h" -#include "html.h" -#include "ui-shared.h" -void cgit_print_blob(const char *hex, char *path) +void cgit_print_blob(struct cacheitem *item, const char *hex, char *path) { unsigned char sha1[20]; @@ -36,8 +26,6 @@ void cgit_print_blob(const char *hex, char *path) } buf[size] = '\0'; - ctx.page.mimetype = "text/plain"; - ctx.page.filename = path; - cgit_print_http_headers(&ctx); + cgit_print_snapshot_start("text/plain", path, item); write(htmlfd, buf, size); } diff --git a/ui-blob.h b/ui-blob.h deleted file mode 100644 index 5a920a8..0000000 --- a/ui-blob.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_BLOB_H -#define UI_BLOB_H - -extern void cgit_print_blob(const char *hex, char *path); - -#endif /* UI_BLOB_H */ diff --git a/ui-commit.c b/ui-commit.c index 8019e36..bd55a33 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -7,8 +7,6 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" static int files, slots; static int total_adds, total_rems, max_changes; @@ -64,20 +62,20 @@ void print_fileinfo(struct fileinfo *info) html("<tr>"); htmlf("<td class='mode'>"); if (is_null_sha1(info->new_sha1)) { - cgit_print_filemode(info->old_mode); + html_filemode(info->old_mode); } else { - cgit_print_filemode(info->new_mode); + html_filemode(info->new_mode); } if (info->old_mode != info->new_mode && !is_null_sha1(info->old_sha1) && !is_null_sha1(info->new_sha1)) { html("<span class='modechange'>["); - cgit_print_filemode(info->old_mode); + html_filemode(info->old_mode); html("]</span>"); } htmlf("</td><td class='%s'>", class); - cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, curr_rev, + cgit_diff_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev, NULL, info->new_path); if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) htmlf(" (%s from %s)", @@ -145,7 +143,7 @@ void cgit_print_commit(char *hex) int i; if (!hex) - hex = ctx.qry.head; + hex = cgit_query_head; curr_rev = hex; if (get_sha1(hex, sha1)) { @@ -177,7 +175,7 @@ void cgit_print_commit(char *hex) html("<tr><th>tree</th><td colspan='2' class='sha1'>"); tmp = xstrdup(hex); cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, - ctx.qry.head, tmp, NULL); + cgit_query_head, tmp, NULL); html("</td></tr>\n"); for (p = commit->parents; p ; p = p->next) { parent = lookup_commit_reference(p->item->object.sha1); @@ -190,16 +188,16 @@ void cgit_print_commit(char *hex) html("<tr><th>parent</th>" "<td colspan='2' class='sha1'>"); cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, - ctx.qry.head, sha1_to_hex(p->item->object.sha1)); + cgit_query_head, sha1_to_hex(p->item->object.sha1)); html(" ("); - cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, + cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex, sha1_to_hex(p->item->object.sha1), NULL); html(")</td></tr>"); } - if (ctx.repo->snapshots) { + if (cgit_repo->snapshots) { html("<tr><th>download</th><td colspan='2' class='sha1'>"); - cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, - hex, ctx.repo->snapshots); + cgit_print_snapshot_links(cgit_query_repo, cgit_query_head, + hex, cgit_repo->snapshots); html("</td></tr>"); } html("</table>\n"); @@ -220,7 +218,7 @@ void cgit_print_commit(char *hex) html("<div class='diffstat-summary'>"); htmlf("%d files changed, %d insertions, %d deletions (", files, total_adds, total_rems); - cgit_diff_link("show diff", NULL, NULL, ctx.qry.head, hex, + cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex, NULL, NULL); html(")</div>"); } diff --git a/ui-commit.h b/ui-commit.h deleted file mode 100644 index 40bcb31..0000000 --- a/ui-commit.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_COMMIT_H -#define UI_COMMIT_H - -extern void cgit_print_commit(char *hex); - -#endif /* UI_COMMIT_H */ @@ -7,8 +7,7 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" + unsigned char old_rev_sha1[20]; unsigned char new_rev_sha1[20]; @@ -72,13 +71,13 @@ static void header(unsigned char *sha1, char *path1, int mode1, } html("<br/>--- a/"); if (mode1 != 0) - cgit_tree_link(path1, NULL, NULL, ctx.qry.head, + cgit_tree_link(path1, NULL, NULL, cgit_query_head, sha1_to_hex(old_rev_sha1), path1); else html_txt(path1); html("<br/>+++ b/"); if (mode2 != 0) - cgit_tree_link(path2, NULL, NULL, ctx.qry.head, + cgit_tree_link(path2, NULL, NULL, cgit_query_head, sha1_to_hex(new_rev_sha1), path2); else html_txt(path2); @@ -108,7 +107,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi struct commit *commit, *commit2; if (!new_rev) - new_rev = ctx.qry.head; + new_rev = cgit_query_head; get_sha1(new_rev, new_rev_sha1); type = sha1_object_info(new_rev_sha1, &size); if (type == OBJ_BAD) { diff --git a/ui-diff.h b/ui-diff.h deleted file mode 100644 index 2307322..0000000 --- a/ui-diff.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef UI_DIFF_H -#define UI_DIFF_H - -extern void cgit_print_diff(const char *new_hex, const char *old_hex, - const char *prefix); - -#endif /* UI_DIFF_H */ @@ -7,8 +7,6 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" int files, add_lines, rem_lines; @@ -27,7 +25,7 @@ void count_lines(char *line, int size) void inspect_files(struct diff_filepair *pair) { files++; - if (ctx.repo->enable_log_linecount) + if (cgit_repo->enable_log_linecount) cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines); } @@ -39,16 +37,16 @@ void print_commit(struct commit *commit) html("<tr><td>"); cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); html("</td><td>"); - cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, + cgit_commit_link(info->subject, NULL, NULL, cgit_query_head, sha1_to_hex(commit->object.sha1)); - if (ctx.repo->enable_log_filecount) { + if (cgit_repo->enable_log_filecount) { files = 0; add_lines = 0; rem_lines = 0; cgit_diff_commit(commit, inspect_files); html("</td><td class='right'>"); htmlf("%d", files); - if (ctx.repo->enable_log_linecount) { + if (cgit_repo->enable_log_linecount) { html("</td><td class='right'>"); htmlf("-%d/+%d", rem_lines, add_lines); } @@ -69,7 +67,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern int i; if (!tip) - argv[1] = ctx.qry.head; + argv[1] = cgit_query_head; if (grep && pattern && (!strcmp(grep, "grep") || !strcmp(grep, "author") || @@ -96,9 +94,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern html("<tr class='nohover'><th class='left'>Age</th>" "<th class='left'>Message</th>"); - if (ctx.repo->enable_log_filecount) { + if (cgit_repo->enable_log_filecount) { html("<th class='right'>Files</th>"); - if (ctx.repo->enable_log_linecount) + if (cgit_repo->enable_log_linecount) html("<th class='right'>Lines</th>"); } html("<th class='left'>Author</th></tr>\n"); @@ -125,17 +123,17 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern if (pager) { html("<div class='pager'>"); if (ofs > 0) { - cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, - ctx.qry.sha1, ctx.qry.path, - ofs - cnt, ctx.qry.grep, - ctx.qry.search); + cgit_log_link("[prev]", NULL, NULL, cgit_query_head, + cgit_query_sha1, cgit_query_path, + ofs - cnt, cgit_query_grep, + cgit_query_search); html(" "); } if ((commit = get_revision(&rev)) != NULL) { - cgit_log_link("[next]", NULL, NULL, ctx.qry.head, - ctx.qry.sha1, ctx.qry.path, - ofs + cnt, ctx.qry.grep, - ctx.qry.search); + cgit_log_link("[next]", NULL, NULL, cgit_query_head, + cgit_query_sha1, cgit_query_path, + ofs + cnt, cgit_query_grep, + cgit_query_search); } html("</div>"); } diff --git a/ui-log.h b/ui-log.h deleted file mode 100644 index 877e40e..0000000 --- a/ui-log.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef UI_LOG_H -#define UI_LOG_H - -extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, - char *pattern, char *path, int pager); - -#endif /* UI_LOG_H */ @@ -7,8 +7,6 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" static void print_line(char *line, int len) { @@ -70,7 +68,7 @@ static void filepair_cb(struct diff_filepair *pair) html("Error running diff"); } -void cgit_print_patch(char *hex) +void cgit_print_patch(char *hex, struct cacheitem *item) { struct commit *commit; struct commitinfo *info; @@ -78,7 +76,7 @@ void cgit_print_patch(char *hex) char *patchname; if (!hex) - hex = ctx.qry.head; + hex = cgit_query_head; if (get_sha1(hex, sha1)) { cgit_print_error(fmt("Bad object id: %s", hex)); @@ -97,9 +95,7 @@ void cgit_print_patch(char *hex) hashclr(old_sha1); patchname = fmt("%s.patch", sha1_to_hex(sha1)); - ctx.page.mimetype = "text/plain"; - ctx.page.filename = patchname; - cgit_print_http_headers(&ctx); + cgit_print_snapshot_start("text/plain", patchname, item); htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); htmlf("From: %s%s\n", info->author, info->author_email); html("Date: "); diff --git a/ui-patch.h b/ui-patch.h deleted file mode 100644 index 9f68212..0000000 --- a/ui-patch.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_PATCH_H -#define UI_PATCH_H - -extern void cgit_print_patch(char *hex); - -#endif /* UI_PATCH_H */ @@ -7,189 +7,18 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" -static int header; -static int cmp_age(int age1, int age2) -{ - if (age1 != 0 && age2 != 0) - return age2 - age1; - - if (age1 == 0 && age2 == 0) - return 0; - - if (age1 == 0) - return +1; - - return -1; -} - -static int cmp_ref_name(const void *a, const void *b) -{ - struct refinfo *r1 = *(struct refinfo **)a; - struct refinfo *r2 = *(struct refinfo **)b; - - return strcmp(r1->refname, r2->refname); -} - -static int cmp_branch_age(const void *a, const void *b) -{ - struct refinfo *r1 = *(struct refinfo **)a; - struct refinfo *r2 = *(struct refinfo **)b; - - return cmp_age(r1->commit->committer_date, r2->commit->committer_date); -} - -static int cmp_tag_age(const void *a, const void *b) -{ - struct refinfo *r1 = *(struct refinfo **)a; - struct refinfo *r2 = *(struct refinfo **)b; - - return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date); -} - -static int print_branch(struct refinfo *ref) -{ - struct commitinfo *info = ref->commit; - char *name = (char *)ref->refname; - - if (!info) - return 1; - html("<tr><td>"); - cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL); - html("</td><td>"); - - if (ref->object->type == OBJ_COMMIT) { - cgit_print_age(info->commit->date, -1, NULL); - html("</td><td>"); - html_txt(info->author); - html("</td><td>"); - cgit_commit_link(info->subject, NULL, NULL, name, NULL); - } else { - html("</td><td></td><td>"); - cgit_object_link(ref->object); - } - html("</td></tr>\n"); - return 0; -} - -static void print_tag_header() -{ - html("<tr class='nohover'><th class='left'>Tag</th>" - "<th class='left'>Age</th>" - "<th class='left'>Author</th>" - "<th class='left'>Reference</th></tr>\n"); - header = 1; -} - -static int print_tag(struct refinfo *ref) -{ - struct tag *tag; - struct taginfo *info; - char *url, *name = (char *)ref->refname; - - if (ref->object->type == OBJ_TAG) { - tag = (struct tag *)ref->object; - info = ref->tag; - if (!tag || !info) - return 1; - html("<tr><td>"); - url = cgit_pageurl(ctx.qry.repo, "tag", - fmt("id=%s", name)); - html_link_open(url, NULL, NULL); - html_txt(name); - html_link_close(); - html("</td><td>"); - if (info->tagger_date > 0) - cgit_print_age(info->tagger_date, -1, NULL); - html("</td><td>"); - if (info->tagger) - html(info->tagger); - html("</td><td>"); - cgit_object_link(tag->tagged); - html("</td></tr>\n"); - } else { - if (!header) - print_tag_header(); - html("<tr><td>"); - html_txt(name); - html("</td><td colspan='2'/><td>"); - cgit_object_link(ref->object); - html("</td></tr>\n"); - } - return 0; -} - -static void print_refs_link(char *path) -{ - html("<tr class='nohover'><td colspan='4'>"); - cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path); - html("</td></tr>"); -} - -void cgit_print_branches(int maxcount) -{ - struct reflist list; - int i; - html("<tr class='nohover'><th class='left'>Branch</th>" - "<th class='left'>Idle</th>" - "<th class='left'>Author</th>" - "<th class='left'>Head commit</th></tr>\n"); - - list.refs = NULL; - list.alloc = list.count = 0; - for_each_branch_ref(cgit_refs_cb, &list); - - if (maxcount == 0 || maxcount > list.count) - maxcount = list.count; - - if (maxcount < list.count) { - qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); - qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); - } - - for(i=0; i<maxcount; i++) - print_branch(list.refs[i]); - - if (maxcount < list.count) - print_refs_link("heads"); -} - -void cgit_print_tags(int maxcount) -{ - struct reflist list; - int i; - - header = 0; - list.refs = NULL; - list.alloc = list.count = 0; - for_each_tag_ref(cgit_refs_cb, &list); - if (list.count == 0) - return; - qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age); - if (!maxcount) - maxcount = list.count; - else if (maxcount > list.count) - maxcount = list.count; - print_tag_header(); - for(i=0; i<maxcount; i++) - print_tag(list.refs[i]); - - if (maxcount < list.count) - print_refs_link("tags"); -} void cgit_print_refs() { html("<table class='list nowrap'>"); - if (ctx.qry.path && !strncmp(ctx.qry.path, "heads", 5)) + if (cgit_query_path && !strncmp(cgit_query_path, "heads", 5)) cgit_print_branches(0); - else if (ctx.qry.path && !strncmp(ctx.qry.path, "tags", 4)) + else if (cgit_query_path && !strncmp(cgit_query_path, "tags", 4)) cgit_print_tags(0); else { cgit_print_branches(0); diff --git a/ui-refs.h b/ui-refs.h deleted file mode 100644 index b35c04a..0000000 --- a/ui-refs.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef UI_REFS_H -#define UI_REFS_H - -extern void cgit_print_branches(int maxcount); -extern void cgit_print_tags(int maxcount); -extern void cgit_print_refs(); - -#endif /* UI_REFS_H */ diff --git a/ui-repolist.c b/ui-repolist.c index eeeaf3d..3e97ca9 100644 --- a/ui-repolist.c +++ b/ui-repolist.c @@ -6,11 +6,9 @@ * (see COPYING for full license text) */ +#include "cgit.h" #include <time.h> -#include "cgit.h" -#include "html.h" -#include "ui-shared.h" time_t read_agefile(char *path) { @@ -27,12 +25,12 @@ time_t read_agefile(char *path) return 0; } -static void print_modtime(struct cgit_repo *repo) +static void print_modtime(struct repoinfo *repo) { char *path; struct stat s; - path = fmt("%s/%s", repo->path, ctx.cfg.agefile); + path = fmt("%s/%s", repo->path, cgit_agefile); if (stat(path, &s) == 0) { cgit_print_age(read_agefile(path), -1, NULL); return; @@ -44,24 +42,22 @@ static void print_modtime(struct cgit_repo *repo) cgit_print_age(s.st_mtime, -1, NULL); } -void cgit_print_repolist() +void cgit_print_repolist(struct cacheitem *item) { int i, columns = 4; char *last_group = NULL; - if (ctx.cfg.enable_index_links) + if (cgit_enable_index_links) columns++; - ctx.page.title = ctx.cfg.root_title; - cgit_print_http_headers(&ctx); - cgit_print_docstart(&ctx); - cgit_print_pageheader(&ctx); + cgit_print_docstart(cgit_root_title, item); + cgit_print_pageheader(cgit_root_title, 0); html("<table summary='repository list' class='list nowrap'>"); - if (ctx.cfg.index_header) { + if (cgit_index_header) { htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>", columns); - html_include(ctx.cfg.index_header); + html_include(cgit_index_header); html("</td></tr>"); } html("<tr class='nohover'>" @@ -69,37 +65,37 @@ void cgit_print_repolist() "<th class='left'>Description</th>" "<th class='left'>Owner</th>" "<th class='left'>Idle</th>"); - if (ctx.cfg.enable_index_links) + if (cgit_enable_index_links) html("<th>Links</th>"); html("</tr>\n"); for (i=0; i<cgit_repolist.count; i++) { - ctx.repo = &cgit_repolist.repos[i]; - if ((last_group == NULL && ctx.repo->group != NULL) || - (last_group != NULL && ctx.repo->group == NULL) || - (last_group != NULL && ctx.repo->group != NULL && - strcmp(ctx.repo->group, last_group))) { + cgit_repo = &cgit_repolist.repos[i]; + if ((last_group == NULL && cgit_repo->group != NULL) || + (last_group != NULL && cgit_repo->group == NULL) || + (last_group != NULL && cgit_repo->group != NULL && + strcmp(cgit_repo->group, last_group))) { htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", columns); - html_txt(ctx.repo->group); + html_txt(cgit_repo->group); html("</td></tr>"); - last_group = ctx.repo->group; + last_group = cgit_repo->group; } htmlf("<tr><td class='%s'>", - ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); - html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); - html_txt(ctx.repo->name); + cgit_repo->group ? "sublevel-repo" : "toplevel-repo"); + html_link_open(cgit_repourl(cgit_repo->url), NULL, NULL); + html_txt(cgit_repo->name); html_link_close(); html("</td><td>"); - html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); + html_ntxt(cgit_max_repodesc_len, cgit_repo->desc); html("</td><td>"); - html_txt(ctx.repo->owner); + html_txt(cgit_repo->owner); html("</td><td>"); - print_modtime(ctx.repo); + print_modtime(cgit_repo); html("</td>"); - if (ctx.cfg.enable_index_links) { + if (cgit_enable_index_links) { html("<td>"); - html_link_open(cgit_repourl(ctx.repo->url), + html_link_open(cgit_repourl(cgit_repo->url), NULL, "button"); html("summary</a>"); cgit_log_link("log", NULL, "button", NULL, NULL, NULL, diff --git a/ui-repolist.h b/ui-repolist.h deleted file mode 100644 index c23e5d2..0000000 --- a/ui-repolist.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_REPOLIST_H -#define UI_REPOLIST_H - -extern void cgit_print_repolist(); - -#endif /* UI_REPOLIST_H */ diff --git a/ui-shared.c b/ui-shared.c index aa65988..60aa2e3 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -7,7 +7,6 @@ */ #include "cgit.h" -#include "html.h" const char cgit_doctype[] = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" @@ -26,6 +25,14 @@ static char *http_date(time_t t) tm->tm_hour, tm->tm_min, tm->tm_sec); } +static long ttl_seconds(long ttl) +{ + if (ttl<0) + return 60 * 60 * 24 * 365; + else + return ttl * 60; +} + void cgit_print_error(char *msg) { html("<div class='error'>"); @@ -35,16 +42,16 @@ void cgit_print_error(char *msg) char *cgit_rooturl() { - if (ctx.cfg.virtual_root) - return fmt("%s/", ctx.cfg.virtual_root); + if (cgit_virtual_root) + return fmt("%s/", cgit_virtual_root); else - return ctx.cfg.script_name; + return cgit_script_name; } char *cgit_repourl(const char *reponame) { - if (ctx.cfg.virtual_root) { - return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); + if (cgit_virtual_root) { + return fmt("%s/%s/", cgit_virtual_root, reponame); } else { return fmt("?r=%s", reponame); } @@ -56,8 +63,8 @@ char *cgit_fileurl(const char *reponame, const char *pagename, char *tmp; char *delim; - if (ctx.cfg.virtual_root) { - tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, + if (cgit_virtual_root) { + tmp = fmt("%s/%s/%s/%s", cgit_virtual_root, reponame, pagename, (filename ? filename:"")); delim = "?"; } else { @@ -103,14 +110,14 @@ const char *cgit_repobasename(const char *reponame) char *cgit_currurl() { - if (!ctx.cfg.virtual_root) - return ctx.cfg.script_name; - else if (ctx.qry.page) - return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); - else if (ctx.qry.repo) - return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); + if (!cgit_virtual_root) + return cgit_script_name; + else if (cgit_query_page) + return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); + else if (cgit_query_repo) + return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); else - return fmt("%s/", ctx.cfg.virtual_root); + return fmt("%s/", cgit_virtual_root); } static char *repolink(char *title, char *class, char *page, char *head, @@ -130,12 +137,12 @@ static char *repolink(char *title, char *class, char *page, char *head, html("'"); } html(" href='"); - if (ctx.cfg.virtual_root) { - html_attr(ctx.cfg.virtual_root); - if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') + if (cgit_virtual_root) { + html_attr(cgit_virtual_root); + if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') html("/"); - html_attr(ctx.repo->url); - if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') + html_attr(cgit_repo->url); + if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') html("/"); if (page) { html(page); @@ -144,10 +151,10 @@ static char *repolink(char *title, char *class, char *page, char *head, html_attr(path); } } else { - html(ctx.cfg.script_name); + html(cgit_script_name); html("?url="); - html_attr(ctx.repo->url); - if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') + html_attr(cgit_repo->url); + if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') html("/"); if (page) { html(page); @@ -157,7 +164,7 @@ static char *repolink(char *title, char *class, char *page, char *head, } delim = "&"; } - if (head && strcmp(head, ctx.repo->defbranch)) { + if (head && strcmp(head, cgit_repo->defbranch)) { html(delim); html("h="); html_attr(head); @@ -172,7 +179,7 @@ static void reporevlink(char *page, char *name, char *title, char *class, char *delim; delim = repolink(title, class, page, head, path); - if (rev && strcmp(rev, ctx.qry.head)) { + if (rev && strcmp(rev, cgit_query_head)) { html(delim); html("id="); html_attr(rev); @@ -194,7 +201,7 @@ void cgit_log_link(char *name, char *title, char *class, char *head, char *delim; delim = repolink(title, class, "log", head, path); - if (rev && strcmp(rev, ctx.qry.head)) { + if (rev && strcmp(rev, cgit_query_head)) { html(delim); html("id="); html_attr(rev); @@ -222,11 +229,11 @@ void cgit_log_link(char *name, char *title, char *class, char *head, void cgit_commit_link(char *name, char *title, char *class, char *head, char *rev) { - 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] = '.'; + if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { + name[cgit_max_msg_len] = '\0'; + name[cgit_max_msg_len - 1] = '.'; + name[cgit_max_msg_len - 2] = '.'; + name[cgit_max_msg_len - 3] = '.'; } reporevlink("commit", name, title, class, head, rev, NULL); } @@ -249,7 +256,7 @@ void cgit_diff_link(char *name, char *title, char *class, char *head, char *delim; delim = repolink(title, class, "diff", head, path); - if (new_rev && strcmp(new_rev, ctx.qry.head)) { + if (new_rev && strcmp(new_rev, cgit_query_head)) { html(delim); html("id="); html_attr(new_rev); @@ -277,7 +284,7 @@ void cgit_object_link(struct object *obj) if (obj->type == OBJ_COMMIT) { cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, - ctx.qry.head, sha1_to_hex(obj->sha1)); + cgit_query_head, sha1_to_hex(obj->sha1)); return; } else if (obj->type == OBJ_TREE) { page = "tree"; @@ -290,7 +297,7 @@ void cgit_object_link(struct object *obj) arg = "id"; } - url = cgit_pageurl(ctx.qry.repo, page, + url = cgit_pageurl(cgit_query_repo, page, fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); html_link_open(url, NULL, NULL); htmlf("%s %s", typename(obj->type), @@ -353,34 +360,24 @@ void cgit_print_age(time_t t, time_t max_relative, char *format) secs * 1.0 / TM_YEAR); } -void cgit_print_http_headers(struct cgit_context *ctx) +void cgit_print_docstart(char *title, struct cacheitem *item) { - if (ctx->page.mimetype && ctx->page.charset) - htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, - ctx->page.charset); - else if (ctx->page.mimetype) - htmlf("Content-Type: %s\n", ctx->page.mimetype); - if (ctx->page.filename) - htmlf("Content-Disposition: inline; filename=\"%s\"\n", - ctx->page.filename); - htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); - htmlf("Expires: %s\n", http_date(ctx->page.expires)); + html("Content-Type: text/html; charset=" PAGE_ENCODING "\n"); + htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); + htmlf("Expires: %s\n", http_date(item->st.st_mtime + + ttl_seconds(item->ttl))); html("\n"); -} - -void cgit_print_docstart(struct cgit_context *ctx) -{ html(cgit_doctype); html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); html("<head>\n"); html("<title>"); - html_txt(ctx->page.title); + html_txt(title); html("</title>\n"); 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); + if (cgit_robots && *cgit_robots) + htmlf("<meta name='robots' content='%s'/>\n", cgit_robots); html("<link rel='stylesheet' type='text/css' href='"); - html_attr(ctx->cfg.css); + html_attr(cgit_css); html("'/>\n"); html("</head>\n"); html("<body>\n"); @@ -395,7 +392,7 @@ int print_branch_option(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { char *name = (char *)refname; - html_option(name, name, ctx.qry.head); + html_option(name, name, cgit_query_head); return 0; } @@ -429,7 +426,7 @@ int print_archive_ref(const char *refname, const unsigned char *sha1, html("<h1>download</h1>\n"); *header = 1; } - url = cgit_pageurl(ctx.qry.repo, "blob", + url = cgit_pageurl(cgit_query_repo, "blob", fmt("id=%s&path=%s", sha1_to_hex(fileid), buf)); html_link_open(url, NULL, "menu"); @@ -442,30 +439,30 @@ void add_hidden_formfields(int incl_head, int incl_search, char *page) { char *url; - if (!ctx.cfg.virtual_root) { - url = fmt("%s/%s", ctx.qry.repo, page); - if (ctx.qry.path) - url = fmt("%s/%s", url, ctx.qry.path); + if (!cgit_virtual_root) { + url = fmt("%s/%s", cgit_query_repo, page); + if (cgit_query_path) + url = fmt("%s/%s", url, cgit_query_path); html_hidden("url", url); } - if (incl_head && strcmp(ctx.qry.head, ctx.repo->defbranch)) - html_hidden("h", ctx.qry.head); + if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch)) + html_hidden("h", cgit_query_head); - if (ctx.qry.sha1) - html_hidden("id", ctx.qry.sha1); - if (ctx.qry.sha2) - html_hidden("id2", ctx.qry.sha2); + if (cgit_query_sha1) + html_hidden("id", cgit_query_sha1); + if (cgit_query_sha2) + html_hidden("id2", cgit_query_sha2); if (incl_search) { - if (ctx.qry.grep) - html_hidden("qt", ctx.qry.grep); - if (ctx.qry.search) - html_hidden("q", ctx.qry.search); + if (cgit_query_grep) + html_hidden("qt", cgit_query_grep); + if (cgit_query_search) + html_hidden("q", cgit_query_search); } } -void cgit_print_pageheader(struct cgit_context *ctx) +void cgit_print_pageheader(char *title, int show_search) { static const char *default_info = "This is cgit, a fast webinterface for git repositories"; int header = 0; @@ -477,40 +474,40 @@ void cgit_print_pageheader(struct cgit_context *ctx) html("<tr><td class='sidebar'>\n<a href='"); html_attr(cgit_rooturl()); htmlf("'><img src='%s' alt='cgit'/></a>\n", - ctx->cfg.logo); + cgit_logo); html("</td></tr>\n<tr><td class='sidebar'>\n"); - if (ctx->repo) { + if (cgit_query_repo) { html("<h1 class='first'>"); - html_txt(strrpart(ctx->repo->name, 20)); + html_txt(strrpart(cgit_repo->name, 20)); html("</h1>\n"); - html_txt(ctx->repo->desc); - if (ctx->repo->owner) { + html_txt(cgit_repo->desc); + if (cgit_repo->owner) { html("<h1>owner</h1>\n"); - html_txt(ctx->repo->owner); + html_txt(cgit_repo->owner); } html("<h1>navigate</h1>\n"); - reporevlink(NULL, "summary", NULL, "menu", ctx->qry.head, + reporevlink(NULL, "summary", NULL, "menu", cgit_query_head, NULL, NULL); - cgit_log_link("log", NULL, "menu", ctx->qry.head, NULL, NULL, + cgit_log_link("log", NULL, "menu", cgit_query_head, NULL, NULL, 0, NULL, NULL); - cgit_tree_link("tree", NULL, "menu", ctx->qry.head, - ctx->qry.sha1, NULL); - cgit_commit_link("commit", NULL, "menu", ctx->qry.head, - ctx->qry.sha1); - cgit_diff_link("diff", NULL, "menu", ctx->qry.head, - ctx->qry.sha1, ctx->qry.sha2, NULL); - cgit_patch_link("patch", NULL, "menu", ctx->qry.head, - ctx->qry.sha1); + cgit_tree_link("tree", NULL, "menu", cgit_query_head, + cgit_query_sha1, NULL); + cgit_commit_link("commit", NULL, "menu", cgit_query_head, + cgit_query_sha1); + cgit_diff_link("diff", NULL, "menu", cgit_query_head, + cgit_query_sha1, cgit_query_sha2, NULL); + cgit_patch_link("patch", NULL, "menu", cgit_query_head, + cgit_query_sha1); for_each_ref(print_archive_ref, &header); - if (ctx->repo->clone_url || ctx->cfg.clone_prefix) { + if (cgit_repo->clone_url || cgit_clone_prefix) { html("<h1>clone</h1>\n"); - if (ctx->repo->clone_url) - url = ctx->repo->clone_url; + if (cgit_repo->clone_url) + url = cgit_repo->clone_url; else - url = fmt("%s%s", ctx->cfg.clone_prefix, - ctx->repo->url); + url = fmt("%s%s", cgit_clone_prefix, + cgit_repo->url); html("<a class='menu' href='"); html_attr(url); html("' title='"); @@ -522,10 +519,10 @@ void cgit_print_pageheader(struct cgit_context *ctx) html("<h1>branch</h1>\n"); html("<form method='get' action=''>\n"); - add_hidden_formfields(0, 1, ctx->qry.page); + add_hidden_formfields(0, 1, cgit_query_page); // html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>"); html("<select name='h' onchange='this.form.submit();'>\n"); - for_each_branch_ref(print_branch_option, ctx->qry.head); + for_each_branch_ref(print_branch_option, cgit_query_head); html("</select>\n"); // html("</td><td>"); html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n"); @@ -534,22 +531,22 @@ void cgit_print_pageheader(struct cgit_context *ctx) html("<h1>search</h1>\n"); html("<form method='get' action='"); - if (ctx->cfg.virtual_root) - html_attr(cgit_fileurl(ctx->qry.repo, "log", - ctx->qry.path, NULL)); + if (cgit_virtual_root) + html_attr(cgit_fileurl(cgit_query_repo, "log", + cgit_query_path, NULL)); html("'>\n"); add_hidden_formfields(1, 0, "log"); html("<select name='qt'>\n"); - html_option("grep", "log msg", ctx->qry.grep); - html_option("author", "author", ctx->qry.grep); - html_option("committer", "committer", ctx->qry.grep); + html_option("grep", "log msg", cgit_query_grep); + html_option("author", "author", cgit_query_grep); + html_option("committer", "committer", cgit_query_grep); html("</select>\n"); html("<input class='txt' type='text' name='q' value='"); - html_attr(ctx->qry.search); + html_attr(cgit_query_search); html("'/>\n"); html("</form>\n"); } else { - if (!ctx->cfg.index_info || html_include(ctx->cfg.index_info)) + if (!cgit_index_info || html_include(cgit_index_info)) html(default_info); } @@ -558,34 +555,16 @@ void cgit_print_pageheader(struct cgit_context *ctx) html("<td id='content'>\n"); } -void cgit_print_filemode(unsigned short mode) -{ - if (S_ISDIR(mode)) - html("d"); - else if (S_ISLNK(mode)) - html("l"); - else if (S_ISGITLINK(mode)) - html("m"); - else - html("-"); - html_fileperm(mode >> 6); - html_fileperm(mode >> 3); - html_fileperm(mode); -} -void cgit_print_snapshot_links(const char *repo, const char *head, - const char *hex, int snapshots) +void cgit_print_snapshot_start(const char *mimetype, const char *filename, + struct cacheitem *item) { - const struct cgit_snapshot_format* f; - char *filename; - - for (f = cgit_snapshot_formats; f->suffix; f++) { - if (!(snapshots & f->bit)) - continue; - filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, - f->suffix); - cgit_snapshot_link(filename, NULL, NULL, (char *)head, - (char *)hex, filename); - html("<br/>"); - } + htmlf("Content-Type: %s\n", mimetype); + htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); + htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); + htmlf("Expires: %s\n", http_date(item->st.st_mtime + + ttl_seconds(item->ttl))); + html("\n"); } + +/* vim:set sw=8: */ diff --git a/ui-shared.h b/ui-shared.h deleted file mode 100644 index 94de884..0000000 --- a/ui-shared.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef UI_SHARED_H -#define UI_SHARED_H - -extern char *cgit_repourl(const char *reponame); -extern char *cgit_fileurl(const char *reponame, const char *pagename, - const char *filename, const char *query); -extern char *cgit_pageurl(const char *reponame, const char *pagename, - const char *query); - -extern void cgit_tree_link(char *name, char *title, char *class, char *head, - char *rev, char *path); -extern void cgit_log_link(char *name, char *title, char *class, char *head, - char *rev, char *path, int ofs, char *grep, - char *pattern); -extern void cgit_commit_link(char *name, char *title, char *class, char *head, - char *rev); -extern void cgit_refs_link(char *name, char *title, char *class, char *head, - char *rev, char *path); -extern void cgit_snapshot_link(char *name, char *title, char *class, - char *head, char *rev, char *archivename); -extern void cgit_diff_link(char *name, char *title, char *class, char *head, - char *new_rev, char *old_rev, char *path); -extern void cgit_object_link(struct object *obj); - -extern void cgit_print_error(char *msg); -extern void cgit_print_date(time_t secs, char *format); -extern void cgit_print_age(time_t t, time_t max_relative, char *format); -extern void cgit_print_http_headers(struct cgit_context *ctx); -extern void cgit_print_docstart(struct cgit_context *ctx); -extern void cgit_print_docend(); -extern void cgit_print_pageheader(struct cgit_context *ctx); -extern void cgit_print_filemode(unsigned short mode); -extern void cgit_print_snapshot_links(const char *repo, const char *head, - const char *hex, int snapshots); - -#endif /* UI_SHARED_H */ diff --git a/ui-snapshot.c b/ui-snapshot.c index 966a140..dfedd8f 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c @@ -7,8 +7,6 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" static int write_compressed_tar_archive(struct archiver_args *args,const char *filter) { @@ -56,59 +54,104 @@ static int write_tar_bzip2_archive(struct archiver_args *args) return write_compressed_tar_archive(args,"bzip2"); } -const struct cgit_snapshot_format cgit_snapshot_formats[] = { +static const struct snapshot_archive_t { + const char *suffix; + const char *mimetype; + write_archive_fn_t write_func; + int bit; +} snapshot_archives[] = { { ".zip", "application/x-zip", write_zip_archive, 0x1 }, { ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 }, { ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 }, - { ".tar", "application/x-tar", write_tar_archive, 0x8 }, - {} + { ".tar", "application/x-tar", write_tar_archive, 0x8 } }; -static int make_snapshot(const struct cgit_snapshot_format *format, +#define snapshot_archives_len (sizeof(snapshot_archives) / sizeof(*snapshot_archives)) + +void cgit_print_snapshot(struct cacheitem *item, const char *head, const char *hex, const char *prefix, - const char *filename) + const char *filename, int snapshots) { + const struct snapshot_archive_t* sat; struct archiver_args args; struct commit *commit; unsigned char sha1[20]; + int f, sl, fnl = strlen(filename); - if(get_sha1(hex, sha1)) { - cgit_print_error(fmt("Bad object id: %s", hex)); - return 1; - } - commit = lookup_commit_reference(sha1); - if(!commit) { - cgit_print_error(fmt("Not a commit reference: %s", hex)); - return 1; + for(f=0; f<snapshot_archives_len; f++) { + sat = &snapshot_archives[f]; + if(!(snapshots & sat->bit)) + continue; + sl = strlen(sat->suffix); + if(fnl<sl || strcmp(&filename[fnl-sl],sat->suffix)) + continue; + if (!hex) + hex = head; + if(get_sha1(hex, sha1)) { + cgit_print_error(fmt("Bad object id: %s", hex)); + return; + } + commit = lookup_commit_reference(sha1); + if(!commit) { + cgit_print_error(fmt("Not a commit reference: %s", hex)); + return;; + } + memset(&args,0,sizeof(args)); + args.base = fmt("%s/", prefix); + args.tree = commit->tree; + args.time = commit->date; + cgit_print_snapshot_start(sat->mimetype, filename, item); + (*sat->write_func)(&args); + return; } - memset(&args, 0, sizeof(args)); - args.base = fmt("%s/", prefix); - args.tree = commit->tree; - args.time = commit->date; - ctx.page.mimetype = xstrdup(format->mimetype); - ctx.page.filename = xstrdup(filename); - cgit_print_http_headers(&ctx); - format->write_func(&args); - return 0; + cgit_print_error(fmt("Unsupported snapshot format: %s", filename)); } -void cgit_print_snapshot(const char *head, const char *hex, const char *prefix, - const char *filename, int snapshots) +void cgit_print_snapshot_links(const char *repo, const char *head, + const char *hex, int snapshots) { - const struct cgit_snapshot_format* f; - int sl, fnl; + const struct snapshot_archive_t* sat; + char *filename; + int f; - fnl = strlen(filename); - if (!hex) - hex = head; - for (f = cgit_snapshot_formats; f->suffix; f++) { - if (!(snapshots & f->bit)) - continue; - sl = strlen(f->suffix); - if(fnl < sl || strcmp(&filename[fnl-sl], f->suffix)) + for(f=0; f<snapshot_archives_len; f++) { + sat = &snapshot_archives[f]; + if(!(snapshots & sat->bit)) continue; - make_snapshot(f, hex, prefix, filename); - return; + filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, + sat->suffix); + cgit_snapshot_link(filename, NULL, NULL, (char *)head, + (char *)hex, filename); + html("<br/>"); } - cgit_print_error(fmt("Unsupported snapshot format: %s", filename)); } + +int cgit_parse_snapshots_mask(const char *str) +{ + const struct snapshot_archive_t* sat; + static const char *delim = " \t,:/|;"; + int f, tl, sl, rv = 0; + + /* favor legacy setting */ + if(atoi(str)) + return 1; + for(;;) { + str += strspn(str,delim); + tl = strcspn(str,delim); + if(!tl) + break; + for(f=0; f<snapshot_archives_len; f++) { + sat = &snapshot_archives[f]; + sl = strlen(sat->suffix); + if((tl == sl && !strncmp(sat->suffix, str, tl)) || + (tl == sl-1 && !strncmp(sat->suffix+1, str, tl-1))) { + rv |= sat->bit; + break; + } + } + str += tl; + } + return rv; +} + +/* vim:set sw=8: */ diff --git a/ui-snapshot.h b/ui-snapshot.h deleted file mode 100644 index 6e03761..0000000 --- a/ui-snapshot.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef UI_SNAPSHOT_H -#define UI_SNAPSHOT_H - -extern void cgit_print_snapshot(const char *head, const char *hex, - const char *prefix, const char *filename, - int snapshot); - -#endif /* UI_SNAPSHOT_H */ diff --git a/ui-summary.c b/ui-summary.c index 0b66b52..b96414e 100644 --- a/ui-summary.c +++ b/ui-summary.c @@ -7,25 +7,194 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-log.h" -#include "ui-refs.h" + +static int header; + +static int cmp_age(int age1, int age2) +{ + if (age1 != 0 && age2 != 0) + return age2 - age1; + + if (age1 == 0 && age2 == 0) + return 0; + + if (age1 == 0) + return +1; + + return -1; +} + +static int cmp_ref_name(const void *a, const void *b) +{ + struct refinfo *r1 = *(struct refinfo **)a; + struct refinfo *r2 = *(struct refinfo **)b; + + return strcmp(r1->refname, r2->refname); +} + +static int cmp_branch_age(const void *a, const void *b) +{ + struct refinfo *r1 = *(struct refinfo **)a; + struct refinfo *r2 = *(struct refinfo **)b; + + return cmp_age(r1->commit->committer_date, r2->commit->committer_date); +} + +static int cmp_tag_age(const void *a, const void *b) +{ + struct refinfo *r1 = *(struct refinfo **)a; + struct refinfo *r2 = *(struct refinfo **)b; + + return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date); +} + +static int print_branch(struct refinfo *ref) +{ + struct commitinfo *info = ref->commit; + char *name = (char *)ref->refname; + + if (!info) + return 1; + html("<tr><td>"); + cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL); + html("</td><td>"); + + if (ref->object->type == OBJ_COMMIT) { + cgit_print_age(info->commit->date, -1, NULL); + html("</td><td>"); + html_txt(info->author); + html("</td><td>"); + cgit_commit_link(info->subject, NULL, NULL, name, NULL); + } else { + html("</td><td></td><td>"); + cgit_object_link(ref->object); + } + html("</td></tr>\n"); + return 0; +} + +static void print_tag_header() +{ + html("<tr class='nohover'><th class='left'>Tag</th>" + "<th class='left'>Age</th>" + "<th class='left'>Author</th>" + "<th class='left'>Reference</th></tr>\n"); + header = 1; +} + +static int print_tag(struct refinfo *ref) +{ + struct tag *tag; + struct taginfo *info; + char *url, *name = (char *)ref->refname; + + if (ref->object->type == OBJ_TAG) { + tag = (struct tag *)ref->object; + info = ref->tag; + if (!tag || !info) + return 1; + html("<tr><td>"); + url = cgit_pageurl(cgit_query_repo, "tag", + fmt("id=%s", name)); + html_link_open(url, NULL, NULL); + html_txt(name); + html_link_close(); + html("</td><td>"); + if (info->tagger_date > 0) + cgit_print_age(info->tagger_date, -1, NULL); + html("</td><td>"); + if (info->tagger) + html(info->tagger); + html("</td><td>"); + cgit_object_link(tag->tagged); + html("</td></tr>\n"); + } else { + if (!header) + print_tag_header(); + html("<tr><td>"); + html_txt(name); + html("</td><td colspan='2'/><td>"); + cgit_object_link(ref->object); + html("</td></tr>\n"); + } + return 0; +} + +static void print_refs_link(char *path) +{ + html("<tr class='nohover'><td colspan='4'>"); + cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path); + html("</td></tr>"); +} + +void cgit_print_branches(int maxcount) +{ + struct reflist list; + int i; + + html("<tr class='nohover'><th class='left'>Branch</th>" + "<th class='left'>Idle</th>" + "<th class='left'>Author</th>" + "<th class='left'>Head commit</th></tr>\n"); + + list.refs = NULL; + list.alloc = list.count = 0; + for_each_branch_ref(cgit_refs_cb, &list); + + if (maxcount == 0 || maxcount > list.count) + maxcount = list.count; + + if (maxcount < list.count) { + qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); + qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); + } + + for(i=0; i<maxcount; i++) + print_branch(list.refs[i]); + + if (maxcount < list.count) + print_refs_link("heads"); +} + +void cgit_print_tags(int maxcount) +{ + struct reflist list; + int i; + + header = 0; + list.refs = NULL; + list.alloc = list.count = 0; + for_each_tag_ref(cgit_refs_cb, &list); + if (list.count == 0) + return; + qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age); + if (!maxcount) + maxcount = list.count; + else if (maxcount > list.count) + maxcount = list.count; + print_tag_header(); + for(i=0; i<maxcount; i++) + print_tag(list.refs[i]); + + if (maxcount < list.count) + print_refs_link("tags"); +} void cgit_print_summary() { - if (ctx.repo->readme) { + if (cgit_repo->readme) { html("<div id='summary'>"); - html_include(ctx.repo->readme); + html_include(cgit_repo->readme); html("</div>"); } - if (ctx.cfg.summary_log > 0) - cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, + if (cgit_summary_log > 0) + cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, NULL, 0); html("<table summary='repository info' class='list nowrap'>"); - if (ctx.cfg.summary_log > 0) + if (cgit_summary_log > 0) html("<tr class='nohover'><td colspan='4'> </td></tr>"); - cgit_print_branches(ctx.cfg.summary_branches); + cgit_print_branches(cgit_summary_branches); html("<tr class='nohover'><td colspan='4'> </td></tr>"); - cgit_print_tags(ctx.cfg.summary_tags); + cgit_print_tags(cgit_summary_tags); html("</table>"); } diff --git a/ui-summary.h b/ui-summary.h deleted file mode 100644 index 37aedd2..0000000 --- a/ui-summary.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_SUMMARY_H -#define UI_SUMMARY_H - -extern void cgit_print_summary(); - -#endif /* UI_SUMMARY_H */ @@ -7,8 +7,7 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" + static void print_tag_content(char *buf) { diff --git a/ui-tag.h b/ui-tag.h deleted file mode 100644 index d295cdc..0000000 --- a/ui-tag.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_TAG_H -#define UI_TAG_H - -extern void cgit_print_tag(char *revname); - -#endif /* UI_TAG_H */ @@ -7,8 +7,6 @@ */ #include "cgit.h" -#include "html.h" -#include "ui-shared.h" char *curr_rev; char *match_path; @@ -36,7 +34,7 @@ static void print_object(const unsigned char *sha1, char *path) } html(" blob: <a href='"); - html_attr(cgit_pageurl(ctx.qry.repo, "blob", fmt("id=%s", sha1_to_hex(sha1)))); + html_attr(cgit_pageurl(cgit_query_repo, "blob", fmt("id=%s", sha1_to_hex(sha1)))); htmlf("'>%s</a>",sha1_to_hex(sha1)); html("<table summary='blob content' class='blob'>\n"); @@ -69,8 +67,8 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen, unsigned long size = 0; name = xstrdup(pathname); - fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", - ctx.qry.path ? "/" : "", name); + fullpath = fmt("%s%s%s", cgit_query_path ? cgit_query_path : "", + cgit_query_path ? "/" : "", name); type = sha1_object_info(sha1, &size); if (type == OBJ_BAD && !S_ISGITLINK(mode)) { @@ -81,27 +79,27 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen, } html("<tr><td class='ls-mode'>"); - cgit_print_filemode(mode); + html_filemode(mode); html("</td><td>"); if (S_ISGITLINK(mode)) { htmlf("<a class='ls-mod' href='"); - html_attr(fmt(ctx.repo->module_link, + html_attr(fmt(cgit_repo->module_link, name, sha1_to_hex(sha1))); html("'>"); html_txt(name); html("</a>"); } else if (S_ISDIR(mode)) { - cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, + cgit_tree_link(name, NULL, "ls-dir", cgit_query_head, curr_rev, fullpath); } else { - cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, + cgit_tree_link(name, NULL, "ls-blob", cgit_query_head, curr_rev, fullpath); } htmlf("</td><td class='ls-size'>%li</td>", size); html("<td>"); - cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, + cgit_log_link("log", NULL, "button", cgit_query_head, curr_rev, fullpath, 0, NULL, NULL); html("</td></tr>\n"); free(name); @@ -155,10 +153,10 @@ static int walk_tree(const unsigned char *sha1, const char *base, int baselen, if (state == 0) { memcpy(buffer, base, baselen); strcpy(buffer+baselen, pathname); - url = cgit_pageurl(ctx.qry.repo, "tree", + url = cgit_pageurl(cgit_query_repo, "tree", fmt("h=%s&path=%s", curr_rev, buffer)); html("/"); - cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, + cgit_tree_link(xstrdup(pathname), NULL, NULL, cgit_query_head, curr_rev, buffer); if (strcmp(match_path, buffer)) @@ -190,7 +188,7 @@ void cgit_print_tree(const char *rev, char *path) const char *paths[] = {path, NULL}; if (!rev) - rev = ctx.qry.head; + rev = cgit_query_head; curr_rev = xstrdup(rev); if (get_sha1(rev, sha1)) { @@ -204,7 +202,7 @@ void cgit_print_tree(const char *rev, char *path) } html("path: <a href='"); - html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); + html_attr(cgit_pageurl(cgit_query_repo, "tree", fmt("h=%s", rev))); html("'>root</a>"); if (path == NULL) { diff --git a/ui-tree.h b/ui-tree.h deleted file mode 100644 index bbd34e3..0000000 --- a/ui-tree.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef UI_TREE_H -#define UI_TREE_H - -extern void cgit_print_tree(const char *rev, char *path); - -#endif /* UI_TREE_H */ |