1 /*
2  *  STFL - The Structured Terminal Forms Language/Library
3  *  Copyright (C) 2006, 2007  Clifford Wolf <clifford@clifford.at>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 3 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *  MA 02110-1301 USA
19  *
20  *  parser.c: STFL Form description file parser
21  */
22 
23 #include "stfl_internals.h"
24 #include "stfl_compat.h"
25 
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <assert.h>
30 
31 #define MYWCSCSPN_SKIP_QUOTED	0x01
32 #define MYWCSCSPN_SKIP_NAMES	0x02
33 
mywcscspn(const wchar_t * wcs,const wchar_t * reject,int flags)34 static size_t mywcscspn(const wchar_t *wcs, const wchar_t *reject, int flags)
35 {
36 	enum {
37 		PLAIN,
38 		NAME_BLOCK,
39 		SINGLE_QUOTE,
40 		SINGLE_QUOTE_NAME,
41 		DOUBLE_QUOTE,
42 		DOUBLE_QUOTE_NAME,
43 	} state = PLAIN;
44 
45 	int len = 0;
46 	int i;
47 
48 	while (1)
49 	{
50 		if (!wcs[len])
51 			return len;
52 
53 		switch (state)
54 		{
55 		case PLAIN:
56 			if ((flags & MYWCSCSPN_SKIP_NAMES) && (wcs[len] == L'[')) {
57 				state = NAME_BLOCK;
58 				break;
59 			}
60 			if ((flags & MYWCSCSPN_SKIP_QUOTED) && (wcs[len] == L'\'')) {
61 				state = SINGLE_QUOTE;
62 				break;
63 			}
64 			if ((flags & MYWCSCSPN_SKIP_QUOTED) && (wcs[len] == L'\"')) {
65 				state = DOUBLE_QUOTE;
66 				break;
67 			}
68 			for (i=0; reject[i]; i++)
69 				if (wcs[len] == reject[i])
70 					return len;
71 			break;
72 		case NAME_BLOCK:
73 			if ((flags & MYWCSCSPN_SKIP_QUOTED) && (wcs[len] == L'\'')) {
74 				state = SINGLE_QUOTE_NAME;
75 				break;
76 			}
77 			if ((flags & MYWCSCSPN_SKIP_QUOTED) && (wcs[len] == L'\"')) {
78 				state = DOUBLE_QUOTE_NAME;
79 				break;
80 			}
81 			if (wcs[len] == L']')
82 				state = PLAIN;
83 			break;
84 		case SINGLE_QUOTE:
85 		case SINGLE_QUOTE_NAME:
86 			if (wcs[len] == L'\'')
87 				state = state == SINGLE_QUOTE ? PLAIN : NAME_BLOCK;
88 			break;
89 		case DOUBLE_QUOTE:
90 		case DOUBLE_QUOTE_NAME:
91 			if (wcs[len] == L'\"')
92 				state = state == DOUBLE_QUOTE ? PLAIN : NAME_BLOCK;
93 			break;
94 		}
95 
96 		len++;
97 	}
98 }
99 
unquote(const wchar_t * text,int tlen)100 static wchar_t *unquote(const wchar_t *text, int tlen)
101 {
102 	int len_v = 0, i, j;
103 	wchar_t *value;
104 
105 	if (!text)
106 		return 0;
107 
108 	for (i=0; (i<tlen || tlen<0) && text[i]; i++)
109 	{
110 		if (text[i] == L'\'')
111 			while (1) {
112 				if (++i == tlen && tlen >= 0)
113 					goto finish_len_v_loop;
114 				if (!text[i] || text[i] == L'\'') break;
115 				len_v++;
116 			}
117 		else
118 		if (text[i] == L'\"')
119 			while (1) {
120 				if (++i == tlen && tlen >= 0)
121 					goto finish_len_v_loop;
122 				if (!text[i] || text[i] == L'\"') break;
123 				len_v++;
124 			}
125 		else
126 			len_v++;
127 finish_len_v_loop:;
128 	}
129 
130 	value = malloc(sizeof(wchar_t)*(len_v+1));
131 
132 	for (i=j=0; (i<tlen || tlen<0) && text[i]; i++)
133 	{
134 		if (text[i] == L'\'')
135 			while (1) {
136 				if (++i == tlen && tlen >= 0)
137 					goto finish_copy_loop;
138 				if (!text[i] || text[i] == L'\'') break;
139 				value[j++] = text[i];
140 			}
141 		else
142 		if (text[i] == L'\"')
143 			while (1) {
144 				if (++i == tlen && tlen >= 0)
145 					goto finish_copy_loop;
146 				if (!text[i] || text[i] == L'\"') break;
147 				value[j++] = text[i];
148 			}
149 		else
150 			value[j++] = text[i];
151 finish_copy_loop:;
152 	}
153 
154 	value[j] = 0;
155 	assert(j == len_v);
156 
157 	return value;
158 }
159 
extract_name(wchar_t ** key,wchar_t ** name)160 static void extract_name(wchar_t **key, wchar_t **name)
161 {
162 	int len = wcscspn(*key, L"[");
163 
164 	if ((*key)[len] == 0) {
165 		*name = 0;
166 		return;
167 	}
168 
169 	*name = compat_wcsdup(*key+len+1);
170 	*key = realloc(*key, sizeof(wchar_t)*(len+1));
171 	(*key)[len] = 0;
172 
173 	len = mywcscspn(*name, L"]", MYWCSCSPN_SKIP_QUOTED);
174 	(*name)[len] = 0;
175 }
176 
extract_class(wchar_t ** key,wchar_t ** cls)177 static void extract_class(wchar_t **key, wchar_t **cls)
178 {
179 	int len = wcscspn(*key, L"#");
180 
181 	if ((*key)[len] == 0) {
182 		*cls = 0;
183 		return;
184 	}
185 
186 	*cls = compat_wcsdup(*key+len+1);
187 	*key = realloc(*key, sizeof(wchar_t)*(len+1));
188 	(*key)[len] = 0;
189 }
190 
read_type(const wchar_t ** text,wchar_t ** type,wchar_t ** name,wchar_t ** cls)191 static int read_type(const wchar_t **text, wchar_t **type, wchar_t **name, wchar_t **cls)
192 {
193 	int len = mywcscspn(*text, L" \t\r\n:{}", MYWCSCSPN_SKIP_QUOTED|MYWCSCSPN_SKIP_NAMES);
194 
195 	if ((*text)[len] == L':' || len == 0)
196 		return 0;
197 
198 	*type = malloc((len+1)*sizeof(wchar_t));
199 	wmemcpy(*type, *text, len);
200 	(*type)[len] = 0;
201 	*text += len;
202 
203 	extract_name(type, name);
204 	extract_class(type, cls);
205 
206 	return 1;
207 }
208 
read_kv(const wchar_t ** text,wchar_t ** key,wchar_t ** name,wchar_t ** value)209 static int read_kv(const wchar_t **text, wchar_t **key, wchar_t **name, wchar_t **value)
210 {
211 	int len_k = mywcscspn(*text, L" \t\r\n:{}", MYWCSCSPN_SKIP_QUOTED|MYWCSCSPN_SKIP_NAMES);
212 
213 	if ((*text)[len_k] != L':' || len_k == 0)
214 		return 0;
215 
216 	*key = malloc((len_k+1)*sizeof(wchar_t));
217 	wmemcpy(*key, *text, len_k);
218 	(*key)[len_k] = 0;
219 	*text += len_k+1;
220 
221 	extract_name(key, name);
222 
223 	int qval_len = mywcscspn(*text, L" \t\r\n{}", MYWCSCSPN_SKIP_QUOTED);
224 	*value = unquote(*text, qval_len);
225 	*text += qval_len;
226 
227 	return 1;
228 }
229 
stfl_parser(const wchar_t * text)230 struct stfl_widget *stfl_parser(const wchar_t *text)
231 {
232 	struct stfl_widget *root = 0;
233 	struct stfl_widget *current = 0;
234 	int bracket_indenting = -1;
235 	int bracket_level = 0;
236 
237 	while (1)
238 	{
239 		int indenting = 0;
240 
241 		if (bracket_indenting >= 0)
242 		{
243 			while (*text == L' ' || *text == L'\t') text++;
244 
245 			while (*text == L'}') {
246 				bracket_level--; text++;
247 				while (*text == L' ' || *text == L'\t') text++;
248 			}
249 
250 			while (*text == L'{') {
251 				bracket_level++; text++;
252 				while (*text == L' ' || *text == L'\t') text++;
253 			}
254 
255 			if (bracket_level == 0)
256 				bracket_indenting = -1;
257 
258 			if (bracket_level < 0)
259 				goto parser_error;
260 		}
261 		else
262 			if (*text == L'}')
263 				goto parser_error;
264 
265 		if (bracket_indenting >= 0)
266 		{
267 			while (*text == L' ' || *text == L'\t')
268 				text++;
269 
270 			if (*text == L'\r' || *text == L'\n')
271 				goto parser_error;
272 
273 			indenting = bracket_indenting + (bracket_level-1);
274 		}
275 		else
276 		{
277 			while (*text == L' ' || *text == L'\t' || *text == L'\r' || *text == L'\n') {
278 				if (*text == L'\r' || *text == L'\n')
279 					indenting = 0;
280 				else
281 				if (*text == L'\t')
282 					indenting = -1;
283 				else
284 				if (indenting >= 0)
285 					indenting++;
286 				text++;
287 			}
288 
289 			if (*text == L'*') {
290 				while (*text && *text != L'\r' && *text != L'\n')
291 					text++;
292 				continue;
293 			}
294 
295 			if (*text == L'{') {
296 				bracket_indenting = indenting;
297 				continue;
298 			}
299 		}
300 
301 		if (*text == 0)
302 			break;
303 
304 		wchar_t *key, *name, *cls, *value;
305 		if (indenting < 0)
306 			goto parser_error;
307 
308 		if (*text == L'<')
309 		{
310 			int filename_len = wcscspn(++text, L">");
311 			wchar_t wfn[filename_len+1];
312 
313 			wmemcpy(wfn, text, filename_len+1);
314 			wfn[filename_len] = 0;
315 
316 			size_t len = wcstombs(NULL,wfn,0)+1;
317 			char filename[len];
318 			size_t rc = wcstombs(filename, wfn, len);
319 			assert(rc != (size_t)-1);
320 
321 			text += filename_len;
322 			if (*text) text++;
323 
324 			struct stfl_widget *n = stfl_parser_file(filename);
325 			if (!n) return 0;
326 
327 			if (root)
328 			{
329 				while (current->parser_indent >= indenting) {
330 					current = current->parent;
331 					if (!current)
332 						goto parser_error;
333 				}
334 
335 				n->parent = current;
336 				if (current->last_child) {
337 					current->last_child->next_sibling = n;
338 					current->last_child = n;
339 				} else {
340 					current->first_child = n;
341 					current->last_child = n;
342 				}
343 
344 				n->parser_indent = indenting;
345 				current = n;
346 			}
347 			else
348 				root = n;
349 		}
350 		else
351 		if (root)
352 		{
353 			while (current->parser_indent >= indenting) {
354 				current = current->parent;
355 				if (!current)
356 					goto parser_error;
357 			}
358 
359 			if (read_type(&text, &key, &name, &cls) == 1)
360 			{
361 				struct stfl_widget *n = stfl_widget_new(key);
362 				if (!n)
363 					goto parser_error;
364 				free(key);
365 
366 				n->parent = current;
367 				if (current->last_child) {
368 					current->last_child->next_sibling = n;
369 					current->last_child = n;
370 				} else {
371 					current->first_child = n;
372 					current->last_child = n;
373 				}
374 
375 				n->parser_indent = indenting;
376 				n->name = unquote(name, -1);
377 				free(name);
378 				n->cls = cls;
379 				current = n;
380 			}
381 			else
382 			if (read_kv(&text, &key, &name, &value) == 1)
383 			{
384 				struct stfl_kv *kv = stfl_widget_setkv_str(current, key, value);
385 				if (kv->name)
386 					free(kv->name);
387 				kv->name = unquote(name, -1);
388 				free(name);
389 
390 				free(key);
391 				free(value);
392 			}
393 			else
394 				goto parser_error;
395 		}
396 		else
397 		{
398 			if (read_type(&text, &key, &name, &cls) == 0)
399 				goto parser_error;
400 
401 			struct stfl_widget *n = stfl_widget_new(key);
402 			if (!n)
403 				goto parser_error;
404 			free(key);
405 
406 			root = n;
407 			current = n;
408 			n->name = unquote(name, -1);
409 			free(name);
410 			n->cls = cls;
411 		}
412 
413 		while (*text && *text != L'\n' && *text != L'\r' && *text != L'{' && *text != L'}')
414 		{
415 			while (*text == L' ' || *text == L'\t')
416 				text++;
417 
418 			if (*text && *text != L'\n' && *text != L'\r' && *text != L'{' && *text != L'}')
419 			{
420 				if (read_kv(&text, &key, &name, &value) == 0)
421 					goto parser_error;
422 
423 				struct stfl_kv *kv = stfl_widget_setkv_str(current, key, value);
424 				if (kv->name)
425 					free(kv->name);
426 				kv->name = unquote(name, -1);
427 				free(name);
428 
429 				free(key);
430 				free(value);
431 			}
432 		}
433 	}
434 
435 	if (root)
436 		return root;
437 
438 parser_error:;
439 	int i;
440 
441 	fprintf(stderr, "STFL Parser Error near '");
442 
443 	for (i=0; *text && i<20; i++, text++)
444 		if (*text == L'\n')
445 			fprintf(stderr, "\\n");
446 		else
447 		if (*text == L'\t')
448 			fprintf(stderr, " ");
449 		else
450 		if (*text < 32)
451 			fprintf(stderr, "\\%03lo", (long unsigned int)*text);
452 		else
453 			fprintf(stderr, "%lc", (wint_t)*text);
454 
455 	fprintf(stderr, "'.\r\n");
456 	abort();
457 
458 	return 0;
459 }
460 
stfl_parser_file(const char * filename)461 struct stfl_widget *stfl_parser_file(const char *filename)
462 {
463 	FILE *f = fopen(filename, "r");
464 
465 	if (!f) {
466 		fprintf(stderr, "STFL Parser Error: Can't read file '%s'!\n", filename);
467 		abort();
468 		return 0;
469 	}
470 
471 	int len = 0;
472 	char *text = 0;
473 
474 	while (1) {
475 		int pos = len;
476 		text = realloc(text, len += 4096);
477 		pos += fread(text+pos, 1, 4096, f);
478 		if (pos < len) {
479 			text[pos] = 0;
480 			fclose(f);
481 			break;
482 		}
483 	}
484 
485 	const char * text1 = text;
486 	size_t wtextsize = mbsrtowcs(NULL,&text1,strlen(text1)+1,NULL)+1;
487 	wchar_t * wtext = malloc(sizeof(wchar_t)*wtextsize);
488 
489 	size_t rc = mbstowcs(wtext, text, wtextsize);
490 	assert(rc != (size_t)-1);
491 
492 #if 0
493 	fprintf(stderr,"strlen(text) = %u wcslen(wtext) = %u rc = %u wtextsize = %u\n", strlen(text), wcslen(wtext), rc, wtextsize);
494 	fprintf(stderr,"this is where is fucked up: `%lc' `%lc' `%lc' `%lc' `%lc'\n",text1[0],text1[1],text1[2],text1[3],text1[4]);
495 	fprintf(stderr,"original: `%s'\n", text);
496 	fprintf(stderr,"converted: `%ls'\n", wtext);
497 #endif
498 
499 	struct stfl_widget *w = stfl_parser(wtext);
500 	free(text);
501 	free(wtext);
502 
503 	return w;
504 }
505 
506