summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.css27
-rwxr-xr-xtests/t0104-tree.sh4
-rw-r--r--ui-tree.c31
3 files changed, 34 insertions, 28 deletions
diff --git a/cgit.css b/cgit.css
index e8214de..f844efa 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,573 +1,578 @@
1body, table, form { 1body, table, form {
2 padding: 0em; 2 padding: 0em;
3 margin: 0em; 3 margin: 0em;
4} 4}
5 5
6body { 6body {
7 font-family: sans-serif; 7 font-family: sans-serif;
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: 250%; 37 font-size: 250%;
38 padding-left: 10px; 38 padding-left: 10px;
39 white-space: nowrap; 39 white-space: nowrap;
40} 40}
41 41
42table#header td.main a { 42table#header td.main a {
43 color: #000; 43 color: #000;
44} 44}
45 45
46table#header td.form { 46table#header td.form {
47 text-align: right; 47 text-align: right;
48 vertical-align: bottom; 48 vertical-align: bottom;
49 padding-right: 1em; 49 padding-right: 1em;
50 padding-bottom: 2px; 50 padding-bottom: 2px;
51 white-space: nowrap; 51 white-space: nowrap;
52} 52}
53 53
54table#header td.form form, 54table#header td.form form,
55table#header td.form input, 55table#header td.form input,
56table#header td.form select { 56table#header td.form select {
57 font-size: 90%; 57 font-size: 90%;
58} 58}
59 59
60table#header td.sub { 60table#header td.sub {
61 color: #777; 61 color: #777;
62 border-top: solid 1px #ccc; 62 border-top: solid 1px #ccc;
63 padding-left: 10px; 63 padding-left: 10px;
64} 64}
65 65
66table.tabs { 66table.tabs {
67 /* border-bottom: solid 2px #ccc; */ 67 /* border-bottom: solid 2px #ccc; */
68 border-collapse: collapse; 68 border-collapse: collapse;
69 margin-top: 2em; 69 margin-top: 2em;
70 margin-bottom: 0px; 70 margin-bottom: 0px;
71 width: 100%; 71 width: 100%;
72} 72}
73 73
74table.tabs td { 74table.tabs td {
75 padding: 0px 1em; 75 padding: 0px 1em;
76 vertical-align: bottom; 76 vertical-align: bottom;
77} 77}
78 78
79table.tabs td a { 79table.tabs td a {
80 padding: 2px 0.75em; 80 padding: 2px 0.75em;
81 color: #777; 81 color: #777;
82 font-size: 110%; 82 font-size: 110%;
83} 83}
84 84
85table.tabs td a.active { 85table.tabs td a.active {
86 color: #000; 86 color: #000;
87 background-color: #ccc; 87 background-color: #ccc;
88} 88}
89 89
90table.tabs td.form { 90table.tabs td.form {
91 text-align: right; 91 text-align: right;
92} 92}
93 93
94table.tabs td.form form { 94table.tabs td.form form {
95 padding-bottom: 2px; 95 padding-bottom: 2px;
96 font-size: 90%; 96 font-size: 90%;
97 white-space: nowrap; 97 white-space: nowrap;
98} 98}
99 99
100table.tabs td.form input, 100table.tabs td.form input,
101table.tabs td.form select { 101table.tabs td.form select {
102 font-size: 90%; 102 font-size: 90%;
103} 103}
104 104
105div.content { 105div.content {
106 margin: 0px; 106 margin: 0px;
107 padding: 2em; 107 padding: 2em;
108 border-top: solid 3px #ccc; 108 border-top: solid 3px #ccc;
109 border-bottom: solid 3px #ccc; 109 border-bottom: solid 3px #ccc;
110} 110}
111 111
112 112
113table.list { 113table.list {
114 width: 100%; 114 width: 100%;
115 border: none; 115 border: none;
116 border-collapse: collapse; 116 border-collapse: collapse;
117} 117}
118 118
119table.list tr { 119table.list tr {
120 background: white; 120 background: white;
121} 121}
122 122
123table.list tr.logheader { 123table.list tr.logheader {
124 background: #eee; 124 background: #eee;
125} 125}
126 126
127table.list tr:hover { 127table.list tr:hover {
128 background: #eee; 128 background: #eee;
129} 129}
130 130
131table.list tr.nohover:hover { 131table.list tr.nohover:hover {
132 background: white; 132 background: white;
133} 133}
134 134
135table.list th { 135table.list th {
136 font-weight: bold; 136 font-weight: bold;
137 /* color: #888; 137 /* color: #888;
138 border-top: dashed 1px #888; 138 border-top: dashed 1px #888;
139 border-bottom: dashed 1px #888; 139 border-bottom: dashed 1px #888;
140 */ 140 */
141 padding: 0.1em 0.5em 0.05em 0.5em; 141 padding: 0.1em 0.5em 0.05em 0.5em;
142 vertical-align: baseline; 142 vertical-align: baseline;
143} 143}
144 144
145table.list td { 145table.list td {
146 border: none; 146 border: none;
147 padding: 0.1em 0.5em 0.1em 0.5em; 147 padding: 0.1em 0.5em 0.1em 0.5em;
148} 148}
149 149
150table.list td.logsubject { 150table.list td.logsubject {
151 font-family: monospace; 151 font-family: monospace;
152 font-weight: bold; 152 font-weight: bold;
153} 153}
154 154
155table.list td.logmsg { 155table.list td.logmsg {
156 font-family: monospace; 156 font-family: monospace;
157 white-space: pre; 157 white-space: pre;
158 padding: 1em 0em 2em 0em; 158 padding: 1em 0em 2em 0em;
159} 159}
160 160
161table.list td a { 161table.list td a {
162 color: black; 162 color: black;
163} 163}
164 164
165table.list td a:hover { 165table.list td a:hover {
166 color: #00f; 166 color: #00f;
167} 167}
168 168
169img { 169img {
170 border: none; 170 border: none;
171} 171}
172 172
173input#switch-btn { 173input#switch-btn {
174 margin: 2px 0px 0px 0px; 174 margin: 2px 0px 0px 0px;
175} 175}
176 176
177td#sidebar input.txt { 177td#sidebar input.txt {
178 width: 100%; 178 width: 100%;
179 margin: 2px 0px 0px 0px; 179 margin: 2px 0px 0px 0px;
180} 180}
181 181
182table#grid { 182table#grid {
183 margin: 0px; 183 margin: 0px;
184} 184}
185 185
186td#content { 186td#content {
187 vertical-align: top; 187 vertical-align: top;
188 padding: 1em 2em 1em 1em; 188 padding: 1em 2em 1em 1em;
189 border: none; 189 border: none;
190} 190}
191 191
192div#summary { 192div#summary {
193 vertical-align: top; 193 vertical-align: top;
194 margin-bottom: 1em; 194 margin-bottom: 1em;
195} 195}
196 196
197table#downloads { 197table#downloads {
198 float: right; 198 float: right;
199 border-collapse: collapse; 199 border-collapse: collapse;
200 border: solid 1px #777; 200 border: solid 1px #777;
201 margin-left: 0.5em; 201 margin-left: 0.5em;
202 margin-bottom: 0.5em; 202 margin-bottom: 0.5em;
203} 203}
204 204
205table#downloads th { 205table#downloads th {
206 background-color: #ccc; 206 background-color: #ccc;
207} 207}
208 208
209div#blob { 209div#blob {
210 border: solid 1px black; 210 border: solid 1px black;
211} 211}
212 212
213div.error { 213div.error {
214 color: red; 214 color: red;
215 font-weight: bold; 215 font-weight: bold;
216 margin: 1em 2em; 216 margin: 1em 2em;
217} 217}
218 218
219a.ls-blob, a.ls-dir, a.ls-mod { 219a.ls-blob, a.ls-dir, a.ls-mod {
220 font-family: monospace; 220 font-family: monospace;
221} 221}
222 222
223td.ls-size { 223td.ls-size {
224 text-align: right; 224 text-align: right;
225 font-family: monospace; 225 font-family: monospace;
226 width: 10em; 226 width: 10em;
227} 227}
228 228
229td.ls-mode { 229td.ls-mode {
230 font-family: monospace; 230 font-family: monospace;
231 width: 10em; 231 width: 10em;
232} 232}
233 233
234table.blob { 234table.blob {
235 margin-top: 0.5em; 235 margin-top: 0.5em;
236 border-top: solid 1px black; 236 border-top: solid 1px black;
237} 237}
238 238
239table.blob td.no { 239table.blob td.lines {
240 border-right: solid 1px black; 240 margin: 0; padding: 0;
241 vertical-align: top;
241 color: black; 242 color: black;
243}
244
245table.blob td.linenumbers {
246 margin: 0; padding: 0;
247 vertical-align: top;
248 border-right: 1px solid gray;
242 background-color: #eee; 249 background-color: #eee;
243 text-align: right;
244} 250}
245 251
246table.blob td.no a { 252table.blob pre {
247 color: black; 253 padding: 0; margin: 0;
248} 254}
249 255
250table.blob td.no a:hover { 256table.blob a.no {
251 color: black; 257 color: gray;
258 text-align: right;
252 text-decoration: none; 259 text-decoration: none;
253} 260}
254 261
255table.blob td.txt { 262table.blob a.no a:hover {
256 white-space: pre; 263 color: black;
257 font-family: monospace;
258 padding-left: 0.5em;
259} 264}
260 265
261table.nowrap td { 266table.nowrap td {
262 white-space: nowrap; 267 white-space: nowrap;
263} 268}
264 269
265table.commit-info { 270table.commit-info {
266 border-collapse: collapse; 271 border-collapse: collapse;
267 margin-top: 1.5em; 272 margin-top: 1.5em;
268} 273}
269 274
270table.commit-info th { 275table.commit-info th {
271 text-align: left; 276 text-align: left;
272 font-weight: normal; 277 font-weight: normal;
273 padding: 0.1em 1em 0.1em 0.1em; 278 padding: 0.1em 1em 0.1em 0.1em;
274 vertical-align: top; 279 vertical-align: top;
275} 280}
276 281
277table.commit-info td { 282table.commit-info td {
278 font-weight: normal; 283 font-weight: normal;
279 padding: 0.1em 1em 0.1em 0.1em; 284 padding: 0.1em 1em 0.1em 0.1em;
280} 285}
281 286
282div.commit-subject { 287div.commit-subject {
283 font-weight: bold; 288 font-weight: bold;
284 font-size: 125%; 289 font-size: 125%;
285 margin: 1.5em 0em 0.5em 0em; 290 margin: 1.5em 0em 0.5em 0em;
286 padding: 0em; 291 padding: 0em;
287} 292}
288 293
289div.commit-msg { 294div.commit-msg {
290 white-space: pre; 295 white-space: pre;
291 font-family: monospace; 296 font-family: monospace;
292} 297}
293 298
294div.diffstat-header { 299div.diffstat-header {
295 font-weight: bold; 300 font-weight: bold;
296 padding-top: 1.5em; 301 padding-top: 1.5em;
297} 302}
298 303
299table.diffstat { 304table.diffstat {
300 border-collapse: collapse; 305 border-collapse: collapse;
301 border: solid 1px #aaa; 306 border: solid 1px #aaa;
302 background-color: #eee; 307 background-color: #eee;
303} 308}
304 309
305table.diffstat th { 310table.diffstat th {
306 font-weight: normal; 311 font-weight: normal;
307 text-align: left; 312 text-align: left;
308 text-decoration: underline; 313 text-decoration: underline;
309 padding: 0.1em 1em 0.1em 0.1em; 314 padding: 0.1em 1em 0.1em 0.1em;
310 font-size: 100%; 315 font-size: 100%;
311} 316}
312 317
313table.diffstat td { 318table.diffstat td {
314 padding: 0.2em 0.2em 0.1em 0.1em; 319 padding: 0.2em 0.2em 0.1em 0.1em;
315 font-size: 100%; 320 font-size: 100%;
316 border: none; 321 border: none;
317} 322}
318 323
319table.diffstat td.mode { 324table.diffstat td.mode {
320 white-space: nowrap; 325 white-space: nowrap;
321} 326}
322 327
323table.diffstat td span.modechange { 328table.diffstat td span.modechange {
324 padding-left: 1em; 329 padding-left: 1em;
325 color: red; 330 color: red;
326} 331}
327 332
328table.diffstat td.add a { 333table.diffstat td.add a {
329 color: green; 334 color: green;
330} 335}
331 336
332table.diffstat td.del a { 337table.diffstat td.del a {
333 color: red; 338 color: red;
334} 339}
335 340
336table.diffstat td.upd a { 341table.diffstat td.upd a {
337 color: blue; 342 color: blue;
338} 343}
339 344
340table.diffstat td.graph { 345table.diffstat td.graph {
341 width: 500px; 346 width: 500px;
342 vertical-align: middle; 347 vertical-align: middle;
343} 348}
344 349
345table.diffstat td.graph table { 350table.diffstat td.graph table {
346 border: none; 351 border: none;
347} 352}
348 353
349table.diffstat td.graph td { 354table.diffstat td.graph td {
350 padding: 0px; 355 padding: 0px;
351 border: 0px; 356 border: 0px;
352 height: 7pt; 357 height: 7pt;
353} 358}
354 359
355table.diffstat td.graph td.add { 360table.diffstat td.graph td.add {
356 background-color: #5c5; 361 background-color: #5c5;
357} 362}
358 363
359table.diffstat td.graph td.rem { 364table.diffstat td.graph td.rem {
360 background-color: #c55; 365 background-color: #c55;
361} 366}
362 367
363div.diffstat-summary { 368div.diffstat-summary {
364 color: #888; 369 color: #888;
365 padding-top: 0.5em; 370 padding-top: 0.5em;
366} 371}
367 372
368table.diff { 373table.diff {
369 width: 100%; 374 width: 100%;
370} 375}
371 376
372table.diff td { 377table.diff td {
373 font-family: monospace; 378 font-family: monospace;
374 white-space: pre; 379 white-space: pre;
375} 380}
376 381
377table.diff td div.head { 382table.diff td div.head {
378 font-weight: bold; 383 font-weight: bold;
379 margin-top: 1em; 384 margin-top: 1em;
380 color: black; 385 color: black;
381} 386}
382 387
383table.diff td div.hunk { 388table.diff td div.hunk {
384 color: #009; 389 color: #009;
385} 390}
386 391
387table.diff td div.add { 392table.diff td div.add {
388 color: green; 393 color: green;
389} 394}
390 395
391table.diff td div.del { 396table.diff td div.del {
392 color: red; 397 color: red;
393} 398}
394 399
395.sha1 { 400.sha1 {
396 font-family: monospace; 401 font-family: monospace;
397 font-size: 90%; 402 font-size: 90%;
398} 403}
399 404
400.left { 405.left {
401 text-align: left; 406 text-align: left;
402} 407}
403 408
404.right { 409.right {
405 text-align: right; 410 text-align: right;
406} 411}
407 412
408table.list td.repogroup { 413table.list td.repogroup {
409 font-style: italic; 414 font-style: italic;
410 color: #888; 415 color: #888;
411} 416}
412 417
413a.button { 418a.button {
414 font-size: 80%; 419 font-size: 80%;
415 padding: 0em 0.5em; 420 padding: 0em 0.5em;
416} 421}
417 422
418a.primary { 423a.primary {
419 font-size: 100%; 424 font-size: 100%;
420} 425}
421 426
422a.secondary { 427a.secondary {
423 font-size: 90%; 428 font-size: 90%;
424} 429}
425 430
426td.toplevel-repo { 431td.toplevel-repo {
427 432
428} 433}
429 434
430table.list td.sublevel-repo { 435table.list td.sublevel-repo {
431 padding-left: 1.5em; 436 padding-left: 1.5em;
432} 437}
433 438
434div.pager { 439div.pager {
435 text-align: center; 440 text-align: center;
436 margin: 1em 0em 0em 0em; 441 margin: 1em 0em 0em 0em;
437} 442}
438 443
439div.pager a { 444div.pager a {
440 color: #777; 445 color: #777;
441 margin: 0em 0.5em; 446 margin: 0em 0.5em;
442} 447}
443 448
444span.age-mins { 449span.age-mins {
445 font-weight: bold; 450 font-weight: bold;
446 color: #080; 451 color: #080;
447} 452}
448 453
449span.age-hours { 454span.age-hours {
450 color: #080; 455 color: #080;
451} 456}
452 457
453span.age-days { 458span.age-days {
454 color: #040; 459 color: #040;
455} 460}
456 461
457span.age-weeks { 462span.age-weeks {
458 color: #444; 463 color: #444;
459} 464}
460 465
461span.age-months { 466span.age-months {
462 color: #888; 467 color: #888;
463} 468}
464 469
465span.age-years { 470span.age-years {
466 color: #bbb; 471 color: #bbb;
467} 472}
468div.footer { 473div.footer {
469 margin-top: 0.5em; 474 margin-top: 0.5em;
470 text-align: center; 475 text-align: center;
471 font-size: 80%; 476 font-size: 80%;
472 color: #ccc; 477 color: #ccc;
473} 478}
474a.branch-deco { 479a.branch-deco {
475 margin: 0px 0.5em; 480 margin: 0px 0.5em;
476 padding: 0px 0.25em; 481 padding: 0px 0.25em;
477 background-color: #88ff88; 482 background-color: #88ff88;
478 border: solid 1px #007700; 483 border: solid 1px #007700;
479} 484}
480a.tag-deco { 485a.tag-deco {
481 margin: 0px 0.5em; 486 margin: 0px 0.5em;
482 padding: 0px 0.25em; 487 padding: 0px 0.25em;
483 background-color: #ffff88; 488 background-color: #ffff88;
484 border: solid 1px #777700; 489 border: solid 1px #777700;
485} 490}
486a.remote-deco { 491a.remote-deco {
487 margin: 0px 0.5em; 492 margin: 0px 0.5em;
488 padding: 0px 0.25em; 493 padding: 0px 0.25em;
489 background-color: #ccccff; 494 background-color: #ccccff;
490 border: solid 1px #000077; 495 border: solid 1px #000077;
491} 496}
492a.deco { 497a.deco {
493 margin: 0px 0.5em; 498 margin: 0px 0.5em;
494 padding: 0px 0.25em; 499 padding: 0px 0.25em;
495 background-color: #ff8888; 500 background-color: #ff8888;
496 border: solid 1px #770000; 501 border: solid 1px #770000;
497} 502}
498table.stats { 503table.stats {
499 border: solid 1px black; 504 border: solid 1px black;
500 border-collapse: collapse; 505 border-collapse: collapse;
501} 506}
502 507
503table.stats th { 508table.stats th {
504 text-align: left; 509 text-align: left;
505 padding: 1px 0.5em; 510 padding: 1px 0.5em;
506 background-color: #eee; 511 background-color: #eee;
507 border: solid 1px black; 512 border: solid 1px black;
508} 513}
509 514
510table.stats td { 515table.stats td {
511 text-align: right; 516 text-align: right;
512 padding: 1px 0.5em; 517 padding: 1px 0.5em;
513 border: solid 1px black; 518 border: solid 1px black;
514} 519}
515 520
516table.stats td.total { 521table.stats td.total {
517 font-weight: bold; 522 font-weight: bold;
518 text-align: left; 523 text-align: left;
519} 524}
520 525
521table.stats td.sum { 526table.stats td.sum {
522 color: #c00; 527 color: #c00;
523 font-weight: bold; 528 font-weight: bold;
524 /*background-color: #eee; */ 529 /*background-color: #eee; */
525} 530}
526 531
527table.stats td.left { 532table.stats td.left {
528 text-align: left; 533 text-align: left;
529} 534}
530 535
531table.vgraph { 536table.vgraph {
532 border-collapse: separate; 537 border-collapse: separate;
533 border: solid 1px black; 538 border: solid 1px black;
534 height: 200px; 539 height: 200px;
535} 540}
536 541
537table.vgraph th { 542table.vgraph th {
538 background-color: #eee; 543 background-color: #eee;
539 font-weight: bold; 544 font-weight: bold;
540 border: solid 1px white; 545 border: solid 1px white;
541 padding: 1px 0.5em; 546 padding: 1px 0.5em;
542} 547}
543 548
544table.vgraph td { 549table.vgraph td {
545 vertical-align: bottom; 550 vertical-align: bottom;
546 padding: 0px 10px; 551 padding: 0px 10px;
547} 552}
548 553
549table.vgraph div.bar { 554table.vgraph div.bar {
550 background-color: #eee; 555 background-color: #eee;
551} 556}
552 557
553table.hgraph { 558table.hgraph {
554 border: solid 1px black; 559 border: solid 1px black;
555 width: 800px; 560 width: 800px;
556} 561}
557 562
558table.hgraph th { 563table.hgraph th {
559 background-color: #eee; 564 background-color: #eee;
560 font-weight: bold; 565 font-weight: bold;
561 border: solid 1px black; 566 border: solid 1px black;
562 padding: 1px 0.5em; 567 padding: 1px 0.5em;
563} 568}
564 569
565table.hgraph td { 570table.hgraph td {
566 vertical-align: center; 571 vertical-align: center;
567 padding: 2px 2px; 572 padding: 2px 2px;
568} 573}
569 574
570table.hgraph div.bar { 575table.hgraph div.bar {
571 background-color: #eee; 576 background-color: #eee;
572 height: 1em; 577 height: 1em;
573} 578}
diff --git a/tests/t0104-tree.sh b/tests/t0104-tree.sh
index 33f4eb0..2ce1251 100755
--- a/tests/t0104-tree.sh
+++ b/tests/t0104-tree.sh
@@ -1,33 +1,33 @@
1#!/bin/sh 1#!/bin/sh
2 2
3. ./setup.sh 3. ./setup.sh
4 4
5prepare_tests "Check content on tree page" 5prepare_tests "Check content on tree page"
6 6
7run_test 'generate bar/tree' 'cgit_url "bar/tree" >trash/tmp' 7run_test 'generate bar/tree' 'cgit_url "bar/tree" >trash/tmp'
8run_test 'find file-1' 'grep -e "file-1" trash/tmp' 8run_test 'find file-1' 'grep -e "file-1" trash/tmp'
9run_test 'find file-50' 'grep -e "file-50" trash/tmp' 9run_test 'find file-50' 'grep -e "file-50" trash/tmp'
10 10
11run_test 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >trash/tmp' 11run_test 'generate bar/tree/file-50' 'cgit_url "bar/tree/file-50" >trash/tmp'
12 12
13run_test 'find line 1' ' 13run_test 'find line 1' '
14 grep -e "<a id=.n1. name=.n1. href=.#n1.>1</a>" trash/tmp 14 grep -e "<a class=.no. id=.n1. name=.n1. href=.#n1.>1</a>" trash/tmp
15' 15'
16 16
17run_test 'no line 2' ' 17run_test 'no line 2' '
18 ! grep -e "<a id=.n2. name=.n2. href=.#n2.>2</a>" trash/tmp 18 ! grep -e "<a class=.no. id=.n2. name=.n2. href=.#n2.>2</a>" trash/tmp
19' 19'
20 20
21run_test 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >trash/tmp' 21run_test 'generate foo+bar/tree' 'cgit_url "foo%2bbar/tree" >trash/tmp'
22 22
23run_test 'verify a+b link' ' 23run_test 'verify a+b link' '
24 grep -e "/foo+bar/tree/a+b" trash/tmp 24 grep -e "/foo+bar/tree/a+b" trash/tmp
25' 25'
26 26
27run_test 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >trash/tmp' 27run_test 'generate foo+bar/tree?h=1+2' 'cgit_url "foo%2bbar/tree&h=1%2b2" >trash/tmp'
28 28
29run_test 'verify a+b?h=1+2 link' ' 29run_test 'verify a+b?h=1+2 link' '
30 grep -e "/foo+bar/tree/a+b?h=1%2b2" trash/tmp 30 grep -e "/foo+bar/tree/a+b?h=1%2b2" trash/tmp
31' 31'
32 32
33tests_done 33tests_done
diff --git a/ui-tree.c b/ui-tree.c
index 4b8e7a0..c26ba4c 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,228 +1,229 @@
1/* ui-tree.c: functions for tree output 1/* ui-tree.c: functions for tree output
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 "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13char *curr_rev; 13char *curr_rev;
14char *match_path; 14char *match_path;
15int header = 0; 15int header = 0;
16 16
17static void print_object(const unsigned char *sha1, char *path) 17static void print_object(const unsigned char *sha1, char *path)
18{ 18{
19 enum object_type type; 19 enum object_type type;
20 char *buf; 20 char *buf;
21 unsigned long size, lineno, start, idx; 21 unsigned long size, lineno, idx;
22 const char *linefmt = "<tr><td class='no'><a id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a></td><td class='txt'>"; 22 const char *numberfmt = "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n";
23 23
24 type = sha1_object_info(sha1, &size); 24 type = sha1_object_info(sha1, &size);
25 if (type == OBJ_BAD) { 25 if (type == OBJ_BAD) {
26 cgit_print_error(fmt("Bad object name: %s", 26 cgit_print_error(fmt("Bad object name: %s",
27 sha1_to_hex(sha1))); 27 sha1_to_hex(sha1)));
28 return; 28 return;
29 } 29 }
30 30
31 buf = read_sha1_file(sha1, &type, &size); 31 buf = read_sha1_file(sha1, &type, &size);
32 if (!buf) { 32 if (!buf) {
33 cgit_print_error(fmt("Error reading object %s", 33 cgit_print_error(fmt("Error reading object %s",
34 sha1_to_hex(sha1))); 34 sha1_to_hex(sha1)));
35 return; 35 return;
36 } 36 }
37 37
38 html(" ("); 38 html(" (");
39 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 39 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
40 curr_rev, path); 40 curr_rev, path);
41 htmlf(")<br/>blob: %s", sha1_to_hex(sha1)); 41 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1));
42 42
43 html("<table summary='blob content' class='blob'>\n"); 43 html("<table summary='blob content' class='blob'>\n");
44 html("<tr>\n");
45
46 html("<td class='linenumbers'><pre>");
44 idx = 0; 47 idx = 0;
45 start = 0;
46 lineno = 0; 48 lineno = 0;
47 while(idx < size) { 49 htmlf(numberfmt, ++lineno);
50 while(idx < size - 1) { // skip absolute last newline
48 if (buf[idx] == '\n') { 51 if (buf[idx] == '\n') {
49 buf[idx] = '\0'; 52 htmlf(numberfmt, ++lineno);
50 htmlf(linefmt, ++lineno);
51 html_txt(buf + start);
52 html("</td></tr>\n");
53 start = idx + 1;
54 } 53 }
55 idx++; 54 idx++;
56 } 55 }
57 if (start < idx) { 56 html("</pre></td>\n");
58 htmlf(linefmt, ++lineno); 57
59 html_txt(buf + start); 58 html("<td class='lines'><pre><code>");
60 } 59 html_txt(buf);
61 html("</td></tr>\n"); 60 html("</code></pre></td>\n");
61
62 html("</tr>\n");
62 html("</table>\n"); 63 html("</table>\n");
63} 64}
64 65
65 66
66static int ls_item(const unsigned char *sha1, const char *base, int baselen, 67static int ls_item(const unsigned char *sha1, const char *base, int baselen,
67 const char *pathname, unsigned int mode, int stage, 68 const char *pathname, unsigned int mode, int stage,
68 void *cbdata) 69 void *cbdata)
69{ 70{
70 char *name; 71 char *name;
71 char *fullpath; 72 char *fullpath;
72 enum object_type type; 73 enum object_type type;
73 unsigned long size = 0; 74 unsigned long size = 0;
74 75
75 name = xstrdup(pathname); 76 name = xstrdup(pathname);
76 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 77 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
77 ctx.qry.path ? "/" : "", name); 78 ctx.qry.path ? "/" : "", name);
78 79
79 if (!S_ISGITLINK(mode)) { 80 if (!S_ISGITLINK(mode)) {
80 type = sha1_object_info(sha1, &size); 81 type = sha1_object_info(sha1, &size);
81 if (type == OBJ_BAD) { 82 if (type == OBJ_BAD) {
82 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 83 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
83 name, 84 name,
84 sha1_to_hex(sha1)); 85 sha1_to_hex(sha1));
85 return 0; 86 return 0;
86 } 87 }
87 } 88 }
88 89
89 html("<tr><td class='ls-mode'>"); 90 html("<tr><td class='ls-mode'>");
90 cgit_print_filemode(mode); 91 cgit_print_filemode(mode);
91 html("</td><td>"); 92 html("</td><td>");
92 if (S_ISGITLINK(mode)) { 93 if (S_ISGITLINK(mode)) {
93 htmlf("<a class='ls-mod' href='"); 94 htmlf("<a class='ls-mod' href='");
94 html_attr(fmt(ctx.repo->module_link, 95 html_attr(fmt(ctx.repo->module_link,
95 name, 96 name,
96 sha1_to_hex(sha1))); 97 sha1_to_hex(sha1)));
97 html("'>"); 98 html("'>");
98 html_txt(name); 99 html_txt(name);
99 html("</a>"); 100 html("</a>");
100 } else if (S_ISDIR(mode)) { 101 } else if (S_ISDIR(mode)) {
101 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, 102 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
102 curr_rev, fullpath); 103 curr_rev, fullpath);
103 } else { 104 } else {
104 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, 105 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head,
105 curr_rev, fullpath); 106 curr_rev, fullpath);
106 } 107 }
107 htmlf("</td><td class='ls-size'>%li</td>", size); 108 htmlf("</td><td class='ls-size'>%li</td>", size);
108 109
109 html("<td>"); 110 html("<td>");
110 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, 111 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
111 fullpath, 0, NULL, NULL, ctx.qry.showmsg); 112 fullpath, 0, NULL, NULL, ctx.qry.showmsg);
112 if (ctx.repo->max_stats) 113 if (ctx.repo->max_stats)
113 cgit_stats_link("stats", NULL, "button", ctx.qry.head, 114 cgit_stats_link("stats", NULL, "button", ctx.qry.head,
114 fullpath); 115 fullpath);
115 html("</td></tr>\n"); 116 html("</td></tr>\n");
116 free(name); 117 free(name);
117 return 0; 118 return 0;
118} 119}
119 120
120static void ls_head() 121static void ls_head()
121{ 122{
122 html("<table summary='tree listing' class='list'>\n"); 123 html("<table summary='tree listing' class='list'>\n");
123 html("<tr class='nohover'>"); 124 html("<tr class='nohover'>");
124 html("<th class='left'>Mode</th>"); 125 html("<th class='left'>Mode</th>");
125 html("<th class='left'>Name</th>"); 126 html("<th class='left'>Name</th>");
126 html("<th class='right'>Size</th>"); 127 html("<th class='right'>Size</th>");
127 html("<th/>"); 128 html("<th/>");
128 html("</tr>\n"); 129 html("</tr>\n");
129 header = 1; 130 header = 1;
130} 131}
131 132
132static void ls_tail() 133static void ls_tail()
133{ 134{
134 if (!header) 135 if (!header)
135 return; 136 return;
136 html("</table>\n"); 137 html("</table>\n");
137 header = 0; 138 header = 0;
138} 139}
139 140
140static void ls_tree(const unsigned char *sha1, char *path) 141static void ls_tree(const unsigned char *sha1, char *path)
141{ 142{
142 struct tree *tree; 143 struct tree *tree;
143 144
144 tree = parse_tree_indirect(sha1); 145 tree = parse_tree_indirect(sha1);
145 if (!tree) { 146 if (!tree) {
146 cgit_print_error(fmt("Not a tree object: %s", 147 cgit_print_error(fmt("Not a tree object: %s",
147 sha1_to_hex(sha1))); 148 sha1_to_hex(sha1)));
148 return; 149 return;
149 } 150 }
150 151
151 ls_head(); 152 ls_head();
152 read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); 153 read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL);
153 ls_tail(); 154 ls_tail();
154} 155}
155 156
156 157
157static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 158static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
158 const char *pathname, unsigned mode, int stage, 159 const char *pathname, unsigned mode, int stage,
159 void *cbdata) 160 void *cbdata)
160{ 161{
161 static int state; 162 static int state;
162 static char buffer[PATH_MAX]; 163 static char buffer[PATH_MAX];
163 char *url; 164 char *url;
164 165
165 if (state == 0) { 166 if (state == 0) {
166 memcpy(buffer, base, baselen); 167 memcpy(buffer, base, baselen);
167 strcpy(buffer+baselen, pathname); 168 strcpy(buffer+baselen, pathname);
168 url = cgit_pageurl(ctx.qry.repo, "tree", 169 url = cgit_pageurl(ctx.qry.repo, "tree",
169 fmt("h=%s&amp;path=%s", curr_rev, buffer)); 170 fmt("h=%s&amp;path=%s", curr_rev, buffer));
170 html("/"); 171 html("/");
171 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, 172 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head,
172 curr_rev, buffer); 173 curr_rev, buffer);
173 174
174 if (strcmp(match_path, buffer)) 175 if (strcmp(match_path, buffer))
175 return READ_TREE_RECURSIVE; 176 return READ_TREE_RECURSIVE;
176 177
177 if (S_ISDIR(mode)) { 178 if (S_ISDIR(mode)) {
178 state = 1; 179 state = 1;
179 ls_head(); 180 ls_head();
180 return READ_TREE_RECURSIVE; 181 return READ_TREE_RECURSIVE;
181 } else { 182 } else {
182 print_object(sha1, buffer); 183 print_object(sha1, buffer);
183 return 0; 184 return 0;
184 } 185 }
185 } 186 }
186 ls_item(sha1, base, baselen, pathname, mode, stage, NULL); 187 ls_item(sha1, base, baselen, pathname, mode, stage, NULL);
187 return 0; 188 return 0;
188} 189}
189 190
190 191
191/* 192/*
192 * Show a tree or a blob 193 * Show a tree or a blob
193 * rev: the commit pointing at the root tree object 194 * rev: the commit pointing at the root tree object
194 * path: path to tree or blob 195 * path: path to tree or blob
195 */ 196 */
196void cgit_print_tree(const char *rev, char *path) 197void cgit_print_tree(const char *rev, char *path)
197{ 198{
198 unsigned char sha1[20]; 199 unsigned char sha1[20];
199 struct commit *commit; 200 struct commit *commit;
200 const char *paths[] = {path, NULL}; 201 const char *paths[] = {path, NULL};
201 202
202 if (!rev) 203 if (!rev)
203 rev = ctx.qry.head; 204 rev = ctx.qry.head;
204 205
205 curr_rev = xstrdup(rev); 206 curr_rev = xstrdup(rev);
206 if (get_sha1(rev, sha1)) { 207 if (get_sha1(rev, sha1)) {
207 cgit_print_error(fmt("Invalid revision name: %s", rev)); 208 cgit_print_error(fmt("Invalid revision name: %s", rev));
208 return; 209 return;
209 } 210 }
210 commit = lookup_commit_reference(sha1); 211 commit = lookup_commit_reference(sha1);
211 if (!commit || parse_commit(commit)) { 212 if (!commit || parse_commit(commit)) {
212 cgit_print_error(fmt("Invalid commit reference: %s", rev)); 213 cgit_print_error(fmt("Invalid commit reference: %s", rev));
213 return; 214 return;
214 } 215 }
215 216
216 html("path: <a href='"); 217 html("path: <a href='");
217 html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); 218 html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev)));
218 html("'>root</a>"); 219 html("'>root</a>");
219 220
220 if (path == NULL) { 221 if (path == NULL) {
221 ls_tree(commit->tree->object.sha1, NULL); 222 ls_tree(commit->tree->object.sha1, NULL);
222 return; 223 return;
223 } 224 }
224 225
225 match_path = path; 226 match_path = path;
226 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); 227 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL);
227 ls_tail(); 228 ls_tail();
228} 229}