-rw-r--r-- | ui-shared.c | 16 |
1 files changed, 16 insertions, 0 deletions
diff --git a/ui-shared.c b/ui-shared.c index cc1ab8b..2eff79d 100644 --- a/ui-shared.c +++ b/ui-shared.c | |||
@@ -1,105 +1,106 @@ | |||
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 "html.h" | ||
10 | 11 | ||
11 | const char cgit_doctype[] = | 12 | const char cgit_doctype[] = |
12 | "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" | 13 | "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" |
13 | " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; | 14 | " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; |
14 | 15 | ||
15 | static char *http_date(time_t t) | 16 | static char *http_date(time_t t) |
16 | { | 17 | { |
17 | static char day[][4] = | 18 | static char day[][4] = |
18 | {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; | 19 | {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; |
19 | static char month[][4] = | 20 | static char month[][4] = |
20 | {"Jan", "Feb", "Mar", "Apr", "May", "Jun", | 21 | {"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
21 | "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; | 22 | "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; |
22 | struct tm *tm = gmtime(&t); | 23 | struct tm *tm = gmtime(&t); |
23 | return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], | 24 | 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, | 25 | tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, |
25 | tm->tm_hour, tm->tm_min, tm->tm_sec); | 26 | tm->tm_hour, tm->tm_min, tm->tm_sec); |
26 | } | 27 | } |
27 | 28 | ||
28 | static long ttl_seconds(long ttl) | 29 | static long ttl_seconds(long ttl) |
29 | { | 30 | { |
30 | if (ttl<0) | 31 | if (ttl<0) |
31 | return 60 * 60 * 24 * 365; | 32 | return 60 * 60 * 24 * 365; |
32 | else | 33 | else |
33 | return ttl * 60; | 34 | return ttl * 60; |
34 | } | 35 | } |
35 | 36 | ||
36 | void cgit_print_error(char *msg) | 37 | void cgit_print_error(char *msg) |
37 | { | 38 | { |
38 | html("<div class='error'>"); | 39 | html("<div class='error'>"); |
39 | html_txt(msg); | 40 | html_txt(msg); |
40 | html("</div>\n"); | 41 | html("</div>\n"); |
41 | } | 42 | } |
42 | 43 | ||
43 | char *cgit_rooturl() | 44 | char *cgit_rooturl() |
44 | { | 45 | { |
45 | if (ctx.cfg.virtual_root) | 46 | if (ctx.cfg.virtual_root) |
46 | return fmt("%s/", ctx.cfg.virtual_root); | 47 | return fmt("%s/", ctx.cfg.virtual_root); |
47 | else | 48 | else |
48 | return ctx.cfg.script_name; | 49 | return ctx.cfg.script_name; |
49 | } | 50 | } |
50 | 51 | ||
51 | char *cgit_repourl(const char *reponame) | 52 | char *cgit_repourl(const char *reponame) |
52 | { | 53 | { |
53 | if (ctx.cfg.virtual_root) { | 54 | if (ctx.cfg.virtual_root) { |
54 | return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); | 55 | return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); |
55 | } else { | 56 | } else { |
56 | return fmt("?r=%s", reponame); | 57 | return fmt("?r=%s", reponame); |
57 | } | 58 | } |
58 | } | 59 | } |
59 | 60 | ||
60 | char *cgit_fileurl(const char *reponame, const char *pagename, | 61 | char *cgit_fileurl(const char *reponame, const char *pagename, |
61 | const char *filename, const char *query) | 62 | const char *filename, const char *query) |
62 | { | 63 | { |
63 | char *tmp; | 64 | char *tmp; |
64 | char *delim; | 65 | char *delim; |
65 | 66 | ||
66 | if (ctx.cfg.virtual_root) { | 67 | if (ctx.cfg.virtual_root) { |
67 | tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, | 68 | tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, |
68 | pagename, (filename ? filename:"")); | 69 | pagename, (filename ? filename:"")); |
69 | delim = "?"; | 70 | delim = "?"; |
70 | } else { | 71 | } else { |
71 | tmp = fmt("?url=%s/%s/%s", reponame, pagename, | 72 | tmp = fmt("?url=%s/%s/%s", reponame, pagename, |
72 | (filename ? filename : "")); | 73 | (filename ? filename : "")); |
73 | delim = "&"; | 74 | delim = "&"; |
74 | } | 75 | } |
75 | if (query) | 76 | if (query) |
76 | tmp = fmt("%s%s%s", tmp, delim, query); | 77 | tmp = fmt("%s%s%s", tmp, delim, query); |
77 | return tmp; | 78 | return tmp; |
78 | } | 79 | } |
79 | 80 | ||
80 | char *cgit_pageurl(const char *reponame, const char *pagename, | 81 | char *cgit_pageurl(const char *reponame, const char *pagename, |
81 | const char *query) | 82 | const char *query) |
82 | { | 83 | { |
83 | return cgit_fileurl(reponame,pagename,0,query); | 84 | return cgit_fileurl(reponame,pagename,0,query); |
84 | } | 85 | } |
85 | 86 | ||
86 | const char *cgit_repobasename(const char *reponame) | 87 | const char *cgit_repobasename(const char *reponame) |
87 | { | 88 | { |
88 | /* I assume we don't need to store more than one repo basename */ | 89 | /* I assume we don't need to store more than one repo basename */ |
89 | static char rvbuf[1024]; | 90 | static char rvbuf[1024]; |
90 | int p; | 91 | int p; |
91 | const char *rv; | 92 | const char *rv; |
92 | strncpy(rvbuf,reponame,sizeof(rvbuf)); | 93 | strncpy(rvbuf,reponame,sizeof(rvbuf)); |
93 | if(rvbuf[sizeof(rvbuf)-1]) | 94 | if(rvbuf[sizeof(rvbuf)-1]) |
94 | die("cgit_repobasename: truncated repository name '%s'", reponame); | 95 | die("cgit_repobasename: truncated repository name '%s'", reponame); |
95 | p = strlen(rvbuf)-1; | 96 | p = strlen(rvbuf)-1; |
96 | /* strip trailing slashes */ | 97 | /* strip trailing slashes */ |
97 | while(p && rvbuf[p]=='/') rvbuf[p--]=0; | 98 | while(p && rvbuf[p]=='/') rvbuf[p--]=0; |
98 | /* strip trailing .git */ | 99 | /* strip trailing .git */ |
99 | if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { | 100 | if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { |
100 | p -= 3; rvbuf[p--] = 0; | 101 | p -= 3; rvbuf[p--] = 0; |
101 | } | 102 | } |
102 | /* strip more trailing slashes if any */ | 103 | /* strip more trailing slashes if any */ |
103 | while( p && rvbuf[p]=='/') rvbuf[p--]=0; | 104 | while( p && rvbuf[p]=='/') rvbuf[p--]=0; |
104 | /* find last slash in the remaining string */ | 105 | /* find last slash in the remaining string */ |
105 | rv = strrchr(rvbuf,'/'); | 106 | rv = strrchr(rvbuf,'/'); |
@@ -474,97 +475,112 @@ void cgit_print_pageheader(char *title, int show_search) | |||
474 | html("<tr><td class='sidebar'>\n<a href='"); | 475 | html("<tr><td class='sidebar'>\n<a href='"); |
475 | html_attr(cgit_rooturl()); | 476 | html_attr(cgit_rooturl()); |
476 | htmlf("'><img src='%s' alt='cgit'/></a>\n", | 477 | htmlf("'><img src='%s' alt='cgit'/></a>\n", |
477 | ctx.cfg.logo); | 478 | ctx.cfg.logo); |
478 | html("</td></tr>\n<tr><td class='sidebar'>\n"); | 479 | html("</td></tr>\n<tr><td class='sidebar'>\n"); |
479 | if (ctx.repo) { | 480 | if (ctx.repo) { |
480 | html("<h1 class='first'>"); | 481 | html("<h1 class='first'>"); |
481 | html_txt(strrpart(ctx.repo->name, 20)); | 482 | html_txt(strrpart(ctx.repo->name, 20)); |
482 | html("</h1>\n"); | 483 | html("</h1>\n"); |
483 | html_txt(ctx.repo->desc); | 484 | html_txt(ctx.repo->desc); |
484 | if (ctx.repo->owner) { | 485 | if (ctx.repo->owner) { |
485 | html("<h1>owner</h1>\n"); | 486 | html("<h1>owner</h1>\n"); |
486 | html_txt(ctx.repo->owner); | 487 | html_txt(ctx.repo->owner); |
487 | } | 488 | } |
488 | html("<h1>navigate</h1>\n"); | 489 | html("<h1>navigate</h1>\n"); |
489 | reporevlink(NULL, "summary", NULL, "menu", ctx.qry.head, | 490 | reporevlink(NULL, "summary", NULL, "menu", ctx.qry.head, |
490 | NULL, NULL); | 491 | NULL, NULL); |
491 | cgit_log_link("log", NULL, "menu", ctx.qry.head, NULL, NULL, | 492 | cgit_log_link("log", NULL, "menu", ctx.qry.head, NULL, NULL, |
492 | 0, NULL, NULL); | 493 | 0, NULL, NULL); |
493 | cgit_tree_link("tree", NULL, "menu", ctx.qry.head, | 494 | cgit_tree_link("tree", NULL, "menu", ctx.qry.head, |
494 | ctx.qry.sha1, NULL); | 495 | ctx.qry.sha1, NULL); |
495 | cgit_commit_link("commit", NULL, "menu", ctx.qry.head, | 496 | cgit_commit_link("commit", NULL, "menu", ctx.qry.head, |
496 | ctx.qry.sha1); | 497 | ctx.qry.sha1); |
497 | cgit_diff_link("diff", NULL, "menu", ctx.qry.head, | 498 | cgit_diff_link("diff", NULL, "menu", ctx.qry.head, |
498 | ctx.qry.sha1, ctx.qry.sha2, NULL); | 499 | ctx.qry.sha1, ctx.qry.sha2, NULL); |
499 | cgit_patch_link("patch", NULL, "menu", ctx.qry.head, | 500 | cgit_patch_link("patch", NULL, "menu", ctx.qry.head, |
500 | ctx.qry.sha1); | 501 | ctx.qry.sha1); |
501 | 502 | ||
502 | for_each_ref(print_archive_ref, &header); | 503 | for_each_ref(print_archive_ref, &header); |
503 | 504 | ||
504 | if (ctx.repo->clone_url || ctx.cfg.clone_prefix) { | 505 | if (ctx.repo->clone_url || ctx.cfg.clone_prefix) { |
505 | html("<h1>clone</h1>\n"); | 506 | html("<h1>clone</h1>\n"); |
506 | if (ctx.repo->clone_url) | 507 | if (ctx.repo->clone_url) |
507 | url = ctx.repo->clone_url; | 508 | url = ctx.repo->clone_url; |
508 | else | 509 | else |
509 | url = fmt("%s%s", ctx.cfg.clone_prefix, | 510 | url = fmt("%s%s", ctx.cfg.clone_prefix, |
510 | ctx.repo->url); | 511 | ctx.repo->url); |
511 | html("<a class='menu' href='"); | 512 | html("<a class='menu' href='"); |
512 | html_attr(url); | 513 | html_attr(url); |
513 | html("' title='"); | 514 | html("' title='"); |
514 | html_attr(url); | 515 | html_attr(url); |
515 | html("'>\n"); | 516 | html("'>\n"); |
516 | html_txt(strrpart(url, 20)); | 517 | html_txt(strrpart(url, 20)); |
517 | html("</a>\n"); | 518 | html("</a>\n"); |
518 | } | 519 | } |
519 | 520 | ||
520 | html("<h1>branch</h1>\n"); | 521 | html("<h1>branch</h1>\n"); |
521 | html("<form method='get' action=''>\n"); | 522 | html("<form method='get' action=''>\n"); |
522 | add_hidden_formfields(0, 1, ctx.qry.page); | 523 | add_hidden_formfields(0, 1, ctx.qry.page); |
523 | // html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>"); | 524 | // html("<table summary='branch selector' class='grid'><tr><td id='branch-dropdown-cell'>"); |
524 | html("<select name='h' onchange='this.form.submit();'>\n"); | 525 | html("<select name='h' onchange='this.form.submit();'>\n"); |
525 | for_each_branch_ref(print_branch_option, ctx.qry.head); | 526 | for_each_branch_ref(print_branch_option, ctx.qry.head); |
526 | html("</select>\n"); | 527 | html("</select>\n"); |
527 | // html("</td><td>"); | 528 | // html("</td><td>"); |
528 | html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n"); | 529 | html("<noscript><input type='submit' id='switch-btn' value='switch'/></noscript>\n"); |
529 | // html("</td></tr></table>"); | 530 | // html("</td></tr></table>"); |
530 | html("</form>\n"); | 531 | html("</form>\n"); |
531 | 532 | ||
532 | html("<h1>search</h1>\n"); | 533 | html("<h1>search</h1>\n"); |
533 | html("<form method='get' action='"); | 534 | html("<form method='get' action='"); |
534 | if (ctx.cfg.virtual_root) | 535 | if (ctx.cfg.virtual_root) |
535 | html_attr(cgit_fileurl(ctx.qry.repo, "log", | 536 | html_attr(cgit_fileurl(ctx.qry.repo, "log", |
536 | ctx.qry.path, NULL)); | 537 | ctx.qry.path, NULL)); |
537 | html("'>\n"); | 538 | html("'>\n"); |
538 | add_hidden_formfields(1, 0, "log"); | 539 | add_hidden_formfields(1, 0, "log"); |
539 | html("<select name='qt'>\n"); | 540 | html("<select name='qt'>\n"); |
540 | html_option("grep", "log msg", ctx.qry.grep); | 541 | html_option("grep", "log msg", ctx.qry.grep); |
541 | html_option("author", "author", ctx.qry.grep); | 542 | html_option("author", "author", ctx.qry.grep); |
542 | html_option("committer", "committer", ctx.qry.grep); | 543 | html_option("committer", "committer", ctx.qry.grep); |
543 | html("</select>\n"); | 544 | html("</select>\n"); |
544 | html("<input class='txt' type='text' name='q' value='"); | 545 | html("<input class='txt' type='text' name='q' value='"); |
545 | html_attr(ctx.qry.search); | 546 | html_attr(ctx.qry.search); |
546 | html("'/>\n"); | 547 | html("'/>\n"); |
547 | html("</form>\n"); | 548 | html("</form>\n"); |
548 | } else { | 549 | } else { |
549 | if (!ctx.cfg.index_info || html_include(ctx.cfg.index_info)) | 550 | if (!ctx.cfg.index_info || html_include(ctx.cfg.index_info)) |
550 | html(default_info); | 551 | html(default_info); |
551 | } | 552 | } |
552 | 553 | ||
553 | html("</td></tr></table></td>\n"); | 554 | html("</td></tr></table></td>\n"); |
554 | 555 | ||
555 | html("<td id='content'>\n"); | 556 | html("<td id='content'>\n"); |
556 | } | 557 | } |
557 | 558 | ||
558 | 559 | ||
559 | void cgit_print_snapshot_start(const char *mimetype, const char *filename, | 560 | void cgit_print_snapshot_start(const char *mimetype, const char *filename, |
560 | struct cacheitem *item) | 561 | struct cacheitem *item) |
561 | { | 562 | { |
562 | htmlf("Content-Type: %s\n", mimetype); | 563 | htmlf("Content-Type: %s\n", mimetype); |
563 | htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); | 564 | htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); |
564 | htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); | 565 | htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); |
565 | htmlf("Expires: %s\n", http_date(item->st.st_mtime + | 566 | htmlf("Expires: %s\n", http_date(item->st.st_mtime + |
566 | ttl_seconds(item->ttl))); | 567 | ttl_seconds(item->ttl))); |
567 | html("\n"); | 568 | html("\n"); |
568 | } | 569 | } |
569 | 570 | ||
571 | void cgit_print_filemode(unsigned short mode) | ||
572 | { | ||
573 | if (S_ISDIR(mode)) | ||
574 | html("d"); | ||
575 | else if (S_ISLNK(mode)) | ||
576 | html("l"); | ||
577 | else if (S_ISGITLINK(mode)) | ||
578 | html("m"); | ||
579 | else | ||
580 | html("-"); | ||
581 | html_fileperm(mode >> 6); | ||
582 | html_fileperm(mode >> 3); | ||
583 | html_fileperm(mode); | ||
584 | } | ||
585 | |||
570 | /* vim:set sw=8: */ | 586 | /* vim:set sw=8: */ |