Fix configure/make reconf
[kugel-rb.git] / utils / themeeditor / skin_parser.c
blob401181cc3d09ac0de54894ef49a03d7b3c83ed11
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2010 Robert Bieber
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
27 #include "skin_parser.h"
28 #include "skin_debug.h"
29 #include "tag_table.h"
30 #include "symbols.h"
31 #include "skin_scan.h"
33 /* Declaration of parse tree buffer */
34 char skin_parse_tree[SKIN_MAX_MEMORY];
35 int skin_current_block = 0;
37 /* Global variables for the parser */
38 int skin_line = 0;
40 /* Auxiliary parsing functions (not visible at global scope) */
41 struct skin_element* skin_parse_viewport(char** document);
42 struct skin_element* skin_parse_line(char** document);
43 struct skin_element* skin_parse_line_optional(char** document, int conditional);
44 struct skin_element* skin_parse_sublines(char** document);
45 struct skin_element* skin_parse_sublines_optional(char** document,
46 int conditional);
48 int skin_parse_tag(struct skin_element* element, char** document);
49 int skin_parse_text(struct skin_element* element, char** document,
50 int conditional);
51 int skin_parse_conditional(struct skin_element* element, char** document);
52 int skin_parse_comment(struct skin_element* element, char** document);
53 struct skin_element* skin_parse_code_as_arg(char** document);
55 struct skin_element* skin_parse(const char* document)
58 struct skin_element* root = NULL;
59 struct skin_element* last = NULL;
61 struct skin_element** to_write = 0;
63 char* cursor = (char*)document; /*Keeps track of location in the document*/
65 skin_line = 1;
67 skin_clear_errors();
69 while(*cursor != '\0')
72 if(!root)
73 to_write = &root;
74 else
75 to_write = &(last->next);
78 *to_write = skin_parse_viewport(&cursor);
79 last = *to_write;
80 if(!last)
82 skin_free_tree(root); /* Clearing any memory already used */
83 return NULL;
86 /* Making sure last is at the end */
87 while(last->next)
88 last = last->next;
92 return root;
96 struct skin_element* skin_parse_viewport(char** document)
99 struct skin_element* root = NULL;
100 struct skin_element* last = NULL;
101 struct skin_element* retval = NULL;
103 retval = skin_alloc_element();
104 retval->type = VIEWPORT;
105 retval->children_count = 1;
106 retval->line = skin_line;
108 struct skin_element** to_write = 0;
110 char* cursor = *document; /* Keeps track of location in the document */
111 char* bookmark; /* Used when we need to look ahead */
113 int sublines = 0; /* Flag for parsing sublines */
115 /* Parsing out the viewport tag if there is one */
116 if(check_viewport(cursor))
118 retval->children_count = 2;
119 retval->children = skin_alloc_children(2);
120 retval->children[0] = skin_alloc_element();
121 skin_parse_tag(retval->children[0], &cursor);
122 if(*cursor == '\n')
124 cursor++;
125 skin_line++;
128 else
130 retval->children_count = 1;
131 retval->children = skin_alloc_children(1);
135 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document))
138 /* First, we check to see if this line will contain sublines */
139 bookmark = cursor;
140 sublines = 0;
141 while(*cursor != '\n' && *cursor != '\0'
142 && !(check_viewport(cursor) && cursor != *document))
144 if(*cursor == MULTILINESYM)
146 sublines = 1;
147 break;
149 else if(*cursor == TAGSYM)
151 /* A ';' directly after a '%' doesn't count */
152 cursor ++;
154 if(*cursor == '\0')
155 break;
157 cursor++;
159 else if(*cursor == COMMENTSYM)
161 skip_comment(&cursor);
163 else if(*cursor == ARGLISTOPENSYM)
165 skip_arglist(&cursor);
167 else if(*cursor == ENUMLISTOPENSYM)
169 skip_arglist(&cursor);
171 else
173 /* Advancing the cursor as normal */
174 cursor++;
177 cursor = bookmark;
179 if(!root)
180 to_write = &root;
181 else
182 to_write = &(last->next);
184 if(sublines)
186 *to_write = skin_parse_sublines(&cursor);
187 last = *to_write;
188 if(!last)
189 return NULL;
191 else
194 *to_write = skin_parse_line(&cursor);
195 last = *to_write;
196 if(!last)
197 return NULL;
201 /* Making sure last is at the end */
202 while(last->next)
203 last = last->next;
205 if(*cursor == '\n')
207 cursor++;
208 skin_line++;
212 *document = cursor;
214 retval->children[retval->children_count - 1] = root;
215 return retval;
219 /* Auxiliary Parsing Functions */
221 struct skin_element* skin_parse_line(char**document)
224 return skin_parse_line_optional(document, 0);
230 * If conditional is set to true, then this will break upon encountering
231 * SEPERATESYM. This should only be used when parsing a line inside a
232 * conditional, otherwise just use the wrapper function skin_parse_line()
234 struct skin_element* skin_parse_line_optional(char** document, int conditional)
236 char* cursor = *document;
238 struct skin_element* root = NULL;
239 struct skin_element* current = NULL;
240 struct skin_element* retval = NULL;
242 /* A wrapper for the line */
243 retval = skin_alloc_element();
244 retval->type = LINE;
245 retval->line = skin_line;
246 retval->children_count = 1;
247 retval->children = skin_alloc_children(1);
249 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
250 && !((*cursor == ARGLISTSEPERATESYM
251 || *cursor == ARGLISTCLOSESYM
252 || *cursor == ENUMLISTSEPERATESYM
253 || *cursor == ENUMLISTCLOSESYM)
254 && conditional)
255 && !(check_viewport(cursor) && cursor != *document))
257 /* Allocating memory if necessary */
258 if(root)
260 current->next = skin_alloc_element();
261 current = current->next;
263 else
265 current = skin_alloc_element();
266 root = current;
269 /* Parsing the current element */
270 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
272 if(!skin_parse_conditional(current, &cursor))
273 return NULL;
275 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
277 if(!skin_parse_tag(current, &cursor))
278 return NULL;
280 else if(*cursor == COMMENTSYM)
282 if(!skin_parse_comment(current, &cursor))
283 return NULL;
285 else
287 if(!skin_parse_text(current, &cursor, conditional))
288 return NULL;
292 /* Moving up the calling function's pointer */
293 *document = cursor;
295 retval->children[0] = root;
296 return retval;
299 struct skin_element* skin_parse_sublines(char** document)
301 return skin_parse_sublines_optional(document, 0);
304 struct skin_element* skin_parse_sublines_optional(char** document,
305 int conditional)
307 struct skin_element* retval;
308 char* cursor = *document;
309 int sublines = 1;
310 int i;
312 retval = skin_alloc_element();
313 retval->type = SUBLINES;
314 retval->next = NULL;
315 retval->line = skin_line;
317 /* First we count the sublines */
318 while(*cursor != '\0' && *cursor != '\n'
319 && !((*cursor == ARGLISTSEPERATESYM
320 || *cursor == ARGLISTCLOSESYM
321 || *cursor == ENUMLISTSEPERATESYM
322 || *cursor == ENUMLISTCLOSESYM)
323 && conditional)
324 && !(check_viewport(cursor) && cursor != *document))
326 if(*cursor == COMMENTSYM)
328 skip_comment(&cursor);
330 else if(*cursor == ENUMLISTOPENSYM)
332 skip_enumlist(&cursor);
334 else if(*cursor == ARGLISTOPENSYM)
336 skip_arglist(&cursor);
338 else if(*cursor == TAGSYM)
340 cursor++;
341 if(*cursor == '\0' || *cursor == '\n')
342 break;
343 cursor++;
345 else if(*cursor == MULTILINESYM)
347 sublines++;
348 cursor++;
350 else
352 cursor++;
356 /* ...and then we parse them */
357 retval->children_count = sublines;
358 retval->children = skin_alloc_children(sublines);
360 cursor = *document;
361 for(i = 0; i < sublines; i++)
363 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
364 skip_whitespace(&cursor);
366 if(*cursor != MULTILINESYM && i != sublines - 1)
368 skin_error(MULTILINE_EXPECTED);
369 return NULL;
371 else if(i != sublines - 1)
373 cursor++;
377 *document = cursor;
379 return retval;
382 int skin_parse_tag(struct skin_element* element, char** document)
385 char* cursor = *document + 1;
386 char* bookmark;
388 char tag_name[3];
389 char* tag_args;
390 struct tag_info *tag;
392 int num_args = 1;
393 int i;
394 int star = 0; /* Flag for the all-or-none option */
395 int req_args; /* To mark when we enter optional arguments */
397 int optional = 0;
399 /* Checking the tag name */
400 tag_name[0] = cursor[0];
401 tag_name[1] = cursor[1];
402 tag_name[2] = '\0';
404 /* First we check the two characters after the '%', then a single char */
405 tag = find_tag(tag_name);
407 if(!tag)
409 tag_name[1] = '\0';
410 tag = find_tag(tag_name);
411 cursor++;
413 else
415 cursor += 2;
418 if(!tag)
420 skin_error(ILLEGAL_TAG);
421 return 0;
424 /* Copying basic tag info */
425 element->type = TAG;
426 element->tag = tag;
427 tag_args = tag->params;
428 element->line = skin_line;
430 /* Checking for the * flag */
431 if(tag_args[0] == '*')
433 star = 1;
434 tag_args++;
437 /* If this tag has no arguments, we can bail out now */
438 if(strlen(tag_args) == 0
439 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
440 || (star && *cursor != ARGLISTOPENSYM))
442 *document = cursor;
443 return 1;
446 /* Checking the number of arguments and allocating args */
447 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
449 skin_error(ARGLIST_EXPECTED);
450 return 0;
452 else
454 cursor++;
457 bookmark = cursor;
458 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
460 /* Skipping over escaped characters */
461 if(*cursor == TAGSYM)
463 cursor++;
464 if(*cursor == '\0')
465 break;
466 cursor++;
468 else if(*cursor == COMMENTSYM)
470 skip_comment(&cursor);
472 else if(*cursor == ARGLISTOPENSYM)
474 skip_arglist(&cursor);
476 else if(*cursor == ARGLISTSEPERATESYM)
478 num_args++;
479 cursor++;
481 else
483 cursor++;
487 cursor = bookmark; /* Restoring the cursor */
488 element->params_count = num_args;
489 element->params = skin_alloc_params(num_args);
491 /* Now we have to actually parse each argument */
492 for(i = 0; i < num_args; i++)
494 /* Making sure we haven't run out of arguments */
495 if(*tag_args == '\0')
497 skin_error(TOO_MANY_ARGS);
498 return 0;
501 /* Checking for the optional bar */
502 if(*tag_args == '|')
504 optional = 1;
505 req_args = i;
506 tag_args++;
509 /* Scanning the arguments */
510 skip_whitespace(&cursor);
513 /* Checking for comments */
514 if(*cursor == COMMENTSYM)
515 skip_comment(&cursor);
517 /* Storing the type code */
518 element->params[i].type_code = *tag_args;
520 /* Checking a nullable argument for null */
521 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
523 if(islower(*tag_args))
525 element->params[i].type = DEFAULT;
526 cursor++;
528 else
530 skin_error(DEFAULT_NOT_ALLOWED);
531 return 0;
534 else if(tolower(*tag_args) == 'i')
536 /* Scanning an int argument */
537 if(!isdigit(*cursor) && *cursor != '-')
539 skin_error(INT_EXPECTED);
540 return 0;
543 element->params[i].type = NUMERIC;
544 element->params[i].data.numeric = scan_int(&cursor);
546 else if(tolower(*tag_args) == 'n' ||
547 tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
549 /* Scanning a string argument */
550 element->params[i].type = STRING;
551 element->params[i].data.text = scan_string(&cursor);
554 else if(tolower(*tag_args) == 'c')
556 /* Recursively parsing a code argument */
557 element->params[i].type = CODE;
558 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
559 if(!element->params[i].data.code)
560 return 0;
563 skip_whitespace(&cursor);
565 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
567 skin_error(SEPERATOR_EXPECTED);
568 return 0;
570 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
572 skin_error(CLOSE_EXPECTED);
573 return 0;
575 else
577 cursor++;
580 if (*tag_args != 'N')
581 tag_args++;
583 /* Checking for the optional bar */
584 if(*tag_args == '|')
586 optional = 1;
587 req_args = i + 1;
588 tag_args++;
593 /* Checking for a premature end */
594 if(*tag_args != '\0' && !optional)
596 skin_error(INSUFFICIENT_ARGS);
597 return 0;
600 *document = cursor;
602 return 1;
606 * If the conditional flag is set true, then parsing text will stop at an
607 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
609 int skin_parse_text(struct skin_element* element, char** document,
610 int conditional)
612 char* cursor = *document;
614 int length = 0;
616 int dest;
618 /* First figure out how much text we're copying */
619 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
620 && *cursor != COMMENTSYM
621 && !((*cursor == ARGLISTSEPERATESYM
622 || *cursor == ARGLISTCLOSESYM
623 || *cursor == ENUMLISTSEPERATESYM
624 || *cursor == ENUMLISTCLOSESYM)
625 && conditional))
627 /* Dealing with possibility of escaped characters */
628 if(*cursor == TAGSYM)
630 if(find_escape_character(cursor[1]))
631 cursor++;
632 else
633 break;
636 length++;
637 cursor++;
640 cursor = *document;
642 /* Copying the text into the element struct */
643 element->type = TEXT;
644 element->line = skin_line;
645 element->next = NULL;
646 element->text = skin_alloc_string(length);
648 for(dest = 0; dest < length; dest++)
650 /* Advancing cursor if we've encountered an escaped character */
651 if(*cursor == TAGSYM)
652 cursor++;
654 element->text[dest] = *cursor;
655 cursor++;
657 element->text[length] = '\0';
659 *document = cursor;
661 return 1;
664 int skin_parse_conditional(struct skin_element* element, char** document)
667 char* cursor = *document + 1; /* Starting past the "%" */
668 char* bookmark;
669 struct skin_element* tag = skin_alloc_element(); /* The tag to evaluate */
670 int children = 1;
671 int i;
673 element->type = CONDITIONAL;
674 element->line = skin_line;
676 /* Parsing the tag first */
677 if(!skin_parse_tag(tag, &cursor))
678 return 0;
680 /* Counting the children */
681 if(*(cursor++) != ENUMLISTOPENSYM)
683 skin_error(ARGLIST_EXPECTED);
684 return 0;
686 bookmark = cursor;
687 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
689 if(*cursor == COMMENTSYM)
691 skip_comment(&cursor);
693 else if(*cursor == ENUMLISTOPENSYM)
695 skip_enumlist(&cursor);
697 else if(*cursor == TAGSYM)
699 cursor++;
700 if(*cursor == '\0' || *cursor == '\n')
701 break;
702 cursor++;
704 else if(*cursor == ENUMLISTSEPERATESYM)
706 children++;
707 cursor++;
709 else
711 cursor++;
714 cursor = bookmark;
716 /* Parsing the children */
717 element->children_count = children + 1; /* Make sure to include the tag */
718 element->children = skin_alloc_children(children + 1);
719 element->children[0] = tag;
721 for(i = 1; i < children + 1; i++)
723 element->children[i] = skin_parse_code_as_arg(&cursor);
724 skip_whitespace(&cursor);
726 if(i < children && *cursor != ENUMLISTSEPERATESYM)
728 skin_error(SEPERATOR_EXPECTED);
729 return 0;
731 else if(i == children && *cursor != ENUMLISTCLOSESYM)
733 skin_error(CLOSE_EXPECTED);
734 return 0;
736 else
738 cursor++;
742 *document = cursor;
744 return 1;
747 int skin_parse_comment(struct skin_element* element, char** document)
749 char* cursor = *document;
751 int length;
753 * Finding the index of the ending newline or null-terminator
754 * The length of the string of interest doesn't include the leading #, the
755 * length we need to reserve is the same as the index of the last character
757 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
759 element->type = COMMENT;
760 element->line = skin_line;
761 element->text = skin_alloc_string(length);
762 /* We copy from one char past cursor to leave out the # */
763 memcpy((void*)(element->text), (void*)(cursor + 1),
764 sizeof(char) * (length-1));
765 element->text[length - 1] = '\0';
767 if(cursor[length] == '\n')
768 skin_line++;
770 *document += (length); /* Move cursor up past # and all text */
771 if(**document == '\n')
772 (*document)++;
774 return 1;
777 struct skin_element* skin_parse_code_as_arg(char** document)
780 int sublines = 0;
781 char* cursor = *document;
783 /* Checking for sublines */
784 while(*cursor != '\n' && *cursor != '\0')
786 if(*cursor == MULTILINESYM)
788 sublines = 1;
789 break;
791 else if(*cursor == TAGSYM)
793 /* A ';' directly after a '%' doesn't count */
794 cursor ++;
796 if(*cursor == '\0')
797 break;
799 cursor++;
801 else
803 /* Advancing the cursor as normal */
804 cursor++;
808 if(sublines)
809 return skin_parse_sublines_optional(document, 1);
810 else
811 return skin_parse_line_optional(document, 1);
815 /* Memory management */
816 struct skin_element* skin_alloc_element()
819 #if 0
821 char* retval = &skin_parse_tree[skin_current_block * 4];
823 int delta = sizeof(struct skin_element) / (sizeof(char) * 4);
825 /* If one block is partially filled, make sure to advance to the
826 * next one for the next allocation
828 if(sizeof(struct skin_element) % (sizeof(char) * 4) != 0)
829 delta++;
831 skin_current_block += delta;
833 return (struct skin_element*)retval;
835 #endif
837 struct skin_element* retval = (struct skin_element*)
838 malloc(sizeof(struct skin_element));
839 retval->next = NULL;
840 retval->params_count = 0;
841 retval->children_count = 0;
843 return retval;
847 struct skin_tag_parameter* skin_alloc_params(int count)
849 #if 0
851 char* retval = &skin_parse_tree[skin_current_block * 4];
853 int delta = sizeof(struct skin_tag_parameter) / (sizeof(char) * 4);
854 delta *= count;
856 /* Correcting uneven alignment */
857 if(count * sizeof(struct skin_tag_parameter) % (sizeof(char) * 4) != 0)
858 delta++;
860 skin_current_block += delta;
862 return (struct skin_tag_parameter*) retval;
864 #endif
866 return (struct skin_tag_parameter*)malloc(sizeof(struct skin_tag_parameter)
867 * count);
871 char* skin_alloc_string(int length)
874 #if 0
875 char* retval = &skin_parse_tree[skin_current_block * 4];
877 /* Checking alignment */
878 length++; /* Accounting for the null terminator */
879 int delta = length / 4;
880 if(length % 4 != 0)
881 delta++;
883 skin_current_block += delta;
885 if(skin_current_block >= SKIN_MAX_MEMORY / 4)
886 skin_error(MEMORY_LIMIT_EXCEEDED);
888 return retval;
890 #endif
892 return (char*)malloc(sizeof(char) * (length + 1));
896 struct skin_element** skin_alloc_children(int count)
898 return (struct skin_element**) malloc(sizeof(struct skin_element*) * count);
901 void skin_free_tree(struct skin_element* root)
903 int i;
905 /* First make the recursive call */
906 if(!root)
907 return;
908 skin_free_tree(root->next);
910 /* Free any text */
911 if(root->type == TEXT)
912 free(root->text);
914 /* Then recursively free any children, before freeing their pointers */
915 for(i = 0; i < root->children_count; i++)
916 skin_free_tree(root->children[i]);
917 if(root->children_count > 0)
918 free(root->children);
920 /* Free any parameters, making sure to deallocate strings */
921 for(i = 0; i < root->params_count; i++)
922 if(root->params[i].type == STRING)
923 free(root->params[i].data.text);
924 if(root->params_count > 0)
925 free(root->params);
927 /* Finally, delete root's memory */
928 free(root);