summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore 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.c172
-rw-r--r--ui-summary.c65
7 files changed, 231 insertions, 197 deletions
diff --git a/cgit.css b/cgit.css
index 5d47099..6cf4517 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,454 +1,431 @@
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}
377 354
378table.diff td div.del { 355table.diff td div.del {
379 color: red; 356 color: red;
380} 357}
381 358
382.sha1 { 359.sha1 {
383 font-family: monospace; 360 font-family: monospace;
384 font-size: 90%; 361 font-size: 90%;
385} 362}
386 363
387.left { 364.left {
388 text-align: left; 365 text-align: left;
389} 366}
390 367
391.right { 368.right {
392 text-align: right; 369 text-align: right;
393} 370}
394 371
395table.list td.repogroup { 372table.list td.repogroup {
396 font-style: italic; 373 font-style: italic;
397 color: #888; 374 color: #888;
398} 375}
399 376
400a.button { 377a.button {
401 font-size: 80%; 378 font-size: 80%;
402 color: #aaa; 379 color: #aaa;
403 background-color: #eee; 380 background-color: #eee;
404 border: solid 1px #aaa; 381 border: solid 1px #aaa;
405 padding: 0em 0.5em; 382 padding: 0em 0.5em;
406 margin: 0.1em 0.25em; 383 margin: 0.1em 0.25em;
407} 384}
408 385
409a.button:hover { 386a.button:hover {
410 text-decoration: none; 387 text-decoration: none;
411 color: #333; 388 color: #333;
412 background-color: #ccc; 389 background-color: #ccc;
413} 390}
414 391
415a.primary { 392a.primary {
416 font-size: 100%; 393 font-size: 100%;
417} 394}
418 395
419a.secondary { 396a.secondary {
420 font-size: 90%; 397 font-size: 90%;
421} 398}
422 399
423td.toplevel-repo { 400td.toplevel-repo {
424 401
425} 402}
426 403
427table.list td.sublevel-repo { 404table.list td.sublevel-repo {
428 padding-left: 1.5em; 405 padding-left: 1.5em;
429} 406}
430 407
431span.age-mins { 408span.age-mins {
432 font-weight: bold; 409 font-weight: bold;
433 color: #080; 410 color: #080;
434} 411}
435 412
436span.age-hours { 413span.age-hours {
437 color: #080; 414 color: #080;
438} 415}
439 416
440span.age-days { 417span.age-days {
441 color: #040; 418 color: #040;
442} 419}
443 420
444span.age-weeks { 421span.age-weeks {
445 color: #444; 422 color: #444;
446} 423}
447 424
448span.age-months { 425span.age-months {
449 color: #888; 426 color: #888;
450} 427}
451 428
452span.age-years { 429span.age-years {
453 color: #bbb; 430 color: #bbb;
454} 431}
diff --git a/cgit.h b/cgit.h
index b8af970..42036c3 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,280 +1,282 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19 19
20 20
21/* 21/*
22 * The valid cgit repo-commands 22 * The valid cgit repo-commands
23 */ 23 */
24#define CMD_LOG 1 24#define CMD_LOG 1
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_BLOB 5 28#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 29#define CMD_SNAPSHOT 6
30#define CMD_TAG 7 30#define CMD_TAG 7
31#define CMD_REFS 8 31#define CMD_REFS 8
32 32
33/* 33/*
34 * Dateformats used on misc. pages 34 * Dateformats used on misc. pages
35 */ 35 */
36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
37#define FMT_SHORTDATE "%Y-%m-%d" 37#define FMT_SHORTDATE "%Y-%m-%d"
38 38
39 39
40/* 40/*
41 * Limits used for relative dates 41 * Limits used for relative dates
42 */ 42 */
43#define TM_MIN 60 43#define TM_MIN 60
44#define TM_HOUR (TM_MIN * 60) 44#define TM_HOUR (TM_MIN * 60)
45#define TM_DAY (TM_HOUR * 24) 45#define TM_DAY (TM_HOUR * 24)
46#define TM_WEEK (TM_DAY * 7) 46#define TM_WEEK (TM_DAY * 7)
47#define TM_YEAR (TM_DAY * 365) 47#define TM_YEAR (TM_DAY * 365)
48#define TM_MONTH (TM_YEAR / 12.0) 48#define TM_MONTH (TM_YEAR / 12.0)
49 49
50 50
51typedef void (*configfn)(const char *name, const char *value); 51typedef void (*configfn)(const char *name, const char *value);
52typedef void (*filepair_fn)(struct diff_filepair *pair); 52typedef void (*filepair_fn)(struct diff_filepair *pair);
53typedef void (*linediff_fn)(char *line, int len); 53typedef void (*linediff_fn)(char *line, int len);
54 54
55struct cacheitem { 55struct cacheitem {
56 char *name; 56 char *name;
57 struct stat st; 57 struct stat st;
58 int ttl; 58 int ttl;
59 int fd; 59 int fd;
60}; 60};
61 61
62struct repoinfo { 62struct repoinfo {
63 char *url; 63 char *url;
64 char *name; 64 char *name;
65 char *path; 65 char *path;
66 char *desc; 66 char *desc;
67 char *owner; 67 char *owner;
68 char *defbranch; 68 char *defbranch;
69 char *group; 69 char *group;
70 char *module_link; 70 char *module_link;
71 char *readme; 71 char *readme;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75}; 75};
76 76
77struct repolist { 77struct repolist {
78 int length; 78 int length;
79 int count; 79 int count;
80 struct repoinfo *repos; 80 struct repoinfo *repos;
81}; 81};
82 82
83struct commitinfo { 83struct commitinfo {
84 struct commit *commit; 84 struct commit *commit;
85 char *author; 85 char *author;
86 char *author_email; 86 char *author_email;
87 unsigned long author_date; 87 unsigned long author_date;
88 char *committer; 88 char *committer;
89 char *committer_email; 89 char *committer_email;
90 unsigned long committer_date; 90 unsigned long committer_date;
91 char *subject; 91 char *subject;
92 char *msg; 92 char *msg;
93}; 93};
94 94
95struct taginfo { 95struct taginfo {
96 char *tagger; 96 char *tagger;
97 char *tagger_email; 97 char *tagger_email;
98 int tagger_date; 98 int tagger_date;
99 char *msg; 99 char *msg;
100}; 100};
101 101
102struct refinfo { 102struct refinfo {
103 const char *refname; 103 const char *refname;
104 struct object *object; 104 struct object *object;
105 union { 105 union {
106 struct taginfo *tag; 106 struct taginfo *tag;
107 struct commitinfo *commit; 107 struct commitinfo *commit;
108 }; 108 };
109}; 109};
110 110
111struct reflist { 111struct reflist {
112 struct refinfo **refs; 112 struct refinfo **refs;
113 int alloc; 113 int alloc;
114 int count; 114 int count;
115}; 115};
116 116
117extern const char *cgit_version; 117extern const char *cgit_version;
118 118
119extern struct repolist cgit_repolist; 119extern struct repolist cgit_repolist;
120extern struct repoinfo *cgit_repo; 120extern struct repoinfo *cgit_repo;
121extern int cgit_cmd; 121extern int cgit_cmd;
122 122
123extern char *cgit_root_title; 123extern char *cgit_root_title;
124extern char *cgit_css; 124extern char *cgit_css;
125extern char *cgit_logo; 125extern char *cgit_logo;
126extern char *cgit_index_header; 126extern char *cgit_index_header;
127extern char *cgit_index_info; 127extern char *cgit_index_info;
128extern char *cgit_logo_link; 128extern char *cgit_logo_link;
129extern char *cgit_module_link; 129extern char *cgit_module_link;
130extern char *cgit_agefile; 130extern char *cgit_agefile;
131extern char *cgit_virtual_root; 131extern char *cgit_virtual_root;
132extern char *cgit_script_name; 132extern char *cgit_script_name;
133extern char *cgit_cache_root; 133extern char *cgit_cache_root;
134extern char *cgit_repo_group; 134extern char *cgit_repo_group;
135 135
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,
232 const char *query); 234 const char *query);
233 235
234extern const char *cgit_repobasename(const char *reponame); 236extern const char *cgit_repobasename(const char *reponame);
235 237
236extern void cgit_tree_link(char *name, char *title, char *class, char *head, 238extern void cgit_tree_link(char *name, char *title, char *class, char *head,
237 char *rev, char *path); 239 char *rev, char *path);
238extern void cgit_log_link(char *name, char *title, char *class, char *head, 240extern void cgit_log_link(char *name, char *title, char *class, char *head,
239 char *rev, char *path, int ofs); 241 char *rev, char *path, int ofs);
240extern void cgit_commit_link(char *name, char *title, char *class, char *head, 242extern void cgit_commit_link(char *name, char *title, char *class, char *head,
241 char *rev); 243 char *rev);
242extern void cgit_refs_link(char *name, char *title, char *class, char *head, 244extern void cgit_refs_link(char *name, char *title, char *class, char *head,
243 char *rev, char *path); 245 char *rev, char *path);
244extern void cgit_snapshot_link(char *name, char *title, char *class, 246extern void cgit_snapshot_link(char *name, char *title, char *class,
245 char *head, char *rev, char *archivename); 247 char *head, char *rev, char *archivename);
246extern void cgit_diff_link(char *name, char *title, char *class, char *head, 248extern void cgit_diff_link(char *name, char *title, char *class, char *head,
247 char *new_rev, char *old_rev, char *path); 249 char *new_rev, char *old_rev, char *path);
248 250
249extern void cgit_object_link(struct object *obj); 251extern void cgit_object_link(struct object *obj);
250 252
251extern void cgit_print_error(char *msg); 253extern void cgit_print_error(char *msg);
252extern void cgit_print_date(time_t secs, char *format); 254extern void cgit_print_date(time_t secs, char *format);
253extern void cgit_print_age(time_t t, time_t max_relative, char *format); 255extern void cgit_print_age(time_t t, time_t max_relative, char *format);
254extern void cgit_print_docstart(char *title, struct cacheitem *item); 256extern void cgit_print_docstart(char *title, struct cacheitem *item);
255extern void cgit_print_docend(); 257extern void cgit_print_docend();
256extern void cgit_print_pageheader(char *title, int show_search); 258extern void cgit_print_pageheader(char *title, int show_search);
257extern void cgit_print_snapshot_start(const char *mimetype, 259extern void cgit_print_snapshot_start(const char *mimetype,
258 const char *filename, 260 const char *filename,
259 struct cacheitem *item); 261 struct cacheitem *item);
260extern void cgit_print_branches(int maxcount); 262extern void cgit_print_branches(int maxcount);
261extern void cgit_print_tags(int maxcount); 263extern void cgit_print_tags(int maxcount);
262 264
263extern void cgit_print_repolist(struct cacheitem *item); 265extern void cgit_print_repolist(struct cacheitem *item);
264extern void cgit_print_summary(); 266extern void cgit_print_summary();
265extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, 267extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
266 char *pattern, char *path, int pager); 268 char *pattern, char *path, int pager);
267extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 269extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
268extern void cgit_print_tree(const char *rev, char *path); 270extern void cgit_print_tree(const char *rev, char *path);
269extern void cgit_print_commit(char *hex); 271extern void cgit_print_commit(char *hex);
270extern void cgit_print_refs(); 272extern void cgit_print_refs();
271extern void cgit_print_tag(char *revname); 273extern void cgit_print_tag(char *revname);
272extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); 274extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix);
273extern void cgit_print_snapshot(struct cacheitem *item, const char *head, 275extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
274 const char *hex, const char *prefix, 276 const char *hex, const char *prefix,
275 const char *filename, int snapshot); 277 const char *filename, int snapshot);
276extern void cgit_print_snapshot_links(const char *repo, const char *head, 278extern void cgit_print_snapshot_links(const char *repo, const char *head,
277 const char *hex, int snapshots); 279 const char *hex, int snapshots);
278extern int cgit_parse_snapshots_mask(const char *str); 280extern int cgit_parse_snapshots_mask(const char *str);
279 281
280#endif /* CGIT_H */ 282#endif /* CGIT_H */
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
@@ -1,177 +1,182 @@
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## Set allowed snapshot types by default. 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# can be any combination of zip/tar.gz/tar.bz2/tar
13#snapshots=0 13#snapshots=0
14 14
15 15
16## 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
17#enable-index-links=0 17#enable-index-links=0
18 18
19 19
20## Enable/disable display of 'number of files changed' in log view 20## Enable/disable display of 'number of files changed' in log view
21#enable-log-filecount=0 21#enable-log-filecount=0
22 22
23 23
24## Enable/disable display of 'number of lines changed' in log view 24## Enable/disable display of 'number of lines changed' in log view
25#enable-log-linecount=0 25#enable-log-linecount=0
26 26
27 27
28## 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
29## number of commits that should be displayed 29## number of commits that should be displayed
30#summary-log=0 30#summary-log=0
31 31
32 32
33## Restrict the number of branches printed in summary view. Set to 0 to 33## Restrict the number of branches printed in summary view. Set to 0 to
34## print all branches. 34## print all branches.
35#summary-branches=0 35#summary-branches=0
36 36
37 37
38## Restrict the number of tags printed in summary view. Set to 0 to 38## Restrict the number of tags printed in summary view. Set to 0 to
39## print all tags. 39## print all tags.
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
138 143
139## ttl for other dynamic pages 144## ttl for other dynamic pages
140#cache-dynamic-ttl=5 145#cache-dynamic-ttl=5
141 146
142## ttl for static pages (addressed by SHA-1) 147## ttl for static pages (addressed by SHA-1)
143#cache-static-ttl=-1 148#cache-static-ttl=-1
144 149
145 150
146 151
147## Example repository entry. Required values are repo.url and repo.path (each 152## Example repository entry. Required values are repo.url and repo.path (each
148## repository section must start with repo.url). 153## repository section must start with repo.url).
149#repo.url=cgit 154#repo.url=cgit
150#repo.name=cgit 155#repo.name=cgit
151#repo.desc=the caching cgi for git 156#repo.desc=the caching cgi for git
152 #repo.path=/pub/git/cgit ## this is the path to $GIT_DIR 157 #repo.path=/pub/git/cgit ## this is the path to $GIT_DIR
153#repo.owner=Lars Hjemli 158#repo.owner=Lars Hjemli
154 #repo.defbranch=master ## define a default branch 159 #repo.defbranch=master ## define a default branch
155 #repo.snapshots=tar.bz2 ## override a sitewide snapshot-setting 160 #repo.snapshots=tar.bz2 ## override a sitewide snapshot-setting
156 #repo.enable-log-filecount=0 ## override the default filecount setting 161 #repo.enable-log-filecount=0 ## override the default filecount setting
157 #repo.enable-log-linecount=0 ## override the default linecount setting 162 #repo.enable-log-linecount=0 ## override the default linecount setting
158 #repo.module-link=/git/%s/commit/?id=%s ## override the standard module-link 163 #repo.module-link=/git/%s/commit/?id=%s ## override the standard module-link
159 #repo.readme=info/web/readme ## specify a file to include on summary page 164 #repo.readme=info/web/readme ## specify a file to include on summary page
160 165
161## Additional repositories grouped under "mirrors" 166## Additional repositories grouped under "mirrors"
162#repo.group=mirrors 167#repo.group=mirrors
163 168
164#repo.url=git 169#repo.url=git
165#repo.path=/pub/git/git 170#repo.path=/pub/git/git
166# 171#
167#repo.url=linux 172#repo.url=linux
168#repo.path=/pub/git/linux 173#repo.path=/pub/git/linux
169 174
170## A group of private repositories (with a working directory) 175## A group of private repositories (with a working directory)
171#repo.group=private 176#repo.group=private
172 177
173#repo.url=larsh/cgit 178#repo.url=larsh/cgit
174#repo.path=/home/larsh/src/cgit/.git 179#repo.path=/home/larsh/src/cgit/.git
175 180
176#repo.url=larsh/git 181#repo.url=larsh/git
177#repo.path=/home/larsh/src/git/.git 182#repo.path=/home/larsh/src/git/.git
diff --git a/shared.c b/shared.c
index 50fe8e1..e06df91 100644
--- a/shared.c
+++ b/shared.c
@@ -1,478 +1,509 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
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
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION; 15const char *cgit_version = CGIT_VERSION;
16 16
17char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
18char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
19char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
20char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
21char *cgit_index_info = NULL; 21char *cgit_index_info = NULL;
22char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 22char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
23char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 23char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
24char *cgit_agefile = "info/web/last-modified"; 24char *cgit_agefile = "info/web/last-modified";
25char *cgit_virtual_root = NULL; 25char *cgit_virtual_root = NULL;
26char *cgit_script_name = CGIT_SCRIPT_NAME; 26char *cgit_script_name = CGIT_SCRIPT_NAME;
27char *cgit_cache_root = CGIT_CACHE_ROOT; 27char *cgit_cache_root = CGIT_CACHE_ROOT;
28char *cgit_repo_group = NULL; 28char *cgit_repo_group = NULL;
29 29
30int cgit_nocache = 0; 30int cgit_nocache = 0;
31int cgit_snapshots = 0; 31int cgit_snapshots = 0;
32int cgit_enable_index_links = 0; 32int cgit_enable_index_links = 0;
33int cgit_enable_log_filecount = 0; 33int cgit_enable_log_filecount = 0;
34int cgit_enable_log_linecount = 0; 34int cgit_enable_log_linecount = 0;
35int cgit_max_lock_attempts = 5; 35int cgit_max_lock_attempts = 5;
36int cgit_cache_root_ttl = 5; 36int cgit_cache_root_ttl = 5;
37int cgit_cache_repo_ttl = 5; 37int cgit_cache_repo_ttl = 5;
38int cgit_cache_dynamic_ttl = 5; 38int cgit_cache_dynamic_ttl = 5;
39int cgit_cache_static_ttl = -1; 39int cgit_cache_static_ttl = -1;
40int cgit_cache_max_create_time = 5; 40int cgit_cache_max_create_time = 5;
41int cgit_summary_log = 0; 41int cgit_summary_log = 0;
42int cgit_summary_tags = 0; 42int cgit_summary_tags = 0;
43int cgit_summary_branches = 0; 43int cgit_summary_branches = 0;
44int cgit_renamelimit = -1; 44int cgit_renamelimit = -1;
45 45
46int cgit_max_msg_len = 60; 46int cgit_max_msg_len = 60;
47int cgit_max_repodesc_len = 60; 47int cgit_max_repodesc_len = 60;
48int cgit_max_commit_count = 50; 48int cgit_max_commit_count = 50;
49 49
50int cgit_query_has_symref = 0; 50int cgit_query_has_symref = 0;
51int cgit_query_has_sha1 = 0; 51int cgit_query_has_sha1 = 0;
52 52
53char *cgit_querystring = NULL; 53char *cgit_querystring = NULL;
54char *cgit_query_repo = NULL; 54char *cgit_query_repo = NULL;
55char *cgit_query_page = NULL; 55char *cgit_query_page = NULL;
56char *cgit_query_head = NULL; 56char *cgit_query_head = NULL;
57char *cgit_query_search = NULL; 57char *cgit_query_search = NULL;
58char *cgit_query_grep = NULL; 58char *cgit_query_grep = NULL;
59char *cgit_query_sha1 = NULL; 59char *cgit_query_sha1 = NULL;
60char *cgit_query_sha2 = NULL; 60char *cgit_query_sha2 = NULL;
61char *cgit_query_path = NULL; 61char *cgit_query_path = NULL;
62char *cgit_query_name = NULL; 62char *cgit_query_name = NULL;
63int cgit_query_ofs = 0; 63int cgit_query_ofs = 0;
64 64
65int htmlfd = 0; 65int htmlfd = 0;
66 66
67 67
68int cgit_get_cmd_index(const char *cmd) 68int cgit_get_cmd_index(const char *cmd)
69{ 69{
70 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 70 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
71 "snapshot", "tag", "refs", NULL}; 71 "snapshot", "tag", "refs", NULL};
72 int i; 72 int i;
73 73
74 for(i = 0; cmds[i]; i++) 74 for(i = 0; cmds[i]; i++)
75 if (!strcmp(cmd, cmds[i])) 75 if (!strcmp(cmd, cmds[i]))
76 return i + 1; 76 return i + 1;
77 return 0; 77 return 0;
78} 78}
79 79
80int chk_zero(int result, char *msg) 80int chk_zero(int result, char *msg)
81{ 81{
82 if (result != 0) 82 if (result != 0)
83 die("%s: %s", msg, strerror(errno)); 83 die("%s: %s", msg, strerror(errno));
84 return result; 84 return result;
85} 85}
86 86
87int chk_positive(int result, char *msg) 87int chk_positive(int result, char *msg)
88{ 88{
89 if (result <= 0) 89 if (result <= 0)
90 die("%s: %s", msg, strerror(errno)); 90 die("%s: %s", msg, strerror(errno));
91 return result; 91 return result;
92} 92}
93 93
94int chk_non_negative(int result, char *msg) 94int chk_non_negative(int result, char *msg)
95{ 95{
96 if (result < 0) 96 if (result < 0)
97 die("%s: %s",msg, strerror(errno)); 97 die("%s: %s",msg, strerror(errno));
98 return result; 98 return result;
99} 99}
100 100
101struct repoinfo *add_repo(const char *url) 101struct repoinfo *add_repo(const char *url)
102{ 102{
103 struct repoinfo *ret; 103 struct repoinfo *ret;
104 104
105 if (++cgit_repolist.count > cgit_repolist.length) { 105 if (++cgit_repolist.count > cgit_repolist.length) {
106 if (cgit_repolist.length == 0) 106 if (cgit_repolist.length == 0)
107 cgit_repolist.length = 8; 107 cgit_repolist.length = 8;
108 else 108 else
109 cgit_repolist.length *= 2; 109 cgit_repolist.length *= 2;
110 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 110 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
111 cgit_repolist.length * 111 cgit_repolist.length *
112 sizeof(struct repoinfo)); 112 sizeof(struct repoinfo));
113 } 113 }
114 114
115 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 115 ret = &cgit_repolist.repos[cgit_repolist.count-1];
116 ret->url = trim_end(url, '/'); 116 ret->url = trim_end(url, '/');
117 ret->name = ret->url; 117 ret->name = ret->url;
118 ret->path = NULL; 118 ret->path = NULL;
119 ret->desc = NULL; 119 ret->desc = NULL;
120 ret->owner = NULL; 120 ret->owner = NULL;
121 ret->group = cgit_repo_group; 121 ret->group = cgit_repo_group;
122 ret->defbranch = "master"; 122 ret->defbranch = "master";
123 ret->snapshots = cgit_snapshots; 123 ret->snapshots = cgit_snapshots;
124 ret->enable_log_filecount = cgit_enable_log_filecount; 124 ret->enable_log_filecount = cgit_enable_log_filecount;
125 ret->enable_log_linecount = cgit_enable_log_linecount; 125 ret->enable_log_linecount = cgit_enable_log_linecount;
126 ret->module_link = cgit_module_link; 126 ret->module_link = cgit_module_link;
127 ret->readme = NULL; 127 ret->readme = NULL;
128 return ret; 128 return ret;
129} 129}
130 130
131struct repoinfo *cgit_get_repoinfo(const char *url) 131struct repoinfo *cgit_get_repoinfo(const char *url)
132{ 132{
133 int i; 133 int i;
134 struct repoinfo *repo; 134 struct repoinfo *repo;
135 135
136 for (i=0; i<cgit_repolist.count; i++) { 136 for (i=0; i<cgit_repolist.count; i++) {
137 repo = &cgit_repolist.repos[i]; 137 repo = &cgit_repolist.repos[i];
138 if (!strcmp(repo->url, url)) 138 if (!strcmp(repo->url, url))
139 return repo; 139 return repo;
140 } 140 }
141 return NULL; 141 return NULL;
142} 142}
143 143
144void cgit_global_config_cb(const char *name, const char *value) 144void cgit_global_config_cb(const char *name, const char *value)
145{ 145{
146 if (!strcmp(name, "root-title")) 146 if (!strcmp(name, "root-title"))
147 cgit_root_title = xstrdup(value); 147 cgit_root_title = xstrdup(value);
148 else if (!strcmp(name, "css")) 148 else if (!strcmp(name, "css"))
149 cgit_css = xstrdup(value); 149 cgit_css = xstrdup(value);
150 else if (!strcmp(name, "logo")) 150 else if (!strcmp(name, "logo"))
151 cgit_logo = xstrdup(value); 151 cgit_logo = xstrdup(value);
152 else if (!strcmp(name, "index-header")) 152 else if (!strcmp(name, "index-header"))
153 cgit_index_header = xstrdup(value); 153 cgit_index_header = xstrdup(value);
154 else if (!strcmp(name, "index-info")) 154 else if (!strcmp(name, "index-info"))
155 cgit_index_info = xstrdup(value); 155 cgit_index_info = xstrdup(value);
156 else if (!strcmp(name, "logo-link")) 156 else if (!strcmp(name, "logo-link"))
157 cgit_logo_link = xstrdup(value); 157 cgit_logo_link = xstrdup(value);
158 else if (!strcmp(name, "module-link")) 158 else if (!strcmp(name, "module-link"))
159 cgit_module_link = xstrdup(value); 159 cgit_module_link = xstrdup(value);
160 else if (!strcmp(name, "virtual-root")) 160 else if (!strcmp(name, "virtual-root"))
161 cgit_virtual_root = trim_end(value, '/'); 161 cgit_virtual_root = trim_end(value, '/');
162 else if (!strcmp(name, "nocache")) 162 else if (!strcmp(name, "nocache"))
163 cgit_nocache = atoi(value); 163 cgit_nocache = atoi(value);
164 else if (!strcmp(name, "snapshots")) 164 else if (!strcmp(name, "snapshots"))
165 cgit_snapshots = cgit_parse_snapshots_mask(value); 165 cgit_snapshots = cgit_parse_snapshots_mask(value);
166 else if (!strcmp(name, "enable-index-links")) 166 else if (!strcmp(name, "enable-index-links"))
167 cgit_enable_index_links = atoi(value); 167 cgit_enable_index_links = atoi(value);
168 else if (!strcmp(name, "enable-log-filecount")) 168 else if (!strcmp(name, "enable-log-filecount"))
169 cgit_enable_log_filecount = atoi(value); 169 cgit_enable_log_filecount = atoi(value);
170 else if (!strcmp(name, "enable-log-linecount")) 170 else if (!strcmp(name, "enable-log-linecount"))
171 cgit_enable_log_linecount = atoi(value); 171 cgit_enable_log_linecount = atoi(value);
172 else if (!strcmp(name, "cache-root")) 172 else if (!strcmp(name, "cache-root"))
173 cgit_cache_root = xstrdup(value); 173 cgit_cache_root = xstrdup(value);
174 else if (!strcmp(name, "cache-root-ttl")) 174 else if (!strcmp(name, "cache-root-ttl"))
175 cgit_cache_root_ttl = atoi(value); 175 cgit_cache_root_ttl = atoi(value);
176 else if (!strcmp(name, "cache-repo-ttl")) 176 else if (!strcmp(name, "cache-repo-ttl"))
177 cgit_cache_repo_ttl = atoi(value); 177 cgit_cache_repo_ttl = atoi(value);
178 else if (!strcmp(name, "cache-static-ttl")) 178 else if (!strcmp(name, "cache-static-ttl"))
179 cgit_cache_static_ttl = atoi(value); 179 cgit_cache_static_ttl = atoi(value);
180 else if (!strcmp(name, "cache-dynamic-ttl")) 180 else if (!strcmp(name, "cache-dynamic-ttl"))
181 cgit_cache_dynamic_ttl = atoi(value); 181 cgit_cache_dynamic_ttl = atoi(value);
182 else if (!strcmp(name, "max-message-length")) 182 else if (!strcmp(name, "max-message-length"))
183 cgit_max_msg_len = atoi(value); 183 cgit_max_msg_len = atoi(value);
184 else if (!strcmp(name, "max-repodesc-length")) 184 else if (!strcmp(name, "max-repodesc-length"))
185 cgit_max_repodesc_len = atoi(value); 185 cgit_max_repodesc_len = atoi(value);
186 else if (!strcmp(name, "max-commit-count")) 186 else if (!strcmp(name, "max-commit-count"))
187 cgit_max_commit_count = atoi(value); 187 cgit_max_commit_count = atoi(value);
188 else if (!strcmp(name, "summary-log")) 188 else if (!strcmp(name, "summary-log"))
189 cgit_summary_log = atoi(value); 189 cgit_summary_log = atoi(value);
190 else if (!strcmp(name, "summary-branches")) 190 else if (!strcmp(name, "summary-branches"))
191 cgit_summary_branches = atoi(value); 191 cgit_summary_branches = atoi(value);
192 else if (!strcmp(name, "summary-tags")) 192 else if (!strcmp(name, "summary-tags"))
193 cgit_summary_tags = atoi(value); 193 cgit_summary_tags = atoi(value);
194 else if (!strcmp(name, "agefile")) 194 else if (!strcmp(name, "agefile"))
195 cgit_agefile = xstrdup(value); 195 cgit_agefile = xstrdup(value);
196 else if (!strcmp(name, "renamelimit")) 196 else if (!strcmp(name, "renamelimit"))
197 cgit_renamelimit = atoi(value); 197 cgit_renamelimit = atoi(value);
198 else if (!strcmp(name, "repo.group")) 198 else if (!strcmp(name, "repo.group"))
199 cgit_repo_group = xstrdup(value); 199 cgit_repo_group = xstrdup(value);
200 else if (!strcmp(name, "repo.url")) 200 else if (!strcmp(name, "repo.url"))
201 cgit_repo = add_repo(value); 201 cgit_repo = add_repo(value);
202 else if (!strcmp(name, "repo.name")) 202 else if (!strcmp(name, "repo.name"))
203 cgit_repo->name = xstrdup(value); 203 cgit_repo->name = xstrdup(value);
204 else if (cgit_repo && !strcmp(name, "repo.path")) 204 else if (cgit_repo && !strcmp(name, "repo.path"))
205 cgit_repo->path = trim_end(value, '/'); 205 cgit_repo->path = trim_end(value, '/');
206 else if (cgit_repo && !strcmp(name, "repo.desc")) 206 else if (cgit_repo && !strcmp(name, "repo.desc"))
207 cgit_repo->desc = xstrdup(value); 207 cgit_repo->desc = xstrdup(value);
208 else if (cgit_repo && !strcmp(name, "repo.owner")) 208 else if (cgit_repo && !strcmp(name, "repo.owner"))
209 cgit_repo->owner = xstrdup(value); 209 cgit_repo->owner = xstrdup(value);
210 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 210 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
211 cgit_repo->defbranch = xstrdup(value); 211 cgit_repo->defbranch = xstrdup(value);
212 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 212 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
213 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 213 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
214 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 214 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
215 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 215 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
216 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 216 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
217 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 217 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
218 else if (cgit_repo && !strcmp(name, "repo.module-link")) 218 else if (cgit_repo && !strcmp(name, "repo.module-link"))
219 cgit_repo->module_link= xstrdup(value); 219 cgit_repo->module_link= xstrdup(value);
220 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 220 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
221 if (*value == '/') 221 if (*value == '/')
222 cgit_repo->readme = xstrdup(value); 222 cgit_repo->readme = xstrdup(value);
223 else 223 else
224 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 224 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
225 } else if (!strcmp(name, "include")) 225 } else if (!strcmp(name, "include"))
226 cgit_read_config(value, cgit_global_config_cb); 226 cgit_read_config(value, cgit_global_config_cb);
227} 227}
228 228
229void cgit_querystring_cb(const char *name, const char *value) 229void cgit_querystring_cb(const char *name, const char *value)
230{ 230{
231 if (!strcmp(name,"r")) { 231 if (!strcmp(name,"r")) {
232 cgit_query_repo = xstrdup(value); 232 cgit_query_repo = xstrdup(value);
233 cgit_repo = cgit_get_repoinfo(value); 233 cgit_repo = cgit_get_repoinfo(value);
234 } else if (!strcmp(name, "p")) { 234 } else if (!strcmp(name, "p")) {
235 cgit_query_page = xstrdup(value); 235 cgit_query_page = xstrdup(value);
236 cgit_cmd = cgit_get_cmd_index(value); 236 cgit_cmd = cgit_get_cmd_index(value);
237 } else if (!strcmp(name, "url")) { 237 } else if (!strcmp(name, "url")) {
238 cgit_parse_url(value); 238 cgit_parse_url(value);
239 } else if (!strcmp(name, "qt")) { 239 } else if (!strcmp(name, "qt")) {
240 cgit_query_grep = xstrdup(value); 240 cgit_query_grep = xstrdup(value);
241 } else if (!strcmp(name, "q")) { 241 } else if (!strcmp(name, "q")) {
242 cgit_query_search = xstrdup(value); 242 cgit_query_search = xstrdup(value);
243 } else if (!strcmp(name, "h")) { 243 } else if (!strcmp(name, "h")) {
244 cgit_query_head = xstrdup(value); 244 cgit_query_head = xstrdup(value);
245 cgit_query_has_symref = 1; 245 cgit_query_has_symref = 1;
246 } else if (!strcmp(name, "id")) { 246 } else if (!strcmp(name, "id")) {
247 cgit_query_sha1 = xstrdup(value); 247 cgit_query_sha1 = xstrdup(value);
248 cgit_query_has_sha1 = 1; 248 cgit_query_has_sha1 = 1;
249 } else if (!strcmp(name, "id2")) { 249 } else if (!strcmp(name, "id2")) {
250 cgit_query_sha2 = xstrdup(value); 250 cgit_query_sha2 = xstrdup(value);
251 cgit_query_has_sha1 = 1; 251 cgit_query_has_sha1 = 1;
252 } else if (!strcmp(name, "ofs")) { 252 } else if (!strcmp(name, "ofs")) {
253 cgit_query_ofs = atoi(value); 253 cgit_query_ofs = atoi(value);
254 } else if (!strcmp(name, "path")) { 254 } else if (!strcmp(name, "path")) {
255 cgit_query_path = trim_end(value, '/'); 255 cgit_query_path = trim_end(value, '/');
256 } else if (!strcmp(name, "name")) { 256 } else if (!strcmp(name, "name")) {
257 cgit_query_name = xstrdup(value); 257 cgit_query_name = xstrdup(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')
354 continue; 385 continue;
355 ((filepair_fn)data)(q->queue[i]); 386 ((filepair_fn)data)(q->queue[i]);
356 } 387 }
357} 388}
358 389
359static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 390static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
360{ 391{
361 enum object_type type; 392 enum object_type type;
362 393
363 if (is_null_sha1(sha1)) { 394 if (is_null_sha1(sha1)) {
364 file->ptr = (char *)""; 395 file->ptr = (char *)"";
365 file->size = 0; 396 file->size = 0;
366 } else { 397 } else {
367 file->ptr = read_sha1_file(sha1, &type, 398 file->ptr = read_sha1_file(sha1, &type,
368 (unsigned long *)&file->size); 399 (unsigned long *)&file->size);
369 } 400 }
370 return 1; 401 return 1;
371} 402}
372 403
373/* 404/*
374 * Receive diff-buffers from xdiff and concatenate them as 405 * Receive diff-buffers from xdiff and concatenate them as
375 * needed across multiple callbacks. 406 * needed across multiple callbacks.
376 * 407 *
377 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 408 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
378 * ripped from git and modified to use globals instead of 409 * ripped from git and modified to use globals instead of
379 * a special callback-struct. 410 * a special callback-struct.
380 */ 411 */
381char *diffbuf = NULL; 412char *diffbuf = NULL;
382int buflen = 0; 413int buflen = 0;
383 414
384int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 415int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
385{ 416{
386 int i; 417 int i;
387 418
388 for (i = 0; i < nbuf; i++) { 419 for (i = 0; i < nbuf; i++) {
389 if (mb[i].ptr[mb[i].size-1] != '\n') { 420 if (mb[i].ptr[mb[i].size-1] != '\n') {
390 /* Incomplete line */ 421 /* Incomplete line */
391 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 422 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
392 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 423 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
393 buflen += mb[i].size; 424 buflen += mb[i].size;
394 continue; 425 continue;
395 } 426 }
396 427
397 /* we have a complete line */ 428 /* we have a complete line */
398 if (!diffbuf) { 429 if (!diffbuf) {
399 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 430 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
400 continue; 431 continue;
401 } 432 }
402 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 433 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
403 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 434 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
404 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 435 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
405 free(diffbuf); 436 free(diffbuf);
406 diffbuf = NULL; 437 diffbuf = NULL;
407 buflen = 0; 438 buflen = 0;
408 } 439 }
409 if (diffbuf) { 440 if (diffbuf) {
410 ((linediff_fn)priv)(diffbuf, buflen); 441 ((linediff_fn)priv)(diffbuf, buflen);
411 free(diffbuf); 442 free(diffbuf);
412 diffbuf = NULL; 443 diffbuf = NULL;
413 buflen = 0; 444 buflen = 0;
414 } 445 }
415 return 0; 446 return 0;
416} 447}
417 448
418int cgit_diff_files(const unsigned char *old_sha1, 449int cgit_diff_files(const unsigned char *old_sha1,
419 const unsigned char *new_sha1, 450 const unsigned char *new_sha1,
420 linediff_fn fn) 451 linediff_fn fn)
421{ 452{
422 mmfile_t file1, file2; 453 mmfile_t file1, file2;
423 xpparam_t diff_params; 454 xpparam_t diff_params;
424 xdemitconf_t emit_params; 455 xdemitconf_t emit_params;
425 xdemitcb_t emit_cb; 456 xdemitcb_t emit_cb;
426 457
427 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 458 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
428 return 1; 459 return 1;
429 460
430 diff_params.flags = XDF_NEED_MINIMAL; 461 diff_params.flags = XDF_NEED_MINIMAL;
431 emit_params.ctxlen = 3; 462 emit_params.ctxlen = 3;
432 emit_params.flags = XDL_EMIT_FUNCNAMES; 463 emit_params.flags = XDL_EMIT_FUNCNAMES;
433 emit_params.find_func = NULL; 464 emit_params.find_func = NULL;
434 emit_cb.outf = filediff_cb; 465 emit_cb.outf = filediff_cb;
435 emit_cb.priv = fn; 466 emit_cb.priv = fn;
436 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 467 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
437 return 0; 468 return 0;
438} 469}
439 470
440void cgit_diff_tree(const unsigned char *old_sha1, 471void cgit_diff_tree(const unsigned char *old_sha1,
441 const unsigned char *new_sha1, 472 const unsigned char *new_sha1,
442 filepair_fn fn, const char *prefix) 473 filepair_fn fn, const char *prefix)
443{ 474{
444 struct diff_options opt; 475 struct diff_options opt;
445 int ret; 476 int ret;
446 int prefixlen; 477 int prefixlen;
447 478
448 diff_setup(&opt); 479 diff_setup(&opt);
449 opt.output_format = DIFF_FORMAT_CALLBACK; 480 opt.output_format = DIFF_FORMAT_CALLBACK;
450 opt.detect_rename = 1; 481 opt.detect_rename = 1;
451 opt.rename_limit = cgit_renamelimit; 482 opt.rename_limit = cgit_renamelimit;
452 opt.recursive = 1; 483 opt.recursive = 1;
453 opt.format_callback = cgit_diff_tree_cb; 484 opt.format_callback = cgit_diff_tree_cb;
454 opt.format_callback_data = fn; 485 opt.format_callback_data = fn;
455 if (prefix) { 486 if (prefix) {
456 opt.nr_paths = 1; 487 opt.nr_paths = 1;
457 opt.paths = &prefix; 488 opt.paths = &prefix;
458 prefixlen = strlen(prefix); 489 prefixlen = strlen(prefix);
459 opt.pathlens = &prefixlen; 490 opt.pathlens = &prefixlen;
460 } 491 }
461 diff_setup_done(&opt); 492 diff_setup_done(&opt);
462 493
463 if (old_sha1 && !is_null_sha1(old_sha1)) 494 if (old_sha1 && !is_null_sha1(old_sha1))
464 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 495 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
465 else 496 else
466 ret = diff_root_tree_sha1(new_sha1, "", &opt); 497 ret = diff_root_tree_sha1(new_sha1, "", &opt);
467 diffcore_std(&opt); 498 diffcore_std(&opt);
468 diff_flush(&opt); 499 diff_flush(&opt);
469} 500}
470 501
471void cgit_diff_commit(struct commit *commit, filepair_fn fn) 502void cgit_diff_commit(struct commit *commit, filepair_fn fn)
472{ 503{
473 unsigned char *old_sha1 = NULL; 504 unsigned char *old_sha1 = NULL;
474 505
475 if (commit->parents) 506 if (commit->parents)
476 old_sha1 = commit->parents->item->object.sha1; 507 old_sha1 = commit->parents->item->object.sha1;
477 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 508 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
478} 509}
diff --git a/ui-shared.c b/ui-shared.c
index 1418010..1d66940 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,447 +1,519 @@
1/* ui-shared.c: common web output functions 1/* ui-shared.c: common web output functions
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
11const char cgit_doctype[] = 11const char cgit_doctype[] =
12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
14 14
15static char *http_date(time_t t) 15static char *http_date(time_t t)
16{ 16{
17 static char day[][4] = 17 static char day[][4] =
18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
19 static char month[][4] = 19 static char month[][4] =
20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
22 struct tm *tm = gmtime(&t); 22 struct tm *tm = gmtime(&t);
23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
25 tm->tm_hour, tm->tm_min, tm->tm_sec); 25 tm->tm_hour, tm->tm_min, tm->tm_sec);
26} 26}
27 27
28static long ttl_seconds(long ttl) 28static long ttl_seconds(long ttl)
29{ 29{
30 if (ttl<0) 30 if (ttl<0)
31 return 60 * 60 * 24 * 365; 31 return 60 * 60 * 24 * 365;
32 else 32 else
33 return ttl * 60; 33 return ttl * 60;
34} 34}
35 35
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_fileurl(const char *reponame, const char *pagename, 60char *cgit_fileurl(const char *reponame, const char *pagename,
61 const char *filename, 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?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
66 pagename, filename?filename:"", 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, 78char *cgit_pageurl(const char *reponame, const char *pagename,
79 const char *query) 79 const char *query)
80{ 80{
81 return cgit_fileurl(reponame,pagename,0,query); 81 return cgit_fileurl(reponame,pagename,0,query);
82} 82}
83 83
84const char *cgit_repobasename(const char *reponame) 84const char *cgit_repobasename(const char *reponame)
85{ 85{
86 /* I assume we don't need to store more than one repo basename */ 86 /* I assume we don't need to store more than one repo basename */
87 static char rvbuf[1024]; 87 static char rvbuf[1024];
88 int p; 88 int p;
89 const char *rv; 89 const char *rv;
90 strncpy(rvbuf,reponame,sizeof(rvbuf)); 90 strncpy(rvbuf,reponame,sizeof(rvbuf));
91 if(rvbuf[sizeof(rvbuf)-1]) 91 if(rvbuf[sizeof(rvbuf)-1])
92 die("cgit_repobasename: truncated repository name '%s'", reponame); 92 die("cgit_repobasename: truncated repository name '%s'", reponame);
93 p = strlen(rvbuf)-1; 93 p = strlen(rvbuf)-1;
94 /* strip trailing slashes */ 94 /* strip trailing slashes */
95 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 95 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
96 /* strip trailing .git */ 96 /* strip trailing .git */
97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
98 p -= 3; rvbuf[p--] = 0; 98 p -= 3; rvbuf[p--] = 0;
99 } 99 }
100 /* strip more trailing slashes if any */ 100 /* strip more trailing slashes if any */
101 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 101 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
102 /* find last slash in the remaining string */ 102 /* find last slash in the remaining string */
103 rv = strrchr(rvbuf,'/'); 103 rv = strrchr(rvbuf,'/');
104 if(rv) 104 if(rv)
105 return ++rv; 105 return ++rv;
106 return rvbuf; 106 return rvbuf;
107} 107}
108 108
109char *cgit_currurl() 109char *cgit_currurl()
110{ 110{
111 if (!cgit_virtual_root) 111 if (!cgit_virtual_root)
112 return cgit_script_name; 112 return cgit_script_name;
113 else if (cgit_query_page) 113 else if (cgit_query_page)
114 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);
115 else if (cgit_query_repo) 115 else if (cgit_query_repo)
116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
117 else 117 else
118 return fmt("%s/", cgit_virtual_root); 118 return fmt("%s/", cgit_virtual_root);
119} 119}
120 120
121static char *repolink(char *title, char *class, char *page, char *head, 121static char *repolink(char *title, char *class, char *page, char *head,
122 char *path) 122 char *path)
123{ 123{
124 char *delim = "?"; 124 char *delim = "?";
125 125
126 html("<a"); 126 html("<a");
127 if (title) { 127 if (title) {
128 html(" title='"); 128 html(" title='");
129 html_attr(title); 129 html_attr(title);
130 html("'"); 130 html("'");
131 } 131 }
132 if (class) { 132 if (class) {
133 html(" class='"); 133 html(" class='");
134 html_attr(class); 134 html_attr(class);
135 html("'"); 135 html("'");
136 } 136 }
137 html(" href='"); 137 html(" href='");
138 if (cgit_virtual_root) { 138 if (cgit_virtual_root) {
139 html_attr(cgit_virtual_root); 139 html_attr(cgit_virtual_root);
140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') 140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
141 html("/"); 141 html("/");
142 html_attr(cgit_repo->url); 142 html_attr(cgit_repo->url);
143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
144 html("/"); 144 html("/");
145 if (page) { 145 if (page) {
146 html(page); 146 html(page);
147 html("/"); 147 html("/");
148 if (path) 148 if (path)
149 html_attr(path); 149 html_attr(path);
150 } 150 }
151 } else { 151 } else {
152 html(cgit_script_name); 152 html(cgit_script_name);
153 html("?url="); 153 html("?url=");
154 html_attr(cgit_repo->url); 154 html_attr(cgit_repo->url);
155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
156 html("/"); 156 html("/");
157 if (page) { 157 if (page) {
158 html(page); 158 html(page);
159 html("/"); 159 html("/");
160 if (path) 160 if (path)
161 html_attr(path); 161 html_attr(path);
162 } 162 }
163 delim = "&amp;"; 163 delim = "&amp;";
164 } 164 }
165 if (head && strcmp(head, cgit_repo->defbranch)) { 165 if (head && strcmp(head, cgit_repo->defbranch)) {
166 html(delim); 166 html(delim);
167 html("h="); 167 html("h=");
168 html_attr(head); 168 html_attr(head);
169 delim = "&amp;"; 169 delim = "&amp;";
170 } 170 }
171 return fmt("%s", delim); 171 return fmt("%s", delim);
172} 172}
173 173
174static void reporevlink(char *page, char *name, char *title, char *class, 174static void reporevlink(char *page, char *name, char *title, char *class,
175 char *head, char *rev, char *path) 175 char *head, char *rev, char *path)
176{ 176{
177 char *delim; 177 char *delim;
178 178
179 delim = repolink(title, class, page, head, path); 179 delim = repolink(title, class, page, head, path);
180 if (rev && strcmp(rev, cgit_query_head)) { 180 if (rev && strcmp(rev, cgit_query_head)) {
181 html(delim); 181 html(delim);
182 html("id="); 182 html("id=");
183 html_attr(rev); 183 html_attr(rev);
184 } 184 }
185 html("'>"); 185 html("'>");
186 html_txt(name); 186 html_txt(name);
187 html("</a>"); 187 html("</a>");
188} 188}
189 189
190void cgit_tree_link(char *name, char *title, char *class, char *head, 190void cgit_tree_link(char *name, char *title, char *class, char *head,
191 char *rev, char *path) 191 char *rev, char *path)
192{ 192{
193 reporevlink("tree", name, title, class, head, rev, path); 193 reporevlink("tree", name, title, class, head, rev, path);
194} 194}
195 195
196void cgit_log_link(char *name, char *title, char *class, char *head, 196void cgit_log_link(char *name, char *title, char *class, char *head,
197 char *rev, char *path, int ofs) 197 char *rev, char *path, int ofs)
198{ 198{
199 char *delim; 199 char *delim;
200 200
201 delim = repolink(title, class, "log", head, path); 201 delim = repolink(title, class, "log", head, path);
202 if (rev && strcmp(rev, cgit_query_head)) { 202 if (rev && strcmp(rev, cgit_query_head)) {
203 html(delim); 203 html(delim);
204 html("id="); 204 html("id=");
205 html_attr(rev); 205 html_attr(rev);
206 delim = "&"; 206 delim = "&";
207 } 207 }
208 if (ofs > 0) { 208 if (ofs > 0) {
209 html(delim); 209 html(delim);
210 html("ofs="); 210 html("ofs=");
211 htmlf("%d", ofs); 211 htmlf("%d", ofs);
212 } 212 }
213 html("'>"); 213 html("'>");
214 html_txt(name); 214 html_txt(name);
215 html("</a>"); 215 html("</a>");
216} 216}
217 217
218void cgit_commit_link(char *name, char *title, char *class, char *head, 218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev) 219 char *rev)
220{ 220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0'; 222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.'; 223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.'; 224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.'; 225 name[cgit_max_msg_len - 3] = '.';
226 } 226 }
227 reporevlink("commit", name, title, class, head, rev, NULL); 227 reporevlink("commit", name, title, class, head, rev, NULL);
228} 228}
229 229
230void cgit_refs_link(char *name, char *title, char *class, char *head, 230void cgit_refs_link(char *name, char *title, char *class, char *head,
231 char *rev, char *path) 231 char *rev, char *path)
232{ 232{
233 reporevlink("refs", name, title, class, head, rev, path); 233 reporevlink("refs", name, title, class, head, rev, path);
234} 234}
235 235
236void cgit_snapshot_link(char *name, char *title, char *class, char *head, 236void cgit_snapshot_link(char *name, char *title, char *class, char *head,
237 char *rev, char *archivename) 237 char *rev, char *archivename)
238{ 238{
239 reporevlink("snapshot", name, title, class, head, rev, archivename); 239 reporevlink("snapshot", name, title, class, head, rev, archivename);
240} 240}
241 241
242void cgit_diff_link(char *name, char *title, char *class, char *head, 242void cgit_diff_link(char *name, char *title, char *class, char *head,
243 char *new_rev, char *old_rev, char *path) 243 char *new_rev, char *old_rev, char *path)
244{ 244{
245 char *delim; 245 char *delim;
246 246
247 delim = repolink(title, class, "diff", head, path); 247 delim = repolink(title, class, "diff", head, path);
248 if (new_rev && strcmp(new_rev, cgit_query_head)) { 248 if (new_rev && strcmp(new_rev, cgit_query_head)) {
249 html(delim); 249 html(delim);
250 html("id="); 250 html("id=");
251 html_attr(new_rev); 251 html_attr(new_rev);
252 delim = "&amp;"; 252 delim = "&amp;";
253 } 253 }
254 if (old_rev) { 254 if (old_rev) {
255 html(delim); 255 html(delim);
256 html("id2="); 256 html("id2=");
257 html_attr(old_rev); 257 html_attr(old_rev);
258 } 258 }
259 html("'>"); 259 html("'>");
260 html_txt(name); 260 html_txt(name);
261 html("</a>"); 261 html("</a>");
262} 262}
263 263
264void cgit_object_link(struct object *obj) 264void cgit_object_link(struct object *obj)
265{ 265{
266 char *page, *arg, *url; 266 char *page, *arg, *url;
267 267
268 if (obj->type == OBJ_COMMIT) { 268 if (obj->type == OBJ_COMMIT) {
269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
270 cgit_query_head, sha1_to_hex(obj->sha1)); 270 cgit_query_head, sha1_to_hex(obj->sha1));
271 return; 271 return;
272 } else if (obj->type == OBJ_TREE) { 272 } else if (obj->type == OBJ_TREE) {
273 page = "tree"; 273 page = "tree";
274 arg = "id"; 274 arg = "id";
275 } else if (obj->type == OBJ_TAG) { 275 } else if (obj->type == OBJ_TAG) {
276 page = "tag"; 276 page = "tag";
277 arg = "id"; 277 arg = "id";
278 } else { 278 } else {
279 page = "blob"; 279 page = "blob";
280 arg = "id"; 280 arg = "id";
281 } 281 }
282 282
283 url = cgit_pageurl(cgit_query_repo, page, 283 url = cgit_pageurl(cgit_query_repo, page,
284 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 284 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
285 html_link_open(url, NULL, NULL); 285 html_link_open(url, NULL, NULL);
286 htmlf("%s %s", typename(obj->type), 286 htmlf("%s %s", typename(obj->type),
287 sha1_to_hex(obj->sha1)); 287 sha1_to_hex(obj->sha1));
288 html_link_close(); 288 html_link_close();
289} 289}
290 290
291void cgit_print_date(time_t secs, char *format) 291void cgit_print_date(time_t secs, char *format)
292{ 292{
293 char buf[64]; 293 char buf[64];
294 struct tm *time; 294 struct tm *time;
295 295
296 time = gmtime(&secs); 296 time = gmtime(&secs);
297 strftime(buf, sizeof(buf)-1, format, time); 297 strftime(buf, sizeof(buf)-1, format, time);
298 html_txt(buf); 298 html_txt(buf);
299} 299}
300 300
301void cgit_print_age(time_t t, time_t max_relative, char *format) 301void cgit_print_age(time_t t, time_t max_relative, char *format)
302{ 302{
303 time_t now, secs; 303 time_t now, secs;
304 304
305 time(&now); 305 time(&now);
306 secs = now - t; 306 secs = now - t;
307 307
308 if (secs > max_relative && max_relative >= 0) { 308 if (secs > max_relative && max_relative >= 0) {
309 cgit_print_date(t, format); 309 cgit_print_date(t, format);
310 return; 310 return;
311 } 311 }
312 312
313 if (secs < TM_HOUR * 2) { 313 if (secs < TM_HOUR * 2) {
314 htmlf("<span class='age-mins'>%.0f min.</span>", 314 htmlf("<span class='age-mins'>%.0f min.</span>",
315 secs * 1.0 / TM_MIN); 315 secs * 1.0 / TM_MIN);
316 return; 316 return;
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"); 366}
367
368int print_branch_option(const char *refname, const unsigned char *sha1,
369 int flags, void *cb_data)
370{
371 char *name = (char *)refname;
372 html_option(name, name, cgit_query_head);
373 return 0;
374}
375
376int print_archive_ref(const char *refname, const unsigned char *sha1,
377 int flags, void *cb_data)
378{
379 struct tag *tag;
380 struct taginfo *info;
381 struct object *obj;
382 char buf[256], *url;
383 unsigned char fileid[20];
384 int *header = (int *)cb_data;
385
386 if (prefixcmp(refname, "refs/archives"))
387 return 0;
388 strncpy(buf, refname+14, sizeof(buf));
389 obj = parse_object(sha1);
390 if (!obj)
391 return 1;
392 if (obj->type == OBJ_TAG) {
393 tag = lookup_tag(sha1);
394 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
395 return 0;
396 hashcpy(fileid, tag->tagged->sha1);
397 } else if (obj->type != OBJ_BLOB) {
398 return 0;
399 } else {
400 hashcpy(fileid, sha1);
401 }
402 if (!*header) {
403 html("<p><h1>download</h1>");
404 *header = 1;
405 }
406 url = cgit_pageurl(cgit_query_repo, "blob",
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{
417 if (!cgit_virtual_root) {
418 if (cgit_query_repo)
419 html_hidden("r", cgit_query_repo);
420 if (cgit_query_page)
421 html_hidden("p", cgit_query_page);
422 }
423
424 if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch))
425 html_hidden("h", cgit_query_head);
426
427 if (cgit_query_sha1)
428 html_hidden("id", cgit_query_sha1);
429 if (cgit_query_sha2)
430 html_hidden("id2", cgit_query_sha2);
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 }
367} 438}
368 439
369void cgit_print_pageheader(char *title, int show_search) 440void cgit_print_pageheader(char *title, int show_search)
370{ 441{
371 html("<table id='layout'>"); 442 static const char *default_info = "This is cgit, a fast webinterface for git repositories";
372 html("<tr><td id='header'><a href='"); 443 int header = 0;
373 html_attr(cgit_rooturl()); 444
374 html("'>"); 445 html("<div id='sidebar'>\n");
375 html_txt(cgit_root_title);
376 html("</a></td><td id='logo'>");
377 html("<a href='"); 446 html("<a href='");
378 html_attr(cgit_logo_link); 447 html_attr(cgit_rooturl());
379 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 448 htmlf("'><div id='logo'><img src='%s' alt='cgit'/></div></a>\n",
380 html("</td></tr>"); 449 cgit_logo);
381 html("<tr><td id='crumb'>"); 450 html("<div class='infobox'>");
382 if (cgit_query_repo) { 451 if (cgit_query_repo) {
383 html_txt(cgit_repo->name); 452 html("<h1>");
384 html(" ("); 453 html_txt(strrpart(cgit_repo->name, 20));
385 html_txt(cgit_query_head); 454 html("</h1>\n");
386 html(") : &nbsp;"); 455 html_txt(cgit_repo->desc);
387 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, 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,
388 NULL, NULL); 462 NULL, NULL);
389 html(" "); 463 cgit_log_link("log", NULL, "menu", cgit_query_head,
390 cgit_log_link("log", NULL, NULL, cgit_query_head,
391 cgit_query_sha1, cgit_query_path, 0); 464 cgit_query_sha1, cgit_query_path, 0);
392 html(" "); 465 cgit_tree_link("tree", NULL, "menu", cgit_query_head,
393 cgit_tree_link("tree", NULL, NULL, cgit_query_head,
394 cgit_query_sha1, NULL); 466 cgit_query_sha1, NULL);
395 html(" "); 467 cgit_commit_link("commit", NULL, "menu", cgit_query_head,
396 cgit_commit_link("commit", NULL, NULL, cgit_query_head,
397 cgit_query_sha1); 468 cgit_query_sha1);
398 html(" "); 469 cgit_diff_link("diff", NULL, "menu", cgit_query_head,
399 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
400 cgit_query_sha1, cgit_query_sha2, 470 cgit_query_sha1, cgit_query_sha2,
401 cgit_query_path); 471 cgit_query_path);
402 } else { 472
403 html_txt("Index of repositories"); 473 for_each_ref(print_archive_ref, &header);
404 } 474
405 html("</td>"); 475 html("<p>\n<h1>branch</h1>\n");
406 html("<td id='search'>"); 476 html("<form method='get' action=''>\n");
407 if (show_search) { 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");
408 html("<form method='get' action='"); 484 html("<form method='get' action='");
409 html_attr(cgit_currurl()); 485 html_attr(cgit_pageurl(cgit_query_repo, "log", NULL));
410 html("'>"); 486 html("'>\n");
411 if (!cgit_virtual_root) { 487 add_hidden_formfields(1, 0);
412 if (cgit_query_repo) 488 html("<select name='qt'>\n");
413 html_hidden("r", cgit_query_repo);
414 if (cgit_query_page)
415 html_hidden("p", cgit_query_page);
416 }
417 if (cgit_query_head)
418 html_hidden("h", cgit_query_head);
419 if (cgit_query_sha1)
420 html_hidden("id", cgit_query_sha1);
421 if (cgit_query_sha2)
422 html_hidden("id2", cgit_query_sha2);
423 html("<select name='qt'>");
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
@@ -1,253 +1,200 @@
1/* ui-summary.c: functions for generating repo summary page 1/* ui-summary.c: functions for generating repo summary page
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 int header; 11static int header;
12 12
13static int cmp_age(int age1, int age2) 13static int cmp_age(int age1, int age2)
14{ 14{
15 if (age1 != 0 && age2 != 0) 15 if (age1 != 0 && age2 != 0)
16 return age2 - age1; 16 return age2 - age1;
17 17
18 if (age1 == 0 && age2 == 0) 18 if (age1 == 0 && age2 == 0)
19 return 0; 19 return 0;
20 20
21 if (age1 == 0) 21 if (age1 == 0)
22 return +1; 22 return +1;
23 23
24 return -1; 24 return -1;
25} 25}
26 26
27static int cmp_ref_name(const void *a, const void *b) 27static int cmp_ref_name(const void *a, const void *b)
28{ 28{
29 struct refinfo *r1 = *(struct refinfo **)a; 29 struct refinfo *r1 = *(struct refinfo **)a;
30 struct refinfo *r2 = *(struct refinfo **)b; 30 struct refinfo *r2 = *(struct refinfo **)b;
31 31
32 return strcmp(r1->refname, r2->refname); 32 return strcmp(r1->refname, r2->refname);
33} 33}
34 34
35static int cmp_branch_age(const void *a, const void *b) 35static int cmp_branch_age(const void *a, const void *b)
36{ 36{
37 struct refinfo *r1 = *(struct refinfo **)a; 37 struct refinfo *r1 = *(struct refinfo **)a;
38 struct refinfo *r2 = *(struct refinfo **)b; 38 struct refinfo *r2 = *(struct refinfo **)b;
39 39
40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date); 40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
41} 41}
42 42
43static int cmp_tag_age(const void *a, const void *b) 43static int cmp_tag_age(const void *a, const void *b)
44{ 44{
45 struct refinfo *r1 = *(struct refinfo **)a; 45 struct refinfo *r1 = *(struct refinfo **)a;
46 struct refinfo *r2 = *(struct refinfo **)b; 46 struct refinfo *r2 = *(struct refinfo **)b;
47 47
48 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date); 48 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
49} 49}
50 50
51static int print_branch(struct refinfo *ref) 51static int print_branch(struct refinfo *ref)
52{ 52{
53 struct commitinfo *info = ref->commit; 53 struct commitinfo *info = ref->commit;
54 char *name = (char *)ref->refname; 54 char *name = (char *)ref->refname;
55 55
56 if (!info) 56 if (!info)
57 return 1; 57 return 1;
58 html("<tr><td>"); 58 html("<tr><td>");
59 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0); 59 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0);
60 html("</td><td>"); 60 html("</td><td>");
61 61
62 if (ref->object->type == OBJ_COMMIT) { 62 if (ref->object->type == OBJ_COMMIT) {
63 cgit_print_age(info->commit->date, -1, NULL); 63 cgit_print_age(info->commit->date, -1, NULL);
64 html("</td><td>"); 64 html("</td><td>");
65 html_txt(info->author); 65 html_txt(info->author);
66 html("</td><td>"); 66 html("</td><td>");
67 cgit_commit_link(info->subject, NULL, NULL, name, NULL); 67 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
68 } else { 68 } else {
69 html("</td><td></td><td>"); 69 html("</td><td></td><td>");
70 cgit_object_link(ref->object); 70 cgit_object_link(ref->object);
71 } 71 }
72 html("</td></tr>\n"); 72 html("</td></tr>\n");
73 return 0; 73 return 0;
74} 74}
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{
234 html("<div id='summary'>"); 185 if (cgit_repo->readme) {
235 cgit_print_archives(); 186 html("<div id='summary'>");
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}