all repos — cgit @ caca860ba79fe9f6bc387f64ddb57ac0db1fac33

a hyperfast web frontend for git written in c

scan-tree.c (view raw)

  1/* scan-tree.c
  2 * 
  3 * Copyright (C) 2008-2009 Lars Hjemli
  4 * Copyright (C) 2010, 2012 Jason A. Donenfeld <Jason@zx2c4.com>
  5 *
  6 * Licensed under GNU General Public License v2
  7 *   (see COPYING for full license text)
  8 */
  9
 10#include "cgit.h"
 11#include "scan-tree.h"
 12#include "configfile.h"
 13#include "html.h"
 14
 15#define MAX_PATH 4096
 16
 17/* return 1 if path contains a objects/ directory and a HEAD file */
 18static int is_git_dir(const char *path)
 19{
 20	struct stat st;
 21	static char buf[MAX_PATH];
 22
 23	if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
 24		fprintf(stderr, "Insanely long path: %s\n", path);
 25		return 0;
 26	}
 27	if (stat(buf, &st)) {
 28		if (errno != ENOENT)
 29			fprintf(stderr, "Error checking path %s: %s (%d)\n",
 30				path, strerror(errno), errno);
 31		return 0;
 32	}
 33	if (!S_ISDIR(st.st_mode))
 34		return 0;
 35
 36	sprintf(buf, "%s/HEAD", path);
 37	if (stat(buf, &st)) {
 38		if (errno != ENOENT)
 39			fprintf(stderr, "Error checking path %s: %s (%d)\n",
 40				path, strerror(errno), errno);
 41		return 0;
 42	}
 43	if (!S_ISREG(st.st_mode))
 44		return 0;
 45
 46	return 1;
 47}
 48
 49struct cgit_repo *repo;
 50repo_config_fn config_fn;
 51
 52static void repo_config(const char *name, const char *value)
 53{
 54	config_fn(repo, name, value);
 55}
 56
 57static int gitconfig_config(const char *key, const char *value, void *cb)
 58{
 59	if (!strcmp(key, "gitweb.owner"))
 60		config_fn(repo, "owner", value);
 61	else if (!strcmp(key, "gitweb.description"))
 62		config_fn(repo, "desc", value);
 63	else if (!strcmp(key, "gitweb.category"))
 64		config_fn(repo, "section", value);
 65	else if (!prefixcmp(key, "cgit."))
 66		config_fn(repo, key + 5, value);
 67
 68	return 0;
 69}
 70
 71static char *xstrrchr(char *s, char *from, int c)
 72{
 73	while (from >= s && *from != c)
 74		from--;
 75	return from < s ? NULL : from;
 76}
 77
 78static void add_repo(const char *base, const char *path, repo_config_fn fn)
 79{
 80	struct stat st;
 81	struct passwd *pwd;
 82	char *rel, *p, *slash;
 83	int n;
 84	size_t size;
 85
 86	if (stat(path, &st)) {
 87		fprintf(stderr, "Error accessing %s: %s (%d)\n",
 88			path, strerror(errno), errno);
 89		return;
 90	}
 91
 92	if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st))
 93		return;
 94
 95	if (!stat(fmt("%s/noweb", path), &st))
 96		return;
 97
 98	if (base == path)
 99		rel = xstrdup(fmt("%s", path));
100	else
101		rel = xstrdup(fmt("%s", path + strlen(base) + 1));
102
103	if (!strcmp(rel + strlen(rel) - 5, "/.git"))
104		rel[strlen(rel) - 5] = '\0';
105
106	repo = cgit_add_repo(rel);
107	config_fn = fn;
108	if (ctx.cfg.enable_git_config)
109		git_config_from_file(gitconfig_config, fmt("%s/config", path), NULL);
110
111	if (ctx.cfg.remove_suffix)
112		if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
113			*p = '\0';
114	repo->path = xstrdup(path);
115	while (!repo->owner) {
116		if ((pwd = getpwuid(st.st_uid)) == NULL) {
117			fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
118				path, strerror(errno), errno);
119			break;
120		}
121		if (pwd->pw_gecos)
122			if ((p = strchr(pwd->pw_gecos, ',')))
123				*p = '\0';
124		repo->owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
125	}
126
127	if (repo->desc == cgit_default_repo_desc || !repo->desc) {
128		p = fmt("%s/description", path);
129		if (!stat(p, &st))
130			readfile(p, &repo->desc, &size);
131	}
132
133	if (!repo->readme) {
134		p = fmt("%s/README.html", path);
135		if (!stat(p, &st))
136			repo->readme = "README.html";
137	}
138	if (ctx.cfg.section_from_path) {
139		n  = ctx.cfg.section_from_path;
140		if (n > 0) {
141			slash = rel;
142			while (slash && n && (slash = strchr(slash, '/')))
143				n--;
144		} else {
145			slash = rel + strlen(rel);
146			while (slash && n && (slash = xstrrchr(rel, slash, '/')))
147				n++;
148		}
149		if (slash && !n) {
150			*slash = '\0';
151			repo->section = xstrdup(rel);
152			*slash = '/';
153			if (!prefixcmp(repo->name, repo->section)) {
154				repo->name += strlen(repo->section);
155				if (*repo->name == '/')
156					repo->name++;
157			}
158		}
159	}
160
161	p = fmt("%s/cgitrc", path);
162	if (!stat(p, &st))
163		parse_configfile(xstrdup(p), &repo_config);
164
165
166	free(rel);
167}
168
169static void scan_path(const char *base, const char *path, repo_config_fn fn)
170{
171	DIR *dir = opendir(path);
172	struct dirent *ent;
173	char *buf;
174	struct stat st;
175
176	if (!dir) {
177		fprintf(stderr, "Error opening directory %s: %s (%d)\n",
178			path, strerror(errno), errno);
179		return;
180	}
181	if (is_git_dir(path)) {
182		add_repo(base, path, fn);
183		goto end;
184	}
185	if (is_git_dir(fmt("%s/.git", path))) {
186		add_repo(base, fmt("%s/.git", path), fn);
187		goto end;
188	}
189	while ((ent = readdir(dir)) != NULL) {
190		if (ent->d_name[0] == '.') {
191			if (ent->d_name[1] == '\0')
192				continue;
193			if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
194				continue;
195			if (!ctx.cfg.scan_hidden_path)
196				continue;
197		}
198		buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
199		if (!buf) {
200			fprintf(stderr, "Alloc error on %s: %s (%d)\n",
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",
207				buf, strerror(errno), errno);
208			free(buf);
209			continue;
210		}
211		if (S_ISDIR(st.st_mode))
212			scan_path(base, buf, fn);
213		free(buf);
214	}
215end:
216	closedir(dir);
217}
218
219#define lastc(s) s[strlen(s) - 1]
220
221void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
222{
223	char line[MAX_PATH * 2], *z;
224	FILE *projects;
225	int err;
226
227	projects = fopen(projectsfile, "r");
228	if (!projects) {
229		fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
230			projectsfile, strerror(errno), errno);
231		return;
232	}
233	while (fgets(line, sizeof(line), projects) != NULL) {
234		for (z = &lastc(line);
235		     strlen(line) && strchr("\n\r", *z);
236		     z = &lastc(line))
237			*z = '\0';
238		if (strlen(line))
239			scan_path(path, fmt("%s/%s", path, line), fn);
240	}
241	if ((err = ferror(projects))) {
242		fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
243			projectsfile, strerror(err), err);
244	}
245	fclose(projects);
246}
247
248void scan_tree(const char *path, repo_config_fn fn)
249{
250	scan_path(path, path, fn);
251}