diff options
| -rw-r--r-- | cache.c | 57 | ||||
| -rw-r--r-- | cgit.c | 72 | ||||
| -rw-r--r-- | scan-tree.c | 160 | ||||
| -rw-r--r-- | ui-log.c | 33 | ||||
| -rw-r--r-- | ui-plain.c | 6 | ||||
| -rw-r--r-- | ui-refs.c | 10 | ||||
| -rw-r--r-- | ui-repolist.c | 28 | ||||
| -rw-r--r-- | ui-shared.c | 63 | ||||
| -rw-r--r-- | ui-snapshot.c | 60 | ||||
| -rw-r--r-- | ui-summary.c | 12 | ||||
| -rw-r--r-- | ui-tag.c | 14 | ||||
| -rw-r--r-- | ui-tree.c | 33 |
12 files changed, 305 insertions, 243 deletions
| @@ -312,9 +312,9 @@ int cache_process(int size, const char *path, const char *key, int ttl, | |||
| 312 | cache_fill_fn fn, void *cbdata) | 312 | cache_fill_fn fn, void *cbdata) |
| 313 | { | 313 | { |
| 314 | unsigned long hash; | 314 | unsigned long hash; |
| 315 | int len, i; | 315 | int i; |
| 316 | char filename[1024]; | 316 | struct strbuf filename = STRBUF_INIT; |
| 317 | char lockname[1024 + 5]; /* 5 = ".lock" */ | 317 | struct strbuf lockname = STRBUF_INIT; |
| 318 | struct cache_slot slot; | 318 | struct cache_slot slot; |
| 319 | 319 | ||
| 320 | /* If the cache is disabled, just generate the content */ | 320 | /* If the cache is disabled, just generate the content */ |
| @@ -329,32 +329,22 @@ int cache_process(int size, const char *path, const char *key, int ttl, | |||
| 329 | fn(cbdata); | 329 | fn(cbdata); |
| 330 | return 0; | 330 | return 0; |
| 331 | } | 331 | } |
| 332 | len = strlen(path); | ||
| 333 | if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */ | ||
| 334 | cache_log("[cgit] Cache path too long, caching is disabled: %s\n", | ||
| 335 | path); | ||
| 336 | fn(cbdata); | ||
| 337 | return 0; | ||
| 338 | } | ||
| 339 | if (!key) | 332 | if (!key) |
| 340 | key = ""; | 333 | key = ""; |
| 341 | hash = hash_str(key) % size; | 334 | hash = hash_str(key) % size; |
| 342 | strcpy(filename, path); | 335 | strbuf_addstr(&filename, path); |
| 343 | if (filename[len - 1] != '/') | 336 | strbuf_ensure_end(&filename, '/'); |
| 344 | filename[len++] = '/'; | ||
| 345 | for (i = 0; i < 8; i++) { | 337 | for (i = 0; i < 8; i++) { |
| 346 | sprintf(filename + len++, "%x", | 338 | strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf)); |
| 347 | (unsigned char)(hash & 0xf)); | ||
| 348 | hash >>= 4; | 339 | hash >>= 4; |
| 349 | } | 340 | } |
| 350 | filename[len] = '\0'; | 341 | strbuf_addbuf(&lockname, &filename); |
| 351 | strcpy(lockname, filename); | 342 | strbuf_addstr(&lockname, ".lock"); |
| 352 | strcpy(lockname + len, ".lock"); | ||
| 353 | slot.fn = fn; | 343 | slot.fn = fn; |
| 354 | slot.cbdata = cbdata; | 344 | slot.cbdata = cbdata; |
| 355 | slot.ttl = ttl; | 345 | slot.ttl = ttl; |
| 356 | slot.cache_name = filename; | 346 | slot.cache_name = strbuf_detach(&filename, NULL); |
| 357 | slot.lock_name = lockname; | 347 | slot.lock_name = strbuf_detach(&lockname, NULL); |
| 358 | slot.key = key; | 348 | slot.key = key; |
| 359 | slot.keylen = strlen(key); | 349 | slot.keylen = strlen(key); |
| 360 | return process_slot(&slot); | 350 | return process_slot(&slot); |
| @@ -381,18 +371,13 @@ int cache_ls(const char *path) | |||
| 381 | struct dirent *ent; | 371 | struct dirent *ent; |
| 382 | int err = 0; | 372 | int err = 0; |
| 383 | struct cache_slot slot; | 373 | struct cache_slot slot; |
| 384 | char fullname[1024]; | 374 | struct strbuf fullname = STRBUF_INIT; |
| 385 | char *name; | 375 | size_t prefixlen; |
| 386 | 376 | ||
| 387 | if (!path) { | 377 | if (!path) { |
| 388 | cache_log("[cgit] cache path not specified\n"); | 378 | cache_log("[cgit] cache path not specified\n"); |
| 389 | return -1; | 379 | return -1; |
| 390 | } | 380 | } |
| 391 | if (strlen(path) > 1024 - 10) { | ||
| 392 | cache_log("[cgit] cache path too long: %s\n", | ||
| 393 | path); | ||
| 394 | return -1; | ||
| 395 | } | ||
| 396 | dir = opendir(path); | 381 | dir = opendir(path); |
| 397 | if (!dir) { | 382 | if (!dir) { |
| 398 | err = errno; | 383 | err = errno; |
| @@ -400,30 +385,28 @@ int cache_ls(const char *path) | |||
| 400 | path, strerror(err), err); | 385 | path, strerror(err), err); |
| 401 | return err; | 386 | return err; |
| 402 | } | 387 | } |
| 403 | strcpy(fullname, path); | 388 | strbuf_addstr(&fullname, path); |
| 404 | name = fullname + strlen(path); | 389 | strbuf_ensure_end(&fullname, '/'); |
| 405 | if (*(name - 1) != '/') { | 390 | prefixlen = fullname.len; |
| 406 | *name++ = '/'; | ||
| 407 | *name = '\0'; | ||
| 408 | } | ||
| 409 | slot.cache_name = fullname; | ||
| 410 | while ((ent = readdir(dir)) != NULL) { | 391 | while ((ent = readdir(dir)) != NULL) { |
| 411 | if (strlen(ent->d_name) != 8) | 392 | if (strlen(ent->d_name) != 8) |
| 412 | continue; | 393 | continue; |
| 413 | strcpy(name, ent->d_name); | 394 | strbuf_setlen(&fullname, prefixlen); |
| 395 | strbuf_addstr(&fullname, ent->d_name); | ||
| 414 | if ((err = open_slot(&slot)) != 0) { | 396 | if ((err = open_slot(&slot)) != 0) { |
| 415 | cache_log("[cgit] unable to open path %s: %s (%d)\n", | 397 | cache_log("[cgit] unable to open path %s: %s (%d)\n", |
| 416 | fullname, strerror(err), err); | 398 | fullname.buf, strerror(err), err); |
| 417 | continue; | 399 | continue; |
| 418 | } | 400 | } |
| 419 | printf("%s %s %10"PRIuMAX" %s\n", | 401 | printf("%s %s %10"PRIuMAX" %s\n", |
| 420 | name, | 402 | fullname.buf, |
| 421 | sprintftime("%Y-%m-%d %H:%M:%S", | 403 | sprintftime("%Y-%m-%d %H:%M:%S", |
| 422 | slot.cache_st.st_mtime), | 404 | slot.cache_st.st_mtime), |
| 423 | (uintmax_t)slot.cache_st.st_size, | 405 | (uintmax_t)slot.cache_st.st_size, |
| 424 | slot.buf); | 406 | slot.buf); |
| 425 | close_slot(&slot); | 407 | close_slot(&slot); |
| 426 | } | 408 | } |
| 409 | slot.cache_name = strbuf_detach(&fullname, NULL); | ||
| 427 | closedir(dir); | 410 | closedir(dir); |
| 428 | return 0; | 411 | return 0; |
| 429 | } | 412 | } |
| @@ -468,8 +468,8 @@ static int prepare_repo_cmd(struct cgit_context *ctx) | |||
| 468 | if (nongit) { | 468 | if (nongit) { |
| 469 | const char *name = ctx->repo->name; | 469 | const char *name = ctx->repo->name; |
| 470 | rc = errno; | 470 | rc = errno; |
| 471 | ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, | 471 | ctx->page.title = fmtalloc("%s - %s", ctx->cfg.root_title, |
| 472 | "config error"); | 472 | "config error"); |
| 473 | ctx->repo = NULL; | 473 | ctx->repo = NULL; |
| 474 | cgit_print_http_headers(ctx); | 474 | cgit_print_http_headers(ctx); |
| 475 | cgit_print_docstart(ctx); | 475 | cgit_print_docstart(ctx); |
| @@ -479,7 +479,7 @@ static int prepare_repo_cmd(struct cgit_context *ctx) | |||
| 479 | cgit_print_docend(); | 479 | cgit_print_docend(); |
| 480 | return 1; | 480 | return 1; |
| 481 | } | 481 | } |
| 482 | ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); | 482 | ctx->page.title = fmtalloc("%s - %s", ctx->repo->name, ctx->repo->desc); |
| 483 | 483 | ||
| 484 | if (!ctx->repo->defbranch) | 484 | if (!ctx->repo->defbranch) |
| 485 | ctx->repo->defbranch = guess_defbranch(); | 485 | ctx->repo->defbranch = guess_defbranch(); |
| @@ -577,21 +577,16 @@ static int cmp_repos(const void *a, const void *b) | |||
| 577 | static char *build_snapshot_setting(int bitmap) | 577 | static char *build_snapshot_setting(int bitmap) |
| 578 | { | 578 | { |
| 579 | const struct cgit_snapshot_format *f; | 579 | const struct cgit_snapshot_format *f; |
| 580 | char *result = xstrdup(""); | 580 | struct strbuf result = STRBUF_INIT; |
| 581 | char *tmp; | ||
| 582 | int len; | ||
| 583 | 581 | ||
| 584 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 582 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
| 585 | if (f->bit & bitmap) { | 583 | if (f->bit & bitmap) { |
| 586 | tmp = result; | 584 | if (result.len) |
| 587 | result = xstrdup(fmt("%s%s ", tmp, f->suffix)); | 585 | strbuf_addch(&result, ' '); |
| 588 | free(tmp); | 586 | strbuf_addstr(&result, f->suffix); |
| 589 | } | 587 | } |
| 590 | } | 588 | } |
| 591 | len = strlen(result); | 589 | return strbuf_detach(&result, NULL); |
| 592 | if (len) | ||
| 593 | result[len - 1] = '\0'; | ||
| 594 | return result; | ||
| 595 | } | 590 | } |
| 596 | 591 | ||
| 597 | static char *get_first_line(char *txt) | 592 | static char *get_first_line(char *txt) |
| @@ -639,7 +634,7 @@ static void print_repo(FILE *f, struct cgit_repo *repo) | |||
| 639 | fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); | 634 | fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); |
| 640 | if (repo->snapshots != ctx.cfg.snapshots) { | 635 | if (repo->snapshots != ctx.cfg.snapshots) { |
| 641 | char *tmp = build_snapshot_setting(repo->snapshots); | 636 | char *tmp = build_snapshot_setting(repo->snapshots); |
| 642 | fprintf(f, "repo.snapshots=%s\n", tmp); | 637 | fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : ""); |
| 643 | free(tmp); | 638 | free(tmp); |
| 644 | } | 639 | } |
| 645 | if (repo->max_stats != ctx.cfg.max_stats) | 640 | if (repo->max_stats != ctx.cfg.max_stats) |
| @@ -661,20 +656,22 @@ static void print_repolist(FILE *f, struct cgit_repolist *list, int start) | |||
| 661 | */ | 656 | */ |
| 662 | static int generate_cached_repolist(const char *path, const char *cached_rc) | 657 | static int generate_cached_repolist(const char *path, const char *cached_rc) |
| 663 | { | 658 | { |
| 664 | char *locked_rc; | 659 | struct strbuf locked_rc = STRBUF_INIT; |
| 660 | int result = 0; | ||
| 665 | int idx; | 661 | int idx; |
| 666 | FILE *f; | 662 | FILE *f; |
| 667 | 663 | ||
| 668 | locked_rc = xstrdup(fmt("%s.lock", cached_rc)); | 664 | strbuf_addf(&locked_rc, "%s.lock", cached_rc); |
| 669 | f = fopen(locked_rc, "wx"); | 665 | f = fopen(locked_rc.buf, "wx"); |
| 670 | if (!f) { | 666 | if (!f) { |
| 671 | /* Inform about the error unless the lockfile already existed, | 667 | /* Inform about the error unless the lockfile already existed, |
| 672 | * since that only means we've got concurrent requests. | 668 | * since that only means we've got concurrent requests. |
| 673 | */ | 669 | */ |
| 674 | if (errno != EEXIST) | 670 | result = errno; |
| 671 | if (result != EEXIST) | ||
| 675 | fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", | 672 | fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", |
| 676 | locked_rc, strerror(errno), errno); | 673 | locked_rc.buf, strerror(result), result); |
| 677 | return errno; | 674 | goto out; |
| 678 | } | 675 | } |
| 679 | idx = cgit_repolist.count; | 676 | idx = cgit_repolist.count; |
| 680 | if (ctx.cfg.project_list) | 677 | if (ctx.cfg.project_list) |
| @@ -682,55 +679,59 @@ static int generate_cached_repolist(const char *path, const char *cached_rc) | |||
| 682 | else | 679 | else |
| 683 | scan_tree(path, repo_config); | 680 | scan_tree(path, repo_config); |
| 684 | print_repolist(f, &cgit_repolist, idx); | 681 | print_repolist(f, &cgit_repolist, idx); |
| 685 | if (rename(locked_rc, cached_rc)) | 682 | if (rename(locked_rc.buf, cached_rc)) |
| 686 | fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", | 683 | fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", |
| 687 | locked_rc, cached_rc, strerror(errno), errno); | 684 | locked_rc.buf, cached_rc, strerror(errno), errno); |
| 688 | fclose(f); | 685 | fclose(f); |
| 689 | return 0; | 686 | out: |
| 687 | strbuf_release(&locked_rc); | ||
| 688 | return result; | ||
| 690 | } | 689 | } |
| 691 | 690 | ||
| 692 | static void process_cached_repolist(const char *path) | 691 | static void process_cached_repolist(const char *path) |
| 693 | { | 692 | { |
| 694 | struct stat st; | 693 | struct stat st; |
| 695 | char *cached_rc; | 694 | struct strbuf cached_rc = STRBUF_INIT; |
| 696 | time_t age; | 695 | time_t age; |
| 697 | unsigned long hash; | 696 | unsigned long hash; |
| 698 | 697 | ||
| 699 | hash = hash_str(path); | 698 | hash = hash_str(path); |
| 700 | if (ctx.cfg.project_list) | 699 | if (ctx.cfg.project_list) |
| 701 | hash += hash_str(ctx.cfg.project_list); | 700 | hash += hash_str(ctx.cfg.project_list); |
| 702 | cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash)); | 701 | strbuf_addf(&cached_rc, "%s/rc-%8lx", ctx.cfg.cache_root, hash); |
| 703 | 702 | ||
| 704 | if (stat(cached_rc, &st)) { | 703 | if (stat(cached_rc.buf, &st)) { |
| 705 | /* Nothing is cached, we need to scan without forking. And | 704 | /* Nothing is cached, we need to scan without forking. And |
| 706 | * if we fail to generate a cached repolist, we need to | 705 | * if we fail to generate a cached repolist, we need to |
| 707 | * invoke scan_tree manually. | 706 | * invoke scan_tree manually. |
| 708 | */ | 707 | */ |
| 709 | if (generate_cached_repolist(path, cached_rc)) { | 708 | if (generate_cached_repolist(path, cached_rc.buf)) { |
| 710 | if (ctx.cfg.project_list) | 709 | if (ctx.cfg.project_list) |
| 711 | scan_projects(path, ctx.cfg.project_list, | 710 | scan_projects(path, ctx.cfg.project_list, |
| 712 | repo_config); | 711 | repo_config); |
| 713 | else | 712 | else |
| 714 | scan_tree(path, repo_config); | 713 | scan_tree(path, repo_config); |
| 715 | } | 714 | } |
| 716 | return; | 715 | goto out; |
| 717 | } | 716 | } |
| 718 | 717 | ||
| 719 | parse_configfile(cached_rc, config_cb); | 718 | parse_configfile(cached_rc.buf, config_cb); |
| 720 | 719 | ||
| 721 | /* If the cached configfile hasn't expired, lets exit now */ | 720 | /* If the cached configfile hasn't expired, lets exit now */ |
| 722 | age = time(NULL) - st.st_mtime; | 721 | age = time(NULL) - st.st_mtime; |
| 723 | if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) | 722 | if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) |
| 724 | return; | 723 | goto out; |
| 725 | 724 | ||
| 726 | /* The cached repolist has been parsed, but it was old. So lets | 725 | /* The cached repolist has been parsed, but it was old. So lets |
| 727 | * rescan the specified path and generate a new cached repolist | 726 | * rescan the specified path and generate a new cached repolist |
| 728 | * in a child-process to avoid latency for the current request. | 727 | * in a child-process to avoid latency for the current request. |
| 729 | */ | 728 | */ |
| 730 | if (fork()) | 729 | if (fork()) |
| 731 | return; | 730 | goto out; |
| 732 | 731 | ||
| 733 | exit(generate_cached_repolist(path, cached_rc)); | 732 | exit(generate_cached_repolist(path, cached_rc.buf)); |
| 733 | out: | ||
| 734 | strbuf_release(&cached_rc); | ||
| 734 | } | 735 | } |
| 735 | 736 | ||
| 736 | static void cgit_parse_args(int argc, const char **argv) | 737 | static void cgit_parse_args(int argc, const char **argv) |
| @@ -812,7 +813,6 @@ static int calc_ttl() | |||
| 812 | int main(int argc, const char **argv) | 813 | int main(int argc, const char **argv) |
| 813 | { | 814 | { |
| 814 | const char *path; | 815 | const char *path; |
| 815 | char *qry; | ||
| 816 | int err, ttl; | 816 | int err, ttl; |
| 817 | 817 | ||
| 818 | prepare_context(&ctx); | 818 | prepare_context(&ctx); |
| @@ -843,9 +843,9 @@ int main(int argc, const char **argv) | |||
| 843 | path++; | 843 | path++; |
| 844 | ctx.qry.url = xstrdup(path); | 844 | ctx.qry.url = xstrdup(path); |
| 845 | if (ctx.qry.raw) { | 845 | if (ctx.qry.raw) { |
| 846 | qry = ctx.qry.raw; | 846 | char *newqry = fmtalloc("%s?%s", path, ctx.qry.raw); |
| 847 | ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); | 847 | free(ctx.qry.raw); |
| 848 | free(qry); | 848 | ctx.qry.raw = newqry; |
| 849 | } else | 849 | } else |
| 850 | ctx.qry.raw = xstrdup(ctx.qry.url); | 850 | ctx.qry.raw = xstrdup(ctx.qry.url); |
| 851 | cgit_parse_url(ctx.qry.url); | 851 | cgit_parse_url(ctx.qry.url); |
diff --git a/scan-tree.c b/scan-tree.c index 05caba5..beb584b 100644 --- a/scan-tree.c +++ b/scan-tree.c | |||
| @@ -12,38 +12,38 @@ | |||
| 12 | #include "configfile.h" | 12 | #include "configfile.h" |
| 13 | #include "html.h" | 13 | #include "html.h" |
| 14 | 14 | ||
| 15 | #define MAX_PATH 4096 | ||
| 16 | |||
| 17 | /* return 1 if path contains a objects/ directory and a HEAD file */ | 15 | /* return 1 if path contains a objects/ directory and a HEAD file */ |
| 18 | static int is_git_dir(const char *path) | 16 | static int is_git_dir(const char *path) |
| 19 | { | 17 | { |
| 20 | struct stat st; | 18 | struct stat st; |
| 21 | static char buf[MAX_PATH]; | 19 | struct strbuf pathbuf = STRBUF_INIT; |
| 20 | int result = 0; | ||
| 22 | 21 | ||
| 23 | if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { | 22 | strbuf_addf(&pathbuf, "%s/objects", path); |
| 24 | fprintf(stderr, "Insanely long path: %s\n", path); | 23 | if (stat(pathbuf.buf, &st)) { |
| 25 | return 0; | ||
| 26 | } | ||
| 27 | if (stat(buf, &st)) { | ||
| 28 | if (errno != ENOENT) | 24 | if (errno != ENOENT) |
| 29 | fprintf(stderr, "Error checking path %s: %s (%d)\n", | 25 | fprintf(stderr, "Error checking path %s: %s (%d)\n", |
| 30 | path, strerror(errno), errno); | 26 | path, strerror(errno), errno); |
| 31 | return 0; | 27 | goto out; |
| 32 | } | 28 | } |
| 33 | if (!S_ISDIR(st.st_mode)) | 29 | if (!S_ISDIR(st.st_mode)) |
| 34 | return 0; | 30 | goto out; |
| 35 | 31 | ||
| 36 | sprintf(buf, "%s/HEAD", path); | 32 | strbuf_reset(&pathbuf); |
| 37 | if (stat(buf, &st)) { | 33 | strbuf_addf(&pathbuf, "%s/HEAD", path); |
| 34 | if (stat(pathbuf.buf, &st)) { | ||
| 38 | if (errno != ENOENT) | 35 | if (errno != ENOENT) |
| 39 | fprintf(stderr, "Error checking path %s: %s (%d)\n", | 36 | fprintf(stderr, "Error checking path %s: %s (%d)\n", |
| 40 | path, strerror(errno), errno); | 37 | path, strerror(errno), errno); |
| 41 | return 0; | 38 | goto out; |
| 42 | } | 39 | } |
| 43 | if (!S_ISREG(st.st_mode)) | 40 | if (!S_ISREG(st.st_mode)) |
| 44 | return 0; | 41 | goto out; |
| 45 | 42 | ||
| 46 | return 1; | 43 | result = 1; |
| 44 | out: | ||
| 45 | strbuf_release(&pathbuf); | ||
| 46 | return result; | ||
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | struct cgit_repo *repo; | 49 | struct cgit_repo *repo; |
| @@ -75,47 +75,61 @@ static char *xstrrchr(char *s, char *from, int c) | |||
| 75 | return from < s ? NULL : from; | 75 | return from < s ? NULL : from; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | static void add_repo(const char *base, const char *path, repo_config_fn fn) | 78 | static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) |
| 79 | { | 79 | { |
| 80 | struct stat st; | 80 | struct stat st; |
| 81 | struct passwd *pwd; | 81 | struct passwd *pwd; |
| 82 | char *rel, *p, *slash; | 82 | size_t pathlen; |
| 83 | struct strbuf rel = STRBUF_INIT; | ||
| 84 | char *p, *slash; | ||
| 83 | int n; | 85 | int n; |
| 84 | size_t size; | 86 | size_t size; |
| 85 | 87 | ||
| 86 | if (stat(path, &st)) { | 88 | if (stat(path->buf, &st)) { |
| 87 | fprintf(stderr, "Error accessing %s: %s (%d)\n", | 89 | fprintf(stderr, "Error accessing %s: %s (%d)\n", |
| 88 | path, strerror(errno), errno); | 90 | path->buf, strerror(errno), errno); |
| 89 | return; | 91 | return; |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 92 | if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st)) | 94 | strbuf_addch(path, '/'); |
| 93 | return; | 95 | pathlen = path->len; |
| 94 | 96 | ||
| 95 | if (!stat(fmt("%s/noweb", path), &st)) | 97 | if (ctx.cfg.strict_export) { |
| 98 | strbuf_addstr(path, ctx.cfg.strict_export); | ||
| 99 | if(stat(path->buf, &st)) | ||
| 100 | return; | ||
| 101 | strbuf_setlen(path, pathlen); | ||
| 102 | } | ||
| 103 | |||
| 104 | strbuf_addstr(path, "noweb"); | ||
| 105 | if (!stat(path->buf, &st)) | ||
| 96 | return; | 106 | return; |
| 107 | strbuf_setlen(path, pathlen); | ||
| 97 | 108 | ||
| 98 | if (base == path) | 109 | if (strncmp(base, path->buf, strlen(base))) |
| 99 | rel = xstrdup(path); | 110 | strbuf_addbuf(&rel, path); |
| 100 | else | 111 | else |
| 101 | rel = xstrdup(path + strlen(base) + 1); | 112 | strbuf_addstr(&rel, path->buf + strlen(base) + 1); |
| 102 | 113 | ||
| 103 | if (!strcmp(rel + strlen(rel) - 5, "/.git")) | 114 | if (!strcmp(rel.buf + rel.len - 5, "/.git")) |
| 104 | rel[strlen(rel) - 5] = '\0'; | 115 | strbuf_setlen(&rel, rel.len - 5); |
| 105 | 116 | ||
| 106 | repo = cgit_add_repo(rel); | 117 | repo = cgit_add_repo(rel.buf); |
| 107 | config_fn = fn; | 118 | config_fn = fn; |
| 108 | if (ctx.cfg.enable_git_config) | 119 | if (ctx.cfg.enable_git_config) { |
| 109 | git_config_from_file(gitconfig_config, fmt("%s/config", path), NULL); | 120 | strbuf_addstr(path, "config"); |
| 121 | git_config_from_file(gitconfig_config, path->buf, NULL); | ||
| 122 | strbuf_setlen(path, pathlen); | ||
| 123 | } | ||
| 110 | 124 | ||
| 111 | if (ctx.cfg.remove_suffix) | 125 | if (ctx.cfg.remove_suffix) |
| 112 | if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) | 126 | if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) |
| 113 | *p = '\0'; | 127 | *p = '\0'; |
| 114 | repo->path = xstrdup(path); | 128 | repo->path = xstrdup(path->buf); |
| 115 | while (!repo->owner) { | 129 | while (!repo->owner) { |
| 116 | if ((pwd = getpwuid(st.st_uid)) == NULL) { | 130 | if ((pwd = getpwuid(st.st_uid)) == NULL) { |
| 117 | fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", | 131 | fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", |
| 118 | path, strerror(errno), errno); | 132 | path->buf, strerror(errno), errno); |
| 119 | break; | 133 | break; |
| 120 | } | 134 | } |
| 121 | if (pwd->pw_gecos) | 135 | if (pwd->pw_gecos) |
| @@ -125,30 +139,32 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn) | |||
| 125 | } | 139 | } |
| 126 | 140 | ||
| 127 | if (repo->desc == cgit_default_repo_desc || !repo->desc) { | 141 | if (repo->desc == cgit_default_repo_desc || !repo->desc) { |
| 128 | p = fmt("%s/description", path); | 142 | strbuf_addstr(path, "description"); |
| 129 | if (!stat(p, &st)) | 143 | if (!stat(path->buf, &st)) |
| 130 | readfile(p, &repo->desc, &size); | 144 | readfile(path->buf, &repo->desc, &size); |
| 145 | strbuf_setlen(path, pathlen); | ||
| 131 | } | 146 | } |
| 132 | 147 | ||
| 133 | if (!repo->readme) { | 148 | if (!repo->readme) { |
| 134 | p = fmt("%s/README.html", path); | 149 | strbuf_addstr(path, "README.html"); |
| 135 | if (!stat(p, &st)) | 150 | if (!stat(path->buf, &st)) |
| 136 | repo->readme = "README.html"; | 151 | repo->readme = "README.html"; |
| 152 | strbuf_setlen(path, pathlen); | ||
| 137 | } | 153 | } |
| 138 | if (ctx.cfg.section_from_path) { | 154 | if (ctx.cfg.section_from_path) { |
| 139 | n = ctx.cfg.section_from_path; | 155 | n = ctx.cfg.section_from_path; |
| 140 | if (n > 0) { | 156 | if (n > 0) { |
| 141 | slash = rel; | 157 | slash = rel.buf; |
| 142 | while (slash && n && (slash = strchr(slash, '/'))) | 158 | while (slash && n && (slash = strchr(slash, '/'))) |
| 143 | n--; | 159 | n--; |
| 144 | } else { | 160 | } else { |
| 145 | slash = rel + strlen(rel); | 161 | slash = rel.buf + rel.len; |
| 146 | while (slash && n && (slash = xstrrchr(rel, slash, '/'))) | 162 | while (slash && n && (slash = xstrrchr(rel.buf, slash, '/'))) |
| 147 | n++; | 163 | n++; |
| 148 | } | 164 | } |
| 149 | if (slash && !n) { | 165 | if (slash && !n) { |
| 150 | *slash = '\0'; | 166 | *slash = '\0'; |
| 151 | repo->section = xstrdup(rel); | 167 | repo->section = xstrdup(rel.buf); |
| 152 | *slash = '/'; | 168 | *slash = '/'; |
| 153 | if (!prefixcmp(repo->name, repo->section)) { | 169 | if (!prefixcmp(repo->name, repo->section)) { |
| 154 | repo->name += strlen(repo->section); | 170 | repo->name += strlen(repo->section); |
| @@ -158,19 +174,19 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn) | |||
| 158 | } | 174 | } |
| 159 | } | 175 | } |
| 160 | 176 | ||
| 161 | p = fmt("%s/cgitrc", path); | 177 | strbuf_addstr(path, "cgitrc"); |
| 162 | if (!stat(p, &st)) | 178 | if (!stat(path->buf, &st)) |
| 163 | parse_configfile(xstrdup(p), &repo_config); | 179 | parse_configfile(xstrdup(path->buf), &repo_config); |
| 164 | |||
| 165 | 180 | ||
| 166 | free(rel); | 181 | strbuf_release(&rel); |
| 167 | } | 182 | } |
| 168 | 183 | ||
| 169 | static void scan_path(const char *base, const char *path, repo_config_fn fn) | 184 | static void scan_path(const char *base, const char *path, repo_config_fn fn) |
| 170 | { | 185 | { |
| 171 | DIR *dir = opendir(path); | 186 | DIR *dir = opendir(path); |
| 172 | struct dirent *ent; | 187 | struct dirent *ent; |
| 173 | char *buf; | 188 | struct strbuf pathbuf = STRBUF_INIT; |
| 189 | size_t pathlen = strlen(path); | ||
| 174 | struct stat st; | 190 | struct stat st; |
| 175 | 191 | ||
| 176 | if (!dir) { | 192 | if (!dir) { |
| @@ -178,14 +194,22 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn) | |||
| 178 | path, strerror(errno), errno); | 194 | path, strerror(errno), errno); |
| 179 | return; | 195 | return; |
| 180 | } | 196 | } |
| 181 | if (is_git_dir(path)) { | 197 | |
| 182 | add_repo(base, path, fn); | 198 | strbuf_add(&pathbuf, path, strlen(path)); |
| 199 | if (is_git_dir(pathbuf.buf)) { | ||
| 200 | add_repo(base, &pathbuf, fn); | ||
| 183 | goto end; | 201 | goto end; |
| 184 | } | 202 | } |
| 185 | if (is_git_dir(fmt("%s/.git", path))) { | 203 | strbuf_addstr(&pathbuf, "/.git"); |
| 186 | add_repo(base, fmt("%s/.git", path), fn); | 204 | if (is_git_dir(pathbuf.buf)) { |
| 205 | add_repo(base, &pathbuf, fn); | ||
| 187 | goto end; | 206 | goto end; |
| 188 | } | 207 | } |
| 208 | /* | ||
| 209 | * Add one because we don't want to lose the trailing '/' when we | ||
| 210 | * reset the length of pathbuf in the loop below. | ||
| 211 | */ | ||
| 212 | pathlen++; | ||
| 189 | while ((ent = readdir(dir)) != NULL) { | 213 | while ((ent = readdir(dir)) != NULL) { |
| 190 | if (ent->d_name[0] == '.') { | 214 | if (ent->d_name[0] == '.') { |
| 191 | if (ent->d_name[1] == '\0') | 215 | if (ent->d_name[1] == '\0') |
| @@ -195,24 +219,18 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn) | |||
| 195 | if (!ctx.cfg.scan_hidden_path) | 219 | if (!ctx.cfg.scan_hidden_path) |
| 196 | continue; | 220 | continue; |
| 197 | } | 221 | } |
| 198 | buf = malloc(strlen(path) + strlen(ent->d_name) + 2); | 222 | strbuf_setlen(&pathbuf, pathlen); |
| 199 | if (!buf) { | 223 | strbuf_addstr(&pathbuf, ent->d_name); |
| 200 | fprintf(stderr, "Alloc error on %s: %s (%d)\n", | 224 | if (stat(pathbuf.buf, &st)) { |
| 201 | path, strerror(errno), errno); | ||
| 202 | exit(1); | ||
| 203 | } | ||
| 204 | sprintf(buf, "%s/%s", path, ent->d_name); | ||
| 205 | if (stat(buf, &st)) { | ||
| 206 | fprintf(stderr, "Error checking path %s: %s (%d)\n", | 225 | fprintf(stderr, "Error checking path %s: %s (%d)\n", |
| 207 | buf, strerror(errno), errno); | 226 | pathbuf.buf, strerror(errno), errno); |
| 208 | free(buf); | ||
| 209 | continue; | 227 | continue; |
| 210 | } | 228 | } |
| 211 | if (S_ISDIR(st.st_mode)) | 229 | if (S_ISDIR(st.st_mode)) |
| 212 | scan_path(base, buf, fn); | 230 | scan_path(base, pathbuf.buf, fn); |
| 213 | free(buf); | ||
| 214 | } | 231 | } |
| 215 | end: | 232 | end: |
| 233 | strbuf_release(&pathbuf); | ||
| 216 | closedir(dir); | 234 | closedir(dir); |
| 217 | } | 235 | } |
| 218 | 236 | ||
| @@ -220,7 +238,7 @@ end: | |||
| 220 | 238 | ||
| 221 | void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) | 239 | void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) |
| 222 | { | 240 | { |
| 223 | char line[MAX_PATH * 2], *z; | 241 | struct strbuf line = STRBUF_INIT; |
| 224 | FILE *projects; | 242 | FILE *projects; |
| 225 | int err; | 243 | int err; |
| 226 | 244 | ||
| @@ -230,19 +248,19 @@ void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn | |||
| 230 | projectsfile, strerror(errno), errno); | 248 | projectsfile, strerror(errno), errno); |
| 231 | return; | 249 | return; |
| 232 | } | 250 | } |
| 233 | while (fgets(line, sizeof(line), projects) != NULL) { | 251 | while (strbuf_getline(&line, projects, '\n') != EOF) { |
| 234 | for (z = &lastc(line); | 252 | if (!line.len) |
| 235 | strlen(line) && strchr("\n\r", *z); | 253 | continue; |
| 236 | z = &lastc(line)) | 254 | strbuf_insert(&line, 0, "/", 1); |
| 237 | *z = '\0'; | 255 | strbuf_insert(&line, 0, path, strlen(path)); |
| 238 | if (strlen(line)) | 256 | scan_path(path, line.buf, fn); |
| 239 | scan_path(path, fmt("%s/%s", path, line), fn); | ||
| 240 | } | 257 | } |
| 241 | if ((err = ferror(projects))) { | 258 | if ((err = ferror(projects))) { |
| 242 | fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", | 259 | fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", |
| 243 | projectsfile, strerror(err), err); | 260 | projectsfile, strerror(err), err); |
| 244 | } | 261 | } |
| 245 | fclose(projects); | 262 | fclose(projects); |
| 263 | strbuf_release(&line); | ||
| 246 | } | 264 | } |
| 247 | 265 | ||
| 248 | void scan_tree(const char *path, repo_config_fn fn) | 266 | void scan_tree(const char *path, repo_config_fn fn) |
| @@ -243,15 +243,19 @@ static void print_commit(struct commit *commit, struct rev_info *revs) | |||
| 243 | cgit_free_commitinfo(info); | 243 | cgit_free_commitinfo(info); |
| 244 | } | 244 | } |
| 245 | 245 | ||
| 246 | static const char *disambiguate_ref(const char *ref) | 246 | static const char *disambiguate_ref(const char *ref, int *must_free_result) |
| 247 | { | 247 | { |
| 248 | unsigned char sha1[20]; | 248 | unsigned char sha1[20]; |
| 249 | const char *longref; | 249 | struct strbuf longref = STRBUF_INIT; |
| 250 | 250 | ||
| 251 | longref = fmt("refs/heads/%s", ref); | 251 | strbuf_addf(&longref, "refs/heads/%s", ref); |
| 252 | if (get_sha1(longref, sha1) == 0) | 252 | if (get_sha1(longref.buf, sha1) == 0) { |
| 253 | return longref; | 253 | *must_free_result = 1; |
| 254 | return strbuf_detach(&longref, NULL); | ||
| 255 | } | ||
| 254 | 256 | ||
| 257 | *must_free_result = 0; | ||
| 258 | strbuf_release(&longref); | ||
| 255 | return ref; | 259 | return ref; |
| 256 | } | 260 | } |
| 257 | 261 | ||
| @@ -284,24 +288,26 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern | |||
| 284 | struct commit *commit; | 288 | struct commit *commit; |
| 285 | struct vector vec = VECTOR_INIT(char *); | 289 | struct vector vec = VECTOR_INIT(char *); |
| 286 | int i, columns = commit_graph ? 4 : 3; | 290 | int i, columns = commit_graph ? 4 : 3; |
| 287 | char *arg; | 291 | int must_free_tip = 0; |
| 292 | struct strbuf argbuf = STRBUF_INIT; | ||
| 288 | 293 | ||
| 289 | /* First argv is NULL */ | 294 | /* First argv is NULL */ |
| 290 | vector_push(&vec, NULL, 0); | 295 | vector_push(&vec, NULL, 0); |
| 291 | 296 | ||
| 292 | if (!tip) | 297 | if (!tip) |
| 293 | tip = ctx.qry.head; | 298 | tip = ctx.qry.head; |
| 294 | tip = disambiguate_ref(tip); | 299 | tip = disambiguate_ref(tip, &must_free_tip); |
| 295 | vector_push(&vec, &tip, 0); | 300 | vector_push(&vec, &tip, 0); |
| 296 | 301 | ||
| 297 | if (grep && pattern && *pattern) { | 302 | if (grep && pattern && *pattern) { |
| 298 | pattern = xstrdup(pattern); | 303 | pattern = xstrdup(pattern); |
| 299 | if (!strcmp(grep, "grep") || !strcmp(grep, "author") || | 304 | if (!strcmp(grep, "grep") || !strcmp(grep, "author") || |
| 300 | !strcmp(grep, "committer")) { | 305 | !strcmp(grep, "committer")) { |
| 301 | arg = fmt("--%s=%s", grep, pattern); | 306 | strbuf_addf(&argbuf, "--%s=%s", grep, pattern); |
| 302 | vector_push(&vec, &arg, 0); | 307 | vector_push(&vec, &argbuf.buf, 0); |
| 303 | } | 308 | } |
| 304 | if (!strcmp(grep, "range")) { | 309 | if (!strcmp(grep, "range")) { |
| 310 | char *arg; | ||
| 305 | /* Split the pattern at whitespace and add each token | 311 | /* Split the pattern at whitespace and add each token |
| 306 | * as a revision expression. Do not accept other | 312 | * as a revision expression. Do not accept other |
| 307 | * rev-list options. Also, replace the previously | 313 | * rev-list options. Also, replace the previously |
| @@ -336,8 +342,8 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern | |||
| 336 | } | 342 | } |
| 337 | 343 | ||
| 338 | if (path) { | 344 | if (path) { |
| 339 | arg = "--"; | 345 | static const char *double_dash_arg = "--"; |
| 340 | vector_push(&vec, &arg, 0); | 346 | vector_push(&vec, &double_dash_arg, 0); |
| 341 | vector_push(&vec, &path, 0); | 347 | vector_push(&vec, &path, 0); |
| 342 | } | 348 | } |
| 343 | 349 | ||
| @@ -430,4 +436,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern | |||
| 430 | ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); | 436 | ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg); |
| 431 | html("</td></tr>\n"); | 437 | html("</td></tr>\n"); |
| 432 | } | 438 | } |
| 439 | |||
| 440 | /* If we allocated tip then it is safe to cast away const. */ | ||
| 441 | if (must_free_tip) | ||
| 442 | free((char*) tip); | ||
| 443 | strbuf_release(&argbuf); | ||
| 433 | } | 444 | } |
| @@ -109,9 +109,9 @@ static int print_object(const unsigned char *sha1, const char *path) | |||
| 109 | static char *buildpath(const char *base, int baselen, const char *path) | 109 | static char *buildpath(const char *base, int baselen, const char *path) |
| 110 | { | 110 | { |
| 111 | if (path[0]) | 111 | if (path[0]) |
| 112 | return fmt("%.*s%s/", baselen, base, path); | 112 | return fmtalloc("%.*s%s/", baselen, base, path); |
| 113 | else | 113 | else |
| 114 | return fmt("%.*s/", baselen, base); | 114 | return fmtalloc("%.*s/", baselen, base); |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | static void print_dir(const unsigned char *sha1, const char *base, | 117 | static void print_dir(const unsigned char *sha1, const char *base, |
| @@ -142,6 +142,7 @@ static void print_dir(const unsigned char *sha1, const char *base, | |||
| 142 | fullpath); | 142 | fullpath); |
| 143 | html("</li>\n"); | 143 | html("</li>\n"); |
| 144 | } | 144 | } |
| 145 | free(fullpath); | ||
| 145 | } | 146 | } |
| 146 | 147 | ||
| 147 | static void print_dir_entry(const unsigned char *sha1, const char *base, | 148 | static void print_dir_entry(const unsigned char *sha1, const char *base, |
| @@ -159,6 +160,7 @@ static void print_dir_entry(const unsigned char *sha1, const char *base, | |||
| 159 | cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, | 160 | cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, |
| 160 | fullpath); | 161 | fullpath); |
| 161 | html("</li>\n"); | 162 | html("</li>\n"); |
| 163 | free(fullpath); | ||
| 162 | } | 164 | } |
| 163 | 165 | ||
| 164 | static void print_dir_tail(void) | 166 | static void print_dir_tail(void) |
| @@ -99,7 +99,7 @@ static void print_tag_header() | |||
| 99 | static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) | 99 | static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) |
| 100 | { | 100 | { |
| 101 | const struct cgit_snapshot_format* f; | 101 | const struct cgit_snapshot_format* f; |
| 102 | char *filename; | 102 | struct strbuf filename = STRBUF_INIT; |
| 103 | const char *basename; | 103 | const char *basename; |
| 104 | int free_ref = 0; | 104 | int free_ref = 0; |
| 105 | 105 | ||
| @@ -111,7 +111,7 @@ static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) | |||
| 111 | if ((ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1])) | 111 | if ((ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1])) |
| 112 | ref++; | 112 | ref++; |
| 113 | if (isdigit(ref[0])) { | 113 | if (isdigit(ref[0])) { |
| 114 | ref = xstrdup(fmt("%s-%s", basename, ref)); | 114 | ref = fmtalloc("%s-%s", basename, ref); |
| 115 | free_ref = 1; | 115 | free_ref = 1; |
| 116 | } | 116 | } |
| 117 | } | 117 | } |
| @@ -119,13 +119,15 @@ static void print_tag_downloads(const struct cgit_repo *repo, const char *ref) | |||
| 119 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 119 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
| 120 | if (!(repo->snapshots & f->bit)) | 120 | if (!(repo->snapshots & f->bit)) |
| 121 | continue; | 121 | continue; |
| 122 | filename = fmt("%s%s", ref, f->suffix); | 122 | strbuf_reset(&filename); |
| 123 | cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename); | 123 | strbuf_addf(&filename, "%s%s", ref, f->suffix); |
| 124 | cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, filename.buf); | ||
| 124 | html(" "); | 125 | html(" "); |
| 125 | } | 126 | } |
| 126 | 127 | ||
| 127 | if (free_ref) | 128 | if (free_ref) |
| 128 | free((char *)ref); | 129 | free((char *)ref); |
| 130 | strbuf_release(&filename); | ||
| 129 | } | 131 | } |
| 130 | 132 | ||
| 131 | static int print_tag(struct refinfo *ref) | 133 | static int print_tag(struct refinfo *ref) |
diff --git a/ui-repolist.c b/ui-repolist.c index 76fe71a..47ca997 100644 --- a/ui-repolist.c +++ b/ui-repolist.c | |||
| @@ -33,7 +33,7 @@ static time_t read_agefile(char *path) | |||
| 33 | 33 | ||
| 34 | static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) | 34 | static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) |
| 35 | { | 35 | { |
| 36 | char *path; | 36 | struct strbuf path = STRBUF_INIT; |
| 37 | struct stat s; | 37 | struct stat s; |
| 38 | struct cgit_repo *r = (struct cgit_repo *)repo; | 38 | struct cgit_repo *r = (struct cgit_repo *)repo; |
| 39 | 39 | ||
| @@ -41,32 +41,36 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) | |||
| 41 | *mtime = repo->mtime; | 41 | *mtime = repo->mtime; |
| 42 | return 1; | 42 | return 1; |
| 43 | } | 43 | } |
| 44 | path = fmt("%s/%s", repo->path, ctx.cfg.agefile); | 44 | strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile); |
| 45 | if (stat(path, &s) == 0) { | 45 | if (stat(path.buf, &s) == 0) { |
| 46 | *mtime = read_agefile(path); | 46 | *mtime = read_agefile(path.buf); |
| 47 | if (*mtime) { | 47 | if (*mtime) { |
| 48 | r->mtime = *mtime; | 48 | r->mtime = *mtime; |
| 49 | return 1; | 49 | goto end; |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch ? | 53 | strbuf_reset(&path); |
| 54 | repo->defbranch : "master"); | 54 | strbuf_addf(&path, "%s/refs/heads/%s", repo->path, |
| 55 | if (stat(path, &s) == 0) { | 55 | repo->defbranch ? repo->defbranch : "master"); |
| 56 | if (stat(path.buf, &s) == 0) { | ||
| 56 | *mtime = s.st_mtime; | 57 | *mtime = s.st_mtime; |
| 57 | r->mtime = *mtime; | 58 | r->mtime = *mtime; |
| 58 | return 1; | 59 | goto end; |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | path = fmt("%s/%s", repo->path, "packed-refs"); | 62 | strbuf_reset(&path); |
| 62 | if (stat(path, &s) == 0) { | 63 | strbuf_addf(&path, "%s/%s", repo->path, "packed-refs"); |
| 64 | if (stat(path.buf, &s) == 0) { | ||
| 63 | *mtime = s.st_mtime; | 65 | *mtime = s.st_mtime; |
| 64 | r->mtime = *mtime; | 66 | r->mtime = *mtime; |
| 65 | return 1; | 67 | goto end; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | *mtime = 0; | 70 | *mtime = 0; |
| 69 | r->mtime = *mtime; | 71 | r->mtime = *mtime; |
| 72 | end: | ||
| 73 | strbuf_release(&path); | ||
| 70 | return (r->mtime != 0); | 74 | return (r->mtime != 0); |
| 71 | } | 75 | } |
| 72 | 76 | ||
diff --git a/ui-shared.c b/ui-shared.c index b93b77a..519eef7 100644 --- a/ui-shared.c +++ b/ui-shared.c | |||
| @@ -62,7 +62,7 @@ const char *cgit_hosturl() | |||
| 62 | return NULL; | 62 | return NULL; |
| 63 | if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) | 63 | if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) |
| 64 | return ctx.env.server_name; | 64 | return ctx.env.server_name; |
| 65 | return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port)); | 65 | return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | const char *cgit_rooturl() | 68 | const char *cgit_rooturl() |
| @@ -75,31 +75,30 @@ const char *cgit_rooturl() | |||
| 75 | 75 | ||
| 76 | char *cgit_repourl(const char *reponame) | 76 | char *cgit_repourl(const char *reponame) |
| 77 | { | 77 | { |
| 78 | if (ctx.cfg.virtual_root) { | 78 | if (ctx.cfg.virtual_root) |
| 79 | return fmt("%s%s/", ctx.cfg.virtual_root, reponame); | 79 | return fmtalloc("%s%s/", ctx.cfg.virtual_root, reponame); |
| 80 | } else { | 80 | else |
| 81 | return fmt("?r=%s", reponame); | 81 | return fmtalloc("?r=%s", reponame); |
| 82 | } | ||
| 83 | } | 82 | } |
| 84 | 83 | ||
| 85 | char *cgit_fileurl(const char *reponame, const char *pagename, | 84 | char *cgit_fileurl(const char *reponame, const char *pagename, |
| 86 | const char *filename, const char *query) | 85 | const char *filename, const char *query) |
| 87 | { | 86 | { |
| 88 | char *tmp; | 87 | struct strbuf sb = STRBUF_INIT; |
| 89 | char *delim; | 88 | char *delim; |
| 90 | 89 | ||
| 91 | if (ctx.cfg.virtual_root) { | 90 | if (ctx.cfg.virtual_root) { |
| 92 | tmp = fmt("%s%s/%s/%s", ctx.cfg.virtual_root, reponame, | 91 | strbuf_addf(&sb, "%s%s/%s/%s", ctx.cfg.virtual_root, reponame, |
| 93 | pagename, (filename ? filename:"")); | 92 | pagename, (filename ? filename:"")); |
| 94 | delim = "?"; | 93 | delim = "?"; |
| 95 | } else { | 94 | } else { |
| 96 | tmp = fmt("?url=%s/%s/%s", reponame, pagename, | 95 | strbuf_addf(&sb, "?url=%s/%s/%s", reponame, pagename, |
| 97 | (filename ? filename : "")); | 96 | (filename ? filename : "")); |
| 98 | delim = "&"; | 97 | delim = "&"; |
| 99 | } | 98 | } |
| 100 | if (query) | 99 | if (query) |
| 101 | tmp = fmt("%s%s%s", tmp, delim, query); | 100 | strbuf_addf(&sb, "%s%s", delim, query); |
| 102 | return tmp; | 101 | return strbuf_detach(&sb, NULL); |
| 103 | } | 102 | } |
| 104 | 103 | ||
| 105 | char *cgit_pageurl(const char *reponame, const char *pagename, | 104 | char *cgit_pageurl(const char *reponame, const char *pagename, |
| @@ -548,21 +547,21 @@ void cgit_submodule_link(const char *class, char *path, const char *rev) | |||
| 548 | htmlf("class='%s' ", class); | 547 | htmlf("class='%s' ", class); |
| 549 | html("href='"); | 548 | html("href='"); |
| 550 | if (item) { | 549 | if (item) { |
| 551 | html_attr(fmt(item->util, rev)); | 550 | html_attrf(item->util, rev); |
| 552 | } else if (ctx.repo->module_link) { | 551 | } else if (ctx.repo->module_link) { |
| 553 | dir = strrchr(path, '/'); | 552 | dir = strrchr(path, '/'); |
| 554 | if (dir) | 553 | if (dir) |
| 555 | dir++; | 554 | dir++; |
| 556 | else | 555 | else |
| 557 | dir = path; | 556 | dir = path; |
| 558 | html_attr(fmt(ctx.repo->module_link, dir, rev)); | 557 | html_attrf(ctx.repo->module_link, dir, rev); |
| 559 | } else { | 558 | } else { |
| 560 | html("#"); | 559 | html("#"); |
| 561 | } | 560 | } |
| 562 | html("'>"); | 561 | html("'>"); |
| 563 | html_txt(path); | 562 | html_txt(path); |
| 564 | html("</a>"); | 563 | html("</a>"); |
| 565 | html_txt(fmt(" @ %.7s", rev)); | 564 | html_txtf(" @ %.7s", rev); |
| 566 | if (item && tail) | 565 | if (item && tail) |
| 567 | path[len - 1] = tail; | 566 | path[len - 1] = tail; |
| 568 | } | 567 | } |
| @@ -678,12 +677,16 @@ void cgit_print_docstart(struct cgit_context *ctx) | |||
| 678 | html("'/>\n"); | 677 | html("'/>\n"); |
| 679 | } | 678 | } |
| 680 | if (host && ctx->repo && ctx->qry.head) { | 679 | if (host && ctx->repo && ctx->qry.head) { |
| 680 | struct strbuf sb = STRBUF_INIT; | ||
| 681 | strbuf_addf(&sb, "h=%s", ctx->qry.head); | ||
| 682 | |||
| 681 | html("<link rel='alternate' title='Atom feed' href='"); | 683 | html("<link rel='alternate' title='Atom feed' href='"); |
| 682 | html(cgit_httpscheme()); | 684 | html(cgit_httpscheme()); |
| 683 | html_attr(cgit_hosturl()); | 685 | html_attr(cgit_hosturl()); |
| 684 | html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, | 686 | html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, |
| 685 | fmt("h=%s", ctx->qry.head))); | 687 | sb.buf)); |
| 686 | html("' type='application/atom+xml'/>\n"); | 688 | html("' type='application/atom+xml'/>\n"); |
| 689 | strbuf_release(&sb); | ||
| 687 | } | 690 | } |
| 688 | if (ctx->cfg.head_include) | 691 | if (ctx->cfg.head_include) |
| 689 | html_include(ctx->cfg.head_include); | 692 | html_include(ctx->cfg.head_include); |
| @@ -725,13 +728,14 @@ static int print_branch_option(const char *refname, const unsigned char *sha1, | |||
| 725 | void cgit_add_hidden_formfields(int incl_head, int incl_search, | 728 | void cgit_add_hidden_formfields(int incl_head, int incl_search, |
| 726 | const char *page) | 729 | const char *page) |
| 727 | { | 730 | { |
| 728 | char *url; | ||
| 729 | |||
| 730 | if (!ctx.cfg.virtual_root) { | 731 | if (!ctx.cfg.virtual_root) { |
| 731 | url = fmt("%s/%s", ctx.qry.repo, page); | 732 | struct strbuf url = STRBUF_INIT; |
| 733 | |||
| 734 | strbuf_addf(&url, "%s/%s", ctx.qry.repo, page); | ||
| 732 | if (ctx.qry.vpath) | 735 | if (ctx.qry.vpath) |
| 733 | url = fmt("%s/%s", url, ctx.qry.vpath); | 736 | strbuf_addf(&url, "/%s", ctx.qry.vpath); |
| 734 | html_hidden("url", url); | 737 | html_hidden("url", url.buf); |
| 738 | strbuf_release(&url); | ||
| 735 | } | 739 | } |
| 736 | 740 | ||
| 737 | if (incl_head && ctx.qry.head && ctx.repo->defbranch && | 741 | if (incl_head && ctx.qry.head && ctx.repo->defbranch && |
| @@ -926,20 +930,23 @@ void cgit_print_snapshot_links(const char *repo, const char *head, | |||
| 926 | const char *hex, int snapshots) | 930 | const char *hex, int snapshots) |
| 927 | { | 931 | { |
| 928 | const struct cgit_snapshot_format* f; | 932 | const struct cgit_snapshot_format* f; |
| 929 | char *prefix; | 933 | struct strbuf filename = STRBUF_INIT; |
| 930 | char *filename; | 934 | size_t prefixlen; |
| 931 | unsigned char sha1[20]; | 935 | unsigned char sha1[20]; |
| 932 | 936 | ||
| 933 | if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 && | 937 | if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 && |
| 934 | (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1])) | 938 | (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1])) |
| 935 | hex++; | 939 | hex++; |
| 936 | prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex)); | 940 | strbuf_addf(&filename, "%s-%s", cgit_repobasename(repo), hex); |
| 941 | prefixlen = filename.len; | ||
| 937 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 942 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
| 938 | if (!(snapshots & f->bit)) | 943 | if (!(snapshots & f->bit)) |
| 939 | continue; | 944 | continue; |
| 940 | filename = fmt("%s%s", prefix, f->suffix); | 945 | strbuf_setlen(&filename, prefixlen); |
| 941 | cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename); | 946 | strbuf_addstr(&filename, f->suffix); |
| 947 | cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, | ||
| 948 | filename.buf); | ||
| 942 | html("<br/>"); | 949 | html("<br/>"); |
| 943 | } | 950 | } |
| 944 | free(prefix); | 951 | strbuf_release(&filename); |
| 945 | } | 952 | } |
diff --git a/ui-snapshot.c b/ui-snapshot.c index a47884e..8e76977 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c | |||
| @@ -15,14 +15,33 @@ | |||
| 15 | static int write_archive_type(const char *format, const char *hex, const char *prefix) | 15 | static int write_archive_type(const char *format, const char *hex, const char *prefix) |
| 16 | { | 16 | { |
| 17 | struct argv_array argv = ARGV_ARRAY_INIT; | 17 | struct argv_array argv = ARGV_ARRAY_INIT; |
| 18 | const char **nargv; | ||
| 19 | int result; | ||
| 18 | argv_array_push(&argv, "snapshot"); | 20 | argv_array_push(&argv, "snapshot"); |
| 19 | argv_array_push(&argv, format); | 21 | argv_array_push(&argv, format); |
| 20 | if (prefix) { | 22 | if (prefix) { |
| 23 | struct strbuf buf = STRBUF_INIT; | ||
| 24 | strbuf_addstr(&buf, prefix); | ||
| 25 | strbuf_addch(&buf, '/'); | ||
| 21 | argv_array_push(&argv, "--prefix"); | 26 | argv_array_push(&argv, "--prefix"); |
| 22 | argv_array_push(&argv, fmt("%s/", prefix)); | 27 | argv_array_push(&argv, buf.buf); |
| 28 | strbuf_release(&buf); | ||
| 23 | } | 29 | } |
| 24 | argv_array_push(&argv, hex); | 30 | argv_array_push(&argv, hex); |
| 25 | return write_archive(argv.argc, argv.argv, NULL, 1, NULL, 0); | 31 | /* |
| 32 | * Now we need to copy the pointers to arguments into a new | ||
| 33 | * structure because write_archive will rearrange its arguments | ||
| 34 | * which may result in duplicated/missing entries causing leaks | ||
| 35 | * or double-frees in argv_array_clear. | ||
| 36 | */ | ||
| 37 | nargv = xmalloc(sizeof(char *) * (argv.argc + 1)); | ||
| 38 | /* argv_array guarantees a trailing NULL entry. */ | ||
| 39 | memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1)); | ||
| 40 | |||
| 41 | result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0); | ||
| 42 | argv_array_clear(&argv); | ||
| 43 | free(nargv); | ||
| 44 | return result; | ||
| 26 | } | 45 | } |
| 27 | 46 | ||
| 28 | static int write_tar_archive(const char *hex, const char *prefix) | 47 | static int write_tar_archive(const char *hex, const char *prefix) |
| @@ -129,29 +148,36 @@ static const char *get_ref_from_filename(const char *url, const char *filename, | |||
| 129 | { | 148 | { |
| 130 | const char *reponame; | 149 | const char *reponame; |
| 131 | unsigned char sha1[20]; | 150 | unsigned char sha1[20]; |
| 132 | char *snapshot; | 151 | struct strbuf snapshot = STRBUF_INIT; |
| 152 | int result = 1; | ||
| 133 | 153 | ||
| 134 | snapshot = xstrdup(filename); | 154 | strbuf_addstr(&snapshot, filename); |
| 135 | snapshot[strlen(snapshot) - strlen(format->suffix)] = '\0'; | 155 | strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix)); |
| 136 | 156 | ||
| 137 | if (get_sha1(snapshot, sha1) == 0) | 157 | if (get_sha1(snapshot.buf, sha1) == 0) |
| 138 | return snapshot; | 158 | goto out; |
| 139 | 159 | ||
| 140 | reponame = cgit_repobasename(url); | 160 | reponame = cgit_repobasename(url); |
| 141 | if (prefixcmp(snapshot, reponame) == 0) { | 161 | if (prefixcmp(snapshot.buf, reponame) == 0) { |
| 142 | snapshot += strlen(reponame); | 162 | const char *new_start = snapshot.buf; |
| 143 | while (snapshot && (*snapshot == '-' || *snapshot == '_')) | 163 | new_start += strlen(reponame); |
| 144 | snapshot++; | 164 | while (new_start && (*new_start == '-' || *new_start == '_')) |
| 165 | new_start++; | ||
| 166 | strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0); | ||
| 145 | } | 167 | } |
| 146 | 168 | ||
| 147 | if (get_sha1(snapshot, sha1) == 0) | 169 | if (get_sha1(snapshot.buf, sha1) == 0) |
| 148 | return snapshot; | 170 | goto out; |
| 149 | 171 | ||
| 150 | snapshot = fmt("v%s", snapshot); | 172 | strbuf_insert(&snapshot, 0, "v", 1); |
| 151 | if (get_sha1(snapshot, sha1) == 0) | 173 | if (get_sha1(snapshot.buf, sha1) == 0) |
| 152 | return snapshot; | 174 | goto out; |
| 153 | 175 | ||
| 154 | return NULL; | 176 | result = 0; |
| 177 | strbuf_release(&snapshot); | ||
| 178 | |||
| 179 | out: | ||
| 180 | return result ? strbuf_detach(&snapshot, NULL) : NULL; | ||
| 155 | } | 181 | } |
| 156 | 182 | ||
| 157 | __attribute__((format (printf, 1, 2))) | 183 | __attribute__((format (printf, 1, 2))) |
diff --git a/ui-summary.c b/ui-summary.c index bd123ef..f965b32 100644 --- a/ui-summary.c +++ b/ui-summary.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | static void print_url(char *base, char *suffix) | 17 | static void print_url(char *base, char *suffix) |
| 18 | { | 18 | { |
| 19 | int columns = 3; | 19 | int columns = 3; |
| 20 | struct strbuf basebuf = STRBUF_INIT; | ||
| 20 | 21 | ||
| 21 | if (ctx.repo->enable_log_filecount) | 22 | if (ctx.repo->enable_log_filecount) |
| 22 | columns++; | 23 | columns++; |
| @@ -25,13 +26,16 @@ static void print_url(char *base, char *suffix) | |||
| 25 | 26 | ||
| 26 | if (!base || !*base) | 27 | if (!base || !*base) |
| 27 | return; | 28 | return; |
| 28 | if (suffix && *suffix) | 29 | if (suffix && *suffix) { |
| 29 | base = fmt("%s/%s", base, suffix); | 30 | strbuf_addf(&basebuf, "%s/%s", base, suffix); |
| 31 | base = basebuf.buf; | ||
| 32 | } | ||
| 30 | htmlf("<tr><td colspan='%d'><a href='", columns); | 33 | htmlf("<tr><td colspan='%d'><a href='", columns); |
| 31 | html_url_path(base); | 34 | html_url_path(base); |
| 32 | html("'>"); | 35 | html("'>"); |
| 33 | html_txt(base); | 36 | html_txt(base); |
| 34 | html("</a></td></tr>\n"); | 37 | html("</a></td></tr>\n"); |
| 38 | strbuf_release(&basebuf); | ||
| 35 | } | 39 | } |
| 36 | 40 | ||
| 37 | static void print_urls(char *txt, char *suffix) | 41 | static void print_urls(char *txt, char *suffix) |
| @@ -112,8 +116,8 @@ void cgit_print_repo_readme(char *path) | |||
| 112 | 116 | ||
| 113 | /* Prepend repo path to relative readme path unless tracked. */ | 117 | /* Prepend repo path to relative readme path unless tracked. */ |
| 114 | if (!ref && *ctx.repo->readme != '/') | 118 | if (!ref && *ctx.repo->readme != '/') |
| 115 | ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, | 119 | ctx.repo->readme = fmtalloc("%s/%s", ctx.repo->path, |
| 116 | ctx.repo->readme)); | 120 | ctx.repo->readme); |
| 117 | 121 | ||
| 118 | /* If a subpath is specified for the about page, make it relative | 122 | /* If a subpath is specified for the about page, make it relative |
| 119 | * to the directory containing the configured readme. | 123 | * to the directory containing the configured readme. |
| @@ -41,6 +41,7 @@ static void print_download_links(char *revname) | |||
| 41 | 41 | ||
| 42 | void cgit_print_tag(char *revname) | 42 | void cgit_print_tag(char *revname) |
| 43 | { | 43 | { |
| 44 | struct strbuf fullref = STRBUF_INIT; | ||
| 44 | unsigned char sha1[20]; | 45 | unsigned char sha1[20]; |
| 45 | struct object *obj; | 46 | struct object *obj; |
| 46 | struct tag *tag; | 47 | struct tag *tag; |
| @@ -49,20 +50,21 @@ void cgit_print_tag(char *revname) | |||
| 49 | if (!revname) | 50 | if (!revname) |
| 50 | revname = ctx.qry.head; | 51 | revname = ctx.qry.head; |
| 51 | 52 | ||
| 52 | if (get_sha1(fmt("refs/tags/%s", revname), sha1)) { | 53 | strbuf_addf(&fullref, "refs/tags/%s", revname); |
| 54 | if (get_sha1(fullref.buf, sha1)) { | ||
| 53 | cgit_print_error("Bad tag reference: %s", revname); | 55 | cgit_print_error("Bad tag reference: %s", revname); |
| 54 | return; | 56 | goto cleanup; |
| 55 | } | 57 | } |
| 56 | obj = parse_object(sha1); | 58 | obj = parse_object(sha1); |
| 57 | if (!obj) { | 59 | if (!obj) { |
| 58 | cgit_print_error("Bad object id: %s", sha1_to_hex(sha1)); | 60 | cgit_print_error("Bad object id: %s", sha1_to_hex(sha1)); |
| 59 | return; | 61 | goto cleanup; |
| 60 | } | 62 | } |
| 61 | if (obj->type == OBJ_TAG) { | 63 | if (obj->type == OBJ_TAG) { |
| 62 | tag = lookup_tag(sha1); | 64 | tag = lookup_tag(sha1); |
| 63 | if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { | 65 | if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { |
| 64 | cgit_print_error("Bad tag object: %s", revname); | 66 | cgit_print_error("Bad tag object: %s", revname); |
| 65 | return; | 67 | goto cleanup; |
| 66 | } | 68 | } |
| 67 | html("<table class='commit-info'>\n"); | 69 | html("<table class='commit-info'>\n"); |
| 68 | htmlf("<tr><td>tag name</td><td>"); | 70 | htmlf("<tr><td>tag name</td><td>"); |
| @@ -101,5 +103,7 @@ void cgit_print_tag(char *revname) | |||
| 101 | print_download_links(revname); | 103 | print_download_links(revname); |
| 102 | html("</table>\n"); | 104 | html("</table>\n"); |
| 103 | } | 105 | } |
| 104 | return; | 106 | |
| 107 | cleanup: | ||
| 108 | strbuf_release(&fullref); | ||
| 105 | } | 109 | } |
| @@ -129,14 +129,14 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen, | |||
| 129 | { | 129 | { |
| 130 | struct walk_tree_context *walk_tree_ctx = cbdata; | 130 | struct walk_tree_context *walk_tree_ctx = cbdata; |
| 131 | char *name; | 131 | char *name; |
| 132 | char *fullpath; | 132 | struct strbuf fullpath = STRBUF_INIT; |
| 133 | char *class; | 133 | struct strbuf class = STRBUF_INIT; |
| 134 | enum object_type type; | 134 | enum object_type type; |
| 135 | unsigned long size = 0; | 135 | unsigned long size = 0; |
| 136 | 136 | ||
| 137 | name = xstrdup(pathname); | 137 | name = xstrdup(pathname); |
| 138 | fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", | 138 | strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", |
| 139 | ctx.qry.path ? "/" : "", name); | 139 | ctx.qry.path ? "/" : "", name); |
| 140 | 140 | ||
| 141 | if (!S_ISGITLINK(mode)) { | 141 | if (!S_ISGITLINK(mode)) { |
| 142 | type = sha1_object_info(sha1, &size); | 142 | type = sha1_object_info(sha1, &size); |
| @@ -152,33 +152,34 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen, | |||
| 152 | cgit_print_filemode(mode); | 152 | cgit_print_filemode(mode); |
| 153 | html("</td><td>"); | 153 | html("</td><td>"); |
| 154 | if (S_ISGITLINK(mode)) { | 154 | if (S_ISGITLINK(mode)) { |
| 155 | cgit_submodule_link("ls-mod", fullpath, sha1_to_hex(sha1)); | 155 | cgit_submodule_link("ls-mod", fullpath.buf, sha1_to_hex(sha1)); |
| 156 | } else if (S_ISDIR(mode)) { | 156 | } else if (S_ISDIR(mode)) { |
| 157 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, | 157 | cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, |
| 158 | walk_tree_ctx->curr_rev, fullpath); | 158 | walk_tree_ctx->curr_rev, fullpath.buf); |
| 159 | } else { | 159 | } else { |
| 160 | class = strrchr(name, '.'); | 160 | char *ext = strrchr(name, '.'); |
| 161 | if (class != NULL) { | 161 | strbuf_addstr(&class, "ls-blob"); |
| 162 | class = fmt("ls-blob %s", class + 1); | 162 | if (ext) |
| 163 | } else | 163 | strbuf_addf(&class, " %s", ext + 1); |
| 164 | class = "ls-blob"; | 164 | cgit_tree_link(name, NULL, class.buf, ctx.qry.head, |
| 165 | cgit_tree_link(name, NULL, class, ctx.qry.head, | 165 | walk_tree_ctx->curr_rev, fullpath.buf); |
| 166 | walk_tree_ctx->curr_rev, fullpath); | ||
| 167 | } | 166 | } |
| 168 | htmlf("</td><td class='ls-size'>%li</td>", size); | 167 | htmlf("</td><td class='ls-size'>%li</td>", size); |
| 169 | 168 | ||
| 170 | html("<td>"); | 169 | html("<td>"); |
| 171 | cgit_log_link("log", NULL, "button", ctx.qry.head, | 170 | cgit_log_link("log", NULL, "button", ctx.qry.head, |
| 172 | walk_tree_ctx->curr_rev, fullpath, 0, NULL, NULL, | 171 | walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL, |
| 173 | ctx.qry.showmsg); | 172 | ctx.qry.showmsg); |
| 174 | if (ctx.repo->max_stats) | 173 | if (ctx.repo->max_stats) |
| 175 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, | 174 | cgit_stats_link("stats", NULL, "button", ctx.qry.head, |
| 176 | fullpath); | 175 | fullpath.buf); |
| 177 | if (!S_ISGITLINK(mode)) | 176 | if (!S_ISGITLINK(mode)) |
| 178 | cgit_plain_link("plain", NULL, "button", ctx.qry.head, | 177 | cgit_plain_link("plain", NULL, "button", ctx.qry.head, |
| 179 | walk_tree_ctx->curr_rev, fullpath); | 178 | walk_tree_ctx->curr_rev, fullpath.buf); |
| 180 | html("</td></tr>\n"); | 179 | html("</td></tr>\n"); |
| 181 | free(name); | 180 | free(name); |
| 181 | strbuf_release(&fullpath); | ||
| 182 | strbuf_release(&class); | ||
| 182 | return 0; | 183 | return 0; |
| 183 | } | 184 | } |
| 184 | 185 | ||
