summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--cgit.css149
-rw-r--r--cgit.h2
-rw-r--r--cgit.pngbin0 -> 3790 bytes
-rw-r--r--cgitrc9
-rw-r--r--shared.c31
-rw-r--r--ui-shared.c166
-rw-r--r--ui-summary.c61
7 files changed, 226 insertions, 192 deletions
diff --git a/cgit.css b/cgit.css
index 5d47099..6cf4517 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,376 +1,353 @@
1body {
2 font-family: arial, sans-serif;
3 font-size: 11pt;
4 color: black;
5 background: white;
6}
7
8body, table { 1body, table {
9 padding: 0em; 2 padding: 0em;
10 margin: 0em; 3 margin: 0em;
11} 4}
12 5
6body {
7 font-family: sans;
8 font-size: 10pt;
9 color: #333;
10 background: white;
11 padding-left: 4px;
12}
13
13table { 14table {
14 border-collapse: collapse; 15 border-collapse: collapse;
15} 16}
16 17
17h2 { 18h2 {
18 font-size: 120%; 19 font-size: 120%;
19 font-weight: bold; 20 font-weight: bold;
20 margin-top: 0em; 21 margin-top: 0em;
21 margin-bottom: 0.25em; 22 margin-bottom: 0.25em;
22} 23}
23 24
24h3 { 25h3 {
25 margin-top: 0em; 26 margin-top: 0em;
26 font-size: 100%; 27 font-size: 100%;
27 font-weight: normal; 28 font-weight: normal;
28} 29}
29 30
30h4 { 31h4 {
31 margin-top: 1.5em; 32 margin-top: 1.5em;
32 margin-bottom: 0.1em; 33 margin-bottom: 0.1em;
33 font-size: 100%; 34 font-size: 100%;
34 font-weight: bold; 35 font-weight: bold;
35} 36}
36 37
37a { 38a {
38 color: blue; 39 color: #600;
39 text-decoration: none; 40 text-decoration: none;
40} 41}
41 42
42a:hover { 43a:hover {
43 text-decoration: underline; 44 background-color: #ddd;
45 text-decoration: none;
44} 46}
45 47
46table.list { 48table.list {
47 border: none; 49 border: none;
48 border-collapse: collapse; 50 border-collapse: collapse;
49} 51}
50 52
51table.list tr { 53table.list tr {
52 background: white; 54 background: white;
53} 55}
54 56
55table.list tr:hover { 57table.list tr:hover {
56 background: #eee; 58 background: #f8f8f8;
57} 59}
58 60
59table.list tr.nohover:hover { 61table.list tr.nohover:hover {
60 background: white; 62 background: white;
61} 63}
62 64
63table.list th { 65table.list th {
64 font-weight: bold; 66 font-weight: bold;
65 border-bottom: solid 1px #777; 67 border-bottom: solid 1px #777;
66 padding: 0.1em 0.5em 0.1em 0.5em; 68 padding: 0.1em 0.5em 0.1em 0.5em;
67 vertical-align: baseline; 69 vertical-align: baseline;
68} 70}
69 71
70table.list td { 72table.list td {
71 border: none; 73 border: none;
72 padding: 0.1em 0.5em 0.1em 0.5em; 74 padding: 0.1em 0.5em 0.1em 0.5em;
73} 75}
74 76
75img { 77img {
76 border: none; 78 border: none;
77} 79}
78 80
79table#layout { 81div#sidebar {
80 width: 100%; 82 vertical-align: top;
81 border-collapse: collapse; 83 width: 162px;
82 margin: 0px; 84 padding: 0px 0px 0px 0px;
83} 85 margin: 4px;
84 86 float: left;
85td#header, td#logo {
86 color: #666;
87 background-color: #ddd;
88 border-bottom: solid 1px #000;
89}
90
91td#header {
92 font-size: 150%;
93 font-weight: bold;
94 padding: 0.2em 0.5em;
95 vertical-align: text-bottom;
96}
97
98td#header a {
99 color: #666;
100}
101
102td#header a:hover {
103 text-decoration: underline;
104} 87}
105 88
106td#logo { 89div#logo {
107 text-align: right; 90 margin: 0px;
108 vertical-align: middle; 91 padding: 4px 0px 4px 0px;
109 padding-right: 0.5em; 92 text-align: center;
93 background-color: #ccc;
94 border-top: solid 1px #eee;
95 border-left: solid 1px #eee;
96 border-right: solid 1px #aaa;
97 border-bottom: solid 1px #aaa;
110} 98}
111 99
112td#crumb, td#search { 100div#sidebar div.infobox {
113 color: #ccc; 101 margin: 0px 0px 0pax 0px;
114 border-top: solid 3px #555; 102 padding: 0.5em;
115 background-color: #666; 103 text-align: left;
116 border-bottom: solid 1px #333; 104 background-color: #ccc;
117 padding: 2px 1em; 105 border-top: solid 1px #eee;
106 border-left: solid 1px #eee;
107 border-right: solid 1px #aaa;
108 border-bottom: solid 1px #aaa;
118} 109}
119 110
120td#crumb { 111div#sidebar div.infobox h1 {
112 font-size: 11pt;
121 font-weight: bold; 113 font-weight: bold;
114 margin: 0px;
122} 115}
123 116
124td#crumb a { 117div#sidebar div.infobox a.menu {
125 color: #ccc; 118 display: block;
126 background-color: #666;
127 padding: 0em 0.5em 0em 0.5em;
128}
129
130td#crumb a:hover {
131 color: #666;
132 background-color: #ccc; 119 background-color: #ccc;
120 padding: 0.1em 0.5em;
133 text-decoration: none; 121 text-decoration: none;
134} 122}
135 123
136td#search { 124div#sidebar div.infobox a.menu:hover {
137 text-align: right; 125 background-color: #bbb;
138 vertical-align: middle; 126 text-decoration: none;
139 padding-right: 0.5em;
140}
141
142td#search form {
143 margin: 0px;
144 padding: 0px;
145} 127}
146 128
147td#search select { 129div#sidebar div.infobox select {
148 font-size: 9pt; 130 width: 100%;
131 border: solid 1px #aaa;
132 background-color: #bbb;
133 margin: 2px 0px 0px 0px;
149 padding: 0px; 134 padding: 0px;
150 border: solid 1px #333;
151 color: #333;
152 background-color: #fff;
153} 135}
154 136
155td#search input { 137div#sidebar div.infobox input.txt {
156 font-size: 9pt; 138 width: 100%;
157 padding: 0px; 139 border: solid 1px #aaa;
140 background-color: #bbb;
141 margin: 2px 0px 0px 0px;
142 padding: 0;
158} 143}
159 144
160td#search input.txt { 145table#grid {
161 width: 8em; 146 margin: 0px;
162 border: solid 1px #333;
163 color: #333;
164 background-color: #fff;
165} 147}
166 148
167td#search input.btn { 149td#content {
168 border: solid 1px #333; 150 vertical-align: top;
169 color: #333; 151 padding: 1em 2em 1em 1em;
170 background-color: #ccc; 152 border: none;
171} 153}
172 154
173div#summary { 155div#summary {
174 vertical-align: top; 156 vertical-align: top;
175 margin-bottom: 1em; 157 margin-bottom: 1em;
176} 158}
177 159
178table#downloads { 160table#downloads {
179 float: right; 161 float: right;
180 border-collapse: collapse; 162 border-collapse: collapse;
181 border: solid 1px #777; 163 border: solid 1px #777;
182 margin-left: 0.5em; 164 margin-left: 0.5em;
183 margin-bottom: 0.5em; 165 margin-bottom: 0.5em;
184} 166}
185 167
186table#downloads th { 168table#downloads th {
187 background-color: #ccc; 169 background-color: #ccc;
188} 170}
189 171
190td#content {
191 padding: 1em 0.5em;
192}
193
194div#blob { 172div#blob {
195 border: solid 1px black; 173 border: solid 1px black;
196} 174}
197 175
198div.error { 176div.error {
199 color: red; 177 color: red;
200 font-weight: bold; 178 font-weight: bold;
201 margin: 1em 2em; 179 margin: 1em 2em;
202} 180}
203 181
204a.ls-blob, a.ls-dir, a.ls-mod { 182a.ls-blob, a.ls-dir, a.ls-mod {
205 font-family: monospace; 183 font-family: monospace;
206} 184}
207 185
208td.ls-size { 186td.ls-size {
209 text-align: right; 187 text-align: right;
210} 188}
211 189
212td.ls-size { 190td.ls-size {
213 font-family: monospace; 191 font-family: monospace;
214} 192}
215 193
216td.ls-mode { 194td.ls-mode {
217 font-family: monospace; 195 font-family: monospace;
218} 196}
219 197
220table.blob { 198table.blob {
221 margin-top: 0.5em; 199 margin-top: 0.5em;
222 border-top: solid 1px black; 200 border-top: solid 1px black;
223} 201}
224 202
225table.blob td.no { 203table.blob td.no {
226 border-right: solid 1px black; 204 border-right: solid 1px black;
227 color: black; 205 color: black;
228 background-color: #eee; 206 background-color: #eee;
229 text-align: right; 207 text-align: right;
230} 208}
231 209
232table.blob td.no a { 210table.blob td.no a {
233 color: black; 211 color: black;
234} 212}
235 213
236table.blob td.no a:hover { 214table.blob td.no a:hover {
237 color: black; 215 color: black;
238 text-decoration: none; 216 text-decoration: none;
239} 217}
240 218
241table.blob td.txt { 219table.blob td.txt {
242 white-space: pre; 220 white-space: pre;
243 font-family: monospace; 221 font-family: monospace;
244 padding-left: 0.5em; 222 padding-left: 0.5em;
245} 223}
246 224
247table.nowrap td { 225table.nowrap td {
248 white-space: nowrap; 226 white-space: nowrap;
249} 227}
250 228
251table.commit-info { 229table.commit-info {
252 border-collapse: collapse; 230 border-collapse: collapse;
253 margin-top: 1.5em; 231 margin-top: 1.5em;
254} 232}
255 233
256table.commit-info th { 234table.commit-info th {
257 text-align: left; 235 text-align: left;
258 font-weight: normal; 236 font-weight: normal;
259 padding: 0.1em 1em 0.1em 0.1em; 237 padding: 0.1em 1em 0.1em 0.1em;
260 vertical-align: top; 238 vertical-align: top;
261} 239}
262 240
263table.commit-info td { 241table.commit-info td {
264 font-weight: normal; 242 font-weight: normal;
265 padding: 0.1em 1em 0.1em 0.1em; 243 padding: 0.1em 1em 0.1em 0.1em;
266} 244}
267 245
268div.commit-subject { 246div.commit-subject {
269 font-weight: bold; 247 font-weight: bold;
270 font-size: 125%; 248 font-size: 125%;
271 margin: 1.5em 0em 0.5em 0em; 249 margin: 1.5em 0em 0.5em 0em;
272 padding: 0em; 250 padding: 0em;
273} 251}
274 252
275div.commit-msg { 253div.commit-msg {
276 white-space: pre; 254 white-space: pre;
277 font-family: monospace; 255 font-family: monospace;
278} 256}
279 257
280div.diffstat-header { 258div.diffstat-header {
281 font-weight: bold; 259 font-weight: bold;
282 padding-top: 1.5em; 260 padding-top: 1.5em;
283} 261}
284 262
285table.diffstat { 263table.diffstat {
286 border-collapse: collapse; 264 border-collapse: collapse;
287 width: 100%;
288 border: solid 1px #aaa; 265 border: solid 1px #aaa;
289 background-color: #eee; 266 background-color: #eee;
290} 267}
291 268
292table.diffstat th { 269table.diffstat th {
293 font-weight: normal; 270 font-weight: normal;
294 text-align: left; 271 text-align: left;
295 text-decoration: underline; 272 text-decoration: underline;
296 padding: 0.1em 1em 0.1em 0.1em; 273 padding: 0.1em 1em 0.1em 0.1em;
297 font-size: 100%; 274 font-size: 100%;
298} 275}
299 276
300table.diffstat td { 277table.diffstat td {
301 padding: 0.2em 0.2em 0.1em 0.1em; 278 padding: 0.2em 0.2em 0.1em 0.1em;
302 font-size: 100%; 279 font-size: 100%;
303 border: none; 280 border: none;
304} 281}
305 282
306table.diffstat td.mode { 283table.diffstat td.mode {
307 white-space: nowrap; 284 white-space: nowrap;
308} 285}
309 286
310table.diffstat td span.modechange { 287table.diffstat td span.modechange {
311 padding-left: 1em; 288 padding-left: 1em;
312 color: red; 289 color: red;
313} 290}
314 291
315table.diffstat td.add a { 292table.diffstat td.add a {
316 color: green; 293 color: green;
317} 294}
318 295
319table.diffstat td.del a { 296table.diffstat td.del a {
320 color: red; 297 color: red;
321} 298}
322 299
323table.diffstat td.upd a { 300table.diffstat td.upd a {
324 color: blue; 301 color: blue;
325} 302}
326 303
327table.diffstat td.graph { 304table.diffstat td.graph {
328 width: 75%; 305 width: 500px;
329 vertical-align: middle; 306 vertical-align: middle;
330} 307}
331 308
332table.diffstat td.graph table { 309table.diffstat td.graph table {
333 border: none; 310 border: none;
334} 311}
335 312
336table.diffstat td.graph td { 313table.diffstat td.graph td {
337 padding: 0px; 314 padding: 0px;
338 border: 0px; 315 border: 0px;
339 height: 7pt; 316 height: 7pt;
340} 317}
341 318
342table.diffstat td.graph td.add { 319table.diffstat td.graph td.add {
343 background-color: #5c5; 320 background-color: #5c5;
344} 321}
345 322
346table.diffstat td.graph td.rem { 323table.diffstat td.graph td.rem {
347 background-color: #c55; 324 background-color: #c55;
348} 325}
349 326
350div.diffstat-summary { 327div.diffstat-summary {
351 color: #888; 328 color: #888;
352 padding-top: 0.5em; 329 padding-top: 0.5em;
353} 330}
354 331
355table.diff { 332table.diff {
356 width: 100%; 333 width: 100%;
357} 334}
358 335
359table.diff td { 336table.diff td {
360 font-family: monospace; 337 font-family: monospace;
361 white-space: pre; 338 white-space: pre;
362} 339}
363 340
364table.diff td div.head { 341table.diff td div.head {
365 font-weight: bold; 342 font-weight: bold;
366 margin-top: 1em; 343 margin-top: 1em;
367 background-color: #eee; 344 background-color: #eee;
368} 345}
369 346
370table.diff td div.hunk { 347table.diff td div.hunk {
371 color: #009; 348 color: #009;
372} 349}
373 350
374table.diff td div.add { 351table.diff td div.add {
375 color: green; 352 color: green;
376} 353}
diff --git a/cgit.h b/cgit.h
index b8af970..42036c3 100644
--- a/cgit.h
+++ b/cgit.h
@@ -136,96 +136,98 @@ extern char *cgit_repo_group;
136extern int cgit_nocache; 136extern int cgit_nocache;
137extern int cgit_snapshots; 137extern int cgit_snapshots;
138extern int cgit_enable_index_links; 138extern int cgit_enable_index_links;
139extern int cgit_enable_log_filecount; 139extern int cgit_enable_log_filecount;
140extern int cgit_enable_log_linecount; 140extern int cgit_enable_log_linecount;
141extern int cgit_max_lock_attempts; 141extern int cgit_max_lock_attempts;
142extern int cgit_cache_root_ttl; 142extern int cgit_cache_root_ttl;
143extern int cgit_cache_repo_ttl; 143extern int cgit_cache_repo_ttl;
144extern int cgit_cache_dynamic_ttl; 144extern int cgit_cache_dynamic_ttl;
145extern int cgit_cache_static_ttl; 145extern int cgit_cache_static_ttl;
146extern int cgit_cache_max_create_time; 146extern int cgit_cache_max_create_time;
147extern int cgit_summary_log; 147extern int cgit_summary_log;
148extern int cgit_summary_tags; 148extern int cgit_summary_tags;
149extern int cgit_summary_branches; 149extern int cgit_summary_branches;
150 150
151extern int cgit_max_msg_len; 151extern int cgit_max_msg_len;
152extern int cgit_max_repodesc_len; 152extern int cgit_max_repodesc_len;
153extern int cgit_max_commit_count; 153extern int cgit_max_commit_count;
154 154
155extern int cgit_query_has_symref; 155extern int cgit_query_has_symref;
156extern int cgit_query_has_sha1; 156extern int cgit_query_has_sha1;
157 157
158extern char *cgit_querystring; 158extern char *cgit_querystring;
159extern char *cgit_query_repo; 159extern char *cgit_query_repo;
160extern char *cgit_query_page; 160extern char *cgit_query_page;
161extern char *cgit_query_search; 161extern char *cgit_query_search;
162extern char *cgit_query_grep; 162extern char *cgit_query_grep;
163extern char *cgit_query_head; 163extern char *cgit_query_head;
164extern char *cgit_query_sha1; 164extern char *cgit_query_sha1;
165extern char *cgit_query_sha2; 165extern char *cgit_query_sha2;
166extern char *cgit_query_path; 166extern char *cgit_query_path;
167extern char *cgit_query_name; 167extern char *cgit_query_name;
168extern int cgit_query_ofs; 168extern int cgit_query_ofs;
169 169
170extern int htmlfd; 170extern int htmlfd;
171 171
172extern int cgit_get_cmd_index(const char *cmd); 172extern int cgit_get_cmd_index(const char *cmd);
173extern struct repoinfo *cgit_get_repoinfo(const char *url); 173extern struct repoinfo *cgit_get_repoinfo(const char *url);
174extern void cgit_global_config_cb(const char *name, const char *value); 174extern void cgit_global_config_cb(const char *name, const char *value);
175extern void cgit_repo_config_cb(const char *name, const char *value); 175extern void cgit_repo_config_cb(const char *name, const char *value);
176extern void cgit_querystring_cb(const char *name, const char *value); 176extern void cgit_querystring_cb(const char *name, const char *value);
177 177
178extern int chk_zero(int result, char *msg); 178extern int chk_zero(int result, char *msg);
179extern int chk_positive(int result, char *msg); 179extern int chk_positive(int result, char *msg);
180extern int chk_non_negative(int result, char *msg); 180extern int chk_non_negative(int result, char *msg);
181 181
182extern int hextoint(char c); 182extern int hextoint(char c);
183extern char *trim_end(const char *str, char c); 183extern char *trim_end(const char *str, char c);
184extern char *strlpart(char *txt, int maxlen);
185extern char *strrpart(char *txt, int maxlen);
184 186
185extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 187extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
186extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 188extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
187 int flags, void *cb_data); 189 int flags, void *cb_data);
188 190
189extern void *cgit_free_commitinfo(struct commitinfo *info); 191extern void *cgit_free_commitinfo(struct commitinfo *info);
190 192
191extern int cgit_diff_files(const unsigned char *old_sha1, 193extern int cgit_diff_files(const unsigned char *old_sha1,
192 const unsigned char *new_sha1, 194 const unsigned char *new_sha1,
193 linediff_fn fn); 195 linediff_fn fn);
194 196
195extern void cgit_diff_tree(const unsigned char *old_sha1, 197extern void cgit_diff_tree(const unsigned char *old_sha1,
196 const unsigned char *new_sha1, 198 const unsigned char *new_sha1,
197 filepair_fn fn, const char *prefix); 199 filepair_fn fn, const char *prefix);
198 200
199extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 201extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
200 202
201extern char *fmt(const char *format,...); 203extern char *fmt(const char *format,...);
202 204
203extern void html(const char *txt); 205extern void html(const char *txt);
204extern void htmlf(const char *format,...); 206extern void htmlf(const char *format,...);
205extern void html_txt(char *txt); 207extern void html_txt(char *txt);
206extern void html_ntxt(int len, char *txt); 208extern void html_ntxt(int len, char *txt);
207extern void html_attr(char *txt); 209extern void html_attr(char *txt);
208extern void html_hidden(char *name, char *value); 210extern void html_hidden(char *name, char *value);
209extern void html_option(char *value, char *text, char *selected_value); 211extern void html_option(char *value, char *text, char *selected_value);
210extern void html_link_open(char *url, char *title, char *class); 212extern void html_link_open(char *url, char *title, char *class);
211extern void html_link_close(void); 213extern void html_link_close(void);
212extern void html_filemode(unsigned short mode); 214extern void html_filemode(unsigned short mode);
213extern int html_include(const char *filename); 215extern int html_include(const char *filename);
214 216
215extern int cgit_read_config(const char *filename, configfn fn); 217extern int cgit_read_config(const char *filename, configfn fn);
216extern int cgit_parse_query(char *txt, configfn fn); 218extern int cgit_parse_query(char *txt, configfn fn);
217extern struct commitinfo *cgit_parse_commit(struct commit *commit); 219extern struct commitinfo *cgit_parse_commit(struct commit *commit);
218extern struct taginfo *cgit_parse_tag(struct tag *tag); 220extern struct taginfo *cgit_parse_tag(struct tag *tag);
219extern void cgit_parse_url(const char *url); 221extern void cgit_parse_url(const char *url);
220 222
221extern char *cache_safe_filename(const char *unsafe); 223extern char *cache_safe_filename(const char *unsafe);
222extern int cache_lock(struct cacheitem *item); 224extern int cache_lock(struct cacheitem *item);
223extern int cache_unlock(struct cacheitem *item); 225extern int cache_unlock(struct cacheitem *item);
224extern int cache_cancel_lock(struct cacheitem *item); 226extern int cache_cancel_lock(struct cacheitem *item);
225extern int cache_exist(struct cacheitem *item); 227extern int cache_exist(struct cacheitem *item);
226extern int cache_expired(struct cacheitem *item); 228extern int cache_expired(struct cacheitem *item);
227 229
228extern char *cgit_repourl(const char *reponame); 230extern char *cgit_repourl(const char *reponame);
229extern char *cgit_fileurl(const char *reponame, const char *pagename, 231extern char *cgit_fileurl(const char *reponame, const char *pagename,
230 const char *filename, const char *query); 232 const char *filename, const char *query);
231extern char *cgit_pageurl(const char *reponame, const char *pagename, 233extern char *cgit_pageurl(const char *reponame, const char *pagename,
diff --git a/cgit.png b/cgit.png
new file mode 100644
index 0000000..ee48197
--- a/dev/null
+++ b/cgit.png
Binary files differ
diff --git a/cgitrc b/cgitrc
index 2b09e01..6363c9c 100644
--- a/cgitrc
+++ b/cgitrc
@@ -40,98 +40,103 @@
40#summary-tags=0 40#summary-tags=0
41 41
42 42
43## The "Idle" column on the repository index page can read a timestamp 43## The "Idle" column on the repository index page can read a timestamp
44## from the specified agefile (if this file cannot be found, the mtime 44## from the specified agefile (if this file cannot be found, the mtime
45## of HEAD is used). 45## of HEAD is used).
46## The cgit repo on hjemli.net uses the the following command in it's 46## The cgit repo on hjemli.net uses the the following command in it's
47## post-receive hook to update the age-file: 47## post-receive hook to update the age-file:
48## git-for-each-ref --format="%(committerdate)" --sort=-committerdate \ 48## git-for-each-ref --format="%(committerdate)" --sort=-committerdate \
49## --count=1 > $GIT_DIR/info/web/last-modifie 49## --count=1 > $GIT_DIR/info/web/last-modifie
50## 50##
51#agefile=info/web/last-modified 51#agefile=info/web/last-modified
52 52
53 53
54## Git detects renames, but with a limit on the number of files to 54## Git detects renames, but with a limit on the number of files to
55## consider. This option can be used to specify another limit (or -1 to 55## consider. This option can be used to specify another limit (or -1 to
56## use the default limit). 56## use the default limit).
57## 57##
58#renamelimit=-1 58#renamelimit=-1
59 59
60 60
61## Specify a root for virtual urls. This makes cgit generate urls like 61## Specify a root for virtual urls. This makes cgit generate urls like
62## 62##
63## http://localhost/git/repo/log/?h=branch 63## http://localhost/git/repo/log/?h=branch
64## 64##
65## instead of 65## instead of
66## 66##
67## http://localhost/cgit/cgit.cgi?url=repo/log&h=branch 67## http://localhost/cgit/cgit.cgi?url=repo/log&h=branch
68## 68##
69## For this to work with apache, a rewrite rule must be added to httpd.conf, 69## For this to work with apache, a rewrite rule must be added to httpd.conf,
70## possibly looking something like this: 70## possibly looking something like this:
71## 71##
72## RewriteRule ^/git/(.*)$ /cgit/cgit.cgi?url=$1 [L,QSA] 72## RewriteRule ^/git/(.*)$ /cgit/cgit.cgi?url=$1 [L,QSA]
73## 73##
74## For this to work with lighttpd, the rewrite rule should look more like this: 74## For this to work with lighttpd, the rewrite rule should look more like this:
75## 75##
76## url.rewrite = ( 76## url.rewrite = (
77## "^/git/([^?/]+/[^?]*)?(?:\?(.*))?$" => "/cgit.cgi?url=$1&$2" 77## "^/git/([^?/]+/[^?]*)?(?:\?(.*))?$" => "/cgit.cgi?url=$1&$2"
78## ) 78## )
79## 79##
80## This setting is disabled by default. 80## This setting is disabled by default.
81#virtual-root=/git 81#virtual-root=/git
82 82
83 83
84## Set the title printed on the root page 84## Set the title printed on the root page
85#root-title=Git repository browser 85#root-title=Git repository browser
86 86
87 87
88## If specified, the file at this path will be included as HTML in the index 88## If specified, the file at this path will be included as HTML in the
89## of repositories 89## sidebar on the repository index page
90#index-info=
91
92
93## If specified, the file at this path will be included as HTML above
94## the repository index
90#index-header= 95#index-header=
91 96
92 97
93## Link to css file 98## Link to css file
94#css=/cgit/cgit.css 99#css=/cgit/cgit.css
95 100
96 101
97## Link to logo file 102## Link to logo file
98#logo=/cgit/git-logo.png 103#logo=/cgit/git-logo.png
99 104
100 105
101## Url loaded when clicking the logo 106## Url loaded when clicking the logo
102#logo-link=http://www.kernel.org/pub/software/scm/git/docs/ 107#logo-link=http://www.kernel.org/pub/software/scm/git/docs/
103 108
104 109
105## Url loaded when clicking a submodule link 110## Url loaded when clicking a submodule link
106#module-link=./?repo=%s&page=commit&id=%s 111#module-link=./?repo=%s&page=commit&id=%s
107 112
108 113
109## Number of chars shown of repo description (in repolist view) 114## Number of chars shown of repo description (in repolist view)
110#max-repodesc-length=60 115#max-repodesc-length=60
111 116
112 117
113## Number of chars shown of commit subject message (in log view) 118## Number of chars shown of commit subject message (in log view)
114#max-message-length=60 119#max-message-length=60
115 120
116 121
117## Number of commits per page in log view 122## Number of commits per page in log view
118#max-commit-count=50 123#max-commit-count=50
119 124
120 125
121## Root of cached output 126## Root of cached output
122#cache-root=/var/cache/cgit 127#cache-root=/var/cache/cgit
123 128
124 129
125## Include another config-file 130## Include another config-file
126#include=/var/cgit/repolist 131#include=/var/cgit/repolist
127 132
128## 133##
129## Time-To-Live settings: specifies how long (in minutes) different pages 134## Time-To-Live settings: specifies how long (in minutes) different pages
130## should be cached (0 for instant expiration, -1 for immortal pages) 135## should be cached (0 for instant expiration, -1 for immortal pages)
131## 136##
132 137
133## ttl for root page 138## ttl for root page
134#cache-root-ttl=5 139#cache-root-ttl=5
135 140
136## ttl for repo summary page 141## ttl for repo summary page
137#cache-repo-ttl=5 142#cache-repo-ttl=5
diff --git a/shared.c b/shared.c
index 50fe8e1..e06df91 100644
--- a/shared.c
+++ b/shared.c
@@ -258,96 +258,127 @@ void cgit_querystring_cb(const char *name, const char *value)
258 } 258 }
259} 259}
260 260
261void *cgit_free_commitinfo(struct commitinfo *info) 261void *cgit_free_commitinfo(struct commitinfo *info)
262{ 262{
263 free(info->author); 263 free(info->author);
264 free(info->author_email); 264 free(info->author_email);
265 free(info->committer); 265 free(info->committer);
266 free(info->committer_email); 266 free(info->committer_email);
267 free(info->subject); 267 free(info->subject);
268 free(info); 268 free(info);
269 return NULL; 269 return NULL;
270} 270}
271 271
272int hextoint(char c) 272int hextoint(char c)
273{ 273{
274 if (c >= 'a' && c <= 'f') 274 if (c >= 'a' && c <= 'f')
275 return 10 + c - 'a'; 275 return 10 + c - 'a';
276 else if (c >= 'A' && c <= 'F') 276 else if (c >= 'A' && c <= 'F')
277 return 10 + c - 'A'; 277 return 10 + c - 'A';
278 else if (c >= '0' && c <= '9') 278 else if (c >= '0' && c <= '9')
279 return c - '0'; 279 return c - '0';
280 else 280 else
281 return -1; 281 return -1;
282} 282}
283 283
284char *trim_end(const char *str, char c) 284char *trim_end(const char *str, char c)
285{ 285{
286 int len; 286 int len;
287 char *s, *t; 287 char *s, *t;
288 288
289 if (str == NULL) 289 if (str == NULL)
290 return NULL; 290 return NULL;
291 t = (char *)str; 291 t = (char *)str;
292 len = strlen(t); 292 len = strlen(t);
293 while(len > 0 && t[len - 1] == c) 293 while(len > 0 && t[len - 1] == c)
294 len--; 294 len--;
295 295
296 if (len == 0) 296 if (len == 0)
297 return NULL; 297 return NULL;
298 298
299 c = t[len]; 299 c = t[len];
300 t[len] = '\0'; 300 t[len] = '\0';
301 s = xstrdup(t); 301 s = xstrdup(t);
302 t[len] = c; 302 t[len] = c;
303 return s; 303 return s;
304} 304}
305 305
306char *strlpart(char *txt, int maxlen)
307{
308 char *result;
309
310 if (!txt)
311 return txt;
312
313 if (strlen(txt) <= maxlen)
314 return txt;
315 result = xmalloc(maxlen + 1);
316 memcpy(result, txt, maxlen - 3);
317 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
318 result[maxlen] = '\0';
319 return result;
320}
321
322char *strrpart(char *txt, int maxlen)
323{
324 char *result;
325
326 if (!txt)
327 return txt;
328
329 if (strlen(txt) <= maxlen)
330 return txt;
331 result = xmalloc(maxlen + 1);
332 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
333 result[0] = result[1] = result[2] = '.';
334 return result;
335}
336
306void cgit_add_ref(struct reflist *list, struct refinfo *ref) 337void cgit_add_ref(struct reflist *list, struct refinfo *ref)
307{ 338{
308 size_t size; 339 size_t size;
309 340
310 if (list->count >= list->alloc) { 341 if (list->count >= list->alloc) {
311 list->alloc += (list->alloc ? list->alloc : 4); 342 list->alloc += (list->alloc ? list->alloc : 4);
312 size = list->alloc * sizeof(struct refinfo *); 343 size = list->alloc * sizeof(struct refinfo *);
313 list->refs = xrealloc(list->refs, size); 344 list->refs = xrealloc(list->refs, size);
314 } 345 }
315 list->refs[list->count++] = ref; 346 list->refs[list->count++] = ref;
316} 347}
317 348
318struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 349struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
319{ 350{
320 struct refinfo *ref; 351 struct refinfo *ref;
321 352
322 ref = xmalloc(sizeof (struct refinfo)); 353 ref = xmalloc(sizeof (struct refinfo));
323 ref->refname = xstrdup(refname); 354 ref->refname = xstrdup(refname);
324 ref->object = parse_object(sha1); 355 ref->object = parse_object(sha1);
325 switch (ref->object->type) { 356 switch (ref->object->type) {
326 case OBJ_TAG: 357 case OBJ_TAG:
327 ref->tag = cgit_parse_tag((struct tag *)ref->object); 358 ref->tag = cgit_parse_tag((struct tag *)ref->object);
328 break; 359 break;
329 case OBJ_COMMIT: 360 case OBJ_COMMIT:
330 ref->commit = cgit_parse_commit((struct commit *)ref->object); 361 ref->commit = cgit_parse_commit((struct commit *)ref->object);
331 break; 362 break;
332 } 363 }
333 return ref; 364 return ref;
334} 365}
335 366
336int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 367int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
337 void *cb_data) 368 void *cb_data)
338{ 369{
339 struct reflist *list = (struct reflist *)cb_data; 370 struct reflist *list = (struct reflist *)cb_data;
340 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 371 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
341 372
342 if (info) 373 if (info)
343 cgit_add_ref(list, info); 374 cgit_add_ref(list, info);
344 return 0; 375 return 0;
345} 376}
346 377
347void cgit_diff_tree_cb(struct diff_queue_struct *q, 378void cgit_diff_tree_cb(struct diff_queue_struct *q,
348 struct diff_options *options, void *data) 379 struct diff_options *options, void *data)
349{ 380{
350 int i; 381 int i;
351 382
352 for (i = 0; i < q->nr; i++) { 383 for (i = 0; i < q->nr; i++) {
353 if (q->queue[i]->status == 'U') 384 if (q->queue[i]->status == 'U')
diff --git a/ui-shared.c b/ui-shared.c
index 1418010..1d66940 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -317,131 +317,203 @@ void cgit_print_age(time_t t, time_t max_relative, char *format)
317 } 317 }
318 if (secs < TM_DAY * 2) { 318 if (secs < TM_DAY * 2) {
319 htmlf("<span class='age-hours'>%.0f hours</span>", 319 htmlf("<span class='age-hours'>%.0f hours</span>",
320 secs * 1.0 / TM_HOUR); 320 secs * 1.0 / TM_HOUR);
321 return; 321 return;
322 } 322 }
323 if (secs < TM_WEEK * 2) { 323 if (secs < TM_WEEK * 2) {
324 htmlf("<span class='age-days'>%.0f days</span>", 324 htmlf("<span class='age-days'>%.0f days</span>",
325 secs * 1.0 / TM_DAY); 325 secs * 1.0 / TM_DAY);
326 return; 326 return;
327 } 327 }
328 if (secs < TM_MONTH * 2) { 328 if (secs < TM_MONTH * 2) {
329 htmlf("<span class='age-weeks'>%.0f weeks</span>", 329 htmlf("<span class='age-weeks'>%.0f weeks</span>",
330 secs * 1.0 / TM_WEEK); 330 secs * 1.0 / TM_WEEK);
331 return; 331 return;
332 } 332 }
333 if (secs < TM_YEAR * 2) { 333 if (secs < TM_YEAR * 2) {
334 htmlf("<span class='age-months'>%.0f months</span>", 334 htmlf("<span class='age-months'>%.0f months</span>",
335 secs * 1.0 / TM_MONTH); 335 secs * 1.0 / TM_MONTH);
336 return; 336 return;
337 } 337 }
338 htmlf("<span class='age-years'>%.0f years</span>", 338 htmlf("<span class='age-years'>%.0f years</span>",
339 secs * 1.0 / TM_YEAR); 339 secs * 1.0 / TM_YEAR);
340} 340}
341 341
342void cgit_print_docstart(char *title, struct cacheitem *item) 342void cgit_print_docstart(char *title, struct cacheitem *item)
343{ 343{
344 html("Content-Type: text/html; charset=utf-8\n"); 344 html("Content-Type: text/html; charset=utf-8\n");
345 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 345 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
346 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 346 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
347 ttl_seconds(item->ttl))); 347 ttl_seconds(item->ttl)));
348 html("\n"); 348 html("\n");
349 html(cgit_doctype); 349 html(cgit_doctype);
350 html("<html>\n"); 350 html("<html>\n");
351 html("<head>\n"); 351 html("<head>\n");
352 html("<title>"); 352 html("<title>");
353 html_txt(title); 353 html_txt(title);
354 html("</title>\n"); 354 html("</title>\n");
355 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 355 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
356 html("<link rel='stylesheet' type='text/css' href='"); 356 html("<link rel='stylesheet' type='text/css' href='");
357 html_attr(cgit_css); 357 html_attr(cgit_css);
358 html("'/>\n"); 358 html("'/>\n");
359 html("</head>\n"); 359 html("</head>\n");
360 html("<body>\n"); 360 html("<body>\n");
361} 361}
362 362
363void cgit_print_docend() 363void cgit_print_docend()
364{ 364{
365 html("</td></tr></table>"); 365 html("</td>\n</tr>\n<table>\n</body>\n</html>\n");
366 html("</body>\n</html>\n");
367} 366}
368 367
369void cgit_print_pageheader(char *title, int show_search) 368int print_branch_option(const char *refname, const unsigned char *sha1,
369 int flags, void *cb_data)
370{ 370{
371 html("<table id='layout'>"); 371 char *name = (char *)refname;
372 html("<tr><td id='header'><a href='"); 372 html_option(name, name, cgit_query_head);
373 html_attr(cgit_rooturl()); 373 return 0;
374 html("'>"); 374}
375 html_txt(cgit_root_title); 375
376 html("</a></td><td id='logo'>"); 376int print_archive_ref(const char *refname, const unsigned char *sha1,
377 html("<a href='"); 377 int flags, void *cb_data)
378 html_attr(cgit_logo_link); 378{
379 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 379 struct tag *tag;
380 html("</td></tr>"); 380 struct taginfo *info;
381 html("<tr><td id='crumb'>"); 381 struct object *obj;
382 if (cgit_query_repo) { 382 char buf[256], *url;
383 html_txt(cgit_repo->name); 383 unsigned char fileid[20];
384 html(" ("); 384 int *header = (int *)cb_data;
385 html_txt(cgit_query_head); 385
386 html(") : &nbsp;"); 386 if (prefixcmp(refname, "refs/archives"))
387 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, 387 return 0;
388 NULL, NULL); 388 strncpy(buf, refname+14, sizeof(buf));
389 html(" "); 389 obj = parse_object(sha1);
390 cgit_log_link("log", NULL, NULL, cgit_query_head, 390 if (!obj)
391 cgit_query_sha1, cgit_query_path, 0); 391 return 1;
392 html(" "); 392 if (obj->type == OBJ_TAG) {
393 cgit_tree_link("tree", NULL, NULL, cgit_query_head, 393 tag = lookup_tag(sha1);
394 cgit_query_sha1, NULL); 394 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
395 html(" "); 395 return 0;
396 cgit_commit_link("commit", NULL, NULL, cgit_query_head, 396 hashcpy(fileid, tag->tagged->sha1);
397 cgit_query_sha1); 397 } else if (obj->type != OBJ_BLOB) {
398 html(" "); 398 return 0;
399 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
400 cgit_query_sha1, cgit_query_sha2,
401 cgit_query_path);
402 } else { 399 } else {
403 html_txt("Index of repositories"); 400 hashcpy(fileid, sha1);
404 } 401 }
405 html("</td>"); 402 if (!*header) {
406 html("<td id='search'>"); 403 html("<p><h1>download</h1>");
407 if (show_search) { 404 *header = 1;
408 html("<form method='get' action='"); 405 }
409 html_attr(cgit_currurl()); 406 url = cgit_pageurl(cgit_query_repo, "blob",
410 html("'>"); 407 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
408 buf));
409 html_link_open(url, NULL, "menu");
410 html_txt(strlpart(buf, 20));
411 html_link_close();
412 return 0;
413}
414
415void add_hidden_formfields(int incl_head, int incl_search)
416{
411 if (!cgit_virtual_root) { 417 if (!cgit_virtual_root) {
412 if (cgit_query_repo) 418 if (cgit_query_repo)
413 html_hidden("r", cgit_query_repo); 419 html_hidden("r", cgit_query_repo);
414 if (cgit_query_page) 420 if (cgit_query_page)
415 html_hidden("p", cgit_query_page); 421 html_hidden("p", cgit_query_page);
416 } 422 }
417 if (cgit_query_head) 423
424 if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch))
418 html_hidden("h", cgit_query_head); 425 html_hidden("h", cgit_query_head);
426
419 if (cgit_query_sha1) 427 if (cgit_query_sha1)
420 html_hidden("id", cgit_query_sha1); 428 html_hidden("id", cgit_query_sha1);
421 if (cgit_query_sha2) 429 if (cgit_query_sha2)
422 html_hidden("id2", cgit_query_sha2); 430 html_hidden("id2", cgit_query_sha2);
423 html("<select name='qt'>"); 431
432 if (incl_search) {
433 if (cgit_query_grep)
434 html_hidden("qt", cgit_query_grep);
435 if (cgit_query_search)
436 html_hidden("q", cgit_query_search);
437 }
438}
439
440void cgit_print_pageheader(char *title, int show_search)
441{
442 static const char *default_info = "This is cgit, a fast webinterface for git repositories";
443 int header = 0;
444
445 html("<div id='sidebar'>\n");
446 html("<a href='");
447 html_attr(cgit_rooturl());
448 htmlf("'><div id='logo'><img src='%s' alt='cgit'/></div></a>\n",
449 cgit_logo);
450 html("<div class='infobox'>");
451 if (cgit_query_repo) {
452 html("<h1>");
453 html_txt(strrpart(cgit_repo->name, 20));
454 html("</h1>\n");
455 html_txt(cgit_repo->desc);
456 if (cgit_repo->owner) {
457 html("<p>\n<h1>owner</h1>\n");
458 html_txt(cgit_repo->owner);
459 }
460 html("<p>\n<h1>navigate</h1>\n");
461 reporevlink(NULL, "summary", NULL, "menu", cgit_query_head,
462 NULL, NULL);
463 cgit_log_link("log", NULL, "menu", cgit_query_head,
464 cgit_query_sha1, cgit_query_path, 0);
465 cgit_tree_link("tree", NULL, "menu", cgit_query_head,
466 cgit_query_sha1, NULL);
467 cgit_commit_link("commit", NULL, "menu", cgit_query_head,
468 cgit_query_sha1);
469 cgit_diff_link("diff", NULL, "menu", cgit_query_head,
470 cgit_query_sha1, cgit_query_sha2,
471 cgit_query_path);
472
473 for_each_ref(print_archive_ref, &header);
474
475 html("<p>\n<h1>branch</h1>\n");
476 html("<form method='get' action=''>\n");
477 add_hidden_formfields(0, 1);
478 html("<select name='h' onchange='this.form.submit();'>\n");
479 for_each_branch_ref(print_branch_option, cgit_query_head);
480 html("</select>\n");
481 html("</form>\n");
482
483 html("<p>\n<h1>search</h1>\n");
484 html("<form method='get' action='");
485 html_attr(cgit_pageurl(cgit_query_repo, "log", NULL));
486 html("'>\n");
487 add_hidden_formfields(1, 0);
488 html("<select name='qt'>\n");
424 html_option("grep", "log msg", cgit_query_grep); 489 html_option("grep", "log msg", cgit_query_grep);
425 html_option("author", "author", cgit_query_grep); 490 html_option("author", "author", cgit_query_grep);
426 html_option("committer", "committer", cgit_query_grep); 491 html_option("committer", "committer", cgit_query_grep);
427 html("</select>"); 492 html("</select>\n");
428 html("<input class='txt' type='text' name='q' value='"); 493 html("<input class='txt' type='text' name='q' value='");
429 html_attr(cgit_query_search); 494 html_attr(cgit_query_search);
430 html("'/><input class='btn' type='submit' value='...'/></form>"); 495 html("'/>\n");
496 html("</form>\n");
497 } else {
498 if (!cgit_index_info || html_include(cgit_index_info))
499 html(default_info);
431 } 500 }
432 html("</td></tr>"); 501
433 html("<tr><td id='content' colspan='2'>"); 502 html("</div>\n");
503
504 html("</div>\n<table class='grid'><tr><td id='content'>\n");
434} 505}
435 506
507
436void cgit_print_snapshot_start(const char *mimetype, const char *filename, 508void cgit_print_snapshot_start(const char *mimetype, const char *filename,
437 struct cacheitem *item) 509 struct cacheitem *item)
438{ 510{
439 htmlf("Content-Type: %s\n", mimetype); 511 htmlf("Content-Type: %s\n", mimetype);
440 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 512 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
441 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 513 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
442 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 514 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
443 ttl_seconds(item->ttl))); 515 ttl_seconds(item->ttl)));
444 html("\n"); 516 html("\n");
445} 517}
446 518
447/* vim:set sw=8: */ 519/* vim:set sw=8: */
diff --git a/ui-summary.c b/ui-summary.c
index ba90510..39fe330 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -75,179 +75,126 @@ static int print_branch(struct refinfo *ref)
75 75
76static void print_tag_header() 76static void print_tag_header()
77{ 77{
78 html("<tr class='nohover'><th class='left'>Tag</th>" 78 html("<tr class='nohover'><th class='left'>Tag</th>"
79 "<th class='left'>Age</th>" 79 "<th class='left'>Age</th>"
80 "<th class='left'>Author</th>" 80 "<th class='left'>Author</th>"
81 "<th class='left'>Reference</th></tr>\n"); 81 "<th class='left'>Reference</th></tr>\n");
82 header = 1; 82 header = 1;
83} 83}
84 84
85static int print_tag(struct refinfo *ref) 85static int print_tag(struct refinfo *ref)
86{ 86{
87 struct tag *tag; 87 struct tag *tag;
88 struct taginfo *info; 88 struct taginfo *info;
89 char *url, *name = (char *)ref->refname; 89 char *url, *name = (char *)ref->refname;
90 90
91 if (ref->object->type == OBJ_TAG) { 91 if (ref->object->type == OBJ_TAG) {
92 tag = (struct tag *)ref->object; 92 tag = (struct tag *)ref->object;
93 info = ref->tag; 93 info = ref->tag;
94 if (!tag || !info) 94 if (!tag || !info)
95 return 1; 95 return 1;
96 html("<tr><td>"); 96 html("<tr><td>");
97 url = cgit_pageurl(cgit_query_repo, "tag", 97 url = cgit_pageurl(cgit_query_repo, "tag",
98 fmt("id=%s", name)); 98 fmt("id=%s", name));
99 html_link_open(url, NULL, NULL); 99 html_link_open(url, NULL, NULL);
100 html_txt(name); 100 html_txt(name);
101 html_link_close(); 101 html_link_close();
102 html("</td><td>"); 102 html("</td><td>");
103 if (info->tagger_date > 0) 103 if (info->tagger_date > 0)
104 cgit_print_age(info->tagger_date, -1, NULL); 104 cgit_print_age(info->tagger_date, -1, NULL);
105 html("</td><td>"); 105 html("</td><td>");
106 if (info->tagger) 106 if (info->tagger)
107 html(info->tagger); 107 html(info->tagger);
108 html("</td><td>"); 108 html("</td><td>");
109 cgit_object_link(tag->tagged); 109 cgit_object_link(tag->tagged);
110 html("</td></tr>\n"); 110 html("</td></tr>\n");
111 } else { 111 } else {
112 if (!header) 112 if (!header)
113 print_tag_header(); 113 print_tag_header();
114 html("<tr><td>"); 114 html("<tr><td>");
115 html_txt(name); 115 html_txt(name);
116 html("</td><td colspan='2'/><td>"); 116 html("</td><td colspan='2'/><td>");
117 cgit_object_link(ref->object); 117 cgit_object_link(ref->object);
118 html("</td></tr>\n"); 118 html("</td></tr>\n");
119 } 119 }
120 return 0; 120 return 0;
121} 121}
122 122
123static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
124 int flags, void *cb_data)
125{
126 struct tag *tag;
127 struct taginfo *info;
128 struct object *obj;
129 char buf[256], *url;
130 unsigned char fileid[20];
131
132 if (prefixcmp(refname, "refs/archives"))
133 return 0;
134 strncpy(buf, refname+14, sizeof(buf));
135 obj = parse_object(sha1);
136 if (!obj)
137 return 1;
138 if (obj->type == OBJ_TAG) {
139 tag = lookup_tag(sha1);
140 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
141 return 0;
142 hashcpy(fileid, tag->tagged->sha1);
143 } else if (obj->type != OBJ_BLOB) {
144 return 0;
145 } else {
146 hashcpy(fileid, sha1);
147 }
148 if (!header) {
149 html("<table id='downloads'>");
150 html("<tr><th>Downloads</th></tr>");
151 header = 1;
152 }
153 html("<tr><td>");
154 url = cgit_pageurl(cgit_query_repo, "blob",
155 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
156 buf));
157 html_link_open(url, NULL, NULL);
158 html_txt(buf);
159 html_link_close();
160 html("</td></tr>");
161 return 0;
162}
163
164static void print_refs_link(char *path) 123static void print_refs_link(char *path)
165{ 124{
166 html("<tr class='nohover'><td colspan='4'>"); 125 html("<tr class='nohover'><td colspan='4'>");
167 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path); 126 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path);
168 html("</td></tr>"); 127 html("</td></tr>");
169} 128}
170 129
171void cgit_print_branches(int maxcount) 130void cgit_print_branches(int maxcount)
172{ 131{
173 struct reflist list; 132 struct reflist list;
174 int i; 133 int i;
175 134
176 html("<tr class='nohover'><th class='left'>Branch</th>" 135 html("<tr class='nohover'><th class='left'>Branch</th>"
177 "<th class='left'>Idle</th>" 136 "<th class='left'>Idle</th>"
178 "<th class='left'>Author</th>" 137 "<th class='left'>Author</th>"
179 "<th class='left'>Head commit</th></tr>\n"); 138 "<th class='left'>Head commit</th></tr>\n");
180 139
181 list.refs = NULL; 140 list.refs = NULL;
182 list.alloc = list.count = 0; 141 list.alloc = list.count = 0;
183 for_each_branch_ref(cgit_refs_cb, &list); 142 for_each_branch_ref(cgit_refs_cb, &list);
184 143
185 if (maxcount == 0 || maxcount > list.count) 144 if (maxcount == 0 || maxcount > list.count)
186 maxcount = list.count; 145 maxcount = list.count;
187 146
188 if (maxcount < list.count) { 147 if (maxcount < list.count) {
189 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); 148 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
190 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); 149 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
191 } 150 }
192 151
193 for(i=0; i<maxcount; i++) 152 for(i=0; i<maxcount; i++)
194 print_branch(list.refs[i]); 153 print_branch(list.refs[i]);
195 154
196 if (maxcount < list.count) 155 if (maxcount < list.count)
197 print_refs_link("heads"); 156 print_refs_link("heads");
198} 157}
199 158
200void cgit_print_tags(int maxcount) 159void cgit_print_tags(int maxcount)
201{ 160{
202 struct reflist list; 161 struct reflist list;
203 int i; 162 int i;
204 163
205 header = 0; 164 header = 0;
206 list.refs = NULL; 165 list.refs = NULL;
207 list.alloc = list.count = 0; 166 list.alloc = list.count = 0;
208 for_each_tag_ref(cgit_refs_cb, &list); 167 for_each_tag_ref(cgit_refs_cb, &list);
209 if (list.count == 0) 168 if (list.count == 0)
210 return; 169 return;
211 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age); 170 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
212 if (!maxcount) 171 if (!maxcount)
213 maxcount = list.count; 172 maxcount = list.count;
214 else if (maxcount > list.count) 173 else if (maxcount > list.count)
215 maxcount = list.count; 174 maxcount = list.count;
216 print_tag_header(); 175 print_tag_header();
217 for(i=0; i<maxcount; i++) 176 for(i=0; i<maxcount; i++)
218 print_tag(list.refs[i]); 177 print_tag(list.refs[i]);
219 178
220 if (maxcount < list.count) 179 if (maxcount < list.count)
221 print_refs_link("tags"); 180 print_refs_link("tags");
222} 181}
223 182
224static void cgit_print_archives()
225{
226 header = 0;
227 for_each_ref(cgit_print_archive_cb, NULL);
228 if (header)
229 html("</table>");
230}
231
232void cgit_print_summary() 183void cgit_print_summary()
233{ 184{
185 if (cgit_repo->readme) {
234 html("<div id='summary'>"); 186 html("<div id='summary'>");
235 cgit_print_archives();
236 html("<h2>");
237 html_txt(cgit_repo->name);
238 html(" - ");
239 html_txt(cgit_repo->desc);
240 html("</h2>");
241 if (cgit_repo->readme)
242 html_include(cgit_repo->readme); 187 html_include(cgit_repo->readme);
243 html("</div>"); 188 html("</div>");
189 }
244 if (cgit_summary_log > 0) 190 if (cgit_summary_log > 0)
245 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, NULL, 0); 191 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL,
192 NULL, NULL, 0);
246 html("<table class='list nowrap'>"); 193 html("<table class='list nowrap'>");
247 if (cgit_summary_log > 0) 194 if (cgit_summary_log > 0)
248 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 195 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
249 cgit_print_branches(cgit_summary_branches); 196 cgit_print_branches(cgit_summary_branches);
250 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 197 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
251 cgit_print_tags(cgit_summary_tags); 198 cgit_print_tags(cgit_summary_tags);
252 html("</table>"); 199 html("</table>");
253} 200}