summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-04-12 18:11:49 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-04-12 18:11:49 (UTC)
commit68cb84839f8fbc20688b22202489f4c2a54d3f55 (patch) (unidiff)
tree315289eaa849faf549c4882b1769b81597587b10
parent7c0d2d9fbd3a29d295c8067f7798507853759eae (diff)
downloadcgit-68cb84839f8fbc20688b22202489f4c2a54d3f55.zip
cgit-68cb84839f8fbc20688b22202489f4c2a54d3f55.tar.gz
cgit-68cb84839f8fbc20688b22202489f4c2a54d3f55.tar.bz2
Reintroduce the branch switcher
Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.css10
-rw-r--r--ui-shared.c7
2 files changed, 17 insertions, 0 deletions
diff --git a/cgit.css b/cgit.css
index 67cf48e..80680c9 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,406 +1,416 @@
1body, table { 1body, table {
2 padding: 0em; 2 padding: 0em;
3 margin: 0em; 3 margin: 0em;
4} 4}
5 5
6body { 6body {
7 font-family: sans; 7 font-family: sans;
8 font-size: 10pt; 8 font-size: 10pt;
9 color: #333; 9 color: #333;
10 background: white; 10 background: white;
11 padding: 4px; 11 padding: 4px;
12} 12}
13 13
14a { 14a {
15 color: blue; 15 color: blue;
16 text-decoration: none; 16 text-decoration: none;
17} 17}
18 18
19a:hover { 19a:hover {
20 text-decoration: underline; 20 text-decoration: underline;
21} 21}
22 22
23table { 23table {
24 border-collapse: collapse; 24 border-collapse: collapse;
25} 25}
26 26
27table#header { 27table#header {
28 width: 100%; 28 width: 100%;
29 margin-bottom: 1em; 29 margin-bottom: 1em;
30} 30}
31 31
32table#header td.logo { 32table#header td.logo {
33 width: 96px; 33 width: 96px;
34} 34}
35 35
36table#header td.main { 36table#header td.main {
37 font-size: 200%; 37 font-size: 200%;
38} 38}
39 39
40table#header td.sub { 40table#header td.sub {
41 color: #777; 41 color: #777;
42 border-top: solid 1px #ccc; 42 border-top: solid 1px #ccc;
43} 43}
44 44
45table.tabs { 45table.tabs {
46 border-bottom: solid 2px #ccc; 46 border-bottom: solid 2px #ccc;
47 border-collapse: collapse; 47 border-collapse: collapse;
48 margin-top: 2em; 48 margin-top: 2em;
49 margin-bottom: 1em; 49 margin-bottom: 1em;
50 width: 100%; 50 width: 100%;
51} 51}
52 52
53table.tabs td { 53table.tabs td {
54 padding: 0px 0.5em; 54 padding: 0px 0.5em;
55 vertical-align: bottom;
55} 56}
56 57
57table.tabs td a { 58table.tabs td a {
58 padding: 2px 1em; 59 padding: 2px 1em;
59 color: #007; 60 color: #007;
60} 61}
61 62
62table.tabs td a.active { 63table.tabs td a.active {
63 color: #000; 64 color: #000;
64 background-color: #ccc; 65 background-color: #ccc;
65} 66}
66 67
68table.tabs td.branch {
69 text-align: right;
70}
71
72table.tabs td.branch form {
73 padding-bottom: 2px;
74 font-size: 90%;
75}
76
67div.content { 77div.content {
68 margin: 0px; 78 margin: 0px;
69 padding: 1em; 79 padding: 1em;
70} 80}
71 81
72 82
73table.list { 83table.list {
74 width: 100%; 84 width: 100%;
75 border: none; 85 border: none;
76 border-collapse: collapse; 86 border-collapse: collapse;
77} 87}
78 88
79table.list tr { 89table.list tr {
80 background: white; 90 background: white;
81} 91}
82 92
83table.list tr:hover { 93table.list tr:hover {
84 background: #eee; 94 background: #eee;
85} 95}
86 96
87table.list tr.nohover:hover { 97table.list tr.nohover:hover {
88 background: white; 98 background: white;
89} 99}
90 100
91table.list th { 101table.list th {
92 font-weight: normal; 102 font-weight: normal;
93 border-bottom: solid 1px #ccc; 103 border-bottom: solid 1px #ccc;
94 padding: 0.1em 0.5em 0.1em 0.5em; 104 padding: 0.1em 0.5em 0.1em 0.5em;
95 vertical-align: baseline; 105 vertical-align: baseline;
96} 106}
97 107
98table.list td { 108table.list td {
99 border: none; 109 border: none;
100 padding: 0.1em 0.5em 0.1em 0.5em; 110 padding: 0.1em 0.5em 0.1em 0.5em;
101} 111}
102 112
103table.list td a { 113table.list td a {
104 color: black; 114 color: black;
105} 115}
106 116
107img { 117img {
108 border: none; 118 border: none;
109} 119}
110 120
111input#switch-btn { 121input#switch-btn {
112 margin: 2px 0px 0px 0px; 122 margin: 2px 0px 0px 0px;
113} 123}
114 124
115td#sidebar input.txt { 125td#sidebar input.txt {
116 width: 100%; 126 width: 100%;
117 margin: 2px 0px 0px 0px; 127 margin: 2px 0px 0px 0px;
118} 128}
119 129
120table#grid { 130table#grid {
121 margin: 0px; 131 margin: 0px;
122} 132}
123 133
124td#content { 134td#content {
125 vertical-align: top; 135 vertical-align: top;
126 padding: 1em 2em 1em 1em; 136 padding: 1em 2em 1em 1em;
127 border: none; 137 border: none;
128} 138}
129 139
130div#summary { 140div#summary {
131 vertical-align: top; 141 vertical-align: top;
132 margin-bottom: 1em; 142 margin-bottom: 1em;
133} 143}
134 144
135table#downloads { 145table#downloads {
136 float: right; 146 float: right;
137 border-collapse: collapse; 147 border-collapse: collapse;
138 border: solid 1px #777; 148 border: solid 1px #777;
139 margin-left: 0.5em; 149 margin-left: 0.5em;
140 margin-bottom: 0.5em; 150 margin-bottom: 0.5em;
141} 151}
142 152
143table#downloads th { 153table#downloads th {
144 background-color: #ccc; 154 background-color: #ccc;
145} 155}
146 156
147div#blob { 157div#blob {
148 border: solid 1px black; 158 border: solid 1px black;
149} 159}
150 160
151div.error { 161div.error {
152 color: red; 162 color: red;
153 font-weight: bold; 163 font-weight: bold;
154 margin: 1em 2em; 164 margin: 1em 2em;
155} 165}
156 166
157a.ls-blob, a.ls-dir, a.ls-mod { 167a.ls-blob, a.ls-dir, a.ls-mod {
158 font-family: monospace; 168 font-family: monospace;
159} 169}
160 170
161td.ls-size { 171td.ls-size {
162 text-align: right; 172 text-align: right;
163} 173}
164 174
165td.ls-size { 175td.ls-size {
166 font-family: monospace; 176 font-family: monospace;
167} 177}
168 178
169td.ls-mode { 179td.ls-mode {
170 font-family: monospace; 180 font-family: monospace;
171} 181}
172 182
173table.blob { 183table.blob {
174 margin-top: 0.5em; 184 margin-top: 0.5em;
175 border-top: solid 1px black; 185 border-top: solid 1px black;
176} 186}
177 187
178table.blob td.no { 188table.blob td.no {
179 border-right: solid 1px black; 189 border-right: solid 1px black;
180 color: black; 190 color: black;
181 background-color: #eee; 191 background-color: #eee;
182 text-align: right; 192 text-align: right;
183} 193}
184 194
185table.blob td.no a { 195table.blob td.no a {
186 color: black; 196 color: black;
187} 197}
188 198
189table.blob td.no a:hover { 199table.blob td.no a:hover {
190 color: black; 200 color: black;
191 text-decoration: none; 201 text-decoration: none;
192} 202}
193 203
194table.blob td.txt { 204table.blob td.txt {
195 white-space: pre; 205 white-space: pre;
196 font-family: monospace; 206 font-family: monospace;
197 padding-left: 0.5em; 207 padding-left: 0.5em;
198} 208}
199 209
200table.nowrap td { 210table.nowrap td {
201 white-space: nowrap; 211 white-space: nowrap;
202} 212}
203 213
204table.commit-info { 214table.commit-info {
205 border-collapse: collapse; 215 border-collapse: collapse;
206 margin-top: 1.5em; 216 margin-top: 1.5em;
207} 217}
208 218
209table.commit-info th { 219table.commit-info th {
210 text-align: left; 220 text-align: left;
211 font-weight: normal; 221 font-weight: normal;
212 padding: 0.1em 1em 0.1em 0.1em; 222 padding: 0.1em 1em 0.1em 0.1em;
213 vertical-align: top; 223 vertical-align: top;
214} 224}
215 225
216table.commit-info td { 226table.commit-info td {
217 font-weight: normal; 227 font-weight: normal;
218 padding: 0.1em 1em 0.1em 0.1em; 228 padding: 0.1em 1em 0.1em 0.1em;
219} 229}
220 230
221div.commit-subject { 231div.commit-subject {
222 font-weight: bold; 232 font-weight: bold;
223 font-size: 125%; 233 font-size: 125%;
224 margin: 1.5em 0em 0.5em 0em; 234 margin: 1.5em 0em 0.5em 0em;
225 padding: 0em; 235 padding: 0em;
226} 236}
227 237
228div.commit-msg { 238div.commit-msg {
229 white-space: pre; 239 white-space: pre;
230 font-family: monospace; 240 font-family: monospace;
231} 241}
232 242
233div.diffstat-header { 243div.diffstat-header {
234 font-weight: bold; 244 font-weight: bold;
235 padding-top: 1.5em; 245 padding-top: 1.5em;
236} 246}
237 247
238table.diffstat { 248table.diffstat {
239 border-collapse: collapse; 249 border-collapse: collapse;
240 border: solid 1px #aaa; 250 border: solid 1px #aaa;
241 background-color: #eee; 251 background-color: #eee;
242} 252}
243 253
244table.diffstat th { 254table.diffstat th {
245 font-weight: normal; 255 font-weight: normal;
246 text-align: left; 256 text-align: left;
247 text-decoration: underline; 257 text-decoration: underline;
248 padding: 0.1em 1em 0.1em 0.1em; 258 padding: 0.1em 1em 0.1em 0.1em;
249 font-size: 100%; 259 font-size: 100%;
250} 260}
251 261
252table.diffstat td { 262table.diffstat td {
253 padding: 0.2em 0.2em 0.1em 0.1em; 263 padding: 0.2em 0.2em 0.1em 0.1em;
254 font-size: 100%; 264 font-size: 100%;
255 border: none; 265 border: none;
256} 266}
257 267
258table.diffstat td.mode { 268table.diffstat td.mode {
259 white-space: nowrap; 269 white-space: nowrap;
260} 270}
261 271
262table.diffstat td span.modechange { 272table.diffstat td span.modechange {
263 padding-left: 1em; 273 padding-left: 1em;
264 color: red; 274 color: red;
265} 275}
266 276
267table.diffstat td.add a { 277table.diffstat td.add a {
268 color: green; 278 color: green;
269} 279}
270 280
271table.diffstat td.del a { 281table.diffstat td.del a {
272 color: red; 282 color: red;
273} 283}
274 284
275table.diffstat td.upd a { 285table.diffstat td.upd a {
276 color: blue; 286 color: blue;
277} 287}
278 288
279table.diffstat td.graph { 289table.diffstat td.graph {
280 width: 500px; 290 width: 500px;
281 vertical-align: middle; 291 vertical-align: middle;
282} 292}
283 293
284table.diffstat td.graph table { 294table.diffstat td.graph table {
285 border: none; 295 border: none;
286} 296}
287 297
288table.diffstat td.graph td { 298table.diffstat td.graph td {
289 padding: 0px; 299 padding: 0px;
290 border: 0px; 300 border: 0px;
291 height: 7pt; 301 height: 7pt;
292} 302}
293 303
294table.diffstat td.graph td.add { 304table.diffstat td.graph td.add {
295 background-color: #5c5; 305 background-color: #5c5;
296} 306}
297 307
298table.diffstat td.graph td.rem { 308table.diffstat td.graph td.rem {
299 background-color: #c55; 309 background-color: #c55;
300} 310}
301 311
302div.diffstat-summary { 312div.diffstat-summary {
303 color: #888; 313 color: #888;
304 padding-top: 0.5em; 314 padding-top: 0.5em;
305} 315}
306 316
307table.diff { 317table.diff {
308 width: 100%; 318 width: 100%;
309} 319}
310 320
311table.diff td { 321table.diff td {
312 font-family: monospace; 322 font-family: monospace;
313 white-space: pre; 323 white-space: pre;
314} 324}
315 325
316table.diff td div.head { 326table.diff td div.head {
317 font-weight: bold; 327 font-weight: bold;
318 margin-top: 1em; 328 margin-top: 1em;
319 color: black; 329 color: black;
320} 330}
321 331
322table.diff td div.hunk { 332table.diff td div.hunk {
323 color: #009; 333 color: #009;
324} 334}
325 335
326table.diff td div.add { 336table.diff td div.add {
327 color: green; 337 color: green;
328} 338}
329 339
330table.diff td div.del { 340table.diff td div.del {
331 color: red; 341 color: red;
332} 342}
333 343
334.sha1 { 344.sha1 {
335 font-family: monospace; 345 font-family: monospace;
336 font-size: 90%; 346 font-size: 90%;
337} 347}
338 348
339.left { 349.left {
340 text-align: left; 350 text-align: left;
341} 351}
342 352
343.right { 353.right {
344 text-align: right; 354 text-align: right;
345} 355}
346 356
347table.list td.repogroup { 357table.list td.repogroup {
348 font-style: italic; 358 font-style: italic;
349 color: #888; 359 color: #888;
350} 360}
351 361
352a.button { 362a.button {
353 font-size: 80%; 363 font-size: 80%;
354 color: #aaa; 364 color: #aaa;
355 background-color: #eee; 365 background-color: #eee;
356 border: solid 1px #aaa; 366 border: solid 1px #aaa;
357 padding: 0em 0.5em; 367 padding: 0em 0.5em;
358 margin: 0.1em 0.25em; 368 margin: 0.1em 0.25em;
359} 369}
360 370
361a.button:hover { 371a.button:hover {
362 text-decoration: none; 372 text-decoration: none;
363 color: #333; 373 color: #333;
364 background-color: #ccc; 374 background-color: #ccc;
365} 375}
366 376
367a.primary { 377a.primary {
368 font-size: 100%; 378 font-size: 100%;
369} 379}
370 380
371a.secondary { 381a.secondary {
372 font-size: 90%; 382 font-size: 90%;
373} 383}
374 384
375td.toplevel-repo { 385td.toplevel-repo {
376 386
377} 387}
378 388
379table.list td.sublevel-repo { 389table.list td.sublevel-repo {
380 padding-left: 1.5em; 390 padding-left: 1.5em;
381} 391}
382 392
383span.age-mins { 393span.age-mins {
384 font-weight: bold; 394 font-weight: bold;
385 color: #080; 395 color: #080;
386} 396}
387 397
388span.age-hours { 398span.age-hours {
389 color: #080; 399 color: #080;
390} 400}
391 401
392span.age-days { 402span.age-days {
393 color: #040; 403 color: #040;
394} 404}
395 405
396span.age-weeks { 406span.age-weeks {
397 color: #444; 407 color: #444;
398} 408}
399 409
400span.age-months { 410span.age-months {
401 color: #888; 411 color: #888;
402} 412}
403 413
404span.age-years { 414span.age-years {
405 color: #bbb; 415 color: #bbb;
406} 416}
diff --git a/ui-shared.c b/ui-shared.c
index 03d147f..73e3d4c 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,560 +1,567 @@
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#include "cmd.h" 10#include "cmd.h"
11#include "html.h" 11#include "html.h"
12 12
13const char cgit_doctype[] = 13const char cgit_doctype[] =
14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
16 16
17static char *http_date(time_t t) 17static char *http_date(time_t t)
18{ 18{
19 static char day[][4] = 19 static char day[][4] =
20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
21 static char month[][4] = 21 static char month[][4] =
22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
23 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 23 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
24 struct tm *tm = gmtime(&t); 24 struct tm *tm = gmtime(&t);
25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
27 tm->tm_hour, tm->tm_min, tm->tm_sec); 27 tm->tm_hour, tm->tm_min, tm->tm_sec);
28} 28}
29 29
30void cgit_print_error(char *msg) 30void cgit_print_error(char *msg)
31{ 31{
32 html("<div class='error'>"); 32 html("<div class='error'>");
33 html_txt(msg); 33 html_txt(msg);
34 html("</div>\n"); 34 html("</div>\n");
35} 35}
36 36
37char *cgit_rooturl() 37char *cgit_rooturl()
38{ 38{
39 if (ctx.cfg.virtual_root) 39 if (ctx.cfg.virtual_root)
40 return fmt("%s/", ctx.cfg.virtual_root); 40 return fmt("%s/", ctx.cfg.virtual_root);
41 else 41 else
42 return ctx.cfg.script_name; 42 return ctx.cfg.script_name;
43} 43}
44 44
45char *cgit_repourl(const char *reponame) 45char *cgit_repourl(const char *reponame)
46{ 46{
47 if (ctx.cfg.virtual_root) { 47 if (ctx.cfg.virtual_root) {
48 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); 48 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
49 } else { 49 } else {
50 return fmt("?r=%s", reponame); 50 return fmt("?r=%s", reponame);
51 } 51 }
52} 52}
53 53
54char *cgit_fileurl(const char *reponame, const char *pagename, 54char *cgit_fileurl(const char *reponame, const char *pagename,
55 const char *filename, const char *query) 55 const char *filename, const char *query)
56{ 56{
57 char *tmp; 57 char *tmp;
58 char *delim; 58 char *delim;
59 59
60 if (ctx.cfg.virtual_root) { 60 if (ctx.cfg.virtual_root) {
61 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, 61 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
62 pagename, (filename ? filename:"")); 62 pagename, (filename ? filename:""));
63 delim = "?"; 63 delim = "?";
64 } else { 64 } else {
65 tmp = fmt("?url=%s/%s/%s", reponame, pagename, 65 tmp = fmt("?url=%s/%s/%s", reponame, pagename,
66 (filename ? filename : "")); 66 (filename ? filename : ""));
67 delim = "&"; 67 delim = "&";
68 } 68 }
69 if (query) 69 if (query)
70 tmp = fmt("%s%s%s", tmp, delim, query); 70 tmp = fmt("%s%s%s", tmp, delim, query);
71 return tmp; 71 return tmp;
72} 72}
73 73
74char *cgit_pageurl(const char *reponame, const char *pagename, 74char *cgit_pageurl(const char *reponame, const char *pagename,
75 const char *query) 75 const char *query)
76{ 76{
77 return cgit_fileurl(reponame,pagename,0,query); 77 return cgit_fileurl(reponame,pagename,0,query);
78} 78}
79 79
80const char *cgit_repobasename(const char *reponame) 80const char *cgit_repobasename(const char *reponame)
81{ 81{
82 /* I assume we don't need to store more than one repo basename */ 82 /* I assume we don't need to store more than one repo basename */
83 static char rvbuf[1024]; 83 static char rvbuf[1024];
84 int p; 84 int p;
85 const char *rv; 85 const char *rv;
86 strncpy(rvbuf,reponame,sizeof(rvbuf)); 86 strncpy(rvbuf,reponame,sizeof(rvbuf));
87 if(rvbuf[sizeof(rvbuf)-1]) 87 if(rvbuf[sizeof(rvbuf)-1])
88 die("cgit_repobasename: truncated repository name '%s'", reponame); 88 die("cgit_repobasename: truncated repository name '%s'", reponame);
89 p = strlen(rvbuf)-1; 89 p = strlen(rvbuf)-1;
90 /* strip trailing slashes */ 90 /* strip trailing slashes */
91 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 91 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
92 /* strip trailing .git */ 92 /* strip trailing .git */
93 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 93 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
94 p -= 3; rvbuf[p--] = 0; 94 p -= 3; rvbuf[p--] = 0;
95 } 95 }
96 /* strip more trailing slashes if any */ 96 /* strip more trailing slashes if any */
97 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 97 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
98 /* find last slash in the remaining string */ 98 /* find last slash in the remaining string */
99 rv = strrchr(rvbuf,'/'); 99 rv = strrchr(rvbuf,'/');
100 if(rv) 100 if(rv)
101 return ++rv; 101 return ++rv;
102 return rvbuf; 102 return rvbuf;
103} 103}
104 104
105char *cgit_currurl() 105char *cgit_currurl()
106{ 106{
107 if (!ctx.cfg.virtual_root) 107 if (!ctx.cfg.virtual_root)
108 return ctx.cfg.script_name; 108 return ctx.cfg.script_name;
109 else if (ctx.qry.page) 109 else if (ctx.qry.page)
110 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); 110 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
111 else if (ctx.qry.repo) 111 else if (ctx.qry.repo)
112 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); 112 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
113 else 113 else
114 return fmt("%s/", ctx.cfg.virtual_root); 114 return fmt("%s/", ctx.cfg.virtual_root);
115} 115}
116 116
117static char *repolink(char *title, char *class, char *page, char *head, 117static char *repolink(char *title, char *class, char *page, char *head,
118 char *path) 118 char *path)
119{ 119{
120 char *delim = "?"; 120 char *delim = "?";
121 121
122 html("<a"); 122 html("<a");
123 if (title) { 123 if (title) {
124 html(" title='"); 124 html(" title='");
125 html_attr(title); 125 html_attr(title);
126 html("'"); 126 html("'");
127 } 127 }
128 if (class) { 128 if (class) {
129 html(" class='"); 129 html(" class='");
130 html_attr(class); 130 html_attr(class);
131 html("'"); 131 html("'");
132 } 132 }
133 html(" href='"); 133 html(" href='");
134 if (ctx.cfg.virtual_root) { 134 if (ctx.cfg.virtual_root) {
135 html_attr(ctx.cfg.virtual_root); 135 html_attr(ctx.cfg.virtual_root);
136 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 136 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
137 html("/"); 137 html("/");
138 html_attr(ctx.repo->url); 138 html_attr(ctx.repo->url);
139 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 139 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
140 html("/"); 140 html("/");
141 if (page) { 141 if (page) {
142 html(page); 142 html(page);
143 html("/"); 143 html("/");
144 if (path) 144 if (path)
145 html_attr(path); 145 html_attr(path);
146 } 146 }
147 } else { 147 } else {
148 html(ctx.cfg.script_name); 148 html(ctx.cfg.script_name);
149 html("?url="); 149 html("?url=");
150 html_attr(ctx.repo->url); 150 html_attr(ctx.repo->url);
151 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 151 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
152 html("/"); 152 html("/");
153 if (page) { 153 if (page) {
154 html(page); 154 html(page);
155 html("/"); 155 html("/");
156 if (path) 156 if (path)
157 html_attr(path); 157 html_attr(path);
158 } 158 }
159 delim = "&amp;"; 159 delim = "&amp;";
160 } 160 }
161 if (head && strcmp(head, ctx.repo->defbranch)) { 161 if (head && strcmp(head, ctx.repo->defbranch)) {
162 html(delim); 162 html(delim);
163 html("h="); 163 html("h=");
164 html_attr(head); 164 html_attr(head);
165 delim = "&amp;"; 165 delim = "&amp;";
166 } 166 }
167 return fmt("%s", delim); 167 return fmt("%s", delim);
168} 168}
169 169
170static void reporevlink(char *page, char *name, char *title, char *class, 170static void reporevlink(char *page, char *name, char *title, char *class,
171 char *head, char *rev, char *path) 171 char *head, char *rev, char *path)
172{ 172{
173 char *delim; 173 char *delim;
174 174
175 delim = repolink(title, class, page, head, path); 175 delim = repolink(title, class, page, head, path);
176 if (rev && strcmp(rev, ctx.qry.head)) { 176 if (rev && strcmp(rev, ctx.qry.head)) {
177 html(delim); 177 html(delim);
178 html("id="); 178 html("id=");
179 html_attr(rev); 179 html_attr(rev);
180 } 180 }
181 html("'>"); 181 html("'>");
182 html_txt(name); 182 html_txt(name);
183 html("</a>"); 183 html("</a>");
184} 184}
185 185
186void cgit_tree_link(char *name, char *title, char *class, char *head, 186void cgit_tree_link(char *name, char *title, char *class, char *head,
187 char *rev, char *path) 187 char *rev, char *path)
188{ 188{
189 reporevlink("tree", name, title, class, head, rev, path); 189 reporevlink("tree", name, title, class, head, rev, path);
190} 190}
191 191
192void cgit_log_link(char *name, char *title, char *class, char *head, 192void cgit_log_link(char *name, char *title, char *class, char *head,
193 char *rev, char *path, int ofs, char *grep, char *pattern) 193 char *rev, char *path, int ofs, char *grep, char *pattern)
194{ 194{
195 char *delim; 195 char *delim;
196 196
197 delim = repolink(title, class, "log", head, path); 197 delim = repolink(title, class, "log", head, path);
198 if (rev && strcmp(rev, ctx.qry.head)) { 198 if (rev && strcmp(rev, ctx.qry.head)) {
199 html(delim); 199 html(delim);
200 html("id="); 200 html("id=");
201 html_attr(rev); 201 html_attr(rev);
202 delim = "&"; 202 delim = "&";
203 } 203 }
204 if (grep && pattern) { 204 if (grep && pattern) {
205 html(delim); 205 html(delim);
206 html("qt="); 206 html("qt=");
207 html_attr(grep); 207 html_attr(grep);
208 delim = "&"; 208 delim = "&";
209 html(delim); 209 html(delim);
210 html("q="); 210 html("q=");
211 html_attr(pattern); 211 html_attr(pattern);
212 } 212 }
213 if (ofs > 0) { 213 if (ofs > 0) {
214 html(delim); 214 html(delim);
215 html("ofs="); 215 html("ofs=");
216 htmlf("%d", ofs); 216 htmlf("%d", ofs);
217 } 217 }
218 html("'>"); 218 html("'>");
219 html_txt(name); 219 html_txt(name);
220 html("</a>"); 220 html("</a>");
221} 221}
222 222
223void cgit_commit_link(char *name, char *title, char *class, char *head, 223void cgit_commit_link(char *name, char *title, char *class, char *head,
224 char *rev) 224 char *rev)
225{ 225{
226 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { 226 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
227 name[ctx.cfg.max_msg_len] = '\0'; 227 name[ctx.cfg.max_msg_len] = '\0';
228 name[ctx.cfg.max_msg_len - 1] = '.'; 228 name[ctx.cfg.max_msg_len - 1] = '.';
229 name[ctx.cfg.max_msg_len - 2] = '.'; 229 name[ctx.cfg.max_msg_len - 2] = '.';
230 name[ctx.cfg.max_msg_len - 3] = '.'; 230 name[ctx.cfg.max_msg_len - 3] = '.';
231 } 231 }
232 reporevlink("commit", name, title, class, head, rev, NULL); 232 reporevlink("commit", name, title, class, head, rev, NULL);
233} 233}
234 234
235void cgit_refs_link(char *name, char *title, char *class, char *head, 235void cgit_refs_link(char *name, char *title, char *class, char *head,
236 char *rev, char *path) 236 char *rev, char *path)
237{ 237{
238 reporevlink("refs", name, title, class, head, rev, path); 238 reporevlink("refs", name, title, class, head, rev, path);
239} 239}
240 240
241void cgit_snapshot_link(char *name, char *title, char *class, char *head, 241void cgit_snapshot_link(char *name, char *title, char *class, char *head,
242 char *rev, char *archivename) 242 char *rev, char *archivename)
243{ 243{
244 reporevlink("snapshot", name, title, class, head, rev, archivename); 244 reporevlink("snapshot", name, title, class, head, rev, archivename);
245} 245}
246 246
247void cgit_diff_link(char *name, char *title, char *class, char *head, 247void cgit_diff_link(char *name, char *title, char *class, char *head,
248 char *new_rev, char *old_rev, char *path) 248 char *new_rev, char *old_rev, char *path)
249{ 249{
250 char *delim; 250 char *delim;
251 251
252 delim = repolink(title, class, "diff", head, path); 252 delim = repolink(title, class, "diff", head, path);
253 if (new_rev && strcmp(new_rev, ctx.qry.head)) { 253 if (new_rev && strcmp(new_rev, ctx.qry.head)) {
254 html(delim); 254 html(delim);
255 html("id="); 255 html("id=");
256 html_attr(new_rev); 256 html_attr(new_rev);
257 delim = "&amp;"; 257 delim = "&amp;";
258 } 258 }
259 if (old_rev) { 259 if (old_rev) {
260 html(delim); 260 html(delim);
261 html("id2="); 261 html("id2=");
262 html_attr(old_rev); 262 html_attr(old_rev);
263 } 263 }
264 html("'>"); 264 html("'>");
265 html_txt(name); 265 html_txt(name);
266 html("</a>"); 266 html("</a>");
267} 267}
268 268
269void cgit_patch_link(char *name, char *title, char *class, char *head, 269void cgit_patch_link(char *name, char *title, char *class, char *head,
270 char *rev) 270 char *rev)
271{ 271{
272 reporevlink("patch", name, title, class, head, rev, NULL); 272 reporevlink("patch", name, title, class, head, rev, NULL);
273} 273}
274 274
275void cgit_object_link(struct object *obj) 275void cgit_object_link(struct object *obj)
276{ 276{
277 char *page, *arg, *url; 277 char *page, *arg, *url;
278 278
279 if (obj->type == OBJ_COMMIT) { 279 if (obj->type == OBJ_COMMIT) {
280 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 280 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
281 ctx.qry.head, sha1_to_hex(obj->sha1)); 281 ctx.qry.head, sha1_to_hex(obj->sha1));
282 return; 282 return;
283 } else if (obj->type == OBJ_TREE) { 283 } else if (obj->type == OBJ_TREE) {
284 page = "tree"; 284 page = "tree";
285 arg = "id"; 285 arg = "id";
286 } else if (obj->type == OBJ_TAG) { 286 } else if (obj->type == OBJ_TAG) {
287 page = "tag"; 287 page = "tag";
288 arg = "id"; 288 arg = "id";
289 } else { 289 } else {
290 page = "blob"; 290 page = "blob";
291 arg = "id"; 291 arg = "id";
292 } 292 }
293 293
294 url = cgit_pageurl(ctx.qry.repo, page, 294 url = cgit_pageurl(ctx.qry.repo, page,
295 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 295 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
296 html_link_open(url, NULL, NULL); 296 html_link_open(url, NULL, NULL);
297 htmlf("%s %s", typename(obj->type), 297 htmlf("%s %s", typename(obj->type),
298 sha1_to_hex(obj->sha1)); 298 sha1_to_hex(obj->sha1));
299 html_link_close(); 299 html_link_close();
300} 300}
301 301
302void cgit_print_date(time_t secs, char *format) 302void cgit_print_date(time_t secs, char *format)
303{ 303{
304 char buf[64]; 304 char buf[64];
305 struct tm *time; 305 struct tm *time;
306 306
307 if (!secs) 307 if (!secs)
308 return; 308 return;
309 time = gmtime(&secs); 309 time = gmtime(&secs);
310 strftime(buf, sizeof(buf)-1, format, time); 310 strftime(buf, sizeof(buf)-1, format, time);
311 html_txt(buf); 311 html_txt(buf);
312} 312}
313 313
314void cgit_print_age(time_t t, time_t max_relative, char *format) 314void cgit_print_age(time_t t, time_t max_relative, char *format)
315{ 315{
316 time_t now, secs; 316 time_t now, secs;
317 317
318 if (!t) 318 if (!t)
319 return; 319 return;
320 time(&now); 320 time(&now);
321 secs = now - t; 321 secs = now - t;
322 322
323 if (secs > max_relative && max_relative >= 0) { 323 if (secs > max_relative && max_relative >= 0) {
324 cgit_print_date(t, format); 324 cgit_print_date(t, format);
325 return; 325 return;
326 } 326 }
327 327
328 if (secs < TM_HOUR * 2) { 328 if (secs < TM_HOUR * 2) {
329 htmlf("<span class='age-mins'>%.0f min.</span>", 329 htmlf("<span class='age-mins'>%.0f min.</span>",
330 secs * 1.0 / TM_MIN); 330 secs * 1.0 / TM_MIN);
331 return; 331 return;
332 } 332 }
333 if (secs < TM_DAY * 2) { 333 if (secs < TM_DAY * 2) {
334 htmlf("<span class='age-hours'>%.0f hours</span>", 334 htmlf("<span class='age-hours'>%.0f hours</span>",
335 secs * 1.0 / TM_HOUR); 335 secs * 1.0 / TM_HOUR);
336 return; 336 return;
337 } 337 }
338 if (secs < TM_WEEK * 2) { 338 if (secs < TM_WEEK * 2) {
339 htmlf("<span class='age-days'>%.0f days</span>", 339 htmlf("<span class='age-days'>%.0f days</span>",
340 secs * 1.0 / TM_DAY); 340 secs * 1.0 / TM_DAY);
341 return; 341 return;
342 } 342 }
343 if (secs < TM_MONTH * 2) { 343 if (secs < TM_MONTH * 2) {
344 htmlf("<span class='age-weeks'>%.0f weeks</span>", 344 htmlf("<span class='age-weeks'>%.0f weeks</span>",
345 secs * 1.0 / TM_WEEK); 345 secs * 1.0 / TM_WEEK);
346 return; 346 return;
347 } 347 }
348 if (secs < TM_YEAR * 2) { 348 if (secs < TM_YEAR * 2) {
349 htmlf("<span class='age-months'>%.0f months</span>", 349 htmlf("<span class='age-months'>%.0f months</span>",
350 secs * 1.0 / TM_MONTH); 350 secs * 1.0 / TM_MONTH);
351 return; 351 return;
352 } 352 }
353 htmlf("<span class='age-years'>%.0f years</span>", 353 htmlf("<span class='age-years'>%.0f years</span>",
354 secs * 1.0 / TM_YEAR); 354 secs * 1.0 / TM_YEAR);
355} 355}
356 356
357void cgit_print_http_headers(struct cgit_context *ctx) 357void cgit_print_http_headers(struct cgit_context *ctx)
358{ 358{
359 if (ctx->page.mimetype && ctx->page.charset) 359 if (ctx->page.mimetype && ctx->page.charset)
360 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 360 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
361 ctx->page.charset); 361 ctx->page.charset);
362 else if (ctx->page.mimetype) 362 else if (ctx->page.mimetype)
363 htmlf("Content-Type: %s\n", ctx->page.mimetype); 363 htmlf("Content-Type: %s\n", ctx->page.mimetype);
364 if (ctx->page.filename) 364 if (ctx->page.filename)
365 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 365 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
366 ctx->page.filename); 366 ctx->page.filename);
367 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 367 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
368 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 368 htmlf("Expires: %s\n", http_date(ctx->page.expires));
369 html("\n"); 369 html("\n");
370} 370}
371 371
372void cgit_print_docstart(struct cgit_context *ctx) 372void cgit_print_docstart(struct cgit_context *ctx)
373{ 373{
374 html(cgit_doctype); 374 html(cgit_doctype);
375 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 375 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
376 html("<head>\n"); 376 html("<head>\n");
377 html("<title>"); 377 html("<title>");
378 html_txt(ctx->page.title); 378 html_txt(ctx->page.title);
379 html("</title>\n"); 379 html("</title>\n");
380 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 380 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
381 if (ctx->cfg.robots && *ctx->cfg.robots) 381 if (ctx->cfg.robots && *ctx->cfg.robots)
382 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 382 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
383 html("<link rel='stylesheet' type='text/css' href='"); 383 html("<link rel='stylesheet' type='text/css' href='");
384 html_attr(ctx->cfg.css); 384 html_attr(ctx->cfg.css);
385 html("'/>\n"); 385 html("'/>\n");
386 html("</head>\n"); 386 html("</head>\n");
387 html("<body>\n"); 387 html("<body>\n");
388} 388}
389 389
390void cgit_print_docend() 390void cgit_print_docend()
391{ 391{
392 html("</td>\n</tr>\n</table>\n</body>\n</html>\n"); 392 html("</td>\n</tr>\n</table>\n</body>\n</html>\n");
393} 393}
394 394
395int print_branch_option(const char *refname, const unsigned char *sha1, 395int print_branch_option(const char *refname, const unsigned char *sha1,
396 int flags, void *cb_data) 396 int flags, void *cb_data)
397{ 397{
398 char *name = (char *)refname; 398 char *name = (char *)refname;
399 html_option(name, name, ctx.qry.head); 399 html_option(name, name, ctx.qry.head);
400 return 0; 400 return 0;
401} 401}
402 402
403int print_archive_ref(const char *refname, const unsigned char *sha1, 403int print_archive_ref(const char *refname, const unsigned char *sha1,
404 int flags, void *cb_data) 404 int flags, void *cb_data)
405{ 405{
406 struct tag *tag; 406 struct tag *tag;
407 struct taginfo *info; 407 struct taginfo *info;
408 struct object *obj; 408 struct object *obj;
409 char buf[256], *url; 409 char buf[256], *url;
410 unsigned char fileid[20]; 410 unsigned char fileid[20];
411 int *header = (int *)cb_data; 411 int *header = (int *)cb_data;
412 412
413 if (prefixcmp(refname, "refs/archives")) 413 if (prefixcmp(refname, "refs/archives"))
414 return 0; 414 return 0;
415 strncpy(buf, refname+14, sizeof(buf)); 415 strncpy(buf, refname+14, sizeof(buf));
416 obj = parse_object(sha1); 416 obj = parse_object(sha1);
417 if (!obj) 417 if (!obj)
418 return 1; 418 return 1;
419 if (obj->type == OBJ_TAG) { 419 if (obj->type == OBJ_TAG) {
420 tag = lookup_tag(sha1); 420 tag = lookup_tag(sha1);
421 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 421 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
422 return 0; 422 return 0;
423 hashcpy(fileid, tag->tagged->sha1); 423 hashcpy(fileid, tag->tagged->sha1);
424 } else if (obj->type != OBJ_BLOB) { 424 } else if (obj->type != OBJ_BLOB) {
425 return 0; 425 return 0;
426 } else { 426 } else {
427 hashcpy(fileid, sha1); 427 hashcpy(fileid, sha1);
428 } 428 }
429 if (!*header) { 429 if (!*header) {
430 html("<h1>download</h1>\n"); 430 html("<h1>download</h1>\n");
431 *header = 1; 431 *header = 1;
432 } 432 }
433 url = cgit_pageurl(ctx.qry.repo, "blob", 433 url = cgit_pageurl(ctx.qry.repo, "blob",
434 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 434 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
435 buf)); 435 buf));
436 html_link_open(url, NULL, "menu"); 436 html_link_open(url, NULL, "menu");
437 html_txt(strlpart(buf, 20)); 437 html_txt(strlpart(buf, 20));
438 html_link_close(); 438 html_link_close();
439 return 0; 439 return 0;
440} 440}
441 441
442void add_hidden_formfields(int incl_head, int incl_search, char *page) 442void add_hidden_formfields(int incl_head, int incl_search, char *page)
443{ 443{
444 char *url; 444 char *url;
445 445
446 if (!ctx.cfg.virtual_root) { 446 if (!ctx.cfg.virtual_root) {
447 url = fmt("%s/%s", ctx.qry.repo, page); 447 url = fmt("%s/%s", ctx.qry.repo, page);
448 if (ctx.qry.path) 448 if (ctx.qry.path)
449 url = fmt("%s/%s", url, ctx.qry.path); 449 url = fmt("%s/%s", url, ctx.qry.path);
450 html_hidden("url", url); 450 html_hidden("url", url);
451 } 451 }
452 452
453 if (incl_head && strcmp(ctx.qry.head, ctx.repo->defbranch)) 453 if (incl_head && strcmp(ctx.qry.head, ctx.repo->defbranch))
454 html_hidden("h", ctx.qry.head); 454 html_hidden("h", ctx.qry.head);
455 455
456 if (ctx.qry.sha1) 456 if (ctx.qry.sha1)
457 html_hidden("id", ctx.qry.sha1); 457 html_hidden("id", ctx.qry.sha1);
458 if (ctx.qry.sha2) 458 if (ctx.qry.sha2)
459 html_hidden("id2", ctx.qry.sha2); 459 html_hidden("id2", ctx.qry.sha2);
460 460
461 if (incl_search) { 461 if (incl_search) {
462 if (ctx.qry.grep) 462 if (ctx.qry.grep)
463 html_hidden("qt", ctx.qry.grep); 463 html_hidden("qt", ctx.qry.grep);
464 if (ctx.qry.search) 464 if (ctx.qry.search)
465 html_hidden("q", ctx.qry.search); 465 html_hidden("q", ctx.qry.search);
466 } 466 }
467} 467}
468 468
469char *hc(struct cgit_cmd *cmd, const char *page) 469char *hc(struct cgit_cmd *cmd, const char *page)
470{ 470{
471 return (strcmp(cmd->name, page) ? NULL : "active"); 471 return (strcmp(cmd->name, page) ? NULL : "active");
472} 472}
473 473
474void cgit_print_pageheader(struct cgit_context *ctx) 474void cgit_print_pageheader(struct cgit_context *ctx)
475{ 475{
476 struct cgit_cmd *cmd = cgit_get_cmd(ctx); 476 struct cgit_cmd *cmd = cgit_get_cmd(ctx);
477 477
478 html("<table id='header'>\n"); 478 html("<table id='header'>\n");
479 html("<tr>\n"); 479 html("<tr>\n");
480 html("<td class='logo' rowspan='2'><a href='"); 480 html("<td class='logo' rowspan='2'><a href='");
481 if (ctx->cfg.logo_link) 481 if (ctx->cfg.logo_link)
482 html_attr(ctx->cfg.logo_link); 482 html_attr(ctx->cfg.logo_link);
483 else 483 else
484 html_attr(cgit_rooturl()); 484 html_attr(cgit_rooturl());
485 html("'><img src='"); 485 html("'><img src='");
486 html_attr(ctx->cfg.logo); 486 html_attr(ctx->cfg.logo);
487 html("'/></a></td>\n"); 487 html("'/></a></td>\n");
488 html("<td class='main'>"); 488 html("<td class='main'>");
489 if (ctx->repo) { 489 if (ctx->repo) {
490 html("<a href='"); 490 html("<a href='");
491 html_attr(cgit_rooturl()); 491 html_attr(cgit_rooturl());
492 html("'>index</a> / "); 492 html("'>index</a> / ");
493 html_txt(ctx->repo->name); 493 html_txt(ctx->repo->name);
494 } else 494 } else
495 html_txt(ctx->cfg.root_title); 495 html_txt(ctx->cfg.root_title);
496 html("</td></tr>\n"); 496 html("</td></tr>\n");
497 html("<tr><td class='sub'>"); 497 html("<tr><td class='sub'>");
498 if (ctx->repo) 498 if (ctx->repo)
499 html_txt(ctx->repo->desc); 499 html_txt(ctx->repo->desc);
500 else 500 else
501 html_txt(ctx->cfg.index_info); 501 html_txt(ctx->cfg.index_info);
502 html("</td></tr>\n"); 502 html("</td></tr>\n");
503 html("</table>\n"); 503 html("</table>\n");
504 504
505 html("<table class='tabs'><tr><td>\n"); 505 html("<table class='tabs'><tr><td>\n");
506 if (ctx->repo) { 506 if (ctx->repo) {
507 reporevlink(NULL, "summary", NULL, hc(cmd, "summary"), 507 reporevlink(NULL, "summary", NULL, hc(cmd, "summary"),
508 ctx->qry.head, NULL, NULL); 508 ctx->qry.head, NULL, NULL);
509 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, 509 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
510 ctx->qry.sha1, NULL); 510 ctx->qry.sha1, NULL);
511 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, 511 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
512 NULL, NULL, 0, NULL, NULL); 512 NULL, NULL, 0, NULL, NULL);
513 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, 513 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
514 ctx->qry.sha1, NULL); 514 ctx->qry.sha1, NULL);
515 cgit_commit_link("commit", NULL, hc(cmd, "commit"), 515 cgit_commit_link("commit", NULL, hc(cmd, "commit"),
516 ctx->qry.head, ctx->qry.sha1); 516 ctx->qry.head, ctx->qry.sha1);
517 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 517 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
518 ctx->qry.sha1, ctx->qry.sha2, NULL); 518 ctx->qry.sha1, ctx->qry.sha2, NULL);
519 cgit_patch_link("patch", NULL, hc(cmd, "patch"), ctx->qry.head, 519 cgit_patch_link("patch", NULL, hc(cmd, "patch"), ctx->qry.head,
520 ctx->qry.sha1); 520 ctx->qry.sha1);
521 html("</td><td class='branch'>");
522 html("<form method='get' action=''>\n");
523 add_hidden_formfields(0, 1, ctx->qry.page);
524 html("<select name='h' onchange='this.form.submit();'>\n");
525 for_each_branch_ref(print_branch_option, ctx->qry.head);
526 html("</select> ");
527 html("<input type='submit' name='' value='switch'/>");
521 } else { 528 } else {
522 html("<a class='active' href='"); 529 html("<a class='active' href='");
523 html_attr(cgit_rooturl()); 530 html_attr(cgit_rooturl());
524 html("'>index</a>\n"); 531 html("'>index</a>\n");
525 } 532 }
526 html("</td></tr></table>\n"); 533 html("</td></tr></table>\n");
527 html("<div class='content'>"); 534 html("<div class='content'>");
528} 535}
529 536
530void cgit_print_filemode(unsigned short mode) 537void cgit_print_filemode(unsigned short mode)
531{ 538{
532 if (S_ISDIR(mode)) 539 if (S_ISDIR(mode))
533 html("d"); 540 html("d");
534 else if (S_ISLNK(mode)) 541 else if (S_ISLNK(mode))
535 html("l"); 542 html("l");
536 else if (S_ISGITLINK(mode)) 543 else if (S_ISGITLINK(mode))
537 html("m"); 544 html("m");
538 else 545 else
539 html("-"); 546 html("-");
540 html_fileperm(mode >> 6); 547 html_fileperm(mode >> 6);
541 html_fileperm(mode >> 3); 548 html_fileperm(mode >> 3);
542 html_fileperm(mode); 549 html_fileperm(mode);
543} 550}
544 551
545void cgit_print_snapshot_links(const char *repo, const char *head, 552void cgit_print_snapshot_links(const char *repo, const char *head,
546 const char *hex, int snapshots) 553 const char *hex, int snapshots)
547{ 554{
548 const struct cgit_snapshot_format* f; 555 const struct cgit_snapshot_format* f;
549 char *filename; 556 char *filename;
550 557
551 for (f = cgit_snapshot_formats; f->suffix; f++) { 558 for (f = cgit_snapshot_formats; f->suffix; f++) {
552 if (!(snapshots & f->bit)) 559 if (!(snapshots & f->bit))
553 continue; 560 continue;
554 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, 561 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
555 f->suffix); 562 f->suffix);
556 cgit_snapshot_link(filename, NULL, NULL, (char *)head, 563 cgit_snapshot_link(filename, NULL, NULL, (char *)head,
557 (char *)hex, filename); 564 (char *)hex, filename);
558 html("<br/>"); 565 html("<br/>");
559 } 566 }
560} 567}