diff options
author | June McEnroe <june@causal.agency> | 2020-12-27 18:45:04 -0500 |
---|---|---|
committer | June McEnroe <june@causal.agency> | 2020-12-27 18:45:04 -0500 |
commit | 85016e706cd00e527dba3fa83b2783dfb56a4ffa (patch) | |
tree | fc28a97fede3b74ae2cb2e778ffd4c0ae59c637b /scan-tree.c | |
download | src-85016e706cd00e527dba3fa83b2783dfb56a4ffa.tar.gz src-85016e706cd00e527dba3fa83b2783dfb56a4ffa.zip |
Squashed 'www/git.causal.agency/cgit/' content from commit 02221fd3
git-subtree-dir: www/git.causal.agency/cgit git-subtree-split: 02221fd3fe523a3293d64e3359036e3a71d6fd7e
Diffstat (limited to 'scan-tree.c')
-rw-r--r-- | scan-tree.c | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/scan-tree.c b/scan-tree.c new file mode 100644 index 00000000..6a2f65a8 --- /dev/null +++ b/scan-tree.c @@ -0,0 +1,270 @@ +/* scan-tree.c + * + * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com> + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "scan-tree.h" +#include "configfile.h" +#include "html.h" +#include <config.h> + +/* return 1 if path contains a objects/ directory and a HEAD file */ +static int is_git_dir(const char *path) +{ + struct stat st; + struct strbuf pathbuf = STRBUF_INIT; + int result = 0; + + 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); + goto out; + } + if (!S_ISDIR(st.st_mode)) + goto out; + + 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); + goto out; + } + if (!S_ISREG(st.st_mode)) + goto out; + + result = 1; +out: + strbuf_release(&pathbuf); + return result; +} + +static struct cgit_repo *repo; +static repo_config_fn config_fn; + +static void scan_tree_repo_config(const char *name, const char *value) +{ + config_fn(repo, name, value); +} + +static int gitconfig_config(const char *key, const char *value, void *cb) +{ + const char *name; + + if (!strcmp(key, "gitweb.owner")) + config_fn(repo, "owner", value); + else if (!strcmp(key, "gitweb.description")) + config_fn(repo, "desc", value); + else if (!strcmp(key, "gitweb.category")) + config_fn(repo, "section", value); + else if (!strcmp(key, "gitweb.homepage")) + config_fn(repo, "homepage", value); + else if (skip_prefix(key, "cgit.", &name)) + config_fn(repo, name, value); + + return 0; +} + +static char *xstrrchr(char *s, char *from, int c) +{ + while (from >= s && *from != c) + from--; + return from < s ? NULL : from; +} + +static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) +{ + struct stat st; + struct passwd *pwd; + size_t pathlen; + struct strbuf rel = STRBUF_INIT; + char *p, *slash; + int n; + size_t size; + + if (stat(path->buf, &st)) { + fprintf(stderr, "Error accessing %s: %s (%d)\n", + path->buf, strerror(errno), errno); + return; + } + + strbuf_addch(path, '/'); + pathlen = path->len; + + 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 (!starts_with(path->buf, base)) + strbuf_addbuf(&rel, path); + else + strbuf_addstr(&rel, path->buf + strlen(base) + 1); + + if (!strcmp(rel.buf + rel.len - 5, "/.git")) + strbuf_setlen(&rel, rel.len - 5); + else if (rel.len && rel.buf[rel.len - 1] == '/') + strbuf_setlen(&rel, rel.len - 1); + + repo = cgit_add_repo(rel.buf); + config_fn = fn; + 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) { + size_t urllen; + strip_suffix(repo->url, ".git", &urllen); + strip_suffix_mem(repo->url, &urllen, "/"); + repo->url[urllen] = '\0'; + } + 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->buf, strerror(errno), errno); + break; + } + if (pwd->pw_gecos) + if ((p = strchr(pwd->pw_gecos, ','))) + *p = '\0'; + repo->owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); + } + + if (repo->desc == cgit_default_repo_desc || !repo->desc) { + strbuf_addstr(path, "description"); + if (!stat(path->buf, &st)) + readfile(path->buf, &repo->desc, &size); + strbuf_setlen(path, pathlen); + } + + if (ctx.cfg.section_from_path) { + n = ctx.cfg.section_from_path; + if (n > 0) { + slash = rel.buf - 1; + while (slash && n && (slash = strchr(slash + 1, '/'))) + n--; + } else { + slash = rel.buf + rel.len; + while (slash && n && (slash = xstrrchr(rel.buf, slash - 1, '/'))) + n++; + } + if (slash && !n) { + *slash = '\0'; + repo->section = xstrdup(rel.buf); + *slash = '/'; + if (starts_with(repo->name, repo->section)) { + repo->name += strlen(repo->section); + if (*repo->name == '/') + repo->name++; + } + } + } + + strbuf_addstr(path, "cgitrc"); + if (!stat(path->buf, &st)) + parse_configfile(path->buf, &scan_tree_repo_config); + + strbuf_release(&rel); +} + +static void scan_path(const char *base, const char *path, repo_config_fn fn) +{ + DIR *dir = opendir(path); + struct dirent *ent; + struct strbuf pathbuf = STRBUF_INIT; + size_t pathlen = strlen(path); + struct stat st; + + if (!dir) { + fprintf(stderr, "Error opening directory %s: %s (%d)\n", + path, strerror(errno), errno); + return; + } + + strbuf_add(&pathbuf, path, strlen(path)); + if (is_git_dir(pathbuf.buf)) { + add_repo(base, &pathbuf, fn); + goto end; + } + 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') + continue; + if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') + continue; + if (!ctx.cfg.scan_hidden_path) + continue; + } + strbuf_setlen(&pathbuf, pathlen); + strbuf_addstr(&pathbuf, ent->d_name); + if (stat(pathbuf.buf, &st)) { + fprintf(stderr, "Error checking path %s: %s (%d)\n", + pathbuf.buf, strerror(errno), errno); + continue; + } + if (S_ISDIR(st.st_mode)) + scan_path(base, pathbuf.buf, fn); + } +end: + strbuf_release(&pathbuf); + closedir(dir); +} + +void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) +{ + struct strbuf line = STRBUF_INIT; + FILE *projects; + int err; + + projects = fopen(projectsfile, "r"); + if (!projects) { + fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n", + projectsfile, strerror(errno), errno); + return; + } + while (strbuf_getline(&line, projects) != 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) +{ + scan_path(path, path, fn); +} |