diff options
-rw-r--r-- | cache.c | 57 | ||||
-rw-r--r-- | cgit.c | 72 | ||||
-rw-r--r-- | scan-tree.c | 160 | ||||
-rw-r--r-- | ui-log.c | 33 | ||||
-rw-r--r-- | ui-plain.c | 6 | ||||
-rw-r--r-- | ui-refs.c | 10 | ||||
-rw-r--r-- | ui-repolist.c | 28 | ||||
-rw-r--r-- | ui-shared.c | 63 | ||||
-rw-r--r-- | ui-snapshot.c | 60 | ||||
-rw-r--r-- | ui-summary.c | 12 | ||||
-rw-r--r-- | ui-tag.c | 14 | ||||
-rw-r--r-- | ui-tree.c | 33 |
12 files changed, 305 insertions, 243 deletions
@@ -312,9 +312,9 @@ int cache_process(int size, const char *path, const char *key, int ttl, | |||
312 | cache_fill_fn fn, void *cbdata) | 312 | cache_fill_fn fn, void *cbdata) |
313 | { | 313 | { |
314 | unsigned long hash; | 314 | unsigned long hash; |
315 | int len, i; | 315 | int i; |
316 | char filename[1024]; | 316 | struct strbuf filename = STRBUF_INIT; |
317 | char lockname[1024 + 5]; /* 5 = ".lock" */ | 317 | struct strbuf lockname = STRBUF_INIT; |
318 | struct cache_slot slot; | 318 | struct cache_slot slot; |
319 | 319 | ||
320 | /* If the cache is disabled, just generate the content */ | 320 | /* If the cache is disabled, just generate the content */ |
@@ -329,32 +329,22 @@ int cache_process(int size, const char *path, const char *key, int ttl, | |||
329 | fn(cbdata); | 329 | fn(cbdata); |
330 | return 0; | 330 | return 0; |
331 | } | 331 | } |
332 | len = strlen(path); | ||
333 | if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */ | ||
334 | cache_log("[cgit] Cache path too long, caching is disabled: %s\n", | ||
335 | path); | ||
336 | fn(cbdata); | ||
337 | return 0; | ||
338 | } | ||
339 | if (!key) | 332 | if (!key) |
340 | key = ""; | 333 | key = ""; |
341 | hash = hash_str(key) % size; | 334 | hash = hash_str(key) % size; |
342 | strcpy(filename, path); | 335 | strbuf_addstr(&filename, path); |
343 | if (filename[len - 1] != '/') | 336 | strbuf_ensure_end(&filename, '/'); |
344 | filename[len++] = '/'; | ||
345 | for (i = 0; i < 8; i++) { | 337 | for (i = 0; i < 8; i++) { |
346 | sprintf(filename + len++, "%x", | 338 | strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf)); |
347 | (unsigned char)(hash & 0xf)); | ||
348 | hash >>= 4; | 339 | hash >>= 4; |
349 | } | 340 | } |
350 | filename[len] = '\0'; | 341 | strbuf_addbuf(&lockname, &filename); |
351 | strcpy(lockname, filename); | 342 | strbuf_addstr(&lockname, ".lock"); |
352 | strcpy(lockname + len, ".lock"); | ||
353 | slot.fn = fn; | 343 | slot.fn = fn; |
354 | slot.cbdata = cbdata; | 344 | slot.cbdata = cbdata; |
355 | slot.ttl = ttl; | 345 | slot.ttl = ttl; |
356 | slot.cache_name = filename; | 346 | slot.cache_name = strbuf_detach(&filename, NULL); |
357 | slot.lock_name = lockname; | 347 | slot.lock_name = strbuf_detach(&lockname, NULL); |
358 | slot.key = key; | 348 | slot.key = key; |
359 | slot.keylen = strlen(key); | 349 | slot.keylen = strlen(key); |
360 | return process_slot(&slot); | 350 | return process_slot(&slot); |
@@ -381,18 +371,13 @@ int cache_ls(const char *path) | |||
381 | struct dirent *ent; | 371 | struct dirent *ent; |
382 | int err = 0; | 372 | int err = 0; |
383 | struct cache_slot slot; | 373 | struct cache_slot slot; |
384 | char fullname[1024]; | 374 | struct strbuf fullname = STRBUF_INIT; |
385 | char *name; | 375 | size_t prefixlen; |
386 | 376 | ||
387 | if (!path) { | 377 | if (!path) { |
388 | cache_log("[cgit] cache path not specified\n"); | 378 | cache_log("[cgit] cache path not specified\n"); |
389 | return -1; | 379 | return -1; |
390 | } | 380 | } |
391 | if (strlen(path) > 1024 - 10) { | ||
392 | cache_log("[cgit] cache path too long: %s\n", | ||
393 | path); | ||
394 | return -1; | ||
395 | } | ||
396 | dir = opendir(path); | 381 | dir = opendir(path); |
397 | if (!dir) { | 382 | if (!dir) { |
398 | err = errno; | 383 | err = errno; |
@@ -400,30 +385,28 @@ int cache_ls(const char *path) | |||
400 | path, strerror(err), err); | 385 | path, strerror(err), err); |
401 | return err; | 386 | return err; |
402 | } | 387 | } |
403 | strcpy(fullname, path); | 388 | strbuf_addstr(&fullname, path); |
404 | name = fullname + strlen(path); | 389 | strbuf_ensure_end(&fullname, '/'); |
405 | if (*(name - 1) != '/') { | 390 | prefixlen = fullname.len; |
406 | *name++ = '/'; | ||
407 | *name = '\0'; | ||
408 | } | ||
409 | slot.cache_name = fullname; | ||
410 | while ((ent = readdir(dir)) != NULL) { | 391 | while ((ent = readdir(dir)) != NULL) { |
411 | if (strlen(ent->d_name) != 8) | 392 | if (strlen(ent->d_name) != 8) |
412 | continue; | 393 | continue; |
413 | strcpy(name, ent->d_name); | 394 | strbuf_setlen(&fullname, prefixlen); |
395 | strbuf_addstr(&fullname, ent->d_name); | ||
414 | if ((err = open_slot(&slot)) != 0) { | 396 | if ((err = open_slot(&slot)) != 0) { |
415 | cache_log("[cgit] unable to open path %s: %s (%d)\n", | 397 | cache_log("[cgit] unable to open path %s: %s (%d)\n", |
416 | fullname, strerror(err), err); | 398 | fullname.buf, strerror(err), err); |
417 | continue; | 399 | continue; |
418 | } | 400 | } |
419 | printf("%s %s %10"PRIuMAX" %s\n", | 401 | printf("%s %s %10"PRIuMAX" %s\n", |
420 | name, | 402 | fullname.buf, |
421 | sprintftime("%Y-%m-%d %H:%M:%S", | 403 | sprintftime("%Y-%m-%d %H:%M:%S", |
422 | slot.cache_st.st_mtime), | 404 | slot.cache_st.st_mtime), |
423 | (uintmax_t)slot.cache_st.st_size, | 405 | (uintmax_t)slot.cache_st.st_size, |
424 | slot.buf); | 406 | slot.buf); |
425 | close_slot(&slot); | 407 | close_slot(&slot); |
426 | } | 408 | } |
409 | slot.cache_name = strbuf_detach(&fullname, NULL); | ||
427 | closedir(dir); | 410 | closedir(dir); |
428 | return 0; | 411 | return 0; |
429 | } | 412 | } |
@@ -468,8 +468,8 @@ static int prepare_repo_cmd(struct cgit_context *ctx) | |||
468 | if (nongit) { | 468 | if (nongit) { |
469 | const char *name = ctx->repo->name; | 469 | const char *name = ctx->repo->name; |
470 | rc = errno; | 470 | rc = errno; |
471 | ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, | 471 | ctx->page.title = fmtalloc("%s - %s", ctx->cfg.root_title, |
472 | "config error"); | 472 | "config error"); |
473 | ctx->repo = NULL; | 473 | ctx->repo = NULL; |
474 | cgit_print_http_headers(ctx); | 474 | cgit_print_http_headers(ctx); |
475 | cgit_print_docstart(ctx); | 475 | cgit_print_docstart(ctx); |
@@ -479,7 +479,7 @@ static int prepare_repo_cmd(struct cgit_context *ctx) | |||
479 | cgit_print_docend(); | 479 | cgit_print_docend(); |
480 | return 1; | 480 | return 1; |
481 | } | 481 | } |
482 | ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); | 482 | ctx->page.title = fmtalloc("%s - %s", ctx->repo->name, ctx->repo->desc); |
483 | 483 | ||
484 | if (!ctx->repo->defbranch) | 484 | if (!ctx->repo->defbranch) |
485 | ctx->repo->defbranch = guess_defbranch(); | 485 | ctx->repo->defbranch = guess_defbranch(); |
@@ -577,21 +577,16 @@ static int cmp_repos(const void *a, const void *b) | |||
577 | static char *build_snapshot_setting(int bitmap) | 577 | static char *build_snapshot_setting(int bitmap) |
578 | { | 578 | { |
579 | const struct cgit_snapshot_format *f; | 579 | const struct cgit_snapshot_format *f; |
580 | char *result = xstrdup(""); | 580 | struct strbuf result = STRBUF_INIT; |
581 | char *tmp; | ||
582 | int len; | ||
583 | 581 | ||
584 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 582 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
585 | if (f->bit & bitmap) { | 583 | if (f->bit & bitmap) { |
586 | tmp = result; | 584 | if (result.len) |
587 | result = xstrdup(fmt("%s%s ", tmp, f->suffix)); | 585 | strbuf_addch(&result, ' '); |
588 | free(tmp); | 586 | strbuf_addstr(&result, f->suffix); |
589 | } | 587 | } |
590 | } | 588 | } |
591 | len = strlen(result); | 589 | return strbuf_detach(&result, NULL); |
592 | if (len) | ||
593 | result[len - 1] = '\0'; | ||
594 | return result; | ||
595 | } | 590 | } |
596 | 591 | ||
597 | static char *get_first_line(char *txt) | 592 | static char *get_first_line(char *txt) |
@@ -639,7 +634,7 @@ static void print_repo(FILE *f, struct cgit_repo *repo) | |||
639 | fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); | 634 | fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); |
640 | if (repo->snapshots != ctx.cfg.snapshots) { | 635 | if (repo->snapshots != ctx.cfg.snapshots) { |
641 | char *tmp = build_snapshot_setting(repo->snapshots); | 636 | char *tmp = build_snapshot_setting(repo->snapshots); |
642 | fprintf(f, "repo.snapshots=%s\n", tmp); | 637 | fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : ""); |
643 | free(tmp); | 638 | free(tmp); |
644 | } | 639 | } |
645 | if (repo->max_stats != ctx.cfg.max_stats) | 640 | if (repo->max_stats != ctx.cfg.max_stats) |
@@ -661,20 +656,22 @@ static void print_repolist(FILE *f, struct cgit_repolist *list, int start) | |||
661 | */ | 656 | */ |
662 | static int generate_cached_repolist(const char *path, const char *cached_rc) | 657 | static int generate_cached_repolist(const char *path, const char *cached_rc) |
663 | { | 658 | { |
664 | char *locked_rc; | 659 | struct strbuf locked_rc = STRBUF_INIT; |
660 | int result = 0; | ||
665 | int idx; | 661 | int idx; |
666 | FILE *f; | 662 | FILE *f; |
667 | 663 | ||
668 | locked_rc = xstrdup(fmt("%s.lock", cached_rc)); | 664 | strbuf_addf(&locked_rc, "%s.lock", cached_rc); |
669 | f = fopen(locked_rc, "wx"); | 665 | f = fopen(locked_rc.buf, "wx"); |
670 | if (!f) { | 666 | if (!f) { |
671 | /* Inform about the error unless the lockfile already existed, | 667 | /* Inform about the error unless the lockfile already existed, |
672 | * since that only means we've got concurrent requests. | 668 | * since that only means we've got concurrent requests. |
673 | */ | 669 | */ |
674 | if (errno != EEXIST) | 670 | result = errno; |
671 | if (result != EEXIST) | ||
675 | fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", | 672 | fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", |
676 | locked_rc, strerror(errno), errno); | 673 | locked_rc.buf, strerror(result), result); |
677 | return errno; | 674 | goto out; |
678 | } | 675 | } |
679 | idx = cgit_repolist.count; | 676 | idx = cgit_repolist.count; |
680 | if (ctx.cfg.project_list) | 677 | if (ctx.cfg.project_list) |
@@ -682,55 +679,59 @@ static int generate_cached_repolist(const char *path, const char *cached_rc) | |||
682 | else | 679 | else |
683 | scan_tree(path, repo_config); | 680 | scan_tree(path, repo_config); |
684 | print_repolist(f, &cgit_repolist, idx); | 681 | print_repolist(f, &cgit_repolist, idx); |
685 | if (rename(locked_rc, cached_rc)) | 682 | if (rename(locked_rc.buf, cached_rc)) |
686 | fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", | 683 | fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", |
687 | locked_rc, cached_rc, strerror(errno), errno); | 684 | locked_rc.buf, cached_rc, strerror(errno), errno); |
688 | fclose(f); | 685 | fclose(f); |
689 | return 0; | 686 | out: |
687 | strbuf_release(&locked_rc); | ||
688 | return result; | ||
690 | } | 689 | } |
691 | 690 | ||
692 | static void process_cached_repolist(const char *path) | 691 | static void process_cached_repolist(const char *path) |
693 | { | 692 | { |
694 | struct stat st; | 693 | struct stat st; |
695 | char *cached_rc; | 694 | struct strbuf cached_rc = STRBUF_INIT; |
696 | time_t age; | 695 | time_t age; |
697 | unsigned long hash; | 696 | unsigned long hash; |
698 | 697 | ||
699 | hash = hash_str(path); | 698 | hash = hash_str(path); |
700 | if (ctx.cfg.project_list) | 699 | if (ctx.cfg.project_list) |
701 | hash += hash_str(ctx.cfg.project_list); | 700 | hash += hash_str(ctx.cfg.project_list); |
702 | cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash)); | 701 | strbuf_addf(&cached_rc, "%s/rc-%8lx", ctx.cfg.cache_root, hash); |
703 | 702 | ||
704 | if (stat(cached_rc, &st)) { | 703 | if (stat(cached_rc.buf, &st)) { |
705 | /* Nothing is cached, we need to scan without forking. And | 704 | /* Nothing is cached, we need to scan without forking. And |
706 | * if we fail to generate a cached repolist, we need to | 705 | * if we fail to generate a cached repolist, we need to |
707 | * invoke scan_tree manually. | 706 | * invoke scan_tree manually. |
708 | */ | 707 | */ |
709 | if (generate_cached_repolist(path, cached_rc)) { | 708 | if (generate_cached_repolist(path, cached_rc.buf)) { |
710 | if (ctx.cfg.project_list) | 709 | if (ctx.cfg.project_list) |
711 | scan_projects(path, ctx.cfg.project_list, | 710 | scan_projects(path, ctx.cfg.project_list, |
712 | repo_config); | 711 | repo_config); |
713 | else | 712 | else |
714 | scan_tree(path, repo_config); | 713 | scan_tree(path, repo_config); |
715 | } | 714 | } |
716 | return; | 715 | goto out; |
717 | } | 716 | } |
718 | 717 | ||
719 | parse_configfile(cached_rc, config_cb); | 718 | parse_configfile(cached_rc.buf, config_cb); |
720 | 719 | ||
721 | /* If the cached configfile hasn't expired, lets exit now */ | 720 | /* If the cached configfile hasn't expired, lets exit now */ |
722 | age = time(NULL) - st.st_mtime; | 721 | age = time(NULL) - st.st_mtime; |
723 | if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) | 722 | if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) |
724 | return; | 723 | goto out; |
725 | 724 | ||
726 | /* The cached repolist has been parsed, but it was old. So lets | 725 | /* The cached repolist has been parsed, but it was old. So lets |
727 | * rescan the specified path and generate a new cached repolist | 726 | * rescan the specified path and generate a new cached repolist |
728 | * in a child-process to avoid latency for the current request. | 727 | * in a child-process to avoid latency for the current request. |
729 | */ | 728 | */ |
730 | if (fork()) | 729 | if (fork()) |
731 | return; | 730 | goto out; |
732 | 731 | ||
733 | exit(generate_cached_repolist(path, cached_rc)); | 732 | exit(generate_cached_repolist(path, cached_rc.buf)); |
733 | out: | ||
734 | strbuf_release(&cached_rc); | ||
734 | } | 735 | } |
735 | 736 | ||
736 | static void cgit_parse_args(int argc, const char **argv) | 737 | static void cgit_parse_args(int argc, const char **argv) |
@@ -812,7 +813,6 @@ static int calc_ttl() | |||
812 | int main(int argc, const char **argv) | 813 | int main(int argc, const char **argv) |
813 | { | 814 | { |
814 | const char *path; | 815 | const char *path; |
815 | char *qry; | ||
816 | int err, ttl; | 816 | int err, ttl; |
817 | 817 | ||
818 | prepare_context(&ctx); | 818 | prepare_context(&ctx); |
@@ -843,9 +843,9 @@ int main(int argc, const char **argv) | |||
843 | path++; | 843 | path++; |
844 | ctx.qry.url = xstrdup(path); | 844 | ctx.qry.url = xstrdup(path); |
845 | if (ctx.qry.raw) { | 845 | if (ctx.qry.raw) { |
846 | qry = ctx.qry.raw; | 846 | char *newqry = fmtalloc("%s?%s", path, ctx.qry.raw); |
847 | ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); | 847 | free(ctx.qry.raw); |
848 | free(qry); | 848 | ctx.qry.raw = newqry; |
849 | } else | 849 | } else |
850 | ctx.qry.raw = xstrdup(ctx.qry.url); | 850 | ctx.qry.raw = xstrdup(ctx.qry.url); |
851 | cgit_parse_url(ctx.qry.url); | 851 | cgit_parse_url(ctx.qry.url); |
diff --git a/scan-tree.c b/scan-tree.c index 05caba5..beb584b 100644 --- a/scan-tree.c +++ b/scan-tree.c | |||
@@ -12,38 +12,38 @@ | |||
12 | #include "configfile.h" | 12 | #include "configfile.h" |
13 | #include "html.h" | 13 | #include "html.h" |
14 | 14 | ||
15 | #define MAX_PATH 4096 | ||
16 | |||
17 | /* return 1 if path contains a objects/ directory and a HEAD file */ | 15 | /* return 1 if path contains a objects/ directory and a HEAD file */ |
18 | static int is_git_dir(const char *path) | 16 | static int is_git_dir(const char *path) |
19 | { | 17 | { |
20 | struct stat st; | 18 | struct stat st; |
21 | static char buf[MAX_PATH]; | 19 | struct strbuf pathbuf = STRBUF_INIT; |
20 | int result = 0; | ||
22 | 21 | ||
23 | if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { | 22 | strbuf_addf(&pathbuf, "%s/objects", path); |
24 | fprintf(stderr, "Insanely long path: %s\n", path); | 23 | if (stat(pathbuf.buf, &st)) { |
25 | return 0; | ||
26 | } | ||
27 | if (stat(buf, &st)) { | ||
28 | if (errno != ENOENT) | 24 | if (errno != ENOENT) |
29 | fprintf(stderr, "Error checking path %s: %s (%d)\n", | 25 | fprintf(stderr, "Error checking path %s: %s (%d)\n", |
30 | path, strerror(errno), errno); | 26 | path, strerror(errno), errno); |
31 | return 0; | 27 | goto out; |
32 | } | 28 | } |
33 | if (!S_ISDIR(st.st_mode)) | 29 | if (!S_ISDIR(st.st_mode)) |
34 | return 0; | 30 | goto out; |
35 | 31 | ||
36 | sprintf(buf, "%s/HEAD", path); | 32 | strbuf_reset(&pathbuf); |
37 | if (stat(buf, &st)) { | 33 | strbuf_addf(&pathbuf, "%s/HEAD", path); |
34 | if (stat(pathbuf.buf, &st)) { | ||
38 | if (errno != ENOENT) | 35 | if (errno != ENOENT) |
39 | fprintf(stderr, "Error checking path %s: %s (%d)\n", | 36 | fprintf(stderr, "Error checking path %s: %s (%d)\n", |
40 | path, strerror(errno), errno); | 37 | path, strerror(errno), errno); |
41 | return 0; | 38 | goto out; |
42 | } | 39 | } |
43 | if (!S_ISREG(st.st_mode)) | 40 | if (!S_ISREG(st.st_mode)) |
44 | return 0; | 41 | goto out; |
45 | 42 | ||
46 | return 1; | 43 | result = 1; |
44 | out: | ||
45 | strbuf_release(&pathbuf); | ||
46 | return result; | ||
47 | } | 47 | } |
48 | 48 | ||
49 | struct cgit_repo *repo; | 49 | struct cgit_repo *repo; |
@@ -75,47 +75,61 @@ static char *xstrrchr(char *s, char *from, int c) | |||
75 | return from < s ? NULL : from; | 75 | return from < s ? NULL : from; |
76 | } | 76 | } |
77 | 77 | ||
78 | static void add_repo(const char *base, const char *path, repo_config_fn fn) | 78 | static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) |
79 | { |