Author: Lars Hjemli <hjemli@gmail.com>
Add support for snapshots Make a link from the commit viewer to a snapshot of the corresponding tree. Currently only zip-format is supported. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Makefile | 3 ++- cgit.c | 39 ++++++++++++++++++++++++++++++++------- cgit.h | 10 ++++++++++ git.h | 27 +++++++++++++++++++++++++++ shared.c | 17 +++++++++++++++++ ui-commit.c | 7 +++++++ ui-shared.c | 11 +++++++++++ ui-snapshot.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/Makefile b/Makefile index d826f97cf5174c302bace3ae16c54e3e586b4e83..b1c3a4ee82a540143d330eab3738ea9226700813 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,8 @@ CACHE_ROOT = /var/cache/cgit EXTLIBS = $(gitsrc)/libgit.a $(gitsrc)/xdiff/lib.a -lz -lcrypto OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ - ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o + ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o \ + ui-snapshot.o CFLAGS += -Wall diff --git a/cgit.c b/cgit.c index 0fa203cf457f4d07723c1fa0110fb2ef61106cbd..2795ecce02b8b09dd05852d75fcc59f3bb85e634 100644 --- a/cgit.c +++ b/cgit.c @@ -78,6 +78,13 @@ title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); show_search = 0; setenv("GIT_DIR", cgit_repo->path, 1); + + if (cgit_query_page && !strcmp(cgit_query_page, "snapshot")) { + cgit_print_snapshot(item, cgit_query_sha1, "zip", + cgit_repo->url, cgit_query_name); + return; + } + if (cgit_query_page && !strcmp(cgit_query_page, "log")) show_search = 1; cgit_print_docstart(title, item); @@ -85,7 +92,8 @@ cgit_print_pageheader(title, show_search); if (!cgit_query_page) { cgit_print_summary(); } else if (!strcmp(cgit_query_page, "log")) { - cgit_print_log(cgit_query_head, cgit_query_ofs, 100, cgit_query_search); + cgit_print_log(cgit_query_head, cgit_query_ofs, 100, + cgit_query_search); } else if (!strcmp(cgit_query_page, "tree")) { cgit_print_tree(cgit_query_sha1, cgit_query_path); } else if (!strcmp(cgit_query_page, "commit")) { @@ -94,21 +102,39 @@ } else if (!strcmp(cgit_query_page, "view")) { cgit_print_view(cgit_query_sha1); } else if (!strcmp(cgit_query_page, "diff")) { cgit_print_diff(cgit_query_sha1, cgit_query_sha2); + } else { + cgit_print_error("Invalid request"); } cgit_print_docend(); } -static void cgit_fill_cache(struct cacheitem *item) +static void cgit_fill_cache(struct cacheitem *item, int use_cache) { static char buf[PATH_MAX]; + int stdout2; getcwd(buf, sizeof(buf)); - htmlfd = item->fd; 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_query_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); } @@ -127,14 +153,14 @@ sleep(1); goto top; } if (!cache_exist(item)) { - cgit_fill_cache(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); + cgit_fill_cache(item, 1); cache_unlock(item); } else { cache_cancel_lock(item); @@ -209,8 +235,7 @@ cgit_parse_query(cgit_querystring, cgit_querystring_cb); if (!cgit_prepare_cache(&item)) return 0; if (cgit_nocache) { - item.fd = STDOUT_FILENO; - cgit_fill_cache(&item); + cgit_fill_cache(&item, 0); } else { cgit_check_cache(&item); cgit_print_cache(&item); diff --git a/cgit.h b/cgit.h index 2a3cd5ae7fe8603e333ce01c5e19d26b15ace092..03c2fdb7cf3f1cbb446aeb2dd7812e7face6b4f9 100644 --- a/cgit.h +++ b/cgit.h @@ -85,6 +85,7 @@ 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; @@ -92,6 +93,9 @@ 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 hextoint(char c); @@ -130,6 +134,9 @@ extern void cgit_print_date(unsigned long secs); 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(); @@ -138,5 +145,8 @@ extern void cgit_print_view(const char *hex); extern void cgit_print_tree(const char *hex, char *path); extern void cgit_print_commit(const char *hex); extern void cgit_print_diff(const char *old_hex, const char *new_hex); +extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, + const char *format, const char *prefix, + const char *filename); #endif /* CGIT_H */ diff --git a/git.h b/git.h index eca48d5b1484024e7a2f61de8b782c956513ca8f..a1d1c4bea4a5d7f27cc33eca026729e6bdedb9e6 100644 --- a/git.h +++ b/git.h @@ -669,4 +669,31 @@ int log_tree_commit(struct rev_info *, struct commit *); +/* from git:archive.h */ + +struct archiver_args { + const char *base; + struct tree *tree; + const unsigned char *commit_sha1; + time_t time; + const char **pathspec; + unsigned int verbose : 1; + void *extra; +}; + +typedef int (*write_archive_fn_t)(struct archiver_args *); + +typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv); + +struct archiver { + const char *name; + struct archiver_args args; + write_archive_fn_t write_archive; + parse_extra_args_fn_t parse_extra; +}; + +extern int write_tar_archive(struct archiver_args *); +extern int write_zip_archive(struct archiver_args *); +extern void *parse_extra_zip_args(int argc, const char **argv); + #endif /* GIT_H */ diff --git a/shared.c b/shared.c index 6fd70a80cbd6dc92f3f68859e205b648703c8103..5757d0c53b2cc2c6b869a4f0e593b2a76fa57a0a 100644 --- a/shared.c +++ b/shared.c @@ -44,9 +44,24 @@ char *cgit_query_search = 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 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; +} struct repoinfo *add_repo(const char *url) { @@ -140,6 +155,8 @@ } else if (!strcmp(name, "ofs")) { cgit_query_ofs = atoi(value); } else if (!strcmp(name, "path")) { cgit_query_path = xstrdup(value); + } else if (!strcmp(name, "name")) { + cgit_query_name = xstrdup(value); } } diff --git a/ui-commit.c b/ui-commit.c index 73fa1043e08265111da4a1c0de8e680a33d070fe..de3f2cf7bae7647f8c8b65f27fd1e74ad5133855 100644 --- a/ui-commit.c +++ b/ui-commit.c @@ -128,6 +128,7 @@ struct commitinfo *info; struct commit_list *p; unsigned char sha1[20]; char *query; + char *filename; if (get_sha1(hex, sha1)) { cgit_print_error(fmt("Bad object id: %s", hex)); @@ -168,6 +169,12 @@ html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); htmlf("'>%s</a></td></tr>\n", sha1_to_hex(p->item->object.sha1)); } + htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='"); + filename = fmt("%s-%s.zip", cgit_query_repo, hex); + html_attr(cgit_pageurl(cgit_query_repo, "snapshot", + fmt("id=%s&name=%s", hex, filename))); + htmlf("'>%s</a></td></tr>", filename); + html("</table>\n"); html("<div class='commit-subject'>"); html_txt(info->subject); diff --git a/ui-shared.c b/ui-shared.c index 3322561f77dc4b00efad598fc884bdc0b64863ea..172499ce807a16ae4c6c1b89a6c983637d59f05f 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -144,3 +144,14 @@ if (cgit_query_repo) html("</a>"); html("</td></tr><tr><td id='content'>"); } + +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"); +} diff --git a/ui-snapshot.c b/ui-snapshot.c new file mode 100644 index 0000000000000000000000000000000000000000..2257d6b9e574171c185e76a5fc21b7aeb7826882 --- /dev/null +++ b/ui-snapshot.c @@ -0,0 +1,47 @@ +/* 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 void cgit_print_zip(struct cacheitem *item, const char *hex, + const char *prefix, const char *filename) +{ + struct archiver_args args; + struct commit *commit; + unsigned char sha1[20]; + + 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("application/x-zip", filename, item); + write_zip_archive(&args); +} + + +void cgit_print_snapshot(struct cacheitem *item, const char *hex, + const char *format, const char *prefix, + const char *filename) +{ + if (!strcmp(format, "zip")) + cgit_print_zip(item, hex, prefix, filename); + else + cgit_print_error(fmt("Unsupported snapshot format: %s", + format)); +}