Theme Editor: Added extern C declarations to header files
[kugel-rb.git] / utils / themeeditor / skin_parser.c
blob655c3d18a59ca5037c4f125f5511c8b50beca473
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_line(char** document);
42 struct skin_element* skin_parse_line_optional(char** document, int conditional);
43 struct skin_element* skin_parse_sublines(char** document);
44 struct skin_element* skin_parse_sublines_optional(char** document,
45 int conditional);
47 int skin_parse_tag(struct skin_element* element, char** document);
48 int skin_parse_text(struct skin_element* element, char** document,
49 int conditional);
50 int skin_parse_conditional(struct skin_element* element, char** document);
51 int skin_parse_newline(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(char* document)
58 struct skin_element* root = NULL;
59 struct skin_element* last = NULL;
61 struct skin_element** to_write = 0;
63 char* cursor = document; /* Keeps track of location in the document */
64 char* bookmark; /* Used when we need to look ahead */
66 int sublines = 0; /* Flag for parsing sublines */
68 skin_line = 1;
70 while(*cursor != '\0')
73 /* First, we check to see if this line will contain sublines */
74 bookmark = cursor;
75 sublines = 0;
76 while(*cursor != '\n' && *cursor != '\0')
78 if(*cursor == MULTILINESYM)
80 sublines = 1;
81 break;
83 else if(*cursor == TAGSYM)
85 /* A ';' directly after a '%' doesn't count */
86 cursor ++;
88 if(*cursor == '\0')
89 break;
91 cursor++;
93 else if(*cursor == COMMENTSYM)
95 skip_comment(&cursor);
97 else
99 /* Advancing the cursor as normal */
100 cursor++;
103 cursor = bookmark;
105 if(!root)
106 to_write = &root;
107 else
108 to_write = &(last->next);
110 if(*cursor == '\n')
112 *to_write = skin_alloc_element();
113 skin_parse_newline(*to_write, &cursor);
114 if(!last)
115 return NULL;
117 else if(sublines)
119 *to_write = skin_parse_sublines(&cursor);
120 last = *to_write;
121 if(!last)
122 return NULL;
124 else
127 *to_write = skin_parse_line(&cursor);
128 last = *to_write;
129 if(!last)
130 return NULL;
134 /* Making sure last is at the end */
135 while(last->next)
136 last = last->next;
140 return root;
144 /* Auxiliary Parsing Functions */
146 struct skin_element* skin_parse_line(char**document)
149 return skin_parse_line_optional(document, 0);
155 * If conditional is set to true, then this will break upon encountering
156 * SEPERATESYM. This should only be used when parsing a line inside a
157 * conditional, otherwise just use the wrapper function skin_parse_line()
159 struct skin_element* skin_parse_line_optional(char** document, int conditional)
161 char* cursor = *document;
163 struct skin_element* root = NULL;
164 struct skin_element* current = NULL;
165 struct skin_element* retval = NULL;
167 /* A wrapper for the line */
168 retval = skin_alloc_element();
169 retval->type = LINE;
170 retval->line = skin_line;
171 retval->children_count = 1;
172 retval->children = skin_alloc_children(1);
174 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
175 && !((*cursor == ARGLISTSEPERATESYM
176 || *cursor == ARGLISTCLOSESYM
177 || *cursor == ENUMLISTSEPERATESYM
178 || *cursor == ENUMLISTCLOSESYM)
179 && conditional))
181 /* Allocating memory if necessary */
182 if(root)
184 current->next = skin_alloc_element();
185 current = current->next;
187 else
189 current = skin_alloc_element();
190 root = current;
193 /* Parsing the current element */
194 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
196 if(!skin_parse_conditional(current, &cursor))
197 return NULL;
199 else if(*cursor == TAGSYM)
201 if(!skin_parse_tag(current, &cursor))
202 return NULL;
204 else if(*cursor == COMMENTSYM)
206 if(!skin_parse_comment(current, &cursor))
207 return NULL;
209 else
211 if(!skin_parse_text(current, &cursor, conditional))
212 return NULL;
216 /* Moving up the calling function's pointer */
217 *document = cursor;
219 retval->children[0] = root;
220 return retval;
223 struct skin_element* skin_parse_sublines(char** document)
225 return skin_parse_sublines_optional(document, 0);
228 struct skin_element* skin_parse_sublines_optional(char** document,
229 int conditional)
231 struct skin_element* retval;
232 char* cursor = *document;
233 int sublines = 1;
234 int i;
236 retval = skin_alloc_element();
237 retval->type = SUBLINES;
238 retval->next = NULL;
239 retval->line = skin_line;
241 /* First we count the sublines */
242 while(*cursor != '\0' && *cursor != '\n'
243 && !((*cursor == ARGLISTSEPERATESYM
244 || *cursor == ARGLISTCLOSESYM
245 || *cursor == ENUMLISTSEPERATESYM
246 || *cursor == ENUMLISTCLOSESYM)
247 && conditional))
249 if(*cursor == COMMENTSYM)
250 skip_comment(&cursor);
252 /* Accounting for escaped subline symbols */
253 if(*cursor == TAGSYM)
255 cursor++;
256 if(*cursor == '\0' || *cursor == '\n')
257 break;
260 if(*cursor == MULTILINESYM)
262 sublines++;
265 cursor++;
268 /* ...and then we parse them */
269 retval->children_count = sublines;
270 retval->children = skin_alloc_children(sublines);
272 cursor = *document;
273 for(i = 0; i < sublines; i++)
275 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
276 skip_whitespace(&cursor);
278 if(*cursor != MULTILINESYM && i != sublines - 1)
280 skin_error(MULTILINE_EXPECTED);
281 return NULL;
283 else if(i != sublines - 1)
285 cursor++;
289 *document = cursor;
291 return retval;
294 int skin_parse_tag(struct skin_element* element, char** document)
297 char* cursor = *document + 1;
298 char* bookmark;
300 char tag_name[3];
301 char* tag_args;
303 int num_args = 1;
304 int i;
305 int star = 0; /* Flag for the all-or-none option */
306 int req_args; /* To mark when we enter optional arguments */
308 int optional = 0;
310 /* Checking the tag name */
311 tag_name[0] = cursor[0];
312 tag_name[1] = cursor[1];
313 tag_name[2] = '\0';
315 /* First we check the two characters after the '%', then a single char */
316 tag_args = find_tag(tag_name);
318 if(!tag_args)
320 tag_name[1] = '\0';
321 tag_args = find_tag(tag_name);
322 cursor++;
324 else
326 cursor += 2;
329 if(!tag_args)
331 skin_error(ILLEGAL_TAG);
332 return 0;
335 /* Copying basic tag info */
336 element->type = TAG;
337 strcpy(element->name, tag_name);
338 element->line = skin_line;
340 /* Checking for the * flag */
341 if(tag_args[0] == '*')
343 star = 1;
344 tag_args++;
347 /* If this tag has no arguments, we can bail out now */
348 if(strlen(tag_args) == 0
349 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM))
351 *document = cursor;
352 return 1;
355 /* Checking the number of arguments and allocating args */
356 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
358 skin_error(ARGLIST_EXPECTED);
359 return 0;
361 else
363 cursor++;
366 for(bookmark = cursor; *cursor != '\n' && *cursor != '\0' &&
367 *cursor != ARGLISTCLOSESYM; cursor++)
369 /* Skipping over escaped characters */
370 if(*cursor == TAGSYM)
372 cursor++;
373 if(*cursor == '\0')
374 break;
377 /* Skipping comments */
378 if(*cursor == COMMENTSYM)
379 skip_comment(&cursor);
381 /* Commas inside of contained lists don't count */
382 if(*cursor == ARGLISTOPENSYM)
383 while(*cursor != ARGLISTCLOSESYM && *cursor != '\0')
384 cursor++;
386 if(*cursor == ARGLISTSEPERATESYM)
387 num_args++;
390 cursor = bookmark; /* Restoring the cursor */
391 element->params_count = num_args;
392 element->params = skin_alloc_params(num_args);
394 /* Now we have to actually parse each argument */
395 for(i = 0; i < num_args; i++)
397 /* Making sure we haven't run out of arguments */
398 if(*tag_args == '\0')
400 skin_error(TOO_MANY_ARGS);
401 return 0;
404 /* Checking for the optional bar */
405 if(*tag_args == '|')
407 optional = 1;
408 req_args = i - 1;
409 tag_args++;
412 /* Scanning the arguments */
413 skip_whitespace(&cursor);
416 /* Checking for comments */
417 if(*cursor == COMMENTSYM)
418 skip_comment(&cursor);
420 /* Checking a nullable argument for null */
421 if(*cursor == DEFAULTSYM)
423 if(islower(*tag_args))
425 element->params[i].type = DEFAULT;
426 cursor++;
428 else
430 skin_error(DEFAULT_NOT_ALLOWED);
431 return 0;
434 else if(tolower(*tag_args) == 'i')
436 /* Scanning an int argument */
437 if(!isdigit(*cursor))
439 skin_error(INT_EXPECTED);
440 return 0;
443 element->params[i].type = NUMERIC;
444 element->params[i].data.numeric = scan_int(&cursor);
446 else if(tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
448 /* Scanning a string argument */
449 element->params[i].type = STRING;
450 element->params[i].data.text = scan_string(&cursor);
453 else if(tolower(*tag_args) == 'c')
455 /* Recursively parsing a code argument */
456 element->params[i].type = CODE;
457 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
458 if(!element->params[i].data.code)
459 return 0;
462 skip_whitespace(&cursor);
464 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
466 skin_error(SEPERATOR_EXPECTED);
467 return 0;
469 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
471 skin_error(CLOSE_EXPECTED);
472 return 0;
474 else
476 cursor++;
479 tag_args++;
483 /* Checking for a premature end */
484 if(*tag_args != '\0' && !(optional && (!star || num_args == req_args)))
486 skin_error(INSUFFICIENT_ARGS);
487 return 0;
490 *document = cursor;
492 return 1;
496 * If the conditional flag is set true, then parsing text will stop at an
497 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
499 int skin_parse_text(struct skin_element* element, char** document,
500 int conditional)
502 char* cursor = *document;
504 int length = 0;
506 int dest;
508 /* First figure out how much text we're copying */
509 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
510 && *cursor != COMMENTSYM
511 && !((*cursor == ARGLISTSEPERATESYM
512 || *cursor == ARGLISTCLOSESYM
513 || *cursor == ENUMLISTSEPERATESYM
514 || *cursor == ENUMLISTCLOSESYM)
515 && conditional))
517 /* Dealing with possibility of escaped characters */
518 if(*cursor == TAGSYM)
520 if(find_escape_character(cursor[1]))
521 cursor++;
522 else
523 break;
526 length++;
527 cursor++;
530 cursor = *document;
532 /* Copying the text into the element struct */
533 element->type = TEXT;
534 element->line = skin_line;
535 element->next = NULL;
536 element->text = skin_alloc_string(length);
538 for(dest = 0; dest < length; dest++)
540 /* Advancing cursor if we've encountered an escaped character */
541 if(*cursor == TAGSYM)
542 cursor++;
544 element->text[dest] = *cursor;
545 cursor++;
547 element->text[length] = '\0';
549 *document = cursor;
551 return 1;
554 int skin_parse_conditional(struct skin_element* element, char** document)
557 char* cursor = *document + 1; /* Starting past the "%" */
558 char* bookmark;
559 struct skin_element* tag = skin_alloc_element(); /* The tag to evaluate */
560 int children = 1;
561 int i;
563 element->type = CONDITIONAL;
564 element->line = skin_line;
566 /* Parsing the tag first */
567 if(!skin_parse_tag(tag, &cursor))
568 return 0;
570 /* Counting the children */
571 if(*(cursor++) != ENUMLISTOPENSYM)
573 skin_error(ARGLIST_EXPECTED);
574 return 0;
576 bookmark = cursor;
577 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
579 if(*cursor == COMMENTSYM)
581 skip_comment(&cursor);
582 continue;
585 if(*cursor == TAGSYM)
587 cursor++;
588 if(*cursor == '\0' || *cursor == '\n')
589 break;
590 cursor++;
591 continue;
594 if(*cursor == ENUMLISTSEPERATESYM)
595 children++;
597 cursor++;
599 cursor = bookmark;
601 /* Parsing the children */
602 element->children_count = children + 1; /* Make sure to include the tag */
603 element->children = skin_alloc_children(children + 1);
604 element->children[0] = tag;
606 for(i = 1; i < children + 1; i++)
608 element->children[i] = skin_parse_code_as_arg(&cursor);
609 skip_whitespace(&cursor);
611 if(i < children && *cursor != ENUMLISTSEPERATESYM)
613 skin_error(SEPERATOR_EXPECTED);
614 return 0;
616 else if(i == children && *cursor != ENUMLISTCLOSESYM)
618 skin_error(CLOSE_EXPECTED);
619 return 0;
621 else
623 cursor++;
627 *document = cursor;
629 return 1;
632 int skin_parse_newline(struct skin_element* element, char** document)
635 char* cursor = *document;
636 if(*cursor != '\n')
638 skin_error(NEWLINE_EXPECTED);
639 return 0;
641 cursor++;
643 /* Assembling a skin_element struct for a newline */
644 element->type = NEWLINE;
645 element->line = skin_line;
646 skin_line++;
647 element->next = NULL;
649 *document = cursor;
651 return 1;
654 int skin_parse_comment(struct skin_element* element, char** document)
656 char* cursor = *document;
658 int length;
660 * Finding the index of the ending newline or null-terminator
661 * The length of the string of interest doesn't include the leading #, the
662 * length we need to reserve is the same as the index of the last character
664 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
666 element->type = COMMENT;
667 element->line = skin_line;
668 element->text = skin_alloc_string(length);
669 /* We copy from one char past cursor to leave out the # */
670 memcpy((void*)(element->text), (void*)(cursor + 1), sizeof(char) * length);
671 element->text[length] = '\0';
673 if(cursor[length] == '\n')
674 skin_line++;
676 *document += (length + 1); /* Move cursor up past # and all text */
678 return 1;
681 struct skin_element* skin_parse_code_as_arg(char** document)
684 int sublines = 0;
685 char* cursor = *document;
687 /* Checking for sublines */
688 while(*cursor != '\n' && *cursor != '\0')
690 if(*cursor == MULTILINESYM)
692 sublines = 1;
693 break;
695 else if(*cursor == TAGSYM)
697 /* A ';' directly after a '%' doesn't count */
698 cursor ++;
700 if(*cursor == '\0')
701 break;
703 cursor++;
705 else
707 /* Advancing the cursor as normal */
708 cursor++;
712 if(sublines)
713 return skin_parse_sublines_optional(document, 1);
714 else
715 return skin_parse_line_optional(document, 1);
719 /* Memory management */
720 struct skin_element* skin_alloc_element()
723 #if 0
725 char* retval = &skin_parse_tree[skin_current_block * 4];
727 int delta = sizeof(struct skin_element) / (sizeof(char) * 4);
729 /* If one block is partially filled, make sure to advance to the
730 * next one for the next allocation
732 if(sizeof(struct skin_element) % (sizeof(char) * 4) != 0)
733 delta++;
735 skin_current_block += delta;
737 return (struct skin_element*)retval;
739 #endif
741 struct skin_element* retval = (struct skin_element*)
742 malloc(sizeof(struct skin_element));
743 retval->next = NULL;
744 retval->params_count = 0;
745 retval->children_count = 0;
747 return retval;
751 struct skin_tag_parameter* skin_alloc_params(int count)
753 #if 0
755 char* retval = &skin_parse_tree[skin_current_block * 4];
757 int delta = sizeof(struct skin_tag_parameter) / (sizeof(char) * 4);
758 delta *= count;
760 /* Correcting uneven alignment */
761 if(count * sizeof(struct skin_tag_parameter) % (sizeof(char) * 4) != 0)
762 delta++;
764 skin_current_block += delta;
766 return (struct skin_tag_parameter*) retval;
768 #endif
770 return (struct skin_tag_parameter*)malloc(sizeof(struct skin_tag_parameter)
771 * count);
775 char* skin_alloc_string(int length)
778 #if 0
779 char* retval = &skin_parse_tree[skin_current_block * 4];
781 /* Checking alignment */
782 length++; /* Accounting for the null terminator */
783 int delta = length / 4;
784 if(length % 4 != 0)
785 delta++;
787 skin_current_block += delta;
789 if(skin_current_block >= SKIN_MAX_MEMORY / 4)
790 skin_error(MEMORY_LIMIT_EXCEEDED);
792 return retval;
794 #endif
796 return (char*)malloc(sizeof(char) * (length + 1));
800 struct skin_element** skin_alloc_children(int count)
802 return (struct skin_element**) malloc(sizeof(struct skin_element*) * count);
805 void skin_free_tree(struct skin_element* root)
807 int i;
809 /* First make the recursive call */
810 if(!root)
811 return;
812 skin_free_tree(root->next);
814 /* Free any text */
815 if(root->type == TEXT)
816 free(root->text);
818 /* Then recursively free any children, before freeing their pointers */
819 for(i = 0; i < root->children_count; i++)
820 skin_free_tree(root->children[i]);
821 if(root->children_count > 0)
822 free(root->children);
824 /* Free any parameters, making sure to deallocate strings */
825 for(i = 0; i < root->params_count; i++)
826 if(root->params[i].type == STRING)
827 free(root->params[i].data.text);
828 if(root->params_count > 0)
829 free(root->params);
831 /* Finally, delete root's memory */
832 free(root);