1 /*
2 * Copyright 2009-2018 Peter Kosyh <p.kosyh at gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 */
24
25 #include "externals.h"
26 #include "utils.h"
27
process_cmd(char * n,char * v,struct parser * cmd_parser)28 int process_cmd(char *n, char *v, struct parser *cmd_parser)
29 {
30 int i;
31 n = strip(n);
32 v = strip(v);
33 for (i = 0; cmd_parser[i].cmd; i++) {
34 if (!strcmp(cmd_parser[i].cmd, n)) {
35 return cmd_parser[i].fn(v, cmd_parser[i].p);
36 }
37 }
38 return -1;
39 }
40
fgetsesc(char * oline,size_t size,char * (* getl)(void * p,char * s,int size),void * fp)41 static int fgetsesc(char *oline, size_t size, char *(*getl)(void *p, char *s, int size), void *fp)
42 {
43 int nr = 0;
44 char line[4096];
45 size_t len;
46 *oline = 0;
47 *line = 0;
48 while (getl(fp, line, sizeof(line))) {
49 int i;
50 nr ++;
51 i = strcspn(line, "\n\r");
52 if (!i || !line[i])
53 break;
54 line[i] = 0;
55 if (line[i - 1] == '\\') {
56 line[i - 1] = 0;
57 strncat(oline, line, size - 1);
58 len = strlen(line);
59 if (len >= size)
60 return nr;
61 size -= len;
62 line[0] = 0;
63 } else {
64 break;
65 }
66 }
67 strncat(oline, line, size - 1);
68 return nr;
69 }
70
find_in_esc(const char * l,const char * s)71 char *find_in_esc(const char *l, const char *s)
72 {
73 int esc = 0;
74 for (; *l; l++) {
75 if (esc) {
76 esc = 0;
77 continue;
78 }
79 l += strcspn(l, s);
80 if (*l == '\\') {
81 esc = 1;
82 continue;
83 }
84 if (!esc)
85 return (char*)l;
86 }
87 return NULL;
88 }
89
unescape_sym(char * l,const char * s)90 static void unescape_sym(char *l, const char *s)
91 {
92 int esc = 0;
93 char *r = l;
94 int d = 0;
95 for (; *l; ) {
96 if (esc) {
97 esc = 0;
98 if (strspn(l, s))
99 r --;
100 *r ++ = *l ++;
101 continue;
102 }
103 d = strcspn(l, s);
104 if (d)
105 memmove(r, l, d);
106 r += d; l += d;
107 if (*l == '\\')
108 esc = 1;
109 if (*l)
110 *r ++ = *l ++;
111 }
112 *r = 0;
113 }
114
comments_zap(char * p)115 static void comments_zap(char *p)
116 {
117 char *l = find_in_esc(p, "\\;\n");
118 if (l)
119 *l = 0;
120 unescape_sym(p, "\\;\n");
121 }
122
parse_all(void * fp,char * (* getl)(void * p,char * s,int size),const char * path,struct parser * cmd_parser)123 int parse_all(void *fp, char *(*getl)(void *p, char *s, int size), const char *path, struct parser *cmd_parser)
124 {
125 int nr;
126 int rc = 0;
127 int line_nr = 1;
128
129 char line[4096];
130 if (!fp)
131 return -1;
132
133 while ((nr = fgetsesc(line, sizeof(line), getl, fp))) {
134 char *p = line;
135 char *val;
136 int len;
137 line_nr += nr;
138 p += strspn(p, " \t");
139 if (*p == ';')
140 continue;
141 len = strcspn(p, "=");
142 if (p[len] != '=') /* just ignore it */
143 continue;
144 p[len] = 0;
145 val = p + len + 1;
146 len = strcspn(p, " \t");
147 p[len] = 0;
148 /* printf("%s\n", p); */
149 val += strspn(val, " \t");
150 comments_zap(val);
151 /* val[strcspn(val, ";\n")] = 0; */
152 if (process_cmd(p, val, cmd_parser)) {
153 rc = -1;
154 fprintf(stderr, "Can't process cmd '%s' on line %d in '%s': %s\n", p, line_nr - nr, path, strerror(errno));
155 }
156 }
157 return rc;
158 }
159
file_gets(void * fd,char * s,int size)160 static char *file_gets(void *fd, char *s, int size)
161 {
162 return fgets(s, size, (FILE *)fd);
163 }
164
idff_gets(void * fd,char * s,int size)165 static char *idff_gets(void *fd, char *s, int size)
166 {
167 return idf_gets((idff_t)fd, s, size);
168 }
169
parse_ini(const char * path,struct parser * cmd_parser)170 int parse_ini(const char *path, struct parser *cmd_parser)
171 {
172 int rc = 0;
173 FILE *fp;
174 fp = fopen(path, "rb");
175 if (!fp)
176 return -1;
177 rc = parse_all(fp, file_gets, path, cmd_parser);
178 fclose(fp);
179 return rc;
180 }
181
parse_idff(idff_t idff,const char * path,struct parser * cmd_parser)182 int parse_idff(idff_t idff, const char *path, struct parser *cmd_parser)
183 {
184 if (!idff)
185 return -1;
186 return parse_all(idff, idff_gets, path, cmd_parser);
187 }
188
parse_string(const char * v,void * data)189 int parse_string(const char *v, void *data)
190 {
191 char **p = ((char **)data);
192 if (*p)
193 free(*p);
194 *p = strdup(v);
195 if (!*p)
196 return -1;
197 return 0;
198 }
199
encode_esc_string(const char * v)200 char *encode_esc_string(const char *v)
201 {
202 char *r, *p;
203 if (!v)
204 return NULL;
205 p = r = malloc((strlen(v)*2) + 1);
206 if (!r)
207 return NULL;
208 while (*v) {
209 switch (*v) {
210 case ' ':
211 *p ++ = '\\';
212 *p ++ = ' ';
213 break;
214 case '"':
215 *p ++ = '\\';
216 *p ++ = '"';
217 break;
218 case '\'':
219 *p ++ = '\\';
220 *p ++ = '\'';
221 break;
222 case '\\':
223 *p ++ = '\\';
224 *p ++ = '\\';
225 break;
226 case '\n':
227 *p ++ ='\\';
228 *p ++ ='\n';
229 break;
230 default:
231 *p ++ = *v;
232 }
233 v ++;
234 }
235 *p ++ = 0;
236 return r;
237 }
238
parse_esc_string(const char * v,void * data)239 int parse_esc_string(const char *v, void *data)
240 {
241 int esc = 0;
242 char *ptr;
243 char **p = ((char **)data);
244 if (*p)
245 free(*p);
246 *p = strdup(v);
247 if (!*p)
248 return -1;
249 for (ptr = *p; *v; v ++) {
250 if (esc) {
251 switch (*v) {
252 case 'n':
253 *ptr = '\n';
254 break;
255 case '$':
256 *ptr = '$';
257 break;
258 case '\\':
259 *ptr = '\\';
260 break;
261 case ';':
262 *ptr = ';';
263 break;
264 case 'r':
265 *ptr = '\n';
266 break;
267 default:
268 *ptr = *v;
269 break;
270 }
271 esc = 0;
272 ptr ++;
273 continue;
274 } else if (*v != '\\') {
275 *ptr = *v;
276 ptr ++;
277 continue;
278 } else
279 esc = 1;
280 }
281 *ptr = 0;
282 return 0;
283 }
284
parse_int(const char * v,void * data)285 int parse_int(const char *v, void *data)
286 {
287 int *i = (int *)data;
288 char *eptr = NULL;
289 *i = strtol(v, &eptr, 0);
290 if (!eptr || *eptr)
291 return -1;
292 return 0;
293 }
294
parse_float(const char * v,void * data)295 int parse_float(const char *v, void *data)
296 {
297 float *f = (float *)data;
298 if (sscanf(v, "%f", f) != 1)
299 return -1;
300 return 0;
301 }
302
parse_path(const char * v,void * data)303 int parse_path(const char *v, void *data)
304 {
305 char **p = ((char **)data);
306 if (*p)
307 free(*p);
308 if (!v[0]) {
309 *p = strdup("");
310 return (*p)?0:-1;
311 }
312 *p = strdup(v);
313 if (!*p)
314 return -1;
315 /* *p = sdl_path(*p); note: do not convert relative paths to avoid double encoding */
316 unix_path(*p);
317 return 0;
318 }
319 #if defined(_WIN32) && !defined(_WIN32_WCE)
320 #include <wchar.h>
321 #include <windows.h>
322
wchar2utf(const wchar_t * wc)323 static char *wchar2utf(const wchar_t *wc)
324 {
325 char *buf;
326 int size = WideCharToMultiByte (CP_UTF8, 0, wc, -1, NULL, 0, NULL, NULL);
327 if (!size)
328 return NULL;
329 buf = malloc(size);
330 WideCharToMultiByte (CP_UTF8, 0, wc, -1, buf, size, NULL, NULL);
331 return buf;
332 }
333
w32_getdir(char * path,size_t size)334 char *w32_getdir(char *path, size_t size)
335 {
336 wchar_t *wp;
337 char *p = NULL;
338 path[0] = 0;
339 wp = _wgetcwd(NULL, 0);
340 if (!wp)
341 return path;
342 p = wchar2utf(wp);
343 free(wp);
344 if (!p)
345 return path;
346 snprintf(path, size, "%s", p);
347 free(p);
348 return path;
349 }
350 #endif
parse_full_path(const char * v,void * data)351 int parse_full_path(const char *v, void *data)
352 {
353 char cwd[PATH_MAX];
354 char **p = ((char **)data);
355
356 if (*p)
357 free(*p);
358 if (!v[0]) {
359 *p = strdup("");
360 return (*p)?0:-1;
361 }
362 #if defined(_WIN32) && !defined(_WIN32_WCE)
363 w32_getdir(cwd, sizeof(cwd));
364 #else
365 getdir(cwd, sizeof(cwd));
366 #endif
367 *p = malloc(strlen(v) + strlen(cwd) + 2);
368 if (!*p)
369 return -1;
370 strcpy(*p, cwd);
371 strcat(*p,"/");
372 strcat(*p, v);
373 *p = sdl_path(*p);
374 return 0;
375 }
376
lookup_tag_all(const char * tag,const char * comm,char * (* getl)(void * p,char * s,int size),void * fp)377 static char *lookup_tag_all(const char *tag, const char *comm, char *(*getl)(void *p, char *s, int size), void *fp)
378 {
379 int brk = 0;
380 char *l; char line[1024];
381 while ((l = getl(fp, line, sizeof(line))) && !brk) {
382 l = parse_tag(l, tag, comm, &brk);
383 if (l)
384 return l;
385 }
386 return NULL;
387
388 }
389
lookup_tag(const char * fname,const char * tag,const char * comm)390 char *lookup_tag(const char *fname, const char *tag, const char *comm)
391 {
392 char *l;
393 FILE *fd = fopen(fname, "rb");
394 if (!fd)
395 return NULL;
396 l = lookup_tag_all(tag, comm, file_gets, fd);
397 fclose(fd);
398 return l;
399 }
400
lookup_lang_tag(const char * fname,const char * tag,const char * comm,const char * opt_lang)401 char *lookup_lang_tag(const char *fname, const char *tag, const char *comm, const char *opt_lang)
402 {
403 char lang_tag[1024];
404 char *l;
405 snprintf(lang_tag, sizeof(lang_tag), "%s(%s)", tag, opt_lang);
406 l = lookup_tag(fname, lang_tag, comm);
407 if (!l)
408 l = lookup_tag(fname, tag, comm);
409 return l;
410 }
411
lookup_lang_tag_idf(idf_t idf,const char * fname,const char * tag,const char * comm,const char * opt_lang)412 char *lookup_lang_tag_idf(idf_t idf, const char *fname, const char *tag, const char *comm, const char *opt_lang)
413 {
414 char lang_tag[1024];
415 char *l;
416 idff_t idff;
417 if (!idf)
418 return NULL;
419 idff = idf_open(idf, fname);
420 if (!idff)
421 return NULL;
422 snprintf(lang_tag, sizeof(lang_tag), "%s(%s)", tag, opt_lang);
423 l = lookup_tag_all(lang_tag, comm, idff_gets, idff);
424 if (!l) {
425 idf_seek(idff, 0, SEEK_SET);
426 l = lookup_tag_all(tag, comm, idff_gets, idff);
427 }
428 idf_close(idff);
429 return l;
430 }
431
parse_tag(char * line,const char * tag,const char * comm,int * brk)432 char *parse_tag(char *line, const char *tag, const char *comm, int *brk)
433 {
434 char *l = line;
435 char *ns = NULL;
436 char ftag[1024];
437 snprintf(ftag, sizeof(ftag), "$%s:", tag);
438 l += strspn(l, " \t");
439 if (strncmp(l, comm, strlen(comm))) { /* non coment block */
440 *brk = 1;
441 return NULL;
442 }
443 l += strlen(comm); l += strspn(l, " \t");
444 if (strncmp(l, ftag, strlen(ftag)))
445 return NULL;
446 l += strlen(ftag);
447 l += strspn(l, " \t");
448 ns = l;
449 l = find_in_esc(l, "\\$");
450 if (l)
451 *l = 0;
452 l = ns; ns = NULL;
453 if (parse_esc_string(l, &ns))
454 return NULL;
455 l = ns + strlen(ns);
456 while (l != ns) {
457 l --;
458 if (*l != '\r' && *l != '\n')
459 break;
460 *l = 0;
461 }
462 return ns;
463 }
464
remove_dir(const char * path)465 int remove_dir(const char *path)
466 {
467 DIR *d;
468 struct dirent *de;
469 if (!path)
470 return 0;
471 d = opendir(path);
472 if (!d) {
473 if (!access(path, F_OK)) {
474 unlink(path);
475 }
476 return -1;
477 }
478 while ((de = readdir(d))) {
479 char *p;
480 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
481 continue;
482 p = getfilepath(path, de->d_name);
483 if (p) {
484 remove_dir(p);
485 free(p);
486 }
487 }
488 closedir(d);
489 rmdir(path);
490 return 0;
491 }
492
data2hex(const void * d,int len,void * o)493 void data2hex(const void *d, int len, void *o)
494 {
495 unsigned char *data = (unsigned char *)d;
496 unsigned char *out = (unsigned char *)o;
497
498 static char map[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
499 'a', 'b', 'c', 'd', 'e', 'f' };
500
501 while (len --) {
502 unsigned char a = *data ++;
503 *out ++ = map[a & 0xf];
504 *out ++ = map[a >> 4];
505 }
506 }
507
hex2data(const void * d,void * o,int len)508 int hex2data(const void *d, void *o, int len)
509 {
510 unsigned char *data = (unsigned char *)d;
511 unsigned char *out = (unsigned char *)o;
512 unsigned char b = 0;
513 int rc = 0;
514 len *= 2;
515
516 while (len --) {
517 unsigned char c = *data ++;
518 if ((c < '0' || c > '9') && (c < 'a' || c > 'f'))
519 break;
520 if (c >= 'a')
521 c -= ('a' - 10);
522 else
523 c -= '0';
524 if (len & 1)
525 b = c;
526 else
527 *out ++ = b + (c << 4);
528 rc ++;
529 }
530 return rc;
531 }
532
trunc_lines(char * pp,int max)533 void trunc_lines(char *pp, int max)
534 {
535 int n = 0;
536
537 if (!pp)
538 return;
539
540 if (max == 0) {
541 pp[strcspn(pp, "\n\r")] = 0;
542 return;
543 }
544
545 while (pp[strcspn(pp, "\n\r")]) {
546 n ++;
547 pp += strcspn(pp, "\n\r");
548 if (n >= max) {
549 *pp = 0;
550 break;
551 }
552 pp ++;
553 }
554 }
555
556 #ifdef __EMSCRIPTEN__
data_sync(void)557 void data_sync(void)
558 {
559 EM_ASM(FS.syncfs(function(error) {
560 if (error) {
561 console.log("Error while syncing:", error);
562 } else {
563 console.log("Config synced");
564 }
565 }););
566 }
567 #else
data_sync(void)568 void data_sync(void)
569 {
570 }
571 #endif
572