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 | { | 79 | { |
80 | struct stat st; | 80 | struct stat st; |
81 | struct passwd *pwd; | 81 | struct passwd *pwd; |
82 | char *rel, *p, *slash; | 82 | size_t pathlen; |
83 | struct strbuf rel = STRBUF_INIT; | ||
84 | char *p, *slash; | ||
83 | int n; | 85 | int n; |
84 | size_t size; | 86 | size_t size; |
85 | 87 | ||
86 | if (stat(path, &st)) { | 88 | if (stat(path->buf, &st)) { |
87 | fprintf(stderr, "Error accessing %s: %s (%d)\n", | 89 | fprintf(stderr, "Error accessing %s: %s (%d)\n", |
88 | path, strerror(errno), errno); | 90 | path->buf, strerror(errno), errno); |
89 | return; | 91 | return; |
90 | } | 92 | } |
91 | 93 | ||
92 | if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st)) | 94 | strbuf_addch(path, '/'); |
93 | return; | 95 | pathlen = path->len; |
94 | 96 | ||
95 | if (!stat(fmt("%s/noweb", path), &st)) | 97 | if (ctx.cfg.strict_export) { |
98 | strbuf_addstr(path, ctx.cfg.strict_export); | ||
99 | if(stat(path->buf, &st)) | ||
100 | return; | ||
101 | strbuf_setlen(path, pathlen); | ||
102 | } | ||
103 | |||
104 | strbuf_addstr(path, "noweb"); | ||
105 | if (!stat(path->buf, &st)) | ||
96 | return; | 106 | return; |
107 | strbuf_setlen(path, pathlen); | ||
97 | 108 | ||
98 | if (base == path) | 109 | if (strncmp(base, path->buf, strlen(base))) |
99 | rel = xstrdup(path); | 110 | strbuf_addbuf(&rel, path); |
100 | else | 111 | else |
101 | rel = xstrdup(path + strlen(base) + 1); | 112 | strbuf_addstr(&rel, path->buf + strlen(base) + 1); |
102 | 113 | ||
103 | if (!strcmp(rel + strlen(rel) - 5, "/.git")) | 114 | if (!strcmp(rel.buf + rel.len - 5, "/.git")) |
104 | rel[strlen(rel) - 5] = '\0'; | 115 | strbuf_setlen(&rel, rel.len - 5); |
105 | 116 | ||
106 | repo = cgit_add_repo(rel); | 117 | repo = cgit_add_repo(rel.buf); |
107 | config_fn = fn; | 118 | config_fn = fn; |
108 | if (ctx.cfg.enable_git_config) | 119 | if (ctx.cfg.enable_git_config) { |
109 | git_config_from_file(gitconfig_config, fmt("%s/config", path), NULL); | 120 | strbuf_addstr(path, "config"); |
121 | git_config_from_file(gitconfig_config, path->buf, NULL); | ||
122 | strbuf_setlen(path, pathlen); | ||
123 | } | ||
110 | 124 | ||
111 | if (ctx.cfg.remove_suffix) | 125 | if (ctx.cfg.remove_suffix) |
112 | if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) | 126 | if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) |
113 | *p = '\0'; | 127 | *p = '\0'; |
114 | repo->path = xstrdup(path); | 128 | repo->path = xstrdup(path->buf); |
115 | while (!repo->owner) { | 129 | while (!repo->owner) { |
116 | if ((pwd = getpwuid(st.st_uid)) == NULL) { | 130 | if ((pwd = getpwuid(st.st_uid)) == NULL) { |
117 | fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", | 131 | fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", |
118 | path, strerror(errno), errno); | 132 | path->buf, strerror(errno), errno); |
119 | break; | 133 | break; |
120 | } | 134 | } |
121 | if (pwd->pw_gecos) | 135 | if (pwd->pw_gecos) |
@@ -125,30 +139,32 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn) | |||
125 | } | 139 | } |
126 | 140 | ||
127 | if (repo->desc == cgit_default_repo_desc || !repo->desc) { | 141 | if (repo->desc == cgit_default_repo_desc || !repo->desc) { |
128 | p = fmt("%s/description", path); | 142 | strbuf_addstr(path, "description"); |
129 | if (!stat(p, &st)) | 143 | if (!stat(path->buf, &st)) |
130 | readfile(p, &repo->desc, &size); | 144 | readfile(path->buf, &repo->desc, &size); |
145 | strbuf_setlen(path, pathlen); | ||
131 | } | 146 | } |
132 | 147 | ||
133 | if (!repo->readme) { | 148 | if (!repo->readme) { |
134 | p = fmt("%s/README.html", path); | 149 | strbuf_addstr(path, "README.html"); |
135 | if (!stat(p, &st)) | 150 | if (!stat(path->buf, &st)) |
136 | repo->readme = "README.html"; | 151 | repo->readme = "README.html"; |
152 | strbuf_setlen(path, pathlen); | ||
137 | } | 153 | } |
138 | if (ctx.cfg.section_from_path) { | 154 | if (ctx.cfg.section_from_path) { |
139 | n = ctx.cfg.section_from_path; | 155 | n = ctx.cfg.section_from_path; |
140 | if (n > 0) { | 156 | if (n > 0) { |
141 | slash = rel; | 157 | slash = rel.buf; |
142 | while (slash && n && (slash = strchr(slash, '/'))) | 158 | while (slash && n && (slash = strchr(slash, '/'))) |
143 | n--; | 159 | n--; |
144 | } else { | 160 | } else { |
145 | slash = rel + strlen(rel); | 161 | slash = rel.buf + rel.len; |
146 | while (slash && n && (slash = xstrrchr(rel, slash, '/'))) | 162 | while (slash && n && (slash = xstrrchr(rel.buf, slash, '/'))) |
147 | n++; | 163 | n++; |
148 | } | 164 | } |
149 | if (slash && !n) { | 165 | if (slash && !n) { |
150 | *slash = '\0'; | 166 | *slash = '\0'; |
151 | repo->section = xstrdup(rel); | 167 | repo->section = xstrdup(rel.buf); |
152 | *slash = '/'; | 168 | *slash = '/'; |
153 | if (!prefixcmp(repo->name, repo->section)) { | 169 | if (!prefixcmp(repo->name, repo->section)) { |
154 | repo->name += strlen(repo->section); | 170 | repo->name += strlen(repo->section); |
@@ -158,19 +174,19 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn) | |||
158 | } | 174 | } |
159 | } | 175 | } |
160 | 176 | ||
161 | p = fmt("%s/cgitrc", path); | 177 | strbuf_addstr(path, "cgitrc"); |
162 | if (!stat(p, &st)) | 178 | if (!stat(path->buf, &st)) |
163 | parse_configfile(xstrdup(p), &repo_config); | 179 | parse_configfile(xstrdup(path->buf), &repo_config); |
164 | |||
165 | 180 | ||
166 | free(rel); | 181 | strbuf_release(&rel); |
167 | } | 182 | } |
168 | 183 | ||
169 | static void scan_path(const char *base, const char *path, repo_config_fn fn) | 184 | static void scan_path(const char *base, const char *path, repo_config_fn fn) |
170 | { | 185 | { |
171 | DIR *dir = opendir(path); | 186 | DIR *dir = opendir(path); |
172 | struct dirent *ent; | 187 | struct dirent *ent; |
173 | char *buf; | 188 | struct strbuf pathbuf = STRBUF_INIT; |
189 | size_t pathlen = strlen(path); | ||
174 | struct stat st; | 190 | struct stat st; |
175 | 191 | ||
176 | if (!dir) { | 192 | if (!dir) { |
@@ -178,14 +194,22 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn) | |||
178 | path, strerror(errno), errno); | 194 | path, strerror(errno), errno); |
179 | return; | 195 | return; |
180 | } | 196 | } |
181 | if (is_git_dir(path)) { | 197 | |
182 | add_repo(base, path, fn); | 198 | strbuf_add(&pathbuf, path, strlen(path)); |
199 | if (is_git_dir(pathbuf.buf)) { | ||
200 | add_repo(base, &pathbuf, fn); | ||
183 | goto end; | 201 | goto end; |
184 | } | 202 | } |
185 | if (is_git_dir(fmt("%s/.git", path))) { | 203 | strbuf_addstr(&pathbuf, "/.git"); |
186 | add_repo(base, fmt("%s/.git", path), fn); | 204 | if (is_git_dir(pathbuf.buf)) { |
205 | add_repo(base, &pathbuf, fn); | ||
187 | goto end; | 206 | goto end; |
188 | } | 207 | } |
208 | /* | ||
209 | * Add one because we don't want to lose the trailing '/' when we | ||
210 | * reset the length of pathbuf in the loop below. | ||
211 | */ | ||
212 | pathlen++; | ||
189 | while ((ent = readdir(dir)) != NULL) { | 213 | while ((ent = readdir(dir)) != NULL) { |
190 | if (ent->d_name[0] == '.') { | 214 | if (ent->d_name[0] == '.') { |
191 | if (ent->d_name[1] == '\0') | 215 | if (ent->d_name[1] == '\0') |
@@ -195,24 +219,18 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn) | |||
195 | if (!ctx.cfg.scan_hidden_path) | 219 | if (!ctx.cfg.scan_hidden_path) |
196 | continue; | 220 | continue; |
197 | } | 221 | } |
198 | buf = malloc(strlen(path) + strlen(ent->d_name) + 2); | 222 | strbuf_setlen(&pathbuf, pathlen); |
199 | if (!buf) { | 223 | strbuf_addstr(&pathbuf, ent->d_name); |
200 | fprintf(stderr, "Alloc error on %s: %s (%d)\n", | 224 | if (stat(pathbuf.buf, &st)) { |
201 | path, strerror(errno), errno); | ||
202 | exit(1); | ||
203 | } | ||
204 | sprintf(buf, "%s/%s", path, ent->d_name); | ||
205 | if (stat(buf, &st)) { | ||
206 | fprintf(stderr, "Error checking path %s: %s (%d)\n", | 225 | fprintf(stderr, "Error checking path %s: %s (%d)\n", |
207 | buf, strerror(errno), errno); | 226 | pathbuf.buf, strerror(errno), errno); |
208 | free(buf); | ||
209 | continue; | 227 | continue; |
210 | } | 228 | } |
211 | if (S_ISDIR(st.st_mode)) | 229 | if (S_ISDIR(st.st_mode)) |
212 | scan_path(base, buf, fn); | 230 | scan_path(base, pathbuf.buf, fn); |
213 | free(buf); | ||
214 | } | 231 | } |
215 | end: | 232 | end: |
233 | strbuf_release(&pathbuf); | ||
216 | closedir(dir); | 234 | closedir(dir); |
217 | } | 235 | } |
218 | 236 | ||
@@ -220,7 +238,7 @@ end: | |||
220 | 238 | ||
221 | void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) | 239 | void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) |
222 | { | 240 | { |
223 | char line[MAX_PATH * 2], *z; | 241 | struct strbuf line = STRBUF_INIT; |
224 | FILE *projects; | 242 | FILE *projects; |
225 | int err; | 243 | int err; |
226 | 244 | ||
@@ -230,19 +248,19 @@ void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn | |||
230 | projectsfile, strerror(errno), errno); | 248 | projectsfile, strerror(errno), errno); |
231 | return; | 249 | return; |
232 | } | 250 | } |
233 | while (fgets(line, sizeof(line), projects) != NULL) { | 251 | while (strbuf_getline(&line, projects, '\n') != EOF) { |
234 | for (z = &lastc(line); | 252 | if (!line.len) |
235 | strlen(line) && strchr("\n\r", *z); | 253 | continue; |
236 | z = &lastc(line)) | 254 | strbuf_insert(&line, 0, "/", 1); |
237 | *z = '\0'; | 255 | strbuf_insert(&line, 0, path, strlen(path)); |
238 | if (strlen(line)) | 256 | scan_path(path, line.buf, fn); |
239 | scan_path(path, fmt("%s/%s", path, line), fn); | ||
240 | } | 257 | } |
241 | if ((err = ferror(projects))) { | 258 | if ((err = ferror(projects))) { |
242 | fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", | 259 | fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", |
243 | projectsfile, strerror(err), err); | 260 | projectsfile, strerror(err), err); |
244 | } | 261 | } |
245 | fclose(projects); | 262 | fclose(projects); |
263 | strbuf_release(&line); | ||
246 | } | 264 | } |
247 | 265 | ||
248 | void scan_tree(const char *path, repo_config_fn fn) | 266 | void scan_tree(const char *path, repo_config_fn fn) |
@@ -243,15 +243,19 @@ static void print_commit(struct commit *commit, struct rev_info *revs) | |||
243 | cgit_free_commitinfo(info); | 243 | cgit_free_commitinfo(info); |
244 | } | 244 | } |
245 | 245 | ||
246 | static const char *disambiguate_ref(const char *ref) | 246 | static const char *disambiguate_ref(const char *ref, int *must_free_result) |
247 | { | 247 | { |
248 | unsigned char sha1[20]; | 248 | unsigned char sha1[20]; |
249 | const char *longref; | 249 | struct strbuf longref = STRBUF_INIT; |
250 | 250 | ||
251 | longref = fmt("refs/heads/%s", ref); | 251 | strbuf_addf(&longref, "refs/heads/%s", ref); |
252 | if (get_sha1(longref, sha1) == 0) | 252 | if (get_sha1(longref.buf, sha1) == 0) { |
253 | return longref; | 253 | *must_free_result = 1; |
254 | return strbuf_detach(&longref, NULL); | ||
255 | } | ||
254 | 256 | ||
257 | *must_free_result = 0; | ||
258 | strbuf_release(&longref); | ||
255 | return ref; | 259 | return ref; |
256 | } | 260 | } |
257 | 261 | ||
@@ -284,24 +288,26 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern | |||
284 | struct commit *commit; | 288 | struct commit *commit; |
285 | struct vector vec = VECTOR_INIT(char *); | 289 | struct vector vec = VECTOR_INIT(char *); |
286 | int i, columns = commit_graph ? 4 : 3; | 290 | int i, columns = commit_graph ? 4 : 3; |
287 | char *arg; | 291 | int must_free_tip = 0; |
292 | struct strbuf argbuf = STRBUF_INIT; | ||
288 | 293 | ||
289 | /* First argv is NULL */ | 294 | /* First argv is NULL */ |
290 | vector_push(&vec, NULL, 0); | 295 | vector_push(&vec, NULL, 0); |
291 | 296 | ||
292 | if (!tip) | 297 | if (!tip) |
293 | tip = ctx.qry.head; | 298 | tip = ctx.qry.head; |
294 | tip = disambiguate_ref(tip); | 299 | tip = disambiguate_ref(tip, &must_free_tip); |
295 | vector_push(&vec, &tip, 0); | 300 | vector_push(&vec, &tip, 0); |
296 | 301 | ||
297 | if (grep && pattern && *pattern) { | 302 | if (grep && pattern && *pattern) { |
298 | pattern = xstrdup(pattern); | 303 | pattern = xstrdup(pattern); |
299 | if (!strcmp(grep, "grep") || !strcmp(grep, "author") || | 304 | if (!strcmp(grep, "grep") || !strcmp(grep, "author") || |
300 | !strcmp(grep, "committer")) { | 305 | !strcmp(grep, "committer")) { |
301 | arg = fmt("--%s=%s", grep, pattern); | 306 | strbuf_addf(&argbuf, "--%s=%s", grep, pattern); |
302 | vector_push(&vec, &arg, 0); | 307 | vector_push(&vec, &argbuf.buf, 0); |
303 | } | 308 | } |
304 | if (!strcmp(grep, "range")) { | 309 | if (!strcmp(grep, "range")) { |
310 | char *arg; | ||
305 | /* Split the pattern at whitespace and add each token | 311 | /* Split the pattern at whitespace and add each token |
306 | * as a revision expression. Do not accept other | 312 | * as a revision expression. Do not accept other |
307 | * rev-list options. Also, replace the previously | 313 | * rev-list options. Also, replace the previously |
@@ -336,8 +342,8 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern | |||
336 | } | 342 | } |
337 | 343 | ||
338 | if (path) { | 344 | if (path) { |
339 | arg = "--"; | 345 | static const char *double_dash_arg = "--"; |
340 | vector_push(&vec, &arg, 0); | 346 | vector_push(&vec, &double_dash_arg, 0); |
341 | vector_push(&vec, &path, 0); | 347 | vector_push(&vec, &path, 0); |
342 | } | 348 | } |
343 | 349 | ||
@@ -430,4 +436,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern | |||
430 | ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); | 436 | ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); |
431 | html("</td></tr>\n"); | 437 | html("</td></tr>\n"); |
432 | } | 438 | } |
439 | |||
440 | /* If we allocated tip then it is safe to cast away const. */ | ||
441 | if (must_free_tip) | ||
442 | free((char*) tip); | ||
443 | strbuf_release(&argbuf); | ||
433 | } | 444 | } |
@@ -109,9 +109,9 @@ static int print_object(const unsigned char *sha1, const char *path) | |||
109 | static char *buildpath(const char *base, int baselen, const char *path) | 109 | static char *buildpath(const char *base, int baselen, const char *path) |
110 | { | 110 | { |
111 | if (path[0]) | 111 | if (path[0]) |
112 | return fmt("%.*s%s/", baselen, base, path); | 112 | return fmtalloc("%.*s%s/", baselen, base, path); |
113 | else | 113 | else |
114 | return fmt("%.*s/", baselen, base); | 114 | return fmtalloc("%.*s/", baselen, base); |
115 | } | 115 | } |
116 | 116 | ||
117 | static void print_dir(const unsigned char *sha1, const char *base, | 117 | static void print_dir(const unsigned char *sha1, const char *base, |
@@ -142,6 +142,7 @@ static void print_dir(const unsigned char *sha1, const char *base, | |||
142 | fullpath); | 142 | fullpath); |
143 | html("</li>\n"); | 143 | html("</li>\n"); |
144 | } | 144 | } |
145 | free(fullpath); | ||
145 | } | 146 | } |
146 | 147 | ||
147 | static void print_dir_entry(const unsigned char *sha1, const char *base, | 148 | static void print_dir_entry(const unsigned char *sha1, const char *base, |
@@ -159,6 +160,7 @@ static void print_dir_entry(const unsigned char *sha1, const char *base, | |||
159 | cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, | 160 | cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, |
160 | fullpath); | 161 | fullpath); |
161 | html("</li>\n"); | 162 | html("</li>\n"); |
163 | free(fullpath); | ||
162 | } | 164 | } |
163 | 165 | ||
164 | static void print_dir_tail(void) | 166 | static void print_dir_tail(void) |
@@ -99,7 +99,7 @@ static void print_tag_header() | |||
99 | static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) | 99 | static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) |
100 | { | 100 | { |
101 | const struct cgit_snapshot_format* f; | 101 | const struct cgit_snapshot_format* f; |
102 | char *filename; | 102 | struct strbuf filename = STRBUF_INIT; |
103 | const char *basename; | 103 | const char *basename; |
104 | int free_ref = 0; | 104 | int free_ref = 0; |
105 | 105 | ||
@@ -111,7 +111,7 @@ static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) | |||
111 | if ((ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1])) | 111 | if ((ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1])) |
112 | ref++; | 112 | ref++; |
113 | if (isdigit(ref[0])) { | 113 | if (isdigit(ref[0])) { |
114 | ref = xstrdup(fmt("%s-%s", basename, ref)); | 114 | ref = fmtalloc("%s-%s", basename, ref); |
115 | free_ref = 1; | 115 | free_ref = 1; |
116 | } | 116 | } |
117 | } | 117 | } |
@@ -119,13 +119,15 @@ static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) | |||
119 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 119 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
120 | if (!(repo->snapshots & f->bit)) | 120 | if (!(repo->snapshots & f->bit)) |
121 | continue; | 121 | continue; |
122 | filename = fmt("%s%s", ref, f->suffix); | 122 | strbuf_reset(&filename); |
123 | cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename); | 123 | strbuf_addf(&filename, "%s%s", ref, f->suffix); |
124 | cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, filename.buf); | ||
124 | html(" "); | 125 | html(" "); |
125 | } | 126 | } |
126 | 127 | ||
127 | if (free_ref) | 128 | if (free_ref) |
128 | free((char *)ref); | 129 | free((char *)ref); |
130 | strbuf_release(&filename); | ||
129 | } | 131 | } |
130 | 132 | ||
131 | static int print_tag(struct refinfo *ref) | 133 | static int print_tag(struct refinfo *ref) |
diff --git a/ui-repolist.c b/ui-repolist.c index 76fe71a..47ca997 100644 --- a/ui-repolist.c +++ b/ui-repolist.c | |||
@@ -33,7 +33,7 @@ static time_t read_agefile(char *path) | |||
33 | 33 | ||
34 | static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) | 34 | static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) |
35 | { | 35 | { |
36 | char *path; | 36 | struct strbuf path = STRBUF_INIT; |
37 | struct stat s; | 37 | struct stat s; |
38 | struct cgit_repo *r = (struct cgit_repo *)repo; | 38 | struct cgit_repo *r = (struct cgit_repo *)repo; |
39 | 39 | ||
@@ -41,32 +41,36 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) | |||
41 | *mtime = repo->mtime; | 41 | *mtime = repo->mtime; |
42 | return 1; | 42 | return 1; |
43 | } | 43 | } |
44 | path = fmt("%s/%s", repo->path, ctx.cfg.agefile); | 44 | strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile); |
45 | if (stat(path, &s) == 0) { | 45 | if (stat(path.buf, &s) == 0) { |
46 | *mtime = read_agefile(path); | 46 | *mtime = read_agefile(path.buf); |
47 | if (*mtime) { | 47 | if (*mtime) { |
48 | r->mtime = *mtime; | 48 | r->mtime = *mtime; |
49 | return 1; | 49 | goto end; |
50 | } | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
53 | path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch ? | 53 | strbuf_reset(&path); |
54 | repo->defbranch : "master"); | 54 | strbuf_addf(&path, "%s/refs/heads/%s", repo->path, |
55 | if (stat(path, &s) == 0) { | 55 | repo->defbranch ? repo->defbranch : "master"); |
56 | if (stat(path.buf, &s) == 0) { | ||
56 | *mtime = s.st_mtime; | 57 | *mtime = s.st_mtime; |
57 | r->mtime = *mtime; | 58 | r->mtime = *mtime; |
58 | return 1; | 59 | goto end; |
59 | } | 60 | } |
60 | 61 | ||
61 | path = fmt("%s/%s", repo->path, "packed-refs"); | 62 | strbuf_reset(&path); |
62 | if (stat(path, &s) == 0) { | 63 | strbuf_addf(&path, "%s/%s", repo->path, "packed-refs"); |
64 | if (stat(path.buf, &s) == 0) { | ||
63 | *mtime = s.st_mtime; | 65 | *mtime = s.st_mtime; |
64 | r->mtime = *mtime; | 66 | r->mtime = *mtime; |
65 | return 1; | 67 | goto end; |
66 | } | 68 | } |
67 | 69 | ||
68 | *mtime = 0; | 70 | *mtime = 0; |
69 | r->mtime = *mtime; | 71 | r->mtime = *mtime; |
72 | end: | ||
73 | strbuf_release(&path); | ||
70 | return (r->mtime != 0); | 74 | return (r->mtime != 0); |
71 | } | 75 | } |
72 | 76 | ||
diff --git a/ui-shared.c b/ui-shared.c index b93b77a..519eef7 100644 --- a/ui-shared.c +++ b/ui-shared.c | |||
@@ -62,7 +62,7 @@ const char *cgit_hosturl() | |||
62 | return NULL; | 62 | return NULL; |
63 | if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) | 63 | if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) |
64 | return ctx.env.server_name; | 64 | return ctx.env.server_name; |
65 | return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port)); | 65 | return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port); |
66 | } | 66 | } |
67 | 67 | ||
68 | const char *cgit_rooturl() | 68 | const char *cgit_rooturl() |
@@ -75,31 +75,30 @@ const char *cgit_rooturl() | |||
75 | 75 | ||
76 | char *cgit_repourl(const char *reponame) | 76 | char *cgit_repourl(const char *reponame) |
77 | { | 77 | { |
78 | if (ctx.cfg.virtual_root) { | 78 | if (ctx.cfg.virtual_root) |
79 | return fmt("%s%s/", ctx.cfg.virtual_root, reponame); | 79 | return fmtalloc("%s%s/", ctx.cfg.virtual_root, reponame); |
80 | } else { | 80 | else |
81 | return fmt("?r=%s", reponame); | 81 | return fmtalloc("?r=%s", reponame); |
82 | } | ||
83 | } | 82 | } |
84 | 83 | ||
85 | char *cgit_fileurl(const char *reponame, const char *pagename, | 84 | char *cgit_fileurl(const char *reponame, const char *pagename, |
86 | const char *filename, const char *query) | 85 | const char *filename, const char *query) |
87 | { | 86 | { |
88 | char *tmp; | 87 | struct strbuf sb = STRBUF_INIT; |
89 | char *delim; | 88 | char *delim; |
90 | 89 | ||
91 | if (ctx.cfg.virtual_root) { | 90 | if (ctx.cfg.virtual_root) { |
92 | tmp = fmt("%s%s/%s/%s", ctx.cfg.virtual_root, reponame, | 91 | strbuf_addf(&sb, "%s%s/%s/%s", ctx.cfg.virtual_root, reponame, |
93 | pagename, (filename ? filename:"")); | 92 | pagename, (filename ? filename:"")); |
94 | delim = "?"; | 93 | delim = "?"; |
95 | } else { | 94 | } else { |
96 | tmp = fmt("?url=%s/%s/%s", reponame, pagename, | 95 | strbuf_addf(&sb, "?url=%s/%s/%s", reponame, pagename, |
97 | (filename ? filename : "")); | 96 | (filename ? filename : "")); |
98 | delim = "&"; | 97 | delim = "&"; |
99 | } | 98 | } |
100 | if (query) | 99 | if (query) |
101 | tmp = fmt("%s%s%s", tmp, delim, query); | 100 | strbuf_addf(&sb, "%s%s", delim, query); |
102 | return tmp; | 101 | return strbuf_detach(&sb, NULL); |
103 | } | 102 | } |
104 | 103 | ||
105 | char *cgit_pageurl(const char *reponame, const char *pagename, | 104 | char *cgit_pageurl(const char *reponame, const char *pagename, |
@@ -548,21 +547,21 @@ void cgit_submodule_link(const char *class, char *path, const char *rev) | |||
548 | htmlf("class='%s' ", class); | 547 | htmlf("class='%s' ", class); |
549 | html("href='"); | 548 | html("href='"); |
550 | if (item) { | 549 | if (item) { |
551 | html_attr(fmt(item->util, rev)); | 550 | html_attrf(item->util, rev); |
552 | } else if (ctx.repo->module_link) { | 551 | } else if (ctx.repo->module_link) { |
553 | dir = strrchr(path, '/'); | 552 | dir = strrchr(path, '/'); |
554 | if (dir) | 553 | if (dir) |
555 | dir++; | 554 | dir++; |
556 | else | 555 | else |
557 | dir = path; | 556 | dir = path; |
558 | html_attr(fmt(ctx.repo->module_link, dir, rev)); | 557 | html_attrf(ctx.repo->module_link, dir, rev); |
559 | } else { | 558 | } else { |
560 | html("#"); | 559 | html("#"); |
561 | } | 560 | } |
562 | html("'>"); | 561 | html("'>"); |
563 | html_txt(path); | 562 | html_txt(path); |
564 | html("</a>"); | 563 | html("</a>"); |
565 | html_txt(fmt(" @ %.7s", rev)); | 564 | html_txtf(" @ %.7s", rev); |
566 | if (item && tail) | 565 | if (item && tail) |
567 | path[len - 1] = tail; | 566 | path[len - 1] = tail; |
568 | } | 567 | } |
@@ -678,12 +677,16 @@ void cgit_print_docstart(struct cgit_context *ctx) | |||
678 | html("'/>\n"); | 677 | html("'/>\n"); |
679 | } | 678 | } |
680 | if (host && ctx->repo && ctx->qry.head) { | 679 | if (host && ctx->repo && ctx->qry.head) { |
680 | struct strbuf sb = STRBUF_INIT; | ||
681 | strbuf_addf(&sb, "h=%s", ctx->qry.head); | ||
682 | |||
681 | html("<link rel='alternate' title='Atom feed' href='"); | 683 | html("<link rel='alternate' title='Atom feed' href='"); |
682 | html(cgit_httpscheme()); | 684 | html(cgit_httpscheme()); |
683 | html_attr(cgit_hosturl()); | 685 | html_attr(cgit_hosturl()); |
684 | html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, | 686 | html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, |
685 | fmt("h=%s", ctx->qry.head))); | 687 | sb.buf)); |
686 | html("' type='application/atom+xml'/>\n"); | 688 | html("' type='application/atom+xml'/>\n"); |
689 | strbuf_release(&sb); | ||
687 | } | 690 | } |
688 | if (ctx->cfg.head_include) | 691 | if (ctx->cfg.head_include) |
689 | html_include(ctx->cfg.head_include); | 692 | html_include(ctx->cfg.head_include); |
@@ -725,13 +728,14 @@ static int print_branch_option(const char *refname, const unsigned char *sha1, | |||
725 | void cgit_add_hidden_formfields(int incl_head, int incl_search, | 728 | void cgit_add_hidden_formfields(int incl_head, int incl_search, |
726 | const char *page) | 729 | const char *page) |
727 | { | 730 | { |
728 | char *url; | ||
729 | |||
730 | if (!ctx.cfg.virtual_root) { | 731 | if (!ctx.cfg.virtual_root) { |
731 | url = fmt("%s/%s", ctx.qry.repo, page); | 732 | struct strbuf url = STRBUF_INIT; |
733 | |||
734 | strbuf_addf(&url, "%s/%s", ctx.qry.repo, page); | ||
732 | if (ctx.qry.vpath) | 735 | if (ctx.qry.vpath) |
733 | url = fmt("%s/%s", url, ctx.qry.vpath); | 736 | strbuf_addf(&url, "/%s", ctx.qry.vpath); |
734 | html_hidden("url", url); | 737 | html_hidden("url", url.buf); |
738 | strbuf_release(&url); | ||
735 | } | 739 | } |
736 | 740 | ||
737 | if (incl_head && ctx.qry.head && ctx.repo->defbranch && | 741 | if (incl_head && ctx.qry.head && ctx.repo->defbranch && |
@@ -926,20 +930,23 @@ void cgit_print_snapshot_links(const char *repo, const char *head, | |||
926 | const char *hex, int snapshots) | 930 | const char *hex, int snapshots) |
927 | { | 931 | { |
928 | const struct cgit_snapshot_format* f; | 932 | const struct cgit_snapshot_format* f; |
929 | char *prefix; | 933 | struct strbuf filename = STRBUF_INIT; |
930 | char *filename; | 934 | size_t prefixlen; |
931 | unsigned char sha1[20]; | 935 | unsigned char sha1[20]; |
932 | 936 | ||
933 | if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 && | 937 | if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 && |
934 | (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1])) | 938 | (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1])) |
935 | hex++; | 939 | hex++; |
936 | prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex)); | 940 | strbuf_addf(&filename, "%s-%s", cgit_repobasename(repo), hex); |
941 | prefixlen = filename.len; | ||
937 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 942 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
938 | if (!(snapshots & f->bit)) | 943 | if (!(snapshots & f->bit)) |
939 | continue; | 944 | continue; |
940 | filename = fmt("%s%s", prefix, f->suffix); | 945 | strbuf_setlen(&filename, prefixlen); |
941 | cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename); | 946 | strbuf_addstr(&filename, f->suffix); |
947 | cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, | ||
948 | filename.buf); | ||
942 | html("<br/>"); | 949 | html("<br/>"); |
943 | } | 950 | } |
944 | free(prefix); | 951 | strbuf_release(&filename); |
945 | } | 952 | } |
diff --git a/ui-snapshot.c b/ui-snapshot.c index a47884e..8e76977 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c | |||
@@ -15,14 +15,33 @@ | |||
15 | static int write_archive_type(const char *format, const char *hex, const char *prefix) | 15 | static int write_archive_type(const char *format, const char *hex, const char *prefix) |
16 | { | 16 | { |
17 | struct argv_array argv = ARGV_ARRAY_INIT; | 17 | struct argv_array argv = ARGV_ARRAY_INIT; |
18 | const char **nargv; | ||
19 | int result; | ||
18 | argv_array_push(&argv, "snapshot"); | 20 | argv_array_push(&argv, "snapshot"); |
19 | argv_array_push(&argv, format); | 21 | argv_array_push(&argv, format); |
20 | if (prefix) { | 22 | if (prefix) { |
23 | struct strbuf buf = STRBUF_INIT; | ||
24 | strbuf_addstr(&buf, prefix); | ||
25 | strbuf_addch(&buf, '/'); | ||
21 | argv_array_push(&argv, "--prefix"); | 26 | argv_array_push(&argv, "--prefix"); |
22 | argv_array_push(&argv, fmt("%s/", prefix)); | 27 | argv_array_push(&argv, buf.buf); |
28 | strbuf_release(&buf); | ||
23 | } | 29 | } |
24 | argv_array_push(&argv, hex); | 30 | argv_array_push(&argv, hex); |
25 | return write_archive(argv.argc, argv.argv, NULL, 1, NULL, 0); | 31 | /* |
32 | * Now we need to copy the pointers to arguments into a new | ||
33 | * structure because write_archive will rearrange its arguments | ||
34 | * which may result in duplicated/missing entries causing leaks | ||
35 | * or double-frees in argv_array_clear. | ||
36 | */ | ||
37 | nargv = xmalloc(sizeof(char *) * (argv.argc + 1)); | ||
38 | /* argv_array guarantees a trailing NULL entry. */ | ||
39 | memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1)); | ||
40 | |||
41 | result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0); | ||
42 | argv_array_clear(&argv); | ||
43 | free(nargv); | ||
44 | return result; | ||
26 | } | 45 | } |
27 | 46 | ||
28 | static int write_tar_archive(const char *hex, const char *prefix) | 47 | static int write_tar_archive(const char *hex, const char *prefix) |
@@ -129,29 +148,36 @@ static const char *get_ref_from_filename(const char *url, const char *filename, | |||
129 | { | 148 | { |
130 | const char *reponame; | 149 | const char *reponame; |
131 | unsigned char sha1[20]; | 150 | unsigned char sha1[20]; |
132 | char *snapshot; | 151 | struct strbuf snapshot = STRBUF_INIT; |
152 | int result = 1; | ||
133 | 153 | ||
134 | snapshot = xstrdup(filename); | 154 | strbuf_addstr(&snapshot, filename); |
135 | snapshot[strlen(snapshot) - strlen(format->suffix)] = '\0'; | 155 | strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix)); |
136 | 156 | ||
137 | if (get_sha1(snapshot, sha1) == 0) | 157 | if (get_sha1(snapshot.buf, sha1) == 0) |
138 | return snapshot; | 158 | goto out; |
139 | 159 | ||
140 | reponame = cgit_repobasename(url); | 160 | reponame = cgit_repobasename(url); |
141 | if (prefixcmp(snapshot, reponame) == 0) { | 161 | if (prefixcmp(snapshot.buf, reponame) == 0) { |
142 | snapshot += strlen(reponame); | 162 | const char *new_start = snapshot.buf; |
143 | while (snapshot && (*snapshot == '-' || *snapshot == '_')) | 163 | new_start += strlen(reponame); |
144 | snapshot++; | 164 | while (new_start && (*new_start == '-' || *new_start == '_')) |
165 | new_start++; | ||
166 | strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0); | ||
145 | } | 167 | } |
146 | 168 | ||
147 | if (get_sha1(snapshot, sha1) == 0) | 169 | if (get_sha1(snapshot.buf, sha1) == 0) |
148 | return snapshot; | 170 | goto out; |
149 | 171 | ||
150 | snapshot = fmt("v%s", snapshot); | 172 | strbuf_insert(&snapshot, 0, "v", 1); |
151 | if (get_sha1(snapshot, sha1) == 0) | 173 | if (get_sha1(snapshot.buf, sha1) == 0) |
152 | return snapshot; | 174 | goto out; |
153 | 175 | ||
154 | return NULL; | 176 | result = 0; |
177 | strbuf_release(&snapshot); | ||
178 | |||
179 | out: | ||
180 | return result ? strbuf_detach(&snapshot, NULL) : NULL; | ||
155 | } | 181 | } |
156 | 182 | ||
157 | __attribute__((format (printf, 1, 2))) | 183 | __attribute__((format (printf, 1, 2))) |
diff --git a/ui-summary.c b/ui-summary.c index bd123ef..f965b32 100644 --- a/ui-summary.c +++ b/ui-summary.c | |||
@@ -17,6 +17,7 @@ | |||
17 | static void print_url(char *base, char *suffix) | 17 | static void print_url(char *base, char *suffix) |
18 | { | 18 | { |
19 | int columns = 3; | 19 | int columns = 3; |
20 | struct strbuf basebuf = STRBUF_INIT; | ||
20 | 21 | ||
21 | if (ctx.repo->enable_log_filecount) | 22 | if (ctx.repo->enable_log_filecount) |
22 | columns++; | 23 | columns++; |
@@ -25,13 +26,16 @@ static void print_url(char *base, char *suffix) | |||
25 | 26 | ||
26 | if (!base || !*base) | 27 | if (!base || !*base) |
27 | return; | 28 | return; |
28 | if (suffix && *suffix) | 29 | if (suffix && *suffix) { |
29 | base = fmt("%s/%s", base, suffix); | 30 | strbuf_addf(&basebuf, "%s/%s", base, suffix); |
31 | base = basebuf.buf; | ||
32 | } | ||
30 | htmlf("<tr><td colspan='%d'><a href='", columns); | 33 | htmlf("<tr><td colspan='%d'><a href='", columns); |
31 | html_url_path(base); | 34 | html_url_path(base); |
32 | html("'>"); | 35 | html("'>"); |
33 | html_txt(base); | 36 | html_txt(base); |
34 | html("</a></td></tr>\n"); | 37 | html("</a></td></tr>\n"); |
38 | strbuf_release(&basebuf); | ||
35 | } | 39 | } |
36 | 40 | ||
37 | static void print_urls(char *txt, char *suffix) | 41 | static void print_urls(char *txt, char *suffix) |
@@ -112,8 +116,8 @@ void cgit_print_repo_readme(char *path) | |||
112 | 116 | ||
113 | /* Prepend repo path to relative readme path unless tracked. */ | 117 | /* Prepend repo path to relative readme path unless tracked. */ |
114 | if (!ref && *ctx.repo->readme != '/') | 118 | if (!ref && *ctx.repo->readme != '/') |
115 | ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, | 119 | ctx.repo->readme = fmtalloc("%s/%s", ctx.repo->path, |
116 | ctx.repo->readme)); | 120 | ctx.repo->readme); |
117 | 121 | ||
118 | /* If a subpath is specified for the about page, make it relative | 122 | /* If a subpath is specified for the about page, make it relative |
119 | * to the directory containing the configured readme. | 123 | * to the directory containing the configured readme. |
@@ -41,6 +41,7 @@ static void print_download_links(char *revname) | |||
41 | 41 | ||
42 | void cgit_print_tag(char *revname) | 42 | void cgit_print_tag(char *revname) |
43 | { | 43 | { |
44 | struct strbuf fullref = STRBUF_INIT; | ||
44 | unsigned char sha1[20]; | 45 | unsigned char sha1[20]; |
45 | struct object *obj; | 46 | struct object *obj; |
46 | struct tag *tag; | 47 | struct tag *tag; |
@@ -49,20 +50,21 @@ void cgit_print_tag(char *revname) | |||
49 | if (!revname) | 50 | if (!revname) |
50 | revname = ctx.qry.head; | 51 | revname = ctx.qry.head; |
51 | 52 | ||
52 | if (get_sha1(fmt("refs/tags/%s", revname), sha1)) { | 53 | strbuf_addf(&fullref, "refs/tags/%s", revname); |
54 | if (get_sha1(fullref.buf, sha1)) { | ||
53 | cgit_print_error("Bad tag reference: %s", revname); | 55 | cgit_print_error("Bad tag reference: %s", revname); |
54 | return; | 56 | goto cleanup; |
55 | } | 57 | } |
56 | obj = parse_object(sha1); | 58 | obj = parse_object(sha1); |
57 | if (!obj) { | 59 | if (!obj) { |
58 | cgit_print_error("Bad object id: %s", sha1_to_hex(sha1)); | 60 | cgit_print_error("Bad object id: %s", sha1_to_hex(sha1)); |
59 | return; | 61 | goto cleanup; |
60 | } | 62 | } |
61 | if (obj->type == OBJ_TAG) { | 63 | if (obj->type == OBJ_TAG) { |
62 | tag = lookup_tag(sha1); | 64 | tag = lookup_tag(sha1); |
63 | if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { | 65 | if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { |
64 | cgit_print_error("Bad tag object: %s", revname); | 66 | cgit_print_error("Bad tag object: %s", revname); |
65 | return; | 67 | goto cleanup; |
66 | } | 68 | } |
67 | html("<table class='commit-info'>\n"); | 69 | html("<table class='commit-info'>\n"); |
68 | htmlf("<tr><td>tag name</td><td>"); | 70 | htmlf("<tr><td>tag name</td><td>"); |
@@ -101,5 +103,7 @@ void cgit_print_tag(char *revname) | |||
101 | print_download_links(revname); | 103 | print_download_links(revname); |
102 | html("</table>\n"); | 104 | html("</table>\n"); |
103 | } | 105 | } |
104 | return; | 106 | |
107 | cleanup: | ||
108 | strbuf_release(&fullref); | ||
105 | } | 109 | } |
@@ -129,14 +129,14 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen, | |||
129 | { | 129 | { |
130 | struct walk_tree_context *walk_tree_ctx = cbdata; | 130 | struct walk_tree_context *walk_tree_ctx = cbdata; |
131 | char *name; | 131 | char *name; |
132 | char *fullpath; | 132 | struct strbuf fullpath = STRBUF_INIT; |
133 | char *class; | 133 | struct strbuf class = STRBUF_INIT; |
134 | enum object_type type; | 134 | enum object_type type; |
135 | unsigned long size = 0; | 135 | unsigned long size = 0; |
136 | 136 | ||
137 | name = xstrdup(pathname); | 137 | name = xstrdup(pathname); |
138 | fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", | 138 | strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", |
139 | ctx.qry.path ? "/" : "", name); | 139 | ctx.qry.path ? "/" : "", name); |
140 | 140 | ||
141 | if (!S_ISGITLINK(mode)) { | 141 | if (!S_ISGITLINK(mode)) { |
142 | type = sha1_object_info(sha1, &size); | 142 | type = sha1_object_info(sha1, &size); |
@@ -152,33 +152,34 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen, | |||
152 | cgit_print_filemode(mode); | 152 | cgit_print_filemode(mode); |
153 | html("</td><td>"); | 153 | html("</td><td>"); |
154 | if (S_ISGITLINK(mode)) { | 154 | if (S_ISGITLINK(mode)) { |
155 | cgit_submodule_link("ls-mod", fullpath, sha1_to_hex(sha1)); | 155 | cgit_submodule_link("ls-mod", fullpath.buf, sha1_to_hex(sha1)); |
156 | } else if (S_ISDIR(mode)) { | 156 | } else if (S_ISDIR(mode)) { |
157 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, | 157 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, |
158 | walk_tree_ctx->curr_rev, fullpath); | 158 | walk_tree_ctx->curr_rev, fullpath.buf); |
159 | } else { | 159 | } else { |
160 | class = strrchr(name, '.'); | 160 | char *ext = strrchr(name, '.'); |
161 | if (class != NULL) { | 161 | strbuf_addstr(&class, "ls-blob"); |
162 | class = fmt("ls-blob %s", class + 1); | 162 | if (ext) |
163 | } else | 163 | strbuf_addf(&class, " %s", ext + 1); |
164 | class = "ls-blob"; | 164 | cgit_tree_link(name, NULL, class.buf, ctx.qry.head, |
165 | cgit_tree_link(name, NULL, class, ctx.qry.head, | 165 | walk_tree_ctx->curr_rev, fullpath.buf); |
166 | walk_tree_ctx->curr_rev, fullpath); | ||
167 | } | 166 | } |
168 | htmlf("</td><td class='ls-size'>%li</td>", size); | 167 | htmlf("</td><td class='ls-size'>%li</td>", size); |
169 | 168 | ||
170 | html("<td>"); | 169 | html("<td>"); |
171 | cgit_log_link("log", NULL, "button", ctx.qry.head, | 170 | cgit_log_link("log", NULL, "button", ctx.qry.head, |
172 | walk_tree_ctx->curr_rev, fullpath, 0, NULL, NULL, | 171 | walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL, |
173 | ctx.qry.showmsg); | 172 | ctx.qry.showmsg); |
174 | if (ctx.repo->max_stats) | 173 | if (ctx.repo->max_stats) |
175 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, | 174 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, |
176 | fullpath); | 175 | fullpath.buf); |
177 | if (!S_ISGITLINK(mode)) | 176 | if (!S_ISGITLINK(mode)) |
178 | cgit_plain_link("plain", NULL, "button", ctx.qry.head, | 177 | cgit_plain_link("plain", NULL, "button", ctx.qry.head, |
179 | walk_tree_ctx->curr_rev, fullpath); | 178 | walk_tree_ctx->curr_rev, fullpath.buf); |
180 | html("</td></tr>\n"); | 179 | html("</td></tr>\n"); |
181 | free(name); | 180 | free(name); |
181 | strbuf_release(&fullpath); | ||
182 | strbuf_release(&class); | ||
182 | return 0; | 183 | return 0; |
183 | } | 184 | } |
184 | 185 | ||