]> gitweb.ps.run Git - ps-cgit/blob - parsing.c
Add iconv_msg function.
[ps-cgit] / 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         return ret;
324 }
325
326
327 struct 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 }