summaryrefslogtreecommitdiffabout
authorJohan Herland <johan@herland.net>2010-06-09 23:09:29 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-06-19 08:40:22 (UTC)
commit24fd7e54c82294efa68ecae5dd9cb8a8986c04bf (patch) (unidiff)
tree5a4824456d046f40717fc50686c1e03b5c6efdf4
parentc93ef96aaf77437abeb552bd9e30973f90365f3a (diff)
downloadcgit-24fd7e54c82294efa68ecae5dd9cb8a8986c04bf.zip
cgit-24fd7e54c82294efa68ecae5dd9cb8a8986c04bf.tar.gz
cgit-24fd7e54c82294efa68ecae5dd9cb8a8986c04bf.tar.bz2
ui-shared: Teach "breadcrumb" navigation to path limit display beneath tab bar
When a path limit is in effect, and displayed directly beneath the tab bar, it should offer breadcrumb navigation (like what the 'tree' page does), to allow changing the path limit easily. Implementing this requires a robust way to link back to the current page with a changed ctx->qry.path, but without losing track of the other query arguments. This is solved by adding the new cgit_self_link() function, which is then invoked repeatedly by the new cgit_print_path_crumbs() function while manipulating ctx->qry.path. Signed-off-by: Johan Herland <johan@herland.net> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--ui-shared.c81
-rw-r--r--ui-shared.h2
2 files changed, 82 insertions, 1 deletions
diff --git a/ui-shared.c b/ui-shared.c
index bc14e70..4fa506f 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,803 +1,882 @@
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", "Nov", "Dec"}; 23 "Jul", "Aug", "Sep", "Oct", "Nov", "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(const char *msg) 30void cgit_print_error(const 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_httpscheme() 37char *cgit_httpscheme()
38{ 38{
39 if (ctx.env.https && !strcmp(ctx.env.https, "on")) 39 if (ctx.env.https && !strcmp(ctx.env.https, "on"))
40 return "https://"; 40 return "https://";
41 else 41 else
42 return "http://"; 42 return "http://";
43} 43}
44 44
45char *cgit_hosturl() 45char *cgit_hosturl()
46{ 46{
47 if (ctx.env.http_host) 47 if (ctx.env.http_host)
48 return ctx.env.http_host; 48 return ctx.env.http_host;
49 if (!ctx.env.server_name) 49 if (!ctx.env.server_name)
50 return NULL; 50 return NULL;
51 if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) 51 if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
52 return ctx.env.server_name; 52 return ctx.env.server_name;
53 return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port)); 53 return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port));
54} 54}
55 55
56char *cgit_rooturl() 56char *cgit_rooturl()
57{ 57{
58 if (ctx.cfg.virtual_root) 58 if (ctx.cfg.virtual_root)
59 return fmt("%s/", ctx.cfg.virtual_root); 59 return fmt("%s/", ctx.cfg.virtual_root);
60 else 60 else
61 return ctx.cfg.script_name; 61 return ctx.cfg.script_name;
62} 62}
63 63
64char *cgit_repourl(const char *reponame) 64char *cgit_repourl(const char *reponame)
65{ 65{
66 if (ctx.cfg.virtual_root) { 66 if (ctx.cfg.virtual_root) {
67 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); 67 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
68 } else { 68 } else {
69 return fmt("?r=%s", reponame); 69 return fmt("?r=%s", reponame);
70 } 70 }
71} 71}
72 72
73char *cgit_fileurl(const char *reponame, const char *pagename, 73char *cgit_fileurl(const char *reponame, const char *pagename,
74 const char *filename, const char *query) 74 const char *filename, const char *query)
75{ 75{
76 char *tmp; 76 char *tmp;
77 char *delim; 77 char *delim;
78 78
79 if (ctx.cfg.virtual_root) { 79 if (ctx.cfg.virtual_root) {
80 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, 80 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
81 pagename, (filename ? filename:"")); 81 pagename, (filename ? filename:""));
82 delim = "?"; 82 delim = "?";
83 } else { 83 } else {
84 tmp = fmt("?url=%s/%s/%s", reponame, pagename, 84 tmp = fmt("?url=%s/%s/%s", reponame, pagename,
85 (filename ? filename : "")); 85 (filename ? filename : ""));
86 delim = "&"; 86 delim = "&";
87 } 87 }
88 if (query) 88 if (query)
89 tmp = fmt("%s%s%s", tmp, delim, query); 89 tmp = fmt("%s%s%s", tmp, delim, query);
90 return tmp; 90 return tmp;
91} 91}
92 92
93char *cgit_pageurl(const char *reponame, const char *pagename, 93char *cgit_pageurl(const char *reponame, const char *pagename,
94 const char *query) 94 const char *query)
95{ 95{
96 return cgit_fileurl(reponame,pagename,0,query); 96 return cgit_fileurl(reponame,pagename,0,query);
97} 97}
98 98
99const char *cgit_repobasename(const char *reponame) 99const char *cgit_repobasename(const char *reponame)
100{ 100{
101 /* I assume we don't need to store more than one repo basename */ 101 /* I assume we don't need to store more than one repo basename */
102 static char rvbuf[1024]; 102 static char rvbuf[1024];
103 int p; 103 int p;
104 const char *rv; 104 const char *rv;
105 strncpy(rvbuf,reponame,sizeof(rvbuf)); 105 strncpy(rvbuf,reponame,sizeof(rvbuf));
106 if(rvbuf[sizeof(rvbuf)-1]) 106 if(rvbuf[sizeof(rvbuf)-1])
107 die("cgit_repobasename: truncated repository name '%s'", reponame); 107 die("cgit_repobasename: truncated repository name '%s'", reponame);
108 p = strlen(rvbuf)-1; 108 p = strlen(rvbuf)-1;
109 /* strip trailing slashes */ 109 /* strip trailing slashes */
110 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 110 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
111 /* strip trailing .git */ 111 /* strip trailing .git */
112 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 112 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
113 p -= 3; rvbuf[p--] = 0; 113 p -= 3; rvbuf[p--] = 0;
114 } 114 }
115 /* strip more trailing slashes if any */ 115 /* strip more trailing slashes if any */
116 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 116 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
117 /* find last slash in the remaining string */ 117 /* find last slash in the remaining string */
118 rv = strrchr(rvbuf,'/'); 118 rv = strrchr(rvbuf,'/');
119 if(rv) 119 if(rv)
120 return ++rv; 120 return ++rv;
121 return rvbuf; 121 return rvbuf;
122} 122}
123 123
124char *cgit_currurl() 124char *cgit_currurl()
125{ 125{
126 if (!ctx.cfg.virtual_root) 126 if (!ctx.cfg.virtual_root)
127 return ctx.cfg.script_name; 127 return ctx.cfg.script_name;
128 else if (ctx.qry.page) 128 else if (ctx.qry.page)
129 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); 129 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
130 else if (ctx.qry.repo) 130 else if (ctx.qry.repo)
131 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); 131 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
132 else 132 else
133 return fmt("%s/", ctx.cfg.virtual_root); 133 return fmt("%s/", ctx.cfg.virtual_root);
134} 134}
135 135
136static void site_url(const char *page, const char *search, int ofs) 136static void site_url(const char *page, const char *search, int ofs)
137{ 137{
138 char *delim = "?"; 138 char *delim = "?";
139 139
140 if (ctx.cfg.virtual_root) { 140 if (ctx.cfg.virtual_root) {
141 html_attr(ctx.cfg.virtual_root); 141 html_attr(ctx.cfg.virtual_root);
142 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 142 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
143 html("/"); 143 html("/");
144 } else 144 } else
145 html(ctx.cfg.script_name); 145 html(ctx.cfg.script_name);
146 146
147 if (page) { 147 if (page) {
148 htmlf("?p=%s", page); 148 htmlf("?p=%s", page);
149 delim = "&"; 149 delim = "&";
150 } 150 }
151 if (search) { 151 if (search) {
152 html(delim); 152 html(delim);
153 html("q="); 153 html("q=");
154 html_attr(search); 154 html_attr(search);
155 delim = "&"; 155 delim = "&";
156 } 156 }
157 if (ofs) { 157 if (ofs) {
158 html(delim); 158 html(delim);
159 htmlf("ofs=%d", ofs); 159 htmlf("ofs=%d", ofs);
160 } 160 }
161} 161}
162 162
163static void site_link(const char *page, const char *name, const char *title, 163static void site_link(const char *page, const char *name, const char *title,
164 const char *class, const char *search, int ofs) 164 const char *class, const char *search, int ofs)
165{ 165{
166 html("<a"); 166 html("<a");
167 if (title) { 167 if (title) {
168 html(" title='"); 168 html(" title='");
169 html_attr(title); 169 html_attr(title);
170 html("'"); 170 html("'");
171 } 171 }
172 if (class) { 172 if (class) {
173 html(" class='"); 173 html(" class='");
174 html_attr(class); 174 html_attr(class);
175 html("'"); 175 html("'");
176 } 176 }
177 html(" href='"); 177 html(" href='");
178 site_url(page, search, ofs); 178 site_url(page, search, ofs);
179 html("'>"); 179 html("'>");
180 html_txt(name); 180 html_txt(name);
181 html("</a>"); 181 html("</a>");
182} 182}
183 183
184void cgit_index_link(const char *name, const char *title, const char *class, 184void cgit_index_link(const char *name, const char *title, const char *class,
185 const char *pattern, int ofs) 185 const char *pattern, int ofs)
186{ 186{
187 site_link(NULL, name, title, class, pattern, ofs); 187 site_link(NULL, name, title, class, pattern, ofs);
188} 188}
189 189
190static char *repolink(const char *title, const char *class, const char *page, 190static char *repolink(const char *title, const char *class, const char *page,
191 const char *head, const char *path) 191 const char *head, const char *path)
192{ 192{
193 char *delim = "?"; 193 char *delim = "?";
194 194
195 html("<a"); 195 html("<a");
196 if (title) { 196 if (title) {
197 html(" title='"); 197 html(" title='");
198 html_attr(title); 198 html_attr(title);
199 html("'"); 199 html("'");
200 } 200 }
201 if (class) { 201 if (class) {
202 html(" class='"); 202 html(" class='");
203 html_attr(class); 203 html_attr(class);
204 html("'"); 204 html("'");
205 } 205 }
206 html(" href='"); 206 html(" href='");
207 if (ctx.cfg.virtual_root) { 207 if (ctx.cfg.virtual_root) {
208 html_url_path(ctx.cfg.virtual_root); 208 html_url_path(ctx.cfg.virtual_root);
209 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 209 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
210 html("/"); 210 html("/");
211 html_url_path(ctx.repo->url); 211 html_url_path(ctx.repo->url);
212 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 212 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
213 html("/"); 213 html("/");
214 if (page) { 214 if (page) {
215 html_url_path(page); 215 html_url_path(page);
216 html("/"); 216 html("/");
217 if (path) 217 if (path)
218 html_url_path(path); 218 html_url_path(path);
219 } 219 }
220 } else { 220 } else {
221 html(ctx.cfg.script_name); 221 html(ctx.cfg.script_name);
222 html("?url="); 222 html("?url=");
223 html_url_arg(ctx.repo->url); 223 html_url_arg(ctx.repo->url);
224 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 224 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
225 html("/"); 225 html("/");
226 if (page) { 226 if (page) {
227 html_url_arg(page); 227 html_url_arg(page);
228 html("/"); 228 html("/");
229 if (path) 229 if (path)
230 html_url_arg(path); 230 html_url_arg(path);
231 } 231 }
232 delim = "&amp;"; 232 delim = "&amp;";
233 } 233 }
234 if (head && strcmp(head, ctx.repo->defbranch)) { 234 if (head && strcmp(head, ctx.repo->defbranch)) {
235 html(delim); 235 html(delim);
236 html("h="); 236 html("h=");
237 html_url_arg(head); 237 html_url_arg(head);
238 delim = "&amp;"; 238 delim = "&amp;";
239 } 239 }
240 return fmt("%s", delim); 240 return fmt("%s", delim);
241} 241}
242 242
243static void reporevlink(const char *page, const char *name, const char *title, 243static void reporevlink(const char *page, const char *name, const char *title,
244 const char *class, const char *head, const char *rev, 244 const char *class, const char *head, const char *rev,
245 const char *path) 245 const char *path)
246{ 246{
247 char *delim; 247 char *delim;
248 248
249 delim = repolink(title, class, page, head, path); 249 delim = repolink(title, class, page, head, path);
250 if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) { 250 if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) {
251 html(delim); 251 html(delim);
252 html("id="); 252 html("id=");
253 html_url_arg(rev); 253 html_url_arg(rev);
254 } 254 }
255 html("'>"); 255 html("'>");
256 html_txt(name); 256 html_txt(name);
257 html("</a>"); 257 html("</a>");
258} 258}
259 259
260void cgit_summary_link(const char *name, const char *title, const char *class, 260void cgit_summary_link(const char *name, const char *title, const char *class,
261 const char *head) 261 const char *head)
262{ 262{
263 reporevlink(NULL, name, title, class, head, NULL, NULL); 263 reporevlink(NULL, name, title, class, head, NULL, NULL);
264} 264}
265 265
266void cgit_tag_link(const char *name, const char *title, const char *class, 266void cgit_tag_link(const char *name, const char *title, const char *class,
267 const char *head, const char *rev) 267 const char *head, const char *rev)
268{ 268{
269 reporevlink("tag", name, title, class, head, rev, NULL); 269 reporevlink("tag", name, title, class, head, rev, NULL);
270} 270}
271 271
272void cgit_tree_link(const char *name, const char *title, const char *class, 272void cgit_tree_link(const char *name, const char *title, const char *class,
273 const char *head, const char *rev, const char *path) 273 const char *head, const char *rev, const char *path)
274{ 274{
275 reporevlink("tree", name, title, class, head, rev, path); 275 reporevlink("tree", name, title, class, head, rev, path);
276} 276}
277 277
278void cgit_plain_link(const char *name, const char *title, const char *class, 278void cgit_plain_link(const char *name, const char *title, const char *class,
279 const char *head, const char *rev, const char *path) 279 const char *head, const char *rev, const char *path)
280{ 280{
281 reporevlink("plain", name, title, class, head, rev, path); 281 reporevlink("plain", name, title, class, head, rev, path);
282} 282}
283 283
284void cgit_log_link(const char *name, const char *title, const char *class, 284void cgit_log_link(const char *name, const char *title, const char *class,
285 const char *head, const char *rev, const char *path, 285 const char *head, const char *rev, const char *path,
286 int ofs, const char *grep, const char *pattern, int showmsg) 286 int ofs, const char *grep, const char *pattern, int showmsg)
287{ 287{
288 char *delim; 288 char *delim;
289 289
290 delim = repolink(title, class, "log", head, path); 290 delim = repolink(title, class, "log", head, path);
291 if (rev && strcmp(rev, ctx.qry.head)) { 291 if (rev && strcmp(rev, ctx.qry.head)) {
292 html(delim); 292 html(delim);
293 html("id="); 293 html("id=");
294 html_url_arg(rev); 294 html_url_arg(rev);
295 delim = "&"; 295 delim = "&";
296 } 296 }
297 if (grep && pattern) { 297 if (grep && pattern) {
298 html(delim); 298 html(delim);
299 html("qt="); 299 html("qt=");
300 html_url_arg(grep); 300 html_url_arg(grep);
301 delim = "&"; 301 delim = "&";
302 html(delim); 302 html(delim);
303 html("q="); 303 html("q=");
304 html_url_arg(pattern); 304 html_url_arg(pattern);
305 } 305 }
306 if (ofs > 0) { 306 if (ofs > 0) {
307 html(delim); 307 html(delim);
308 html("ofs="); 308 html("ofs=");
309 htmlf("%d", ofs); 309 htmlf("%d", ofs);
310 delim = "&"; 310 delim = "&";
311 } 311 }
312 if (showmsg) { 312 if (showmsg) {
313 html(delim); 313 html(delim);
314 html("showmsg=1"); 314 html("showmsg=1");
315 } 315 }
316 html("'>"); 316 html("'>");
317 html_txt(name); 317 html_txt(name);
318 html("</a>"); 318 html("</a>");
319} 319}
320 320
321void cgit_commit_link(char *name, const char *title, const char *class, 321void cgit_commit_link(char *name, const char *title, const char *class,
322 const char *head, const char *rev, int toggle_ssdiff) 322 const char *head, const char *rev, int toggle_ssdiff)
323{ 323{
324 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { 324 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
325 name[ctx.cfg.max_msg_len] = '\0'; 325 name[ctx.cfg.max_msg_len] = '\0';
326 name[ctx.cfg.max_msg_len - 1] = '.'; 326 name[ctx.cfg.max_msg_len - 1] = '.';
327 name[ctx.cfg.max_msg_len - 2] = '.'; 327 name[ctx.cfg.max_msg_len - 2] = '.';
328 name[ctx.cfg.max_msg_len - 3] = '.'; 328 name[ctx.cfg.max_msg_len - 3] = '.';
329 } 329 }
330 330
331 char *delim; 331 char *delim;
332 332
333 delim = repolink(title, class, "commit", head, NULL); 333 delim = repolink(title, class, "commit", head, NULL);
334 if (rev && strcmp(rev, ctx.qry.head)) { 334 if (rev && strcmp(rev, ctx.qry.head)) {
335 html(delim); 335 html(delim);
336 html("id="); 336 html("id=");
337 html_url_arg(rev); 337 html_url_arg(rev);
338 delim = "&amp;"; 338 delim = "&amp;";
339 } 339 }
340 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { 340 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
341 html(delim); 341 html(delim);
342 html("ss=1"); 342 html("ss=1");
343 } 343 }
344 html("'>"); 344 html("'>");
345 html_txt(name); 345 html_txt(name);
346 html("</a>"); 346 html("</a>");
347} 347}
348 348
349void cgit_refs_link(const char *name, const char *title, const char *class, 349void cgit_refs_link(const char *name, const char *title, const char *class,
350 const char *head, const char *rev, const char *path) 350 const char *head, const char *rev, const char *path)
351{ 351{
352 reporevlink("refs", name, title, class, head, rev, path); 352 reporevlink("refs", name, title, class, head, rev, path);
353} 353}
354 354
355void cgit_snapshot_link(const char *name, const char *title, const char *class, 355void cgit_snapshot_link(const char *name, const char *title, const char *class,
356 const char *head, const char *rev, 356 const char *head, const char *rev,
357 const char *archivename) 357 const char *archivename)
358{ 358{
359 reporevlink("snapshot", name, title, class, head, rev, archivename); 359 reporevlink("snapshot", name, title, class, head, rev, archivename);
360} 360}
361 361
362void cgit_diff_link(const char *name, const char *title, const char *class, 362void cgit_diff_link(const char *name, const char *title, const char *class,
363 const char *head, const char *new_rev, const char *old_rev, 363 const char *head, const char *new_rev, const char *old_rev,
364 const char *path, int toggle_ssdiff) 364 const char *path, int toggle_ssdiff)
365{ 365{
366 char *delim; 366 char *delim;
367 367
368 delim = repolink(title, class, "diff", head, path); 368 delim = repolink(title, class, "diff", head, path);
369 if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) { 369 if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) {
370 html(delim); 370 html(delim);
371 html("id="); 371 html("id=");
372 html_url_arg(new_rev); 372 html_url_arg(new_rev);
373 delim = "&amp;"; 373 delim = "&amp;";
374 } 374 }
375 if (old_rev) { 375 if (old_rev) {
376 html(delim); 376 html(delim);
377 html("id2="); 377 html("id2=");
378 html_url_arg(old_rev); 378 html_url_arg(old_rev);
379 delim = "&amp;"; 379 delim = "&amp;";
380 } 380 }
381 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { 381 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
382 html(delim); 382 html(delim);
383 html("ss=1"); 383 html("ss=1");
384 } 384 }
385 html("'>"); 385 html("'>");
386 html_txt(name); 386 html_txt(name);
387 html("</a>"); 387 html("</a>");
388} 388}
389 389
390void cgit_patch_link(const char *name, const char *title, const char *class, 390void cgit_patch_link(const char *name, const char *title, const char *class,
391 const char *head, const char *rev) 391 const char *head, const char *rev)
392{ 392{
393 reporevlink("patch", name, title, class, head, rev, NULL); 393 reporevlink("patch", name, title, class, head, rev, NULL);
394} 394}
395 395
396void cgit_stats_link(const char *name, const char *title, const char *class, 396void cgit_stats_link(const char *name, const char *title, const char *class,
397 const char *head, const char *path) 397 const char *head, const char *path)
398{ 398{
399 reporevlink("stats", name, title, class, head, NULL, path); 399 reporevlink("stats", name, title, class, head, NULL, path);
400} 400}
401 401
402void cgit_self_link(char *name, const char *title, const char *class,
403 struct cgit_context *ctx)
404{
405 if (!strcmp(ctx->qry.page, "repolist"))
406 return cgit_index_link(name, title, class, ctx->qry.search,
407 ctx->qry.ofs);
408 else if (!strcmp(ctx->qry.page, "summary"))
409 return cgit_summary_link(name, title, class, ctx->qry.head);
410 else if (!strcmp(ctx->qry.page, "tag"))
411 return cgit_tag_link(name, title, class, ctx->qry.head,
412 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL);
413 else if (!strcmp(ctx->qry.page, "tree"))
414 return cgit_tree_link(name, title, class, ctx->qry.head,
415 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
416 ctx->qry.path);
417 else if (!strcmp(ctx->qry.page, "plain"))
418 return cgit_plain_link(name, title, class, ctx->qry.head,
419 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
420 ctx->qry.path);
421 else if (!strcmp(ctx->qry.page, "log"))
422 return cgit_log_link(name, title, class, ctx->qry.head,
423 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
424 ctx->qry.path, ctx->qry.ofs,
425 ctx->qry.grep, ctx->qry.search,
426 ctx->qry.showmsg);
427 else if (!strcmp(ctx->qry.page, "commit"))
428 return cgit_commit_link(name, title, class, ctx->qry.head,
429 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
430 ctx->qry.path, 0);
431 else if (!strcmp(ctx->qry.page, "patch"))
432 return cgit_patch_link(name, title, class, ctx->qry.head,
433 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
434 ctx->qry.path);
435 else if (!strcmp(ctx->qry.page, "refs"))
436 return cgit_refs_link(name, title, class, ctx->qry.head,
437 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
438 ctx->qry.path);
439 else if (!strcmp(ctx->qry.page, "snapshot"))
440 return cgit_snapshot_link(name, title, class, ctx->qry.head,
441 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
442 ctx->qry.path);
443 else if (!strcmp(ctx->qry.page, "diff"))
444 return cgit_diff_link(name, title, class, ctx->qry.head,
445 ctx->qry.sha1, ctx->qry.sha2,
446 ctx->qry.path, 0);
447 else if (!strcmp(ctx->qry.page, "stats"))
448 return cgit_stats_link(name, title, class, ctx->qry.head,
449 ctx->qry.path);
450
451 /* Don't known how to make link for this page */
452 repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
453 html("><!-- cgit_self_link() doesn't know how to make link for page '");
454 html_txt(ctx->qry.page);
455 html("' -->");
456 html_txt(name);
457 html("</a>");
458}
459
402void cgit_object_link(struct object *obj) 460void cgit_object_link(struct object *obj)
403{ 461{
404 char *page, *shortrev, *fullrev, *name; 462 char *page, *shortrev, *fullrev, *name;
405 463
406 fullrev = sha1_to_hex(obj->sha1); 464 fullrev = sha1_to_hex(obj->sha1);
407 shortrev = xstrdup(fullrev); 465 shortrev = xstrdup(fullrev);
408 shortrev[10] = '\0'; 466 shortrev[10] = '\0';
409 if (obj->type == OBJ_COMMIT) { 467 if (obj->type == OBJ_COMMIT) {
410 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, 468 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
411 ctx.qry.head, fullrev, 0); 469 ctx.qry.head, fullrev, 0);
412 return; 470 return;
413 } else if (obj->type == OBJ_TREE) 471 } else if (obj->type == OBJ_TREE)
414 page = "tree"; 472 page = "tree";
415 else if (obj->type == OBJ_TAG) 473 else if (obj->type == OBJ_TAG)
416 page = "tag"; 474 page = "tag";
417 else 475 else
418 page = "blob"; 476 page = "blob";
419 name = fmt("%s %s...", typename(obj->type), shortrev); 477 name = fmt("%s %s...", typename(obj->type), shortrev);
420 reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL); 478 reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
421} 479}
422 480
423void cgit_print_date(time_t secs, const char *format, int local_time) 481void cgit_print_date(time_t secs, const char *format, int local_time)
424{ 482{
425 char buf[64]; 483 char buf[64];
426 struct tm *time; 484 struct tm *time;
427 485
428 if (!secs) 486 if (!secs)
429 return; 487 return;
430 if(local_time) 488 if(local_time)
431 time = localtime(&secs); 489 time = localtime(&secs);
432 else 490 else
433 time = gmtime(&secs); 491 time = gmtime(&secs);
434 strftime(buf, sizeof(buf)-1, format, time); 492 strftime(buf, sizeof(buf)-1, format, time);
435 html_txt(buf); 493 html_txt(buf);
436} 494}
437 495
438void cgit_print_age(time_t t, time_t max_relative, const char *format) 496void cgit_print_age(time_t t, time_t max_relative, const char *format)
439{ 497{
440 time_t now, secs; 498 time_t now, secs;
441 499
442 if (!t) 500 if (!t)
443 return; 501 return;
444 time(&now); 502 time(&now);
445 secs = now - t; 503 secs = now - t;
446 504
447 if (secs > max_relative && max_relative >= 0) { 505 if (secs > max_relative && max_relative >= 0) {
448 cgit_print_date(t, format, ctx.cfg.local_time); 506 cgit_print_date(t, format, ctx.cfg.local_time);
449 return; 507 return;
450 } 508 }
451 509
452 if (secs < TM_HOUR * 2) { 510 if (secs < TM_HOUR * 2) {
453 htmlf("<span class='age-mins'>%.0f min.</span>", 511 htmlf("<span class='age-mins'>%.0f min.</span>",
454 secs * 1.0 / TM_MIN); 512 secs * 1.0 / TM_MIN);
455 return; 513 return;
456 } 514 }
457 if (secs < TM_DAY * 2) { 515 if (secs < TM_DAY * 2) {
458 htmlf("<span class='age-hours'>%.0f hours</span>", 516 htmlf("<span class='age-hours'>%.0f hours</span>",
459 secs * 1.0 / TM_HOUR); 517 secs * 1.0 / TM_HOUR);
460 return; 518 return;
461 } 519 }
462 if (secs < TM_WEEK * 2) { 520 if (secs < TM_WEEK * 2) {
463 htmlf("<span class='age-days'>%.0f days</span>", 521 htmlf("<span class='age-days'>%.0f days</span>",
464 secs * 1.0 / TM_DAY); 522 secs * 1.0 / TM_DAY);
465 return; 523 return;
466 } 524 }
467 if (secs < TM_MONTH * 2) { 525 if (secs < TM_MONTH * 2) {
468 htmlf("<span class='age-weeks'>%.0f weeks</span>", 526 htmlf("<span class='age-weeks'>%.0f weeks</span>",
469 secs * 1.0 / TM_WEEK); 527 secs * 1.0 / TM_WEEK);
470 return; 528 return;
471 } 529 }
472 if (secs < TM_YEAR * 2) { 530 if (secs < TM_YEAR * 2) {
473 htmlf("<span class='age-months'>%.0f months</span>", 531 htmlf("<span class='age-months'>%.0f months</span>",
474 secs * 1.0 / TM_MONTH); 532 secs * 1.0 / TM_MONTH);
475 return; 533 return;
476 } 534 }
477 htmlf("<span class='age-years'>%.0f years</span>", 535 htmlf("<span class='age-years'>%.0f years</span>",
478 secs * 1.0 / TM_YEAR); 536 secs * 1.0 / TM_YEAR);
479} 537}
480 538
481void cgit_print_http_headers(struct cgit_context *ctx) 539void cgit_print_http_headers(struct cgit_context *ctx)
482{ 540{
483 if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1")) 541 if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1"))
484 return; 542 return;
485 543
486 if (ctx->page.status) 544 if (ctx->page.status)
487 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg); 545 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg);
488 if (ctx->page.mimetype && ctx->page.charset) 546 if (ctx->page.mimetype && ctx->page.charset)
489 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 547 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
490 ctx->page.charset); 548 ctx->page.charset);
491 else if (ctx->page.mimetype) 549 else if (ctx->page.mimetype)
492 htmlf("Content-Type: %s\n", ctx->page.mimetype); 550 htmlf("Content-Type: %s\n", ctx->page.mimetype);
493 if (ctx->page.size) 551 if (ctx->page.size)
494 htmlf("Content-Length: %ld\n", ctx->page.size); 552 htmlf("Content-Length: %ld\n", ctx->page.size);
495 if (ctx->page.filename) 553 if (ctx->page.filename)
496 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 554 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
497 ctx->page.filename); 555 ctx->page.filename);
498 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 556 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
499 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 557 htmlf("Expires: %s\n", http_date(ctx->page.expires));
500 if (ctx->page.etag) 558 if (ctx->page.etag)
501 htmlf("ETag: \"%s\"\n", ctx->page.etag); 559 htmlf("ETag: \"%s\"\n", ctx->page.etag);
502 html("\n"); 560 html("\n");
503 if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD")) 561 if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD"))
504 exit(0); 562 exit(0);
505} 563}
506 564
507void cgit_print_docstart(struct cgit_context *ctx) 565void cgit_print_docstart(struct cgit_context *ctx)
508{ 566{
509 if (ctx->cfg.embedded) { 567 if (ctx->cfg.embedded) {
510 if (ctx->cfg.header) 568 if (ctx->cfg.header)
511 html_include(ctx->cfg.header); 569 html_include(ctx->cfg.header);
512 return; 570 return;
513 } 571 }
514 572
515 char *host = cgit_hosturl(); 573 char *host = cgit_hosturl();
516 html(cgit_doctype); 574 html(cgit_doctype);
517 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 575 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
518 html("<head>\n"); 576 html("<head>\n");
519 html("<title>"); 577 html("<title>");
520 html_txt(ctx->page.title); 578 html_txt(ctx->page.title);
521 html("</title>\n"); 579 html("</title>\n");
522 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 580 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
523 if (ctx->cfg.robots && *ctx->cfg.robots) 581 if (ctx->cfg.robots && *ctx->cfg.robots)
524 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 582 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
525 html("<link rel='stylesheet' type='text/css' href='"); 583 html("<link rel='stylesheet' type='text/css' href='");
526 html_attr(ctx->cfg.css); 584 html_attr(ctx->cfg.css);
527 html("'/>\n"); 585 html("'/>\n");
528 if (ctx->cfg.favicon) { 586 if (ctx->cfg.favicon) {
529 html("<link rel='shortcut icon' href='"); 587 html("<link rel='shortcut icon' href='");
530 html_attr(ctx->cfg.favicon); 588 html_attr(ctx->cfg.favicon);
531 html("'/>\n"); 589 html("'/>\n");
532 } 590 }
533 if (host && ctx->repo) { 591 if (host && ctx->repo) {
534 html("<link rel='alternate' title='Atom feed' href='"); 592 html("<link rel='alternate' title='Atom feed' href='");
535 html(cgit_httpscheme()); 593 html(cgit_httpscheme());
536 html_attr(cgit_hosturl()); 594 html_attr(cgit_hosturl());
537 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, 595 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
538 fmt("h=%s", ctx->qry.head))); 596 fmt("h=%s", ctx->qry.head)));
539 html("' type='application/atom+xml'/>\n"); 597 html("' type='application/atom+xml'/>\n");
540 } 598 }
541 if (ctx->cfg.head_include) 599 if (ctx->cfg.head_include)
542 html_include(ctx->cfg.head_include); 600 html_include(ctx->cfg.head_include);
543 html("</head>\n"); 601 html("</head>\n");
544 html("<body>\n"); 602 html("<body>\n");
545 if (ctx->cfg.header) 603 if (ctx->cfg.header)
546 html_include(ctx->cfg.header); 604 html_include(ctx->cfg.header);
547} 605}
548 606
549void cgit_print_docend() 607void cgit_print_docend()
550{ 608{
551 html("</div> <!-- class=content -->\n"); 609 html("</div> <!-- class=content -->\n");
552 if (ctx.cfg.embedded) { 610 if (ctx.cfg.embedded) {
553 html("</div> <!-- id=cgit -->\n"); 611 html("</div> <!-- id=cgit -->\n");
554 if (ctx.cfg.footer) 612 if (ctx.cfg.footer)
555 html_include(ctx.cfg.footer); 613 html_include(ctx.cfg.footer);
556 return; 614 return;
557 } 615 }
558 if (ctx.cfg.footer) 616 if (ctx.cfg.footer)
559 html_include(ctx.cfg.footer); 617 html_include(ctx.cfg.footer);
560 else { 618 else {
561 htmlf("<div class='footer'>generated by cgit %s at ", 619 htmlf("<div class='footer'>generated by cgit %s at ",
562 cgit_version); 620 cgit_version);
563 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 621 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
564 html("</div>\n"); 622 html("</div>\n");
565 } 623 }
566 html("</div> <!-- id=cgit -->\n"); 624 html("</div> <!-- id=cgit -->\n");
567 html("</body>\n</html>\n"); 625 html("</body>\n</html>\n");
568} 626}
569 627
570int print_branch_option(const char *refname, const unsigned char *sha1, 628int print_branch_option(const char *refname, const unsigned char *sha1,
571 int flags, void *cb_data) 629 int flags, void *cb_data)
572{ 630{
573 char *name = (char *)refname; 631 char *name = (char *)refname;
574 html_option(name, name, ctx.qry.head); 632 html_option(name, name, ctx.qry.head);
575 return 0; 633 return 0;
576} 634}
577 635
578int print_archive_ref(const char *refname, const unsigned char *sha1, 636int print_archive_ref(const char *refname, const unsigned char *sha1,
579 int flags, void *cb_data) 637 int flags, void *cb_data)
580{ 638{
581 struct tag *tag; 639 struct tag *tag;
582 struct taginfo *info; 640 struct taginfo *info;
583 struct object *obj; 641 struct object *obj;
584 char buf[256], *url; 642 char buf[256], *url;
585 unsigned char fileid[20]; 643 unsigned char fileid[20];
586 int *header = (int *)cb_data; 644 int *header = (int *)cb_data;
587 645
588 if (prefixcmp(refname, "refs/archives")) 646 if (prefixcmp(refname, "refs/archives"))
589 return 0; 647 return 0;
590 strncpy(buf, refname+14, sizeof(buf)); 648 strncpy(buf, refname+14, sizeof(buf));
591 obj = parse_object(sha1); 649 obj = parse_object(sha1);
592 if (!obj) 650 if (!obj)
593 return 1; 651 return 1;
594 if (obj->type == OBJ_TAG) { 652 if (obj->type == OBJ_TAG) {
595 tag = lookup_tag(sha1); 653 tag = lookup_tag(sha1);
596 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 654 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
597 return 0; 655 return 0;
598 hashcpy(fileid, tag->tagged->sha1); 656 hashcpy(fileid, tag->tagged->sha1);
599 } else if (obj->type != OBJ_BLOB) { 657 } else if (obj->type != OBJ_BLOB) {
600 return 0; 658 return 0;
601 } else { 659 } else {
602 hashcpy(fileid, sha1); 660 hashcpy(fileid, sha1);
603 } 661 }
604 if (!*header) { 662 if (!*header) {
605 html("<h1>download</h1>\n"); 663 html("<h1>download</h1>\n");
606 *header = 1; 664 *header = 1;
607 } 665 }
608 url = cgit_pageurl(ctx.qry.repo, "blob", 666 url = cgit_pageurl(ctx.qry.repo, "blob",
609 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 667 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
610 buf)); 668 buf));
611 html_link_open(url, NULL, "menu"); 669 html_link_open(url, NULL, "menu");
612 html_txt(strlpart(buf, 20)); 670 html_txt(strlpart(buf, 20));
613 html_link_close(); 671 html_link_close();
614 return 0; 672 return 0;
615} 673}
616 674
617void cgit_add_hidden_formfields(int incl_head, int incl_search, 675void cgit_add_hidden_formfields(int incl_head, int incl_search,
618 const char *page) 676 const char *page)
619{ 677{
620 char *url; 678 char *url;
621 679
622 if (!ctx.cfg.virtual_root) { 680 if (!ctx.cfg.virtual_root) {
623 url = fmt("%s/%s", ctx.qry.repo, page); 681 url = fmt("%s/%s", ctx.qry.repo, page);
624 if (ctx.qry.vpath) 682 if (ctx.qry.vpath)
625 url = fmt("%s/%s", url, ctx.qry.vpath); 683 url = fmt("%s/%s", url, ctx.qry.vpath);
626 html_hidden("url", url); 684 html_hidden("url", url);
627 } 685 }
628 686
629 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 687 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
630 strcmp(ctx.qry.head, ctx.repo->defbranch)) 688 strcmp(ctx.qry.head, ctx.repo->defbranch))
631 html_hidden("h", ctx.qry.head); 689 html_hidden("h", ctx.qry.head);
632 690
633 if (ctx.qry.sha1) 691 if (ctx.qry.sha1)
634 html_hidden("id", ctx.qry.sha1); 692 html_hidden("id", ctx.qry.sha1);
635 if (ctx.qry.sha2) 693 if (ctx.qry.sha2)
636 html_hidden("id2", ctx.qry.sha2); 694 html_hidden("id2", ctx.qry.sha2);
637 if (ctx.qry.showmsg) 695 if (ctx.qry.showmsg)
638 html_hidden("showmsg", "1"); 696 html_hidden("showmsg", "1");
639 697
640 if (incl_search) { 698 if (incl_search) {
641 if (ctx.qry.grep) 699 if (ctx.qry.grep)
642 html_hidden("qt", ctx.qry.grep); 700 html_hidden("qt", ctx.qry.grep);
643 if (ctx.qry.search) 701 if (ctx.qry.search)
644 html_hidden("q", ctx.qry.search); 702 html_hidden("q", ctx.qry.search);
645 } 703 }
646} 704}
647 705
648static const char *hc(struct cgit_context *ctx, const char *page) 706static const char *hc(struct cgit_context *ctx, const char *page)
649{ 707{
650 return strcmp(ctx->qry.page, page) ? NULL : "active"; 708 return strcmp(ctx->qry.page, page) ? NULL : "active";
651} 709}
652 710
711static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path)
712{
713 char *old_path = ctx->qry.path;
714 char *p = path, *q, *end = path + strlen(path);
715
716 ctx->qry.path = NULL;
717 cgit_self_link("root", NULL, NULL, ctx);
718 ctx->qry.path = p = path;
719 while (p < end) {
720 if (!(q = strchr(p, '/')))
721 q = end;
722 *q = '\0';
723 html_txt("/");
724 cgit_self_link(p, NULL, NULL, ctx);
725 if (q < end)
726 *q = '/';
727 p = q + 1;
728 }
729 ctx->qry.path = old_path;
730}
731
653static void print_header(struct cgit_context *ctx) 732static void print_header(struct cgit_context *ctx)
654{ 733{
655 html("<table id='header'>\n"); 734 html("<table id='header'>\n");
656 html("<tr>\n"); 735 html("<tr>\n");
657 736
658 if (ctx->cfg.logo && ctx->cfg.logo[0] != 0) { 737 if (ctx->cfg.logo && ctx->cfg.logo[0] != 0) {
659 html("<td class='logo' rowspan='2'><a href='"); 738 html("<td class='logo' rowspan='2'><a href='");
660 if (ctx->cfg.logo_link) 739 if (ctx->cfg.logo_link)
661 html_attr(ctx->cfg.logo_link); 740 html_attr(ctx->cfg.logo_link);
662 else 741 else
663 html_attr(cgit_rooturl()); 742 html_attr(cgit_rooturl());
664 html("'><img src='"); 743 html("'><img src='");
665 html_attr(ctx->cfg.logo); 744 html_attr(ctx->cfg.logo);
666 html("' alt='cgit logo'/></a></td>\n"); 745 html("' alt='cgit logo'/></a></td>\n");
667 } 746 }
668 747
669 html("<td class='main'>"); 748 html("<td class='main'>");
670 if (ctx->repo) { 749 if (ctx->repo) {
671 cgit_index_link("index", NULL, NULL, NULL, 0); 750 cgit_index_link("index", NULL, NULL, NULL, 0);
672 html(" : "); 751 html(" : ");
673 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL); 752 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
674 html("</td><td class='form'>"); 753 html("</td><td class='form'>");
675 html("<form method='get' action=''>\n"); 754 html("<form method='get' action=''>\n");
676 cgit_add_hidden_formfields(0, 1, ctx->qry.page); 755 cgit_add_hidden_formfields(0, 1, ctx->qry.page);
677 html("<select name='h' onchange='this.form.submit();'>\n"); 756 html("<select name='h' onchange='this.form.submit();'>\n");
678 for_each_branch_ref(print_branch_option, ctx->qry.head); 757 for_each_branch_ref(print_branch_option, ctx->qry.head);
679 html("</select> "); 758 html("</select> ");
680 html("<input type='submit' name='' value='switch'/>"); 759 html("<input type='submit' name='' value='switch'/>");
681 html("</form>"); 760 html("</form>");
682 } else 761 } else
683 html_txt(ctx->cfg.root_title); 762 html_txt(ctx->cfg.root_title);
684 html("</td></tr>\n"); 763 html("</td></tr>\n");
685 764
686 html("<tr><td class='sub'>"); 765 html("<tr><td class='sub'>");
687 if (ctx->repo) { 766 if (ctx->repo) {
688 html_txt(ctx->repo->desc); 767 html_txt(ctx->repo->desc);
689 html("</td><td class='sub right'>"); 768 html("</td><td class='sub right'>");
690 html_txt(ctx->repo->owner); 769 html_txt(ctx->repo->owner);
691 } else { 770 } else {
692 if (ctx->cfg.root_desc) 771 if (ctx->cfg.root_desc)
693 html_txt(ctx->cfg.root_desc); 772 html_txt(ctx->cfg.root_desc);
694 else if (ctx->cfg.index_info) 773 else if (ctx->cfg.index_info)
695 html_include(ctx->cfg.index_info); 774 html_include(ctx->cfg.index_info);
696 } 775 }
697 html("</td></tr></table>\n"); 776 html("</td></tr></table>\n");
698} 777}
699 778
700void cgit_print_pageheader(struct cgit_context *ctx) 779void cgit_print_pageheader(struct cgit_context *ctx)
701{ 780{
702 html("<div id='cgit'>"); 781 html("<div id='cgit'>");
703 if (!ctx->cfg.noheader) 782 if (!ctx->cfg.noheader)
704 print_header(ctx); 783 print_header(ctx);
705 784
706 html("<table class='tabs'><tr><td>\n"); 785 html("<table class='tabs'><tr><td>\n");
707 if (ctx->repo) { 786 if (ctx->repo) {
708 cgit_summary_link("summary", NULL, hc(ctx, "summary"), 787 cgit_summary_link("summary", NULL, hc(ctx, "summary"),
709 ctx->qry.head); 788 ctx->qry.head);
710 cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head, 789 cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head,
711 ctx->qry.sha1, NULL); 790 ctx->qry.sha1, NULL);
712 cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head, 791 cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head,
713 NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg); 792 NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg);
714 cgit_tree_link("tree", NULL, hc(ctx, "tree"), ctx->qry.head, 793 cgit_tree_link("tree", NULL, hc(ctx, "tree"), ctx->qry.head,
715 ctx->qry.sha1, NULL); 794 ctx->qry.sha1, NULL);
716 cgit_commit_link("commit", NULL, hc(ctx, "commit"), 795 cgit_commit_link("commit", NULL, hc(ctx, "commit"),
717 ctx->qry.head, ctx->qry.sha1, 0); 796 ctx->qry.head, ctx->qry.sha1, 0);
718 cgit_diff_link("diff", NULL, hc(ctx, "diff"), ctx->qry.head, 797 cgit_diff_link("diff", NULL, hc(ctx, "diff"), ctx->qry.head,
719 ctx->qry.sha1, ctx->qry.sha2, NULL, 0); 798 ctx->qry.sha1, ctx->qry.sha2, NULL, 0);
720 if (ctx->repo->max_stats) 799 if (ctx->repo->max_stats)
721 cgit_stats_link("stats", NULL, hc(ctx, "stats"), 800 cgit_stats_link("stats", NULL, hc(ctx, "stats"),
722 ctx->qry.head, NULL); 801 ctx->qry.head, NULL);
723 if (ctx->repo->readme) 802 if (ctx->repo->readme)
724 reporevlink("about", "about", NULL, 803 reporevlink("about", "about", NULL,
725 hc(ctx, "about"), ctx->qry.head, NULL, 804 hc(ctx, "about"), ctx->qry.head, NULL,
726 NULL); 805 NULL);
727 html("</td><td class='form'>"); 806 html("</td><td class='form'>");
728 html("<form class='right' method='get' action='"); 807 html("<form class='right' method='get' action='");
729 if (ctx->cfg.virtual_root) 808 if (ctx->cfg.virtual_root)
730 html_url_path(cgit_fileurl(ctx->qry.repo, "log", 809 html_url_path(cgit_fileurl(ctx->qry.repo, "log",
731 ctx->qry.vpath, NULL)); 810 ctx->qry.vpath, NULL));
732 html("'>\n"); 811 html("'>\n");
733 cgit_add_hidden_formfields(1, 0, "log"); 812 cgit_add_hidden_formfields(1, 0, "log");
734 html("<select name='qt'>\n"); 813 html("<select name='qt'>\n");
735 html_option("grep", "log msg", ctx->qry.grep); 814 html_option("grep", "log msg", ctx->qry.grep);
736 html_option("author", "author", ctx->qry.grep); 815 html_option("author", "author", ctx->qry.grep);
737 html_option("committer", "committer", ctx->qry.grep); 816 html_option("committer", "committer", ctx->qry.grep);
738 html("</select>\n"); 817 html("</select>\n");
739 html("<input class='txt' type='text' size='10' name='q' value='"); 818 html("<input class='txt' type='text' size='10' name='q' value='");
740 html_attr(ctx->qry.search); 819 html_attr(ctx->qry.search);
741 html("'/>\n"); 820 html("'/>\n");
742 html("<input type='submit' value='search'/>\n"); 821 html("<input type='submit' value='search'/>\n");
743 html("</form>\n"); 822 html("</form>\n");
744 } else { 823 } else {
745 site_link(NULL, "index", NULL, hc(ctx, "repolist"), NULL, 0); 824 site_link(NULL, "index", NULL, hc(ctx, "repolist"), NULL, 0);
746 if (ctx->cfg.root_readme) 825 if (ctx->cfg.root_readme)
747 site_link("about", "about", NULL, hc(ctx, "about"), 826 site_link("about", "about", NULL, hc(ctx, "about"),
748 NULL, 0); 827 NULL, 0);
749 html("</td><td class='form'>"); 828 html("</td><td class='form'>");
750 html("<form method='get' action='"); 829 html("<form method='get' action='");
751 html_attr(cgit_rooturl()); 830 html_attr(cgit_rooturl());
752 html("'>\n"); 831 html("'>\n");
753 html("<input type='text' name='q' size='10' value='"); 832 html("<input type='text' name='q' size='10' value='");
754 html_attr(ctx->qry.search); 833 html_attr(ctx->qry.search);
755 html("'/>\n"); 834 html("'/>\n");
756 html("<input type='submit' value='search'/>\n"); 835 html("<input type='submit' value='search'/>\n");
757 html("</form>"); 836 html("</form>");
758 } 837 }
759 html("</td></tr></table>\n"); 838 html("</td></tr></table>\n");
760 if (ctx->qry.vpath) { 839 if (ctx->qry.vpath) {
761 html("<div class='path'>"); 840 html("<div class='path'>");
762 html("path: "); 841 html("path: ");
763 html_txt(ctx->qry.vpath); 842 cgit_print_path_crumbs(ctx, ctx->qry.vpath);
764 html("</div>"); 843 html("</div>");
765 } 844 }
766 html("<div class='content'>"); 845 html("<div class='content'>");
767} 846}
768 847
769void cgit_print_filemode(unsigned short mode) 848void cgit_print_filemode(unsigned short mode)
770{ 849{
771 if (S_ISDIR(mode)) 850 if (S_ISDIR(mode))
772 html("d"); 851 html("d");
773 else if (S_ISLNK(mode)) 852 else if (S_ISLNK(mode))
774 html("l"); 853 html("l");
775 else if (S_ISGITLINK(mode)) 854 else if (S_ISGITLINK(mode))
776 html("m"); 855 html("m");
777 else 856 else
778 html("-"); 857 html("-");
779 html_fileperm(mode >> 6); 858 html_fileperm(mode >> 6);
780 html_fileperm(mode >> 3); 859 html_fileperm(mode >> 3);
781 html_fileperm(mode); 860 html_fileperm(mode);
782} 861}
783 862
784void cgit_print_snapshot_links(const char *repo, const char *head, 863void cgit_print_snapshot_links(const char *repo, const char *head,
785 const char *hex, int snapshots) 864 const char *hex, int snapshots)
786{ 865{
787 const struct cgit_snapshot_format* f; 866 const struct cgit_snapshot_format* f;
788 char *prefix; 867 char *prefix;
789 char *filename; 868 char *filename;
790 unsigned char sha1[20]; 869 unsigned char sha1[20];
791 870
792 if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 && 871 if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 &&
793 (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1])) 872 (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1]))
794 hex++; 873 hex++;
795 prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex)); 874 prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex));
796 for (f = cgit_snapshot_formats; f->suffix; f++) { 875 for (f = cgit_snapshot_formats; f->suffix; f++) {
797 if (!(snapshots & f->bit)) 876 if (!(snapshots & f->bit))
798 continue; 877 continue;
799 filename = fmt("%s%s", prefix, f->suffix); 878 filename = fmt("%s%s", prefix, f->suffix);
800 cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename); 879 cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename);
801 html("<br/>"); 880 html("<br/>");
802 } 881 }
803} 882}
diff --git a/ui-shared.h b/ui-shared.h
index 308c982..3df5464 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,63 +1,65 @@
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_httpscheme(); 4extern char *cgit_httpscheme();
5extern char *cgit_hosturl(); 5extern char *cgit_hosturl();
6extern char *cgit_rooturl(); 6extern char *cgit_rooturl();
7extern char *cgit_repourl(const char *reponame); 7extern char *cgit_repourl(const char *reponame);
8extern char *cgit_fileurl(const char *reponame, const char *pagename, 8extern char *cgit_fileurl(const char *reponame, const char *pagename,
9 const char *filename, const char *query); 9 const char *filename, const char *query);
10extern char *cgit_pageurl(const char *reponame, const char *pagename, 10extern char *cgit_pageurl(const char *reponame, const char *pagename,
11 const char *query); 11 const char *query);
12 12
13extern void cgit_index_link(const char *name, const char *title, 13extern void cgit_index_link(const char *name, const char *title,
14 const char *class, const char *pattern, int ofs); 14 const char *class, const char *pattern, int ofs);
15extern void cgit_summary_link(const char *name, const char *title, 15extern void cgit_summary_link(const char *name, const char *title,
16 const char *class, const char *head); 16 const char *class, const char *head);
17extern void cgit_tag_link(const char *name, const char *title, 17extern void cgit_tag_link(const char *name, const char *title,
18 const char *class, const char *head, 18 const char *class, const char *head,
19 const char *rev); 19 const char *rev);
20extern void cgit_tree_link(const char *name, const char *title, 20extern void cgit_tree_link(const char *name, const char *title,
21 const char *class, const char *head, 21 const char *class, const char *head,
22 const char *rev, const char *path); 22 const char *rev, const char *path);
23extern void cgit_plain_link(const char *name, const char *title, 23extern void cgit_plain_link(const char *name, const char *title,
24 const char *class, const char *head, 24 const char *class, const char *head,
25 const char *rev, const char *path); 25 const char *rev, const char *path);
26extern void cgit_log_link(const char *name, const char *title, 26extern void cgit_log_link(const char *name, const char *title,
27 const char *class, const char *head, const char *rev, 27 const char *class, const char *head, const char *rev,
28 const char *path, int ofs, const char *grep, 28 const char *path, int ofs, const char *grep,
29 const char *pattern, int showmsg); 29 const char *pattern, int showmsg);
30extern void cgit_commit_link(char *name, const char *title, 30extern void cgit_commit_link(char *name, const char *title,
31 const char *class, const char *head, 31 const char *class, const char *head,
32 const char *rev, int toggle_ssdiff); 32 const char *rev, int toggle_ssdiff);
33extern void cgit_patch_link(const char *name, const char *title, 33extern void cgit_patch_link(const char *name, const char *title,
34 const char *class, const char *head, 34 const char *class, const char *head,
35 const char *rev); 35 const char *rev);
36extern void cgit_refs_link(const char *name, const char *title, 36extern void cgit_refs_link(const char *name, const char *title,
37 const char *class, const char *head, 37 const char *class, const char *head,
38 const char *rev, const char *path); 38 const char *rev, const char *path);
39extern void cgit_snapshot_link(const char *name, const char *title, 39extern void cgit_snapshot_link(const char *name, const char *title,
40 const char *class, const char *head, 40 const char *class, const char *head,
41 const char *rev, const char *archivename); 41 const char *rev, const char *archivename);
42extern void cgit_diff_link(const char *name, const char *title, 42extern void cgit_diff_link(const char *name, const char *title,
43 const char *class, const char *head, 43 const char *class, const char *head,
44 const char *new_rev, const char *old_rev, 44 const char *new_rev, const char *old_rev,
45 const char *path, int toggle_ssdiff); 45 const char *path, int toggle_ssdiff);
46extern void cgit_stats_link(const char *name, const char *title, 46extern void cgit_stats_link(const char *name, const char *title,
47 const char *class, const char *head, 47 const char *class, const char *head,
48 const char *path); 48 const char *path);
49extern void cgit_self_link(char *name, const char *title,
50 const char *class, struct cgit_context *ctx);
49extern void cgit_object_link(struct object *obj); 51extern void cgit_object_link(struct object *obj);
50 52
51extern void cgit_print_error(const char *msg); 53extern void cgit_print_error(const char *msg);
52extern void cgit_print_date(time_t secs, const char *format, int local_time); 54extern void cgit_print_date(time_t secs, const char *format, int local_time);
53extern void cgit_print_age(time_t t, time_t max_relative, const char *format); 55extern void cgit_print_age(time_t t, time_t max_relative, const char *format);
54extern void cgit_print_http_headers(struct cgit_context *ctx); 56extern void cgit_print_http_headers(struct cgit_context *ctx);
55extern void cgit_print_docstart(struct cgit_context *ctx); 57extern void cgit_print_docstart(struct cgit_context *ctx);
56extern void cgit_print_docend(); 58extern void cgit_print_docend();
57extern void cgit_print_pageheader(struct cgit_context *ctx); 59extern void cgit_print_pageheader(struct cgit_context *ctx);
58extern void cgit_print_filemode(unsigned short mode); 60extern void cgit_print_filemode(unsigned short mode);
59extern void cgit_print_snapshot_links(const char *repo, const char *head, 61extern void cgit_print_snapshot_links(const char *repo, const char *head,
60 const char *hex, int snapshots); 62 const char *hex, int snapshots);
61extern void cgit_add_hidden_formfields(int incl_head, int incl_search, 63extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
62 const char *page); 64 const char *page);
63#endif /* UI_SHARED_H */ 65#endif /* UI_SHARED_H */