1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
27 #include "skin_buffer.h"
28 #include "skin_parser.h"
29 #include "skin_debug.h"
30 #include "tag_table.h"
32 #include "skin_scan.h"
34 /* Global variables for the parser */
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
,
42 static struct skin_element
* skin_parse_sublines(char** document
);
43 static struct skin_element
* skin_parse_sublines_optional(char** document
,
46 static int skin_parse_tag(struct skin_element
* element
, char** document
);
47 static int skin_parse_text(struct skin_element
* element
, char** document
,
49 static int skin_parse_conditional(struct skin_element
* element
,
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*/
70 while(*cursor
!= '\0')
76 to_write
= &(last
->next
);
79 *to_write
= skin_parse_viewport(&cursor
);
83 skin_free_tree(root
); /* Clearing any memory already used */
87 /* Making sure last is at the end */
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
);
127 retval
->children_count
= 1;
128 retval
->children
= skin_alloc_children(1);
134 /* First, we check to see if this line will contain sublines */
137 while(*cursor
!= '\n' && *cursor
!= '\0'
138 && !(check_viewport(cursor
) && cursor
!= *document
))
140 if(*cursor
== MULTILINESYM
)
145 else if(*cursor
== TAGSYM
)
147 /* A ';' directly after a '%' doesn't count */
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
);
169 /* Advancing the cursor as normal */
178 to_write
= &(last
->next
);
182 *to_write
= skin_parse_sublines(&cursor
);
190 *to_write
= skin_parse_line(&cursor
);
197 /* Making sure last is at the end */
207 while(*cursor
!= '\0' && !(check_viewport(cursor
) && cursor
!= *document
));
211 retval
->children
[0] = root
;
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
,
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();
243 retval
->line
= skin_line
;
244 if(*cursor
!= '\0' && *cursor
!= '\n' && *cursor
!= MULTILINESYM
245 && !(conditional
&& (*cursor
== ARGLISTSEPERATESYM
246 || *cursor
== ARGLISTCLOSESYM
247 || *cursor
== ENUMLISTSEPERATESYM
248 || *cursor
== ENUMLISTCLOSESYM
)))
250 retval
->children_count
= 1;
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
)
266 && !(check_viewport(cursor
) && cursor
!= *document
))
268 /* Allocating memory if necessary */
271 current
->next
= skin_alloc_element();
272 current
= current
->next
;
276 current
= skin_alloc_element();
280 /* Parsing the current element */
281 if(*cursor
== TAGSYM
&& cursor
[1] == CONDITIONSYM
)
283 if(!skin_parse_conditional(current
, &cursor
))
286 else if(*cursor
== TAGSYM
&& !find_escape_character(cursor
[1]))
288 if(!skin_parse_tag(current
, &cursor
))
291 else if(*cursor
== COMMENTSYM
)
293 if(!skin_parse_comment(current
, &cursor
))
298 if(!skin_parse_text(current
, &cursor
, conditional
))
303 /* Moving up the calling function's pointer */
307 retval
->children
[0] = root
;
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
,
319 struct skin_element
* retval
;
320 char* cursor
= *document
;
324 retval
= skin_alloc_element();
325 retval
->type
= SUBLINES
;
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
)
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
)
353 if(*cursor
== '\0' || *cursor
== '\n')
357 else if(*cursor
== MULTILINESYM
)
368 /* ...and then we parse them */
369 retval
->children_count
= sublines
;
370 retval
->children
= skin_alloc_children(sublines
);
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
);
383 else if(i
!= sublines
- 1)
394 static int skin_parse_tag(struct skin_element
* element
, char** document
)
397 char* cursor
= *document
+ 1;
402 struct tag_info
*tag
;
406 int star
= 0; /* Flag for the all-or-none option */
407 int req_args
; /* To mark when we enter optional arguments */
411 /* Checking the tag name */
412 tag_name
[0] = cursor
[0];
413 tag_name
[1] = cursor
[1];
416 /* First we check the two characters after the '%', then a single char */
417 tag
= find_tag(tag_name
);
422 tag
= find_tag(tag_name
);
432 skin_error(ILLEGAL_TAG
);
436 /* Copying basic tag info */
437 if(element
->type
!= CONDITIONAL
&& element
->type
!= VIEWPORT
)
440 tag_args
= tag
->params
;
441 element
->line
= skin_line
;
443 /* Checking for the * flag */
444 if(tag_args
[0] == '*')
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
))
459 /* Checking the number of arguments and allocating args */
460 if(*cursor
!= ARGLISTOPENSYM
&& tag_args
[0] != '|')
462 skin_error(ARGLIST_EXPECTED
);
471 while(*cursor
!= '\n' && *cursor
!= '\0' && *cursor
!= ARGLISTCLOSESYM
)
473 /* Skipping over escaped characters */
474 if(*cursor
== TAGSYM
)
481 else if(*cursor
== COMMENTSYM
)
483 skip_comment(&cursor
);
485 else if(*cursor
== ARGLISTOPENSYM
)
487 skip_arglist(&cursor
);
489 else if(*cursor
== ARGLISTSEPERATESYM
)
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
);
514 /* Checking for the optional bar */
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
;
543 skin_error(DEFAULT_NOT_ALLOWED
);
547 else if(tolower(*tag_args
) == 'i')
549 /* Scanning an int argument */
550 if(!isdigit(*cursor
) && *cursor
!= '-')
552 skin_error(INT_EXPECTED
);
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
)
576 skip_whitespace(&cursor
);
578 if(*cursor
!= ARGLISTSEPERATESYM
&& i
< num_args
- 1)
580 skin_error(SEPERATOR_EXPECTED
);
583 else if(*cursor
!= ARGLISTCLOSESYM
&& i
== num_args
- 1)
585 skin_error(CLOSE_EXPECTED
);
593 if (*tag_args
!= 'N')
596 /* Checking for the optional bar */
606 /* Checking for a premature end */
607 if(*tag_args
!= '\0' && !optional
)
609 skin_error(INSUFFICIENT_ARGS
);
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
,
625 char* cursor
= *document
;
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
)
639 /* Dealing with possibility of escaped characters */
640 if(*cursor
== TAGSYM
)
642 if(find_escape_character(cursor
[1]))
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
)
666 text
[dest
] = *cursor
;
676 static int skin_parse_conditional(struct skin_element
* element
, char** document
)
679 char* cursor
= *document
+ 1; /* Starting past the "%" */
684 element
->type
= CONDITIONAL
;
685 element
->line
= skin_line
;
687 /* Parsing the tag first */
688 if(!skin_parse_tag(element
, &cursor
))
691 /* Counting the children */
692 if(*(cursor
++) != ENUMLISTOPENSYM
)
694 skin_error(ARGLIST_EXPECTED
);
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
)
711 if(*cursor
== '\0' || *cursor
== '\n')
715 else if(*cursor
== ENUMLISTSEPERATESYM
)
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
);
741 else if(i
== children
- 1 && *cursor
!= ENUMLISTCLOSESYM
)
743 skin_error(CLOSE_EXPECTED
);
757 static int skin_parse_comment(struct skin_element
* element
, char** document
)
759 char* cursor
= *document
;
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
;
774 element
->data
= NULL
;
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';
782 if(cursor
[length
] == '\n')
785 *document
+= (length
); /* Move cursor up past # and all text */
786 if(**document
== '\n')
792 static struct skin_element
* skin_parse_code_as_arg(char** document
)
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
)
808 else if(*cursor
== TAGSYM
)
810 /* A ';' directly after a '%' doesn't count */
818 else if(*cursor
== ARGLISTOPENSYM
)
820 skip_arglist(&cursor
);
822 else if(*cursor
== ENUMLISTOPENSYM
)
824 skip_enumlist(&cursor
);
828 /* Advancing the cursor as normal */
834 return skin_parse_sublines_optional(document
, 1);
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
;
848 retval
->params_count
= 0;
849 retval
->children_count
= 0;
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
)
878 /* First make the recursive call */
881 skin_free_tree(root
->next
);
884 if(root
->type
== TEXT
|| root
->type
== COMMENT
)
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)
900 /* Finally, delete root's memory */