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