author | Lars Hjemli <hjemli@gmail.com> | 2007-07-22 22:11:15 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2007-07-22 22:27:32 (UTC) |
commit | eb45342e735818b3c68cbab9b61b23e79ae74418 (patch) (side-by-side diff) | |
tree | 43fed08927a2118a22cf686ee58b8d464233cd5d | |
parent | 1d4aaff696ee1b9085dda0f0f3d84d9d20d96db0 (diff) | |
download | cgit-eb45342e735818b3c68cbab9b61b23e79ae74418.zip cgit-eb45342e735818b3c68cbab9b61b23e79ae74418.tar.gz cgit-eb45342e735818b3c68cbab9b61b23e79ae74418.tar.bz2 |
cgit_print_snapshot_links: use url to specify snapshot name
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
-rw-r--r-- | cgit.c | 4 | ||||
-rw-r--r-- | cgit.h | 11 | ||||
-rw-r--r-- | ui-commit.c | 3 | ||||
-rw-r--r-- | ui-shared.c | 6 | ||||
-rw-r--r-- | ui-snapshot.c | 38 |
5 files changed, 41 insertions, 21 deletions
@@ -1,256 +1,256 @@ /* 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" static int cgit_prepare_cache(struct cacheitem *item) { 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 (!cgit_repo) { item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); item->ttl = cgit_cache_root_ttl; return 1; } 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", 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 = cgit_cache_repo_ttl; } return 1; } static void cgit_print_repo_page(struct cacheitem *item) { char *title; int show_search; if (!cgit_query_head) cgit_query_head = cgit_repo->defbranch; 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; } title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); show_search = 0; setenv("GIT_DIR", cgit_repo->path, 1); if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { - cgit_print_snapshot(item, cgit_query_sha1, + cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1, cgit_repobasename(cgit_repo->url), - cgit_query_name, + cgit_query_path, cgit_repo->snapshots ); return; } if (cgit_cmd == CMD_BLOB) { cgit_print_blob(item, cgit_query_sha1, cgit_query_path); return; } 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; } 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_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_TAG: cgit_print_tag(cgit_query_sha1); break; case CMD_DIFF: cgit_print_diff(cgit_query_sha1, cgit_query_sha2); 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"); chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); } if (cgit_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"); } chdir(buf); } static void cgit_check_cache(struct cacheitem *item) { int i = 0; top: if (++i > cgit_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)) { cgit_cache_root = xstrdup(argv[i]+8); } if (!strcmp(argv[i], "--nocache")) { cgit_nocache = 1; } if (!strncmp(argv[i], "--query=", 8)) { cgit_querystring = xstrdup(argv[i]+8); } if (!strncmp(argv[i], "--repo=", 7)) { cgit_query_repo = xstrdup(argv[i]+7); } if (!strncmp(argv[i], "--page=", 7)) { cgit_query_page = xstrdup(argv[i]+7); } if (!strncmp(argv[i], "--head=", 7)) { cgit_query_head = xstrdup(argv[i]+7); cgit_query_has_symref = 1; } if (!strncmp(argv[i], "--sha1=", 7)) { cgit_query_sha1 = xstrdup(argv[i]+7); cgit_query_has_sha1 = 1; } if (!strncmp(argv[i], "--ofs=", 6)) { cgit_query_ofs = atoi(argv[i]+6); } } } int main(int argc, const char **argv) { struct cacheitem item; const char *cgit_config_env = getenv("CGIT_CONFIG"); htmlfd = STDOUT_FILENO; 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); cgit_repo = NULL; if (getenv("SCRIPT_NAME")) cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); if (getenv("QUERY_STRING")) cgit_querystring = xstrdup(getenv("QUERY_STRING")); cgit_parse_args(argc, argv); cgit_parse_query(cgit_querystring, cgit_querystring_cb); if (!cgit_prepare_cache(&item)) return 0; if (cgit_nocache) { cgit_fill_cache(&item, 0); } else { cgit_check_cache(&item); cgit_print_cache(&item); } return 0; } @@ -1,246 +1,249 @@ #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> /* * 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 /* * 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) 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 repoinfo { char *url; char *name; char *path; char *desc; char *owner; char *defbranch; char *group; char *module_link; char *readme; int snapshots; int enable_log_filecount; int enable_log_linecount; }; struct repolist { int length; int count; struct repoinfo *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; }; struct taginfo { char *tagger; char *tagger_email; int tagger_date; char *msg; }; extern const char *cgit_version; extern struct repolist cgit_repolist; extern struct repoinfo *cgit_repo; extern int cgit_cmd; extern char *cgit_root_title; extern char *cgit_css; extern char *cgit_logo; extern char *cgit_index_header; 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 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_max_msg_len; extern int cgit_max_repodesc_len; extern int cgit_max_commit_count; 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_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 int htmlfd; 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 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); 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_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); extern void cgit_commit_link(char *name, char *title, char *class, char *head, char *rev); +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_repolist(struct cacheitem *item); extern void cgit_print_summary(); extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, 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_tag(char *revname); extern void cgit_print_diff(const char *new_hex, const char *old_hex); -extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, - const char *prefix, const char *filename, - int snapshot); -extern void cgit_print_snapshot_links(const char *repo, const char *hex,int snapshots); +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 */ diff --git a/ui-commit.c b/ui-commit.c index 50e9e11..90e09ed 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -1,225 +1,226 @@ /* ui-commit.c: generate commit view * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" static int files, slots; static int total_adds, total_rems, max_changes; static int lines_added, lines_removed; static char *curr_rev; static struct fileinfo { char status; unsigned char old_sha1[20]; unsigned char new_sha1[20]; unsigned short old_mode; unsigned short new_mode; char *old_path; char *new_path; unsigned int added; unsigned int removed; } *items; void print_fileinfo(struct fileinfo *info) { char *class; switch (info->status) { case DIFF_STATUS_ADDED: class = "add"; break; case DIFF_STATUS_COPIED: class = "cpy"; break; case DIFF_STATUS_DELETED: class = "del"; break; case DIFF_STATUS_MODIFIED: class = "upd"; break; case DIFF_STATUS_RENAMED: class = "mov"; break; case DIFF_STATUS_TYPE_CHANGED: class = "typ"; break; case DIFF_STATUS_UNKNOWN: class = "unk"; break; case DIFF_STATUS_UNMERGED: class = "stg"; break; default: die("bug: unhandled diff status %c", info->status); } html("<tr>"); htmlf("<td class='mode'>"); if (is_null_sha1(info->new_sha1)) { html_filemode(info->old_mode); } else { 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'>["); html_filemode(info->old_mode); html("]</span>"); } htmlf("</td><td class='%s'>", class); cgit_tree_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev, info->new_path); if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) htmlf(" (%s from %s)", info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", info->old_path); html("</td><td class='right'>"); htmlf("%d", info->added + info->removed); html("</td><td class='graph'>"); htmlf("<table width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); htmlf("<td class='add' style='width: %.1f%%;'/>", info->added * 100.0 / max_changes); htmlf("<td class='rem' style='width: %.1f%%;'/>", info->removed * 100.0 / max_changes); htmlf("<td class='none' style='width: %.1f%%;'/>", (max_changes - info->removed - info->added) * 100.0 / max_changes); html("</tr></table></td></tr>\n"); } void cgit_count_diff_lines(char *line, int len) { if (line && (len > 0)) { if (line[0] == '+') lines_added++; else if (line[0] == '-') lines_removed++; } } void inspect_filepair(struct diff_filepair *pair) { files++; lines_added = 0; lines_removed = 0; cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines); if (files >= slots) { if (slots == 0) slots = 4; else slots = slots * 2; items = xrealloc(items, slots * sizeof(struct fileinfo)); } items[files-1].status = pair->status; hashcpy(items[files-1].old_sha1, pair->one->sha1); hashcpy(items[files-1].new_sha1, pair->two->sha1); items[files-1].old_mode = pair->one->mode; items[files-1].new_mode = pair->two->mode; items[files-1].old_path = xstrdup(pair->one->path); items[files-1].new_path = xstrdup(pair->two->path); items[files-1].added = lines_added; items[files-1].removed = lines_removed; if (lines_added + lines_removed > max_changes) max_changes = lines_added + lines_removed; total_adds += lines_added; total_rems += lines_removed; } void cgit_print_commit(char *hex) { struct commit *commit, *parent; struct commitinfo *info; struct commit_list *p; unsigned char sha1[20]; char *tmp; int i; if (!hex) hex = cgit_query_head; curr_rev = hex; 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); html("<table class='commit-info'>\n"); html("<tr><th>author</th><td>"); html_txt(info->author); html(" "); html_txt(info->author_email); html("</td><td class='right'>"); cgit_print_date(info->author_date, FMT_LONGDATE); html("</td></tr>\n"); html("<tr><th>committer</th><td>"); html_txt(info->committer); html(" "); html_txt(info->committer_email); html("</td><td class='right'>"); cgit_print_date(info->committer_date, FMT_LONGDATE); html("</td></tr>\n"); 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, 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); if (!parent) { html("<tr><td colspan='3'>"); cgit_print_error("Error reading parent commit"); html("</td></tr>"); continue; } html("<tr><th>parent</th>" "<td colspan='2' class='sha1'>"); cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, cgit_query_head, sha1_to_hex(p->item->object.sha1)); html(" ("); cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex, sha1_to_hex(p->item->object.sha1), NULL); html(")</td></tr>"); } if (cgit_repo->snapshots) { html("<tr><th>download</th><td colspan='2' class='sha1'>"); - cgit_print_snapshot_links(cgit_query_repo,hex,cgit_repo->snapshots); + cgit_print_snapshot_links(cgit_query_repo, cgit_query_head, + hex, cgit_repo->snapshots); html("</td></tr>"); } html("</table>\n"); html("<div class='commit-subject'>"); html_txt(info->subject); html("</div>"); html("<div class='commit-msg'>"); html_txt(info->msg); html("</div>"); if (!(commit->parents && commit->parents->next && commit->parents->next->next)) { html("<div class='diffstat-header'>Diffstat</div>"); html("<table class='diffstat'>"); max_changes = 0; cgit_diff_commit(commit, inspect_filepair); for(i = 0; i<files; i++) print_fileinfo(&items[i]); html("</table>"); 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, cgit_query_head, hex, NULL, NULL); html(")</div>"); } cgit_free_commitinfo(info); } diff --git a/ui-shared.c b/ui-shared.c index ca2ee82..5c5bcf3 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1,427 +1,433 @@ /* 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" 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 (cgit_virtual_root) return fmt("%s/", cgit_virtual_root); else return cgit_script_name; } char *cgit_repourl(const char *reponame) { if (cgit_virtual_root) { return fmt("%s/%s/", cgit_virtual_root, reponame); } else { return fmt("?r=%s", reponame); } } char *cgit_fileurl(const char *reponame, const char *pagename, const char *filename, const char *query) { if (cgit_virtual_root) { if (query) return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame, pagename, filename?filename:"", query); else return fmt("%s/%s/%s/", cgit_virtual_root, reponame, pagename); } else { if (query) return fmt("?r=%s&p=%s&%s", reponame, pagename, query); else return fmt("?r=%s&p=%s", reponame, pagename); } } 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 (!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/", cgit_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 (cgit_virtual_root) { html_attr(cgit_virtual_root); if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') html("/"); html_attr(cgit_repo->url); if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } } else { html(cgit_script_name); html("?url="); html_attr(cgit_repo->url); if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } delim = "&"; } if (head && strcmp(head, cgit_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, cgit_query_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 *delim; delim = repolink(title, class, "log", head, path); if (rev && strcmp(rev, cgit_query_head)) { html(delim); html("id="); html_attr(rev); delim = "&"; } 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) > 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); } +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, cgit_query_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_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, cgit_query_head, sha1_to_hex(obj->sha1)); return; } else if (obj->type == OBJ_TREE) { page = "tree"; arg = "id"; } else { page = "blob"; arg = "id"; } 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), sha1_to_hex(obj->sha1)); html_link_close(); } void cgit_print_date(time_t secs, char *format) { char buf[64]; struct tm *time; 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; 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) { html("Content-Type: text/html; charset=utf-8\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"); html(cgit_doctype); html("<html>\n"); html("<head>\n"); html("<title>"); html_txt(title); html("</title>\n"); htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); html("<link rel='stylesheet' type='text/css' href='"); html_attr(cgit_css); html("'/>\n"); html("</head>\n"); html("<body>\n"); } void cgit_print_docend() { html("</td></tr></table>"); html("</body>\n</html>\n"); } void cgit_print_pageheader(char *title, int show_search) { html("<table id='layout'>"); html("<tr><td id='header'><a href='"); html_attr(cgit_rooturl()); html("'>"); html_txt(cgit_root_title); html("</a></td><td id='logo'>"); html("<a href='"); html_attr(cgit_logo_link); htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); html("</td></tr>"); html("<tr><td id='crumb'>"); if (cgit_query_repo) { html_txt(cgit_repo->name); html(" ("); html_txt(cgit_query_head); html(") : "); reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, NULL, NULL); html(" "); cgit_log_link("log", NULL, NULL, cgit_query_head, cgit_query_sha1, cgit_query_path, 0); html(" "); cgit_tree_link("tree", NULL, NULL, cgit_query_head, cgit_query_sha1, NULL); html(" "); cgit_commit_link("commit", NULL, NULL, cgit_query_head, cgit_query_sha1); html(" "); cgit_diff_link("diff", NULL, NULL, cgit_query_head, cgit_query_sha1, cgit_query_sha2, cgit_query_path); } else { html_txt("Index of repositories"); } html("</td>"); html("<td id='search'>"); if (show_search) { html("<form method='get' action='"); html_attr(cgit_currurl()); html("'>"); if (!cgit_virtual_root) { if (cgit_query_repo) html_hidden("r", cgit_query_repo); if (cgit_query_page) html_hidden("p", cgit_query_page); } if (cgit_query_head) html_hidden("h", cgit_query_head); if (cgit_query_sha1) html_hidden("id", cgit_query_sha1); if (cgit_query_sha2) html_hidden("id2", cgit_query_sha2); html("<input type='text' name='q' value='"); html_attr(cgit_query_search); html("'/></form>"); } html("</td></tr>"); html("<tr><td id='content' colspan='2'>"); } 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"); } /* vim:set sw=8: */ diff --git a/ui-snapshot.c b/ui-snapshot.c index d6be55b..f9879ed 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c @@ -1,145 +1,155 @@ /* 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" 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 } }; -void cgit_print_snapshot(struct cacheitem *item, const char *hex, - const char *prefix, const char *filename, - int snapshots) +void cgit_print_snapshot(struct cacheitem *item, const char *head, + const char *hex, const char *prefix, + const char *filename, int snapshots) { int fnl = strlen(filename); - int f; - for(f=0;f<(sizeof(snapshot_archives)/sizeof(*snapshot_archives));++f) { + int f, n; + + n = sizeof(snapshot_archives) / sizeof(*snapshot_archives); + for(f=0; f<n; f++) { const struct snapshot_archive_t* sat = &snapshot_archives[f]; int sl; - if(!(snapshots&sat->bit)) continue; + if(!(snapshots & sat->bit)) + continue; sl = strlen(sat->suffix); if(fnl<sl || strcmp(&filename[fnl-sl],sat->suffix)) continue; struct archiver_args args; struct commit *commit; unsigned char sha1[20]; + 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; cgit_print_snapshot_start(sat->mimetype, filename, item); (*sat->write_func)(&args); return; } cgit_print_error(fmt("Unsupported snapshot format: %s", filename)); } -void cgit_print_snapshot_links(const char *repo,const char *hex,int snapshots) +void cgit_print_snapshot_links(const char *repo, const char *head, + const char *hex, int snapshots) { char *filename; - int f; - for(f=0;f<(sizeof(snapshot_archives)/sizeof(*snapshot_archives));++f) { + int f, n; + + n = sizeof(snapshot_archives) / sizeof(*snapshot_archives); + for(f=0; f<n ;f++) { const struct snapshot_archive_t* sat = &snapshot_archives[f]; - if(!(snapshots&sat->bit)) continue; - filename = fmt("%s-%s%s",cgit_repobasename(repo),hex,sat->suffix); - htmlf("<a href='%s'>%s</a><br/>", - cgit_fileurl(repo,"snapshot",filename, - fmt("id=%s&name=%s",hex,filename)), filename); + 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) { static const char *delim = " \t,:/|;"; int f, tl, 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<(sizeof(snapshot_archives)/sizeof(*snapshot_archives));++f) { const struct snapshot_archive_t* sat = &snapshot_archives[f]; if(! ( strncmp(sat->suffix,str,tl) && strncmp(sat->suffix+1,str,tl-1) ) ) { rv |= sat->bit; break; } } str += tl; } return rv; } /* vim:set sw=8: */ |