neocgit

a more 'modern' version of cgit
Log | Files | Refs | Submodules | README | LICENSE | git clone https://git.ne02ptzero.me/git/neocgit

scan-tree.c (6505B)


      1 /* scan-tree.c
      2  *
      3  * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
      4  *
      5  * Licensed under GNU General Public License v2
      6  *   (see COPYING for full license text)
      7  */
      8 
      9 #include "cgit.h"
     10 #include "scan-tree.h"
     11 #include "configfile.h"
     12 #include "html.h"
     13 #include <config.h>
     14 
     15 /* return 1 if path contains a objects/ directory and a HEAD file */
     16 static int is_git_dir(const char *path)
     17 {
     18 	struct stat st;
     19 	struct strbuf pathbuf = STRBUF_INIT;
     20 	int result = 0;
     21 
     22 	strbuf_addf(&pathbuf, "%s/objects", path);
     23 	if (stat(pathbuf.buf, &st)) {
     24 		if (errno != ENOENT)
     25 			fprintf(stderr, "Error checking path %s: %s (%d)\n",
     26 				path, strerror(errno), errno);
     27 		goto out;
     28 	}
     29 	if (!S_ISDIR(st.st_mode))
     30 		goto out;
     31 
     32 	strbuf_reset(&pathbuf);
     33 	strbuf_addf(&pathbuf, "%s/HEAD", path);
     34 	if (stat(pathbuf.buf, &st)) {
     35 		if (errno != ENOENT)
     36 			fprintf(stderr, "Error checking path %s: %s (%d)\n",
     37 				path, strerror(errno), errno);
     38 		goto out;
     39 	}
     40 	if (!S_ISREG(st.st_mode))
     41 		goto out;
     42 
     43 	result = 1;
     44 out:
     45 	strbuf_release(&pathbuf);
     46 	return result;
     47 }
     48 
     49 static struct cgit_repo *repo;
     50 static repo_config_fn config_fn;
     51 
     52 static void scan_tree_repo_config(const char *name, const char *value)
     53 {
     54 	config_fn(repo, name, value);
     55 }
     56 
     57 static int gitconfig_config(const char *key, const char *value, void *cb)
     58 {
     59 	const char *name;
     60 
     61 	if (!strcmp(key, "gitweb.owner"))
     62 		config_fn(repo, "owner", value);
     63 	else if (!strcmp(key, "gitweb.description"))
     64 		config_fn(repo, "desc", value);
     65 	else if (!strcmp(key, "gitweb.category"))
     66 		config_fn(repo, "section", value);
     67 	else if (!strcmp(key, "gitweb.homepage"))
     68 		config_fn(repo, "homepage", value);
     69 	else if (skip_prefix(key, "cgit.", &name))
     70 		config_fn(repo, name, value);
     71 
     72 	return 0;
     73 }
     74 
     75 static char *xstrrchr(char *s, char *from, int c)
     76 {
     77 	while (from >= s && *from != c)
     78 		from--;
     79 	return from < s ? NULL : from;
     80 }
     81 
     82 static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
     83 {
     84 	struct stat st;
     85 	struct passwd *pwd;
     86 	size_t pathlen;
     87 	struct strbuf rel = STRBUF_INIT;
     88 	char *p, *slash;
     89 	int n;
     90 	size_t size;
     91 
     92 	if (stat(path->buf, &st)) {
     93 		fprintf(stderr, "Error accessing %s: %s (%d)\n",
     94 			path->buf, strerror(errno), errno);
     95 		return;
     96 	}
     97 
     98 	strbuf_addch(path, '/');
     99 	pathlen = path->len;
    100 
    101 	if (ctx.cfg.strict_export) {
    102 		strbuf_addstr(path, ctx.cfg.strict_export);
    103 		if(stat(path->buf, &st))
    104 			return;
    105 		strbuf_setlen(path, pathlen);
    106 	}
    107 
    108 	strbuf_addstr(path, "noweb");
    109 	if (!stat(path->buf, &st))
    110 		return;
    111 	strbuf_setlen(path, pathlen);
    112 
    113 	if (!starts_with(path->buf, base))
    114 		strbuf_addbuf(&rel, path);
    115 	else
    116 		strbuf_addstr(&rel, path->buf + strlen(base) + 1);
    117 
    118 	if (!strcmp(rel.buf + rel.len - 5, "/.git"))
    119 		strbuf_setlen(&rel, rel.len - 5);
    120 	else if (rel.len && rel.buf[rel.len - 1] == '/')
    121 		strbuf_setlen(&rel, rel.len - 1);
    122 
    123 	repo = cgit_add_repo(rel.buf);
    124 	config_fn = fn;
    125 	if (ctx.cfg.enable_git_config) {
    126 		strbuf_addstr(path, "config");
    127 		git_config_from_file(gitconfig_config, path->buf, NULL);
    128 		strbuf_setlen(path, pathlen);
    129 	}
    130 
    131 	if (ctx.cfg.remove_suffix) {
    132 		size_t urllen;
    133 		strip_suffix(repo->url, ".git", &urllen);
    134 		strip_suffix_mem(repo->url, &urllen, "/");
    135 		repo->url[urllen] = '\0';
    136 	}
    137 	repo->path = xstrdup(path->buf);
    138 	while (!repo->owner) {
    139 		if ((pwd = getpwuid(st.st_uid)) == NULL) {
    140 			fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
    141 				path->buf, strerror(errno), errno);
    142 			break;
    143 		}
    144 		if (pwd->pw_gecos)
    145 			if ((p = strchr(pwd->pw_gecos, ',')))
    146 				*p = '\0';
    147 		repo->owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
    148 	}
    149 
    150 	if (repo->desc == cgit_default_repo_desc || !repo->desc) {
    151 		strbuf_addstr(path, "description");
    152 		if (!stat(path->buf, &st))
    153 			readfile(path->buf, &repo->desc, &size);
    154 		strbuf_setlen(path, pathlen);
    155 	}
    156 
    157 	if (ctx.cfg.section_from_path) {
    158 		n = ctx.cfg.section_from_path;
    159 		if (n > 0) {
    160 			slash = rel.buf - 1;
    161 			while (slash && n && (slash = strchr(slash + 1, '/')))
    162 				n--;
    163 		} else {
    164 			slash = rel.buf + rel.len;
    165 			while (slash && n && (slash = xstrrchr(rel.buf, slash - 1, '/')))
    166 				n++;
    167 		}
    168 		if (slash && !n) {
    169 			*slash = '\0';
    170 			repo->section = xstrdup(rel.buf);
    171 			*slash = '/';
    172 			if (starts_with(repo->name, repo->section)) {
    173 				repo->name += strlen(repo->section);
    174 				if (*repo->name == '/')
    175 					repo->name++;
    176 			}
    177 		}
    178 	}
    179 
    180 	strbuf_addstr(path, "cgitrc");
    181 	if (!stat(path->buf, &st))
    182 		parse_configfile(path->buf, &scan_tree_repo_config);
    183 
    184 	strbuf_release(&rel);
    185 }
    186 
    187 static void scan_path(const char *base, const char *path, repo_config_fn fn)
    188 {
    189 	DIR *dir = opendir(path);
    190 	struct dirent *ent;
    191 	struct strbuf pathbuf = STRBUF_INIT;
    192 	size_t pathlen = strlen(path);
    193 	struct stat st;
    194 
    195 	if (!dir) {
    196 		fprintf(stderr, "Error opening directory %s: %s (%d)\n",
    197 			path, strerror(errno), errno);
    198 		return;
    199 	}
    200 
    201 	strbuf_add(&pathbuf, path, strlen(path));
    202 	if (is_git_dir(pathbuf.buf)) {
    203 		add_repo(base, &pathbuf, fn);
    204 		goto end;
    205 	}
    206 	strbuf_addstr(&pathbuf, "/.git");
    207 	if (is_git_dir(pathbuf.buf)) {
    208 		add_repo(base, &pathbuf, fn);
    209 		goto end;
    210 	}
    211 	/*
    212 	 * Add one because we don't want to lose the trailing '/' when we
    213 	 * reset the length of pathbuf in the loop below.
    214 	 */
    215 	pathlen++;
    216 	while ((ent = readdir(dir)) != NULL) {
    217 		if (ent->d_name[0] == '.') {
    218 			if (ent->d_name[1] == '\0')
    219 				continue;
    220 			if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
    221 				continue;
    222 			if (!ctx.cfg.scan_hidden_path)
    223 				continue;
    224 		}
    225 		strbuf_setlen(&pathbuf, pathlen);
    226 		strbuf_addstr(&pathbuf, ent->d_name);
    227 		if (stat(pathbuf.buf, &st)) {
    228 			fprintf(stderr, "Error checking path %s: %s (%d)\n",
    229 				pathbuf.buf, strerror(errno), errno);
    230 			continue;
    231 		}
    232 		if (S_ISDIR(st.st_mode))
    233 			scan_path(base, pathbuf.buf, fn);
    234 	}
    235 end:
    236 	strbuf_release(&pathbuf);
    237 	closedir(dir);
    238 }
    239 
    240 void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
    241 {
    242 	struct strbuf line = STRBUF_INIT;
    243 	FILE *projects;
    244 	int err;
    245 
    246 	projects = fopen(projectsfile, "r");
    247 	if (!projects) {
    248 		fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
    249 			projectsfile, strerror(errno), errno);
    250 		return;
    251 	}
    252 	while (strbuf_getline(&line, projects) != EOF) {
    253 		if (!line.len)
    254 			continue;
    255 		strbuf_insert(&line, 0, "/", 1);
    256 		strbuf_insert(&line, 0, path, strlen(path));
    257 		scan_path(path, line.buf, fn);
    258 	}
    259 	if ((err = ferror(projects))) {
    260 		fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
    261 			projectsfile, strerror(err), err);
    262 	}
    263 	fclose(projects);
    264 	strbuf_release(&line);
    265 }
    266 
    267 void scan_tree(const char *path, repo_config_fn fn)
    268 {
    269 	scan_path(path, path, fn);
    270 }