summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c6
-rw-r--r--cgit.css1
-rw-r--r--cgit.h11
-rw-r--r--cgitrc5
-rw-r--r--shared.c11
-rw-r--r--ui-commit.c9
-rw-r--r--ui-shared.c41
-rw-r--r--ui-snapshot.c150
-rw-r--r--ui-tree.c4
9 files changed, 194 insertions, 44 deletions
diff --git a/cgit.c b/cgit.c
index 4b91829..6597529 100644
--- a/cgit.c
+++ b/cgit.c
@@ -47,50 +47,52 @@ static int cgit_prepare_cache(struct cacheitem *item)
47 47
48static void cgit_print_repo_page(struct cacheitem *item) 48static void cgit_print_repo_page(struct cacheitem *item)
49{ 49{
50 char *title; 50 char *title;
51 int show_search; 51 int show_search;
52 52
53 if (!cgit_query_head) 53 if (!cgit_query_head)
54 cgit_query_head = cgit_repo->defbranch; 54 cgit_query_head = cgit_repo->defbranch;
55 55
56 if (chdir(cgit_repo->path)) { 56 if (chdir(cgit_repo->path)) {
57 title = fmt("%s - %s", cgit_root_title, "Bad request"); 57 title = fmt("%s - %s", cgit_root_title, "Bad request");
58 cgit_print_docstart(title, item); 58 cgit_print_docstart(title, item);
59 cgit_print_pageheader(title, 0); 59 cgit_print_pageheader(title, 0);
60 cgit_print_error(fmt("Unable to scan repository: %s", 60 cgit_print_error(fmt("Unable to scan repository: %s",
61 strerror(errno))); 61 strerror(errno)));
62 cgit_print_docend(); 62 cgit_print_docend();
63 return; 63 return;
64 } 64 }
65 65
66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
67 show_search = 0; 67 show_search = 0;
68 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
69 69
70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
71 cgit_print_snapshot(item, cgit_query_sha1, "zip", 71 cgit_print_snapshot(item, cgit_query_sha1,
72 cgit_repo->url, cgit_query_name); 72 cgit_repobasename(cgit_repo->url),
73 cgit_query_name,
74 cgit_repo->snapshots );
73 return; 75 return;
74 } 76 }
75 77
76 if (cgit_cmd == CMD_BLOB) { 78 if (cgit_cmd == CMD_BLOB) {
77 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 79 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
78 return; 80 return;
79 } 81 }
80 82
81 show_search = (cgit_cmd == CMD_LOG); 83 show_search = (cgit_cmd == CMD_LOG);
82 cgit_print_docstart(title, item); 84 cgit_print_docstart(title, item);
83 if (!cgit_cmd) { 85 if (!cgit_cmd) {
84 cgit_print_pageheader("summary", show_search); 86 cgit_print_pageheader("summary", show_search);
85 cgit_print_summary(); 87 cgit_print_summary();
86 cgit_print_docend(); 88 cgit_print_docend();
87 return; 89 return;
88 } 90 }
89 91
90 cgit_print_pageheader(cgit_query_page, show_search); 92 cgit_print_pageheader(cgit_query_page, show_search);
91 93
92 switch(cgit_cmd) { 94 switch(cgit_cmd) {
93 case CMD_LOG: 95 case CMD_LOG:
94 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 96 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
95 cgit_max_commit_count, cgit_query_search, 97 cgit_max_commit_count, cgit_query_search,
96 cgit_query_path, 1); 98 cgit_query_path, 1);
diff --git a/cgit.css b/cgit.css
index 112dac1..43a40a3 100644
--- a/cgit.css
+++ b/cgit.css
@@ -210,48 +210,49 @@ table.blob td.no {
210 color: black; 210 color: black;
211 background-color: #eee; 211 background-color: #eee;
212 text-align: right; 212 text-align: right;
213} 213}
214 214
215table.blob td.txt { 215table.blob td.txt {
216 white-space: pre; 216 white-space: pre;
217 font-family: monospace; 217 font-family: monospace;
218 padding-left: 0.5em; 218 padding-left: 0.5em;
219} 219}
220 220
221table.nowrap td { 221table.nowrap td {
222 white-space: nowrap; 222 white-space: nowrap;
223} 223}
224 224
225table.commit-info { 225table.commit-info {
226 border-collapse: collapse; 226 border-collapse: collapse;
227 margin-top: 1.5em; 227 margin-top: 1.5em;
228} 228}
229 229
230table.commit-info th { 230table.commit-info th {
231 text-align: left; 231 text-align: left;
232 font-weight: normal; 232 font-weight: normal;
233 padding: 0.1em 1em 0.1em 0.1em; 233 padding: 0.1em 1em 0.1em 0.1em;
234 vertical-align: top;
234} 235}
235 236
236table.commit-info td { 237table.commit-info td {
237 font-weight: normal; 238 font-weight: normal;
238 padding: 0.1em 1em 0.1em 0.1em; 239 padding: 0.1em 1em 0.1em 0.1em;
239} 240}
240 241
241div.commit-subject { 242div.commit-subject {
242 font-weight: bold; 243 font-weight: bold;
243 font-size: 125%; 244 font-size: 125%;
244 margin: 1.5em 0em 0.5em 0em; 245 margin: 1.5em 0em 0.5em 0em;
245 padding: 0em; 246 padding: 0em;
246} 247}
247 248
248div.commit-msg { 249div.commit-msg {
249 white-space: pre; 250 white-space: pre;
250 font-family: monospace; 251 font-family: monospace;
251} 252}
252 253
253div.diffstat-header { 254div.diffstat-header {
254 font-weight: bold; 255 font-weight: bold;
255 padding-top: 1.5em; 256 padding-top: 1.5em;
256} 257}
257 258
diff --git a/cgit.h b/cgit.h
index 610a16d..eddcaa3 100644
--- a/cgit.h
+++ b/cgit.h
@@ -136,104 +136,111 @@ extern int cgit_max_commit_count;
136extern int cgit_query_has_symref; 136extern int cgit_query_has_symref;
137extern int cgit_query_has_sha1; 137extern int cgit_query_has_sha1;
138 138
139extern char *cgit_querystring; 139extern char *cgit_querystring;
140extern char *cgit_query_repo; 140extern char *cgit_query_repo;
141extern char *cgit_query_page; 141extern char *cgit_query_page;
142extern char *cgit_query_search; 142extern char *cgit_query_search;
143extern char *cgit_query_head; 143extern char *cgit_query_head;
144extern char *cgit_query_sha1; 144extern char *cgit_query_sha1;
145extern char *cgit_query_sha2; 145extern char *cgit_query_sha2;
146extern char *cgit_query_path; 146extern char *cgit_query_path;
147extern char *cgit_query_name; 147extern char *cgit_query_name;
148extern int cgit_query_ofs; 148extern int cgit_query_ofs;
149 149
150extern int htmlfd; 150extern int htmlfd;
151 151
152extern int cgit_get_cmd_index(const char *cmd); 152extern int cgit_get_cmd_index(const char *cmd);
153extern struct repoinfo *cgit_get_repoinfo(const char *url); 153extern struct repoinfo *cgit_get_repoinfo(const char *url);
154extern void cgit_global_config_cb(const char *name, const char *value); 154extern void cgit_global_config_cb(const char *name, const char *value);
155extern void cgit_repo_config_cb(const char *name, const char *value); 155extern void cgit_repo_config_cb(const char *name, const char *value);
156extern void cgit_querystring_cb(const char *name, const char *value); 156extern void cgit_querystring_cb(const char *name, const char *value);
157 157
158extern int chk_zero(int result, char *msg); 158extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 159extern int chk_positive(int result, char *msg);
160extern int chk_non_negative(int result, char *msg);
160 161
161extern int hextoint(char c); 162extern int hextoint(char c);
162extern char *trim_end(const char *str, char c); 163extern char *trim_end(const char *str, char c);
163 164
164extern void *cgit_free_commitinfo(struct commitinfo *info); 165extern void *cgit_free_commitinfo(struct commitinfo *info);
165 166
166extern int cgit_diff_files(const unsigned char *old_sha1, 167extern int cgit_diff_files(const unsigned char *old_sha1,
167 const unsigned char *new_sha1, 168 const unsigned char *new_sha1,
168 linediff_fn fn); 169 linediff_fn fn);
169 170
170extern void cgit_diff_tree(const unsigned char *old_sha1, 171extern void cgit_diff_tree(const unsigned char *old_sha1,
171 const unsigned char *new_sha1, 172 const unsigned char *new_sha1,
172 filepair_fn fn); 173 filepair_fn fn);
173 174
174extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 175extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
175 176
176extern char *fmt(const char *format,...); 177extern char *fmt(const char *format,...);
177 178
178extern void html(const char *txt); 179extern void html(const char *txt);
179extern void htmlf(const char *format,...); 180extern void htmlf(const char *format,...);
180extern void html_txt(char *txt); 181extern void html_txt(char *txt);
181extern void html_ntxt(int len, char *txt); 182extern void html_ntxt(int len, char *txt);
182extern void html_attr(char *txt); 183extern void html_attr(char *txt);
183extern void html_hidden(char *name, char *value); 184extern void html_hidden(char *name, char *value);
184extern void html_link_open(char *url, char *title, char *class); 185extern void html_link_open(char *url, char *title, char *class);
185extern void html_link_close(void); 186extern void html_link_close(void);
186extern void html_filemode(unsigned short mode); 187extern void html_filemode(unsigned short mode);
187extern int html_include(const char *filename); 188extern int html_include(const char *filename);
188 189
189extern int cgit_read_config(const char *filename, configfn fn); 190extern int cgit_read_config(const char *filename, configfn fn);
190extern int cgit_parse_query(char *txt, configfn fn); 191extern int cgit_parse_query(char *txt, configfn fn);
191extern struct commitinfo *cgit_parse_commit(struct commit *commit); 192extern struct commitinfo *cgit_parse_commit(struct commit *commit);
192extern struct taginfo *cgit_parse_tag(struct tag *tag); 193extern struct taginfo *cgit_parse_tag(struct tag *tag);
193extern void cgit_parse_url(const char *url); 194extern void cgit_parse_url(const char *url);
194 195
195extern char *cache_safe_filename(const char *unsafe); 196extern char *cache_safe_filename(const char *unsafe);
196extern int cache_lock(struct cacheitem *item); 197extern int cache_lock(struct cacheitem *item);
197extern int cache_unlock(struct cacheitem *item); 198extern int cache_unlock(struct cacheitem *item);
198extern int cache_cancel_lock(struct cacheitem *item); 199extern int cache_cancel_lock(struct cacheitem *item);
199extern int cache_exist(struct cacheitem *item); 200extern int cache_exist(struct cacheitem *item);
200extern int cache_expired(struct cacheitem *item); 201extern int cache_expired(struct cacheitem *item);
201 202
202extern char *cgit_repourl(const char *reponame); 203extern char *cgit_repourl(const char *reponame);
204extern char *cgit_fileurl(const char *reponame, const char *pagename,
205 const char *filename, const char *query);
203extern char *cgit_pageurl(const char *reponame, const char *pagename, 206extern char *cgit_pageurl(const char *reponame, const char *pagename,
204 const char *query); 207 const char *query);
205 208
209extern const char *cgit_repobasename(const char *reponame);
210
206extern void cgit_tree_link(char *name, char *title, char *class, char *head, 211extern void cgit_tree_link(char *name, char *title, char *class, char *head,
207 char *rev, char *path); 212 char *rev, char *path);
208extern void cgit_log_link(char *name, char *title, char *class, char *head, 213extern void cgit_log_link(char *name, char *title, char *class, char *head,
209 char *rev, char *path, int ofs); 214 char *rev, char *path, int ofs);
210extern void cgit_commit_link(char *name, char *title, char *class, char *head, 215extern void cgit_commit_link(char *name, char *title, char *class, char *head,
211 char *rev); 216 char *rev);
212extern void cgit_diff_link(char *name, char *title, char *class, char *head, 217extern void cgit_diff_link(char *name, char *title, char *class, char *head,
213 char *new_rev, char *old_rev, char *path); 218 char *new_rev, char *old_rev, char *path);
214 219
215extern void cgit_object_link(struct object *obj); 220extern void cgit_object_link(struct object *obj);
216 221
217extern void cgit_print_error(char *msg); 222extern void cgit_print_error(char *msg);
218extern void cgit_print_date(time_t secs, char *format); 223extern void cgit_print_date(time_t secs, char *format);
219extern void cgit_print_age(time_t t, time_t max_relative, char *format); 224extern void cgit_print_age(time_t t, time_t max_relative, char *format);
220extern void cgit_print_docstart(char *title, struct cacheitem *item); 225extern void cgit_print_docstart(char *title, struct cacheitem *item);
221extern void cgit_print_docend(); 226extern void cgit_print_docend();
222extern void cgit_print_pageheader(char *title, int show_search); 227extern void cgit_print_pageheader(char *title, int show_search);
223extern void cgit_print_snapshot_start(const char *mimetype, 228extern void cgit_print_snapshot_start(const char *mimetype,
224 const char *filename, 229 const char *filename,
225 struct cacheitem *item); 230 struct cacheitem *item);
226 231
227extern void cgit_print_repolist(struct cacheitem *item); 232extern void cgit_print_repolist(struct cacheitem *item);
228extern void cgit_print_summary(); 233extern void cgit_print_summary();
229extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 234extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
230extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 235extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
231extern void cgit_print_tree(const char *rev, char *path); 236extern void cgit_print_tree(const char *rev, char *path);
232extern void cgit_print_commit(char *hex); 237extern void cgit_print_commit(char *hex);
233extern void cgit_print_tag(char *revname); 238extern void cgit_print_tag(char *revname);
234extern void cgit_print_diff(const char *new_hex, const char *old_hex); 239extern void cgit_print_diff(const char *new_hex, const char *old_hex);
235extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 240extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
236 const char *format, const char *prefix, 241 const char *prefix, const char *filename,
237 const char *filename); 242 int snapshot);
243extern void cgit_print_snapshot_links(const char *repo, const char *hex,int snapshots);
244extern int cgit_parse_snapshots_mask(const char *str);
238 245
239#endif /* CGIT_H */ 246#endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 40877f8..1040997 100644
--- a/cgitrc
+++ b/cgitrc
@@ -1,35 +1,36 @@
1## 1##
2## cgitrc: template for /etc/cgitrc 2## cgitrc: template for /etc/cgitrc
3## 3##
4 4
5 5
6## Uncomment and set to 1 to deactivate caching of generated pages. Mostly 6## Uncomment and set to 1 to deactivate caching of generated pages. Mostly
7## usefull for testing. 7## usefull for testing.
8#nocache=0 8#nocache=0
9 9
10 10
11## Enable/disable snapshots by default. This can be overridden per repo 11## Set allowed snapshot types by default. Can be overridden per repo
12# can be any combination of zip/tar.gz/tar.bz2/tar
12#snapshots=0 13#snapshots=0
13 14
14 15
15## Enable/disable extra links to summary/log/tree per repo on index page 16## Enable/disable extra links to summary/log/tree per repo on index page
16#enable-index-links=0 17#enable-index-links=0
17 18
18 19
19## Enable/disable display of 'number of files changed' in log view 20## Enable/disable display of 'number of files changed' in log view
20#enable-log-filecount=0 21#enable-log-filecount=0
21 22
22 23
23## Enable/disable display of 'number of lines changed' in log view 24## Enable/disable display of 'number of lines changed' in log view
24#enable-log-linecount=0 25#enable-log-linecount=0
25 26
26 27
27## Enable/disable display of HEAD shortlog in summary view. Set it to maximum 28## Enable/disable display of HEAD shortlog in summary view. Set it to maximum
28## number of commits that should be displayed 29## number of commits that should be displayed
29#summary-log=0 30#summary-log=0
30 31
31 32
32## Specify a root for virtual urls. This makes cgit generate urls like 33## Specify a root for virtual urls. This makes cgit generate urls like
33## 34##
34## http://localhost/git/repo/log/?id=master 35## http://localhost/git/repo/log/?id=master
35## 36##
@@ -92,29 +93,29 @@
92## should be cached (0 for instant expiration, -1 for immortal pages) 93## should be cached (0 for instant expiration, -1 for immortal pages)
93## 94##
94 95
95## ttl for root page 96## ttl for root page
96#cache-root-ttl=5 97#cache-root-ttl=5
97 98
98## ttl for repo summary page 99## ttl for repo summary page
99#cache-repo-ttl=5 100#cache-repo-ttl=5
100 101
101## ttl for other dynamic pages 102## ttl for other dynamic pages
102#cache-dynamic-ttl=5 103#cache-dynamic-ttl=5
103 104
104## ttl for static pages (addressed by SHA-1) 105## ttl for static pages (addressed by SHA-1)
105#cache-static-ttl=-1 106#cache-static-ttl=-1
106 107
107 108
108 109
109## Example repository entry. Required values are repo.url and repo.path (each 110## Example repository entry. Required values are repo.url and repo.path (each
110## repository section must start with repo.url). 111## repository section must start with repo.url).
111#repo.url=cgit 112#repo.url=cgit
112#repo.name=cgit 113#repo.name=cgit
113#repo.desc=the caching cgi for git 114#repo.desc=the caching cgi for git
114#repo.path=/pub/git/cgit 115#repo.path=/pub/git/cgit
115#repo.owner=Lars Hjemli 116#repo.owner=Lars Hjemli
116 #repo.snapshots=1 # override a sitewide snapshot-setting 117 #repo.snapshots=tar.bz2 # override a sitewide snapshot-setting
117 #repo.enable-log-filecount=0 # override the default filecount setting 118 #repo.enable-log-filecount=0 # override the default filecount setting
118 #repo.enable-log-linecount=0 # override the default linecount setting 119 #repo.enable-log-linecount=0 # override the default linecount setting
119 #repo.module-link=/git/%s/commit/?id=%s # override the standard module-link 120 #repo.module-link=/git/%s/commit/?id=%s # override the standard module-link
120 #repo.readme=info/web/readme # specify a file to include on summary page 121 #repo.readme=info/web/readme # specify a file to include on summary page
diff --git a/shared.c b/shared.c
index 06693b0..077934f 100644
--- a/shared.c
+++ b/shared.c
@@ -65,48 +65,55 @@ int cgit_get_cmd_index(const char *cmd)
65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
66 "snapshot", "tag", NULL}; 66 "snapshot", "tag", NULL};
67 int i; 67 int i;
68 68
69 for(i = 0; cmds[i]; i++) 69 for(i = 0; cmds[i]; i++)
70 if (!strcmp(cmd, cmds[i])) 70 if (!strcmp(cmd, cmds[i]))
71 return i + 1; 71 return i + 1;
72 return 0; 72 return 0;
73} 73}
74 74
75int chk_zero(int result, char *msg) 75int chk_zero(int result, char *msg)
76{ 76{
77 if (result != 0) 77 if (result != 0)
78 die("%s: %s", msg, strerror(errno)); 78 die("%s: %s", msg, strerror(errno));
79 return result; 79 return result;
80} 80}
81 81
82int chk_positive(int result, char *msg) 82int chk_positive(int result, char *msg)
83{ 83{
84 if (result <= 0) 84 if (result <= 0)
85 die("%s: %s", msg, strerror(errno)); 85 die("%s: %s", msg, strerror(errno));
86 return result; 86 return result;
87} 87}
88 88
89int chk_non_negative(int result, char *msg)
90{
91 if (result < 0)
92 die("%s: %s",msg, strerror(errno));
93 return result;
94}
95
89struct repoinfo *add_repo(const char *url) 96struct repoinfo *add_repo(const char *url)
90{ 97{
91 struct repoinfo *ret; 98 struct repoinfo *ret;
92 99
93 if (++cgit_repolist.count > cgit_repolist.length) { 100 if (++cgit_repolist.count > cgit_repolist.length) {
94 if (cgit_repolist.length == 0) 101 if (cgit_repolist.length == 0)
95 cgit_repolist.length = 8; 102 cgit_repolist.length = 8;
96 else 103 else
97 cgit_repolist.length *= 2; 104 cgit_repolist.length *= 2;
98 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 105 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
99 cgit_repolist.length * 106 cgit_repolist.length *
100 sizeof(struct repoinfo)); 107 sizeof(struct repoinfo));
101 } 108 }
102 109
103 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 110 ret = &cgit_repolist.repos[cgit_repolist.count-1];
104 ret->url = xstrdup(url); 111 ret->url = xstrdup(url);
105 ret->name = ret->url; 112 ret->name = ret->url;
106 ret->path = NULL; 113 ret->path = NULL;
107 ret->desc = NULL; 114 ret->desc = NULL;
108 ret->owner = NULL; 115 ret->owner = NULL;
109 ret->group = cgit_repo_group; 116 ret->group = cgit_repo_group;
110 ret->defbranch = "master"; 117 ret->defbranch = "master";
111 ret->snapshots = cgit_snapshots; 118 ret->snapshots = cgit_snapshots;
112 ret->enable_log_filecount = cgit_enable_log_filecount; 119 ret->enable_log_filecount = cgit_enable_log_filecount;
@@ -127,91 +134,91 @@ struct repoinfo *cgit_get_repoinfo(const char *url)
127 return repo; 134 return repo;
128 } 135 }
129 return NULL; 136 return NULL;
130} 137}
131 138
132void cgit_global_config_cb(const char *name, const char *value) 139void cgit_global_config_cb(const char *name, const char *value)
133{ 140{
134 if (!strcmp(name, "root-title")) 141 if (!strcmp(name, "root-title"))
135 cgit_root_title = xstrdup(value); 142 cgit_root_title = xstrdup(value);
136 else if (!strcmp(name, "css")) 143 else if (!strcmp(name, "css"))
137 cgit_css = xstrdup(value); 144 cgit_css = xstrdup(value);
138 else if (!strcmp(name, "logo")) 145 else if (!strcmp(name, "logo"))
139 cgit_logo = xstrdup(value); 146 cgit_logo = xstrdup(value);
140 else if (!strcmp(name, "index-header")) 147 else if (!strcmp(name, "index-header"))
141 cgit_index_header = xstrdup(value); 148 cgit_index_header = xstrdup(value);
142 else if (!strcmp(name, "logo-link")) 149 else if (!strcmp(name, "logo-link"))
143 cgit_logo_link = xstrdup(value); 150 cgit_logo_link = xstrdup(value);
144 else if (!strcmp(name, "module-link")) 151 else if (!strcmp(name, "module-link"))
145 cgit_module_link = xstrdup(value); 152 cgit_module_link = xstrdup(value);
146 else if (!strcmp(name, "virtual-root")) 153 else if (!strcmp(name, "virtual-root"))
147 cgit_virtual_root = xstrdup(value); 154 cgit_virtual_root = xstrdup(value);
148 else if (!strcmp(name, "nocache")) 155 else if (!strcmp(name, "nocache"))
149 cgit_nocache = atoi(value); 156 cgit_nocache = atoi(value);
150 else if (!strcmp(name, "snapshots")) 157 else if (!strcmp(name, "snapshots"))
151 cgit_snapshots = atoi(value); 158 cgit_snapshots = cgit_parse_snapshots_mask(value);
152 else if (!strcmp(name, "enable-index-links")) 159 else if (!strcmp(name, "enable-index-links"))
153 cgit_enable_index_links = atoi(value); 160 cgit_enable_index_links = atoi(value);
154 else if (!strcmp(name, "enable-log-filecount")) 161 else if (!strcmp(name, "enable-log-filecount"))
155 cgit_enable_log_filecount = atoi(value); 162 cgit_enable_log_filecount = atoi(value);
156 else if (!strcmp(name, "enable-log-linecount")) 163 else if (!strcmp(name, "enable-log-linecount"))
157 cgit_enable_log_linecount = atoi(value); 164 cgit_enable_log_linecount = atoi(value);
158 else if (!strcmp(name, "cache-root")) 165 else if (!strcmp(name, "cache-root"))
159 cgit_cache_root = xstrdup(value); 166 cgit_cache_root = xstrdup(value);
160 else if (!strcmp(name, "cache-root-ttl")) 167 else if (!strcmp(name, "cache-root-ttl"))
161 cgit_cache_root_ttl = atoi(value); 168 cgit_cache_root_ttl = atoi(value);
162 else if (!strcmp(name, "cache-repo-ttl")) 169 else if (!strcmp(name, "cache-repo-ttl"))
163 cgit_cache_repo_ttl = atoi(value); 170 cgit_cache_repo_ttl = atoi(value);
164 else if (!strcmp(name, "cache-static-ttl")) 171 else if (!strcmp(name, "cache-static-ttl"))
165 cgit_cache_static_ttl = atoi(value); 172 cgit_cache_static_ttl = atoi(value);
166 else if (!strcmp(name, "cache-dynamic-ttl")) 173 else if (!strcmp(name, "cache-dynamic-ttl"))
167 cgit_cache_dynamic_ttl = atoi(value); 174 cgit_cache_dynamic_ttl = atoi(value);
168 else if (!strcmp(name, "max-message-length")) 175 else if (!strcmp(name, "max-message-length"))
169 cgit_max_msg_len = atoi(value); 176 cgit_max_msg_len = atoi(value);
170 else if (!strcmp(name, "max-repodesc-length")) 177 else if (!strcmp(name, "max-repodesc-length"))
171 cgit_max_repodesc_len = atoi(value); 178 cgit_max_repodesc_len = atoi(value);
172 else if (!strcmp(name, "max-commit-count")) 179 else if (!strcmp(name, "max-commit-count"))
173 cgit_max_commit_count = atoi(value); 180 cgit_max_commit_count = atoi(value);
174 else if (!strcmp(name, "summary-log")) 181 else if (!strcmp(name, "summary-log"))
175 cgit_summary_log = atoi(value); 182 cgit_summary_log = atoi(value);
176 else if (!strcmp(name, "agefile")) 183 else if (!strcmp(name, "agefile"))
177 cgit_agefile = xstrdup(value); 184 cgit_agefile = xstrdup(value);
178 else if (!strcmp(name, "repo.group")) 185 else if (!strcmp(name, "repo.group"))
179 cgit_repo_group = xstrdup(value); 186 cgit_repo_group = xstrdup(value);
180 else if (!strcmp(name, "repo.url")) 187 else if (!strcmp(name, "repo.url"))
181 cgit_repo = add_repo(value); 188 cgit_repo = add_repo(value);
182 else if (!strcmp(name, "repo.name")) 189 else if (!strcmp(name, "repo.name"))
183 cgit_repo->name = xstrdup(value); 190 cgit_repo->name = xstrdup(value);
184 else if (cgit_repo && !strcmp(name, "repo.path")) 191 else if (cgit_repo && !strcmp(name, "repo.path"))
185 cgit_repo->path = xstrdup(value); 192 cgit_repo->path = xstrdup(value);
186 else if (cgit_repo && !strcmp(name, "repo.desc")) 193 else if (cgit_repo && !strcmp(name, "repo.desc"))
187 cgit_repo->desc = xstrdup(value); 194 cgit_repo->desc = xstrdup(value);
188 else if (cgit_repo && !strcmp(name, "repo.owner")) 195 else if (cgit_repo && !strcmp(name, "repo.owner"))
189 cgit_repo->owner = xstrdup(value); 196 cgit_repo->owner = xstrdup(value);
190 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 197 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
191 cgit_repo->defbranch = xstrdup(value); 198 cgit_repo->defbranch = xstrdup(value);
192 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 199 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
193 cgit_repo->snapshots = cgit_snapshots * atoi(value); 200 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
194 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 201 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
195 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 202 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
196 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 203 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
197 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 204 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
198 else if (cgit_repo && !strcmp(name, "repo.module-link")) 205 else if (cgit_repo && !strcmp(name, "repo.module-link"))
199 cgit_repo->module_link= xstrdup(value); 206 cgit_repo->module_link= xstrdup(value);
200 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 207 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
201 if (*value == '/') 208 if (*value == '/')
202 cgit_repo->readme = xstrdup(value); 209 cgit_repo->readme = xstrdup(value);
203 else 210 else
204 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 211 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
205 } else if (!strcmp(name, "include")) 212 } else if (!strcmp(name, "include"))
206 cgit_read_config(value, cgit_global_config_cb); 213 cgit_read_config(value, cgit_global_config_cb);
207} 214}
208 215
209void cgit_querystring_cb(const char *name, const char *value) 216void cgit_querystring_cb(const char *name, const char *value)
210{ 217{
211 if (!strcmp(name,"r")) { 218 if (!strcmp(name,"r")) {
212 cgit_query_repo = xstrdup(value); 219 cgit_query_repo = xstrdup(value);
213 cgit_repo = cgit_get_repoinfo(value); 220 cgit_repo = cgit_get_repoinfo(value);
214 } else if (!strcmp(name, "p")) { 221 } else if (!strcmp(name, "p")) {
215 cgit_query_page = xstrdup(value); 222 cgit_query_page = xstrdup(value);
216 cgit_cmd = cgit_get_cmd_index(value); 223 cgit_cmd = cgit_get_cmd_index(value);
217 } else if (!strcmp(name, "url")) { 224 } else if (!strcmp(name, "url")) {
diff --git a/ui-commit.c b/ui-commit.c
index 2679b59..50e9e11 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -118,49 +118,48 @@ void inspect_filepair(struct diff_filepair *pair)
118 items = xrealloc(items, slots * sizeof(struct fileinfo)); 118 items = xrealloc(items, slots * sizeof(struct fileinfo));
119 } 119 }
120 items[files-1].status = pair->status; 120 items[files-1].status = pair->status;
121 hashcpy(items[files-1].old_sha1, pair->one->sha1); 121 hashcpy(items[files-1].old_sha1, pair->one->sha1);
122 hashcpy(items[files-1].new_sha1, pair->two->sha1); 122 hashcpy(items[files-1].new_sha1, pair->two->sha1);
123 items[files-1].old_mode = pair->one->mode; 123 items[files-1].old_mode = pair->one->mode;
124 items[files-1].new_mode = pair->two->mode; 124 items[files-1].new_mode = pair->two->mode;
125 items[files-1].old_path = xstrdup(pair->one->path); 125 items[files-1].old_path = xstrdup(pair->one->path);
126 items[files-1].new_path = xstrdup(pair->two->path); 126 items[files-1].new_path = xstrdup(pair->two->path);
127 items[files-1].added = lines_added; 127 items[files-1].added = lines_added;
128 items[files-1].removed = lines_removed; 128 items[files-1].removed = lines_removed;
129 if (lines_added + lines_removed > max_changes) 129 if (lines_added + lines_removed > max_changes)
130 max_changes = lines_added + lines_removed; 130 max_changes = lines_added + lines_removed;
131 total_adds += lines_added; 131 total_adds += lines_added;
132 total_rems += lines_removed; 132 total_rems += lines_removed;
133} 133}
134 134
135 135
136void cgit_print_commit(char *hex) 136void cgit_print_commit(char *hex)
137{ 137{
138 struct commit *commit, *parent; 138 struct commit *commit, *parent;
139 struct commitinfo *info; 139 struct commitinfo *info;
140 struct commit_list *p; 140 struct commit_list *p;
141 unsigned char sha1[20]; 141 unsigned char sha1[20];
142 char *filename;
143 char *tmp; 142 char *tmp;
144 int i; 143 int i;
145 144
146 if (!hex) 145 if (!hex)
147 hex = cgit_query_head; 146 hex = cgit_query_head;
148 curr_rev = hex; 147 curr_rev = hex;
149 148
150 if (get_sha1(hex, sha1)) { 149 if (get_sha1(hex, sha1)) {
151 cgit_print_error(fmt("Bad object id: %s", hex)); 150 cgit_print_error(fmt("Bad object id: %s", hex));
152 return; 151 return;
153 } 152 }
154 commit = lookup_commit_reference(sha1); 153 commit = lookup_commit_reference(sha1);
155 if (!commit) { 154 if (!commit) {
156 cgit_print_error(fmt("Bad commit reference: %s", hex)); 155 cgit_print_error(fmt("Bad commit reference: %s", hex));
157 return; 156 return;
158 } 157 }
159 info = cgit_parse_commit(commit); 158 info = cgit_parse_commit(commit);
160 159
161 html("<table class='commit-info'>\n"); 160 html("<table class='commit-info'>\n");
162 html("<tr><th>author</th><td>"); 161 html("<tr><th>author</th><td>");
163 html_txt(info->author); 162 html_txt(info->author);
164 html(" "); 163 html(" ");
165 html_txt(info->author_email); 164 html_txt(info->author_email);
166 html("</td><td class='right'>"); 165 html("</td><td class='right'>");
@@ -175,53 +174,51 @@ void cgit_print_commit(char *hex)
175 html("</td></tr>\n"); 174 html("</td></tr>\n");
176 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 175 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
177 tmp = xstrdup(hex); 176 tmp = xstrdup(hex);
178 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 177 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
179 cgit_query_head, tmp, NULL); 178 cgit_query_head, tmp, NULL);
180 html("</td></tr>\n"); 179 html("</td></tr>\n");
181 for (p = commit->parents; p ; p = p->next) { 180 for (p = commit->parents; p ; p = p->next) {
182 parent = lookup_commit_reference(p->item->object.sha1); 181 parent = lookup_commit_reference(p->item->object.sha1);
183 if (!parent) { 182 if (!parent) {
184 html("<tr><td colspan='3'>"); 183 html("<tr><td colspan='3'>");
185 cgit_print_error("Error reading parent commit"); 184 cgit_print_error("Error reading parent commit");
186 html("</td></tr>"); 185 html("</td></tr>");
187 continue; 186 continue;
188 } 187 }
189 html("<tr><th>parent</th>" 188 html("<tr><th>parent</th>"
190 "<td colspan='2' class='sha1'>"); 189 "<td colspan='2' class='sha1'>");
191 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 190 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
192 cgit_query_head, sha1_to_hex(p->item->object.sha1)); 191 cgit_query_head, sha1_to_hex(p->item->object.sha1));
193 html(" ("); 192 html(" (");
194 cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex, 193 cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex,
195 sha1_to_hex(p->item->object.sha1), NULL); 194 sha1_to_hex(p->item->object.sha1), NULL);
196 html(")</td></tr>"); 195 html(")</td></tr>");
197 } 196 }
198 if (cgit_repo->snapshots) { 197 if (cgit_repo->snapshots) {
199 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='"); 198 html("<tr><th>download</th><td colspan='2' class='sha1'>");
200 filename = fmt("%s-%s.zip", cgit_query_repo, hex); 199 cgit_print_snapshot_links(cgit_query_repo,hex,cgit_repo->snapshots);
201 html_attr(cgit_pageurl(cgit_query_repo, "snapshot", 200 html("</td></tr>");
202 fmt("id=%s&amp;name=%s", hex, filename)));
203 htmlf("'>%s</a></td></tr>", filename);
204 } 201 }
205 html("</table>\n"); 202 html("</table>\n");
206 html("<div class='commit-subject'>"); 203 html("<div class='commit-subject'>");
207 html_txt(info->subject); 204 html_txt(info->subject);
208 html("</div>"); 205 html("</div>");
209 html("<div class='commit-msg'>"); 206 html("<div class='commit-msg'>");
210 html_txt(info->msg); 207 html_txt(info->msg);
211 html("</div>"); 208 html("</div>");
212 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) { 209 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) {
213 html("<div class='diffstat-header'>Diffstat</div>"); 210 html("<div class='diffstat-header'>Diffstat</div>");
214 html("<table class='diffstat'>"); 211 html("<table class='diffstat'>");
215 max_changes = 0; 212 max_changes = 0;
216 cgit_diff_commit(commit, inspect_filepair); 213 cgit_diff_commit(commit, inspect_filepair);
217 for(i = 0; i<files; i++) 214 for(i = 0; i<files; i++)
218 print_fileinfo(&items[i]); 215 print_fileinfo(&items[i]);
219 html("</table>"); 216 html("</table>");
220 html("<div class='diffstat-summary'>"); 217 html("<div class='diffstat-summary'>");
221 htmlf("%d files changed, %d insertions, %d deletions (", 218 htmlf("%d files changed, %d insertions, %d deletions (",
222 files, total_adds, total_rems); 219 files, total_adds, total_rems);
223 cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex, 220 cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex,
224 NULL, NULL); 221 NULL, NULL);
225 html(")</div>"); 222 html(")</div>");
226 } 223 }
227 cgit_free_commitinfo(info); 224 cgit_free_commitinfo(info);
diff --git a/ui-shared.c b/ui-shared.c
index fd71c12..ca2ee82 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -36,66 +36,97 @@ static long ttl_seconds(long ttl)
36void cgit_print_error(char *msg) 36void cgit_print_error(char *msg)
37{ 37{
38 html("<div class='error'>"); 38 html("<div class='error'>");
39 html_txt(msg); 39 html_txt(msg);
40 html("</div>\n"); 40 html("</div>\n");
41} 41}
42 42
43char *cgit_rooturl() 43char *cgit_rooturl()
44{ 44{
45 if (cgit_virtual_root) 45 if (cgit_virtual_root)
46 return fmt("%s/", cgit_virtual_root); 46 return fmt("%s/", cgit_virtual_root);
47 else 47 else
48 return cgit_script_name; 48 return cgit_script_name;
49} 49}
50 50
51char *cgit_repourl(const char *reponame) 51char *cgit_repourl(const char *reponame)
52{ 52{
53 if (cgit_virtual_root) { 53 if (cgit_virtual_root) {
54 return fmt("%s/%s/", cgit_virtual_root, reponame); 54 return fmt("%s/%s/", cgit_virtual_root, reponame);
55 } else { 55 } else {
56 return fmt("?r=%s", reponame); 56 return fmt("?r=%s", reponame);
57 } 57 }
58} 58}
59 59
60char *cgit_pageurl(const char *reponame, const char *pagename, 60char *cgit_fileurl(const char *reponame, const char *pagename,
61 const char *query) 61 const char *filename, const char *query)
62{ 62{
63 if (cgit_virtual_root) { 63 if (cgit_virtual_root) {
64 if (query) 64 if (query)
65 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
66 pagename, query); 66 pagename, filename?filename:"", query);
67 else 67 else
68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame, 68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
69 pagename); 69 pagename);
70 } else { 70 } else {
71 if (query) 71 if (query)
72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query); 72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query);
73 else 73 else
74 return fmt("?r=%s&amp;p=%s", reponame, pagename); 74 return fmt("?r=%s&amp;p=%s", reponame, pagename);
75 } 75 }
76} 76}
77 77
78char *cgit_pageurl(const char *reponame, const char *pagename,
79 const char *query)
80{
81 return cgit_fileurl(reponame,pagename,0,query);
82}
83
84const char *cgit_repobasename(const char *reponame)
85{
86 /* I assume we don't need to store more than one repo basename */
87 static char rvbuf[1024];
88 int p;
89 const char *rv;
90 strncpy(rvbuf,reponame,sizeof(rvbuf));
91 if(rvbuf[sizeof(rvbuf)-1])
92 die("cgit_repobasename: truncated repository name '%s'", reponame);
93 p = strlen(rvbuf)-1;
94 /* strip trailing slashes */
95 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
96 /* strip trailing .git */
97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
98 p -= 3; rvbuf[p--] = 0;
99 }
100 /* strip more trailing slashes if any */
101 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
102 /* find last slash in the remaining string */
103 rv = strrchr(rvbuf,'/');
104 if(rv)
105 return ++rv;
106 return rvbuf;
107}
108
78char *cgit_currurl() 109char *cgit_currurl()
79{ 110{
80 if (!cgit_virtual_root) 111 if (!cgit_virtual_root)
81 return cgit_script_name; 112 return cgit_script_name;
82 else if (cgit_query_page) 113 else if (cgit_query_page)
83 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); 114 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
84 else if (cgit_query_repo) 115 else if (cgit_query_repo)
85 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
86 else 117 else
87 return fmt("%s/", cgit_virtual_root); 118 return fmt("%s/", cgit_virtual_root);
88} 119}
89 120
90static char *repolink(char *title, char *class, char *page, char *head, 121static char *repolink(char *title, char *class, char *page, char *head,
91 char *path) 122 char *path)
92{ 123{
93 char *delim = "?"; 124 char *delim = "?";
94 125
95 html("<a"); 126 html("<a");
96 if (title) { 127 if (title) {
97 html(" title='"); 128 html(" title='");
98 html_attr(title); 129 html_attr(title);
99 html("'"); 130 html("'");
100 } 131 }
101 if (class) { 132 if (class) {
@@ -371,24 +402,26 @@ void cgit_print_pageheader(char *title, int show_search)
371 if (cgit_query_head) 402 if (cgit_query_head)
372 html_hidden("h", cgit_query_head); 403 html_hidden("h", cgit_query_head);
373 if (cgit_query_sha1) 404 if (cgit_query_sha1)
374 html_hidden("id", cgit_query_sha1); 405 html_hidden("id", cgit_query_sha1);
375 if (cgit_query_sha2) 406 if (cgit_query_sha2)
376 html_hidden("id2", cgit_query_sha2); 407 html_hidden("id2", cgit_query_sha2);
377 html("<input type='text' name='q' value='"); 408 html("<input type='text' name='q' value='");
378 html_attr(cgit_query_search); 409 html_attr(cgit_query_search);
379 html("'/></form>"); 410 html("'/></form>");
380 } 411 }
381 html("</td></tr>"); 412 html("</td></tr>");
382 html("<tr><td id='content' colspan='2'>"); 413 html("<tr><td id='content' colspan='2'>");
383} 414}
384 415
385void cgit_print_snapshot_start(const char *mimetype, const char *filename, 416void cgit_print_snapshot_start(const char *mimetype, const char *filename,
386 struct cacheitem *item) 417 struct cacheitem *item)
387{ 418{
388 htmlf("Content-Type: %s\n", mimetype); 419 htmlf("Content-Type: %s\n", mimetype);
389 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 420 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
390 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 421 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
391 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 422 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
392 ttl_seconds(item->ttl))); 423 ttl_seconds(item->ttl)));
393 html("\n"); 424 html("\n");
394} 425}
426
427/* vim:set sw=8: */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 2257d6b..d6be55b 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -1,47 +1,145 @@
1/* ui-snapshot.c: generate snapshot of a commit 1/* ui-snapshot.c: generate snapshot of a commit
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11static void cgit_print_zip(struct cacheitem *item, const char *hex, 11static int write_compressed_tar_archive(struct archiver_args *args,const char *filter)
12 const char *prefix, const char *filename)
13{ 12{
14 struct archiver_args args; 13 int rw[2];
15 struct commit *commit; 14 pid_t gzpid;
16 unsigned char sha1[20]; 15 int stdout2;
16 int status;
17 int rv;
17 18
18 if (get_sha1(hex, sha1)) { 19 stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing");
19 cgit_print_error(fmt("Bad object id: %s", hex)); 20 chk_zero(pipe(rw), "Opening pipe from compressor subprocess");
20 return; 21 gzpid = chk_non_negative(fork(), "Forking compressor subprocess");
22 if(gzpid==0) {
23 /* child */
24 chk_zero(close(rw[1]), "Closing write end of pipe in child");
25 chk_zero(close(STDIN_FILENO), "Closing STDIN");
26 chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin");
27 execlp(filter,filter,NULL);
28 _exit(-1);
21 } 29 }
22 commit = lookup_commit_reference(sha1); 30 /* parent */
31 chk_zero(close(rw[0]), "Closing read end of pipe");
32 chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor");
33
34 rv = write_tar_archive(args);
23 35
24 if (!commit) { 36 chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor");
25 cgit_print_error(fmt("Not a commit reference: %s", hex)); 37 chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT");
26 return; 38 chk_zero(close(stdout2), "Closing uncompressed STDOUT");
27 } 39 chk_zero(close(rw[1]), "Closing write end of pipe in parent");
40 chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process");
41 if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) )
42 cgit_print_error("Failed to compress archive");
28 43
29 memset(&args, 0, sizeof(args)); 44 return rv;
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} 45}
36 46
47static int write_tar_gzip_archive(struct archiver_args *args)
48{
49 return write_compressed_tar_archive(args,"gzip");
50}
51static int write_tar_bzip2_archive(struct archiver_args *args)
52{
53 return write_compressed_tar_archive(args,"bzip2");
54}
55
56static const struct snapshot_archive_t {
57 const char *suffix;
58 const char *mimetype;
59 write_archive_fn_t write_func;
60 int bit;
61 }snapshot_archives[] = {
62 { ".zip", "application/x-zip", write_zip_archive, 0x1 },
63 { ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 },
64 { ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 },
65 { ".tar", "application/x-tar", write_tar_archive, 0x8 }
66};
37 67
38void cgit_print_snapshot(struct cacheitem *item, const char *hex, 68void cgit_print_snapshot(struct cacheitem *item, const char *hex,
39 const char *format, const char *prefix, 69 const char *prefix, const char *filename,
40 const char *filename) 70 int snapshots)
71{
72 int fnl = strlen(filename);
73 int f;
74 for(f=0;f<(sizeof(snapshot_archives)/sizeof(*snapshot_archives));++f) {
75 const struct snapshot_archive_t* sat = &snapshot_archives[f];
76 int sl;
77 if(!(snapshots&sat->bit)) continue;
78 sl = strlen(sat->suffix);
79 if(fnl<sl || strcmp(&filename[fnl-sl],sat->suffix))
80 continue;
81
82 struct archiver_args args;
83 struct commit *commit;
84 unsigned char sha1[20];
85
86 if(get_sha1(hex, sha1)) {
87 cgit_print_error(fmt("Bad object id: %s", hex));
88 return;
89 }
90 commit = lookup_commit_reference(sha1);
91
92 if(!commit) {
93 cgit_print_error(fmt("Not a commit reference: %s", hex));
94 return;;
95 }
96
97 memset(&args,0,sizeof(args));
98 args.base = fmt("%s/", prefix);
99 args.tree = commit->tree;
100
101 cgit_print_snapshot_start(sat->mimetype, filename, item);
102 (*sat->write_func)(&args);
103 return;
104 }
105 cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
106}
107
108void cgit_print_snapshot_links(const char *repo,const char *hex,int snapshots)
41{ 109{
42 if (!strcmp(format, "zip")) 110 char *filename;
43 cgit_print_zip(item, hex, prefix, filename); 111 int f;
44 else 112 for(f=0;f<(sizeof(snapshot_archives)/sizeof(*snapshot_archives));++f) {
45 cgit_print_error(fmt("Unsupported snapshot format: %s", 113 const struct snapshot_archive_t* sat = &snapshot_archives[f];
46 format)); 114 if(!(snapshots&sat->bit)) continue;
115 filename = fmt("%s-%s%s",cgit_repobasename(repo),hex,sat->suffix);
116 htmlf("<a href='%s'>%s</a><br/>",
117 cgit_fileurl(repo,"snapshot",filename,
118 fmt("id=%s&amp;name=%s",hex,filename)), filename);
119 }
120}
121
122int cgit_parse_snapshots_mask(const char *str)
123{
124 static const char *delim = " \t,:/|;";
125 int f, tl, rv = 0;
126 /* favor legacy setting */
127 if(atoi(str)) return 1;
128 for(;;) {
129 str += strspn(str,delim);
130 tl = strcspn(str,delim);
131 if(!tl)
132 break;
133 for(f=0;f<(sizeof(snapshot_archives)/sizeof(*snapshot_archives));++f) {
134 const struct snapshot_archive_t* sat = &snapshot_archives[f];
135 if(! ( strncmp(sat->suffix,str,tl) && strncmp(sat->suffix+1,str,tl-1) ) ) {
136 rv |= sat->bit;
137 break;
138 }
139 }
140 str += tl;
141 }
142 return rv;
47} 143}
144
145/* vim:set sw=8: */
diff --git a/ui-tree.c b/ui-tree.c
index c5d64ff..75ce449 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -11,48 +11,52 @@
11char *curr_rev; 11char *curr_rev;
12char *match_path; 12char *match_path;
13int header = 0; 13int header = 0;
14 14
15static void print_object(const unsigned char *sha1, char *path) 15static void print_object(const unsigned char *sha1, char *path)
16{ 16{
17 enum object_type type; 17 enum object_type type;
18 unsigned char *buf; 18 unsigned char *buf;
19 unsigned long size, lineno, start, idx; 19 unsigned long size, lineno, start, idx;
20 20
21 type = sha1_object_info(sha1, &size); 21 type = sha1_object_info(sha1, &size);
22 if (type == OBJ_BAD) { 22 if (type == OBJ_BAD) {
23 cgit_print_error(fmt("Bad object name: %s", 23 cgit_print_error(fmt("Bad object name: %s",
24 sha1_to_hex(sha1))); 24 sha1_to_hex(sha1)));
25 return; 25 return;
26 } 26 }
27 27
28 buf = read_sha1_file(sha1, &type, &size); 28 buf = read_sha1_file(sha1, &type, &size);
29 if (!buf) { 29 if (!buf) {
30 cgit_print_error(fmt("Error reading object %s", 30 cgit_print_error(fmt("Error reading object %s",
31 sha1_to_hex(sha1))); 31 sha1_to_hex(sha1)));
32 return; 32 return;
33 } 33 }
34 34
35 html(" blob: <a href='");
36 html_attr(cgit_pageurl(cgit_query_repo, "blob", fmt("id=%s", sha1_to_hex(sha1))));
37 htmlf("'>%s</a>",sha1_to_hex(sha1));
38
35 html("<table class='blob'>\n"); 39 html("<table class='blob'>\n");
36 idx = 0; 40 idx = 0;
37 start = 0; 41 start = 0;
38 lineno = 0; 42 lineno = 0;
39 while(idx < size) { 43 while(idx < size) {
40 if (buf[idx] == '\n') { 44 if (buf[idx] == '\n') {
41 buf[idx] = '\0'; 45 buf[idx] = '\0';
42 htmlf("<tr><td class='no'>%d</td><td class='txt'>", 46 htmlf("<tr><td class='no'>%d</td><td class='txt'>",
43 ++lineno); 47 ++lineno);
44 html_txt(buf + start); 48 html_txt(buf + start);
45 html("</td></tr>\n"); 49 html("</td></tr>\n");
46 start = idx + 1; 50 start = idx + 1;
47 } 51 }
48 idx++; 52 idx++;
49 } 53 }
50 html("</table>\n"); 54 html("</table>\n");
51} 55}
52 56
53 57
54static int ls_item(const unsigned char *sha1, const char *base, int baselen, 58static int ls_item(const unsigned char *sha1, const char *base, int baselen,
55 const char *pathname, unsigned int mode, int stage) 59 const char *pathname, unsigned int mode, int stage)
56{ 60{
57 char *name; 61 char *name;
58 char *fullpath; 62 char *fullpath;