]> git.cameronkatri.com Git - cgit.git/blob - parsing.c
c7310847561d8d2a7edc11912c2991d12600fef9
[cgit.git] / parsing.c
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
13 int 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
26 void skip_line(FILE *f)
27 {
28 int c;
29
30 while((c=next_char(f)) && c!='\n' && c!=EOF)
31 ;
32 }
33
34 int 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
67 int 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
88 char *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
107 int 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 */
144 void 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
181 static 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
237 char *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
247 struct 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 if(strcmp(ret->msg_encoding, PAGE_ENCODING)) {
324 t = iconv_msg(ret->subject, ret->msg_encoding);
325 if(t) {
326 free(ret->subject);
327 ret->subject = t;
328 }
329
330 t = iconv_msg(ret->msg, ret->msg_encoding);
331 if(t) {
332 free(ret->msg);
333 ret->msg = t;
334 }
335 }
336
337 return ret;
338 }
339
340
341 struct taginfo *cgit_parse_tag(struct tag *tag)
342 {
343 void *data;
344 enum object_type type;
345 unsigned long size;
346 char *p, *t;
347 struct taginfo *ret;
348
349 data = read_sha1_file(tag->object.sha1, &type, &size);
350 if (!data || type != OBJ_TAG) {
351 free(data);
352 return 0;
353 }
354
355 ret = xmalloc(sizeof(*ret));
356 ret->tagger = NULL;
357 ret->tagger_email = NULL;
358 ret->tagger_date = 0;
359 ret->msg = NULL;
360
361 p = data;
362
363 while (p && *p) {
364 if (*p == '\n')
365 break;
366
367 if (!strncmp(p, "tagger ", 7)) {
368 p += 7;
369 t = strchr(p, '<') - 1;
370 ret->tagger = substr(p, t);
371 p = t;
372 t = strchr(t, '>') + 1;
373 ret->tagger_email = substr(p, t);
374 ret->tagger_date = atol(++t);
375 }
376 p = strchr(p, '\n') + 1;
377 }
378
379 while (p && *p && (*p != '\n'))
380 p = strchr(p, '\n') + 1; // skip unknown tag fields
381
382 while (p && (*p == '\n'))
383 p = strchr(p, '\n') + 1;
384 if (p && *p)
385 ret->msg = xstrdup(p);
386 free(data);
387 return ret;
388 }