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;
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(&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 = "&amp;"; 97 delim = "&amp;";
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
105char *cgit_pageurl(const char *reponame, const char *pagename, 104char *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,
725void cgit_add_hidden_formfields(int incl_head, int incl_search, 728void 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 @@
15static int write_archive_type(const char *format, const char *hex, const char *prefix) 15static 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
28static int write_tar_archive(const char *hex, const char *prefix) 47static 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
179out:
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 @@
17static void print_url(char *base, char *suffix) 17static 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
37static void print_urls(char *txt, char *suffix) 41static 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.
diff --git a/ui-tag.c b/ui-tag.c
index 397e15b..aea7958 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -41,6 +41,7 @@ static void print_download_links(char *revname)
41 41
42void cgit_print_tag(char *revname) 42void 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
107cleanup:
108 strbuf_release(&fullref);
105} 109}
diff --git a/ui-tree.c b/ui-tree.c
index aebe145..aa5dee9 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -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