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
@@ -59,26 +59,28 @@ static void cgit_print_repo_page(struct cacheitem *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);
diff --git a/cgit.css b/cgit.css
index 112dac1..43a40a3 100644
--- a/cgit.css
+++ b/cgit.css
@@ -222,24 +222,25 @@ table.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;
diff --git a/cgit.h b/cgit.h
index 610a16d..eddcaa3 100644
--- a/cgit.h
+++ b/cgit.h
@@ -148,24 +148,25 @@ extern 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,
@@ -191,27 +192,31 @@ extern 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);
@@ -224,16 +229,18 @@ extern 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,23 +1,24 @@
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
@@ -104,17 +105,17 @@
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
@@ -77,24 +77,31 @@ int chk_zero(int result, char *msg)
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));
@@ -139,25 +146,25 @@ void cgit_global_config_cb(const char *name, const char *value)
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);
@@ -181,25 +188,25 @@ void cgit_global_config_cb(const char *name, const char *value)
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"))
diff --git a/ui-commit.c b/ui-commit.c
index 2679b59..50e9e11 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -130,25 +130,24 @@ void inspect_filepair(struct diff_filepair *pair)
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);
@@ -187,29 +186,27 @@ void cgit_print_commit(char *hex)
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;
diff --git a/ui-shared.c b/ui-shared.c
index fd71c12..ca2ee82 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -48,42 +48,73 @@ char *cgit_rooturl()
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
@@ -383,12 +414,14 @@ void cgit_print_pageheader(char *title, int show_search)
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
@@ -23,24 +23,28 @@ static void print_object(const unsigned char *sha1, char *path)
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;