summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-04-13 10:20:00 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-04-13 10:20:00 (UTC)
commit39912a24edf45497e668ebda25636aa6f6db0c9b (patch) (unidiff)
tree5de079ba1441a4f9546252e38c31d4d1b5f17b40
parent536b0541fcfea2169e4df33043cd9ff14c657bce (diff)
downloadcgit-39912a24edf45497e668ebda25636aa6f6db0c9b.zip
cgit-39912a24edf45497e668ebda25636aa6f6db0c9b.tar.gz
cgit-39912a24edf45497e668ebda25636aa6f6db0c9b.tar.bz2
Remove 'patch' link from tab, add to commit view
It's a bit confusing to enter the patch view from the tab, since it has no layout. And the commit view has always lacked showing the commit id. Both of these warts are fixed by this commit, which adds a new header line in the commit view which shows the commit id as a 'permalink' to the current commit and also adds a link to the patch view of the current commit. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--ui-commit.c6
-rw-r--r--ui-shared.c2
-rw-r--r--ui-shared.h2
3 files changed, 8 insertions, 2 deletions
diff --git a/ui-commit.c b/ui-commit.c
index c2fafd7..dd36cc0 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,230 +1,236 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
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#include "ui-diff.h" 12#include "ui-diff.h"
13 13
14static int files, slots; 14static int files, slots;
15static int total_adds, total_rems, max_changes; 15static int total_adds, total_rems, max_changes;
16static int lines_added, lines_removed; 16static int lines_added, lines_removed;
17static char *curr_rev; 17static char *curr_rev;
18 18
19static struct fileinfo { 19static struct fileinfo {
20 char status; 20 char status;
21 unsigned char old_sha1[20]; 21 unsigned char old_sha1[20];
22 unsigned char new_sha1[20]; 22 unsigned char new_sha1[20];
23 unsigned short old_mode; 23 unsigned short old_mode;
24 unsigned short new_mode; 24 unsigned short new_mode;
25 char *old_path; 25 char *old_path;
26 char *new_path; 26 char *new_path;
27 unsigned int added; 27 unsigned int added;
28 unsigned int removed; 28 unsigned int removed;
29} *items; 29} *items;
30 30
31 31
32void print_fileinfo(struct fileinfo *info) 32void print_fileinfo(struct fileinfo *info)
33{ 33{
34 char *class; 34 char *class;
35 35
36 switch (info->status) { 36 switch (info->status) {
37 case DIFF_STATUS_ADDED: 37 case DIFF_STATUS_ADDED:
38 class = "add"; 38 class = "add";
39 break; 39 break;
40 case DIFF_STATUS_COPIED: 40 case DIFF_STATUS_COPIED:
41 class = "cpy"; 41 class = "cpy";
42 break; 42 break;
43 case DIFF_STATUS_DELETED: 43 case DIFF_STATUS_DELETED:
44 class = "del"; 44 class = "del";
45 break; 45 break;
46 case DIFF_STATUS_MODIFIED: 46 case DIFF_STATUS_MODIFIED:
47 class = "upd"; 47 class = "upd";
48 break; 48 break;
49 case DIFF_STATUS_RENAMED: 49 case DIFF_STATUS_RENAMED:
50 class = "mov"; 50 class = "mov";
51 break; 51 break;
52 case DIFF_STATUS_TYPE_CHANGED: 52 case DIFF_STATUS_TYPE_CHANGED:
53 class = "typ"; 53 class = "typ";
54 break; 54 break;
55 case DIFF_STATUS_UNKNOWN: 55 case DIFF_STATUS_UNKNOWN:
56 class = "unk"; 56 class = "unk";
57 break; 57 break;
58 case DIFF_STATUS_UNMERGED: 58 case DIFF_STATUS_UNMERGED:
59 class = "stg"; 59 class = "stg";
60 break; 60 break;
61 default: 61 default:
62 die("bug: unhandled diff status %c", info->status); 62 die("bug: unhandled diff status %c", info->status);
63 } 63 }
64 64
65 html("<tr>"); 65 html("<tr>");
66 htmlf("<td class='mode'>"); 66 htmlf("<td class='mode'>");
67 if (is_null_sha1(info->new_sha1)) { 67 if (is_null_sha1(info->new_sha1)) {
68 cgit_print_filemode(info->old_mode); 68 cgit_print_filemode(info->old_mode);
69 } else { 69 } else {
70 cgit_print_filemode(info->new_mode); 70 cgit_print_filemode(info->new_mode);
71 } 71 }
72 72
73 if (info->old_mode != info->new_mode && 73 if (info->old_mode != info->new_mode &&
74 !is_null_sha1(info->old_sha1) && 74 !is_null_sha1(info->old_sha1) &&
75 !is_null_sha1(info->new_sha1)) { 75 !is_null_sha1(info->new_sha1)) {
76 html("<span class='modechange'>["); 76 html("<span class='modechange'>[");
77 cgit_print_filemode(info->old_mode); 77 cgit_print_filemode(info->old_mode);
78 html("]</span>"); 78 html("]</span>");
79 } 79 }
80 htmlf("</td><td class='%s'>", class); 80 htmlf("</td><td class='%s'>", class);
81 cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, curr_rev, 81 cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, curr_rev,
82 NULL, info->new_path); 82 NULL, info->new_path);
83 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) 83 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
84 htmlf(" (%s from %s)", 84 htmlf(" (%s from %s)",
85 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", 85 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
86 info->old_path); 86 info->old_path);
87 html("</td><td class='right'>"); 87 html("</td><td class='right'>");
88 htmlf("%d", info->added + info->removed); 88 htmlf("%d", info->added + info->removed);
89 html("</td><td class='graph'>"); 89 html("</td><td class='graph'>");
90 htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); 90 htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
91 htmlf("<td class='add' style='width: %.1f%%;'/>", 91 htmlf("<td class='add' style='width: %.1f%%;'/>",
92 info->added * 100.0 / max_changes); 92 info->added * 100.0 / max_changes);
93 htmlf("<td class='rem' style='width: %.1f%%;'/>", 93 htmlf("<td class='rem' style='width: %.1f%%;'/>",
94 info->removed * 100.0 / max_changes); 94 info->removed * 100.0 / max_changes);
95 htmlf("<td class='none' style='width: %.1f%%;'/>", 95 htmlf("<td class='none' style='width: %.1f%%;'/>",
96 (max_changes - info->removed - info->added) * 100.0 / max_changes); 96 (max_changes - info->removed - info->added) * 100.0 / max_changes);
97 html("</tr></table></td></tr>\n"); 97 html("</tr></table></td></tr>\n");
98} 98}
99 99
100void cgit_count_diff_lines(char *line, int len) 100void cgit_count_diff_lines(char *line, int len)
101{ 101{
102 if (line && (len > 0)) { 102 if (line && (len > 0)) {
103 if (line[0] == '+') 103 if (line[0] == '+')
104 lines_added++; 104 lines_added++;
105 else if (line[0] == '-') 105 else if (line[0] == '-')
106 lines_removed++; 106 lines_removed++;
107 } 107 }
108} 108}
109 109
110void inspect_filepair(struct diff_filepair *pair) 110void inspect_filepair(struct diff_filepair *pair)
111{ 111{
112 files++; 112 files++;
113 lines_added = 0; 113 lines_added = 0;
114 lines_removed = 0; 114 lines_removed = 0;
115 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines); 115 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines);
116 if (files >= slots) { 116 if (files >= slots) {
117 if (slots == 0) 117 if (slots == 0)
118 slots = 4; 118 slots = 4;
119 else 119 else
120 slots = slots * 2; 120 slots = slots * 2;
121 items = xrealloc(items, slots * sizeof(struct fileinfo)); 121 items = xrealloc(items, slots * sizeof(struct fileinfo));
122 } 122 }
123 items[files-1].status = pair->status; 123 items[files-1].status = pair->status;
124 hashcpy(items[files-1].old_sha1, pair->one->sha1); 124 hashcpy(items[files-1].old_sha1, pair->one->sha1);
125 hashcpy(items[files-1].new_sha1, pair->two->sha1); 125 hashcpy(items[files-1].new_sha1, pair->two->sha1);
126 items[files-1].old_mode = pair->one->mode; 126 items[files-1].old_mode = pair->one->mode;
127 items[files-1].new_mode = pair->two->mode; 127 items[files-1].new_mode = pair->two->mode;
128 items[files-1].old_path = xstrdup(pair->one->path); 128 items[files-1].old_path = xstrdup(pair->one->path);
129 items[files-1].new_path = xstrdup(pair->two->path); 129 items[files-1].new_path = xstrdup(pair->two->path);
130 items[files-1].added = lines_added; 130 items[files-1].added = lines_added;
131 items[files-1].removed = lines_removed; 131 items[files-1].removed = lines_removed;
132 if (lines_added + lines_removed > max_changes) 132 if (lines_added + lines_removed > max_changes)
133 max_changes = lines_added + lines_removed; 133 max_changes = lines_added + lines_removed;
134 total_adds += lines_added; 134 total_adds += lines_added;
135 total_rems += lines_removed; 135 total_rems += lines_removed;
136} 136}
137 137
138 138
139void cgit_print_commit(char *hex) 139void cgit_print_commit(char *hex)
140{ 140{
141 struct commit *commit, *parent; 141 struct commit *commit, *parent;
142 struct commitinfo *info; 142 struct commitinfo *info;
143 struct commit_list *p; 143 struct commit_list *p;
144 unsigned char sha1[20]; 144 unsigned char sha1[20];
145 char *tmp; 145 char *tmp;
146 int i; 146 int i;
147 147
148 if (!hex) 148 if (!hex)
149 hex = ctx.qry.head; 149 hex = ctx.qry.head;
150 curr_rev = hex; 150 curr_rev = hex;
151 151
152 if (get_sha1(hex, sha1)) { 152 if (get_sha1(hex, sha1)) {
153 cgit_print_error(fmt("Bad object id: %s", hex)); 153 cgit_print_error(fmt("Bad object id: %s", hex));
154 return; 154 return;
155 } 155 }
156 commit = lookup_commit_reference(sha1); 156 commit = lookup_commit_reference(sha1);
157 if (!commit) { 157 if (!commit) {
158 cgit_print_error(fmt("Bad commit reference: %s", hex)); 158 cgit_print_error(fmt("Bad commit reference: %s", hex));
159 return; 159 return;
160 } 160 }
161 info = cgit_parse_commit(commit); 161 info = cgit_parse_commit(commit);
162 162
163 html("<table summary='commit info' class='commit-info'>\n"); 163 html("<table summary='commit info' class='commit-info'>\n");
164 html("<tr><th>author</th><td>"); 164 html("<tr><th>author</th><td>");
165 html_txt(info->author); 165 html_txt(info->author);
166 html(" "); 166 html(" ");
167 html_txt(info->author_email); 167 html_txt(info->author_email);
168 html("</td><td class='right'>"); 168 html("</td><td class='right'>");
169 cgit_print_date(info->author_date, FMT_LONGDATE); 169 cgit_print_date(info->author_date, FMT_LONGDATE);
170 html("</td></tr>\n"); 170 html("</td></tr>\n");
171 html("<tr><th>committer</th><td>"); 171 html("<tr><th>committer</th><td>");
172 html_txt(info->committer); 172 html_txt(info->committer);
173 html(" "); 173 html(" ");
174 html_txt(info->committer_email); 174 html_txt(info->committer_email);
175 html("</td><td class='right'>"); 175 html("</td><td class='right'>");
176 cgit_print_date(info->committer_date, FMT_LONGDATE); 176 cgit_print_date(info->committer_date, FMT_LONGDATE);
177 html("</td></tr>\n"); 177 html("</td></tr>\n");
178 html("<tr><th>commit</th><td colspan='2' class='sha1'>");
179 tmp = sha1_to_hex(commit->object.sha1);
180 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp);
181 html(" (");
182 cgit_patch_link("patch", NULL, NULL, NULL, tmp);
183 html(")</td></tr>\n");
178 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 184 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
179 tmp = xstrdup(hex); 185 tmp = xstrdup(hex);
180 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 186 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
181 ctx.qry.head, tmp, NULL); 187 ctx.qry.head, tmp, NULL);
182 html("</td></tr>\n"); 188 html("</td></tr>\n");
183 for (p = commit->parents; p ; p = p->next) { 189 for (p = commit->parents; p ; p = p->next) {
184 parent = lookup_commit_reference(p->item->object.sha1); 190 parent = lookup_commit_reference(p->item->object.sha1);
185 if (!parent) { 191 if (!parent) {
186 html("<tr><td colspan='3'>"); 192 html("<tr><td colspan='3'>");
187 cgit_print_error("Error reading parent commit"); 193 cgit_print_error("Error reading parent commit");
188 html("</td></tr>"); 194 html("</td></tr>");
189 continue; 195 continue;
190 } 196 }
191 html("<tr><th>parent</th>" 197 html("<tr><th>parent</th>"
192 "<td colspan='2' class='sha1'>"); 198 "<td colspan='2' class='sha1'>");
193 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 199 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
194 ctx.qry.head, sha1_to_hex(p->item->object.sha1)); 200 ctx.qry.head, sha1_to_hex(p->item->object.sha1));
195 html(" ("); 201 html(" (");
196 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 202 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
197 sha1_to_hex(p->item->object.sha1), NULL); 203 sha1_to_hex(p->item->object.sha1), NULL);
198 html(")</td></tr>"); 204 html(")</td></tr>");
199 } 205 }
200 if (ctx.repo->snapshots) { 206 if (ctx.repo->snapshots) {
201 html("<tr><th>download</th><td colspan='2' class='sha1'>"); 207 html("<tr><th>download</th><td colspan='2' class='sha1'>");
202 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, 208 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
203 hex, ctx.repo->snapshots); 209 hex, ctx.repo->snapshots);
204 html("</td></tr>"); 210 html("</td></tr>");
205 } 211 }
206 html("</table>\n"); 212 html("</table>\n");
207 html("<div class='commit-subject'>"); 213 html("<div class='commit-subject'>");
208 html_txt(info->subject); 214 html_txt(info->subject);
209 html("</div>"); 215 html("</div>");
210 html("<div class='commit-msg'>"); 216 html("<div class='commit-msg'>");
211 html_txt(info->msg); 217 html_txt(info->msg);
212 html("</div>"); 218 html("</div>");
213 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) { 219 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) {
214 html("<div class='diffstat-header'>Diffstat</div>"); 220 html("<div class='diffstat-header'>Diffstat</div>");
215 html("<table summary='diffstat' class='diffstat'>"); 221 html("<table summary='diffstat' class='diffstat'>");
216 max_changes = 0; 222 max_changes = 0;
217 cgit_diff_commit(commit, inspect_filepair); 223 cgit_diff_commit(commit, inspect_filepair);
218 for(i = 0; i<files; i++) 224 for(i = 0; i<files; i++)
219 print_fileinfo(&items[i]); 225 print_fileinfo(&items[i]);
220 html("</table>"); 226 html("</table>");
221 html("<div class='diffstat-summary'>"); 227 html("<div class='diffstat-summary'>");
222 htmlf("%d files changed, %d insertions, %d deletions", 228 htmlf("%d files changed, %d insertions, %d deletions",
223 files, total_adds, total_rems); 229 files, total_adds, total_rems);
224 cgit_print_diff(ctx.qry.sha1, 230 cgit_print_diff(ctx.qry.sha1,
225 sha1_to_hex(commit->parents->item->object.sha1), 231 sha1_to_hex(commit->parents->item->object.sha1),
226 NULL); 232 NULL);
227 html(")</div>"); 233 html(")</div>");
228 } 234 }
229 cgit_free_commitinfo(info); 235 cgit_free_commitinfo(info);
230} 236}
diff --git a/ui-shared.c b/ui-shared.c
index 6253a90..bb08c4a 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,611 +1,609 @@
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("</div>\n</body>\n</html>\n"); 392 html("</div>\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 488
489 html("<td class='main'>"); 489 html("<td class='main'>");
490 if (ctx->repo) { 490 if (ctx->repo) {
491/* 491/*
492 html("<a href='"); 492 html("<a href='");
493 html_attr(cgit_rooturl()); 493 html_attr(cgit_rooturl());
494 html("'>index</a> : "); 494 html("'>index</a> : ");
495*/ 495*/
496 reporevlink(NULL, ctx->repo->name, NULL, hc(cmd, "summary"), 496 reporevlink(NULL, ctx->repo->name, NULL, hc(cmd, "summary"),
497 ctx->qry.head, NULL, NULL); 497 ctx->qry.head, NULL, NULL);
498 html(" : "); 498 html(" : ");
499 html_txt(ctx->qry.page); 499 html_txt(ctx->qry.page);
500 html("</td><td class='form'>"); 500 html("</td><td class='form'>");
501 html("<form method='get' action=''>\n"); 501 html("<form method='get' action=''>\n");
502 add_hidden_formfields(0, 1, ctx->qry.page); 502 add_hidden_formfields(0, 1, ctx->qry.page);
503 html("<select name='h' onchange='this.form.submit();'>\n"); 503 html("<select name='h' onchange='this.form.submit();'>\n");
504 for_each_branch_ref(print_branch_option, ctx->qry.head); 504 for_each_branch_ref(print_branch_option, ctx->qry.head);
505 html("</select> "); 505 html("</select> ");
506 html("<input type='submit' name='' value='switch'/>"); 506 html("<input type='submit' name='' value='switch'/>");
507 html("</form>"); 507 html("</form>");
508 } else 508 } else
509 html_txt(ctx->cfg.root_title); 509 html_txt(ctx->cfg.root_title);
510 html("</td>\n"); 510 html("</td>\n");
511 511
512 html("<tr><td class='sub'"); 512 html("<tr><td class='sub'");
513 if (ctx->repo) { 513 if (ctx->repo) {
514 html(" colspan='2'>"); 514 html(" colspan='2'>");
515 html_txt(ctx->repo->desc); 515 html_txt(ctx->repo->desc);
516 } 516 }
517/* 517/*
518 else if (ctx->cfg.root_subtitle) 518 else if (ctx->cfg.root_subtitle)
519 html_txt(ctx->cfg.root_subtitle); 519 html_txt(ctx->cfg.root_subtitle);
520*/ 520*/
521 else { 521 else {
522 html(">"); 522 html(">");
523 html_txt("a fast webinterface for the git dscm"); 523 html_txt("a fast webinterface for the git dscm");
524 } 524 }
525 html("</td></tr>\n"); 525 html("</td></tr>\n");
526 526
527 html("</tr>\n"); 527 html("</tr>\n");
528 html("</table>\n"); 528 html("</table>\n");
529 529
530 html("<table class='tabs'><tr><td>\n"); 530 html("<table class='tabs'><tr><td>\n");
531 if (ctx->repo) { 531 if (ctx->repo) {
532 reporevlink(NULL, "summary", NULL, hc(cmd, "summary"), 532 reporevlink(NULL, "summary", NULL, hc(cmd, "summary"),
533 ctx->qry.head, NULL, NULL); 533 ctx->qry.head, NULL, NULL);
534 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, 534 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
535 ctx->qry.sha1, NULL); 535 ctx->qry.sha1, NULL);
536 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, 536 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
537 NULL, NULL, 0, NULL, NULL); 537 NULL, NULL, 0, NULL, NULL);
538 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, 538 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
539 ctx->qry.sha1, NULL); 539 ctx->qry.sha1, NULL);
540 cgit_commit_link("commit", NULL, hc(cmd, "commit"), 540 cgit_commit_link("commit", NULL, hc(cmd, "commit"),
541 ctx->qry.head, ctx->qry.sha1); 541 ctx->qry.head, ctx->qry.sha1);
542 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 542 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
543 ctx->qry.sha1, ctx->qry.sha2, NULL); 543 ctx->qry.sha1, ctx->qry.sha2, NULL);
544 cgit_patch_link("patch", NULL, hc(cmd, "patch"), ctx->qry.head,
545 ctx->qry.sha1);
546 html("</td><td class='form'>"); 544 html("</td><td class='form'>");
547 html("<form class='right' method='get' action='"); 545 html("<form class='right' method='get' action='");
548 if (ctx->cfg.virtual_root) 546 if (ctx->cfg.virtual_root)
549 html_attr(cgit_fileurl(ctx->qry.repo, "log", 547 html_attr(cgit_fileurl(ctx->qry.repo, "log",
550 ctx->qry.path, NULL)); 548 ctx->qry.path, NULL));
551 html("'>\n"); 549 html("'>\n");
552 add_hidden_formfields(1, 0, "log"); 550 add_hidden_formfields(1, 0, "log");
553 html("<select name='qt'>\n"); 551 html("<select name='qt'>\n");
554 html_option("grep", "log msg", ctx->qry.grep); 552 html_option("grep", "log msg", ctx->qry.grep);
555 html_option("author", "author", ctx->qry.grep); 553 html_option("author", "author", ctx->qry.grep);
556 html_option("committer", "committer", ctx->qry.grep); 554 html_option("committer", "committer", ctx->qry.grep);
557 html("</select>\n"); 555 html("</select>\n");
558 html("<input class='txt' type='text' size='10' name='q' value='"); 556 html("<input class='txt' type='text' size='10' name='q' value='");
559 html_attr(ctx->qry.search); 557 html_attr(ctx->qry.search);
560 html("'/>\n"); 558 html("'/>\n");
561 html("<input type='submit' value='search'/>\n"); 559 html("<input type='submit' value='search'/>\n");
562 html("</form>\n"); 560 html("</form>\n");
563 } else { 561 } else {
564 html("<a class='active' href='"); 562 html("<a class='active' href='");
565 html_attr(cgit_rooturl()); 563 html_attr(cgit_rooturl());
566 html("'>index</a>\n"); 564 html("'>index</a>\n");
567 html("</td><td class='form'>"); 565 html("</td><td class='form'>");
568 html("<form method='get' action='"); 566 html("<form method='get' action='");
569 html_attr(cgit_rooturl()); 567 html_attr(cgit_rooturl());
570 html("'>\n"); 568 html("'>\n");
571 html("<input type='text' name='q' size='10' value='"); 569 html("<input type='text' name='q' size='10' value='");
572 html_attr(ctx->qry.search); 570 html_attr(ctx->qry.search);
573 html("'/>\n"); 571 html("'/>\n");
574 html("<input type='submit' value='search'/>\n"); 572 html("<input type='submit' value='search'/>\n");
575 html("</form>"); 573 html("</form>");
576 } 574 }
577 html("</td></tr></table>\n"); 575 html("</td></tr></table>\n");
578 html("<div class='content'>"); 576 html("<div class='content'>");
579} 577}
580 578
581void cgit_print_filemode(unsigned short mode) 579void cgit_print_filemode(unsigned short mode)
582{ 580{
583 if (S_ISDIR(mode)) 581 if (S_ISDIR(mode))
584 html("d"); 582 html("d");
585 else if (S_ISLNK(mode)) 583 else if (S_ISLNK(mode))
586 html("l"); 584 html("l");
587 else if (S_ISGITLINK(mode)) 585 else if (S_ISGITLINK(mode))
588 html("m"); 586 html("m");
589 else 587 else
590 html("-"); 588 html("-");
591 html_fileperm(mode >> 6); 589 html_fileperm(mode >> 6);
592 html_fileperm(mode >> 3); 590 html_fileperm(mode >> 3);
593 html_fileperm(mode); 591 html_fileperm(mode);
594} 592}
595 593
596void cgit_print_snapshot_links(const char *repo, const char *head, 594void cgit_print_snapshot_links(const char *repo, const char *head,
597 const char *hex, int snapshots) 595 const char *hex, int snapshots)
598{ 596{
599 const struct cgit_snapshot_format* f; 597 const struct cgit_snapshot_format* f;
600 char *filename; 598 char *filename;
601 599
602 for (f = cgit_snapshot_formats; f->suffix; f++) { 600 for (f = cgit_snapshot_formats; f->suffix; f++) {
603 if (!(snapshots & f->bit)) 601 if (!(snapshots & f->bit))
604 continue; 602 continue;
605 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, 603 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
606 f->suffix); 604 f->suffix);
607 cgit_snapshot_link(filename, NULL, NULL, (char *)head, 605 cgit_snapshot_link(filename, NULL, NULL, (char *)head,
608 (char *)hex, filename); 606 (char *)hex, filename);
609 html("<br/>"); 607 html("<br/>");
610 } 608 }
611} 609}
diff --git a/ui-shared.h b/ui-shared.h
index 94de884..76c2b1f 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,36 +1,38 @@
1#ifndef UI_SHARED_H 1#ifndef UI_SHARED_H
2#define UI_SHARED_H 2#define UI_SHARED_H
3 3
4extern char *cgit_repourl(const char *reponame); 4extern char *cgit_repourl(const char *reponame);
5extern char *cgit_fileurl(const char *reponame, const char *pagename, 5extern char *cgit_fileurl(const char *reponame, const char *pagename,
6 const char *filename, const char *query); 6 const char *filename, const char *query);
7extern char *cgit_pageurl(const char *reponame, const char *pagename, 7extern char *cgit_pageurl(const char *reponame, const char *pagename,
8 const char *query); 8 const char *query);
9 9
10extern void cgit_tree_link(char *name, char *title, char *class, char *head, 10extern void cgit_tree_link(char *name, char *title, char *class, char *head,
11 char *rev, char *path); 11 char *rev, char *path);
12extern void cgit_log_link(char *name, char *title, char *class, char *head, 12extern void cgit_log_link(char *name, char *title, char *class, char *head,
13 char *rev, char *path, int ofs, char *grep, 13 char *rev, char *path, int ofs, char *grep,
14 char *pattern); 14 char *pattern);
15extern void cgit_commit_link(char *name, char *title, char *class, char *head, 15extern void cgit_commit_link(char *name, char *title, char *class, char *head,
16 char *rev); 16 char *rev);
17extern void cgit_patch_link(char *name, char *title, char *class, char *head,
18 char *rev);
17extern void cgit_refs_link(char *name, char *title, char *class, char *head, 19extern void cgit_refs_link(char *name, char *title, char *class, char *head,
18 char *rev, char *path); 20 char *rev, char *path);
19extern void cgit_snapshot_link(char *name, char *title, char *class, 21extern void cgit_snapshot_link(char *name, char *title, char *class,
20 char *head, char *rev, char *archivename); 22 char *head, char *rev, char *archivename);
21extern void cgit_diff_link(char *name, char *title, char *class, char *head, 23extern void cgit_diff_link(char *name, char *title, char *class, char *head,
22 char *new_rev, char *old_rev, char *path); 24 char *new_rev, char *old_rev, char *path);
23extern void cgit_object_link(struct object *obj); 25extern void cgit_object_link(struct object *obj);
24 26
25extern void cgit_print_error(char *msg); 27extern void cgit_print_error(char *msg);
26extern void cgit_print_date(time_t secs, char *format); 28extern void cgit_print_date(time_t secs, char *format);
27extern void cgit_print_age(time_t t, time_t max_relative, char *format); 29extern void cgit_print_age(time_t t, time_t max_relative, char *format);
28extern void cgit_print_http_headers(struct cgit_context *ctx); 30extern void cgit_print_http_headers(struct cgit_context *ctx);
29extern void cgit_print_docstart(struct cgit_context *ctx); 31extern void cgit_print_docstart(struct cgit_context *ctx);
30extern void cgit_print_docend(); 32extern void cgit_print_docend();
31extern void cgit_print_pageheader(struct cgit_context *ctx); 33extern void cgit_print_pageheader(struct cgit_context *ctx);
32extern void cgit_print_filemode(unsigned short mode); 34extern void cgit_print_filemode(unsigned short mode);
33extern void cgit_print_snapshot_links(const char *repo, const char *head, 35extern void cgit_print_snapshot_links(const char *repo, const char *head,
34 const char *hex, int snapshots); 36 const char *hex, int snapshots);
35 37
36#endif /* UI_SHARED_H */ 38#endif /* UI_SHARED_H */