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