aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar John Keeping <john@keeping.me.uk>2013-04-06 18:28:57 (JST)
committerGravatar Jason A. Donenfeld <Jason@zx2c4.com>2013-04-08 23:12:52 (JST)
commitfb3655df3bf85bd405c5921bbd4b3a54c705c839 (patch)
tree419a962a0b82f5ba3023791549044ff462229250
parent42d5476f258e7909682f1b611da00d64507d45c6 (diff)
downloadcgit-fb3655df3bf85bd405c5921bbd4b3a54c705c839.zip
cgit-fb3655df3bf85bd405c5921bbd4b3a54c705c839.tar.gz
use struct strbuf instead of static buffers
Use "struct strbuf" from Git to remove the limit on file path length. Notes on scan-tree: This is slightly involved since I decided to pass the strbuf into add_repo() and modify if whenever a new file name is required, which should avoid any extra allocations within that function. The pattern there is to append the filename, use it and then reset the buffer to its original length (retaining a trailing '/'). Notes on ui-snapshot: Since write_archive modifies the argv array passed to it we copy the argv_array values into a new array of char* and then free the original argv_array structure and the new array without worrying about what the values now look like. Signed-off-by: John Keeping <john@keeping.me.uk>
-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;