about summary refs log tree commit diff stats
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(&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