blob: 2ce41c6d9a26a9517859bd9488bbf4134e037b12 [file] [log] [blame]
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2010 Robert Bieber
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <ctype.h>
26
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +000027#include "skin_buffer.h"
Robert Bieberd5b24dd2010-05-25 15:19:52 +000028#include "skin_parser.h"
29#include "skin_debug.h"
30#include "tag_table.h"
31#include "symbols.h"
32#include "skin_scan.h"
33
Robert Bieberd5b24dd2010-05-25 15:19:52 +000034/* Global variables for the parser */
35int skin_line = 0;
Robert Bieber4429fb02010-07-02 08:05:03 +000036int viewport_line = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +000037
38/* Auxiliary parsing functions (not visible at global scope) */
Robert Bieber64321ad2010-06-10 21:02:44 +000039static struct skin_element* skin_parse_viewport(char** document);
40static struct skin_element* skin_parse_line(char** document);
41static struct skin_element* skin_parse_line_optional(char** document,
42 int conditional);
43static struct skin_element* skin_parse_sublines(char** document);
44static struct skin_element* skin_parse_sublines_optional(char** document,
45 int conditional);
Robert Bieberd5b24dd2010-05-25 15:19:52 +000046
Robert Bieber64321ad2010-06-10 21:02:44 +000047static int skin_parse_tag(struct skin_element* element, char** document);
48static int skin_parse_text(struct skin_element* element, char** document,
49 int conditional);
50static int skin_parse_conditional(struct skin_element* element,
51 char** document);
52static int skin_parse_comment(struct skin_element* element, char** document);
53static struct skin_element* skin_parse_code_as_arg(char** document);
Robert Bieberd5b24dd2010-05-25 15:19:52 +000054
Jonathan Gordon95b0cef2010-06-17 13:54:09 +000055
56
Robert Biebera9848ce2010-06-01 21:25:02 +000057struct skin_element* skin_parse(const char* document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +000058{
Robert Bieberd1659d62010-06-01 07:11:23 +000059
Robert Bieberd5b24dd2010-05-25 15:19:52 +000060 struct skin_element* root = NULL;
61 struct skin_element* last = NULL;
62
63 struct skin_element** to_write = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +000064
Robert Biebera9848ce2010-06-01 21:25:02 +000065 char* cursor = (char*)document; /*Keeps track of location in the document*/
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +000066
Robert Bieberd5b24dd2010-05-25 15:19:52 +000067 skin_line = 1;
Robert Bieber4429fb02010-07-02 08:05:03 +000068 viewport_line = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +000069
Robert Bieber594d7342010-06-07 21:09:13 +000070 skin_clear_errors();
71
Robert Bieberd5b24dd2010-05-25 15:19:52 +000072 while(*cursor != '\0')
73 {
74
Robert Bieberd1659d62010-06-01 07:11:23 +000075 if(!root)
76 to_write = &root;
77 else
78 to_write = &(last->next);
79
80
81 *to_write = skin_parse_viewport(&cursor);
82 last = *to_write;
83 if(!last)
Robert Bieber6c9b0352010-06-08 19:34:27 +000084 {
85 skin_free_tree(root); /* Clearing any memory already used */
Robert Bieberd1659d62010-06-01 07:11:23 +000086 return NULL;
Robert Bieber6c9b0352010-06-08 19:34:27 +000087 }
Robert Bieberd1659d62010-06-01 07:11:23 +000088
89 /* Making sure last is at the end */
90 while(last->next)
91 last = last->next;
92
93 }
94
95 return root;
96
97}
98
Robert Bieber64321ad2010-06-10 21:02:44 +000099static struct skin_element* skin_parse_viewport(char** document)
Robert Bieberd1659d62010-06-01 07:11:23 +0000100{
101
102 struct skin_element* root = NULL;
103 struct skin_element* last = NULL;
104 struct skin_element* retval = NULL;
105
106 retval = skin_alloc_element();
107 retval->type = VIEWPORT;
Robert Bieberd1659d62010-06-01 07:11:23 +0000108 retval->children_count = 1;
109 retval->line = skin_line;
Robert Bieber4429fb02010-07-02 08:05:03 +0000110 viewport_line = skin_line;
Robert Bieberd1659d62010-06-01 07:11:23 +0000111
112 struct skin_element** to_write = 0;
113
114 char* cursor = *document; /* Keeps track of location in the document */
115 char* bookmark; /* Used when we need to look ahead */
116
117 int sublines = 0; /* Flag for parsing sublines */
118
Robert Bieber47cd8782010-06-01 18:31:58 +0000119 /* Parsing out the viewport tag if there is one */
120 if(check_viewport(cursor))
121 {
Robert Bieber1b613f52010-06-11 08:03:32 +0000122 skin_parse_tag(retval, &cursor);
Robert Bieber7f10b032010-06-02 07:04:33 +0000123 if(*cursor == '\n')
124 {
125 cursor++;
126 skin_line++;
127 }
Robert Bieber47cd8782010-06-01 18:31:58 +0000128 }
Robert Bieber1b613f52010-06-11 08:03:32 +0000129
130 retval->children_count = 1;
131 retval->children = skin_alloc_children(1);
Robert Bieber47cd8782010-06-01 18:31:58 +0000132
133
Robert Bieber1b613f52010-06-11 08:03:32 +0000134 do
Robert Bieberd1659d62010-06-01 07:11:23 +0000135 {
136
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000137 /* First, we check to see if this line will contain sublines */
138 bookmark = cursor;
139 sublines = 0;
Robert Bieberd1659d62010-06-01 07:11:23 +0000140 while(*cursor != '\n' && *cursor != '\0'
141 && !(check_viewport(cursor) && cursor != *document))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000142 {
143 if(*cursor == MULTILINESYM)
144 {
145 sublines = 1;
146 break;
147 }
148 else if(*cursor == TAGSYM)
149 {
150 /* A ';' directly after a '%' doesn't count */
151 cursor ++;
152
153 if(*cursor == '\0')
154 break;
155
156 cursor++;
157 }
Robert Bieberf9f6f902010-05-30 01:56:50 +0000158 else if(*cursor == COMMENTSYM)
159 {
160 skip_comment(&cursor);
161 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000162 else if(*cursor == ARGLISTOPENSYM)
163 {
164 skip_arglist(&cursor);
165 }
166 else if(*cursor == ENUMLISTOPENSYM)
167 {
Robert Bieber4b77e822010-06-10 21:22:16 +0000168 skip_enumlist(&cursor);
Robert Bieber06ea93d2010-06-07 23:49:06 +0000169 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000170 else
171 {
172 /* Advancing the cursor as normal */
173 cursor++;
174 }
175 }
176 cursor = bookmark;
177
178 if(!root)
179 to_write = &root;
180 else
181 to_write = &(last->next);
182
Robert Biebere25c9032010-06-02 06:41:41 +0000183 if(sublines)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000184 {
185 *to_write = skin_parse_sublines(&cursor);
186 last = *to_write;
187 if(!last)
188 return NULL;
189 }
190 else
191 {
192
193 *to_write = skin_parse_line(&cursor);
194 last = *to_write;
195 if(!last)
196 return NULL;
197
198 }
199
200 /* Making sure last is at the end */
201 while(last->next)
202 last = last->next;
203
Robert Biebere25c9032010-06-02 06:41:41 +0000204 if(*cursor == '\n')
Robert Bieber7f10b032010-06-02 07:04:33 +0000205 {
Robert Biebere25c9032010-06-02 06:41:41 +0000206 cursor++;
Robert Bieber7f10b032010-06-02 07:04:33 +0000207 skin_line++;
208 }
Robert Bieberd1659d62010-06-01 07:11:23 +0000209 }
Robert Bieber1b613f52010-06-11 08:03:32 +0000210 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000211
Robert Bieberd1659d62010-06-01 07:11:23 +0000212 *document = cursor;
213
Robert Bieber1b613f52010-06-11 08:03:32 +0000214 retval->children[0] = root;
Robert Bieberd1659d62010-06-01 07:11:23 +0000215 return retval;
216
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000217}
218
219/* Auxiliary Parsing Functions */
220
Robert Bieber64321ad2010-06-10 21:02:44 +0000221static struct skin_element* skin_parse_line(char**document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000222{
223
224 return skin_parse_line_optional(document, 0);
225
226}
227
228
229/*
230 * If conditional is set to true, then this will break upon encountering
231 * SEPERATESYM. This should only be used when parsing a line inside a
232 * conditional, otherwise just use the wrapper function skin_parse_line()
233 */
Robert Bieber64321ad2010-06-10 21:02:44 +0000234static struct skin_element* skin_parse_line_optional(char** document,
235 int conditional)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000236{
237 char* cursor = *document;
238
239 struct skin_element* root = NULL;
240 struct skin_element* current = NULL;
Robert Bieber8ea056d2010-05-27 19:57:15 +0000241 struct skin_element* retval = NULL;
242
243 /* A wrapper for the line */
244 retval = skin_alloc_element();
245 retval->type = LINE;
246 retval->line = skin_line;
Robert Bieber09db2ae2010-06-22 18:51:44 +0000247 if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
Robert Bieber3c95dbb2010-06-11 19:51:34 +0000248 && !(conditional && (*cursor == ARGLISTSEPERATESYM
249 || *cursor == ARGLISTCLOSESYM
250 || *cursor == ENUMLISTSEPERATESYM
251 || *cursor == ENUMLISTCLOSESYM)))
252 {
Robert Bieber1b613f52010-06-11 08:03:32 +0000253 retval->children_count = 1;
Robert Bieber3c95dbb2010-06-11 19:51:34 +0000254 }
255 else
256 {
257 retval->children_count = 0;
258 }
259
Robert Bieber1b613f52010-06-11 08:03:32 +0000260 if(retval->children_count > 0)
261 retval->children = skin_alloc_children(1);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000262
263 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000264 && !((*cursor == ARGLISTSEPERATESYM
265 || *cursor == ARGLISTCLOSESYM
266 || *cursor == ENUMLISTSEPERATESYM
267 || *cursor == ENUMLISTCLOSESYM)
Robert Bieberd1659d62010-06-01 07:11:23 +0000268 && conditional)
269 && !(check_viewport(cursor) && cursor != *document))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000270 {
271 /* Allocating memory if necessary */
272 if(root)
273 {
274 current->next = skin_alloc_element();
275 current = current->next;
276 }
277 else
278 {
279 current = skin_alloc_element();
280 root = current;
281 }
282
283 /* Parsing the current element */
284 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
285 {
286 if(!skin_parse_conditional(current, &cursor))
287 return NULL;
288 }
Robert Bieber999990c2010-06-02 05:45:34 +0000289 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000290 {
291 if(!skin_parse_tag(current, &cursor))
292 return NULL;
293 }
294 else if(*cursor == COMMENTSYM)
295 {
296 if(!skin_parse_comment(current, &cursor))
297 return NULL;
298 }
299 else
300 {
301 if(!skin_parse_text(current, &cursor, conditional))
302 return NULL;
303 }
304 }
305
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000306 /* Moving up the calling function's pointer */
307 *document = cursor;
308
Robert Bieber1b613f52010-06-11 08:03:32 +0000309 if(root)
310 retval->children[0] = root;
Robert Bieber8ea056d2010-05-27 19:57:15 +0000311 return retval;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000312}
313
Robert Bieber64321ad2010-06-10 21:02:44 +0000314static struct skin_element* skin_parse_sublines(char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000315{
316 return skin_parse_sublines_optional(document, 0);
317}
318
Robert Bieber64321ad2010-06-10 21:02:44 +0000319static struct skin_element* skin_parse_sublines_optional(char** document,
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000320 int conditional)
321{
322 struct skin_element* retval;
323 char* cursor = *document;
324 int sublines = 1;
325 int i;
326
327 retval = skin_alloc_element();
Jonathan Gordondc347852010-07-04 02:04:14 +0000328 retval->type = LINE_ALTERNATOR;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000329 retval->next = NULL;
Robert Bieber8ea056d2010-05-27 19:57:15 +0000330 retval->line = skin_line;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000331
332 /* First we count the sublines */
333 while(*cursor != '\0' && *cursor != '\n'
Robert Bieber1937b1b2010-05-25 17:22:39 +0000334 && !((*cursor == ARGLISTSEPERATESYM
335 || *cursor == ARGLISTCLOSESYM
336 || *cursor == ENUMLISTSEPERATESYM
337 || *cursor == ENUMLISTCLOSESYM)
Robert Bieberd1659d62010-06-01 07:11:23 +0000338 && conditional)
339 && !(check_viewport(cursor) && cursor != *document))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000340 {
341 if(*cursor == COMMENTSYM)
Robert Bieber496bcf32010-06-02 07:48:48 +0000342 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000343 skip_comment(&cursor);
Robert Bieber496bcf32010-06-02 07:48:48 +0000344 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000345 else if(*cursor == ENUMLISTOPENSYM)
Robert Bieber496bcf32010-06-02 07:48:48 +0000346 {
Robert Bieber06ea93d2010-06-07 23:49:06 +0000347 skip_enumlist(&cursor);
Robert Bieber496bcf32010-06-02 07:48:48 +0000348 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000349 else if(*cursor == ARGLISTOPENSYM)
350 {
351 skip_arglist(&cursor);
352 }
353 else if(*cursor == TAGSYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000354 {
355 cursor++;
356 if(*cursor == '\0' || *cursor == '\n')
357 break;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000358 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000359 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000360 else if(*cursor == MULTILINESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000361 {
362 sublines++;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000363 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000364 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000365 else
366 {
367 cursor++;
368 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000369 }
370
371 /* ...and then we parse them */
372 retval->children_count = sublines;
373 retval->children = skin_alloc_children(sublines);
374
375 cursor = *document;
376 for(i = 0; i < sublines; i++)
377 {
378 retval->children[i] = skin_parse_line_optional(&cursor, conditional);
379 skip_whitespace(&cursor);
380
381 if(*cursor != MULTILINESYM && i != sublines - 1)
382 {
383 skin_error(MULTILINE_EXPECTED);
384 return NULL;
385 }
Robert Bieber8ea056d2010-05-27 19:57:15 +0000386 else if(i != sublines - 1)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000387 {
388 cursor++;
389 }
390 }
391
392 *document = cursor;
393
394 return retval;
395}
396
Robert Bieber64321ad2010-06-10 21:02:44 +0000397static int skin_parse_tag(struct skin_element* element, char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000398{
399
400 char* cursor = *document + 1;
401 char* bookmark;
402
403 char tag_name[3];
404 char* tag_args;
Robert Bieber0a054b22010-06-01 16:44:52 +0000405 struct tag_info *tag;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000406
407 int num_args = 1;
408 int i;
Robert Bieber1937b1b2010-05-25 17:22:39 +0000409 int star = 0; /* Flag for the all-or-none option */
410 int req_args; /* To mark when we enter optional arguments */
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000411
412 int optional = 0;
413
414 /* Checking the tag name */
415 tag_name[0] = cursor[0];
416 tag_name[1] = cursor[1];
417 tag_name[2] = '\0';
418
419 /* First we check the two characters after the '%', then a single char */
Robert Bieber0a054b22010-06-01 16:44:52 +0000420 tag = find_tag(tag_name);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000421
Robert Bieber0a054b22010-06-01 16:44:52 +0000422 if(!tag)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000423 {
424 tag_name[1] = '\0';
Robert Bieber0a054b22010-06-01 16:44:52 +0000425 tag = find_tag(tag_name);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000426 cursor++;
427 }
428 else
429 {
430 cursor += 2;
431 }
432
Robert Bieber0a054b22010-06-01 16:44:52 +0000433 if(!tag)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000434 {
435 skin_error(ILLEGAL_TAG);
436 return 0;
437 }
438
439 /* Copying basic tag info */
Robert Bieber1b613f52010-06-11 08:03:32 +0000440 if(element->type != CONDITIONAL && element->type != VIEWPORT)
Robert Bieber64321ad2010-06-10 21:02:44 +0000441 element->type = TAG;
Robert Bieber0a054b22010-06-01 16:44:52 +0000442 element->tag = tag;
443 tag_args = tag->params;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000444 element->line = skin_line;
445
Robert Bieber1937b1b2010-05-25 17:22:39 +0000446 /* Checking for the * flag */
447 if(tag_args[0] == '*')
448 {
449 star = 1;
450 tag_args++;
451 }
452
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000453 /* If this tag has no arguments, we can bail out now */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000454 if(strlen(tag_args) == 0
Robert Bieberf02a2442010-06-02 05:27:37 +0000455 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
456 || (star && *cursor != ARGLISTOPENSYM))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000457 {
458 *document = cursor;
459 return 1;
460 }
461
Robert Bieber7dfd0c02010-07-01 08:45:49 +0000462
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000463 /* Checking the number of arguments and allocating args */
Robert Bieber32ff4e92010-07-04 01:39:57 +0000464 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000465 {
466 skin_error(ARGLIST_EXPECTED);
467 return 0;
468 }
469 else
470 {
471 cursor++;
472 }
473
Robert Bieber06ea93d2010-06-07 23:49:06 +0000474 bookmark = cursor;
475 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000476 {
477 /* Skipping over escaped characters */
478 if(*cursor == TAGSYM)
479 {
480 cursor++;
481 if(*cursor == '\0')
482 break;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000483 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000484 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000485 else if(*cursor == COMMENTSYM)
486 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000487 skip_comment(&cursor);
Robert Bieber06ea93d2010-06-07 23:49:06 +0000488 }
489 else if(*cursor == ARGLISTOPENSYM)
490 {
491 skip_arglist(&cursor);
492 }
493 else if(*cursor == ARGLISTSEPERATESYM)
494 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000495 num_args++;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000496 cursor++;
497 }
498 else
499 {
500 cursor++;
501 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000502 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000503
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000504 cursor = bookmark; /* Restoring the cursor */
505 element->params_count = num_args;
506 element->params = skin_alloc_params(num_args);
507
508 /* Now we have to actually parse each argument */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000509 for(i = 0; i < num_args; i++)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000510 {
511 /* Making sure we haven't run out of arguments */
Robert Bieber32ff4e92010-07-04 01:39:57 +0000512 if(*tag_args == '\0')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000513 {
514 skin_error(TOO_MANY_ARGS);
515 return 0;
516 }
517
518 /* Checking for the optional bar */
519 if(*tag_args == '|')
520 {
521 optional = 1;
Robert Bieberd1659d62010-06-01 07:11:23 +0000522 req_args = i;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000523 tag_args++;
524 }
525
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000526 /* Scanning the arguments */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000527 skip_whitespace(&cursor);
528
529
530 /* Checking for comments */
531 if(*cursor == COMMENTSYM)
532 skip_comment(&cursor);
533
Robert Bieber5943f4c2010-06-01 19:55:20 +0000534 /* Storing the type code */
535 element->params[i].type_code = *tag_args;
536
Robert Bieber1937b1b2010-05-25 17:22:39 +0000537 /* Checking a nullable argument for null */
Robert Bieberea864be2010-06-02 06:52:17 +0000538 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000539 {
Robert Bieber1937b1b2010-05-25 17:22:39 +0000540 if(islower(*tag_args))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000541 {
Robert Bieber1937b1b2010-05-25 17:22:39 +0000542 element->params[i].type = DEFAULT;
543 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000544 }
545 else
546 {
Robert Bieber1937b1b2010-05-25 17:22:39 +0000547 skin_error(DEFAULT_NOT_ALLOWED);
548 return 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000549 }
550 }
Robert Bieber1937b1b2010-05-25 17:22:39 +0000551 else if(tolower(*tag_args) == 'i')
552 {
553 /* Scanning an int argument */
Robert Bieberea864be2010-06-02 06:52:17 +0000554 if(!isdigit(*cursor) && *cursor != '-')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000555 {
556 skin_error(INT_EXPECTED);
557 return 0;
558 }
559
560 element->params[i].type = NUMERIC;
561 element->params[i].data.numeric = scan_int(&cursor);
562 }
Jonathan Gordon9ac4b442010-06-09 16:15:01 +0000563 else if(tolower(*tag_args) == 'n' ||
564 tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000565 {
566 /* Scanning a string argument */
567 element->params[i].type = STRING;
568 element->params[i].data.text = scan_string(&cursor);
569
570 }
571 else if(tolower(*tag_args) == 'c')
572 {
573 /* Recursively parsing a code argument */
574 element->params[i].type = CODE;
575 element->params[i].data.code = skin_parse_code_as_arg(&cursor);
576 if(!element->params[i].data.code)
577 return 0;
578 }
579
580 skip_whitespace(&cursor);
581
582 if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
583 {
584 skin_error(SEPERATOR_EXPECTED);
585 return 0;
586 }
587 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
588 {
589 skin_error(CLOSE_EXPECTED);
590 return 0;
591 }
592 else
593 {
594 cursor++;
595 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000596
Jonathan Gordon9ac4b442010-06-09 16:15:01 +0000597 if (*tag_args != 'N')
598 tag_args++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000599
Robert Bieberd1659d62010-06-01 07:11:23 +0000600 /* Checking for the optional bar */
601 if(*tag_args == '|')
602 {
603 optional = 1;
604 req_args = i + 1;
605 tag_args++;
606 }
607
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000608 }
609
Robert Bieber1937b1b2010-05-25 17:22:39 +0000610 /* Checking for a premature end */
Robert Bieber32ff4e92010-07-04 01:39:57 +0000611 if(*tag_args != '\0' && !optional)
Robert Bieber1937b1b2010-05-25 17:22:39 +0000612 {
613 skin_error(INSUFFICIENT_ARGS);
614 return 0;
615 }
Jonathan Gordonaa1a1262010-07-01 10:17:41 +0000616
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000617 *document = cursor;
618
619 return 1;
620}
621
622/*
623 * If the conditional flag is set true, then parsing text will stop at an
624 * ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
625 */
Robert Bieber64321ad2010-06-10 21:02:44 +0000626static int skin_parse_text(struct skin_element* element, char** document,
627 int conditional)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000628{
629 char* cursor = *document;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000630 int length = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000631 int dest;
Robert Bieber64321ad2010-06-10 21:02:44 +0000632 char *text = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000633
634 /* First figure out how much text we're copying */
635 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
636 && *cursor != COMMENTSYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000637 && !((*cursor == ARGLISTSEPERATESYM
638 || *cursor == ARGLISTCLOSESYM
639 || *cursor == ENUMLISTSEPERATESYM
640 || *cursor == ENUMLISTCLOSESYM)
641 && conditional))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000642 {
643 /* Dealing with possibility of escaped characters */
644 if(*cursor == TAGSYM)
645 {
646 if(find_escape_character(cursor[1]))
647 cursor++;
648 else
649 break;
650 }
651
652 length++;
653 cursor++;
654 }
655
656 cursor = *document;
657
658 /* Copying the text into the element struct */
659 element->type = TEXT;
660 element->line = skin_line;
661 element->next = NULL;
Robert Bieber64321ad2010-06-10 21:02:44 +0000662 element->data = text = skin_alloc_string(length);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000663
664 for(dest = 0; dest < length; dest++)
665 {
666 /* Advancing cursor if we've encountered an escaped character */
667 if(*cursor == TAGSYM)
668 cursor++;
669
Robert Bieber64321ad2010-06-10 21:02:44 +0000670 text[dest] = *cursor;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000671 cursor++;
672 }
Robert Bieber64321ad2010-06-10 21:02:44 +0000673 text[length] = '\0';
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000674
675 *document = cursor;
676
677 return 1;
678}
679
Robert Bieber64321ad2010-06-10 21:02:44 +0000680static int skin_parse_conditional(struct skin_element* element, char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000681{
682
683 char* cursor = *document + 1; /* Starting past the "%" */
684 char* bookmark;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000685 int children = 1;
686 int i;
687
688 element->type = CONDITIONAL;
689 element->line = skin_line;
690
691 /* Parsing the tag first */
Robert Bieber64321ad2010-06-10 21:02:44 +0000692 if(!skin_parse_tag(element, &cursor))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000693 return 0;
694
695 /* Counting the children */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000696 if(*(cursor++) != ENUMLISTOPENSYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000697 {
698 skin_error(ARGLIST_EXPECTED);
699 return 0;
700 }
701 bookmark = cursor;
Robert Bieber1937b1b2010-05-25 17:22:39 +0000702 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000703 {
704 if(*cursor == COMMENTSYM)
705 {
706 skip_comment(&cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000707 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000708 else if(*cursor == ENUMLISTOPENSYM)
Robert Bieber496bcf32010-06-02 07:48:48 +0000709 {
Robert Bieber06ea93d2010-06-07 23:49:06 +0000710 skip_enumlist(&cursor);
Robert Bieber496bcf32010-06-02 07:48:48 +0000711 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000712 else if(*cursor == TAGSYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000713 {
714 cursor++;
715 if(*cursor == '\0' || *cursor == '\n')
716 break;
717 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000718 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000719 else if(*cursor == ENUMLISTSEPERATESYM)
720 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000721 children++;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000722 cursor++;
723 }
724 else
725 {
726 cursor++;
727 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000728 }
729 cursor = bookmark;
730
731 /* Parsing the children */
Robert Bieber64321ad2010-06-10 21:02:44 +0000732 element->children = skin_alloc_children(children);
733 element->children_count = children;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000734
Robert Bieber64321ad2010-06-10 21:02:44 +0000735 for(i = 0; i < children; i++)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000736 {
737 element->children[i] = skin_parse_code_as_arg(&cursor);
738 skip_whitespace(&cursor);
739
Robert Bieber64321ad2010-06-10 21:02:44 +0000740 if(i < children - 1 && *cursor != ENUMLISTSEPERATESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000741 {
742 skin_error(SEPERATOR_EXPECTED);
743 return 0;
744 }
Robert Bieber64321ad2010-06-10 21:02:44 +0000745 else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000746 {
747 skin_error(CLOSE_EXPECTED);
748 return 0;
749 }
750 else
751 {
752 cursor++;
753 }
754 }
755
756 *document = cursor;
757
758 return 1;
759}
760
Robert Bieber64321ad2010-06-10 21:02:44 +0000761static int skin_parse_comment(struct skin_element* element, char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000762{
763 char* cursor = *document;
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +0000764#ifndef ROCKBOX
Robert Bieber64321ad2010-06-10 21:02:44 +0000765 char* text = NULL;
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +0000766#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000767 int length;
768 /*
769 * Finding the index of the ending newline or null-terminator
770 * The length of the string of interest doesn't include the leading #, the
771 * length we need to reserve is the same as the index of the last character
772 */
773 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
774
775 element->type = COMMENT;
776 element->line = skin_line;
Robert Bieber64321ad2010-06-10 21:02:44 +0000777#ifdef ROCKBOX
778 element->data = NULL;
779#else
780 element->data = text = skin_alloc_string(length);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000781 /* We copy from one char past cursor to leave out the # */
Robert Bieber64321ad2010-06-10 21:02:44 +0000782 memcpy((void*)text, (void*)(cursor + 1),
Robert Biebera9848ce2010-06-01 21:25:02 +0000783 sizeof(char) * (length-1));
Robert Bieber64321ad2010-06-10 21:02:44 +0000784 text[length - 1] = '\0';
785#endif
Robert Biebera9848ce2010-06-01 21:25:02 +0000786 if(cursor[length] == '\n')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000787 skin_line++;
788
Robert Biebera9848ce2010-06-01 21:25:02 +0000789 *document += (length); /* Move cursor up past # and all text */
790 if(**document == '\n')
791 (*document)++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000792
793 return 1;
794}
795
Robert Bieber64321ad2010-06-10 21:02:44 +0000796static struct skin_element* skin_parse_code_as_arg(char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000797{
798
799 int sublines = 0;
800 char* cursor = *document;
801
802 /* Checking for sublines */
Robert Bieber4b77e822010-06-10 21:22:16 +0000803 while(*cursor != '\n' && *cursor != '\0'
804 && *cursor != ENUMLISTSEPERATESYM && *cursor != ARGLISTSEPERATESYM
805 && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000806 {
807 if(*cursor == MULTILINESYM)
808 {
809 sublines = 1;
810 break;
811 }
812 else if(*cursor == TAGSYM)
813 {
814 /* A ';' directly after a '%' doesn't count */
815 cursor ++;
816
817 if(*cursor == '\0')
818 break;
819
820 cursor++;
821 }
Robert Bieber4b77e822010-06-10 21:22:16 +0000822 else if(*cursor == ARGLISTOPENSYM)
823 {
824 skip_arglist(&cursor);
825 }
826 else if(*cursor == ENUMLISTOPENSYM)
827 {
828 skip_enumlist(&cursor);
829 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000830 else
831 {
832 /* Advancing the cursor as normal */
833 cursor++;
834 }
835 }
836
837 if(sublines)
838 return skin_parse_sublines_optional(document, 1);
839 else
840 return skin_parse_line_optional(document, 1);
841}
842
843
844/* Memory management */
845struct skin_element* skin_alloc_element()
846{
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000847 struct skin_element* retval = (struct skin_element*)
Jonathan Gordon95b0cef2010-06-17 13:54:09 +0000848 skin_buffer_alloc(sizeof(struct skin_element));
Jonathan Gordon35b09cb2010-06-13 03:13:01 +0000849 retval->type = UNKNOWN;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000850 retval->next = NULL;
Robert Bieber1b613f52010-06-11 08:03:32 +0000851 retval->tag = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000852 retval->params_count = 0;
853 retval->children_count = 0;
854
855 return retval;
856
857}
858
859struct skin_tag_parameter* skin_alloc_params(int count)
860{
Robert Bieber64321ad2010-06-10 21:02:44 +0000861 size_t size = sizeof(struct skin_tag_parameter) * count;
Jonathan Gordon95b0cef2010-06-17 13:54:09 +0000862 return (struct skin_tag_parameter*)skin_buffer_alloc(size);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000863
864}
865
866char* skin_alloc_string(int length)
867{
Jonathan Gordon95b0cef2010-06-17 13:54:09 +0000868 return (char*)skin_buffer_alloc(sizeof(char) * (length + 1));
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000869}
870
871struct skin_element** skin_alloc_children(int count)
872{
Robert Bieber64321ad2010-06-10 21:02:44 +0000873 return (struct skin_element**)
Jonathan Gordon95b0cef2010-06-17 13:54:09 +0000874 skin_buffer_alloc(sizeof(struct skin_element*) * count);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000875}
Robert Bieber0769fc52010-05-25 22:24:08 +0000876
877void skin_free_tree(struct skin_element* root)
878{
Robert Bieber64321ad2010-06-10 21:02:44 +0000879#ifndef ROCKBOX
Robert Bieber0769fc52010-05-25 22:24:08 +0000880 int i;
881
882 /* First make the recursive call */
883 if(!root)
884 return;
885 skin_free_tree(root->next);
886
887 /* Free any text */
Robert Bieber64321ad2010-06-10 21:02:44 +0000888 if(root->type == TEXT || root->type == COMMENT)
889 free(root->data);
Robert Bieber0769fc52010-05-25 22:24:08 +0000890
891 /* Then recursively free any children, before freeing their pointers */
892 for(i = 0; i < root->children_count; i++)
893 skin_free_tree(root->children[i]);
894 if(root->children_count > 0)
895 free(root->children);
896
897 /* Free any parameters, making sure to deallocate strings */
898 for(i = 0; i < root->params_count; i++)
899 if(root->params[i].type == STRING)
900 free(root->params[i].data.text);
901 if(root->params_count > 0)
902 free(root->params);
903
904 /* Finally, delete root's memory */
905 free(root);
Robert Bieber64321ad2010-06-10 21:02:44 +0000906#else
907 (void)root;
908#endif
Robert Bieber0769fc52010-05-25 22:24:08 +0000909}