aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cache.c57
-rw-r--r--cgit.c72
-rw-r--r--scan-tree.c160
-rw-r--r--ui-log.c33
-rw-r--r--ui-plain.c6
-rw-r--r--ui-refs.c10
-rw-r--r--ui-repolist.c28
-rw-r--r--ui-shared.c63
-rw-r--r--ui-snapshot.c60
-rw-r--r--ui-summary.c12
-rw-r--r--ui-tag.c14
-rw-r--r--ui-tree.c33
12 files changed, 305 insertions, 243 deletions
diff --git a/cache.c b/cache.c
index 3127fc2..c1d777b 100644
--- a/cache.c
+++ b/cache.c
@@ -312,9 +312,9 @@ int cache_process(int size, const char *path, const char *key, int ttl,
cache_fill_fn fn, void *cbdata)
{
unsigned long hash;
- int len, i;
- char filename[1024];
- char lockname[1024 + 5]; /* 5 = ".lock" */
+ int i;
+ struct strbuf filename = STRBUF_INIT;
+ struct strbuf lockname = STRBUF_INIT;
struct cache_slot slot;
/* 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,
fn(cbdata);
return 0;
}
- len = strlen(path);
- if (len > sizeof(filename) - 10) { /* 10 = "/01234567\0" */
- cache_log("[cgit] Cache path too long, caching is disabled: %s\n",
- path);
- fn(cbdata);
- return 0;
- }
if (!key)
key = "";
hash = hash_str(key) % size;
- strcpy(filename, path);
- if (filename[len - 1] != '/')
- filename[len++] = '/';
+ strbuf_addstr(&filename, path);
+ strbuf_ensure_end(&filename, '/');
for (i = 0; i < 8; i++) {
- sprintf(filename + len++, "%x",
- (unsigned char)(hash & 0xf));
+ strbuf_addf(&filename, "%x", (unsigned char)(hash & 0xf));
hash >>= 4;
}
- filename[len] = '\0';
- strcpy(lockname, filename);
- strcpy(lockname + len, ".lock");
+ strbuf_addbuf(&lockname, &filename);
+ strbuf_addstr(&lockname, ".lock");
slot.fn = fn;
slot.cbdata = cbdata;
slot.ttl = ttl;
- slot.cache_name = filename;
- slot.lock_name = lockname;
+ slot.cache_name = strbuf_detach(&filename, NULL);
+ slot.lock_name = strbuf_detach(&lockname, NULL);
slot.key = key;
slot.keylen = strlen(key);
return process_slot(&slot);
@@ -381,18 +371,13 @@ int cache_ls(const char *path)
struct dirent *ent;
int err = 0;
struct cache_slot slot;
- char fullname[1024];
- char *name;
+ struct strbuf fullname = STRBUF_INIT;
+ size_t prefixlen;
if (!path) {
cache_log("[cgit] cache path not specified\n");
return -1;
}
- if (strlen(path) > 1024 - 10) {
- cache_log("[cgit] cache path too long: %s\n",
- path);
- return -1;
- }
dir = opendir(path);
if (!dir) {
err = errno;
@@ -400,30 +385,28 @@ int cache_ls(const char *path)
path, strerror(err), err);
return err;
}
- strcpy(fullname, path);
- name = fullname + strlen(path);
- if (*(name - 1) != '/') {
- *name++ = '/';
- *name = '\0';
- }
- slot.cache_name = fullname;
+ strbuf_addstr(&fullname, path);
+ strbuf_ensure_end(&fullname, '/');
+ prefixlen = fullname.len;
while ((ent = readdir(dir)) != NULL) {
if (strlen(ent->d_name) != 8)
continue;
- strcpy(name, ent->d_name);
+ strbuf_setlen(&fullname, prefixlen);
+ strbuf_addstr(&fullname, ent->d_name);
if ((err = open_slot(&slot)) != 0) {
cache_log("[cgit] unable to open path %s: %s (%d)\n",
- fullname, strerror(err), err);
+ fullname.buf, strerror(err), err);
continue;
}
printf("%s %s %10"PRIuMAX" %s\n",
- name,
+ fullname.buf,
sprintftime("%Y-%m-%d %H:%M:%S",
slot.cache_st.st_mtime),
(uintmax_t)slot.cache_st.st_size,
slot.buf);
close_slot(&slot);
}
+ slot.cache_name = strbuf_detach(&fullname, NULL);
closedir(dir);
return 0;
}
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)
if (nongit) {
const char *name = ctx->repo->name;
rc = errno;
- ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
- "config error");
+ ctx->page.title = fmtalloc("%s - %s", ctx->cfg.root_title,
+ "config error");
ctx->repo = NULL;
cgit_print_http_headers(ctx);
cgit_print_docstart(ctx);
@@ -479,7 +479,7 @@ static int prepare_repo_cmd(struct cgit_context *ctx)
cgit_print_docend();
return 1;
}
- ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
+ ctx->page.title = fmtalloc("%s - %s", ctx->repo->name, ctx->repo->desc);
if (!ctx->repo->defbranch)
ctx->repo->defbranch = guess_defbranch();
@@ -577,21 +577,16 @@ static int cmp_repos(const void *a, const void *b)
static char *build_snapshot_setting(int bitmap)
{
const struct cgit_snapshot_format *f;
- char *result = xstrdup("");
- char *tmp;
- int len;
+ struct strbuf result = STRBUF_INIT;
for (f = cgit_snapshot_formats; f->suffix; f++) {
if (f->bit & bitmap) {
- tmp = result;
- result = xstrdup(fmt("%s%s ", tmp, f->suffix));
- free(tmp);
+ if (result.len)
+ strbuf_addch(&result, ' ');
+ strbuf_addstr(&result, f->suffix);
}
}
- len = strlen(result);
- if (len)
- result[len - 1] = '\0';
- return result;
+ return strbuf_detach(&result, NULL);
}
static char *get_first_line(char *txt)
@@ -639,7 +634,7 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd);
if (repo->snapshots != ctx.cfg.snapshots) {
char *tmp = build_snapshot_setting(repo->snapshots);
- fprintf(f, "repo.snapshots=%s\n", tmp);
+ fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : "");
free(tmp);
}
if (repo->max_stats != ctx.cfg.max_stats)
@@ -661,20 +656,22 @@ static void print_repolist(FILE *f, struct cgit_repolist *list, int start)
*/
static int generate_cached_repolist(const char *path, const char *cached_rc)
{
- char *locked_rc;
+ struct strbuf locked_rc = STRBUF_INIT;
+ int result = 0;
int idx;
FILE *f;
- locked_rc = xstrdup(fmt("%s.lock", cached_rc));
- f = fopen(locked_rc, "wx");
+ strbuf_addf(&locked_rc, "%s.lock", cached_rc);
+ f = fopen(locked_rc.buf, "wx");
if (!f) {
/* Inform about the error unless the lockfile already existed,
* since that only means we've got concurrent requests.
*/
- if (errno != EEXIST)
+ result = errno;
+ if (result != EEXIST)
fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
- locked_rc, strerror(errno), errno);
- return errno;
+ locked_rc.buf, strerror(result), result);
+ goto out;
}
idx = cgit_repolist.count;
if (ctx.cfg.project_list)
@@ -682,55 +679,59 @@ static int generate_cached_repolist(const char *path, const char *cached_rc)
else
scan_tree(path, repo_config);
print_repolist(f, &cgit_repolist, idx);
- if (rename(locked_rc, cached_rc))
+ if (rename(locked_rc.buf, cached_rc))
fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
- locked_rc, cached_rc, strerror(errno), errno);
+ locked_rc.buf, cached_rc, strerror(errno), errno);
fclose(f);
- return 0;
+out:
+ strbuf_release(&locked_rc);
+ return result;
}
static void process_cached_repolist(const char *path)
{
struct stat st;
- char *cached_rc;
+ struct strbuf cached_rc = STRBUF_INIT;
time_t age;
unsigned long hash;
hash = hash_str(path);
if (ctx.cfg.project_list)
hash += hash_str(ctx.cfg.project_list);
- cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash));
+ strbuf_addf(&cached_rc, "%s/rc-%8lx", ctx.cfg.cache_root, hash);
- if (stat(cached_rc, &st)) {
+ if (stat(cached_rc.buf, &st)) {
/* Nothing is cached, we need to scan without forking. And
* if we fail to generate a cached repolist, we need to
* invoke scan_tree manually.
*/
- if (generate_cached_repolist(path, cached_rc)) {
+ if (generate_cached_repolist(path, cached_rc.buf)) {
if (ctx.cfg.project_list)
scan_projects(path, ctx.cfg.project_list,
repo_config);
else
scan_tree(path, repo_config);
}
- return;
+ goto out;
}
- parse_configfile(cached_rc, config_cb);
+ parse_configfile(cached_rc.buf, config_cb);
/* If the cached configfile hasn't expired, lets exit now */
age = time(NULL) - st.st_mtime;
if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
- return;
+ goto out;
/* The cached repolist has been parsed, but it was old. So lets
* rescan the specified path and generate a new cached repolist
* in a child-process to avoid latency for the current request.
*/
if (fork())
- return;
+ goto out;
- exit(generate_cached_repolist(path, cached_rc));
+ exit(generate_cached_repolist(path, cached_rc.buf));
+out:
+ strbuf_release(&cached_rc);
}
static void cgit_parse_args(int argc, const char **argv)
@@ -812,7 +813,6 @@ static int calc_ttl()
int main(int argc, const char **argv)
{
const char *path;
- char *qry;
int err, ttl;
prepare_context(&ctx);
@@ -843,9 +843,9 @@ int main(int argc, const char **argv)
path++;
ctx.qry.url = xstrdup(path);
if (ctx.qry.raw) {
- qry = ctx.qry.raw;
- ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
- free(qry);
+ char *newqry = fmtalloc("%s?%s", path, ctx.qry.raw);
+ free(ctx.qry.raw);
+ ctx.qry.raw = newqry;
} else
ctx.qry.raw = xstrdup(ctx.qry.url);
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 @@
#include "configfile.h"
#include "html.h"
-#define MAX_PATH 4096
-
/* return 1 if path contains a objects/ directory and a HEAD file */
static int is_git_dir(const char *path)
{
struct stat st;
- static char buf[MAX_PATH];
+ struct strbuf pathbuf = STRBUF_INIT;
+ int result = 0;
- if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
- fprintf(stderr, "Insanely long path: %s\n", path);
- return 0;
- }
- if (stat(buf, &st)) {
+ strbuf_addf(&pathbuf, "%s/objects", path);
+ if (stat(pathbuf.buf, &st)) {
if (errno != ENOENT)
fprintf(stderr, "Error checking path %s: %s (%d)\n",
path, strerror(errno), errno);
- return 0;
+ goto out;
}
if (!S_ISDIR(st.st_mode))
- return 0;
+ goto out;
- sprintf(buf, "%s/HEAD", path);
- if (stat(buf, &st)) {
+ strbuf_reset(&pathbuf);
+ strbuf_addf(&pathbuf, "%s/HEAD", path);
+ if (stat(pathbuf.buf, &st)) {
if (errno != ENOENT)
fprintf(stderr, "Error checking path %s: %s (%d)\n",
path, strerror(errno), errno);
- return 0;
+ goto out;
}
if (!S_ISREG(st.st_mode))
- return 0;
+ goto out;
- return 1;
+ result = 1;
+out:
+ strbuf_release(&pathbuf);
+ return result;
}
struct cgit_repo *repo;
@@ -75,47 +75,61 @@ static char *xstrrchr(char *s, char *from, int c)
return from < s ? NULL : from;
}
-static void add_repo(const char *base, const char *path, repo_config_fn fn)
+static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
{
struct stat st;
struct passwd *pwd;
- char *rel, *p, *slash;
+ size_t pathlen;
+ struct strbuf rel = STRBUF_INIT;
+ char *p, *slash;
int n;
size_t size;
- if (stat(path, &st)) {
+ if (stat(path->buf, &st)) {
fprintf(stderr, "Error accessing %s: %s (%d)\n",
- path, strerror(errno), errno);
+ path->buf, strerror(errno), errno);
return;
}
- if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st))
- return;
+ strbuf_addch(path, '/');
+ pathlen = path->len;
- if (!stat(fmt("%s/noweb", path), &st))
+ if (ctx.cfg.strict_export) {
+ strbuf_addstr(path, ctx.cfg.strict_export);
+ if(stat(path->buf, &st))
+ return;
+ strbuf_setlen(path, pathlen);
+ }
+
+ strbuf_addstr(path, "noweb");
+ if (!stat(path->buf, &st))
return;
+ strbuf_setlen(path, pathlen);
- if (base == path)
- rel = xstrdup(path);
+ if (strncmp(base, path->buf, strlen(base)))
+ strbuf_addbuf(&rel, path);
else
- rel = xstrdup(path + strlen(base) + 1);
+ strbuf_addstr(&rel, path->buf + strlen(base) + 1);
- if (!strcmp(rel + strlen(rel) - 5, "/.git"))
- rel[strlen(rel) - 5] = '\0';
+ if (!strcmp(rel.buf + rel.len - 5, "/.git"))
+ strbuf_setlen(&rel, rel.len - 5);
- repo = cgit_add_repo(rel);
+ repo = cgit_add_repo(rel.buf);
config_fn = fn;
- if (ctx.cfg.enable_git_config)
- git_config_from_file(gitconfig_config, fmt("%s/config", path), NULL);
+ if (ctx.cfg.enable_git_config) {
+ strbuf_addstr(path, "config");
+ git_config_from_file(gitconfig_config, path->buf, NULL);
+ strbuf_setlen(path, pathlen);
+ }
if (ctx.cfg.remove_suffix)
if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
*p = '\0';
- repo->path = xstrdup(path);
+ repo->path = xstrdup(path->buf);
while (!repo->owner) {
if ((pwd = getpwuid(st.st_uid)) == NULL) {
fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
- path, strerror(errno), errno);
+ path->buf, strerror(errno), errno);
break;
}
if (pwd->pw_gecos)
@@ -125,30 +139,32 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
}
if (repo->desc == cgit_default_repo_desc || !repo->desc) {
- p = fmt("%s/description", path);
- if (!stat(p, &st))
- readfile(p, &repo->desc, &size);
+ strbuf_addstr(path, "description");
+ if (!stat(path->buf, &st))
+ readfile(path->buf, &repo->desc, &size);
+ strbuf_setlen(path, pathlen);
}
if (!repo->readme) {
- p = fmt("%s/README.html", path);
- if (!stat(p, &st))
+ strbuf_addstr(path, "README.html");
+ if (!stat(path->buf, &st))
repo->readme = "README.html";
+ strbuf_setlen(path, pathlen);
}
if (ctx.cfg.section_from_path) {
n = ctx.cfg.section_from_path;
if (n > 0) {
- slash = rel;
+ slash = rel.buf;
while (slash && n && (slash = strchr(slash, '/')))
n--;
} else {
- slash = rel + strlen(rel);
- while (slash && n && (slash = xstrrchr(rel, slash, '/')))
+ slash = rel.buf + rel.len;
+ while (slash && n && (slash = xstrrchr(rel.buf, slash, '/')))
n++;
}
if (slash && !n) {
*slash = '\0';
- repo->section = xstrdup(rel);
+ repo->section = xstrdup(rel.buf);
*slash = '/';
if (!prefixcmp(repo->name, repo->section)) {
repo->name += strlen(repo->section);
@@ -158,19 +174,19 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
}
}
- p = fmt("%s/cgitrc", path);
- if (!stat(p, &st))
- parse_configfile(xstrdup(p), &repo_config);
-
+ strbuf_addstr(path, "cgitrc");
+ if (!stat(path->buf, &st))
+ parse_configfile(xstrdup(path->buf), &repo_config);
- free(rel);
+ strbuf_release(&rel);
}
static void scan_path(const char *base, const char *path, repo_config_fn fn)
{
DIR *dir = opendir(path);
struct dirent *ent;
- char *buf;
+ struct strbuf pathbuf = STRBUF_INIT;
+ size_t pathlen = strlen(path);
struct stat st;
if (!dir) {
@@ -178,14 +194,22 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn)
path, strerror(errno), errno);
return;
}
- if (is_git_dir(path)) {
- add_repo(base, path, fn);
+
+ strbuf_add(&pathbuf, path, strlen(path));
+ if (is_git_dir(pathbuf.buf)) {
+ add_repo(base, &pathbuf, fn);
goto end;
}
- if (is_git_dir(fmt("%s/.git", path))) {
- add_repo(base, fmt("%s/.git", path), fn);
+ strbuf_addstr(&pathbuf, "/.git");
+ if (is_git_dir(pathbuf.buf)) {
+ add_repo(base, &pathbuf, fn);
goto end;
}
+ /*
+ * Add one because we don't want to lose the trailing '/' when we
+ * reset the length of pathbuf in the loop below.
+ */
+ pathlen++;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.') {
if (ent->d_name[1] == '\0')
@@ -195,24 +219,18 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn)
if (!ctx.cfg.scan_hidden_path)
continue;
}
- buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
- if (!buf) {
- fprintf(stderr, "Alloc error on %s: %s (%d)\n",
- path, strerror(errno), errno);
- exit(1);
- }
- sprintf(buf, "%s/%s", path, ent->d_name);
- if (stat(buf, &st)) {
+ strbuf_setlen(&pathbuf, pathlen);
+ strbuf_addstr(&pathbuf, ent->d_name);
+ if (stat(pathbuf.buf, &st)) {
fprintf(stderr, "Error checking path %s: %s (%d)\n",
- buf, strerror(errno), errno);
- free(buf);
+ pathbuf.buf, strerror(errno), errno);
continue;
}
if (S_ISDIR(st.st_mode))
- scan_path(base, buf, fn);
- free(buf);
+ scan_path(base, pathbuf.buf, fn);
}
end:
+ strbuf_release(&pathbuf);
closedir(dir);
}
@@ -220,7 +238,7 @@ end:
void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
{
- char line[MAX_PATH * 2], *z;
+ struct strbuf line = STRBUF_INIT;
FILE *projects;
int err;
@@ -230,19 +248,19 @@ void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn
projectsfile, strerror(errno), errno);
return;
}
- while (fgets(line, sizeof(line), projects) != NULL) {
- for (z = &lastc(line);
- strlen(line) && strchr("\n\r", *z);
- z = &lastc(line))
- *z = '\0';
- if (strlen(line))
- scan_path(path, fmt("%s/%s", path, line), fn);
+ while (strbuf_getline(&line, projects, '\n') != EOF) {
+ if (!line.len)
+ continue;
+ strbuf_insert(&line, 0, "/", 1);
+ strbuf_insert(&line, 0, path, strlen(path));
+ scan_path(path, line.buf, fn);
}
if ((err = ferror(projects))) {
fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
projectsfile, strerror(err), err);
}
fclose(projects);
+ strbuf_release(&line);
}
void 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)
cgit_free_commitinfo(info);
}
-static const char *disambiguate_ref(const char *ref)
+static const char *disambiguate_ref(const char *ref, int *must_free_result)
{
unsigned char sha1[20];
- const char *longref;
+ struct strbuf longref = STRBUF_INIT;
- longref = fmt("refs/heads/%s", ref);
- if (get_sha1(longref, sha1) == 0)
- return longref;
+ strbuf_addf(&longref, "refs/heads/%s", ref);
+ if (get_sha1(longref.buf, sha1) == 0) {
+ *must_free_result = 1;
+ return strbuf_detach(&longref, NULL);
+ }
+ *must_free_result = 0;
+ strbuf_release(&longref);
return ref;
}
@@ -284,24 +288,26 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
struct commit *commit;
struct vector vec = VECTOR_INIT(char *);
int i, columns = commit_graph ? 4 : 3;
- char *arg;
+ int must_free_tip = 0;
+ struct strbuf argbuf = STRBUF_INIT;
/* First argv is NULL */
vector_push(&vec, NULL, 0);
if (!tip)
tip = ctx.qry.head;
- tip = disambiguate_ref(tip);
+ tip = disambiguate_ref(tip, &must_free_tip);
vector_push(&vec, &tip, 0);
if (grep && pattern && *pattern) {
pattern = xstrdup(pattern);
if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
!strcmp(grep, "committer")) {
- arg = fmt("--%s=%s", grep, pattern);
- vector_push(&vec, &arg, 0);
+ strbuf_addf(&argbuf, "--%s=%s", grep, pattern);
+ vector_push(&vec, &argbuf.buf, 0);
}
if (!strcmp(grep, "range")) {
+ char *arg;
/* Split the pattern at whitespace and add each token
* as a revision expression. Do not accept other
* 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
}
if (path) {
- arg = "--";
- vector_push(&vec, &arg, 0);
+ static const char *double_dash_arg = "--";
+ vector_push(&vec, &double_dash_arg, 0);
vector_push(&vec, &path, 0);
}
@@ -430,4 +436,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg);
html("</td></tr>\n");
}
+
+ /* If we allocated tip then it is safe to cast away const. */
+ if (must_free_tip)
+ free((char*) tip);
+ strbuf_release(&argbuf);
}
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)
static char *buildpath(const char *base, int baselen, const char *path)
{
if (path[0])
- return fmt("%.*s%s/", baselen, base, path);
+ return fmtalloc("%.*s%s/", baselen, base, path);
else
- return fmt("%.*s/", baselen, base);
+ return fmtalloc("%.*s/", baselen, base);
}
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,
fullpath);
html("</li>\n");
}
+ free(fullpath);
}
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,
cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
fullpath);
html("</li>\n");
+ free(fullpath);
}
static 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()
static void print_tag_downloads(const struct cgit_repo *repo, const char *ref)
{
const struct cgit_snapshot_format* f;
- char *filename;
+ struct strbuf filename = STRBUF_INIT;
const char *basename;
int free_ref = 0;
@@ -111,7 +111,7 @@ static void print_tag_downloads(const struct cgit_repo *repo, const char *ref)
if ((ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]))
ref++;
if (isdigit(ref[0])) {
- ref = xstrdup(fmt("%s-%s", basename, ref));
+ ref = fmtalloc("%s-%s", basename, ref);
free_ref = 1;
}
}
@@ -119,13 +119,15 @@ static void print_tag_downloads(const struct cgit_repo *repo, const char *ref)
for (f = cgit_snapshot_formats; f->suffix; f++) {
if (!(repo->snapshots & f->bit))
continue;
- filename = fmt("%s%s", ref, f->suffix);
- cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename);
+ strbuf_reset(&filename);
+ strbuf_addf(&filename, "%s%s", ref, f->suffix);
+ cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, filename.buf);
html("&nbsp;&nbsp;");
}
if (free_ref)
free((char *)ref);
+ strbuf_release(&filename);
}
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)
static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
{
- char *path;
+ struct strbuf path = STRBUF_INIT;
struct stat s;
struct cgit_repo *r = (struct cgit_repo *)repo;
@@ -41,32 +41,36 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
*mtime = repo->mtime;
return 1;
}
- path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
- if (stat(path, &s) == 0) {
- *mtime = read_agefile(path);
+ strbuf_addf(&path, "%s/%s", repo->path, ctx.cfg.agefile);
+ if (stat(path.buf, &s) == 0) {
+ *mtime = read_agefile(path.buf);
if (*mtime) {
r->mtime = *mtime;
- return 1;
+ goto end;
}
}
- path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch ?
- repo->defbranch : "master");
- if (stat(path, &s) == 0) {
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/refs/heads/%s", repo->path,
+ repo->defbranch ? repo->defbranch : "master");
+ if (stat(path.buf, &s) == 0) {
*mtime = s.st_mtime;
r->mtime = *mtime;
- return 1;
+ goto end;
}
- path = fmt("%s/%s", repo->path, "packed-refs");
- if (stat(path, &s) == 0) {
+ strbuf_reset(&path);
+ strbuf_addf(&path, "%s/%s", repo->path, "packed-refs");
+ if (stat(path.buf, &s) == 0) {
*mtime = s.st_mtime;
r->mtime = *mtime;
- return 1;
+ goto end;
}
*mtime = 0;
r->mtime = *mtime;
+end:
+ strbuf_release(&path);
return (r->mtime != 0);
}
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()
return NULL;
if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
return ctx.env.server_name;
- return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port));
+ return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port);
}
const char *cgit_rooturl()
@@ -75,31 +75,30 @@ const char *cgit_rooturl()
char *cgit_repourl(const char *reponame)
{
- if (ctx.cfg.virtual_root) {
- return fmt("%s%s/", ctx.cfg.virtual_root, reponame);
- } else {
- return fmt("?r=%s", reponame);
- }
+ if (ctx.cfg.virtual_root)
+ return fmtalloc("%s%s/", ctx.cfg.virtual_root, reponame);
+ else
+ return fmtalloc("?r=%s", reponame);
}
char *cgit_fileurl(const char *reponame, const char *pagename,
const char *filename, const char *query)
{
- char *tmp;
+ struct strbuf sb = STRBUF_INIT;
char *delim;
if (ctx.cfg.virtual_root) {
- tmp = fmt("%s%s/%s/%s", ctx.cfg.virtual_root, reponame,
- pagename, (filename ? filename:""));
+ strbuf_addf(&sb, "%s%s/%s/%s", ctx.cfg.virtual_root, reponame,
+ pagename, (filename ? filename:""));
delim = "?";
} else {
- tmp = fmt("?url=%s/%s/%s", reponame, pagename,
- (filename ? filename : ""));
+ strbuf_addf(&sb, "?url=%s/%s/%s", reponame, pagename,
+ (filename ? filename : ""));
delim = "&amp;";
}
if (query)
- tmp = fmt("%s%s%s", tmp, delim, query);
- return tmp;
+ strbuf_addf(&sb, "%s%s", delim, query);
+ return strbuf_detach(&sb, NULL);
}
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)
htmlf("class='%s' ", class);
html("href='");
if (item) {
- html_attr(fmt(item->util, rev));
+ html_attrf(item->util, rev);
} else if (ctx.repo->module_link) {
dir = strrchr(path, '/');
if (dir)
dir++;
else
dir = path;
- html_attr(fmt(ctx.repo->module_link, dir, rev));
+ html_attrf(ctx.repo->module_link, dir, rev);
} else {
html("#");
}
html("'>");
html_txt(path);
html("</a>");
- html_txt(fmt(" @ %.7s", rev));
+ html_txtf(" @ %.7s", rev);
if (item && tail)
path[len - 1] = tail;
}
@@ -678,12 +677,16 @@ void cgit_print_docstart(struct cgit_context *ctx)
html("'/>\n");
}
if (host && ctx->repo && ctx->qry.head) {
+ struct strbuf sb = STRBUF_INIT;
+ strbuf_addf(&sb, "h=%s", ctx->qry.head);
+
html("<link rel='alternate' title='Atom feed' href='");
html(cgit_httpscheme());
html_attr(cgit_hosturl());
html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
- fmt("h=%s", ctx->qry.head)));
+ sb.buf));
html("' type='application/atom+xml'/>\n");
+ strbuf_release(&sb);
}
if (ctx->cfg.head_include)
html_include(ctx->cfg.head_include);
@@ -725,13 +728,14 @@ static int print_branch_option(const char *refname, const unsigned char *sha1,
void cgit_add_hidden_formfields(int incl_head, int incl_search,
const char *page)
{
- char *url;
-
if (!ctx.cfg.virtual_root) {
- url = fmt("%s/%s", ctx.qry.repo, page);
+ struct strbuf url = STRBUF_INIT;
+
+ strbuf_addf(&url, "%s/%s", ctx.qry.repo, page);
if (ctx.qry.vpath)
- url = fmt("%s/%s", url, ctx.qry.vpath);
- html_hidden("url", url);
+ strbuf_addf(&url, "/%s", ctx.qry.vpath);
+ html_hidden("url", url.buf);
+ strbuf_release(&url);
}
if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
@@ -926,20 +930,23 @@ void cgit_print_snapshot_links(const char *repo, const char *head,
const char *hex, int snapshots)
{
const struct cgit_snapshot_format* f;
- char *prefix;
- char *filename;
+ struct strbuf filename = STRBUF_INIT;
+ size_t prefixlen;
unsigned char sha1[20];
if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 &&
(hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1]))
hex++;
- prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex));
+ strbuf_addf(&filename, "%s-%s", cgit_repobasename(repo), hex);
+ prefixlen = filename.len;
for (f = cgit_snapshot_formats; f->suffix; f++) {
if (!(snapshots & f->bit))
continue;
- filename = fmt("%s%s", prefix, f->suffix);
- cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename);
+ strbuf_setlen(&filename, prefixlen);
+ strbuf_addstr(&filename, f->suffix);
+ cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL,
+ filename.buf);
html("<br/>");
}
- free(prefix);
+ strbuf_release(&filename);
}
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 @@
static int write_archive_type(const char *format, const char *hex, const char *prefix)
{
struct argv_array argv = ARGV_ARRAY_INIT;
+ const char **nargv;
+ int result;
argv_array_push(&argv, "snapshot");
argv_array_push(&argv, format);
if (prefix) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addstr(&buf, prefix);
+ strbuf_addch(&buf, '/');
argv_array_push(&argv, "--prefix");
- argv_array_push(&argv, fmt("%s/", prefix));
+ argv_array_push(&argv, buf.buf);
+ strbuf_release(&buf);
}
argv_array_push(&argv, hex);
- return write_archive(argv.argc, argv.argv, NULL, 1, NULL, 0);
+ /*
+ * Now we need to copy the pointers to arguments into a new
+ * structure because write_archive will rearrange its arguments
+ * which may result in duplicated/missing entries causing leaks
+ * or double-frees in argv_array_clear.
+ */
+ nargv = xmalloc(sizeof(char *) * (argv.argc + 1));
+ /* argv_array guarantees a trailing NULL entry. */
+ memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1));
+
+ result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0);
+ argv_array_clear(&argv);
+ free(nargv);
+ return result;
}
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,
{
const char *reponame;
unsigned char sha1[20];
- char *snapshot;
+ struct strbuf snapshot = STRBUF_INIT;
+ int result = 1;
- snapshot = xstrdup(filename);
- snapshot[strlen(snapshot) - strlen(format->suffix)] = '\0';
+ strbuf_addstr(&snapshot, filename);
+ strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix));
- if (get_sha1(snapshot, sha1) == 0)
- return snapshot;
+ if (get_sha1(snapshot.buf, sha1) == 0)
+ goto out;
reponame = cgit_repobasename(url);
- if (prefixcmp(snapshot, reponame) == 0) {
- snapshot += strlen(reponame);
- while (snapshot && (*snapshot == '-' || *snapshot == '_'))
- snapshot++;
+ if (prefixcmp(snapshot.buf, reponame) == 0) {
+ const char *new_start = snapshot.buf;
+ new_start += strlen(reponame);
+ while (new_start && (*new_start == '-' || *new_start == '_'))
+ new_start++;
+ strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0);
}
- if (get_sha1(snapshot, sha1) == 0)
- return snapshot;
+ if (get_sha1(snapshot.buf, sha1) == 0)
+ goto out;
- snapshot = fmt("v%s", snapshot);
- if (get_sha1(snapshot, sha1) == 0)
- return snapshot;
+ strbuf_insert(&snapshot, 0, "v", 1);
+ if (get_sha1(snapshot.buf, sha1) == 0)
+ goto out;
- return NULL;
+ result = 0;
+ strbuf_release(&snapshot);
+
+out:
+ return result ? strbuf_detach(&snapshot, NULL) : NULL;
}
__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 @@
static void print_url(char *base, char *suffix)
{
int columns = 3;
+ struct strbuf basebuf = STRBUF_INIT;
if (ctx.repo->enable_log_filecount)
columns++;
@@ -25,13 +26,16 @@ static void print_url(char *base, char *suffix)
if (!base || !*base)
return;
- if (suffix && *suffix)
- base = fmt("%s/%s", base, suffix);
+ if (suffix && *suffix) {
+ strbuf_addf(&basebuf, "%s/%s", base, suffix);
+ base = basebuf.buf;
+ }
htmlf("<tr><td colspan='%d'><a href='", columns);
html_url_path(base);
html("'>");
html_txt(base);
html("</a></td></tr>\n");
+ strbuf_release(&basebuf);
}
static void print_urls(char *txt, char *suffix)
@@ -112,8 +116,8 @@ void cgit_print_repo_readme(char *path)
/* Prepend repo path to relative readme path unless tracked. */
if (!ref && *ctx.repo->readme != '/')
- ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path,
- ctx.repo->readme));
+ ctx.repo->readme = fmtalloc("%s/%s", ctx.repo->path,
+ ctx.repo->readme);
/* If a subpath is specified for the about page, make it relative
* 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)
void cgit_print_tag(char *revname)
{
+ struct strbuf fullref = STRBUF_INIT;
unsigned char sha1[20];
struct object *obj;
struct tag *tag;
@@ -49,20 +50,21 @@ void cgit_print_tag(char *revname)
if (!revname)
revname = ctx.qry.head;
- if (get_sha1(fmt("refs/tags/%s", revname), sha1)) {
+ strbuf_addf(&fullref, "refs/tags/%s", revname);
+ if (get_sha1(fullref.buf, sha1)) {
cgit_print_error("Bad tag reference: %s", revname);
- return;
+ goto cleanup;
}
obj = parse_object(sha1);
if (!obj) {
cgit_print_error("Bad object id: %s", sha1_to_hex(sha1));
- return;
+ goto cleanup;
}
if (obj->type == OBJ_TAG) {
tag = lookup_tag(sha1);
if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
cgit_print_error("Bad tag object: %s", revname);
- return;
+ goto cleanup;
}
html("<table class='commit-info'>\n");
htmlf("<tr><td>tag name</td><td>");
@@ -101,5 +103,7 @@ void cgit_print_tag(char *revname)
print_download_links(revname);
html("</table>\n");
}
- return;
+
+cleanup:
+ strbuf_release(&fullref);
}
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,
{
struct walk_tree_context *walk_tree_ctx = cbdata;
char *name;
- char *fullpath;
- char *class;
+ struct strbuf fullpath = STRBUF_INIT;
+ struct strbuf class = STRBUF_INIT;
enum object_type type;
unsigned long size = 0;
name = xstrdup(pathname);
- fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
- ctx.qry.path ? "/" : "", name);
+ strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "",
+ ctx.qry.path ? "/" : "", name);
if (!S_ISGITLINK(mode)) {
type = sha1_object_info(sha1, &size);
@@ -152,33 +152,34 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen,
cgit_print_filemode(mode);
html("</td><td>");
if (S_ISGITLINK(mode)) {
- cgit_submodule_link("ls-mod", fullpath, sha1_to_hex(sha1));
+ cgit_submodule_link("ls-mod", fullpath.buf, sha1_to_hex(sha1));
} else if (S_ISDIR(mode)) {
cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
- walk_tree_ctx->curr_rev, fullpath);
+ walk_tree_ctx->curr_rev, fullpath.buf);
} else {
- class = strrchr(name, '.');
- if (class != NULL) {
- class = fmt("ls-blob %s", class + 1);
- } else
- class = "ls-blob";
- cgit_tree_link(name, NULL, class, ctx.qry.head,
- walk_tree_ctx->curr_rev, fullpath);
+ char *ext = strrchr(name, '.');
+ strbuf_addstr(&class, "ls-blob");
+ if (ext)
+ strbuf_addf(&class, " %s", ext + 1);
+ cgit_tree_link(name, NULL, class.buf, ctx.qry.head,
+ walk_tree_ctx->curr_rev, fullpath.buf);
}
htmlf("</td><td class='ls-size'>%li</td>", size);
html("<td>");
cgit_log_link("log", NULL, "button", ctx.qry.head,
- walk_tree_ctx->curr_rev, fullpath, 0, NULL, NULL,
+ walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL,
ctx.qry.showmsg);
if (ctx.repo->max_stats)
cgit_stats_link("stats", NULL, "button", ctx.qry.head,
- fullpath);
+ fullpath.buf);
if (!S_ISGITLINK(mode))
cgit_plain_link("plain", NULL, "button", ctx.qry.head,
- walk_tree_ctx->curr_rev, fullpath);
+ walk_tree_ctx->curr_rev, fullpath.buf);
html("</td></tr>\n");
free(name);
+ strbuf_release(&fullpath);
+ strbuf_release(&class);
return 0;
}