diff options
Diffstat (limited to 'lib/skin_parser/skin_parser.c')
-rw-r--r-- | lib/skin_parser/skin_parser.c | 923 |
1 files changed, 923 insertions, 0 deletions
diff --git a/lib/skin_parser/skin_parser.c b/lib/skin_parser/skin_parser.c new file mode 100644 index 0000000000..93a71919bf --- /dev/null +++ b/lib/skin_parser/skin_parser.c | |||
@@ -0,0 +1,923 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2010 Robert Bieber | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include <stdlib.h> | ||
23 | #include <stdio.h> | ||
24 | #include <string.h> | ||
25 | #include <ctype.h> | ||
26 | |||
27 | #include "skin_parser.h" | ||
28 | #include "skin_debug.h" | ||
29 | #include "tag_table.h" | ||
30 | #include "symbols.h" | ||
31 | #include "skin_scan.h" | ||
32 | |||
33 | #ifdef ROCKBOX | ||
34 | /* Declaration of parse tree buffer */ | ||
35 | #define SKIN_MAX_MEMORY (30*1024) | ||
36 | static char skin_parse_tree[SKIN_MAX_MEMORY]; | ||
37 | static char *skin_buffer; | ||
38 | #endif | ||
39 | |||
40 | /* Global variables for the parser */ | ||
41 | int skin_line = 0; | ||
42 | |||
43 | /* Auxiliary parsing functions (not visible at global scope) */ | ||
44 | static struct skin_element* skin_parse_viewport(char** document); | ||
45 | static struct skin_element* skin_parse_line(char** document); | ||
46 | static struct skin_element* skin_parse_line_optional(char** document, | ||
47 | int conditional); | ||
48 | static struct skin_element* skin_parse_sublines(char** document); | ||
49 | static struct skin_element* skin_parse_sublines_optional(char** document, | ||
50 | int conditional); | ||
51 | |||
52 | static int skin_parse_tag(struct skin_element* element, char** document); | ||
53 | static int skin_parse_text(struct skin_element* element, char** document, | ||
54 | int conditional); | ||
55 | static int skin_parse_conditional(struct skin_element* element, | ||
56 | char** document); | ||
57 | static int skin_parse_comment(struct skin_element* element, char** document); | ||
58 | static struct skin_element* skin_parse_code_as_arg(char** document); | ||
59 | |||
60 | struct skin_element* skin_parse(const char* document) | ||
61 | { | ||
62 | |||
63 | struct skin_element* root = NULL; | ||
64 | struct skin_element* last = NULL; | ||
65 | |||
66 | struct skin_element** to_write = 0; | ||
67 | |||
68 | char* cursor = (char*)document; /*Keeps track of location in the document*/ | ||
69 | #ifdef ROCKBOX | ||
70 | /* FIXME */ | ||
71 | skin_buffer = &skin_parse_tree[0]; | ||
72 | #endif | ||
73 | |||
74 | skin_line = 1; | ||
75 | |||
76 | skin_clear_errors(); | ||
77 | |||
78 | while(*cursor != '\0') | ||
79 | { | ||
80 | |||
81 | if(!root) | ||
82 | to_write = &root; | ||
83 | else | ||
84 | to_write = &(last->next); | ||
85 | |||
86 | |||
87 | *to_write = skin_parse_viewport(&cursor); | ||
88 | last = *to_write; | ||
89 | if(!last) | ||
90 | { | ||
91 | skin_free_tree(root); /* Clearing any memory already used */ | ||
92 | return NULL; | ||
93 | } | ||
94 | |||
95 | /* Making sure last is at the end */ | ||
96 | while(last->next) | ||
97 | last = last->next; | ||
98 | |||
99 | } | ||
100 | |||
101 | return root; | ||
102 | |||
103 | } | ||
104 | |||
105 | static struct skin_element* skin_parse_viewport(char** document) | ||
106 | { | ||
107 | |||
108 | struct skin_element* root = NULL; | ||
109 | struct skin_element* last = NULL; | ||
110 | struct skin_element* retval = NULL; | ||
111 | |||
112 | retval = skin_alloc_element(); | ||
113 | retval->type = VIEWPORT; | ||
114 | retval->children_count = 1; | ||
115 | retval->line = skin_line; | ||
116 | |||
117 | struct skin_element** to_write = 0; | ||
118 | |||
119 | char* cursor = *document; /* Keeps track of location in the document */ | ||
120 | char* bookmark; /* Used when we need to look ahead */ | ||
121 | |||
122 | int sublines = 0; /* Flag for parsing sublines */ | ||
123 | |||
124 | /* Parsing out the viewport tag if there is one */ | ||
125 | if(check_viewport(cursor)) | ||
126 | { | ||
127 | skin_parse_tag(retval, &cursor); | ||
128 | if(*cursor == '\n') | ||
129 | { | ||
130 | cursor++; | ||
131 | skin_line++; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | retval->children_count = 1; | ||
136 | retval->children = skin_alloc_children(1); | ||
137 | |||
138 | |||
139 | do | ||
140 | { | ||
141 | |||
142 | /* First, we check to see if this line will contain sublines */ | ||
143 | bookmark = cursor; | ||
144 | sublines = 0; | ||
145 | while(*cursor != '\n' && *cursor != '\0' | ||
146 | && !(check_viewport(cursor) && cursor != *document)) | ||
147 | { | ||
148 | if(*cursor == MULTILINESYM) | ||
149 | { | ||
150 | sublines = 1; | ||
151 | break; | ||
152 | } | ||
153 | else if(*cursor == TAGSYM) | ||
154 | { | ||
155 | /* A ';' directly after a '%' doesn't count */ | ||
156 | cursor ++; | ||
157 | |||
158 | if(*cursor == '\0') | ||
159 | break; | ||
160 | |||
161 | cursor++; | ||
162 | } | ||
163 | else if(*cursor == COMMENTSYM) | ||
164 | { | ||
165 | skip_comment(&cursor); | ||
166 | } | ||
167 | else if(*cursor == ARGLISTOPENSYM) | ||
168 | { | ||
169 | skip_arglist(&cursor); | ||
170 | } | ||
171 | else if(*cursor == ENUMLISTOPENSYM) | ||
172 | { | ||
173 | skip_enumlist(&cursor); | ||
174 | } | ||
175 | else | ||
176 | { | ||
177 | /* Advancing the cursor as normal */ | ||
178 | cursor++; | ||
179 | } | ||
180 | } | ||
181 | cursor = bookmark; | ||
182 | |||
183 | if(!root) | ||
184 | to_write = &root; | ||
185 | else | ||
186 | to_write = &(last->next); | ||
187 | |||
188 | if(sublines) | ||
189 | { | ||
190 | *to_write = skin_parse_sublines(&cursor); | ||
191 | last = *to_write; | ||
192 | if(!last) | ||
193 | return NULL; | ||
194 | } | ||
195 | else | ||
196 | { | ||
197 | |||
198 | *to_write = skin_parse_line(&cursor); | ||
199 | last = *to_write; | ||
200 | if(!last) | ||
201 | return NULL; | ||
202 | |||
203 | } | ||
204 | |||
205 | /* Making sure last is at the end */ | ||
206 | while(last->next) | ||
207 | last = last->next; | ||
208 | |||
209 | if(*cursor == '\n') | ||
210 | { | ||
211 | cursor++; | ||
212 | skin_line++; | ||
213 | } | ||
214 | } | ||
215 | while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document)); | ||
216 | |||
217 | *document = cursor; | ||
218 | |||
219 | retval->children[0] = root; | ||
220 | return retval; | ||
221 | |||
222 | } | ||
223 | |||
224 | /* Auxiliary Parsing Functions */ | ||
225 | |||
226 | static struct skin_element* skin_parse_line(char**document) | ||
227 | { | ||
228 | |||
229 | return skin_parse_line_optional(document, 0); | ||
230 | |||
231 | } | ||
232 | |||
233 | |||
234 | /* | ||
235 | * If conditional is set to true, then this will break upon encountering | ||
236 | * SEPERATESYM. This should only be used when parsing a line inside a | ||
237 | * conditional, otherwise just use the wrapper function skin_parse_line() | ||
238 | */ | ||
239 | static struct skin_element* skin_parse_line_optional(char** document, | ||
240 | int conditional) | ||
241 | { | ||
242 | char* cursor = *document; | ||
243 | |||
244 | struct skin_element* root = NULL; | ||
245 | struct skin_element* current = NULL; | ||
246 | struct skin_element* retval = NULL; | ||
247 | |||
248 | /* A wrapper for the line */ | ||
249 | retval = skin_alloc_element(); | ||
250 | retval->type = LINE; | ||
251 | retval->line = skin_line; | ||
252 | if(*cursor != '\0' && *cursor != '\n' | ||
253 | && !(conditional && (*cursor == ARGLISTSEPERATESYM | ||
254 | || *cursor == ARGLISTCLOSESYM | ||
255 | || *cursor == ENUMLISTSEPERATESYM | ||
256 | || *cursor == ENUMLISTCLOSESYM))) | ||
257 | { | ||
258 | retval->children_count = 1; | ||
259 | } | ||
260 | else | ||
261 | { | ||
262 | retval->children_count = 0; | ||
263 | } | ||
264 | |||
265 | if(retval->children_count > 0) | ||
266 | retval->children = skin_alloc_children(1); | ||
267 | |||
268 | while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM | ||
269 | && !((*cursor == ARGLISTSEPERATESYM | ||
270 | || *cursor == ARGLISTCLOSESYM | ||
271 | || *cursor == ENUMLISTSEPERATESYM | ||
272 | || *cursor == ENUMLISTCLOSESYM) | ||
273 | && conditional) | ||
274 | && !(check_viewport(cursor) && cursor != *document)) | ||
275 | { | ||
276 | /* Allocating memory if necessary */ | ||
277 | if(root) | ||
278 | { | ||
279 | current->next = skin_alloc_element(); | ||
280 | current = current->next; | ||
281 | } | ||
282 | else | ||
283 | { | ||
284 | current = skin_alloc_element(); | ||
285 | root = current; | ||
286 | } | ||
287 | |||
288 | /* Parsing the current element */ | ||
289 | if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM) | ||
290 | { | ||
291 | if(!skin_parse_conditional(current, &cursor)) | ||
292 | return NULL; | ||
293 | } | ||
294 | else if(*cursor == TAGSYM && !find_escape_character(cursor[1])) | ||
295 | { | ||
296 | if(!skin_parse_tag(current, &cursor)) | ||
297 | return NULL; | ||
298 | } | ||
299 | else if(*cursor == COMMENTSYM) | ||
300 | { | ||
301 | if(!skin_parse_comment(current, &cursor)) | ||
302 | return NULL; | ||
303 | } | ||
304 | else | ||
305 | { | ||
306 | if(!skin_parse_text(current, &cursor, conditional)) | ||
307 | return NULL; | ||
308 | } | ||
309 | } | ||
310 | |||
311 | /* Moving up the calling function's pointer */ | ||
312 | *document = cursor; | ||
313 | |||
314 | if(root) | ||
315 | retval->children[0] = root; | ||
316 | return retval; | ||
317 | } | ||
318 | |||
319 | static struct skin_element* skin_parse_sublines(char** document) | ||
320 | { | ||
321 | return skin_parse_sublines_optional(document, 0); | ||
322 | } | ||
323 | |||
324 | static struct skin_element* skin_parse_sublines_optional(char** document, | ||
325 | int conditional) | ||
326 | { | ||
327 | struct skin_element* retval; | ||
328 | char* cursor = *document; | ||
329 | int sublines = 1; | ||
330 | int i; | ||
331 | |||
332 | retval = skin_alloc_element(); | ||
333 | retval->type = SUBLINES; | ||
334 | retval->next = NULL; | ||
335 | retval->line = skin_line; | ||
336 | |||
337 | /* First we count the sublines */ | ||
338 | while(*cursor != '\0' && *cursor != '\n' | ||
339 | && !((*cursor == ARGLISTSEPERATESYM | ||
340 | || *cursor == ARGLISTCLOSESYM | ||
341 | || *cursor == ENUMLISTSEPERATESYM | ||
342 | || *cursor == ENUMLISTCLOSESYM) | ||
343 | && conditional) | ||
344 | && !(check_viewport(cursor) && cursor != *document)) | ||
345 | { | ||
346 | if(*cursor == COMMENTSYM) | ||
347 | { | ||
348 | skip_comment(&cursor); | ||
349 | } | ||
350 | else if(*cursor == ENUMLISTOPENSYM) | ||
351 | { | ||
352 | skip_enumlist(&cursor); | ||
353 | } | ||
354 | else if(*cursor == ARGLISTOPENSYM) | ||
355 | { | ||
356 | skip_arglist(&cursor); | ||
357 | } | ||
358 | else if(*cursor == TAGSYM) | ||
359 | { | ||
360 | cursor++; | ||
361 | if(*cursor == '\0' || *cursor == '\n') | ||
362 | break; | ||
363 | cursor++; | ||
364 | } | ||
365 | else if(*cursor == MULTILINESYM) | ||
366 | { | ||
367 | sublines++; | ||
368 | cursor++; | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | cursor++; | ||
373 | } | ||
374 | } | ||
375 | |||
376 | /* ...and then we parse them */ | ||
377 | retval->children_count = sublines; | ||
378 | retval->children = skin_alloc_children(sublines); | ||
379 | |||
380 | cursor = *document; | ||
381 | for(i = 0; i < sublines; i++) | ||
382 | { | ||
383 | retval->children[i] = skin_parse_line_optional(&cursor, conditional); | ||
384 | skip_whitespace(&cursor); | ||
385 | |||
386 | if(*cursor != MULTILINESYM && i != sublines - 1) | ||
387 | { | ||
388 | skin_error(MULTILINE_EXPECTED); | ||
389 | return NULL; | ||
390 | } | ||
391 | else if(i != sublines - 1) | ||
392 | { | ||
393 | cursor++; | ||
394 | } | ||
395 | } | ||
396 | |||
397 | *document = cursor; | ||
398 | |||
399 | return retval; | ||
400 | } | ||
401 | |||
402 | static int skin_parse_tag(struct skin_element* element, char** document) | ||
403 | { | ||
404 | |||
405 | char* cursor = *document + 1; | ||
406 | char* bookmark; | ||
407 | |||
408 | char tag_name[3]; | ||
409 | char* tag_args; | ||
410 | struct tag_info *tag; | ||
411 | |||
412 | int num_args = 1; | ||
413 | int i; | ||
414 | int star = 0; /* Flag for the all-or-none option */ | ||
415 | int req_args; /* To mark when we enter optional arguments */ | ||
416 | |||
417 | int optional = 0; | ||
418 | |||
419 | /* Checking the tag name */ | ||
420 | tag_name[0] = cursor[0]; | ||
421 | tag_name[1] = cursor[1]; | ||
422 | tag_name[2] = '\0'; | ||
423 | |||
424 | /* First we check the two characters after the '%', then a single char */ | ||
425 | tag = find_tag(tag_name); | ||
426 | |||
427 | if(!tag) | ||
428 | { | ||
429 | tag_name[1] = '\0'; | ||
430 | tag = find_tag(tag_name); | ||
431 | cursor++; | ||
432 | } | ||
433 | else | ||
434 | { | ||
435 | cursor += 2; | ||
436 | } | ||
437 | |||
438 | if(!tag) | ||
439 | { | ||
440 | skin_error(ILLEGAL_TAG); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | /* Copying basic tag info */ | ||
445 | if(element->type != CONDITIONAL && element->type != VIEWPORT) | ||
446 | element->type = TAG; | ||
447 | element->tag = tag; | ||
448 | tag_args = tag->params; | ||
449 | element->line = skin_line; | ||
450 | |||
451 | /* Checking for the * flag */ | ||
452 | if(tag_args[0] == '*') | ||
453 | { | ||
454 | star = 1; | ||
455 | tag_args++; | ||
456 | } | ||
457 | |||
458 | /* If this tag has no arguments, we can bail out now */ | ||
459 | if(strlen(tag_args) == 0 | ||
460 | || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM) | ||
461 | || (star && *cursor != ARGLISTOPENSYM)) | ||
462 | { | ||
463 | *document = cursor; | ||
464 | return 1; | ||
465 | } | ||
466 | |||
467 | /* Checking the number of arguments and allocating args */ | ||
468 | if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|') | ||
469 | { | ||
470 | skin_error(ARGLIST_EXPECTED); | ||
471 | return 0; | ||
472 | } | ||
473 | else | ||
474 | { | ||
475 | cursor++; | ||
476 | } | ||
477 | |||
478 | bookmark = cursor; | ||
479 | while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM) | ||
480 | { | ||
481 | /* Skipping over escaped characters */ | ||
482 | if(*cursor == TAGSYM) | ||
483 | { | ||
484 | cursor++; | ||
485 | if(*cursor == '\0') | ||
486 | break; | ||
487 | cursor++; | ||
488 | } | ||
489 | else if(*cursor == COMMENTSYM) | ||
490 | { | ||
491 | skip_comment(&cursor); | ||
492 | } | ||
493 | else if(*cursor == ARGLISTOPENSYM) | ||
494 | { | ||
495 | skip_arglist(&cursor); | ||
496 | } | ||
497 | else if(*cursor == ARGLISTSEPERATESYM) | ||
498 | { | ||
499 | num_args++; | ||
500 | cursor++; | ||
501 | } | ||
502 | else | ||
503 | { | ||
504 | cursor++; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | cursor = bookmark; /* Restoring the cursor */ | ||
509 | element->params_count = num_args; | ||
510 | element->params = skin_alloc_params(num_args); | ||
511 | |||
512 | /* Now we have to actually parse each argument */ | ||
513 | for(i = 0; i < num_args; i++) | ||
514 | { | ||
515 | /* Making sure we haven't run out of arguments */ | ||
516 | if(*tag_args == '\0') | ||
517 | { | ||
518 | skin_error(TOO_MANY_ARGS); | ||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /* Checking for the optional bar */ | ||
523 | if(*tag_args == '|') | ||
524 | { | ||
525 | optional = 1; | ||
526 | req_args = i; | ||
527 | tag_args++; | ||
528 | } | ||
529 | |||
530 | /* Scanning the arguments */ | ||
531 | skip_whitespace(&cursor); | ||
532 | |||
533 | |||
534 | /* Checking for comments */ | ||
535 | if(*cursor == COMMENTSYM) | ||
536 | skip_comment(&cursor); | ||
537 | |||
538 | /* Storing the type code */ | ||
539 | element->params[i].type_code = *tag_args; | ||
540 | |||
541 | /* Checking a nullable argument for null */ | ||
542 | if(*cursor == DEFAULTSYM && !isdigit(cursor[1])) | ||
543 | { | ||
544 | if(islower(*tag_args)) | ||
545 | { | ||
546 | element->params[i].type = DEFAULT; | ||
547 | cursor++; | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | skin_error(DEFAULT_NOT_ALLOWED); | ||
552 | return 0; | ||
553 | } | ||
554 | } | ||
555 | else if(tolower(*tag_args) == 'i') | ||
556 | { | ||
557 | /* Scanning an int argument */ | ||
558 | if(!isdigit(*cursor) && *cursor != '-') | ||
559 | { | ||
560 | skin_error(INT_EXPECTED); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | element->params[i].type = NUMERIC; | ||
565 | element->params[i].data.numeric = scan_int(&cursor); | ||
566 | } | ||
567 | else if(tolower(*tag_args) == 'n' || | ||
568 | tolower(*tag_args) == 's' || tolower(*tag_args) == 'f') | ||
569 | { | ||
570 | /* Scanning a string argument */ | ||
571 | element->params[i].type = STRING; | ||
572 | element->params[i].data.text = scan_string(&cursor); | ||
573 | |||
574 | } | ||
575 | else if(tolower(*tag_args) == 'c') | ||
576 | { | ||
577 | /* Recursively parsing a code argument */ | ||
578 | element->params[i].type = CODE; | ||
579 | element->params[i].data.code = skin_parse_code_as_arg(&cursor); | ||
580 | if(!element->params[i].data.code) | ||
581 | return 0; | ||
582 | } | ||
583 | |||
584 | skip_whitespace(&cursor); | ||
585 | |||
586 | if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1) | ||
587 | { | ||
588 | skin_error(SEPERATOR_EXPECTED); | ||
589 | return 0; | ||
590 | } | ||
591 | else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1) | ||
592 | { | ||
593 | skin_error(CLOSE_EXPECTED); | ||
594 | return 0; | ||
595 | } | ||
596 | else | ||
597 | { | ||
598 | cursor++; | ||
599 | } | ||
600 | |||
601 | if (*tag_args != 'N') | ||
602 | tag_args++; | ||
603 | |||
604 | /* Checking for the optional bar */ | ||
605 | if(*tag_args == '|') | ||
606 | { | ||
607 | optional = 1; | ||
608 | req_args = i + 1; | ||
609 | tag_args++; | ||
610 | } | ||
611 | |||
612 | } | ||
613 | |||
614 | /* Checking for a premature end */ | ||
615 | if(*tag_args != '\0' && !optional) | ||
616 | { | ||
617 | skin_error(INSUFFICIENT_ARGS); | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | *document = cursor; | ||
622 | |||
623 | return 1; | ||
624 | } | ||
625 | |||
626 | /* | ||
627 | * If the conditional flag is set true, then parsing text will stop at an | ||
628 | * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional | ||
629 | */ | ||
630 | static int skin_parse_text(struct skin_element* element, char** document, | ||
631 | int conditional) | ||
632 | { | ||
633 | char* cursor = *document; | ||
634 | int length = 0; | ||
635 | int dest; | ||
636 | char *text = NULL; | ||
637 | |||
638 | /* First figure out how much text we're copying */ | ||
639 | while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM | ||
640 | && *cursor != COMMENTSYM | ||
641 | && !((*cursor == ARGLISTSEPERATESYM | ||
642 | || *cursor == ARGLISTCLOSESYM | ||
643 | || *cursor == ENUMLISTSEPERATESYM | ||
644 | || *cursor == ENUMLISTCLOSESYM) | ||
645 | && conditional)) | ||
646 | { | ||
647 | /* Dealing with possibility of escaped characters */ | ||
648 | if(*cursor == TAGSYM) | ||
649 | { | ||
650 | if(find_escape_character(cursor[1])) | ||
651 | cursor++; | ||
652 | else | ||
653 | break; | ||
654 | } | ||
655 | |||
656 | length++; | ||
657 | cursor++; | ||
658 | } | ||
659 | |||
660 | cursor = *document; | ||
661 | |||
662 | /* Copying the text into the element struct */ | ||
663 | element->type = TEXT; | ||
664 | element->line = skin_line; | ||
665 | element->next = NULL; | ||
666 | element->data = text = skin_alloc_string(length); | ||
667 | |||
668 | for(dest = 0; dest < length; dest++) | ||
669 | { | ||
670 | /* Advancing cursor if we've encountered an escaped character */ | ||
671 | if(*cursor == TAGSYM) | ||
672 | cursor++; | ||
673 | |||
674 | text[dest] = *cursor; | ||
675 | cursor++; | ||
676 | } | ||
677 | text[length] = '\0'; | ||
678 | |||
679 | *document = cursor; | ||
680 | |||
681 | return 1; | ||
682 | } | ||
683 | |||
684 | static int skin_parse_conditional(struct skin_element* element, char** document) | ||
685 | { | ||
686 | |||
687 | char* cursor = *document + 1; /* Starting past the "%" */ | ||
688 | char* bookmark; | ||
689 | int children = 1; | ||
690 | int i; | ||
691 | |||
692 | element->type = CONDITIONAL; | ||
693 | element->line = skin_line; | ||
694 | |||
695 | /* Parsing the tag first */ | ||
696 | if(!skin_parse_tag(element, &cursor)) | ||
697 | return 0; | ||
698 | |||
699 | /* Counting the children */ | ||
700 | if(*(cursor++) != ENUMLISTOPENSYM) | ||
701 | { | ||
702 | skin_error(ARGLIST_EXPECTED); | ||
703 | return 0; | ||
704 | } | ||
705 | bookmark = cursor; | ||
706 | while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0') | ||
707 | { | ||
708 | if(*cursor == COMMENTSYM) | ||
709 | { | ||
710 | skip_comment(&cursor); | ||
711 | } | ||
712 | else if(*cursor == ENUMLISTOPENSYM) | ||
713 | { | ||
714 | skip_enumlist(&cursor); | ||
715 | } | ||
716 | else if(*cursor == TAGSYM) | ||
717 | { | ||
718 | cursor++; | ||
719 | if(*cursor == '\0' || *cursor == '\n') | ||
720 | break; | ||
721 | cursor++; | ||
722 | } | ||
723 | else if(*cursor == ENUMLISTSEPERATESYM) | ||
724 | { | ||
725 | children++; | ||
726 | cursor++; | ||
727 | } | ||
728 | else | ||
729 | { | ||
730 | cursor++; | ||
731 | } | ||
732 | } | ||
733 | cursor = bookmark; | ||
734 | |||
735 | /* Parsing the children */ | ||
736 | element->children = skin_alloc_children(children); | ||
737 | element->children_count = children; | ||
738 | |||
739 | for(i = 0; i < children; i++) | ||
740 | { | ||
741 | element->children[i] = skin_parse_code_as_arg(&cursor); | ||
742 | skip_whitespace(&cursor); | ||
743 | |||
744 | if(i < children - 1 && *cursor != ENUMLISTSEPERATESYM) | ||
745 | { | ||
746 | skin_error(SEPERATOR_EXPECTED); | ||
747 | return 0; | ||
748 | } | ||
749 | else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM) | ||
750 | { | ||
751 | skin_error(CLOSE_EXPECTED); | ||
752 | return 0; | ||
753 | } | ||
754 | else | ||
755 | { | ||
756 | cursor++; | ||
757 | } | ||
758 | } | ||
759 | |||
760 | *document = cursor; | ||
761 | |||
762 | return 1; | ||
763 | } | ||
764 | |||
765 | static int skin_parse_comment(struct skin_element* element, char** document) | ||
766 | { | ||
767 | char* cursor = *document; | ||
768 | char* text = NULL; | ||
769 | |||
770 | int length; | ||
771 | /* | ||
772 | * Finding the index of the ending newline or null-terminator | ||
773 | * The length of the string of interest doesn't include the leading #, the | ||
774 | * length we need to reserve is the same as the index of the last character | ||
775 | */ | ||
776 | for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++); | ||
777 | |||
778 | element->type = COMMENT; | ||
779 | element->line = skin_line; | ||
780 | #ifdef ROCKBOX | ||
781 | element->data = NULL; | ||
782 | #else | ||
783 | element->data = text = skin_alloc_string(length); | ||
784 | /* We copy from one char past cursor to leave out the # */ | ||
785 | memcpy((void*)text, (void*)(cursor + 1), | ||
786 | sizeof(char) * (length-1)); | ||
787 | text[length - 1] = '\0'; | ||
788 | #endif | ||
789 | if(cursor[length] == '\n') | ||
790 | skin_line++; | ||
791 | |||
792 | *document += (length); /* Move cursor up past # and all text */ | ||
793 | if(**document == '\n') | ||
794 | (*document)++; | ||
795 | |||
796 | return 1; | ||
797 | } | ||
798 | |||
799 | static struct skin_element* skin_parse_code_as_arg(char** document) | ||
800 | { | ||
801 | |||
802 | int sublines = 0; | ||
803 | char* cursor = *document; | ||
804 | |||
805 | /* Checking for sublines */ | ||
806 | while(*cursor != '\n' && *cursor != '\0' | ||
807 | && *cursor != ENUMLISTSEPERATESYM && *cursor != ARGLISTSEPERATESYM | ||
808 | && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM) | ||
809 | { | ||
810 | if(*cursor == MULTILINESYM) | ||
811 | { | ||
812 | sublines = 1; | ||
813 | break; | ||
814 | } | ||
815 | else if(*cursor == TAGSYM) | ||
816 | { | ||
817 | /* A ';' directly after a '%' doesn't count */ | ||
818 | cursor ++; | ||
819 | |||
820 | if(*cursor == '\0') | ||
821 | break; | ||
822 | |||
823 | cursor++; | ||
824 | } | ||
825 | else if(*cursor == ARGLISTOPENSYM) | ||
826 | { | ||
827 | skip_arglist(&cursor); | ||
828 | } | ||
829 | else if(*cursor == ENUMLISTOPENSYM) | ||
830 | { | ||
831 | skip_enumlist(&cursor); | ||
832 | } | ||
833 | else | ||
834 | { | ||
835 | /* Advancing the cursor as normal */ | ||
836 | cursor++; | ||
837 | } | ||
838 | } | ||
839 | |||
840 | if(sublines) | ||
841 | return skin_parse_sublines_optional(document, 1); | ||
842 | else | ||
843 | return skin_parse_line_optional(document, 1); | ||
844 | } | ||
845 | |||
846 | |||
847 | /* Memory management */ | ||
848 | char* skin_alloc(size_t size) | ||
849 | { | ||
850 | #ifdef ROCKBOX | ||
851 | char *retval = skin_buffer; | ||
852 | skin_buffer = (void *)(((unsigned long)skin_buffer + 3) & ~3); | ||
853 | return retval; | ||
854 | #else | ||
855 | return malloc(size); | ||
856 | #endif | ||
857 | } | ||
858 | |||
859 | struct skin_element* skin_alloc_element() | ||
860 | { | ||
861 | struct skin_element* retval = (struct skin_element*) | ||
862 | skin_alloc(sizeof(struct skin_element)); | ||
863 | retval->type = UNKNOWN; | ||
864 | retval->next = NULL; | ||
865 | retval->tag = NULL; | ||
866 | retval->params_count = 0; | ||
867 | retval->children_count = 0; | ||
868 | |||
869 | return retval; | ||
870 | |||
871 | } | ||
872 | |||
873 | struct skin_tag_parameter* skin_alloc_params(int count) | ||
874 | { | ||
875 | size_t size = sizeof(struct skin_tag_parameter) * count; | ||
876 | return (struct skin_tag_parameter*)skin_alloc(size); | ||
877 | |||
878 | } | ||
879 | |||
880 | char* skin_alloc_string(int length) | ||
881 | { | ||
882 | return (char*)skin_alloc(sizeof(char) * (length + 1)); | ||
883 | } | ||
884 | |||
885 | struct skin_element** skin_alloc_children(int count) | ||
886 | { | ||
887 | return (struct skin_element**) | ||
888 | skin_alloc(sizeof(struct skin_element*) * count); | ||
889 | } | ||
890 | |||
891 | void skin_free_tree(struct skin_element* root) | ||
892 | { | ||
893 | #ifndef ROCKBOX | ||
894 | int i; | ||
895 | |||
896 | /* First make the recursive call */ | ||
897 | if(!root) | ||
898 | return; | ||
899 | skin_free_tree(root->next); | ||
900 | |||
901 | /* Free any text */ | ||
902 | if(root->type == TEXT || root->type == COMMENT) | ||
903 | free(root->data); | ||
904 | |||
905 | /* Then recursively free any children, before freeing their pointers */ | ||
906 | for(i = 0; i < root->children_count; i++) | ||
907 | skin_free_tree(root->children[i]); | ||
908 | if(root->children_count > 0) | ||
909 | free(root->children); | ||
910 | |||
911 | /* Free any parameters, making sure to deallocate strings */ | ||
912 | for(i = 0; i < root->params_count; i++) | ||
913 | if(root->params[i].type == STRING) | ||
914 | free(root->params[i].data.text); | ||
915 | if(root->params_count > 0) | ||
916 | free(root->params); | ||
917 | |||
918 | /* Finally, delete root's memory */ | ||
919 | free(root); | ||
920 | #else | ||
921 | (void)root; | ||
922 | #endif | ||
923 | } | ||