Correct spelling of 'seperate' in the skin parsing code - FS#11724 by Alexander Levin
[kugel-rb.git] / lib / skin_parser / skin_parser.c
blob5a5d746c8ed9b4d11f93bf6b9601dddb527ae433
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 <stdbool.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <stdbool.h>
29 #include "skin_buffer.h"
30 #include "skin_parser.h"
31 #include "skin_debug.h"
32 #include "tag_table.h"
33 #include "symbols.h"
34 #include "skin_scan.h"
36 /* Global variables for the parser */
37 int skin_line = 0;
38 char* skin_start = 0;
39 int viewport_line = 0;
41 static int tag_recursion_level = 0;
43 #ifdef ROCKBOX
44 static skin_callback callback = NULL;
45 static void* callback_data;
46 #endif
48 /* Auxiliary parsing functions (not visible at global scope) */
49 static struct skin_element* skin_parse_viewport(const char** document);
50 static struct skin_element* skin_parse_line(const char** document);
51 static struct skin_element* skin_parse_line_optional(const char** document,
52 int conditional);
53 static struct skin_element* skin_parse_sublines(const char** document);
54 static struct skin_element* skin_parse_sublines_optional(const char** document,
55 int conditional);
57 static int skin_parse_tag(struct skin_element* element, const char** document);
58 static int skin_parse_text(struct skin_element* element, const char** document,
59 int conditional);
60 static int skin_parse_conditional(struct skin_element* element,
61 const char** document);
62 static int skin_parse_comment(struct skin_element* element, const char** document);
63 static struct skin_element* skin_parse_code_as_arg(const char** document);
66 static void skip_whitespace(const char** document)
68 while(**document == ' ' || **document == '\t')
69 (*document)++;
72 #ifdef ROCKBOX
73 struct skin_element* skin_parse(const char* document,
74 skin_callback cb, void* cb_data)
76 callback = cb;
77 callback_data = cb_data;
78 #else
79 struct skin_element* skin_parse(const char* document)
81 #endif
82 struct skin_element* root = NULL;
83 struct skin_element* last = NULL;
85 struct skin_element** to_write = 0;
87 const char* cursor = document; /*Keeps track of location in the document*/
89 skin_line = 1;
90 skin_start = (char*)document;
91 viewport_line = 0;
93 skin_clear_errors();
95 while(*cursor != '\0')
98 if(!root)
99 to_write = &root;
100 else
101 to_write = &(last->next);
104 *to_write = skin_parse_viewport(&cursor);
105 last = *to_write;
106 if(!last)
108 skin_free_tree(root); /* Clearing any memory already used */
109 return NULL;
112 /* Making sure last is at the end */
113 while(last->next)
114 last = last->next;
117 return root;
121 static struct skin_element* skin_parse_viewport(const char** document)
123 struct skin_element* root = NULL;
124 struct skin_element* last = NULL;
125 struct skin_element* retval = NULL;
127 tag_recursion_level = 0;
129 retval = skin_alloc_element();
130 if (!retval)
131 return NULL;
132 retval->type = VIEWPORT;
133 retval->children_count = 1;
134 retval->line = skin_line;
135 viewport_line = skin_line;
137 struct skin_element** to_write = 0;
139 const char* cursor = *document; /* Keeps track of location in the document */
140 const char* bookmark; /* Used when we need to look ahead */
142 int sublines = 0; /* Flag for parsing sublines */
144 /* Parsing out the viewport tag if there is one */
145 if(check_viewport(cursor))
147 skin_parse_tag(retval, &cursor);
148 if(*cursor == '\n')
150 cursor++;
151 skin_line++;
154 #ifdef ROCKBOX
155 else if (callback)
157 if (callback(retval, callback_data) == CALLBACK_ERROR)
158 return NULL;
160 #endif
162 if (check_viewport(cursor))
164 retval->children_count = 0;
165 *document = cursor;
166 return retval;
168 retval->children_count = 1;
169 retval->children = skin_alloc_children(1);
170 if (!retval->children)
171 return NULL;
175 /* First, we check to see if this line will contain sublines */
176 bookmark = cursor;
177 sublines = 0;
178 while(*cursor != '\n' && *cursor != '\0'
179 && !(check_viewport(cursor) && cursor != *document))
181 if(*cursor == MULTILINESYM)
183 sublines = 1;
184 break;
186 else if(*cursor == TAGSYM)
188 /* A ';' directly after a '%' doesn't count */
189 cursor ++;
191 if(*cursor == '\0')
192 break;
194 cursor++;
196 else if(*cursor == COMMENTSYM)
198 skip_comment(&cursor);
200 else if(*cursor == ARGLISTOPENSYM)
202 skip_arglist(&cursor);
204 else if(*cursor == ENUMLISTOPENSYM)
206 skip_enumlist(&cursor);
208 else
210 /* Advancing the cursor as normal */
211 cursor++;
214 cursor = bookmark;
216 if(!root)
217 to_write = &root;
218 else
219 to_write = &(last->next);
221 if(sublines)
223 *to_write = skin_parse_sublines(&cursor);
224 last = *to_write;
225 if(!last)
226 return NULL;
228 else
231 *to_write = skin_parse_line(&cursor);
232 last = *to_write;
233 if(!last)
234 return NULL;
237 /* Making sure last is at the end */
238 while(last->next)
239 last = last->next;
241 if(*cursor == '\n')
243 cursor++;
244 skin_line++;
247 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
249 *document = cursor;
251 retval->children[0] = root;
252 return retval;
255 /* Auxiliary Parsing Functions */
257 static struct skin_element* skin_parse_line(const char**document)
259 return skin_parse_line_optional(document, 0);
263 * If conditional is set to true, then this will break upon encountering
264 * SEPARATESYM. This should only be used when parsing a line inside a
265 * conditional, otherwise just use the wrapper function skin_parse_line()
267 static struct skin_element* skin_parse_line_optional(const char** document,
268 int conditional)
270 const char* cursor = *document;
272 struct skin_element* root = NULL;
273 struct skin_element* current = NULL;
274 struct skin_element* retval = NULL;
276 /* A wrapper for the line */
277 retval = skin_alloc_element();
278 if (!retval)
279 return NULL;
280 retval->type = LINE;
281 retval->line = skin_line;
282 if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
283 && !(conditional && (*cursor == ARGLISTSEPARATESYM
284 || *cursor == ARGLISTCLOSESYM
285 || *cursor == ENUMLISTSEPARATESYM
286 || *cursor == ENUMLISTCLOSESYM)))
288 retval->children_count = 1;
290 else
292 retval->children_count = 0;
295 if(retval->children_count > 0)
297 retval->children = skin_alloc_children(1);
298 if (!retval->children)
299 return NULL;
302 #ifdef ROCKBOX
303 if (callback)
305 switch (callback(retval, callback_data))
307 case CALLBACK_ERROR:
308 return NULL;
309 default:
310 break;
313 #endif
315 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
316 && !((*cursor == ARGLISTSEPARATESYM
317 || *cursor == ARGLISTCLOSESYM
318 || *cursor == ENUMLISTSEPARATESYM
319 || *cursor == ENUMLISTCLOSESYM)
320 && conditional)
321 && !(check_viewport(cursor) && cursor != *document))
323 /* Allocating memory if necessary */
324 if(root)
326 current->next = skin_alloc_element();
327 if (!current->next)
328 return NULL;
329 current = current->next;
331 else
333 current = skin_alloc_element();
334 if (!current)
335 return NULL;
336 root = current;
339 /* Parsing the current element */
340 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
342 if(!skin_parse_conditional(current, &cursor))
343 return NULL;
345 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
347 if(!skin_parse_tag(current, &cursor))
348 return NULL;
350 else if(*cursor == COMMENTSYM)
352 if(!skin_parse_comment(current, &cursor))
353 return NULL;
355 else
357 if(!skin_parse_text(current, &cursor, conditional))
358 return NULL;
363 /* Moving up the calling function's pointer */
364 *document = cursor;
366 if(root)
367 retval->children[0] = root;
368 return retval;
371 static struct skin_element* skin_parse_sublines(const char** document)
373 return skin_parse_sublines_optional(document, 0);
376 static struct skin_element* skin_parse_sublines_optional(const char** document,
377 int conditional)
379 struct skin_element* retval;
380 const char* cursor = *document;
381 int sublines = 1;
382 int i;
384 retval = skin_alloc_element();
385 if (!retval)
386 return NULL;
387 retval->type = LINE_ALTERNATOR;
388 retval->next = NULL;
389 retval->line = skin_line;
391 /* First we count the sublines */
392 while(*cursor != '\0' && *cursor != '\n'
393 && !((*cursor == ARGLISTSEPARATESYM
394 || *cursor == ARGLISTCLOSESYM
395 || *cursor == ENUMLISTSEPARATESYM
396 || *cursor == ENUMLISTCLOSESYM)
397 && conditional)
398 && !(check_viewport(cursor) && cursor != *document))
400 if(*cursor == COMMENTSYM)
402 skip_comment(&cursor);
404 else if(*cursor == ENUMLISTOPENSYM)
406 skip_enumlist(&cursor);
408 else if(*cursor == ARGLISTOPENSYM)
410 skip_arglist(&cursor);
412 else if(*cursor == TAGSYM)
414 cursor++;
415 if(*cursor == '\0' || *cursor == '\n')
416 break;
417 cursor++;
419 else if(*cursor == MULTILINESYM)
421 sublines++;
422 cursor++;
424 else
426 cursor++;
430 /* ...and then we parse them */
431 retval->children_count = sublines;
432 retval->children = skin_alloc_children(sublines);
433 if (!retval->children)
434 return NULL;
436 cursor = *document;
437 for(i = 0; i < sublines; i++)
439 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
440 skip_whitespace(&cursor);
442 if(*cursor != MULTILINESYM && i != sublines - 1)
444 skin_error(MULTILINE_EXPECTED, cursor);
445 return NULL;
447 else if(i != sublines - 1)
449 cursor++;
453 #ifdef ROCKBOX
454 if (callback)
456 if (callback(retval, callback_data) == CALLBACK_ERROR)
457 return NULL;
459 #endif
460 *document = cursor;
462 return retval;
465 static int skin_parse_tag(struct skin_element* element, const char** document)
467 const char* cursor = *document + 1;
468 const char* bookmark;
470 char tag_name[3];
471 char* tag_args;
472 const struct tag_info *tag;
474 int num_args = 1;
475 int i;
476 int star = 0; /* Flag for the all-or-none option */
477 int req_args; /* To mark when we enter optional arguments */
479 int optional = 0;
480 tag_recursion_level++;
482 /* Checking the tag name */
483 tag_name[0] = cursor[0];
484 tag_name[1] = cursor[1];
485 tag_name[2] = '\0';
487 /* First we check the two characters after the '%', then a single char */
488 tag = find_tag(tag_name);
490 if(!tag)
492 tag_name[1] = '\0';
493 tag = find_tag(tag_name);
494 cursor++;
496 else
498 cursor += 2;
501 if(!tag)
503 skin_error(ILLEGAL_TAG, cursor);
504 return 0;
507 /* Copying basic tag info */
508 if(element->type != CONDITIONAL && element->type != VIEWPORT)
509 element->type = TAG;
510 element->tag = tag;
511 tag_args = tag->params;
512 element->line = skin_line;
514 /* Checking for the * flag */
515 if(tag_args[0] == '*')
517 star = 1;
518 tag_args++;
521 /* If this tag has no arguments, we can bail out now */
522 if(strlen(tag_args) == 0
523 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
524 || (star && *cursor != ARGLISTOPENSYM))
527 #ifdef ROCKBOX
528 if (callback)
530 if (callback(element, callback_data) == CALLBACK_ERROR)
531 return 0;
533 #endif
534 *document = cursor;
535 return 1;
538 /* Checking the number of arguments and allocating args */
539 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
541 skin_error(ARGLIST_EXPECTED, cursor);
542 return 0;
544 else
546 cursor++;
549 bookmark = cursor;
550 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
552 /* Skipping over escaped characters */
553 if(*cursor == TAGSYM)
555 cursor++;
556 if(*cursor == '\0')
557 break;
558 cursor++;
560 else if(*cursor == COMMENTSYM)
562 skip_comment(&cursor);
564 else if(*cursor == ARGLISTOPENSYM)
566 skip_arglist(&cursor);
568 else if(*cursor == ARGLISTSEPARATESYM)
570 num_args++;
571 cursor++;
573 else
575 cursor++;
579 cursor = bookmark; /* Restoring the cursor */
580 element->params_count = num_args;
581 element->params = skin_alloc_params(num_args, tag_recursion_level<=1);
582 if (!element->params)
583 return 0;
585 /* Now we have to actually parse each argument */
586 for(i = 0; i < num_args; i++)
588 char type_code;
589 /* Making sure we haven't run out of arguments */
590 if(*tag_args == '\0')
592 skin_error(TOO_MANY_ARGS, cursor);
593 return 0;
596 /* Checking for the optional bar */
597 if(*tag_args == '|')
599 optional = 1;
600 req_args = i;
601 tag_args++;
604 /* Scanning the arguments */
605 skip_whitespace(&cursor);
607 /* Checking for comments */
608 if(*cursor == COMMENTSYM)
609 skip_comment(&cursor);
611 if (*tag_args == '[')
613 /* we need to guess which type of param it is.
614 * guess using this priority:
615 * default > decimal/integer > single tag/code > string
617 int j=0;
618 bool canbedefault = false;
619 bool haspercent = false, number = true, hasdecimal = false;
620 char temp_params[8];
621 tag_args++;
622 while (*tag_args != ']')
624 if (*tag_args >= 'a' && *tag_args <= 'z')
625 canbedefault = true;
626 temp_params[j++] = tolower(*tag_args++);
628 temp_params[j] = '\0';
629 j = 0;
630 while (cursor[j] && cursor[j] != ',' && cursor[j] != ')')
632 haspercent = haspercent || (cursor[j] == '%');
633 hasdecimal = hasdecimal || (cursor[j] == '.');
634 number = number && (isdigit(cursor[j]) ||
635 (cursor[j] == '.') ||
636 (cursor[j] == '-'));
637 j++;
639 type_code = '*';
640 if (canbedefault && *cursor == DEFAULTSYM && !isdigit(cursor[1]))
642 type_code = 'i';
644 else if (number && hasdecimal && strchr(temp_params, 'd'))
646 type_code = 'd';
648 else if (number &&
649 (strchr(temp_params, 'i') || strchr(temp_params, 'd')))
651 type_code = strchr(temp_params, 'i') ? 'i' : 'd';
653 else if (haspercent &&
654 (strchr(temp_params, 't') || strchr(temp_params, 'c')))
656 type_code = strchr(temp_params, 't') ? 't' : 'c';
658 else if (strchr(temp_params, 's'))
660 type_code = 's';
662 if (type_code == '*')
664 skin_error(INSUFFICIENT_ARGS, cursor);
665 return 0;
668 else
669 type_code = *tag_args;
670 /* Storing the type code */
671 element->params[i].type_code = type_code;
673 /* Checking a nullable argument for null. */
674 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
676 if(islower(type_code))
678 element->params[i].type = DEFAULT;
679 cursor++;
681 else
683 skin_error(DEFAULT_NOT_ALLOWED, cursor);
684 return 0;
687 else if(tolower(type_code) == 'i')
689 /* Scanning an int argument */
690 if(!isdigit(*cursor) && *cursor != '-')
692 skin_error(INT_EXPECTED, cursor);
693 return 0;
696 element->params[i].type = INTEGER;
697 element->params[i].data.number = scan_int(&cursor);
699 else if(tolower(type_code) == 'd')
701 int val = 0;
702 bool have_point = false;
703 bool have_tenth = false;
704 while ( isdigit(*cursor) || *cursor == '.' )
706 if (*cursor != '.')
708 val *= 10;
709 val += *cursor - '0';
710 if (have_point)
712 have_tenth = true;
713 cursor++;
714 break;
717 else
718 have_point = true;
719 cursor++;
721 if (have_tenth == false)
722 val *= 10;
723 element->params[i].type = DECIMAL;
724 element->params[i].data.number = val;
726 else if(tolower(type_code) == 'n' ||
727 tolower(type_code) == 's' || tolower(type_code) == 'f')
729 /* Scanning a string argument */
730 element->params[i].type = STRING;
731 element->params[i].data.text = scan_string(&cursor);
734 else if(tolower(type_code) == 'c')
736 /* Recursively parsing a code argument */
737 element->params[i].type = CODE;
738 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
739 if(!element->params[i].data.code)
740 return 0;
742 else if (tolower(type_code) == 't')
744 struct skin_element* child = skin_alloc_element();
745 child->type = TAG;
746 if (!skin_parse_tag(child, &cursor))
747 return 0;
748 child->next = NULL;
749 element->params[i].type = CODE;
750 element->params[i].data.code = child;
754 skip_whitespace(&cursor);
756 if(*cursor != ARGLISTSEPARATESYM && i < num_args - 1)
758 skin_error(SEPARATOR_EXPECTED, cursor);
759 return 0;
761 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
763 skin_error(CLOSE_EXPECTED, cursor);
764 return 0;
766 else
768 cursor++;
771 if (*tag_args != 'N')
772 tag_args++;
774 /* Checking for the optional bar */
775 if(*tag_args == '|')
777 optional = 1;
778 req_args = i + 1;
779 tag_args++;
783 /* Checking for a premature end */
784 if(*tag_args != '\0' && !optional)
786 skin_error(INSUFFICIENT_ARGS, cursor);
787 return 0;
789 #ifdef ROCKBOX
790 if (callback)
792 if (callback(element, callback_data) == CALLBACK_ERROR)
793 return 0;
795 #endif
796 *document = cursor;
797 tag_recursion_level--;
799 return 1;
803 * If the conditional flag is set true, then parsing text will stop at an
804 * ARGLISTSEPARATESYM. Only set that flag when parsing within a conditional
806 static int skin_parse_text(struct skin_element* element, const char** document,
807 int conditional)
809 const char* cursor = *document;
810 int length = 0;
811 int dest;
812 char *text = NULL;
814 /* First figure out how much text we're copying */
815 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
816 && *cursor != COMMENTSYM
817 && !((*cursor == ARGLISTSEPARATESYM
818 || *cursor == ARGLISTCLOSESYM
819 || *cursor == ENUMLISTSEPARATESYM
820 || *cursor == ENUMLISTCLOSESYM)
821 && conditional))
823 /* Dealing with possibility of escaped characters */
824 if(*cursor == TAGSYM)
826 if(find_escape_character(cursor[1]))
827 cursor++;
828 else
829 break;
832 length++;
833 cursor++;
836 cursor = *document;
838 /* Copying the text into the element struct */
839 element->type = TEXT;
840 element->line = skin_line;
841 element->next = NULL;
842 element->data = text = skin_alloc_string(length);
843 if (!element->data)
844 return 0;
846 for(dest = 0; dest < length; dest++)
848 /* Advancing cursor if we've encountered an escaped character */
849 if(*cursor == TAGSYM)
850 cursor++;
852 text[dest] = *cursor;
853 cursor++;
855 text[length] = '\0';
857 #ifdef ROCKBOX
858 if (callback)
860 if (callback(element, callback_data) == CALLBACK_ERROR)
861 return 0;
863 #endif
865 *document = cursor;
867 return 1;
870 static int skin_parse_conditional(struct skin_element* element, const char** document)
872 const char* cursor = *document + 1; /* Starting past the "%" */
873 const char* bookmark;
874 int children = 1;
875 int i;
877 #ifdef ROCKBOX
878 bool feature_available = true;
879 const char *false_branch = NULL;
880 const char *conditional_end = NULL;
881 #endif
883 /* Some conditional tags allow for target feature checking,
884 * so to handle that call the callback as usual with type == TAG
885 * then call it a second time with type == CONDITIONAL and check the return
886 * value */
887 element->type = TAG;
888 element->line = skin_line;
890 /* Parsing the tag first */
891 if(!skin_parse_tag(element, &cursor))
892 return 0;
894 element->type = CONDITIONAL;
895 #ifdef ROCKBOX
896 if (callback)
898 switch (callback(element, callback_data))
900 case FEATURE_NOT_AVAILABLE:
901 feature_available = false;
902 break;
903 case CALLBACK_ERROR:
904 return 0;
905 default:
906 break;
909 #endif
911 /* Counting the children */
912 if(*(cursor++) != ENUMLISTOPENSYM)
914 skin_error(ARGLIST_EXPECTED, cursor);
915 return 0;
917 bookmark = cursor;
918 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
920 if(*cursor == COMMENTSYM)
922 skip_comment(&cursor);
924 else if(*cursor == ENUMLISTOPENSYM)
926 skip_enumlist(&cursor);
928 else if(*cursor == TAGSYM)
930 cursor++;
931 if(*cursor == '\0' || *cursor == '\n')
932 break;
933 cursor++;
935 else if(*cursor == ENUMLISTSEPARATESYM)
937 children++;
938 cursor++;
939 #ifdef ROCKBOX
940 if (false_branch == NULL && !feature_available)
942 false_branch = cursor;
943 children--;
945 #endif
947 else
949 cursor++;
952 #ifdef ROCKBOX
953 if (*cursor == ENUMLISTCLOSESYM &&
954 false_branch == NULL && !feature_available)
956 false_branch = cursor+1;
957 children--;
959 if (element->tag->flags&FEATURE_TAG)
961 if (feature_available && children > 1)
962 children--;
964 conditional_end = cursor;
965 /* if we are skipping the true branch fix that up */
966 cursor = false_branch ? false_branch : bookmark;
967 #else
968 cursor = bookmark;
969 #endif
970 /* Parsing the children */
971 element->children = skin_alloc_children(children);
972 if (!element->children)
973 return 0;
974 element->children_count = children;
976 for(i = 0; i < children; i++)
978 element->children[i] = skin_parse_code_as_arg(&cursor);
979 if (element->children[i] == NULL)
980 return 0;
981 skip_whitespace(&cursor);
982 #ifdef ROCKBOX
983 if ((element->tag->flags&FEATURE_TAG) && feature_available)
984 cursor = conditional_end;
985 #endif
987 if(i < children - 1 && *cursor != ENUMLISTSEPARATESYM)
989 skin_error(SEPARATOR_EXPECTED, cursor);
990 return 0;
992 else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
994 skin_error(CLOSE_EXPECTED, cursor);
995 return 0;
997 else
999 cursor++;
1002 *document = cursor;
1004 return 1;
1007 static int skin_parse_comment(struct skin_element* element, const char** document)
1009 const char* cursor = *document;
1010 #ifndef ROCKBOX
1011 char* text = NULL;
1012 #endif
1013 int length;
1015 * Finding the index of the ending newline or null-terminator
1016 * The length of the string of interest doesn't include the leading #, the
1017 * length we need to reserve is the same as the index of the last character
1019 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
1021 element->type = COMMENT;
1022 element->line = skin_line;
1023 #ifdef ROCKBOX
1024 element->data = NULL;
1025 #else
1026 element->data = text = skin_alloc_string(length);
1027 if (!element->data)
1028 return 0;
1029 /* We copy from one char past cursor to leave out the # */
1030 memcpy((void*)text, (void*)(cursor + 1),
1031 sizeof(char) * (length-1));
1032 text[length - 1] = '\0';
1033 #endif
1034 if(cursor[length] == '\n')
1035 skin_line++;
1037 *document += (length); /* Move cursor up past # and all text */
1038 if(**document == '\n')
1039 (*document)++;
1041 return 1;
1044 static struct skin_element* skin_parse_code_as_arg(const char** document)
1046 int sublines = 0;
1047 const char* cursor = *document;
1049 /* Checking for sublines */
1050 while(*cursor != '\n' && *cursor != '\0'
1051 && *cursor != ENUMLISTSEPARATESYM && *cursor != ARGLISTSEPARATESYM
1052 && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
1054 if(*cursor == MULTILINESYM)
1056 sublines = 1;
1057 break;
1059 else if(*cursor == TAGSYM)
1061 /* A ';' directly after a '%' doesn't count */
1062 cursor ++;
1064 if(*cursor == '\0')
1065 break;
1067 cursor++;
1069 else if(*cursor == ARGLISTOPENSYM)
1071 skip_arglist(&cursor);
1073 else if(*cursor == ENUMLISTOPENSYM)
1075 skip_enumlist(&cursor);
1077 else
1079 /* Advancing the cursor as normal */
1080 cursor++;
1084 if(sublines)
1085 return skin_parse_sublines_optional(document, 1);
1086 else
1087 return skin_parse_line_optional(document, 1);
1091 /* Memory management */
1092 struct skin_element* skin_alloc_element()
1094 struct skin_element* retval = (struct skin_element*)
1095 skin_buffer_alloc(sizeof(struct skin_element));
1096 if (!retval)
1097 return NULL;
1098 retval->type = UNKNOWN;
1099 retval->next = NULL;
1100 retval->tag = NULL;
1101 retval->params_count = 0;
1102 retval->children_count = 0;
1104 return retval;
1107 /* On a ROCKBOX build we try to save space as much as possible
1108 * so if we can, use a shared param pool which should be more then large
1109 * enough for any tag. params should be used straight away by the callback
1110 * so this is safe.
1112 struct skin_tag_parameter* skin_alloc_params(int count, bool use_shared_params)
1114 #ifdef ROCKBOX
1115 static struct skin_tag_parameter params[MAX_TAG_PARAMS];
1116 if (use_shared_params && count <= MAX_TAG_PARAMS)
1118 memset(params, 0, sizeof(params));
1119 return params;
1121 #endif
1122 size_t size = sizeof(struct skin_tag_parameter) * count;
1123 return (struct skin_tag_parameter*)skin_buffer_alloc(size);
1127 char* skin_alloc_string(int length)
1129 return (char*)skin_buffer_alloc(sizeof(char) * (length + 1));
1132 struct skin_element** skin_alloc_children(int count)
1134 return (struct skin_element**)
1135 skin_buffer_alloc(sizeof(struct skin_element*) * count);
1138 void skin_free_tree(struct skin_element* root)
1140 #ifndef ROCKBOX
1141 int i;
1143 /* First make the recursive call */
1144 if(!root)
1145 return;
1146 skin_free_tree(root->next);
1148 /* Free any text */
1149 if(root->type == TEXT || root->type == COMMENT)
1150 free(root->data);
1152 /* Then recursively free any children, before freeing their pointers */
1153 for(i = 0; i < root->children_count; i++)
1154 skin_free_tree(root->children[i]);
1155 if(root->children_count > 0)
1156 free(root->children);
1158 /* Free any parameters, making sure to deallocate strings */
1159 for(i = 0; i < root->params_count; i++)
1160 if(root->params[i].type == STRING)
1161 free(root->params[i].data.text);
1162 if(root->params_count > 0)
1163 free(root->params);
1165 /* Finally, delete root's memory */
1166 free(root);
1167 #else
1168 (void)root;
1169 #endif