summaryrefslogtreecommitdiffabout
path: root/ui-shared.c
Unidiff
Diffstat (limited to 'ui-shared.c') (more/less context) (ignore whitespace changes)
-rw-r--r--ui-shared.c172
1 files changed, 122 insertions, 50 deletions
diff --git a/ui-shared.c b/ui-shared.c
index 1418010..1d66940 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -173,275 +173,347 @@ static char *repolink(char *title, char *class, char *page, char *head,
173 173
174static void reporevlink(char *page, char *name, char *title, char *class, 174static void reporevlink(char *page, char *name, char *title, char *class,
175 char *head, char *rev, char *path) 175 char *head, char *rev, char *path)
176{ 176{
177 char *delim; 177 char *delim;
178 178
179 delim = repolink(title, class, page, head, path); 179 delim = repolink(title, class, page, head, path);
180 if (rev && strcmp(rev, cgit_query_head)) { 180 if (rev && strcmp(rev, cgit_query_head)) {
181 html(delim); 181 html(delim);
182 html("id="); 182 html("id=");
183 html_attr(rev); 183 html_attr(rev);
184 } 184 }
185 html("'>"); 185 html("'>");
186 html_txt(name); 186 html_txt(name);
187 html("</a>"); 187 html("</a>");
188} 188}
189 189
190void cgit_tree_link(char *name, char *title, char *class, char *head, 190void cgit_tree_link(char *name, char *title, char *class, char *head,
191 char *rev, char *path) 191 char *rev, char *path)
192{ 192{
193 reporevlink("tree", name, title, class, head, rev, path); 193 reporevlink("tree", name, title, class, head, rev, path);
194} 194}
195 195
196void cgit_log_link(char *name, char *title, char *class, char *head, 196void cgit_log_link(char *name, char *title, char *class, char *head,
197 char *rev, char *path, int ofs) 197 char *rev, char *path, int ofs)
198{ 198{
199 char *delim; 199 char *delim;
200 200
201 delim = repolink(title, class, "log", head, path); 201 delim = repolink(title, class, "log", head, path);
202 if (rev && strcmp(rev, cgit_query_head)) { 202 if (rev && strcmp(rev, cgit_query_head)) {
203 html(delim); 203 html(delim);
204 html("id="); 204 html("id=");
205 html_attr(rev); 205 html_attr(rev);
206 delim = "&"; 206 delim = "&";
207 } 207 }
208 if (ofs > 0) { 208 if (ofs > 0) {
209 html(delim); 209 html(delim);
210 html("ofs="); 210 html("ofs=");
211 htmlf("%d", ofs); 211 htmlf("%d", ofs);
212 } 212 }
213 html("'>"); 213 html("'>");
214 html_txt(name); 214 html_txt(name);
215 html("</a>"); 215 html("</a>");
216} 216}
217 217
218void cgit_commit_link(char *name, char *title, char *class, char *head, 218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev) 219 char *rev)
220{ 220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0'; 222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.'; 223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.'; 224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.'; 225 name[cgit_max_msg_len - 3] = '.';
226 } 226 }
227 reporevlink("commit", name, title, class, head, rev, NULL); 227 reporevlink("commit", name, title, class, head, rev, NULL);
228} 228}
229 229
230void cgit_refs_link(char *name, char *title, char *class, char *head, 230void cgit_refs_link(char *name, char *title, char *class, char *head,
231 char *rev, char *path) 231 char *rev, char *path)
232{ 232{
233 reporevlink("refs", name, title, class, head, rev, path); 233 reporevlink("refs", name, title, class, head, rev, path);
234} 234}
235 235
236void cgit_snapshot_link(char *name, char *title, char *class, char *head, 236void cgit_snapshot_link(char *name, char *title, char *class, char *head,
237 char *rev, char *archivename) 237 char *rev, char *archivename)
238{ 238{
239 reporevlink("snapshot", name, title, class, head, rev, archivename); 239 reporevlink("snapshot", name, title, class, head, rev, archivename);
240} 240}
241 241
242void cgit_diff_link(char *name, char *title, char *class, char *head, 242void cgit_diff_link(char *name, char *title, char *class, char *head,
243 char *new_rev, char *old_rev, char *path) 243 char *new_rev, char *old_rev, char *path)
244{ 244{
245 char *delim; 245 char *delim;
246 246
247 delim = repolink(title, class, "diff", head, path); 247 delim = repolink(title, class, "diff", head, path);
248 if (new_rev && strcmp(new_rev, cgit_query_head)) { 248 if (new_rev && strcmp(new_rev, cgit_query_head)) {
249 html(delim); 249 html(delim);
250 html("id="); 250 html("id=");
251 html_attr(new_rev); 251 html_attr(new_rev);
252 delim = "&amp;"; 252 delim = "&amp;";
253 } 253 }
254 if (old_rev) { 254 if (old_rev) {
255 html(delim); 255 html(delim);
256 html("id2="); 256 html("id2=");
257 html_attr(old_rev); 257 html_attr(old_rev);
258 } 258 }
259 html("'>"); 259 html("'>");
260 html_txt(name); 260 html_txt(name);
261 html("</a>"); 261 html("</a>");
262} 262}
263 263
264void cgit_object_link(struct object *obj) 264void cgit_object_link(struct object *obj)
265{ 265{
266 char *page, *arg, *url; 266 char *page, *arg, *url;
267 267
268 if (obj->type == OBJ_COMMIT) { 268 if (obj->type == OBJ_COMMIT) {
269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
270 cgit_query_head, sha1_to_hex(obj->sha1)); 270 cgit_query_head, sha1_to_hex(obj->sha1));
271 return; 271 return;
272 } else if (obj->type == OBJ_TREE) { 272 } else if (obj->type == OBJ_TREE) {
273 page = "tree"; 273 page = "tree";
274 arg = "id"; 274 arg = "id";
275 } else if (obj->type == OBJ_TAG) { 275 } else if (obj->type == OBJ_TAG) {
276 page = "tag"; 276 page = "tag";
277 arg = "id"; 277 arg = "id";
278 } else { 278 } else {
279 page = "blob"; 279 page = "blob";
280 arg = "id"; 280 arg = "id";
281 } 281 }
282 282
283 url = cgit_pageurl(cgit_query_repo, page, 283 url = cgit_pageurl(cgit_query_repo, page,
284 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 284 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
285 html_link_open(url, NULL, NULL); 285 html_link_open(url, NULL, NULL);
286 htmlf("%s %s", typename(obj->type), 286 htmlf("%s %s", typename(obj->type),
287 sha1_to_hex(obj->sha1)); 287 sha1_to_hex(obj->sha1));
288 html_link_close(); 288 html_link_close();
289} 289}
290 290
291void cgit_print_date(time_t secs, char *format) 291void cgit_print_date(time_t secs, char *format)
292{ 292{
293 char buf[64]; 293 char buf[64];
294 struct tm *time; 294 struct tm *time;
295 295
296 time = gmtime(&secs); 296 time = gmtime(&secs);
297 strftime(buf, sizeof(buf)-1, format, time); 297 strftime(buf, sizeof(buf)-1, format, time);
298 html_txt(buf); 298 html_txt(buf);
299} 299}
300 300
301void cgit_print_age(time_t t, time_t max_relative, char *format) 301void cgit_print_age(time_t t, time_t max_relative, char *format)
302{ 302{
303 time_t now, secs; 303 time_t now, secs;
304 304
305 time(&now); 305 time(&now);
306 secs = now - t; 306 secs = now - t;
307 307
308 if (secs > max_relative && max_relative >= 0) { 308 if (secs > max_relative && max_relative >= 0) {
309 cgit_print_date(t, format); 309 cgit_print_date(t, format);
310 return; 310 return;
311 } 311 }
312 312
313 if (secs < TM_HOUR * 2) { 313 if (secs < TM_HOUR * 2) {
314 htmlf("<span class='age-mins'>%.0f min.</span>", 314 htmlf("<span class='age-mins'>%.0f min.</span>",
315 secs * 1.0 / TM_MIN); 315 secs * 1.0 / TM_MIN);
316 return; 316 return;
317 } 317 }
318 if (secs < TM_DAY * 2) { 318 if (secs < TM_DAY * 2) {
319 htmlf("<span class='age-hours'>%.0f hours</span>", 319 htmlf("<span class='age-hours'>%.0f hours</span>",
320 secs * 1.0 / TM_HOUR); 320 secs * 1.0 / TM_HOUR);
321 return; 321 return;
322 } 322 }
323 if (secs < TM_WEEK * 2) { 323 if (secs < TM_WEEK * 2) {
324 htmlf("<span class='age-days'>%.0f days</span>", 324 htmlf("<span class='age-days'>%.0f days</span>",
325 secs * 1.0 / TM_DAY); 325 secs * 1.0 / TM_DAY);
326 return; 326 return;
327 } 327 }
328 if (secs < TM_MONTH * 2) { 328 if (secs < TM_MONTH * 2) {
329 htmlf("<span class='age-weeks'>%.0f weeks</span>", 329 htmlf("<span class='age-weeks'>%.0f weeks</span>",
330 secs * 1.0 / TM_WEEK); 330 secs * 1.0 / TM_WEEK);
331 return; 331 return;
332 } 332 }
333 if (secs < TM_YEAR * 2) { 333 if (secs < TM_YEAR * 2) {
334 htmlf("<span class='age-months'>%.0f months</span>", 334 htmlf("<span class='age-months'>%.0f months</span>",
335 secs * 1.0 / TM_MONTH); 335 secs * 1.0 / TM_MONTH);
336 return; 336 return;
337 } 337 }
338 htmlf("<span class='age-years'>%.0f years</span>", 338 htmlf("<span class='age-years'>%.0f years</span>",
339 secs * 1.0 / TM_YEAR); 339 secs * 1.0 / TM_YEAR);
340} 340}
341 341
342void cgit_print_docstart(char *title, struct cacheitem *item) 342void cgit_print_docstart(char *title, struct cacheitem *item)
343{ 343{
344 html("Content-Type: text/html; charset=utf-8\n"); 344 html("Content-Type: text/html; charset=utf-8\n");
345 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 345 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
346 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 346 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
347 ttl_seconds(item->ttl))); 347 ttl_seconds(item->ttl)));
348 html("\n"); 348 html("\n");
349 html(cgit_doctype); 349 html(cgit_doctype);
350 html("<html>\n"); 350 html("<html>\n");
351 html("<head>\n"); 351 html("<head>\n");
352 html("<title>"); 352 html("<title>");
353 html_txt(title); 353 html_txt(title);
354 html("</title>\n"); 354 html("</title>\n");
355 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 355 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
356 html("<link rel='stylesheet' type='text/css' href='"); 356 html("<link rel='stylesheet' type='text/css' href='");
357 html_attr(cgit_css); 357 html_attr(cgit_css);
358 html("'/>\n"); 358 html("'/>\n");
359 html("</head>\n"); 359 html("</head>\n");
360 html("<body>\n"); 360 html("<body>\n");
361} 361}
362 362
363void cgit_print_docend() 363void cgit_print_docend()
364{ 364{
365 html("</td></tr></table>"); 365 html("</td>\n</tr>\n<table>\n</body>\n</html>\n");
366 html("</body>\n</html>\n"); 366}
367
368int print_branch_option(const char *refname, const unsigned char *sha1,
369 int flags, void *cb_data)
370{
371 char *name = (char *)refname;
372 html_option(name, name, cgit_query_head);
373 return 0;
374}
375
376int print_archive_ref(const char *refname, const unsigned char *sha1,
377 int flags, void *cb_data)
378{
379 struct tag *tag;
380 struct taginfo *info;
381 struct object *obj;
382 char buf[256], *url;
383 unsigned char fileid[20];
384 int *header = (int *)cb_data;
385
386 if (prefixcmp(refname, "refs/archives"))
387 return 0;
388 strncpy(buf, refname+14, sizeof(buf));
389 obj = parse_object(sha1);
390 if (!obj)
391 return 1;
392 if (obj->type == OBJ_TAG) {
393 tag = lookup_tag(sha1);
394 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
395 return 0;
396 hashcpy(fileid, tag->tagged->sha1);
397 } else if (obj->type != OBJ_BLOB) {
398 return 0;
399 } else {
400 hashcpy(fileid, sha1);
401 }
402 if (!*header) {
403 html("<p><h1>download</h1>");
404 *header = 1;
405 }
406 url = cgit_pageurl(cgit_query_repo, "blob",
407 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
408 buf));
409 html_link_open(url, NULL, "menu");
410 html_txt(strlpart(buf, 20));
411 html_link_close();
412 return 0;
413}
414
415void add_hidden_formfields(int incl_head, int incl_search)
416{
417 if (!cgit_virtual_root) {
418 if (cgit_query_repo)
419 html_hidden("r", cgit_query_repo);
420 if (cgit_query_page)
421 html_hidden("p", cgit_query_page);
422 }
423
424 if (incl_head && strcmp(cgit_query_head, cgit_repo->defbranch))
425 html_hidden("h", cgit_query_head);
426
427 if (cgit_query_sha1)
428 html_hidden("id", cgit_query_sha1);
429 if (cgit_query_sha2)
430 html_hidden("id2", cgit_query_sha2);
431
432 if (incl_search) {
433 if (cgit_query_grep)
434 html_hidden("qt", cgit_query_grep);
435 if (cgit_query_search)
436 html_hidden("q", cgit_query_search);
437 }
367} 438}
368 439
369void cgit_print_pageheader(char *title, int show_search) 440void cgit_print_pageheader(char *title, int show_search)
370{ 441{
371 html("<table id='layout'>"); 442 static const char *default_info = "This is cgit, a fast webinterface for git repositories";
372 html("<tr><td id='header'><a href='"); 443 int header = 0;
373 html_attr(cgit_rooturl()); 444
374 html("'>"); 445 html("<div id='sidebar'>\n");
375 html_txt(cgit_root_title);
376 html("</a></td><td id='logo'>");
377 html("<a href='"); 446 html("<a href='");
378 html_attr(cgit_logo_link); 447 html_attr(cgit_rooturl());
379 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 448 htmlf("'><div id='logo'><img src='%s' alt='cgit'/></div></a>\n",
380 html("</td></tr>"); 449 cgit_logo);
381 html("<tr><td id='crumb'>"); 450 html("<div class='infobox'>");
382 if (cgit_query_repo) { 451 if (cgit_query_repo) {
383 html_txt(cgit_repo->name); 452 html("<h1>");
384 html(" ("); 453 html_txt(strrpart(cgit_repo->name, 20));
385 html_txt(cgit_query_head); 454 html("</h1>\n");
386 html(") : &nbsp;"); 455 html_txt(cgit_repo->desc);
387 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, 456 if (cgit_repo->owner) {
457 html("<p>\n<h1>owner</h1>\n");
458 html_txt(cgit_repo->owner);
459 }
460 html("<p>\n<h1>navigate</h1>\n");
461 reporevlink(NULL, "summary", NULL, "menu", cgit_query_head,
388 NULL, NULL); 462 NULL, NULL);
389 html(" "); 463 cgit_log_link("log", NULL, "menu", cgit_query_head,
390 cgit_log_link("log", NULL, NULL, cgit_query_head,
391 cgit_query_sha1, cgit_query_path, 0); 464 cgit_query_sha1, cgit_query_path, 0);
392 html(" "); 465 cgit_tree_link("tree", NULL, "menu", cgit_query_head,
393 cgit_tree_link("tree", NULL, NULL, cgit_query_head,
394 cgit_query_sha1, NULL); 466 cgit_query_sha1, NULL);
395 html(" "); 467 cgit_commit_link("commit", NULL, "menu", cgit_query_head,
396 cgit_commit_link("commit", NULL, NULL, cgit_query_head,
397 cgit_query_sha1); 468 cgit_query_sha1);
398 html(" "); 469 cgit_diff_link("diff", NULL, "menu", cgit_query_head,
399 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
400 cgit_query_sha1, cgit_query_sha2, 470 cgit_query_sha1, cgit_query_sha2,
401 cgit_query_path); 471 cgit_query_path);
402 } else { 472
403 html_txt("Index of repositories"); 473 for_each_ref(print_archive_ref, &header);
404 } 474
405 html("</td>"); 475 html("<p>\n<h1>branch</h1>\n");
406 html("<td id='search'>"); 476 html("<form method='get' action=''>\n");
407 if (show_search) { 477 add_hidden_formfields(0, 1);
478 html("<select name='h' onchange='this.form.submit();'>\n");
479 for_each_branch_ref(print_branch_option, cgit_query_head);
480 html("</select>\n");
481 html("</form>\n");
482
483 html("<p>\n<h1>search</h1>\n");
408 html("<form method='get' action='"); 484 html("<form method='get' action='");
409 html_attr(cgit_currurl()); 485 html_attr(cgit_pageurl(cgit_query_repo, "log", NULL));
410 html("'>"); 486 html("'>\n");
411 if (!cgit_virtual_root) { 487 add_hidden_formfields(1, 0);
412 if (cgit_query_repo) 488 html("<select name='qt'>\n");
413 html_hidden("r", cgit_query_repo);
414 if (cgit_query_page)
415 html_hidden("p", cgit_query_page);
416 }
417 if (cgit_query_head)
418 html_hidden("h", cgit_query_head);
419 if (cgit_query_sha1)
420 html_hidden("id", cgit_query_sha1);
421 if (cgit_query_sha2)
422 html_hidden("id2", cgit_query_sha2);
423 html("<select name='qt'>");
424 html_option("grep", "log msg", cgit_query_grep); 489 html_option("grep", "log msg", cgit_query_grep);
425 html_option("author", "author", cgit_query_grep); 490 html_option("author", "author", cgit_query_grep);
426 html_option("committer", "committer", cgit_query_grep); 491 html_option("committer", "committer", cgit_query_grep);
427 html("</select>"); 492 html("</select>\n");
428 html("<input class='txt' type='text' name='q' value='"); 493 html("<input class='txt' type='text' name='q' value='");
429 html_attr(cgit_query_search); 494 html_attr(cgit_query_search);
430 html("'/><input class='btn' type='submit' value='...'/></form>"); 495 html("'/>\n");
496 html("</form>\n");
497 } else {
498 if (!cgit_index_info || html_include(cgit_index_info))
499 html(default_info);
431 } 500 }
432 html("</td></tr>"); 501
433 html("<tr><td id='content' colspan='2'>"); 502 html("</div>\n");
503
504 html("</div>\n<table class='grid'><tr><td id='content'>\n");
434} 505}
435 506
507
436void cgit_print_snapshot_start(const char *mimetype, const char *filename, 508void cgit_print_snapshot_start(const char *mimetype, const char *filename,
437 struct cacheitem *item) 509 struct cacheitem *item)
438{ 510{
439 htmlf("Content-Type: %s\n", mimetype); 511 htmlf("Content-Type: %s\n", mimetype);
440 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 512 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
441 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 513 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
442 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 514 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
443 ttl_seconds(item->ttl))); 515 ttl_seconds(item->ttl)));
444 html("\n"); 516 html("\n");
445} 517}
446 518
447/* vim:set sw=8: */ 519/* vim:set sw=8: */