all repos — cgit @ af0819830445e39584a0137034562086a55deaf2

a hyperfast web frontend for git written in c

parsing.c (view raw)

  1/* config.c: parsing of config files
  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 <iconv.h>
 10
 11#include "cgit.h"
 12
 13int next_char(FILE *f)
 14{
 15	int c = fgetc(f);
 16	if (c=='\r') {
 17		c = fgetc(f);
 18		if (c!='\n') {
 19			ungetc(c, f);
 20			c = '\r';
 21		}
 22	}
 23	return c;
 24}
 25
 26void skip_line(FILE *f)
 27{
 28	int c;
 29
 30	while((c=next_char(f)) && c!='\n' && c!=EOF)
 31		;
 32}
 33
 34int read_config_line(FILE *f, char *line, const char **value, int bufsize)
 35{
 36	int i = 0, isname = 0;
 37
 38	*value = NULL;
 39	while(i<bufsize-1) {
 40		int c = next_char(f);
 41		if (!isname && (c=='#' || c==';')) {
 42			skip_line(f);
 43			continue;
 44		}
 45		if (!isname && isspace(c))
 46			continue;
 47
 48		if (c=='=' && !*value) {
 49			line[i] = 0;
 50			*value = &line[i+1];
 51		} else if (c=='\n' && !isname) {
 52			i = 0;
 53			continue;
 54		} else if (c=='\n' || c==EOF) {
 55			line[i] = 0;
 56			break;
 57		} else {
 58			line[i]=c;
 59		}
 60		isname = 1;
 61		i++;
 62	}
 63	line[i+1] = 0;
 64	return i;
 65}
 66
 67int cgit_read_config(const char *filename, configfn fn)
 68{
 69	static int nesting;
 70	int len;
 71	char line[256];
 72	const char *value;
 73	FILE *f;
 74
 75	/* cancel deeply nested include-commands */
 76	if (nesting > 8)
 77		return -1;
 78	if (!(f = fopen(filename, "r")))
 79		return -1;
 80	nesting++;
 81	while((len = read_config_line(f, line, &value, sizeof(line))) > 0)
 82		(*fn)(line, value);
 83	nesting--;
 84	fclose(f);
 85	return 0;
 86}
 87
 88char *convert_query_hexchar(char *txt)
 89{
 90	int d1, d2;
 91	if (strlen(txt) < 3) {
 92		*txt = '\0';
 93		return txt-1;
 94	}
 95	d1 = hextoint(*(txt+1));
 96	d2 = hextoint(*(txt+2));
 97	if (d1<0 || d2<0) {
 98		strcpy(txt, txt+3);
 99		return txt-1;
100	} else {
101		*txt = d1 * 16 + d2;
102		strcpy(txt+1, txt+3);
103		return txt;
104	}
105}
106
107int cgit_parse_query(char *txt, configfn fn)
108{
109	char *t, *value = NULL, c;
110
111	if (!txt)
112		return 0;
113
114	t = txt = xstrdup(txt);
115
116	while((c=*t) != '\0') {
117		if (c=='=') {
118			*t = '\0';
119			value = t+1;
120		} else if (c=='+') {
121			*t = ' ';
122		} else if (c=='%') {
123			t = convert_query_hexchar(t);
124		} else if (c=='&') {
125			*t = '\0';
126			(*fn)(txt, value);
127			txt = t+1;
128			value = NULL;
129		}
130		t++;
131	}
132	if (t!=txt)
133		(*fn)(txt, value);
134	return 0;
135}
136
137/*
138 * url syntax: [repo ['/' cmd [ '/' path]]]
139 *   repo: any valid repo url, may contain '/'
140 *   cmd:  log | commit | diff | tree | view | blob | snapshot
141 *   path: any valid path, may contain '/'
142 *
143 */
144void cgit_parse_url(const char *url)
145{
146	char *cmd, *p;
147
148	cgit_repo = NULL;
149	if (!url || url[0] == '\0')
150		return;
151
152	cgit_repo = cgit_get_repoinfo(url);
153	if (cgit_repo) {
154		cgit_query_repo = cgit_repo->url;
155		return;
156	}
157
158	cmd = strchr(url, '/');
159	while (!cgit_repo && cmd) {
160		cmd[0] = '\0';
161		cgit_repo = cgit_get_repoinfo(url);
162		if (cgit_repo == NULL) {
163			cmd[0] = '/';
164			cmd = strchr(cmd + 1, '/');
165			continue;
166		}
167
168		cgit_query_repo = cgit_repo->url;
169		p = strchr(cmd + 1, '/');
170		if (p) {
171			p[0] = '\0';
172			if (p[1])
173				cgit_query_path = trim_end(p + 1, '/');
174		}
175		cgit_cmd = cgit_get_cmd_index(cmd + 1);
176		cgit_query_page = xstrdup(cmd + 1);
177		return;
178	}
179}
180
181static char *iconv_msg(char *msg, const char *encoding)
182{
183	iconv_t msg_conv = iconv_open(PAGE_ENCODING, encoding);
184	size_t inlen = strlen(msg);
185	char *in;
186	char *out;
187	size_t inleft;
188	size_t outleft;
189	char *buf;
190	char *ret;
191	size_t buf_sz;
192	int again, fail;
193
194	if(msg_conv == (iconv_t)-1)
195		return NULL;
196
197	buf_sz = inlen * 2;
198	buf = xmalloc(buf_sz+1);
199	do {
200		in = msg;
201		inleft = inlen;
202
203		out = buf;
204		outleft = buf_sz;
205		iconv(msg_conv, &in, &inleft, &out, &outleft);
206
207		if(inleft == 0) {
208			fail = 0;
209			again = 0;
210		} else if(inleft != 0 && errno == E2BIG) {
211			fail = 0;
212			again = 1;
213
214			buf_sz *= 2;
215			free(buf);
216			buf = xmalloc(buf_sz+1);
217		} else {
218			fail = 1;
219			again = 0;
220		}
221	} while(again && !fail);
222
223	if(fail) {
224		free(buf);
225		ret = NULL;
226	} else {
227		buf = xrealloc(buf, out - buf);
228		*out = 0;
229		ret = buf;
230	}
231
232	iconv_close(msg_conv);
233
234	return ret;
235}
236
237char *substr(const char *head, const char *tail)
238{
239	char *buf;
240
241	buf = xmalloc(tail - head + 1);
242	strncpy(buf, head, tail - head);
243	buf[tail - head] = '\0';
244	return buf;
245}
246
247struct commitinfo *cgit_parse_commit(struct commit *commit)
248{
249	struct commitinfo *ret;
250	char *p = commit->buffer, *t = commit->buffer;
251
252	ret = xmalloc(sizeof(*ret));
253	ret->commit = commit;
254	ret->author = NULL;
255	ret->author_email = NULL;
256	ret->committer = NULL;
257	ret->committer_email = NULL;
258	ret->subject = NULL;
259	ret->msg = NULL;
260	ret->msg_encoding = NULL;
261
262	if (p == NULL)
263		return ret;
264
265	if (strncmp(p, "tree ", 5))
266		die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
267	else
268		p += 46; // "tree " + hex[40] + "\n"
269
270	while (!strncmp(p, "parent ", 7))
271		p += 48; // "parent " + hex[40] + "\n"
272
273	if (!strncmp(p, "author ", 7)) {
274		p += 7;
275		t = strchr(p, '<') - 1;
276		ret->author = substr(p, t);
277		p = t;
278		t = strchr(t, '>') + 1;
279		ret->author_email = substr(p, t);
280		ret->author_date = atol(++t);
281		p = strchr(t, '\n') + 1;
282	}
283
284	if (!strncmp(p, "committer ", 9)) {
285		p += 9;
286		t = strchr(p, '<') - 1;
287		ret->committer = substr(p, t);
288		p = t;
289		t = strchr(t, '>') + 1;
290		ret->committer_email = substr(p, t);
291		ret->committer_date = atol(++t);
292		p = strchr(t, '\n') + 1;
293	}
294
295	if (!strncmp(p, "encoding ", 9)) {
296		p += 9;
297		t = strchr(p, '\n') + 1;
298		ret->msg_encoding = substr(p, t);
299		p = t;
300	} else
301		ret->msg_encoding = xstrdup(PAGE_ENCODING);
302
303	while (*p && (*p != '\n'))
304		p = strchr(p, '\n') + 1; // skip unknown header fields
305
306	while (*p == '\n')
307		p = strchr(p, '\n') + 1;
308
309	t = strchr(p, '\n');
310	if (t) {
311		if (*t == '\0')
312			ret->subject = "** empty **";
313		else
314			ret->subject = substr(p, t);
315		p = t + 1;
316
317		while (*p == '\n')
318			p = strchr(p, '\n') + 1;
319		ret->msg = xstrdup(p);
320	} else
321		ret->subject = substr(p, p+strlen(p));
322
323	return ret;
324}
325
326
327struct taginfo *cgit_parse_tag(struct tag *tag)
328{
329	void *data;
330	enum object_type type;
331	unsigned long size;
332	char *p, *t;
333	struct taginfo *ret;
334
335	data = read_sha1_file(tag->object.sha1, &type, &size);
336	if (!data || type != OBJ_TAG) {
337		free(data);
338		return 0;
339	}
340
341	ret = xmalloc(sizeof(*ret));
342	ret->tagger = NULL;
343	ret->tagger_email = NULL;
344	ret->tagger_date = 0;
345	ret->msg = NULL;
346
347	p = data;
348
349	while (p && *p) {
350		if (*p == '\n')
351			break;
352
353		if (!strncmp(p, "tagger ", 7)) {
354			p += 7;
355			t = strchr(p, '<') - 1;
356			ret->tagger = substr(p, t);
357			p = t;
358			t = strchr(t, '>') + 1;
359			ret->tagger_email = substr(p, t);
360			ret->tagger_date = atol(++t);
361		}
362		p = strchr(p, '\n') + 1;
363	}
364
365	while (p && *p && (*p != '\n'))
366		p = strchr(p, '\n') + 1; // skip unknown tag fields
367
368	while (p && (*p == '\n'))
369		p = strchr(p, '\n') + 1;
370	if (p && *p)
371		ret->msg = xstrdup(p);
372	free(data);
373	return ret;
374}