Define LCD dpi for n900, n8xx and the pandora
[maemo-rb.git] / lib / skin_parser / skin_parser.c
blobe9f0bf8b476ff7dc50bc43c82092658c01859ced
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 if (!skin_parse_tag(retval, &cursor))
148 return NULL;
149 if(*cursor == '\n')
151 cursor++;
152 skin_line++;
155 #ifdef ROCKBOX
156 else if (callback)
158 if (callback(retval, callback_data) == CALLBACK_ERROR)
159 return NULL;
161 #endif
163 if (check_viewport(cursor))
165 retval->children_count = 0;
166 *document = cursor;
167 return retval;
169 retval->children_count = 1;
170 retval->children = skin_alloc_children(1);
171 if (!retval->children)
172 return NULL;
176 /* First, we check to see if this line will contain sublines */
177 bookmark = cursor;
178 sublines = 0;
179 while(*cursor != '\n' && *cursor != '\0'
180 && !(check_viewport(cursor) && cursor != *document))
182 if(*cursor == MULTILINESYM)
184 sublines = 1;
185 break;
187 else if(*cursor == TAGSYM)
189 /* A ';' directly after a '%' doesn't count */
190 cursor ++;
192 if(*cursor == '\0')
193 break;
195 cursor++;
197 else if(*cursor == COMMENTSYM)
199 skip_comment(&cursor);
201 else if(*cursor == ARGLISTOPENSYM)
203 skip_arglist(&cursor);
205 else if(*cursor == ENUMLISTOPENSYM)
207 skip_enumlist(&cursor);
209 else
211 /* Advancing the cursor as normal */
212 cursor++;
215 cursor = bookmark;
217 if(!root)
218 to_write = &root;
219 else
220 to_write = &(last->next);
222 if(sublines)
224 *to_write = skin_parse_sublines(&cursor);
225 last = *to_write;
226 if(!last)
227 return NULL;
229 else
232 *to_write = skin_parse_line(&cursor);
233 last = *to_write;
234 if(!last)
235 return NULL;
238 /* Making sure last is at the end */
239 while(last->next)
240 last = last->next;
242 if(*cursor == '\n')
244 cursor++;
245 skin_line++;
248 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
250 *document = cursor;
252 retval->children[0] = root;
253 return retval;
256 /* Auxiliary Parsing Functions */
258 static struct skin_element* skin_parse_line(const char**document)
260 return skin_parse_line_optional(document, 0);
264 * If conditional is set to true, then this will break upon encountering
265 * SEPARATESYM. This should only be used when parsing a line inside a
266 * conditional, otherwise just use the wrapper function skin_parse_line()
268 static struct skin_element* skin_parse_line_optional(const char** document,
269 int conditional)
271 const char* cursor = *document;
273 struct skin_element* root = NULL;
274 struct skin_element* current = NULL;
275 struct skin_element* retval = NULL;
277 /* A wrapper for the line */
278 retval = skin_alloc_element();
279 if (!retval)
280 return NULL;
281 retval->type = LINE;
282 retval->line = skin_line;
283 if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
284 && !(conditional && (*cursor == ARGLISTSEPARATESYM
285 || *cursor == ARGLISTCLOSESYM
286 || *cursor == ENUMLISTSEPARATESYM
287 || *cursor == ENUMLISTCLOSESYM)))
289 retval->children_count = 1;
291 else
293 retval->children_count = 0;
296 if(retval->children_count > 0)
298 retval->children = skin_alloc_children(1);
299 if (!retval->children)
300 return NULL;
303 #ifdef ROCKBOX
304 if (callback)
306 switch (callback(retval, callback_data))
308 case CALLBACK_ERROR:
309 return NULL;
310 default:
311 break;
314 #endif
316 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
317 && !((*cursor == ARGLISTSEPARATESYM
318 || *cursor == ARGLISTCLOSESYM
319 || *cursor == ENUMLISTSEPARATESYM
320 || *cursor == ENUMLISTCLOSESYM)
321 && conditional)
322 && !(check_viewport(cursor) && cursor != *document))
324 /* Allocating memory if necessary */
325 if(root)
327 current->next = skin_alloc_element();
328 if (!current->next)
329 return NULL;
330 current = current->next;
332 else
334 current = skin_alloc_element();
335 if (!current)
336 return NULL;
337 root = current;
340 /* Parsing the current element */
341 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
343 if(!skin_parse_conditional(current, &cursor))
344 return NULL;
346 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
348 if(!skin_parse_tag(current, &cursor))
349 return NULL;
351 else if(*cursor == COMMENTSYM)
353 if(!skin_parse_comment(current, &cursor))
354 return NULL;
356 else
358 if(!skin_parse_text(current, &cursor, conditional))
359 return NULL;
364 /* Moving up the calling function's pointer */
365 *document = cursor;
367 if(root)
368 retval->children[0] = root;
369 return retval;
372 static struct skin_element* skin_parse_sublines(const char** document)
374 return skin_parse_sublines_optional(document, 0);
377 static struct skin_element* skin_parse_sublines_optional(const char** document,
378 int conditional)
380 struct skin_element* retval;
381 const char* cursor = *document;
382 int sublines = 1;
383 int i;
385 retval = skin_alloc_element();
386 if (!retval)
387 return NULL;
388 retval->type = LINE_ALTERNATOR;
389 retval->next = NULL;
390 retval->line = skin_line;
392 /* First we count the sublines */
393 while(*cursor != '\0' && *cursor != '\n'
394 && !((*cursor == ARGLISTSEPARATESYM
395 || *cursor == ARGLISTCLOSESYM
396 || *cursor == ENUMLISTSEPARATESYM
397 || *cursor == ENUMLISTCLOSESYM)
398 && conditional)
399 && !(check_viewport(cursor) && cursor != *document))
401 if(*cursor == COMMENTSYM)
403 skip_comment(&cursor);
405 else if(*cursor == ENUMLISTOPENSYM)
407 skip_enumlist(&cursor);
409 else if(*cursor == ARGLISTOPENSYM)
411 skip_arglist(&cursor);
413 else if(*cursor == TAGSYM)
415 cursor++;
416 if(*cursor == '\0' || *cursor == '\n')
417 break;
418 cursor++;
420 else if(*cursor == MULTILINESYM)
422 sublines++;
423 cursor++;
425 else
427 cursor++;
431 /* ...and then we parse them */
432 retval->children_count = sublines;
433 retval->children = skin_alloc_children(sublines);
434 if (!retval->children)
435 return NULL;
437 cursor = *document;
438 for(i = 0; i < sublines; i++)
440 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
441 skip_whitespace(&cursor);
443 if(*cursor != MULTILINESYM && i != sublines - 1)
445 skin_error(MULTILINE_EXPECTED, cursor);
446 return NULL;
448 else if(i != sublines - 1)
450 cursor++;
454 #ifdef ROCKBOX
455 if (callback)
457 if (callback(retval, callback_data) == CALLBACK_ERROR)
458 return NULL;
460 #endif
461 *document = cursor;
463 return retval;
466 static int skin_parse_tag(struct skin_element* element, const char** document)
468 const char* cursor = *document + 1;
469 const char* bookmark;
471 char tag_name[3];
472 char* tag_args;
473 const struct tag_info *tag;
475 int num_args = 1;
476 int i;
477 int star = 0; /* Flag for the all-or-none option */
478 int req_args; /* To mark when we enter optional arguments */
480 int optional = 0;
481 tag_recursion_level++;
483 /* Checking the tag name */
484 tag_name[0] = cursor[0];
485 tag_name[1] = cursor[1];
486 tag_name[2] = '\0';
488 /* First we check the two characters after the '%', then a single char */
489 tag = find_tag(tag_name);
491 if(!tag)
493 tag_name[1] = '\0';
494 tag = find_tag(tag_name);
495 cursor++;
497 else
499 cursor += 2;
502 if(!tag)
504 skin_error(ILLEGAL_TAG, cursor);
505 return 0;
508 /* Copying basic tag info */
509 if(element->type != CONDITIONAL && element->type != VIEWPORT)
510 element->type = TAG;
511 element->tag = tag;
512 tag_args = tag->params;
513 element->line = skin_line;
515 /* Checking for the * flag */
516 if(tag_args[0] == '*')
518 star = 1;
519 tag_args++;
522 /* If this tag has no arguments, we can bail out now */
523 if(strlen(tag_args) == 0
524 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
525 || (star && *cursor != ARGLISTOPENSYM))
528 #ifdef ROCKBOX
529 if (callback)
531 if (callback(element, callback_data) == CALLBACK_ERROR)
532 return 0;
534 #endif
535 *document = cursor;
536 return 1;
539 /* Checking the number of arguments and allocating args */
540 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
542 skin_error(ARGLIST_EXPECTED, cursor);
543 return 0;
545 else
547 cursor++;
550 bookmark = cursor;
551 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
553 /* Skipping over escaped characters */
554 if(*cursor == TAGSYM)
556 cursor++;
557 if(*cursor == '\0')
558 break;
559 cursor++;
561 else if(*cursor == COMMENTSYM)
563 skip_comment(&cursor);
565 else if(*cursor == ARGLISTOPENSYM)
567 skip_arglist(&cursor);
569 else if(*cursor == ARGLISTSEPARATESYM)
571 num_args++;
572 cursor++;
574 else
576 cursor++;
580 cursor = bookmark; /* Restoring the cursor */
581 element->params_count = num_args;
582 element->params = skin_alloc_params(num_args, tag_recursion_level<=1);
583 if (!element->params)
584 return 0;
586 /* Now we have to actually parse each argument */
587 for(i = 0; i < num_args; i++)
589 char type_code;
590 /* Making sure we haven't run out of arguments */
591 if(*tag_args == '\0')
593 skin_error(TOO_MANY_ARGS, cursor);
594 return 0;
597 /* Checking for the optional bar */
598 if(*tag_args == '|')
600 optional = 1;
601 req_args = i;
602 tag_args++;
605 /* Scanning the arguments */
606 skip_whitespace(&cursor);
608 /* Checking for comments */
609 if(*cursor == COMMENTSYM)
610 skip_comment(&cursor);
612 if (*tag_args == '[')
614 /* we need to guess which type of param it is.
615 * guess using this priority:
616 * default > decimal/integer > single tag/code > string
618 int j=0;
619 bool canbedefault = false;
620 bool haspercent = false, number = true, hasdecimal = false;
621 char temp_params[8];
622 tag_args++;
623 while (*tag_args != ']')
625 if (*tag_args >= 'a' && *tag_args <= 'z')
626 canbedefault = true;
627 temp_params[j++] = tolower(*tag_args++);
629 temp_params[j] = '\0';
630 j = 0;
631 while (cursor[j] && cursor[j] != ',' && cursor[j] != ')')
633 haspercent = haspercent || (cursor[j] == '%');
634 hasdecimal = hasdecimal || (cursor[j] == '.');
635 number = number && (isdigit(cursor[j]) ||
636 (cursor[j] == '.') ||
637 (cursor[j] == '-'));
638 j++;
640 type_code = '*';
641 if (canbedefault && *cursor == DEFAULTSYM && !isdigit(cursor[1]))
643 type_code = 'i';
645 else if (number && hasdecimal && strchr(temp_params, 'd'))
647 type_code = 'd';
649 else if (number &&
650 (strchr(temp_params, 'i') || strchr(temp_params, 'd')))
652 type_code = strchr(temp_params, 'i') ? 'i' : 'd';
654 else if (haspercent &&
655 (strchr(temp_params, 't') || strchr(temp_params, 'c')))
657 type_code = strchr(temp_params, 't') ? 't' : 'c';
659 else if (strchr(temp_params, 's'))
661 type_code = 's';
663 if (type_code == '*')
665 skin_error(INSUFFICIENT_ARGS, cursor);
666 return 0;
669 else
670 type_code = *tag_args;
671 /* Storing the type code */
672 element->params[i].type_code = type_code;
674 /* Checking a nullable argument for null. */
675 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
677 if(islower(type_code))
679 element->params[i].type = DEFAULT;
680 cursor++;
682 else
684 skin_error(DEFAULT_NOT_ALLOWED, cursor);
685 return 0;
688 else if(tolower(type_code) == 'i')
690 /* Scanning an int argument */
691 if(!isdigit(*cursor) && *cursor != '-')
693 skin_error(INT_EXPECTED, cursor);
694 return 0;
697 element->params[i].type = INTEGER;
698 element->params[i].data.number = scan_int(&cursor);
700 else if(tolower(type_code) == 'd')
702 int val = 0;
703 bool have_point = false;
704 bool have_tenth = false;
705 while ( isdigit(*cursor) || *cursor == '.' )
707 if (*cursor != '.')
709 val *= 10;
710 val += *cursor - '0';
711 if (have_point)
713 have_tenth = true;
714 cursor++;
715 break;
718 else
719 have_point = true;
720 cursor++;
722 if (have_tenth == false)
723 val *= 10;
724 element->params[i].type = DECIMAL;
725 element->params[i].data.number = val;
727 else if(tolower(type_code) == 'n' ||
728 tolower(type_code) == 's' || tolower(type_code) == 'f')
730 /* Scanning a string argument */
731 element->params[i].type = STRING;
732 element->params[i].data.text = scan_string(&cursor);
735 else if(tolower(type_code) == 'c')
737 /* Recursively parsing a code argument */
738 element->params[i].type = CODE;
739 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
740 if(!element->params[i].data.code)
741 return 0;
743 else if (tolower(type_code) == 't')
745 struct skin_element* child = skin_alloc_element();
746 child->type = TAG;
747 if (!skin_parse_tag(child, &cursor))
748 return 0;
749 child->next = NULL;
750 element->params[i].type = CODE;
751 element->params[i].data.code = child;
755 skip_whitespace(&cursor);
757 if(*cursor != ARGLISTSEPARATESYM && i < num_args - 1)
759 skin_error(SEPARATOR_EXPECTED, cursor);
760 return 0;
762 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
764 skin_error(CLOSE_EXPECTED, cursor);
765 return 0;
767 else
769 cursor++;
772 if (*tag_args != 'N')
773 tag_args++;
775 /* Checking for the optional bar */
776 if(*tag_args == '|')
778 optional = 1;
779 req_args = i + 1;
780 tag_args++;
784 /* Checking for a premature end */
785 if(*tag_args != '\0' && !optional)
787 skin_error(INSUFFICIENT_ARGS, cursor);
788 return 0;
790 #ifdef ROCKBOX
791 if (callback)
793 if (callback(element, callback_data) == CALLBACK_ERROR)
794 return 0;
796 #endif
797 *document = cursor;
798 tag_recursion_level--;
800 return 1;
804 * If the conditional flag is set true, then parsing text will stop at an
805 * ARGLISTSEPARATESYM. Only set that flag when parsing within a conditional
807 static int skin_parse_text(struct skin_element* element, const char** document,
808 int conditional)
810 const char* cursor = *document;
811 int length = 0;
812 int dest;
813 char *text = NULL;
815 /* First figure out how much text we're copying */
816 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
817 && *cursor != COMMENTSYM
818 && !((*cursor == ARGLISTSEPARATESYM
819 || *cursor == ARGLISTCLOSESYM
820 || *cursor == ENUMLISTSEPARATESYM
821 || *cursor == ENUMLISTCLOSESYM)
822 && conditional))
824 /* Dealing with possibility of escaped characters */
825 if(*cursor == TAGSYM)
827 if(find_escape_character(cursor[1]))
828 cursor++;
829 else
830 break;
833 length++;
834 cursor++;
837 cursor = *document;
839 /* Copying the text into the element struct */
840 element->type = TEXT;
841 element->line = skin_line;
842 element->next = NULL;
843 element->data = text = skin_alloc_string(length);
844 if (!element->data)
845 return 0;
847 for(dest = 0; dest < length; dest++)
849 /* Advancing cursor if we've encountered an escaped character */
850 if(*cursor == TAGSYM)
851 cursor++;
853 text[dest] = *cursor;
854 cursor++;
856 text[length] = '\0';
858 #ifdef ROCKBOX
859 if (callback)
861 if (callback(element, callback_data) == CALLBACK_ERROR)
862 return 0;
864 #endif
866 *document = cursor;
868 return 1;
871 static int skin_parse_conditional(struct skin_element* element, const char** document)
873 const char* cursor = *document + 1; /* Starting past the "%" */
874 const char* bookmark;
875 int children = 1;
876 int i;
878 #ifdef ROCKBOX
879 bool feature_available = true;
880 const char *false_branch = NULL;
881 const char *conditional_end = NULL;
882 #endif
884 /* Some conditional tags allow for target feature checking,
885 * so to handle that call the callback as usual with type == TAG
886 * then call it a second time with type == CONDITIONAL and check the return
887 * value */
888 element->type = TAG;
889 element->line = skin_line;
891 /* Parsing the tag first */
892 if(!skin_parse_tag(element, &cursor))
893 return 0;
895 element->type = CONDITIONAL;
896 #ifdef ROCKBOX
897 if (callback)
899 switch (callback(element, callback_data))
901 case FEATURE_NOT_AVAILABLE:
902 feature_available = false;
903 break;
904 case CALLBACK_ERROR:
905 return 0;
906 default:
907 break;
910 #endif
912 /* Counting the children */
913 if(*(cursor++) != ENUMLISTOPENSYM)
915 skin_error(ARGLIST_EXPECTED, cursor);
916 return 0;
918 bookmark = cursor;
919 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
921 if(*cursor == COMMENTSYM)
923 skip_comment(&cursor);
925 else if(*cursor == ENUMLISTOPENSYM)
927 skip_enumlist(&cursor);
929 else if(*cursor == TAGSYM)
931 cursor++;
932 if(*cursor == '\0' || *cursor == '\n')
933 break;
934 cursor++;
936 else if(*cursor == ENUMLISTSEPARATESYM)
938 children++;
939 cursor++;
940 #ifdef ROCKBOX
941 if (false_branch == NULL && !feature_available)
943 false_branch = cursor;
944 children--;
946 #endif
948 else
950 cursor++;
953 #ifdef ROCKBOX
954 if (*cursor == ENUMLISTCLOSESYM &&
955 false_branch == NULL && !feature_available)
957 false_branch = cursor+1;
958 children--;
960 if (element->tag->flags&FEATURE_TAG)
962 if (feature_available && children > 1)
963 children--;
965 conditional_end = cursor;
966 /* if we are skipping the true branch fix that up */
967 cursor = false_branch ? false_branch : bookmark;
968 #else
969 cursor = bookmark;
970 #endif
971 /* Parsing the children */
973 /* Feature tags could end up having 0 children which breaks
974 * the render in dangerous ways. Minor hack, but insert an empty
975 * child. (e.g %?xx<foo> when xx isnt available ) */
977 if (children == 0)
979 const char* emptyline= "";
980 children = 1;
981 element->children = skin_alloc_children(children);
982 if (!element->children)
983 return 0;
984 element->children_count = children;
985 element->children[0] = skin_parse_code_as_arg(&emptyline);
987 else
989 element->children = skin_alloc_children(children);
990 if (!element->children)
991 return 0;
992 element->children_count = children;
994 for(i = 0; i < children; i++)
996 element->children[i] = skin_parse_code_as_arg(&cursor);
997 if (element->children[i] == NULL)
998 return 0;
999 skip_whitespace(&cursor);
1000 #ifdef ROCKBOX
1001 if ((element->tag->flags&FEATURE_TAG) && feature_available)
1002 cursor = conditional_end;
1003 #endif
1005 if(i < children - 1 && *cursor != ENUMLISTSEPARATESYM)
1007 skin_error(SEPARATOR_EXPECTED, cursor);
1008 return 0;
1010 else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
1012 skin_error(CLOSE_EXPECTED, cursor);
1013 return 0;
1015 else
1017 cursor++;
1021 *document = cursor;
1023 return 1;
1026 static int skin_parse_comment(struct skin_element* element, const char** document)
1028 const char* cursor = *document;
1029 #ifndef ROCKBOX
1030 char* text = NULL;
1031 #endif
1032 int length;
1034 * Finding the index of the ending newline or null-terminator
1035 * The length of the string of interest doesn't include the leading #, the
1036 * length we need to reserve is the same as the index of the last character
1038 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
1040 element->type = COMMENT;
1041 element->line = skin_line;
1042 #ifdef ROCKBOX
1043 element->data = NULL;
1044 #else
1045 element->data = text = skin_alloc_string(length);
1046 if (!element->data)
1047 return 0;
1048 /* We copy from one char past cursor to leave out the # */
1049 memcpy((void*)text, (void*)(cursor + 1),
1050 sizeof(char) * (length-1));
1051 text[length - 1] = '\0';
1052 #endif
1053 if(cursor[length] == '\n')
1054 skin_line++;
1056 *document += (length); /* Move cursor up past # and all text */
1057 if(**document == '\n')
1058 (*document)++;
1060 return 1;
1063 static struct skin_element* skin_parse_code_as_arg(const char** document)
1065 int sublines = 0;
1066 const char* cursor = *document;
1068 /* Checking for sublines */
1069 while(*cursor != '\n' && *cursor != '\0'
1070 && *cursor != ENUMLISTSEPARATESYM && *cursor != ARGLISTSEPARATESYM
1071 && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
1073 if(*cursor == MULTILINESYM)
1075 sublines = 1;
1076 break;
1078 else if(*cursor == TAGSYM)
1080 /* A ';' directly after a '%' doesn't count */
1081 cursor ++;
1083 if(*cursor == '\0')
1084 break;
1086 cursor++;
1088 else if(*cursor == ARGLISTOPENSYM)
1090 skip_arglist(&cursor);
1092 else if(*cursor == ENUMLISTOPENSYM)
1094 skip_enumlist(&cursor);
1096 else
1098 /* Advancing the cursor as normal */
1099 cursor++;
1103 if(sublines)
1104 return skin_parse_sublines_optional(document, 1);
1105 else
1106 return skin_parse_line_optional(document, 1);
1110 /* Memory management */
1111 struct skin_element* skin_alloc_element()
1113 struct skin_element* retval = (struct skin_element*)
1114 skin_buffer_alloc(sizeof(struct skin_element));
1115 if (!retval)
1116 return NULL;
1117 retval->type = UNKNOWN;
1118 retval->next = NULL;
1119 retval->tag = NULL;
1120 retval->params_count = 0;
1121 retval->children_count = 0;
1123 return retval;
1126 /* On a ROCKBOX build we try to save space as much as possible
1127 * so if we can, use a shared param pool which should be more then large
1128 * enough for any tag. params should be used straight away by the callback
1129 * so this is safe.
1131 struct skin_tag_parameter* skin_alloc_params(int count, bool use_shared_params)
1133 #ifdef ROCKBOX
1134 static struct skin_tag_parameter params[MAX_TAG_PARAMS];
1135 if (use_shared_params && count <= MAX_TAG_PARAMS)
1137 memset(params, 0, sizeof(params));
1138 return params;
1140 #endif
1141 size_t size = sizeof(struct skin_tag_parameter) * count;
1142 return (struct skin_tag_parameter*)skin_buffer_alloc(size);
1146 char* skin_alloc_string(int length)
1148 return (char*)skin_buffer_alloc(sizeof(char) * (length + 1));
1151 struct skin_element** skin_alloc_children(int count)
1153 return (struct skin_element**)
1154 skin_buffer_alloc(sizeof(struct skin_element*) * count);
1157 void skin_free_tree(struct skin_element* root)
1159 #ifndef ROCKBOX
1160 int i;
1162 /* First make the recursive call */
1163 if(!root)
1164 return;
1165 skin_free_tree(root->next);
1167 /* Free any text */
1168 if(root->type == TEXT || root->type == COMMENT)
1169 free(root->data);
1171 /* Then recursively free any children, before freeing their pointers */
1172 for(i = 0; i < root->children_count; i++)
1173 skin_free_tree(root->children[i]);
1174 if(root->children_count > 0)
1175 free(root->children);
1177 /* Free any parameters, making sure to deallocate strings */
1178 for(i = 0; i < root->params_count; i++)
1179 if(root->params[i].type == STRING)
1180 free(root->params[i].data.text);
1181 if(root->params_count > 0)
1182 free(root->params);
1184 /* Finally, delete root's memory */
1185 free(root);
1186 #else
1187 (void)root;
1188 #endif