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