summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile3
-rw-r--r--cgit.c39
-rw-r--r--cgit.h10
-rw-r--r--git.h27
-rw-r--r--shared.c17
-rw-r--r--ui-commit.c7
-rw-r--r--ui-shared.c11
-rw-r--r--ui-snapshot.c47
8 files changed, 153 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index d826f97..b1c3a4e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,33 +1,34 @@
1CGIT_VERSION = 0.2 1CGIT_VERSION = 0.2
2 2
3prefix = /var/www/htdocs/cgit 3prefix = /var/www/htdocs/cgit
4gitsrc = ../git 4gitsrc = ../git
5 5
6CACHE_ROOT = /var/cache/cgit 6CACHE_ROOT = /var/cache/cgit
7EXTLIBS = $(gitsrc)/libgit.a $(gitsrc)/xdiff/lib.a -lz -lcrypto 7EXTLIBS = $(gitsrc)/libgit.a $(gitsrc)/xdiff/lib.a -lz -lcrypto
8OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 8OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
9 ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o 9 ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o \
10 ui-snapshot.o
10 11
11CFLAGS += -Wall 12CFLAGS += -Wall
12 13
13ifdef DEBUG 14ifdef DEBUG
14 CFLAGS += -g 15 CFLAGS += -g
15endif 16endif
16 17
17all: cgit 18all: cgit
18 19
19install: all clean-cache 20install: all clean-cache
20 mkdir -p $(prefix) 21 mkdir -p $(prefix)
21 install cgit $(prefix)/cgit.cgi 22 install cgit $(prefix)/cgit.cgi
22 install cgit.css $(prefix)/cgit.css 23 install cgit.css $(prefix)/cgit.css
23 24
24cgit: cgit.c cgit.h git.h $(OBJECTS) $(gitsrc)/libgit.a 25cgit: cgit.c cgit.h git.h $(OBJECTS) $(gitsrc)/libgit.a
25 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \ 26 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \
26 $(OBJECTS) $(EXTLIBS) 27 $(OBJECTS) $(EXTLIBS)
27 28
28$(OBJECTS): cgit.h git.h 29$(OBJECTS): cgit.h git.h
29 30
30ui-diff.o: xdiff.h 31ui-diff.o: xdiff.h
31 32
32$(gitsrc)/libgit.a: 33$(gitsrc)/libgit.a:
33 $(MAKE) -C $(gitsrc) 34 $(MAKE) -C $(gitsrc)
diff --git a/cgit.c b/cgit.c
index 0fa203c..2795ecc 100644
--- a/cgit.c
+++ b/cgit.c
@@ -57,105 +57,131 @@ static int cgit_prepare_cache(struct cacheitem *item)
57 else 57 else
58 item->ttl = cgit_cache_repo_ttl; 58 item->ttl = cgit_cache_repo_ttl;
59 } 59 }
60 return 1; 60 return 1;
61} 61}
62 62
63static void cgit_print_repo_page(struct cacheitem *item) 63static void cgit_print_repo_page(struct cacheitem *item)
64{ 64{
65 char *title; 65 char *title;
66 int show_search; 66 int show_search;
67 67
68 if (chdir(cgit_repo->path)) { 68 if (chdir(cgit_repo->path)) {
69 title = fmt("%s - %s", cgit_root_title, "Bad request"); 69 title = fmt("%s - %s", cgit_root_title, "Bad request");
70 cgit_print_docstart(title, item); 70 cgit_print_docstart(title, item);
71 cgit_print_pageheader(title, 0); 71 cgit_print_pageheader(title, 0);
72 cgit_print_error(fmt("Unable to scan repository: %s", 72 cgit_print_error(fmt("Unable to scan repository: %s",
73 strerror(errno))); 73 strerror(errno)));
74 cgit_print_docend(); 74 cgit_print_docend();
75 return; 75 return;
76 } 76 }
77 77
78 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 78 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
79 show_search = 0; 79 show_search = 0;
80 setenv("GIT_DIR", cgit_repo->path, 1); 80 setenv("GIT_DIR", cgit_repo->path, 1);
81
82 if (cgit_query_page && !strcmp(cgit_query_page, "snapshot")) {
83 cgit_print_snapshot(item, cgit_query_sha1, "zip",
84 cgit_repo->url, cgit_query_name);
85 return;
86 }
87
81 if (cgit_query_page && !strcmp(cgit_query_page, "log")) 88 if (cgit_query_page && !strcmp(cgit_query_page, "log"))
82 show_search = 1; 89 show_search = 1;
83 cgit_print_docstart(title, item); 90 cgit_print_docstart(title, item);
84 cgit_print_pageheader(title, show_search); 91 cgit_print_pageheader(title, show_search);
85 if (!cgit_query_page) { 92 if (!cgit_query_page) {
86 cgit_print_summary(); 93 cgit_print_summary();
87 } else if (!strcmp(cgit_query_page, "log")) { 94 } else if (!strcmp(cgit_query_page, "log")) {
88 cgit_print_log(cgit_query_head, cgit_query_ofs, 100, cgit_query_search); 95 cgit_print_log(cgit_query_head, cgit_query_ofs, 100,
96 cgit_query_search);
89 } else if (!strcmp(cgit_query_page, "tree")) { 97 } else if (!strcmp(cgit_query_page, "tree")) {
90 cgit_print_tree(cgit_query_sha1, cgit_query_path); 98 cgit_print_tree(cgit_query_sha1, cgit_query_path);
91 } else if (!strcmp(cgit_query_page, "commit")) { 99 } else if (!strcmp(cgit_query_page, "commit")) {
92 cgit_print_commit(cgit_query_sha1); 100 cgit_print_commit(cgit_query_sha1);
93 } else if (!strcmp(cgit_query_page, "view")) { 101 } else if (!strcmp(cgit_query_page, "view")) {
94 cgit_print_view(cgit_query_sha1); 102 cgit_print_view(cgit_query_sha1);
95 } else if (!strcmp(cgit_query_page, "diff")) { 103 } else if (!strcmp(cgit_query_page, "diff")) {
96 cgit_print_diff(cgit_query_sha1, cgit_query_sha2); 104 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
105 } else {
106 cgit_print_error("Invalid request");
97 } 107 }
98 cgit_print_docend(); 108 cgit_print_docend();
99} 109}
100 110
101static void cgit_fill_cache(struct cacheitem *item) 111static void cgit_fill_cache(struct cacheitem *item, int use_cache)
102{ 112{
103 static char buf[PATH_MAX]; 113 static char buf[PATH_MAX];
114 int stdout2;
104 115
105 getcwd(buf, sizeof(buf)); 116 getcwd(buf, sizeof(buf));
106 htmlfd = item->fd;
107 item->st.st_mtime = time(NULL); 117 item->st.st_mtime = time(NULL);
118
119 if (use_cache) {
120 stdout2 = chk_positive(dup(STDOUT_FILENO),
121 "Preserving STDOUT");
122 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
123 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
124 }
125
108 if (cgit_query_repo) 126 if (cgit_query_repo)
109 cgit_print_repo_page(item); 127 cgit_print_repo_page(item);
110 else 128 else
111 cgit_print_repolist(item); 129 cgit_print_repolist(item);
130
131 if (use_cache) {
132 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
133 chk_positive(dup2(stdout2, STDOUT_FILENO),
134 "Restoring original STDOUT");
135 chk_zero(close(stdout2), "Closing temporary STDOUT");
136 }
137
112 chdir(buf); 138 chdir(buf);
113} 139}
114 140
115static void cgit_check_cache(struct cacheitem *item) 141static void cgit_check_cache(struct cacheitem *item)
116{ 142{
117 int i = 0; 143 int i = 0;
118 144
119 top: 145 top:
120 if (++i > cgit_max_lock_attempts) { 146 if (++i > cgit_max_lock_attempts) {
121 die("cgit_refresh_cache: unable to lock %s: %s", 147 die("cgit_refresh_cache: unable to lock %s: %s",
122 item->name, strerror(errno)); 148 item->name, strerror(errno));
123 } 149 }
124 if (!cache_exist(item)) { 150 if (!cache_exist(item)) {
125 if (!cache_lock(item)) { 151 if (!cache_lock(item)) {
126 sleep(1); 152 sleep(1);
127 goto top; 153 goto top;
128 } 154 }
129 if (!cache_exist(item)) { 155 if (!cache_exist(item)) {
130 cgit_fill_cache(item); 156 cgit_fill_cache(item, 1);
131 cache_unlock(item); 157 cache_unlock(item);
132 } else { 158 } else {
133 cache_cancel_lock(item); 159 cache_cancel_lock(item);
134 } 160 }
135 } else if (cache_expired(item) && cache_lock(item)) { 161 } else if (cache_expired(item) && cache_lock(item)) {
136 if (cache_expired(item)) { 162 if (cache_expired(item)) {
137 cgit_fill_cache(item); 163 cgit_fill_cache(item, 1);
138 cache_unlock(item); 164 cache_unlock(item);
139 } else { 165 } else {
140 cache_cancel_lock(item); 166 cache_cancel_lock(item);
141 } 167 }
142 } 168 }
143} 169}
144 170
145static void cgit_print_cache(struct cacheitem *item) 171static void cgit_print_cache(struct cacheitem *item)
146{ 172{
147 static char buf[4096]; 173 static char buf[4096];
148 ssize_t i; 174 ssize_t i;
149 175
150 int fd = open(item->name, O_RDONLY); 176 int fd = open(item->name, O_RDONLY);
151 if (fd<0) 177 if (fd<0)
152 die("Unable to open cached file %s", item->name); 178 die("Unable to open cached file %s", item->name);
153 179
154 while((i=read(fd, buf, sizeof(buf))) > 0) 180 while((i=read(fd, buf, sizeof(buf))) > 0)
155 write(STDOUT_FILENO, buf, i); 181 write(STDOUT_FILENO, buf, i);
156 182
157 close(fd); 183 close(fd);
158} 184}
159 185
160static void cgit_parse_args(int argc, const char **argv) 186static void cgit_parse_args(int argc, const char **argv)
161{ 187{
@@ -188,32 +214,31 @@ static void cgit_parse_args(int argc, const char **argv)
188 if (!strncmp(argv[i], "--ofs=", 6)) { 214 if (!strncmp(argv[i], "--ofs=", 6)) {
189 cgit_query_ofs = atoi(argv[i]+6); 215 cgit_query_ofs = atoi(argv[i]+6);
190 } 216 }
191 } 217 }
192} 218}
193 219
194int main(int argc, const char **argv) 220int main(int argc, const char **argv)
195{ 221{
196 struct cacheitem item; 222 struct cacheitem item;
197 223
198 htmlfd = STDOUT_FILENO; 224 htmlfd = STDOUT_FILENO;
199 item.st.st_mtime = time(NULL); 225 item.st.st_mtime = time(NULL);
200 cgit_repolist.length = 0; 226 cgit_repolist.length = 0;
201 cgit_repolist.count = 0; 227 cgit_repolist.count = 0;
202 cgit_repolist.repos = NULL; 228 cgit_repolist.repos = NULL;
203 229
204 cgit_read_config("/etc/cgitrc", cgit_global_config_cb); 230 cgit_read_config("/etc/cgitrc", cgit_global_config_cb);
205 if (getenv("QUERY_STRING")) 231 if (getenv("QUERY_STRING"))
206 cgit_querystring = xstrdup(getenv("QUERY_STRING")); 232 cgit_querystring = xstrdup(getenv("QUERY_STRING"));
207 cgit_parse_args(argc, argv); 233 cgit_parse_args(argc, argv);
208 cgit_parse_query(cgit_querystring, cgit_querystring_cb); 234 cgit_parse_query(cgit_querystring, cgit_querystring_cb);
209 if (!cgit_prepare_cache(&item)) 235 if (!cgit_prepare_cache(&item))
210 return 0; 236 return 0;
211 if (cgit_nocache) { 237 if (cgit_nocache) {
212 item.fd = STDOUT_FILENO; 238 cgit_fill_cache(&item, 0);
213 cgit_fill_cache(&item);
214 } else { 239 } else {
215 cgit_check_cache(&item); 240 cgit_check_cache(&item);
216 cgit_print_cache(&item); 241 cgit_print_cache(&item);
217 } 242 }
218 return 0; 243 return 0;
219} 244}
diff --git a/cgit.h b/cgit.h
index 2a3cd5a..03c2fdb 100644
--- a/cgit.h
+++ b/cgit.h
@@ -64,79 +64,89 @@ extern int cgit_nocache;
64extern int cgit_max_lock_attempts; 64extern int cgit_max_lock_attempts;
65extern int cgit_cache_root_ttl; 65extern int cgit_cache_root_ttl;
66extern int cgit_cache_repo_ttl; 66extern int cgit_cache_repo_ttl;
67extern int cgit_cache_dynamic_ttl; 67extern int cgit_cache_dynamic_ttl;
68extern int cgit_cache_static_ttl; 68extern int cgit_cache_static_ttl;
69extern int cgit_cache_max_create_time; 69extern int cgit_cache_max_create_time;
70 70
71extern int cgit_max_msg_len; 71extern int cgit_max_msg_len;
72 72
73extern char *cgit_repo_name; 73extern char *cgit_repo_name;
74extern char *cgit_repo_desc; 74extern char *cgit_repo_desc;
75extern char *cgit_repo_owner; 75extern char *cgit_repo_owner;
76 76
77extern int cgit_query_has_symref; 77extern int cgit_query_has_symref;
78extern int cgit_query_has_sha1; 78extern int cgit_query_has_sha1;
79 79
80extern char *cgit_querystring; 80extern char *cgit_querystring;
81extern char *cgit_query_repo; 81extern char *cgit_query_repo;
82extern char *cgit_query_page; 82extern char *cgit_query_page;
83extern char *cgit_query_search; 83extern char *cgit_query_search;
84extern char *cgit_query_head; 84extern char *cgit_query_head;
85extern char *cgit_query_sha1; 85extern char *cgit_query_sha1;
86extern char *cgit_query_sha2; 86extern char *cgit_query_sha2;
87extern char *cgit_query_path; 87extern char *cgit_query_path;
88extern char *cgit_query_name;
88extern int cgit_query_ofs; 89extern int cgit_query_ofs;
89 90
90extern int htmlfd; 91extern int htmlfd;
91 92
92extern void cgit_global_config_cb(const char *name, const char *value); 93extern void cgit_global_config_cb(const char *name, const char *value);
93extern void cgit_repo_config_cb(const char *name, const char *value); 94extern void cgit_repo_config_cb(const char *name, const char *value);
94extern void cgit_querystring_cb(const char *name, const char *value); 95extern void cgit_querystring_cb(const char *name, const char *value);
95 96
97extern int chk_zero(int result, char *msg);
98extern int chk_positive(int result, char *msg);
99
96extern int hextoint(char c); 100extern int hextoint(char c);
97 101
98extern void *cgit_free_commitinfo(struct commitinfo *info); 102extern void *cgit_free_commitinfo(struct commitinfo *info);
99 103
100extern char *fmt(const char *format,...); 104extern char *fmt(const char *format,...);
101 105
102extern void html(const char *txt); 106extern void html(const char *txt);
103extern void htmlf(const char *format,...); 107extern void htmlf(const char *format,...);
104extern void html_txt(char *txt); 108extern void html_txt(char *txt);
105extern void html_ntxt(int len, char *txt); 109extern void html_ntxt(int len, char *txt);
106extern void html_attr(char *txt); 110extern void html_attr(char *txt);
107extern void html_hidden(char *name, char *value); 111extern void html_hidden(char *name, char *value);
108extern void html_link_open(char *url, char *title, char *class); 112extern void html_link_open(char *url, char *title, char *class);
109extern void html_link_close(void); 113extern void html_link_close(void);
110extern void html_filemode(unsigned short mode); 114extern void html_filemode(unsigned short mode);
111 115
112extern int cgit_read_config(const char *filename, configfn fn); 116extern int cgit_read_config(const char *filename, configfn fn);
113extern int cgit_parse_query(char *txt, configfn fn); 117extern int cgit_parse_query(char *txt, configfn fn);
114extern struct commitinfo *cgit_parse_commit(struct commit *commit); 118extern struct commitinfo *cgit_parse_commit(struct commit *commit);
115extern struct taginfo *cgit_parse_tag(struct tag *tag); 119extern struct taginfo *cgit_parse_tag(struct tag *tag);
116 120
117extern char *cache_safe_filename(const char *unsafe); 121extern char *cache_safe_filename(const char *unsafe);
118extern int cache_lock(struct cacheitem *item); 122extern int cache_lock(struct cacheitem *item);
119extern int cache_unlock(struct cacheitem *item); 123extern int cache_unlock(struct cacheitem *item);
120extern int cache_cancel_lock(struct cacheitem *item); 124extern int cache_cancel_lock(struct cacheitem *item);
121extern int cache_exist(struct cacheitem *item); 125extern int cache_exist(struct cacheitem *item);
122extern int cache_expired(struct cacheitem *item); 126extern int cache_expired(struct cacheitem *item);
123 127
124extern char *cgit_repourl(const char *reponame); 128extern char *cgit_repourl(const char *reponame);
125extern char *cgit_pageurl(const char *reponame, const char *pagename, 129extern char *cgit_pageurl(const char *reponame, const char *pagename,
126 const char *query); 130 const char *query);
127 131
128extern void cgit_print_error(char *msg); 132extern void cgit_print_error(char *msg);
129extern void cgit_print_date(unsigned long secs); 133extern void cgit_print_date(unsigned long secs);
130extern void cgit_print_docstart(char *title, struct cacheitem *item); 134extern void cgit_print_docstart(char *title, struct cacheitem *item);
131extern void cgit_print_docend(); 135extern void cgit_print_docend();
132extern void cgit_print_pageheader(char *title, int show_search); 136extern void cgit_print_pageheader(char *title, int show_search);
137extern void cgit_print_snapshot_start(const char *mimetype,
138 const char *filename,
139 struct cacheitem *item);
133 140
134extern void cgit_print_repolist(struct cacheitem *item); 141extern void cgit_print_repolist(struct cacheitem *item);
135extern void cgit_print_summary(); 142extern void cgit_print_summary();
136extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep); 143extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep);
137extern void cgit_print_view(const char *hex); 144extern void cgit_print_view(const char *hex);
138extern void cgit_print_tree(const char *hex, char *path); 145extern void cgit_print_tree(const char *hex, char *path);
139extern void cgit_print_commit(const char *hex); 146extern void cgit_print_commit(const char *hex);
140extern void cgit_print_diff(const char *old_hex, const char *new_hex); 147extern void cgit_print_diff(const char *old_hex, const char *new_hex);
148extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
149 const char *format, const char *prefix,
150 const char *filename);
141 151
142#endif /* CGIT_H */ 152#endif /* CGIT_H */
diff --git a/git.h b/git.h
index eca48d5..a1d1c4b 100644
--- a/git.h
+++ b/git.h
@@ -648,25 +648,52 @@ struct rev_info {
648 /* diff info for patches and for paths limiting */ 648 /* diff info for patches and for paths limiting */
649 struct diff_options diffopt; 649 struct diff_options diffopt;
650 struct diff_options pruning; 650 struct diff_options pruning;
651 651
652 topo_sort_set_fn_t topo_setter; 652 topo_sort_set_fn_t topo_setter;
653 topo_sort_get_fn_t topo_getter; 653 topo_sort_get_fn_t topo_getter;
654}; 654};
655 655
656 656
657extern void init_revisions(struct rev_info *revs, const char *prefix); 657extern void init_revisions(struct rev_info *revs, const char *prefix);
658extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def); 658extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
659extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename); 659extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
660 660
661extern void prepare_revision_walk(struct rev_info *revs); 661extern void prepare_revision_walk(struct rev_info *revs);
662extern struct commit *get_revision(struct rev_info *revs); 662extern struct commit *get_revision(struct rev_info *revs);
663 663
664 664
665 665
666/* from git:log-tree.h */ 666/* from git:log-tree.h */
667 667
668int log_tree_commit(struct rev_info *, struct commit *); 668int log_tree_commit(struct rev_info *, struct commit *);
669 669
670 670
671 671
672/* from git:archive.h */
673
674struct archiver_args {
675 const char *base;
676 struct tree *tree;
677 const unsigned char *commit_sha1;
678 time_t time;
679 const char **pathspec;
680 unsigned int verbose : 1;
681 void *extra;
682};
683
684typedef int (*write_archive_fn_t)(struct archiver_args *);
685
686typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);
687
688struct archiver {
689 const char *name;
690 struct archiver_args args;
691 write_archive_fn_t write_archive;
692 parse_extra_args_fn_t parse_extra;
693};
694
695extern int write_tar_archive(struct archiver_args *);
696extern int write_zip_archive(struct archiver_args *);
697extern void *parse_extra_zip_args(int argc, const char **argv);
698
672#endif /* GIT_H */ 699#endif /* GIT_H */
diff --git a/shared.c b/shared.c
index 6fd70a8..5757d0c 100644
--- a/shared.c
+++ b/shared.c
@@ -23,52 +23,67 @@ int cgit_nocache = 0;
23int cgit_max_lock_attempts = 5; 23int cgit_max_lock_attempts = 5;
24int cgit_cache_root_ttl = 5; 24int cgit_cache_root_ttl = 5;
25int cgit_cache_repo_ttl = 5; 25int cgit_cache_repo_ttl = 5;
26int cgit_cache_dynamic_ttl = 5; 26int cgit_cache_dynamic_ttl = 5;
27int cgit_cache_static_ttl = -1; 27int cgit_cache_static_ttl = -1;
28int cgit_cache_max_create_time = 5; 28int cgit_cache_max_create_time = 5;
29 29
30int cgit_max_msg_len = 60; 30int cgit_max_msg_len = 60;
31 31
32char *cgit_repo_name = NULL; 32char *cgit_repo_name = NULL;
33char *cgit_repo_desc = NULL; 33char *cgit_repo_desc = NULL;
34char *cgit_repo_owner = NULL; 34char *cgit_repo_owner = NULL;
35 35
36int cgit_query_has_symref = 0; 36int cgit_query_has_symref = 0;
37int cgit_query_has_sha1 = 0; 37int cgit_query_has_sha1 = 0;
38 38
39char *cgit_querystring = NULL; 39char *cgit_querystring = NULL;
40char *cgit_query_repo = NULL; 40char *cgit_query_repo = NULL;
41char *cgit_query_page = NULL; 41char *cgit_query_page = NULL;
42char *cgit_query_head = NULL; 42char *cgit_query_head = NULL;
43char *cgit_query_search = NULL; 43char *cgit_query_search = NULL;
44char *cgit_query_sha1 = NULL; 44char *cgit_query_sha1 = NULL;
45char *cgit_query_sha2 = NULL; 45char *cgit_query_sha2 = NULL;
46char *cgit_query_path = NULL; 46char *cgit_query_path = NULL;
47char *cgit_query_name = NULL;
47int cgit_query_ofs = 0; 48int cgit_query_ofs = 0;
48 49
49int htmlfd = 0; 50int htmlfd = 0;
50 51
52int chk_zero(int result, char *msg)
53{
54 if (result != 0)
55 die("%s: %s", msg, strerror(errno));
56 return result;
57}
58
59int chk_positive(int result, char *msg)
60{
61 if (result <= 0)
62 die("%s: %s", msg, strerror(errno));
63 return result;
64}
65
51struct repoinfo *add_repo(const char *url) 66struct repoinfo *add_repo(const char *url)
52{ 67{
53 struct repoinfo *ret; 68 struct repoinfo *ret;
54 69
55 if (++cgit_repolist.count > cgit_repolist.length) { 70 if (++cgit_repolist.count > cgit_repolist.length) {
56 if (cgit_repolist.length == 0) 71 if (cgit_repolist.length == 0)
57 cgit_repolist.length = 8; 72 cgit_repolist.length = 8;
58 else 73 else
59 cgit_repolist.length *= 2; 74 cgit_repolist.length *= 2;
60 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 75 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
61 cgit_repolist.length * 76 cgit_repolist.length *
62 sizeof(struct repoinfo)); 77 sizeof(struct repoinfo));
63 } 78 }
64 79
65 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 80 ret = &cgit_repolist.repos[cgit_repolist.count-1];
66 ret->url = xstrdup(url); 81 ret->url = xstrdup(url);
67 ret->name = ret->url; 82 ret->name = ret->url;
68 ret->path = NULL; 83 ret->path = NULL;
69 ret->desc = NULL; 84 ret->desc = NULL;
70 ret->owner = NULL; 85 ret->owner = NULL;
71 return ret; 86 return ret;
72} 87}
73 88
74void cgit_global_config_cb(const char *name, const char *value) 89void cgit_global_config_cb(const char *name, const char *value)
@@ -119,48 +134,50 @@ void cgit_repo_config_cb(const char *name, const char *value)
119 cgit_repo_owner = xstrdup(value); 134 cgit_repo_owner = xstrdup(value);
120} 135}
121 136
122void cgit_querystring_cb(const char *name, const char *value) 137void cgit_querystring_cb(const char *name, const char *value)
123{ 138{
124 if (!strcmp(name,"r")) { 139 if (!strcmp(name,"r")) {
125 cgit_query_repo = xstrdup(value); 140 cgit_query_repo = xstrdup(value);
126 } else if (!strcmp(name, "p")) { 141 } else if (!strcmp(name, "p")) {
127 cgit_query_page = xstrdup(value); 142 cgit_query_page = xstrdup(value);
128 } else if (!strcmp(name, "q")) { 143 } else if (!strcmp(name, "q")) {
129 cgit_query_search = xstrdup(value); 144 cgit_query_search = xstrdup(value);
130 } else if (!strcmp(name, "h")) { 145 } else if (!strcmp(name, "h")) {
131 cgit_query_head = xstrdup(value); 146 cgit_query_head = xstrdup(value);
132 cgit_query_has_symref = 1; 147 cgit_query_has_symref = 1;
133 } else if (!strcmp(name, "id")) { 148 } else if (!strcmp(name, "id")) {
134 cgit_query_sha1 = xstrdup(value); 149 cgit_query_sha1 = xstrdup(value);
135 cgit_query_has_sha1 = 1; 150 cgit_query_has_sha1 = 1;
136 } else if (!strcmp(name, "id2")) { 151 } else if (!strcmp(name, "id2")) {
137 cgit_query_sha2 = xstrdup(value); 152 cgit_query_sha2 = xstrdup(value);
138 cgit_query_has_sha1 = 1; 153 cgit_query_has_sha1 = 1;
139 } else if (!strcmp(name, "ofs")) { 154 } else if (!strcmp(name, "ofs")) {
140 cgit_query_ofs = atoi(value); 155 cgit_query_ofs = atoi(value);
141 } else if (!strcmp(name, "path")) { 156 } else if (!strcmp(name, "path")) {
142 cgit_query_path = xstrdup(value); 157 cgit_query_path = xstrdup(value);
158 } else if (!strcmp(name, "name")) {
159 cgit_query_name = xstrdup(value);
143 } 160 }
144} 161}
145 162
146void *cgit_free_commitinfo(struct commitinfo *info) 163void *cgit_free_commitinfo(struct commitinfo *info)
147{ 164{
148 free(info->author); 165 free(info->author);
149 free(info->author_email); 166 free(info->author_email);
150 free(info->committer); 167 free(info->committer);
151 free(info->committer_email); 168 free(info->committer_email);
152 free(info->subject); 169 free(info->subject);
153 free(info); 170 free(info);
154 return NULL; 171 return NULL;
155} 172}
156 173
157int hextoint(char c) 174int hextoint(char c)
158{ 175{
159 if (c >= 'a' && c <= 'f') 176 if (c >= 'a' && c <= 'f')
160 return 10 + c - 'a'; 177 return 10 + c - 'a';
161 else if (c >= 'A' && c <= 'F') 178 else if (c >= 'A' && c <= 'F')
162 return 10 + c - 'A'; 179 return 10 + c - 'A';
163 else if (c >= '0' && c <= '9') 180 else if (c >= '0' && c <= '9')
164 return c - '0'; 181 return c - '0';
165 else 182 else
166 return -1; 183 return -1;
diff --git a/ui-commit.c b/ui-commit.c
index 73fa104..de3f2cf 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -107,79 +107,86 @@ void cgit_diffstat(struct commit *commit)
107 opt.output_format = DIFF_FORMAT_CALLBACK; 107 opt.output_format = DIFF_FORMAT_CALLBACK;
108 opt.detect_rename = 1; 108 opt.detect_rename = 1;
109 opt.recursive = 1; 109 opt.recursive = 1;
110 opt.format_callback = diff_format_cb; 110 opt.format_callback = diff_format_cb;
111 diff_setup_done(&opt); 111 diff_setup_done(&opt);
112 112
113 if (commit->parents) 113 if (commit->parents)
114 ret = diff_tree_sha1(commit->parents->item->object.sha1, 114 ret = diff_tree_sha1(commit->parents->item->object.sha1,
115 commit->object.sha1, 115 commit->object.sha1,
116 "", &opt); 116 "", &opt);
117 else 117 else
118 ret = diff_root_tree_sha1(commit->object.sha1, "", &opt); 118 ret = diff_root_tree_sha1(commit->object.sha1, "", &opt);
119 119
120 diffcore_std(&opt); 120 diffcore_std(&opt);
121 diff_flush(&opt); 121 diff_flush(&opt);
122} 122}
123 123
124void cgit_print_commit(const char *hex) 124void cgit_print_commit(const char *hex)
125{ 125{
126 struct commit *commit; 126 struct commit *commit;
127 struct commitinfo *info; 127 struct commitinfo *info;
128 struct commit_list *p; 128 struct commit_list *p;
129 unsigned char sha1[20]; 129 unsigned char sha1[20];
130 char *query; 130 char *query;
131 char *filename;
131 132
132 if (get_sha1(hex, sha1)) { 133 if (get_sha1(hex, sha1)) {
133 cgit_print_error(fmt("Bad object id: %s", hex)); 134 cgit_print_error(fmt("Bad object id: %s", hex));
134 return; 135 return;
135 } 136 }
136 commit = lookup_commit_reference(sha1); 137 commit = lookup_commit_reference(sha1);
137 if (!commit) { 138 if (!commit) {
138 cgit_print_error(fmt("Bad commit reference: %s", hex)); 139 cgit_print_error(fmt("Bad commit reference: %s", hex));
139 return; 140 return;
140 } 141 }
141 info = cgit_parse_commit(commit); 142 info = cgit_parse_commit(commit);
142 143
143 html("<table class='commit-info'>\n"); 144 html("<table class='commit-info'>\n");
144 html("<tr><th>author</th><td>"); 145 html("<tr><th>author</th><td>");
145 html_txt(info->author); 146 html_txt(info->author);
146 html(" "); 147 html(" ");
147 html_txt(info->author_email); 148 html_txt(info->author_email);
148 html("</td><td class='right'>"); 149 html("</td><td class='right'>");
149 cgit_print_date(info->author_date); 150 cgit_print_date(info->author_date);
150 html("</td></tr>\n"); 151 html("</td></tr>\n");
151 html("<tr><th>committer</th><td>"); 152 html("<tr><th>committer</th><td>");
152 html_txt(info->committer); 153 html_txt(info->committer);
153 html(" "); 154 html(" ");
154 html_txt(info->committer_email); 155 html_txt(info->committer_email);
155 html("</td><td class='right'>"); 156 html("</td><td class='right'>");
156 cgit_print_date(info->committer_date); 157 cgit_print_date(info->committer_date);
157 html("</td></tr>\n"); 158 html("</td></tr>\n");
158 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='"); 159 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
159 query = fmt("id=%s", sha1_to_hex(commit->tree->object.sha1)); 160 query = fmt("id=%s", sha1_to_hex(commit->tree->object.sha1));
160 html_attr(cgit_pageurl(cgit_query_repo, "tree", query)); 161 html_attr(cgit_pageurl(cgit_query_repo, "tree", query));
161 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1)); 162 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
162 for (p = commit->parents; p ; p = p->next) { 163 for (p = commit->parents; p ; p = p->next) {
163 html("<tr><th>parent</th>" 164 html("<tr><th>parent</th>"
164 "<td colspan='2' class='sha1'>" 165 "<td colspan='2' class='sha1'>"
165 "<a href='"); 166 "<a href='");
166 query = fmt("id=%s", sha1_to_hex(p->item->object.sha1)); 167 query = fmt("id=%s", sha1_to_hex(p->item->object.sha1));
167 html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); 168 html_attr(cgit_pageurl(cgit_query_repo, "commit", query));
168 htmlf("'>%s</a></td></tr>\n", 169 htmlf("'>%s</a></td></tr>\n",
169 sha1_to_hex(p->item->object.sha1)); 170 sha1_to_hex(p->item->object.sha1));
170 } 171 }
172 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='");
173 filename = fmt("%s-%s.zip", cgit_query_repo, hex);
174 html_attr(cgit_pageurl(cgit_query_repo, "snapshot",
175 fmt("id=%s&name=%s", hex, filename)));
176 htmlf("'>%s</a></td></tr>", filename);
177
171 html("</table>\n"); 178 html("</table>\n");
172 html("<div class='commit-subject'>"); 179 html("<div class='commit-subject'>");
173 html_txt(info->subject); 180 html_txt(info->subject);
174 html("</div>"); 181 html("</div>");
175 html("<div class='commit-msg'>"); 182 html("<div class='commit-msg'>");
176 html_txt(info->msg); 183 html_txt(info->msg);
177 html("</div>"); 184 html("</div>");
178 html("<table class='diffstat'>"); 185 html("<table class='diffstat'>");
179 html("<tr><th colspan='3'>Affected files</tr>\n"); 186 html("<tr><th colspan='3'>Affected files</tr>\n");
180 cgit_diffstat(commit); 187 cgit_diffstat(commit);
181 htmlf("<tr><td colspan='3' class='summary'>" 188 htmlf("<tr><td colspan='3' class='summary'>"
182 "%d file%s changed</td></tr>\n", files, files > 1 ? "s" : ""); 189 "%d file%s changed</td></tr>\n", files, files > 1 ? "s" : "");
183 html("</table>"); 190 html("</table>");
184 cgit_free_commitinfo(info); 191 cgit_free_commitinfo(info);
185} 192}
diff --git a/ui-shared.c b/ui-shared.c
index 3322561..172499c 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -123,24 +123,35 @@ void cgit_print_pageheader(char *title, int show_search)
123 html("'>"); 123 html("'>");
124 if (!cgit_virtual_root) { 124 if (!cgit_virtual_root) {
125 if (cgit_query_repo) 125 if (cgit_query_repo)
126 html_hidden("r", cgit_query_repo); 126 html_hidden("r", cgit_query_repo);
127 if (cgit_query_page) 127 if (cgit_query_page)
128 html_hidden("p", cgit_query_page); 128 html_hidden("p", cgit_query_page);
129 } 129 }
130 if (cgit_query_head) 130 if (cgit_query_head)
131 html_hidden("h", cgit_query_head); 131 html_hidden("h", cgit_query_head);
132 if (cgit_query_sha1) 132 if (cgit_query_sha1)
133 html_hidden("id", cgit_query_sha1); 133 html_hidden("id", cgit_query_sha1);
134 if (cgit_query_sha2) 134 if (cgit_query_sha2)
135 html_hidden("id2", cgit_query_sha2); 135 html_hidden("id2", cgit_query_sha2);
136 html("<input type='text' name='q' value='"); 136 html("<input type='text' name='q' value='");
137 html_attr(cgit_query_search); 137 html_attr(cgit_query_search);
138 html("'/></form>"); 138 html("'/></form>");
139 } 139 }
140 if (cgit_query_repo) 140 if (cgit_query_repo)
141 htmlf("<a href='%s'>", cgit_repourl(cgit_query_repo)); 141 htmlf("<a href='%s'>", cgit_repourl(cgit_query_repo));
142 html_txt(title); 142 html_txt(title);
143 if (cgit_query_repo) 143 if (cgit_query_repo)
144 html("</a>"); 144 html("</a>");
145 html("</td></tr><tr><td id='content'>"); 145 html("</td></tr><tr><td id='content'>");
146} 146}
147
148void cgit_print_snapshot_start(const char *mimetype, const char *filename,
149 struct cacheitem *item)
150{
151 htmlf("Content-Type: %s\n", mimetype);
152 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
153 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
154 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
155 ttl_seconds(item->ttl)));
156 html("\n");
157}
diff --git a/ui-snapshot.c b/ui-snapshot.c
new file mode 100644
index 0000000..2257d6b
--- a/dev/null
+++ b/ui-snapshot.c
@@ -0,0 +1,47 @@
1/* ui-snapshot.c: generate snapshot of a commit
2 *
3 * Copyright (C) 2006 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10
11static void cgit_print_zip(struct cacheitem *item, const char *hex,
12 const char *prefix, const char *filename)
13{
14 struct archiver_args args;
15 struct commit *commit;
16 unsigned char sha1[20];
17
18 if (get_sha1(hex, sha1)) {
19 cgit_print_error(fmt("Bad object id: %s", hex));
20 return;
21 }
22 commit = lookup_commit_reference(sha1);
23
24 if (!commit) {
25 cgit_print_error(fmt("Not a commit reference: %s", hex));
26 return;
27 }
28
29 memset(&args, 0, sizeof(args));
30 args.base = fmt("%s/", prefix);
31 args.tree = commit->tree;
32
33 cgit_print_snapshot_start("application/x-zip", filename, item);
34 write_zip_archive(&args);
35}
36
37
38void cgit_print_snapshot(struct cacheitem *item, const char *hex,
39 const char *format, const char *prefix,
40 const char *filename)
41{
42 if (!strcmp(format, "zip"))
43 cgit_print_zip(item, hex, prefix, filename);
44 else
45 cgit_print_error(fmt("Unsupported snapshot format: %s",
46 format));
47}