aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cache.c57
-rw-r--r--cgit.c72
-rw-r--r--scan-tree.c160
-rw-r--r--ui-log.c33
-rw-r--r--ui-plain.c6
-rw-r--r--ui-refs.c10
-rw-r--r--ui-repolist.c28
-rw-r--r--ui-shared.c63
-rw-r--r--ui-snapshot.c60
-rw-r--r--ui-summary.c12
-rw-r--r--ui-tag.c14
-rw-r--r--ui-tree.c33
12 files changed, 305 insertions, 243 deletions
diff --git a/cache.c b/cache.c
index 3127fc2..c1d777b 100644
--- a/cache.c
+++ b/cache.c
@@ -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}
diff --git a/cgit.c b/cgit.c
index 4e51283..f73c7b0 100644
--- a/cgit.c
+++ b/cgit.c
@@ -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)
577static char *build_snapshot_setting(int bitmap) 577static 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
597static char *get_first_line(char *txt) 592static 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 */
662static int generate_cached_repolist(const char *path, const char *cached_rc) 657static 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; 686out:
687 strbuf_release(&locked_rc);
688 return result;
690} 689}
691 690
692static void process_cached_repolist(const char *path) 691static 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));
733out:
734 strbuf_release(&cached_rc);
734} 735}
735 736
736static void cgit_parse_args(int argc, const char **argv) 737static void cgit_parse_args(int argc, const char **argv)
@@ -812,7 +813,6 @@ static int calc_ttl()
812int main(int argc, const char **argv) 813int 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 */
18static int is_git_dir(const char *path) 16static 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;
44out:
45 strbuf_release(&pathbuf);
46 return result;
47} 47}
48 48
49struct cgit_repo *repo; 49struct 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
78static void add_repo(const char *base, const char *path, repo_config_fn fn) 78static 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
169static void scan_path(const char *base, const char *path, repo_config_fn fn) 184static 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 }
215end: 232end:
233 strbuf_release(&pathbuf);
216 closedir(dir); 234 closedir(dir);
217} 235}
218 236
@@ -220,7 +238,7 @@ end:
220 238
221void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) 239void 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
248void scan_tree(const char *path, repo_config_fn fn) 266void scan_tree(const char *path, repo_config_fn fn)
diff --git a/ui-log.c b/ui-log.c
index 8592843..93af0ce 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -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
246static const char *disambiguate_ref(const char *ref) 246static 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}
diff --git a/ui-plain.c b/ui-plain.c
index 6b0d84b..9c86542 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -109,9 +109,9 @@ static int print_object(const unsigned char *sha1, const char *path)
109static char *buildpath(const char *base, int baselen, const char *path) 109static 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
117static void print_dir(const unsigned char *sha1, const char *base, 117static 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
147static void print_dir_entry(const unsigned char *sha1, const char *base, 148static 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
164static void print_dir_tail(void) 166static void print_dir_tail(void)
diff --git a/ui-refs.c b/ui-refs.c
index 7406478..3fbaad0 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -99,7 +99,7 @@ static void print_tag_header()
99static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) 99static 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("&nbsp;&nbsp;"); 125 html("&nbsp;&nbsp;");
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
131static int print_tag(struct refinfo *ref) 133static 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
34static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) 34static 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;
72end:
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
68const char *cgit_rooturl() 68const char *cgit_rooturl()
@@ -75,31 +75,30 @@ const char *cgit_rooturl()
75 75
76char *cgit_repourl(const char *reponame) 76char *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
85char *cgit_fileurl(const char *reponame, const char *pagename, 84char *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(