tiny clean up of memory allocation
[kugel-rb.git] / lib / skin_parser / skin_parser.c
blob9c360231e1d598079c466c742aaa797a51499f3e
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_buffer.h"
28 #include "skin_parser.h"
29 #include "skin_debug.h"
30 #include "tag_table.h"
31 #include "symbols.h"
32 #include "skin_scan.h"
34 /* Global variables for the parser */
35 int skin_line = 0;
37 /* Auxiliary parsing functions (not visible at global scope) */
38 static struct skin_element* skin_parse_viewport(char** document);
39 static struct skin_element* skin_parse_line(char** document);
40 static struct skin_element* skin_parse_line_optional(char** document,
41 int conditional);
42 static struct skin_element* skin_parse_sublines(char** document);
43 static struct skin_element* skin_parse_sublines_optional(char** document,
44 int conditional);
46 static int skin_parse_tag(struct skin_element* element, char** document);
47 static int skin_parse_text(struct skin_element* element, char** document,
48 int conditional);
49 static int skin_parse_conditional(struct skin_element* element,
50 char** document);
51 static int skin_parse_comment(struct skin_element* element, char** document);
52 static struct skin_element* skin_parse_code_as_arg(char** document);
56 struct skin_element* skin_parse(const char* document)
59 struct skin_element* root = NULL;
60 struct skin_element* last = NULL;
62 struct skin_element** to_write = 0;
64 char* cursor = (char*)document; /*Keeps track of location in the document*/
66 skin_line = 1;
68 skin_clear_errors();
70 while(*cursor != '\0')
73 if(!root)
74 to_write = &root;
75 else
76 to_write = &(last->next);
79 *to_write = skin_parse_viewport(&cursor);
80 last = *to_write;
81 if(!last)
83 skin_free_tree(root); /* Clearing any memory already used */
84 return NULL;
87 /* Making sure last is at the end */
88 while(last->next)
89 last = last->next;
93 return root;
97 static struct skin_element* skin_parse_viewport(char** document)
100 struct skin_element* root = NULL;
101 struct skin_element* last = NULL;
102 struct skin_element* retval = NULL;
104 retval = skin_alloc_element();
105 retval->type = VIEWPORT;
106 retval->children_count = 1;
107 retval->line = skin_line;
109 struct skin_element** to_write = 0;
111 char* cursor = *document; /* Keeps track of location in the document */
112 char* bookmark; /* Used when we need to look ahead */
114 int sublines = 0; /* Flag for parsing sublines */
116 /* Parsing out the viewport tag if there is one */
117 if(check_viewport(cursor))
119 skin_parse_tag(retval, &cursor);
120 if(*cursor == '\n')
122 cursor++;
123 skin_line++;
127 retval->children_count = 1;
128 retval->children = skin_alloc_children(1);
134 /* First, we check to see if this line will contain sublines */
135 bookmark = cursor;
136 sublines = 0;
137 while(*cursor != '\n' && *cursor != '\0'
138 && !(check_viewport(cursor) && cursor != *document))
140 if(*cursor == MULTILINESYM)
142 sublines = 1;
143 break;
145 else if(*cursor == TAGSYM)
147 /* A ';' directly after a '%' doesn't count */
148 cursor ++;
150 if(*cursor == '\0')
151 break;
153 cursor++;
155 else if(*cursor == COMMENTSYM)
157 skip_comment(&cursor);
159 else if(*cursor == ARGLISTOPENSYM)
161 skip_arglist(&cursor);
163 else if(*cursor == ENUMLISTOPENSYM)
165 skip_enumlist(&cursor);
167 else
169 /* Advancing the cursor as normal */
170 cursor++;
173 cursor = bookmark;
175 if(!root)
176 to_write = &root;
177 else
178 to_write = &(last->next);
180 if(sublines)
182 *to_write = skin_parse_sublines(&cursor);
183 last = *to_write;
184 if(!last)
185 return NULL;
187 else
190 *to_write = skin_parse_line(&cursor);
191 last = *to_write;
192 if(!last)
193 return NULL;
197 /* Making sure last is at the end */
198 while(last->next)
199 last = last->next;
201 if(*cursor == '\n')
203 cursor++;
204 skin_line++;
207 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
209 *document = cursor;
211 retval->children[0] = root;
212 return retval;
216 /* Auxiliary Parsing Functions */
218 static struct skin_element* skin_parse_line(char**document)
221 return skin_parse_line_optional(document, 0);
227 * If conditional is set to true, then this will break upon encountering
228 * SEPERATESYM. This should only be used when parsing a line inside a
229 * conditional, otherwise just use the wrapper function skin_parse_line()
231 static struct skin_element* skin_parse_line_optional(char** document,
232 int conditional)
234 char* cursor = *document;
236 struct skin_element* root = NULL;
237 struct skin_element* current = NULL;
238 struct skin_element* retval = NULL;
240 /* A wrapper for the line */
241 retval = skin_alloc_element();
242 retval->type = LINE;
243 retval->line = skin_line;
244 if(*cursor != '\0' && *cursor != '\n'
245 && !(conditional && (*cursor == ARGLISTSEPERATESYM
246 || *cursor == ARGLISTCLOSESYM
247 || *cursor == ENUMLISTSEPERATESYM
248 || *cursor == ENUMLISTCLOSESYM)))
250 retval->children_count = 1;
252 else
254 retval->children_count = 0;
257 if(retval->children_count > 0)
258 retval->children = skin_alloc_children(1);
260 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
261 && !((*cursor == ARGLISTSEPERATESYM
262 || *cursor == ARGLISTCLOSESYM
263 || *cursor == ENUMLISTSEPERATESYM
264 || *cursor == ENUMLISTCLOSESYM)
265 && conditional)
266 && !(check_viewport(cursor) && cursor != *document))
268 /* Allocating memory if necessary */
269 if(root)
271 current->next = skin_alloc_element();
272 current = current->next;
274 else
276 current = skin_alloc_element();
277 root = current;
280 /* Parsing the current element */
281 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
283 if(!skin_parse_conditional(current, &cursor))
284 return NULL;
286 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
288 if(!skin_parse_tag(current, &cursor))
289 return NULL;
291 else if(*cursor == COMMENTSYM)
293 if(!skin_parse_comment(current, &cursor))
294 return NULL;
296 else
298 if(!skin_parse_text(current, &cursor, conditional))
299 return NULL;
303 /* Moving up the calling function's pointer */
304 *document = cursor;
306 if(root)
307 retval->children[0] = root;
308 return retval;
311 static struct skin_element* skin_parse_sublines(char** document)
313 return skin_parse_sublines_optional(document, 0);
316 static struct skin_element* skin_parse_sublines_optional(char** document,
317 int conditional)
319 struct skin_element* retval;
320 char* cursor = *document;
321 int sublines = 1;
322 int i;
324 retval = skin_alloc_element();
325 retval->type = SUBLINES;
326 retval->next = NULL;
327 retval->line = skin_line;
329 /* First we count the sublines */
330 while(*cursor != '\0' && *cursor != '\n'
331 && !((*cursor == ARGLISTSEPERATESYM
332 || *cursor == ARGLISTCLOSESYM
333 || *cursor == ENUMLISTSEPERATESYM
334 || *cursor == ENUMLISTCLOSESYM)
335 && conditional)
336 && !(check_viewport(cursor) && cursor != *document))
338 if(*cursor == COMMENTSYM)
340 skip_comment(&cursor);
342 else if(*cursor == ENUMLISTOPENSYM)
344 skip_enumlist(&cursor);
346 else if(*cursor == ARGLISTOPENSYM)
348 skip_arglist(&cursor);
350 else if(*cursor == TAGSYM)
352 cursor++;
353 if(*cursor == '\0' || *cursor == '\n')
354 break;
355 cursor++;
357 else if(*cursor == MULTILINESYM)
359 sublines++;
360 cursor++;
362 else
364 cursor++;
368 /* ...and then we parse them */
369 retval->children_count = sublines;
370 retval->children = skin_alloc_children(sublines);
372 cursor = *document;
373 for(i = 0; i < sublines; i++)
375 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
376 skip_whitespace(&cursor);
378 if(*cursor != MULTILINESYM && i != sublines - 1)
380 skin_error(MULTILINE_EXPECTED);
381 return NULL;
383 else if(i != sublines - 1)
385 cursor++;
389 *document = cursor;
391 return retval;
394 static int skin_parse_tag(struct skin_element* element, char** document)
397 char* cursor = *document + 1;
398 char* bookmark;
400 char tag_name[3];
401 char* tag_args;
402 struct tag_info *tag;
404 int num_args = 1;
405 int i;
406 int star = 0; /* Flag for the all-or-none option */
407 int req_args; /* To mark when we enter optional arguments */
409 int optional = 0;
411 /* Checking the tag name */
412 tag_name[0] = cursor[0];
413 tag_name[1] = cursor[1];
414 tag_name[2] = '\0';
416 /* First we check the two characters after the '%', then a single char */
417 tag = find_tag(tag_name);
419 if(!tag)
421 tag_name[1] = '\0';
422 tag = find_tag(tag_name);
423 cursor++;
425 else
427 cursor += 2;
430 if(!tag)
432 skin_error(ILLEGAL_TAG);
433 return 0;
436 /* Copying basic tag info */
437 if(element->type != CONDITIONAL && element->type != VIEWPORT)
438 element->type = TAG;
439 element->tag = tag;
440 tag_args = tag->params;
441 element->line = skin_line;
443 /* Checking for the * flag */
444 if(tag_args[0] == '*')
446 star = 1;
447 tag_args++;
450 /* If this tag has no arguments, we can bail out now */
451 if(strlen(tag_args) == 0
452 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
453 || (star && *cursor != ARGLISTOPENSYM))
455 *document = cursor;
456 return 1;
459 /* Checking the number of arguments and allocating args */
460 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
462 skin_error(ARGLIST_EXPECTED);
463 return 0;
465 else
467 cursor++;
470 bookmark = cursor;
471 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
473 /* Skipping over escaped characters */
474 if(*cursor == TAGSYM)
476 cursor++;
477 if(*cursor == '\0')
478 break;
479 cursor++;
481 else if(*cursor == COMMENTSYM)
483 skip_comment(&cursor);
485 else if(*cursor == ARGLISTOPENSYM)
487 skip_arglist(&cursor);
489 else if(*cursor == ARGLISTSEPERATESYM)
491 num_args++;
492 cursor++;
494 else
496 cursor++;
500 cursor = bookmark; /* Restoring the cursor */
501 element->params_count = num_args;
502 element->params = skin_alloc_params(num_args);
504 /* Now we have to actually parse each argument */
505 for(i = 0; i < num_args; i++)
507 /* Making sure we haven't run out of arguments */
508 if(*tag_args == '\0')
510 skin_error(TOO_MANY_ARGS);
511 return 0;
514 /* Checking for the optional bar */
515 if(*tag_args == '|')
517 optional = 1;
518 req_args = i;
519 tag_args++;
522 /* Scanning the arguments */
523 skip_whitespace(&cursor);
526 /* Checking for comments */
527 if(*cursor == COMMENTSYM)
528 skip_comment(&cursor);
530 /* Storing the type code */
531 element->params[i].type_code = *tag_args;
533 /* Checking a nullable argument for null */
534 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
536 if(islower(*tag_args))
538 element->params[i].type = DEFAULT;
539 cursor++;
541 else
543 skin_error(DEFAULT_NOT_ALLOWED);
544 return 0;
547 else if(tolower(*tag_args) == 'i')
549 /* Scanning an int argument */
550 if(!isdigit(*cursor) && *cursor != '-')
552 skin_error(INT_EXPECTED);
553 return 0;
556 element->params[i].type = NUMERIC;
557 element->params[i].data.numeric = scan_int(&cursor);
559 else if(tolower(*tag_args) == 'n' ||
560 tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
562 /* Scanning a string argument */
563 element->params[i].type = STRING;
564 element->params[i].data.text = scan_string(&cursor);
567 else if(tolower(*tag_args) == 'c')
569 /* Recursively parsing a code argument */
570 element->params[i].type = CODE;
571 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
572 if(!element->params[i].data.code)
573 return 0;
576 skip_whitespace(&cursor);
578 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
580 skin_error(SEPERATOR_EXPECTED);
581 return 0;
583 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
585 skin_error(CLOSE_EXPECTED);
586 return 0;
588 else
590 cursor++;
593 if (*tag_args != 'N')
594 tag_args++;
596 /* Checking for the optional bar */
597 if(*tag_args == '|')
599 optional = 1;
600 req_args = i + 1;
601 tag_args++;
606 /* Checking for a premature end */
607 if(*tag_args != '\0' && !optional)
609 skin_error(INSUFFICIENT_ARGS);
610 return 0;
613 *document = cursor;
615 return 1;
619 * If the conditional flag is set true, then parsing text will stop at an
620 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
622 static int skin_parse_text(struct skin_element* element, char** document,
623 int conditional)
625 char* cursor = *document;
626 int length = 0;
627 int dest;
628 char *text = NULL;
630 /* First figure out how much text we're copying */
631 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
632 && *cursor != COMMENTSYM
633 && !((*cursor == ARGLISTSEPERATESYM
634 || *cursor == ARGLISTCLOSESYM
635 || *cursor == ENUMLISTSEPERATESYM
636 || *cursor == ENUMLISTCLOSESYM)
637 && conditional))
639 /* Dealing with possibility of escaped characters */
640 if(*cursor == TAGSYM)
642 if(find_escape_character(cursor[1]))
643 cursor++;
644 else
645 break;
648 length++;
649 cursor++;
652 cursor = *document;
654 /* Copying the text into the element struct */
655 element->type = TEXT;
656 element->line = skin_line;
657 element->next = NULL;
658 element->data = text = skin_alloc_string(length);
660 for(dest = 0; dest < length; dest++)
662 /* Advancing cursor if we've encountered an escaped character */
663 if(*cursor == TAGSYM)
664 cursor++;
666 text[dest] = *cursor;
667 cursor++;
669 text[length] = '\0';
671 *document = cursor;
673 return 1;
676 static int skin_parse_conditional(struct skin_element* element, char** document)
679 char* cursor = *document + 1; /* Starting past the "%" */
680 char* bookmark;
681 int children = 1;
682 int i;
684 element->type = CONDITIONAL;
685 element->line = skin_line;
687 /* Parsing the tag first */
688 if(!skin_parse_tag(element, &cursor))
689 return 0;
691 /* Counting the children */
692 if(*(cursor++) != ENUMLISTOPENSYM)
694 skin_error(ARGLIST_EXPECTED);
695 return 0;
697 bookmark = cursor;
698 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
700 if(*cursor == COMMENTSYM)
702 skip_comment(&cursor);
704 else if(*cursor == ENUMLISTOPENSYM)
706 skip_enumlist(&cursor);
708 else if(*cursor == TAGSYM)
710 cursor++;
711 if(*cursor == '\0' || *cursor == '\n')
712 break;
713 cursor++;
715 else if(*cursor == ENUMLISTSEPERATESYM)
717 children++;
718 cursor++;
720 else
722 cursor++;
725 cursor = bookmark;
727 /* Parsing the children */
728 element->children = skin_alloc_children(children);
729 element->children_count = children;
731 for(i = 0; i < children; i++)
733 element->children[i] = skin_parse_code_as_arg(&cursor);
734 skip_whitespace(&cursor);
736 if(i < children - 1 && *cursor != ENUMLISTSEPERATESYM)
738 skin_error(SEPERATOR_EXPECTED);
739 return 0;
741 else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
743 skin_error(CLOSE_EXPECTED);
744 return 0;
746 else
748 cursor++;
752 *document = cursor;
754 return 1;
757 static int skin_parse_comment(struct skin_element* element, char** document)
759 char* cursor = *document;
760 #ifndef ROCKBOX
761 char* text = NULL;
762 #endif
763 int length;
765 * Finding the index of the ending newline or null-terminator
766 * The length of the string of interest doesn't include the leading #, the
767 * length we need to reserve is the same as the index of the last character
769 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
771 element->type = COMMENT;
772 element->line = skin_line;
773 #ifdef ROCKBOX
774 element->data = NULL;
775 #else
776 element->data = text = skin_alloc_string(length);
777 /* We copy from one char past cursor to leave out the # */
778 memcpy((void*)text, (void*)(cursor + 1),
779 sizeof(char) * (length-1));
780 text[length - 1] = '\0';
781 #endif
782 if(cursor[length] == '\n')
783 skin_line++;
785 *document += (length); /* Move cursor up past # and all text */
786 if(**document == '\n')
787 (*document)++;
789 return 1;
792 static struct skin_element* skin_parse_code_as_arg(char** document)
795 int sublines = 0;
796 char* cursor = *document;
798 /* Checking for sublines */
799 while(*cursor != '\n' && *cursor != '\0'
800 && *cursor != ENUMLISTSEPERATESYM && *cursor != ARGLISTSEPERATESYM
801 && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
803 if(*cursor == MULTILINESYM)
805 sublines = 1;
806 break;
808 else if(*cursor == TAGSYM)
810 /* A ';' directly after a '%' doesn't count */
811 cursor ++;
813 if(*cursor == '\0')
814 break;
816 cursor++;
818 else if(*cursor == ARGLISTOPENSYM)
820 skip_arglist(&cursor);
822 else if(*cursor == ENUMLISTOPENSYM)
824 skip_enumlist(&cursor);
826 else
828 /* Advancing the cursor as normal */
829 cursor++;
833 if(sublines)
834 return skin_parse_sublines_optional(document, 1);
835 else
836 return skin_parse_line_optional(document, 1);
840 /* Memory management */
841 struct skin_element* skin_alloc_element()
843 struct skin_element* retval = (struct skin_element*)
844 skin_buffer_alloc(sizeof(struct skin_element));
845 retval->type = UNKNOWN;
846 retval->next = NULL;
847 retval->tag = NULL;
848 retval->params_count = 0;
849 retval->children_count = 0;
851 return retval;
855 struct skin_tag_parameter* skin_alloc_params(int count)
857 size_t size = sizeof(struct skin_tag_parameter) * count;
858 return (struct skin_tag_parameter*)skin_buffer_alloc(size);
862 char* skin_alloc_string(int length)
864 return (char*)skin_buffer_alloc(sizeof(char) * (length + 1));
867 struct skin_element** skin_alloc_children(int count)
869 return (struct skin_element**)
870 skin_buffer_alloc(sizeof(struct skin_element*) * count);
873 void skin_free_tree(struct skin_element* root)
875 #ifndef ROCKBOX
876 int i;
878 /* First make the recursive call */
879 if(!root)
880 return;
881 skin_free_tree(root->next);
883 /* Free any text */
884 if(root->type == TEXT || root->type == COMMENT)
885 free(root->data);
887 /* Then recursively free any children, before freeing their pointers */
888 for(i = 0; i < root->children_count; i++)
889 skin_free_tree(root->children[i]);
890 if(root->children_count > 0)
891 free(root->children);
893 /* Free any parameters, making sure to deallocate strings */
894 for(i = 0; i < root->params_count; i++)
895 if(root->params[i].type == STRING)
896 free(root->params[i].data.text);
897 if(root->params_count > 0)
898 free(root->params);
900 /* Finally, delete root's memory */
901 free(root);
902 #else
903 (void)root;
904 #endif