author | Lars Hjemli <hjemli@gmail.com> | 2008-03-23 23:51:19 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2008-03-23 23:51:19 (UTC) |
commit | f3c1a187fe2bc33f8423cd535d5045899699995b (patch) (side-by-side diff) | |
tree | b5c553da7b108900535fcfcd24b78bdd0ac62387 | |
parent | b1f9b9c1459cb9a30ebf80721aff6ef788d1f891 (diff) | |
download | cgit-f3c1a187fe2bc33f8423cd535d5045899699995b.zip cgit-f3c1a187fe2bc33f8423cd535d5045899699995b.tar.gz cgit-f3c1a187fe2bc33f8423cd535d5045899699995b.tar.bz2 |
Add struct cgit_page to cgit_context
This struct is used when generating http headers, and as such is another
small step towards the goal of the whole cleanup series; to invoke each
page/view function with a function pointer.
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | cgit.c | 50 | ||||
-rw-r--r-- | cgit.h | 18 | ||||
-rw-r--r-- | shared.c | 3 | ||||
-rw-r--r-- | ui-blob.c | 4 | ||||
-rw-r--r-- | ui-patch.c | 4 | ||||
-rw-r--r-- | ui-repolist.c | 6 | ||||
-rw-r--r-- | ui-shared.c | 112 | ||||
-rw-r--r-- | ui-snapshot.c | 4 |
8 files changed, 113 insertions, 88 deletions
@@ -1,316 +1,332 @@ /* cgit.c: cgi for the git scm * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" +#include "cmd.h" static int cgit_prepare_cache(struct cacheitem *item) { if (!ctx.repo && ctx.qry.repo) { - char *title = fmt("%s - %s", ctx.cfg.root_title, "Bad request"); - cgit_print_docstart(title, item); - cgit_print_pageheader(title, 0); + 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)); 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; return 1; } if (!cgit_cmd) { 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; } 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; else item->ttl = ctx.cfg.cache_repo_ttl; } return 1; } struct refmatch { char *req_ref; char *first_ref; int match; }; int find_current_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct refmatch *info; info = (struct refmatch *)cb_data; if (!strcmp(refname, info->req_ref)) info->match = 1; if (!info->first_ref) info->first_ref = xstrdup(refname); return info->match; } char *find_default_branch(struct cgit_repo *repo) { struct refmatch info; info.req_ref = repo->defbranch; info.first_ref = NULL; info.match = 0; for_each_branch_ref(find_current_ref, &info); if (info.match) return info.req_ref; else return info.first_ref; } static void cgit_print_repo_page(struct cacheitem *item) { - char *title, *tmp; + char *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) { - title = fmt("%s - %s", ctx.cfg.root_title, "config error"); + 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_docstart(title, item); - cgit_print_pageheader(title, 0); + cgit_print_http_headers(&ctx); + cgit_print_docstart(&ctx); + cgit_print_pageheader(&ctx); cgit_print_error(tmp); cgit_print_docend(); return; } - title = fmt("%s - %s", ctx.repo->name, ctx.repo->desc); + ctx.page.title = fmt("%s - %s", ctx.repo->name, ctx.repo->desc); show_search = 0; if (!ctx.qry.head) { ctx.qry.head = xstrdup(find_default_branch(ctx.repo)); ctx.repo->defbranch = ctx.qry.head; } if (!ctx.qry.head) { - cgit_print_docstart(title, item); - cgit_print_pageheader(title, 0); + cgit_print_http_headers(&ctx); + cgit_print_docstart(&ctx); + cgit_print_pageheader(&ctx); cgit_print_error("Repository seems to be empty"); cgit_print_docend(); return; } if (get_sha1(ctx.qry.head, sha1)) { tmp = xstrdup(ctx.qry.head); ctx.qry.head = ctx.repo->defbranch; - cgit_print_docstart(title, item); - cgit_print_pageheader(title, 0); + cgit_print_http_headers(&ctx); + cgit_print_docstart(&ctx); + cgit_print_pageheader(&ctx); cgit_print_error(fmt("Invalid branch: %s", tmp)); cgit_print_docend(); return; } if ((cgit_cmd == CMD_SNAPSHOT) && ctx.repo->snapshots) { cgit_print_snapshot(item, ctx.qry.head, ctx.qry.sha1, cgit_repobasename(ctx.repo->url), ctx.qry.path, ctx.repo->snapshots ); return; } if (cgit_cmd == CMD_PATCH) { cgit_print_patch(ctx.qry.sha1, item); return; } if (cgit_cmd == CMD_BLOB) { cgit_print_blob(item, ctx.qry.sha1, ctx.qry.path); return; } show_search = (cgit_cmd == CMD_LOG); - cgit_print_docstart(title, item); + cgit_print_http_headers(&ctx); + cgit_print_docstart(&ctx); if (!cgit_cmd) { - cgit_print_pageheader("summary", show_search); + cgit_print_pageheader(&ctx); cgit_print_summary(); cgit_print_docend(); return; } - cgit_print_pageheader(ctx.qry.page, show_search); + cgit_print_pageheader(&ctx); switch(cgit_cmd) { case CMD_LOG: cgit_print_log(ctx.qry.sha1, ctx.qry.ofs, ctx.cfg.max_commit_count, ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1); break; case CMD_TREE: cgit_print_tree(ctx.qry.sha1, ctx.qry.path); break; case CMD_COMMIT: cgit_print_commit(ctx.qry.sha1); break; case CMD_REFS: cgit_print_refs(); break; case CMD_TAG: cgit_print_tag(ctx.qry.sha1); break; case CMD_DIFF: cgit_print_diff(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path); break; default: cgit_print_error("Invalid request"); } cgit_print_docend(); } +static long ttl_seconds(long ttl) +{ + if (ttl<0) + return 60 * 60 * 24 * 365; + else + return ttl * 60; +} + static void cgit_fill_cache(struct cacheitem *item, int use_cache) { int stdout2; - item->st.st_mtime = time(NULL); - if (use_cache) { stdout2 = chk_positive(dup(STDOUT_FILENO), "Preserving STDOUT"); chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); } + ctx.page.modified = time(NULL); + ctx.page.expires = ctx.page.modified + ttl_seconds(item->ttl); if (ctx.repo) cgit_print_repo_page(item); else cgit_print_repolist(item); if (use_cache) { chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); chk_positive(dup2(stdout2, STDOUT_FILENO), "Restoring original STDOUT"); chk_zero(close(stdout2), "Closing temporary STDOUT"); } } static void cgit_check_cache(struct cacheitem *item) { int i = 0; top: if (++i > ctx.cfg.max_lock_attempts) { die("cgit_refresh_cache: unable to lock %s: %s", item->name, strerror(errno)); } if (!cache_exist(item)) { if (!cache_lock(item)) { sleep(1); goto top; } if (!cache_exist(item)) { cgit_fill_cache(item, 1); cache_unlock(item); } else { cache_cancel_lock(item); } } else if (cache_expired(item) && cache_lock(item)) { if (cache_expired(item)) { cgit_fill_cache(item, 1); cache_unlock(item); } else { cache_cancel_lock(item); } } } static void cgit_print_cache(struct cacheitem *item) { static char buf[4096]; ssize_t i; int fd = open(item->name, O_RDONLY); if (fd<0) die("Unable to open cached file %s", item->name); while((i=read(fd, buf, sizeof(buf))) > 0) write(STDOUT_FILENO, buf, i); close(fd); } static void cgit_parse_args(int argc, const char **argv) { int i; for (i = 1; i < argc; i++) { if (!strncmp(argv[i], "--cache=", 8)) { ctx.cfg.cache_root = xstrdup(argv[i]+8); } if (!strcmp(argv[i], "--nocache")) { ctx.cfg.nocache = 1; } if (!strncmp(argv[i], "--query=", 8)) { ctx.qry.raw = xstrdup(argv[i]+8); } if (!strncmp(argv[i], "--repo=", 7)) { ctx.qry.repo = xstrdup(argv[i]+7); } if (!strncmp(argv[i], "--page=", 7)) { ctx.qry.page = xstrdup(argv[i]+7); } if (!strncmp(argv[i], "--head=", 7)) { ctx.qry.head = xstrdup(argv[i]+7); ctx.qry.has_symref = 1; } if (!strncmp(argv[i], "--sha1=", 7)) { ctx.qry.sha1 = xstrdup(argv[i]+7); ctx.qry.has_sha1 = 1; } if (!strncmp(argv[i], "--ofs=", 6)) { ctx.qry.ofs = atoi(argv[i]+6); } } } int main(int argc, const char **argv) { struct cacheitem item; const char *cgit_config_env = getenv("CGIT_CONFIG"); cgit_prepare_context(&ctx); item.st.st_mtime = time(NULL); cgit_repolist.length = 0; cgit_repolist.count = 0; cgit_repolist.repos = NULL; cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG, cgit_global_config_cb); if (getenv("SCRIPT_NAME")) ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); if (getenv("QUERY_STRING")) ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); cgit_parse_args(argc, argv); cgit_parse_query(ctx.qry.raw, cgit_querystring_cb); if (!cgit_prepare_cache(&item)) return 0; if (ctx.cfg.nocache) { cgit_fill_cache(&item, 0); } else { cgit_check_cache(&item); cgit_print_cache(&item); } return 0; } @@ -1,291 +1,299 @@ #ifndef CGIT_H #define CGIT_H #include <git-compat-util.h> #include <cache.h> #include <grep.h> #include <object.h> #include <tree.h> #include <commit.h> #include <tag.h> #include <diff.h> #include <diffcore.h> #include <refs.h> #include <revision.h> #include <log-tree.h> #include <archive.h> #include <xdiff/xdiff.h> #include <utf8.h> /* * 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" #define FMT_SHORTDATE "%Y-%m-%d" /* * Limits used for relative dates */ #define TM_MIN 60 #define TM_HOUR (TM_MIN * 60) #define TM_DAY (TM_HOUR * 24) #define TM_WEEK (TM_DAY * 7) #define TM_YEAR (TM_DAY * 365) #define TM_MONTH (TM_YEAR / 12.0) /* * Default encoding */ #define PAGE_ENCODING "UTF-8" 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 cacheitem { char *name; struct stat st; int ttl; int fd; }; struct cgit_repo { char *url; char *name; char *path; char *desc; char *owner; char *defbranch; char *group; char *module_link; char *readme; char *clone_url; int snapshots; int enable_log_filecount; int enable_log_linecount; }; struct cgit_repolist { int length; int count; struct cgit_repo *repos; }; struct commitinfo { struct commit *commit; char *author; char *author_email; unsigned long author_date; char *committer; char *committer_email; unsigned long committer_date; char *subject; char *msg; char *msg_encoding; }; struct taginfo { char *tagger; char *tagger_email; int tagger_date; char *msg; }; struct refinfo { const char *refname; struct object *object; union { struct taginfo *tag; struct commitinfo *commit; }; }; struct reflist { struct refinfo **refs; int alloc; 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; }; 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; }; +struct cgit_page { + time_t modified; + time_t expires; + char *mimetype; + char *charset; + char *filename; + char *title; +}; + struct cgit_context { struct cgit_query qry; struct cgit_config cfg; struct cgit_repo *repo; + struct cgit_page page; }; extern const char *cgit_version; extern struct cgit_repolist cgit_repolist; extern struct cgit_context ctx; extern int cgit_cmd; extern void cgit_prepare_context(struct cgit_context *ctx); extern int cgit_get_cmd_index(const char *cmd); extern struct cgit_repo *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); extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, void *cb_data); extern void *cgit_free_commitinfo(struct commitinfo *info); extern int cgit_diff_files(const unsigned char *old_sha1, const unsigned char *new_sha1, linediff_fn fn); extern void cgit_diff_tree(const unsigned char *old_sha1, const unsigned char *new_sha1, filepair_fn fn, const char *prefix); extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); extern char *fmt(const char *format,...); 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_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(char *title, int show_search); -extern void cgit_print_snapshot_start(const char *mimetype, - const char *filename, - struct cacheitem *item); +extern void cgit_print_pageheader(struct cgit_context *ctx); extern void cgit_print_filemode(unsigned short mode); 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,491 +1,494 @@ /* shared.c: global vars + some callback functions * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" struct cgit_repolist cgit_repolist; struct cgit_context ctx; int cgit_cmd; const char *cgit_version = CGIT_VERSION; void cgit_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; } 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) die("%s: %s", msg, strerror(errno)); return result; } int chk_positive(int result, char *msg) { if (result <= 0) die("%s: %s", msg, strerror(errno)); return result; } int chk_non_negative(int result, char *msg) { if (result < 0) die("%s: %s",msg, strerror(errno)); return result; } struct cgit_repo *add_repo(const char *url) { struct cgit_repo *ret; if (++cgit_repolist.count > cgit_repolist.length) { if (cgit_repolist.length == 0) cgit_repolist.length = 8; else cgit_repolist.length *= 2; cgit_repolist.repos = xrealloc(cgit_repolist.repos, cgit_repolist.length * sizeof(struct cgit_repo)); } ret = &cgit_repolist.repos[cgit_repolist.count-1]; ret->url = trim_end(url, '/'); ret->name = ret->url; ret->path = NULL; ret->desc = "[no description]"; ret->owner = NULL; ret->group = ctx.cfg.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->readme = NULL; return ret; } struct cgit_repo *cgit_get_repoinfo(const char *url) { int i; struct cgit_repo *repo; for (i=0; i<cgit_repolist.count; i++) { repo = &cgit_repolist.repos[i]; if (!strcmp(repo->url, url)) return repo; } return NULL; } void cgit_global_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 = 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")) cgit_read_config(value, cgit_global_config_cb); } void cgit_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); cgit_cmd = cgit_get_cmd_index(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); } } void *cgit_free_commitinfo(struct commitinfo *info) { free(info->author); free(info->author_email); free(info->committer); free(info->committer_email); free(info->subject); free(info->msg); free(info->msg_encoding); free(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; char *s, *t; if (str == NULL) return NULL; t = (char *)str; len = strlen(t); while(len > 0 && t[len - 1] == c) len--; if (len == 0) return NULL; c = t[len]; t[len] = '\0'; s = xstrdup(t); t[len] = c; return s; } char *strlpart(char *txt, int maxlen) { char *result; if (!txt) return txt; if (strlen(txt) <= maxlen) return txt; result = xmalloc(maxlen + 1); memcpy(result, txt, maxlen - 3); result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.'; result[maxlen] = '\0'; return result; } char *strrpart(char *txt, int maxlen) { char *result; if (!txt) return txt; if (strlen(txt) <= maxlen) return txt; result = xmalloc(maxlen + 1); memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); result[0] = result[1] = result[2] = '.'; return result; } void cgit_add_ref(struct reflist *list, struct refinfo *ref) { size_t size; if (list->count >= list->alloc) { list->alloc += (list->alloc ? list->alloc : 4); size = list->alloc * sizeof(struct refinfo *); list->refs = xrealloc(list->refs, size); } list->refs[list->count++] = ref; } struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) { struct refinfo *ref; ref = xmalloc(sizeof (struct refinfo)); ref->refname = xstrdup(refname); ref->object = parse_object(sha1); switch (ref->object->type) { case OBJ_TAG: ref->tag = cgit_parse_tag((struct tag *)ref->object); break; case OBJ_COMMIT: ref->commit = cgit_parse_commit((struct commit *)ref->object); break; } return ref; } int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct reflist *list = (struct reflist *)cb_data; struct refinfo *info = cgit_mk_refinfo(refname, sha1); if (info) cgit_add_ref(list, info); return 0; } void cgit_diff_tree_cb(struct diff_queue_struct *q, struct diff_options *options, void *data) { int i; for (i = 0; i < q->nr; i++) { if (q->queue[i]->status == 'U') continue; ((filepair_fn)data)(q->queue[i]); } } static int load_mmfile(mmfile_t *file, const unsigned char *sha1) { enum object_type type; if (is_null_sha1(sha1)) { file->ptr = (char *)""; file->size = 0; } else { file->ptr = read_sha1_file(sha1, &type, (unsigned long *)&file->size); } return 1; } /* * Receive diff-buffers from xdiff and concatenate them as * needed across multiple callbacks. * * This is basically a copy of xdiff-interface.c/xdiff_outf(), * ripped from git and modified to use globals instead of * a special callback-struct. */ char *diffbuf = NULL; int buflen = 0; int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) { int i; for (i = 0; i < nbuf; i++) { if (mb[i].ptr[mb[i].size-1] != '\n') { /* Incomplete line */ diffbuf = xrealloc(diffbuf, buflen + mb[i].size); memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); buflen += mb[i].size; continue; } /* we have a complete line */ if (!diffbuf) { ((linediff_fn)priv)(mb[i].ptr, mb[i].size); continue; } diffbuf = xrealloc(diffbuf, buflen + mb[i].size); memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); free(diffbuf); diffbuf = NULL; buflen = 0; } if (diffbuf) { ((linediff_fn)priv)(diffbuf, buflen); free(diffbuf); diffbuf = NULL; buflen = 0; } return 0; } int cgit_diff_files(const unsigned char *old_sha1, const unsigned char *new_sha1, linediff_fn fn) { mmfile_t file1, file2; xpparam_t diff_params; xdemitconf_t emit_params; xdemitcb_t emit_cb; if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) return 1; diff_params.flags = XDF_NEED_MINIMAL; emit_params.ctxlen = 3; emit_params.flags = XDL_EMIT_FUNCNAMES; emit_params.find_func = NULL; emit_cb.outf = filediff_cb; emit_cb.priv = fn; xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); return 0; } void cgit_diff_tree(const unsigned char *old_sha1, const unsigned char *new_sha1, filepair_fn fn, const char *prefix) { struct diff_options opt; int ret; int prefixlen; diff_setup(&opt); opt.output_format = DIFF_FORMAT_CALLBACK; opt.detect_rename = 1; opt.rename_limit = ctx.cfg.renamelimit; DIFF_OPT_SET(&opt, RECURSIVE); opt.format_callback = cgit_diff_tree_cb; opt.format_callback_data = fn; if (prefix) { opt.nr_paths = 1; opt.paths = &prefix; prefixlen = strlen(prefix); opt.pathlens = &prefixlen; } diff_setup_done(&opt); if (old_sha1 && !is_null_sha1(old_sha1)) ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); else ret = diff_root_tree_sha1(new_sha1, "", &opt); diffcore_std(&opt); diff_flush(&opt); } void cgit_diff_commit(struct commit *commit, filepair_fn fn) { unsigned char *old_sha1 = NULL; if (commit->parents) old_sha1 = commit->parents->item->object.sha1; cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); } @@ -1,40 +1,42 @@ /* 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" void cgit_print_blob(struct cacheitem *item, const char *hex, char *path) { unsigned char sha1[20]; enum object_type type; unsigned char *buf; unsigned long size; if (get_sha1_hex(hex, sha1)){ cgit_print_error(fmt("Bad hex value: %s", hex)); return; } type = sha1_object_info(sha1, &size); if (type == OBJ_BAD) { cgit_print_error(fmt("Bad object name: %s", hex)); return; } buf = read_sha1_file(sha1, &type, &size); if (!buf) { cgit_print_error(fmt("Error reading object %s", hex)); return; } buf[size] = '\0'; - cgit_print_snapshot_start("text/plain", path, item); + ctx.page.mimetype = "text/plain"; + ctx.page.filename = path; + cgit_print_http_headers(&ctx); write(htmlfd, buf, size); } @@ -1,111 +1,113 @@ /* ui-patch.c: generate patch view * * Copyright (C) 2007 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" #include "html.h" static void print_line(char *line, int len) { char c = line[len-1]; line[len-1] = '\0'; htmlf("%s\n", line); line[len-1] = c; } static void header(unsigned char *sha1, char *path1, int mode1, unsigned char *sha2, char *path2, int mode2) { char *abbrev1, *abbrev2; int subproject; subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); htmlf("diff --git a/%s b/%s\n", path1, path2); if (is_null_sha1(sha1)) path1 = "dev/null"; if (is_null_sha1(sha2)) path2 = "dev/null"; if (mode1 == 0) htmlf("new file mode %.6o\n", mode2); if (mode2 == 0) htmlf("deleted file mode %.6o\n", mode1); if (!subproject) { abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); htmlf("index %s..%s", abbrev1, abbrev2); free(abbrev1); free(abbrev2); if (mode1 != 0 && mode2 != 0) { htmlf(" %.6o", mode1); if (mode2 != mode1) htmlf("..%.6o", mode2); } htmlf("\n--- a/%s\n", path1); htmlf("+++ b/%s\n", path2); } } static void filepair_cb(struct diff_filepair *pair) { header(pair->one->sha1, pair->one->path, pair->one->mode, pair->two->sha1, pair->two->path, pair->two->mode); if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { if (S_ISGITLINK(pair->one->mode)) print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); if (S_ISGITLINK(pair->two->mode)) print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); return; } if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line)) html("Error running diff"); } void cgit_print_patch(char *hex, struct cacheitem *item) { struct commit *commit; struct commitinfo *info; unsigned char sha1[20], old_sha1[20]; char *patchname; if (!hex) hex = ctx.qry.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("Bad commit reference: %s", hex)); return; } info = cgit_parse_commit(commit); hashcpy(old_sha1, commit->parents->item->object.sha1); patchname = fmt("%s.patch", sha1_to_hex(sha1)); - cgit_print_snapshot_start("text/plain", patchname, item); + ctx.page.mimetype = "text/plain"; + ctx.page.filename = patchname; + cgit_print_http_headers(&ctx); 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: "); cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n"); htmlf("Subject: %s\n\n", info->subject); if (info->msg && *info->msg) { htmlf("%s", info->msg); if (info->msg[strlen(info->msg) - 1] != '\n') html("\n"); } html("---\n"); cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); html("--\n"); htmlf("cgit %s\n", CGIT_VERSION); cgit_free_commitinfo(info); } diff --git a/ui-repolist.c b/ui-repolist.c index cd4e41d..e663585 100644 --- a/ui-repolist.c +++ b/ui-repolist.c @@ -1,111 +1,113 @@ /* ui-repolist.c: functions for generating the repolist page * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include <time.h> #include "cgit.h" #include "html.h" time_t read_agefile(char *path) { FILE *f; static char buf[64], buf2[64]; if (!(f = fopen(path, "r"))) return -1; fgets(buf, sizeof(buf), f); fclose(f); if (parse_date(buf, buf2, sizeof(buf2))) return strtoul(buf2, NULL, 10); else return 0; } static void print_modtime(struct cgit_repo *repo) { char *path; struct stat s; path = fmt("%s/%s", repo->path, ctx.cfg.agefile); if (stat(path, &s) == 0) { cgit_print_age(read_agefile(path), -1, NULL); return; } path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); if (stat(path, &s) != 0) return; cgit_print_age(s.st_mtime, -1, NULL); } void cgit_print_repolist(struct cacheitem *item) { int i, columns = 4; char *last_group = NULL; if (ctx.cfg.enable_index_links) columns++; - cgit_print_docstart(ctx.cfg.root_title, item); - cgit_print_pageheader(ctx.cfg.root_title, 0); + ctx.page.title = ctx.cfg.root_title; + cgit_print_http_headers(&ctx); + cgit_print_docstart(&ctx); + cgit_print_pageheader(&ctx); html("<table summary='repository list' class='list nowrap'>"); if (ctx.cfg.index_header) { htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>", columns); html_include(ctx.cfg.index_header); html("</td></tr>"); } html("<tr class='nohover'>" "<th class='left'>Name</th>" "<th class='left'>Description</th>" "<th class='left'>Owner</th>" "<th class='left'>Idle</th>"); if (ctx.cfg.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))) { htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", columns); html_txt(ctx.repo->group); html("</td></tr>"); last_group = ctx.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); html_link_close(); html("</td><td>"); html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); html("</td><td>"); html_txt(ctx.repo->owner); html("</td><td>"); print_modtime(ctx.repo); html("</td>"); if (ctx.cfg.enable_index_links) { html("<td>"); html_link_open(cgit_repourl(ctx.repo->url), NULL, "button"); html("summary</a>"); cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 0, NULL, NULL); cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); html("</td>"); } html("</tr>\n"); } html("</table>"); cgit_print_docend(); } diff --git a/ui-shared.c b/ui-shared.c index 2eff79d..2596023 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1,586 +1,576 @@ /* ui-shared.c: common web output functions * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" #include "html.h" const char cgit_doctype[] = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; static char *http_date(time_t t) { static char day[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char month[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; struct tm *tm = gmtime(&t); return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 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'>"); html_txt(msg); html("</div>\n"); } char *cgit_rooturl() { if (ctx.cfg.virtual_root) return fmt("%s/", ctx.cfg.virtual_root); else return ctx.cfg.script_name; } char *cgit_repourl(const char *reponame) { if (ctx.cfg.virtual_root) { return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); } else { return fmt("?r=%s", reponame); } } char *cgit_fileurl(const char *reponame, const char *pagename, const char *filename, const char *query) { char *tmp; char *delim; if (ctx.cfg.virtual_root) { tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, pagename, (filename ? filename:"")); delim = "?"; } else { tmp = fmt("?url=%s/%s/%s", reponame, pagename, (filename ? filename : "")); delim = "&"; } if (query) tmp = fmt("%s%s%s", tmp, delim, query); return tmp; } char *cgit_pageurl(const char *reponame, const char *pagename, const char *query) { return cgit_fileurl(reponame,pagename,0,query); } const char *cgit_repobasename(const char *reponame) { /* I assume we don't need to store more than one repo basename */ static char rvbuf[1024]; int p; const char *rv; strncpy(rvbuf,reponame,sizeof(rvbuf)); if(rvbuf[sizeof(rvbuf)-1]) die("cgit_repobasename: truncated repository name '%s'", reponame); p = strlen(rvbuf)-1; /* strip trailing slashes */ while(p && rvbuf[p]=='/') rvbuf[p--]=0; /* strip trailing .git */ if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { p -= 3; rvbuf[p--] = 0; } /* strip more trailing slashes if any */ while( p && rvbuf[p]=='/') rvbuf[p--]=0; /* find last slash in the remaining string */ rv = strrchr(rvbuf,'/'); if(rv) return ++rv; return rvbuf; } 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); else return fmt("%s/", ctx.cfg.virtual_root); } static char *repolink(char *title, char *class, char *page, char *head, char *path) { char *delim = "?"; html("<a"); if (title) { html(" title='"); html_attr(title); html("'"); } if (class) { html(" class='"); html_attr(class); 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] != '/') html("/"); html_attr(ctx.repo->url); if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } } else { html(ctx.cfg.script_name); html("?url="); html_attr(ctx.repo->url); if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } delim = "&"; } if (head && strcmp(head, ctx.repo->defbranch)) { html(delim); html("h="); html_attr(head); delim = "&"; } return fmt("%s", delim); } static void reporevlink(char *page, char *name, char *title, char *class, char *head, char *rev, char *path) { char *delim; delim = repolink(title, class, page, head, path); if (rev && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_attr(rev); } html("'>"); html_txt(name); html("</a>"); } void cgit_tree_link(char *name, char *title, char *class, char *head, char *rev, char *path) { reporevlink("tree", name, title, class, head, rev, path); } void cgit_log_link(char *name, char *title, char *class, char *head, char *rev, char *path, int ofs, char *grep, char *pattern) { char *delim; delim = repolink(title, class, "log", head, path); if (rev && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_attr(rev); delim = "&"; } if (grep && pattern) { html(delim); html("qt="); html_attr(grep); delim = "&"; html(delim); html("q="); html_attr(pattern); } if (ofs > 0) { html(delim); html("ofs="); htmlf("%d", ofs); } html("'>"); html_txt(name); html("</a>"); } 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] = '.'; } reporevlink("commit", name, title, class, head, rev, NULL); } void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path) { reporevlink("refs", name, title, class, head, rev, path); } void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename) { reporevlink("snapshot", name, title, class, head, rev, archivename); } void cgit_diff_link(char *name, char *title, char *class, char *head, char *new_rev, char *old_rev, char *path) { char *delim; delim = repolink(title, class, "diff", head, path); if (new_rev && strcmp(new_rev, ctx.qry.head)) { html(delim); html("id="); html_attr(new_rev); delim = "&"; } if (old_rev) { html(delim); html("id2="); html_attr(old_rev); } html("'>"); html_txt(name); html("</a>"); } void cgit_patch_link(char *name, char *title, char *class, char *head, char *rev) { reporevlink("patch", name, title, class, head, rev, NULL); } void cgit_object_link(struct object *obj) { char *page, *arg, *url; 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)); return; } else if (obj->type == OBJ_TREE) { page = "tree"; arg = "id"; } else if (obj->type == OBJ_TAG) { page = "tag"; arg = "id"; } else { page = "blob"; arg = "id"; } url = cgit_pageurl(ctx.qry.repo, page, fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); html_link_open(url, NULL, NULL); htmlf("%s %s", typename(obj->type), sha1_to_hex(obj->sha1)); html_link_close(); } void cgit_print_date(time_t secs, char *format) { char buf[64]; struct tm *time; if (!secs) return; time = gmtime(&secs); strftime(buf, sizeof(buf)-1, format, time); html_txt(buf); } void cgit_print_age(time_t t, time_t max_relative, char *format) { time_t now, secs; if (!t) return; time(&now); secs = now - t; if (secs > max_relative && max_relative >= 0) { cgit_print_date(t, format); return; } if (secs < TM_HOUR * 2) { htmlf("<span class='age-mins'>%.0f min.</span>", secs * 1.0 / TM_MIN); return; } if (secs < TM_DAY * 2) { htmlf("<span class='age-hours'>%.0f hours</span>", secs * 1.0 / TM_HOUR); return; } if (secs < TM_WEEK * 2) { htmlf("<span class='age-days'>%.0f days</span>", secs * 1.0 / TM_DAY); return; } if (secs < TM_MONTH * 2) { htmlf("<span class='age-weeks'>%.0f weeks</span>", secs * 1.0 / TM_WEEK); return; } if (secs < TM_YEAR * 2) { htmlf("<span class='age-months'>%.0f months</span>", secs * 1.0 / TM_MONTH); return; } htmlf("<span class='age-years'>%.0f years</span>", secs * 1.0 / TM_YEAR); } -void cgit_print_docstart(char *title, struct cacheitem *item) +void cgit_print_http_headers(struct cgit_context *ctx) { - 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))); + 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("\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(title); + html_txt(ctx->page.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 (ctx->cfg.robots && *ctx->cfg.robots) + htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); html("<link rel='stylesheet' type='text/css' href='"); - html_attr(ctx.cfg.css); + html_attr(ctx->cfg.css); html("'/>\n"); html("</head>\n"); html("<body>\n"); } void cgit_print_docend() { html("</td>\n</tr>\n</table>\n</body>\n</html>\n"); } 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); return 0; } int print_archive_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct tag *tag; struct taginfo *info; struct object *obj; char buf[256], *url; unsigned char fileid[20]; int *header = (int *)cb_data; if (prefixcmp(refname, "refs/archives")) return 0; strncpy(buf, refname+14, sizeof(buf)); obj = parse_object(sha1); if (!obj) return 1; if (obj->type == OBJ_TAG) { tag = lookup_tag(sha1); if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) return 0; hashcpy(fileid, tag->tagged->sha1); } else if (obj->type != OBJ_BLOB) { return 0; } else { hashcpy(fileid, sha1); } if (!*header) { html("<h1>download</h1>\n"); *header = 1; } url = cgit_pageurl(ctx.qry.repo, "blob", fmt("id=%s&path=%s", sha1_to_hex(fileid), buf)); html_link_open(url, NULL, "menu"); html_txt(strlpart(buf, 20)); html_link_close(); return 0; } 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); html_hidden("url", url); } if (incl_head && strcmp(ctx.qry.head, ctx.repo->defbranch)) html_hidden("h", ctx.qry.head); if (ctx.qry.sha1) html_hidden("id", ctx.qry.sha1); if (ctx.qry.sha2) html_hidden("id2", ctx.qry.sha2); if (incl_search) { if (ctx.qry.grep) html_hidden("qt", ctx.qry.grep); if (ctx.qry.search) html_hidden("q", ctx.qry.search); } } -void cgit_print_pageheader(char *title, int show_search) +void cgit_print_pageheader(struct cgit_context *ctx) { static const char *default_info = "This is cgit, a fast webinterface for git repositories"; int header = 0; char *url; html("<table id='layout' summary=''>\n"); html("<tr><td id='sidebar'>\n"); html("<table class='sidebar' cellspacing='0' summary=''>\n"); html("<tr><td class='sidebar'>\n<a href='"); html_attr(cgit_rooturl()); htmlf("'><img src='%s' alt='cgit'/></a>\n", - ctx.cfg.logo); + ctx->cfg.logo); html("</td></tr>\n<tr><td class='sidebar'>\n"); - if (ctx.repo) { + if (ctx->repo) { html("<h1 class='first'>"); - html_txt(strrpart(ctx.repo->name, 20)); + html_txt(strrpart(ctx->repo->name, 20)); html("</h1>\n"); - html_txt(ctx.repo->desc); - if (ctx.repo->owner) { + html_txt(ctx->repo->desc); + if (ctx->repo->owner) { html("<h1>owner</h1>\n"); - html_txt(ctx.repo->owner); + html_txt(ctx->repo->owner); } html("<h1>navigate</h1>\n"); - reporevlink(NULL, "summary", NULL, "menu", ctx.qry.head, + reporevlink(NULL, "summary", NULL, "menu", ctx->qry.head, NULL, NULL); - cgit_log_link("log", NULL, "menu", ctx.qry.head, NULL, NULL, + cgit_log_link("log", NULL, "menu", ctx->qry.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", 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); for_each_ref(print_archive_ref, &header); - if (ctx.repo->clone_url || ctx.cfg.clone_prefix) { + if (ctx->repo->clone_url || ctx->cfg.clone_prefix) { html("<h1>clone</h1>\n"); - if (ctx.repo->clone_url) - url = ctx.repo->clone_url; + if (ctx->repo->clone_url) + url = ctx->repo->clone_url; else - url = fmt("%s%s", ctx.cfg.clone_prefix, - ctx.repo->url); + url = fmt("%s%s", ctx->cfg.clone_prefix, + ctx->repo->url); html("<a class='menu' href='"); html_attr(url); html("' title='"); html_attr(url); html("'>\n"); html_txt(strrpart(url, 20)); html("</a>\n"); } html("<h1>branch</h1>\n"); html("<form method='get' action=''>\n"); - add_hidden_formfields(0, 1, ctx.qry.page); + add_hidden_formfields(0, 1, ctx->qry.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, ctx->qry.head); html("</select>\n"); // html("</td><td>"); html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n"); // html("</td></tr></table>"); html("</form>\n"); 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 (ctx->cfg.virtual_root) + html_attr(cgit_fileurl(ctx->qry.repo, "log", + ctx->qry.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", ctx->qry.grep); + html_option("author", "author", ctx->qry.grep); + html_option("committer", "committer", ctx->qry.grep); html("</select>\n"); html("<input class='txt' type='text' name='q' value='"); - html_attr(ctx.qry.search); + html_attr(ctx->qry.search); html("'/>\n"); html("</form>\n"); } else { - if (!ctx.cfg.index_info || html_include(ctx.cfg.index_info)) + if (!ctx->cfg.index_info || html_include(ctx->cfg.index_info)) html(default_info); } html("</td></tr></table></td>\n"); html("<td id='content'>\n"); } - -void cgit_print_snapshot_start(const char *mimetype, const char *filename, - struct cacheitem *item) -{ - 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"); -} - 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); } /* vim:set sw=8: */ diff --git a/ui-snapshot.c b/ui-snapshot.c index 67dbbdd..4449803 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c @@ -1,158 +1,160 @@ /* ui-snapshot.c: generate snapshot of a commit * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" #include "html.h" static int write_compressed_tar_archive(struct archiver_args *args,const char *filter) { int rw[2]; pid_t gzpid; int stdout2; int status; int rv; stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing"); chk_zero(pipe(rw), "Opening pipe from compressor subprocess"); gzpid = chk_non_negative(fork(), "Forking compressor subprocess"); if(gzpid==0) { /* child */ chk_zero(close(rw[1]), "Closing write end of pipe in child"); chk_zero(close(STDIN_FILENO), "Closing STDIN"); chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin"); execlp(filter,filter,NULL); _exit(-1); } /* parent */ chk_zero(close(rw[0]), "Closing read end of pipe"); chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor"); rv = write_tar_archive(args); chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor"); chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT"); chk_zero(close(stdout2), "Closing uncompressed STDOUT"); chk_zero(close(rw[1]), "Closing write end of pipe in parent"); chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process"); if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) ) cgit_print_error("Failed to compress archive"); return rv; } static int write_tar_gzip_archive(struct archiver_args *args) { return write_compressed_tar_archive(args,"gzip"); } static int write_tar_bzip2_archive(struct archiver_args *args) { return write_compressed_tar_archive(args,"bzip2"); } 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 } }; #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, 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); 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); + ctx.page.mimetype = xstrdup(sat->mimetype); + ctx.page.filename = xstrdup(filename); + cgit_print_http_headers(&ctx); (*sat->write_func)(&args); return; } cgit_print_error(fmt("Unsupported snapshot format: %s", filename)); } void cgit_print_snapshot_links(const char *repo, const char *head, const char *hex, int snapshots) { const struct snapshot_archive_t* sat; char *filename; int f; for(f=0; f<snapshot_archives_len; f++) { sat = &snapshot_archives[f]; if(!(snapshots & sat->bit)) continue; filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, sat->suffix); cgit_snapshot_link(filename, NULL, NULL, (char *)head, (char *)hex, filename); html("<br/>"); } } 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: */ |