all repos — cgit @ a4d1ca1dc6ff8171694d9e2280b6075a1beced0c

a hyperfast web frontend for git written in c

shared.c (view raw)

  1/* shared.c: global vars + some callback functions
  2 *
  3 * Copyright (C) 2006 Lars Hjemli
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include "cgit.h"
 10
 11struct cgit_repolist cgit_repolist;
 12struct cgit_context ctx;
 13int cgit_cmd;
 14
 15const char *cgit_version = CGIT_VERSION;
 16
 17void cgit_prepare_context(struct cgit_context *ctx)
 18{
 19	memset(ctx, 0, sizeof(ctx));
 20	ctx->cfg.agefile = "info/web/last-modified";
 21	ctx->cfg.cache_dynamic_ttl = 5;
 22	ctx->cfg.cache_max_create_time = 5;
 23	ctx->cfg.cache_repo_ttl = 5;
 24	ctx->cfg.cache_root = CGIT_CACHE_ROOT;
 25	ctx->cfg.cache_root_ttl = 5;
 26	ctx->cfg.cache_static_ttl = -1;
 27	ctx->cfg.css = "/cgit.css";
 28	ctx->cfg.logo = "/git-logo.png";
 29	ctx->cfg.max_commit_count = 50;
 30	ctx->cfg.max_lock_attempts = 5;
 31	ctx->cfg.max_msg_len = 60;
 32	ctx->cfg.max_repodesc_len = 60;
 33	ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
 34	ctx->cfg.renamelimit = -1;
 35	ctx->cfg.robots = "index, nofollow";
 36	ctx->cfg.root_title = "Git repository browser";
 37	ctx->cfg.script_name = CGIT_SCRIPT_NAME;
 38	ctx->page.mimetype = "text/html";
 39	ctx->page.charset = PAGE_ENCODING;
 40	ctx->page.filename = NULL;
 41}
 42
 43int chk_zero(int result, char *msg)
 44{
 45	if (result != 0)
 46		die("%s: %s", msg, strerror(errno));
 47	return result;
 48}
 49
 50int chk_positive(int result, char *msg)
 51{
 52	if (result <= 0)
 53		die("%s: %s", msg, strerror(errno));
 54	return result;
 55}
 56
 57int chk_non_negative(int result, char *msg)
 58{
 59    	if (result < 0)
 60	    	die("%s: %s",msg, strerror(errno));
 61	return result;
 62}
 63
 64struct cgit_repo *add_repo(const char *url)
 65{
 66	struct cgit_repo *ret;
 67
 68	if (++cgit_repolist.count > cgit_repolist.length) {
 69		if (cgit_repolist.length == 0)
 70			cgit_repolist.length = 8;
 71		else
 72			cgit_repolist.length *= 2;
 73		cgit_repolist.repos = xrealloc(cgit_repolist.repos,
 74					       cgit_repolist.length *
 75					       sizeof(struct cgit_repo));
 76	}
 77
 78	ret = &cgit_repolist.repos[cgit_repolist.count-1];
 79	ret->url = trim_end(url, '/');
 80	ret->name = ret->url;
 81	ret->path = NULL;
 82	ret->desc = "[no description]";
 83	ret->owner = NULL;
 84	ret->group = ctx.cfg.repo_group;
 85	ret->defbranch = "master";
 86	ret->snapshots = ctx.cfg.snapshots;
 87	ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
 88	ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
 89	ret->module_link = ctx.cfg.module_link;
 90	ret->readme = NULL;
 91	return ret;
 92}
 93
 94struct cgit_repo *cgit_get_repoinfo(const char *url)
 95{
 96	int i;
 97	struct cgit_repo *repo;
 98
 99	for (i=0; i<cgit_repolist.count; i++) {
100		repo = &cgit_repolist.repos[i];
101		if (!strcmp(repo->url, url))
102			return repo;
103	}
104	return NULL;
105}
106
107void cgit_global_config_cb(const char *name, const char *value)
108{
109	if (!strcmp(name, "root-title"))
110		ctx.cfg.root_title = xstrdup(value);
111	else if (!strcmp(name, "css"))
112		ctx.cfg.css = xstrdup(value);
113	else if (!strcmp(name, "logo"))
114		ctx.cfg.logo = xstrdup(value);
115	else if (!strcmp(name, "index-header"))
116		ctx.cfg.index_header = xstrdup(value);
117	else if (!strcmp(name, "index-info"))
118		ctx.cfg.index_info = xstrdup(value);
119	else if (!strcmp(name, "logo-link"))
120		ctx.cfg.logo_link = xstrdup(value);
121	else if (!strcmp(name, "module-link"))
122		ctx.cfg.module_link = xstrdup(value);
123	else if (!strcmp(name, "virtual-root")) {
124		ctx.cfg.virtual_root = trim_end(value, '/');
125		if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
126			ctx.cfg.virtual_root = "";
127	} else if (!strcmp(name, "nocache"))
128		ctx.cfg.nocache = atoi(value);
129	else if (!strcmp(name, "snapshots"))
130		ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
131	else if (!strcmp(name, "enable-index-links"))
132		ctx.cfg.enable_index_links = atoi(value);
133	else if (!strcmp(name, "enable-log-filecount"))
134		ctx.cfg.enable_log_filecount = atoi(value);
135	else if (!strcmp(name, "enable-log-linecount"))
136		ctx.cfg.enable_log_linecount = atoi(value);
137	else if (!strcmp(name, "cache-root"))
138		ctx.cfg.cache_root = xstrdup(value);
139	else if (!strcmp(name, "cache-root-ttl"))
140		ctx.cfg.cache_root_ttl = atoi(value);
141	else if (!strcmp(name, "cache-repo-ttl"))
142		ctx.cfg.cache_repo_ttl = atoi(value);
143	else if (!strcmp(name, "cache-static-ttl"))
144		ctx.cfg.cache_static_ttl = atoi(value);
145	else if (!strcmp(name, "cache-dynamic-ttl"))
146		ctx.cfg.cache_dynamic_ttl = atoi(value);
147	else if (!strcmp(name, "max-message-length"))
148		ctx.cfg.max_msg_len = atoi(value);
149	else if (!strcmp(name, "max-repodesc-length"))
150		ctx.cfg.max_repodesc_len = atoi(value);
151	else if (!strcmp(name, "max-commit-count"))
152		ctx.cfg.max_commit_count = atoi(value);
153	else if (!strcmp(name, "summary-log"))
154		ctx.cfg.summary_log = atoi(value);
155	else if (!strcmp(name, "summary-branches"))
156		ctx.cfg.summary_branches = atoi(value);
157	else if (!strcmp(name, "summary-tags"))
158		ctx.cfg.summary_tags = atoi(value);
159	else if (!strcmp(name, "agefile"))
160		ctx.cfg.agefile = xstrdup(value);
161	else if (!strcmp(name, "renamelimit"))
162		ctx.cfg.renamelimit = atoi(value);
163	else if (!strcmp(name, "robots"))
164		ctx.cfg.robots = xstrdup(value);
165	else if (!strcmp(name, "clone-prefix"))
166		ctx.cfg.clone_prefix = xstrdup(value);
167	else if (!strcmp(name, "repo.group"))
168		ctx.cfg.repo_group = xstrdup(value);
169	else if (!strcmp(name, "repo.url"))
170		ctx.repo = add_repo(value);
171	else if (!strcmp(name, "repo.name"))
172		ctx.repo->name = xstrdup(value);
173	else if (ctx.repo && !strcmp(name, "repo.path"))
174		ctx.repo->path = trim_end(value, '/');
175	else if (ctx.repo && !strcmp(name, "repo.clone-url"))
176		ctx.repo->clone_url = xstrdup(value);
177	else if (ctx.repo && !strcmp(name, "repo.desc"))
178		ctx.repo->desc = xstrdup(value);
179	else if (ctx.repo && !strcmp(name, "repo.owner"))
180		ctx.repo->owner = xstrdup(value);
181	else if (ctx.repo && !strcmp(name, "repo.defbranch"))
182		ctx.repo->defbranch = xstrdup(value);
183	else if (ctx.repo && !strcmp(name, "repo.snapshots"))
184		ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
185	else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
186		ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
187	else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
188		ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
189	else if (ctx.repo && !strcmp(name, "repo.module-link"))
190		ctx.repo->module_link= xstrdup(value);
191	else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
192		if (*value == '/')
193			ctx.repo->readme = xstrdup(value);
194		else
195			ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
196	} else if (!strcmp(name, "include"))
197		cgit_read_config(value, cgit_global_config_cb);
198}
199
200void cgit_querystring_cb(const char *name, const char *value)
201{
202	if (!strcmp(name,"r")) {
203		ctx.qry.repo = xstrdup(value);
204		ctx.repo = cgit_get_repoinfo(value);
205	} else if (!strcmp(name, "p")) {
206		ctx.qry.page = xstrdup(value);
207	} else if (!strcmp(name, "url")) {
208		cgit_parse_url(value);
209	} else if (!strcmp(name, "qt")) {
210		ctx.qry.grep = xstrdup(value);
211	} else if (!strcmp(name, "q")) {
212		ctx.qry.search = xstrdup(value);
213	} else if (!strcmp(name, "h")) {
214		ctx.qry.head = xstrdup(value);
215		ctx.qry.has_symref = 1;
216	} else if (!strcmp(name, "id")) {
217		ctx.qry.sha1 = xstrdup(value);
218		ctx.qry.has_sha1 = 1;
219	} else if (!strcmp(name, "id2")) {
220		ctx.qry.sha2 = xstrdup(value);
221		ctx.qry.has_sha1 = 1;
222	} else if (!strcmp(name, "ofs")) {
223		ctx.qry.ofs = atoi(value);
224	} else if (!strcmp(name, "path")) {
225		ctx.qry.path = trim_end(value, '/');
226	} else if (!strcmp(name, "name")) {
227		ctx.qry.name = xstrdup(value);
228	}
229}
230
231void *cgit_free_commitinfo(struct commitinfo *info)
232{
233	free(info->author);
234	free(info->author_email);
235	free(info->committer);
236	free(info->committer_email);
237	free(info->subject);
238	free(info->msg);
239	free(info->msg_encoding);
240	free(info);
241	return NULL;
242}
243
244int hextoint(char c)
245{
246	if (c >= 'a' && c <= 'f')
247		return 10 + c - 'a';
248	else if (c >= 'A' && c <= 'F')
249		return 10 + c - 'A';
250	else if (c >= '0' && c <= '9')
251		return c - '0';
252	else
253		return -1;
254}
255
256char *trim_end(const char *str, char c)
257{
258	int len;
259	char *s, *t;
260
261	if (str == NULL)
262		return NULL;
263	t = (char *)str;
264	len = strlen(t);
265	while(len > 0 && t[len - 1] == c)
266		len--;
267
268	if (len == 0)
269		return NULL;
270
271	c = t[len];
272	t[len] = '\0';
273	s = xstrdup(t);
274	t[len] = c;
275	return s;
276}
277
278char *strlpart(char *txt, int maxlen)
279{
280	char *result;
281
282	if (!txt)
283		return txt;
284
285	if (strlen(txt) <= maxlen)
286		return txt;
287	result = xmalloc(maxlen + 1);
288	memcpy(result, txt, maxlen - 3);
289	result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
290	result[maxlen] = '\0';
291	return result;
292}
293
294char *strrpart(char *txt, int maxlen)
295{
296	char *result;
297
298	if (!txt)
299		return txt;
300
301	if (strlen(txt) <= maxlen)
302		return txt;
303	result = xmalloc(maxlen + 1);
304	memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
305	result[0] = result[1] = result[2] = '.';
306	return result;
307}
308
309void cgit_add_ref(struct reflist *list, struct refinfo *ref)
310{
311	size_t size;
312
313	if (list->count >= list->alloc) {
314		list->alloc += (list->alloc ? list->alloc : 4);
315		size = list->alloc * sizeof(struct refinfo *);
316		list->refs = xrealloc(list->refs, size);
317	}
318	list->refs[list->count++] = ref;
319}
320
321struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
322{
323	struct refinfo *ref;
324
325	ref = xmalloc(sizeof (struct refinfo));
326	ref->refname = xstrdup(refname);
327	ref->object = parse_object(sha1);
328	switch (ref->object->type) {
329	case OBJ_TAG:
330		ref->tag = cgit_parse_tag((struct tag *)ref->object);
331		break;
332	case OBJ_COMMIT:
333		ref->commit = cgit_parse_commit((struct commit *)ref->object);
334		break;
335	}
336	return ref;
337}
338
339int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
340		  void *cb_data)
341{
342	struct reflist *list = (struct reflist *)cb_data;
343	struct refinfo *info = cgit_mk_refinfo(refname, sha1);
344
345	if (info)
346		cgit_add_ref(list, info);
347	return 0;
348}
349
350void cgit_diff_tree_cb(struct diff_queue_struct *q,
351		       struct diff_options *options, void *data)
352{
353	int i;
354
355	for (i = 0; i < q->nr; i++) {
356		if (q->queue[i]->status == 'U')
357			continue;
358		((filepair_fn)data)(q->queue[i]);
359	}
360}
361
362static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
363{
364	enum object_type type;
365
366	if (is_null_sha1(sha1)) {
367		file->ptr = (char *)"";
368		file->size = 0;
369	} else {
370		file->ptr = read_sha1_file(sha1, &type, 
371		                           (unsigned long *)&file->size);
372	}
373	return 1;
374}
375
376/*
377 * Receive diff-buffers from xdiff and concatenate them as
378 * needed across multiple callbacks.
379 *
380 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
381 * ripped from git and modified to use globals instead of
382 * a special callback-struct.
383 */
384char *diffbuf = NULL;
385int buflen = 0;
386
387int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
388{
389	int i;
390
391	for (i = 0; i < nbuf; i++) {
392		if (mb[i].ptr[mb[i].size-1] != '\n') {
393			/* Incomplete line */
394			diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
395			memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
396			buflen += mb[i].size;
397			continue;
398		}
399
400		/* we have a complete line */
401		if (!diffbuf) {
402			((linediff_fn)priv)(mb[i].ptr, mb[i].size);
403			continue;
404		}
405		diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
406		memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
407		((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
408		free(diffbuf);
409		diffbuf = NULL;
410		buflen = 0;
411	}
412	if (diffbuf) {
413		((linediff_fn)priv)(diffbuf, buflen);
414		free(diffbuf);
415		diffbuf = NULL;
416		buflen = 0;
417	}
418	return 0;
419}
420
421int cgit_diff_files(const unsigned char *old_sha1,
422		     const unsigned char *new_sha1,
423		     linediff_fn fn)
424{
425	mmfile_t file1, file2;
426	xpparam_t diff_params;
427	xdemitconf_t emit_params;
428	xdemitcb_t emit_cb;
429
430	if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
431		return 1;
432
433	diff_params.flags = XDF_NEED_MINIMAL;
434	emit_params.ctxlen = 3;
435	emit_params.flags = XDL_EMIT_FUNCNAMES;
436	emit_params.find_func = NULL;
437	emit_cb.outf = filediff_cb;
438	emit_cb.priv = fn;
439	xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
440	return 0;
441}
442
443void cgit_diff_tree(const unsigned char *old_sha1,
444		    const unsigned char *new_sha1,
445		    filepair_fn fn, const char *prefix)
446{
447	struct diff_options opt;
448	int ret;
449	int prefixlen;
450
451	diff_setup(&opt);
452	opt.output_format = DIFF_FORMAT_CALLBACK;
453	opt.detect_rename = 1;
454	opt.rename_limit = ctx.cfg.renamelimit;
455	DIFF_OPT_SET(&opt, RECURSIVE);
456	opt.format_callback = cgit_diff_tree_cb;
457	opt.format_callback_data = fn;
458	if (prefix) {
459		opt.nr_paths = 1;
460		opt.paths = &prefix;
461		prefixlen = strlen(prefix);
462		opt.pathlens = &prefixlen;
463	}
464	diff_setup_done(&opt);
465
466	if (old_sha1 && !is_null_sha1(old_sha1))
467		ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
468	else
469		ret = diff_root_tree_sha1(new_sha1, "", &opt);
470	diffcore_std(&opt);
471	diff_flush(&opt);
472}
473
474void cgit_diff_commit(struct commit *commit, filepair_fn fn)
475{
476	unsigned char *old_sha1 = NULL;
477
478	if (commit->parents)
479		old_sha1 = commit->parents->item->object.sha1;
480	cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
481}
482
483int cgit_parse_snapshots_mask(const char *str)
484{
485	const struct cgit_snapshot_format *f;
486	static const char *delim = " \t,:/|;";
487	int tl, sl, rv = 0;
488
489	/* favor legacy setting */
490	if(atoi(str))
491		return 1;
492	for(;;) {
493		str += strspn(str,delim);
494		tl = strcspn(str,delim);
495		if (!tl)
496			break;
497		for (f = cgit_snapshot_formats; f->suffix; f++) {
498			sl = strlen(f->suffix);
499			if((tl == sl && !strncmp(f->suffix, str, tl)) ||
500			   (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
501				rv |= f->bit;
502				break;
503			}
504		}
505		str += tl;
506	}
507	return rv;
508}