blob: 44a1c0324545f52451306d10fef889c3464b9d41 [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>
Jonathan Gordon2d31d772010-07-29 12:37:48 +000024#include <stdbool.h>
Robert Bieberd5b24dd2010-05-25 15:19:52 +000025#include <string.h>
26#include <ctype.h>
27
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +000028#include "skin_buffer.h"
Robert Bieberd5b24dd2010-05-25 15:19:52 +000029#include "skin_parser.h"
30#include "skin_debug.h"
31#include "tag_table.h"
32#include "symbols.h"
33#include "skin_scan.h"
34
Robert Bieberd5b24dd2010-05-25 15:19:52 +000035/* Global variables for the parser */
36int skin_line = 0;
Robert Bieber3c4fb8a2010-07-18 00:59:02 +000037char* skin_start = 0;
Robert Bieber4429fb02010-07-02 08:05:03 +000038int viewport_line = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +000039
Jonathan Gordon2d31d772010-07-29 12:37:48 +000040#ifdef ROCKBOX
41static skin_callback callback = NULL;
42static void* callback_data;
43#endif
44
Robert Bieberd5b24dd2010-05-25 15:19:52 +000045/* Auxiliary parsing functions (not visible at global scope) */
Nils Wallménius597ccdd2010-07-31 16:25:41 +000046static struct skin_element* skin_parse_viewport(const char** document);
47static struct skin_element* skin_parse_line(const char** document);
48static struct skin_element* skin_parse_line_optional(const char** document,
Robert Bieber64321ad2010-06-10 21:02:44 +000049 int conditional);
Nils Wallménius597ccdd2010-07-31 16:25:41 +000050static struct skin_element* skin_parse_sublines(const char** document);
51static struct skin_element* skin_parse_sublines_optional(const char** document,
Robert Bieber64321ad2010-06-10 21:02:44 +000052 int conditional);
Robert Bieberd5b24dd2010-05-25 15:19:52 +000053
Nils Wallménius597ccdd2010-07-31 16:25:41 +000054static int skin_parse_tag(struct skin_element* element, const char** document);
55static int skin_parse_text(struct skin_element* element, const char** document,
Robert Bieber64321ad2010-06-10 21:02:44 +000056 int conditional);
57static int skin_parse_conditional(struct skin_element* element,
Nils Wallménius597ccdd2010-07-31 16:25:41 +000058 const char** document);
59static int skin_parse_comment(struct skin_element* element, const char** document);
60static struct skin_element* skin_parse_code_as_arg(const char** document);
Robert Bieberd5b24dd2010-05-25 15:19:52 +000061
Jonathan Gordon95b0cef2010-06-17 13:54:09 +000062
Nils Wallménius597ccdd2010-07-31 16:25:41 +000063static void skip_whitespace(const char** document)
Jonathan Gordon2d31d772010-07-29 12:37:48 +000064{
65 while(**document == ' ' || **document == '\t')
66 (*document)++;
67}
Jonathan Gordon95b0cef2010-06-17 13:54:09 +000068
Jonathan Gordon2d31d772010-07-29 12:37:48 +000069#ifdef ROCKBOX
70struct skin_element* skin_parse(const char* document,
71 skin_callback cb, void* cb_data)
Jonathan Gordon2d31d772010-07-29 12:37:48 +000072{
73 callback = cb;
74 callback_data = cb_data;
75#else
Robert Biebera9848ce2010-06-01 21:25:02 +000076struct skin_element* skin_parse(const char* document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +000077{
Jonathan Gordon2d31d772010-07-29 12:37:48 +000078#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +000079 struct skin_element* root = NULL;
80 struct skin_element* last = NULL;
81
Nils Wallménius597ccdd2010-07-31 16:25:41 +000082 const char* cursor = document; /*Keeps track of location in the document*/
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +000083
Robert Bieberd5b24dd2010-05-25 15:19:52 +000084 skin_line = 1;
Robert Bieber3c4fb8a2010-07-18 00:59:02 +000085 skin_start = (char*)document;
Robert Bieber4429fb02010-07-02 08:05:03 +000086 viewport_line = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +000087
Robert Bieber594d7342010-06-07 21:09:13 +000088 skin_clear_errors();
89
Robert Bieberd5b24dd2010-05-25 15:19:52 +000090 while(*cursor != '\0')
91 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +000092 struct skin_element* tree = skin_parse_viewport(&cursor);
Robert Bieberd1659d62010-06-01 07:11:23 +000093 if(!root)
Jonathan Gordon9e07ef22011-11-15 14:11:08 +000094 {
95 root = tree;
96 last = root;
97 }
Robert Bieberd1659d62010-06-01 07:11:23 +000098 else
Jonathan Gordon9e07ef22011-11-15 14:11:08 +000099 {
100 last->next = skin_buffer_to_offset(tree);
101 last = tree;
102 }
Robert Bieberd1659d62010-06-01 07:11:23 +0000103
Robert Bieberd1659d62010-06-01 07:11:23 +0000104 if(!last)
Robert Bieber6c9b0352010-06-08 19:34:27 +0000105 {
106 skin_free_tree(root); /* Clearing any memory already used */
Robert Bieberd1659d62010-06-01 07:11:23 +0000107 return NULL;
Robert Bieber6c9b0352010-06-08 19:34:27 +0000108 }
Robert Bieberd1659d62010-06-01 07:11:23 +0000109
110 /* Making sure last is at the end */
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000111 while(IS_VALID_OFFSET(last->next))
112 last = skin_buffer_from_offset(last->next);
Robert Bieberd1659d62010-06-01 07:11:23 +0000113
114 }
Robert Bieberd1659d62010-06-01 07:11:23 +0000115 return root;
116
117}
118
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000119static struct skin_element* skin_parse_viewport(const char** document)
Robert Bieberd1659d62010-06-01 07:11:23 +0000120{
Robert Bieberd1659d62010-06-01 07:11:23 +0000121 struct skin_element* root = NULL;
122 struct skin_element* last = NULL;
123 struct skin_element* retval = NULL;
124
125 retval = skin_alloc_element();
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000126 if (!retval)
127 return NULL;
Robert Bieberd1659d62010-06-01 07:11:23 +0000128 retval->type = VIEWPORT;
Robert Bieberd1659d62010-06-01 07:11:23 +0000129 retval->children_count = 1;
130 retval->line = skin_line;
Robert Bieber4429fb02010-07-02 08:05:03 +0000131 viewport_line = skin_line;
Robert Bieberd1659d62010-06-01 07:11:23 +0000132
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000133 OFFSETTYPE(struct skin_element*)* children;
Robert Bieberd1659d62010-06-01 07:11:23 +0000134
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000135 const char* cursor = *document; /* Keeps track of location in the document */
136 const char* bookmark; /* Used when we need to look ahead */
Robert Bieberd1659d62010-06-01 07:11:23 +0000137
138 int sublines = 0; /* Flag for parsing sublines */
139
Robert Bieber47cd8782010-06-01 18:31:58 +0000140 /* Parsing out the viewport tag if there is one */
141 if(check_viewport(cursor))
142 {
Thomas Martitzd98f2922010-11-05 18:46:21 +0000143 if (!skin_parse_tag(retval, &cursor))
144 return NULL;
Robert Bieber7f10b032010-06-02 07:04:33 +0000145 if(*cursor == '\n')
146 {
147 cursor++;
148 skin_line++;
149 }
Robert Bieber47cd8782010-06-01 18:31:58 +0000150 }
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000151#ifdef ROCKBOX
152 else if (callback)
153 {
154 if (callback(retval, callback_data) == CALLBACK_ERROR)
155 return NULL;
156 }
157#endif
Robert Bieber1b613f52010-06-11 08:03:32 +0000158
Jonathan Gordond402abe2010-10-05 13:02:34 +0000159 if (check_viewport(cursor))
160 {
161 retval->children_count = 0;
162 *document = cursor;
163 return retval;
164 }
Robert Bieber1b613f52010-06-11 08:03:32 +0000165 retval->children_count = 1;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000166 children = skin_alloc_children(1);
167 if (!children)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000168 return NULL;
Robert Bieber1b613f52010-06-11 08:03:32 +0000169 do
Robert Bieberd1659d62010-06-01 07:11:23 +0000170 {
171
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000172 /* First, we check to see if this line will contain sublines */
173 bookmark = cursor;
174 sublines = 0;
Robert Bieberd1659d62010-06-01 07:11:23 +0000175 while(*cursor != '\n' && *cursor != '\0'
176 && !(check_viewport(cursor) && cursor != *document))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000177 {
178 if(*cursor == MULTILINESYM)
179 {
180 sublines = 1;
181 break;
182 }
183 else if(*cursor == TAGSYM)
184 {
Jonathan Gordon2d3c43d2012-04-21 23:34:42 +1000185 skip_tag(&cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000186 }
Robert Bieberf9f6f902010-05-30 01:56:50 +0000187 else if(*cursor == COMMENTSYM)
188 {
189 skip_comment(&cursor);
190 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000191 else
192 {
193 /* Advancing the cursor as normal */
194 cursor++;
195 }
196 }
197 cursor = bookmark;
198
Robert Biebere25c9032010-06-02 06:41:41 +0000199 if(sublines)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000200 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000201 struct skin_element* out = skin_parse_sublines(&cursor);
202 if (!root)
203 {
204 root = out;
205 last = root;
206 }
207 else
208 {
209 last->next = skin_buffer_to_offset(out);
210 last = out;
211 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000212 if(!last)
213 return NULL;
214 }
215 else
216 {
Jonathan Gordond7372532011-09-25 07:43:36 +0000217#ifdef ROCKBOX
218 /* strip all leading comments */
219 while(*cursor == '#')
220 {
221 skip_comment(&cursor);
222 skin_line++;
223
224 }
225 if (check_viewport(cursor))
226 break;
227#endif
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000228
229 struct skin_element* out = skin_parse_line(&cursor);
230 if (!root)
231 {
232 root = out;
233 last = root;
234 }
235 else
236 {
237 last->next = skin_buffer_to_offset(out);
238 last = out;
239 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000240 if(!last)
241 return NULL;
242
243 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000244 /* Making sure last is at the end */
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000245 while(IS_VALID_OFFSET(last->next))
246 last = skin_buffer_from_offset(last->next);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000247
Robert Biebere25c9032010-06-02 06:41:41 +0000248 if(*cursor == '\n')
Robert Bieber7f10b032010-06-02 07:04:33 +0000249 {
Robert Biebere25c9032010-06-02 06:41:41 +0000250 cursor++;
Robert Bieber7f10b032010-06-02 07:04:33 +0000251 skin_line++;
252 }
Jonathan Gordond7372532011-09-25 07:43:36 +0000253#ifdef ROCKBOX
254 /* strip all comments */
255 while(*cursor == '#')
256 {
257 skip_comment(&cursor);
258 skin_line++;
259
260 }
261 if (check_viewport(cursor))
262 break;
263#endif
264
Robert Bieberd1659d62010-06-01 07:11:23 +0000265 }
Robert Bieber1b613f52010-06-11 08:03:32 +0000266 while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000267
Robert Bieberd1659d62010-06-01 07:11:23 +0000268 *document = cursor;
269
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000270 children[0] = skin_buffer_to_offset(root);
271 retval->children = skin_buffer_to_offset(children);
Robert Bieberd1659d62010-06-01 07:11:23 +0000272 return retval;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000273}
274
275/* Auxiliary Parsing Functions */
276
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000277static struct skin_element* skin_parse_line(const char**document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000278{
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000279 return skin_parse_line_optional(document, 0);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000280}
281
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000282/*
283 * If conditional is set to true, then this will break upon encountering
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000284 * SEPARATESYM. This should only be used when parsing a line inside a
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000285 * conditional, otherwise just use the wrapper function skin_parse_line()
286 */
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000287static struct skin_element* skin_parse_line_optional(const char** document,
Robert Bieber64321ad2010-06-10 21:02:44 +0000288 int conditional)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000289{
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000290 const char* cursor = *document;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000291
292 struct skin_element* root = NULL;
293 struct skin_element* current = NULL;
Robert Bieber8ea056d2010-05-27 19:57:15 +0000294 struct skin_element* retval = NULL;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000295 OFFSETTYPE(struct skin_element*)* children = NULL;
Robert Bieber8ea056d2010-05-27 19:57:15 +0000296
297 /* A wrapper for the line */
298 retval = skin_alloc_element();
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000299 if (!retval)
300 return NULL;
Robert Bieber8ea056d2010-05-27 19:57:15 +0000301 retval->type = LINE;
302 retval->line = skin_line;
Jonathan Gordon5f387c22012-02-01 21:27:18 +1100303 while (*cursor == '\t')
304 cursor++;
305
Robert Bieber09db2ae2010-06-22 18:51:44 +0000306 if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000307 && !(conditional && (*cursor == ARGLISTSEPARATESYM
Robert Bieber3c95dbb2010-06-11 19:51:34 +0000308 || *cursor == ARGLISTCLOSESYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000309 || *cursor == ENUMLISTSEPARATESYM
Robert Bieber3c95dbb2010-06-11 19:51:34 +0000310 || *cursor == ENUMLISTCLOSESYM)))
311 {
Robert Bieber1b613f52010-06-11 08:03:32 +0000312 retval->children_count = 1;
Robert Bieber3c95dbb2010-06-11 19:51:34 +0000313 }
314 else
315 {
316 retval->children_count = 0;
317 }
318
Robert Bieber1b613f52010-06-11 08:03:32 +0000319 if(retval->children_count > 0)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000320 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000321 children = skin_alloc_children(1);
322 if (!children)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000323 return NULL;
324 }
325
326#ifdef ROCKBOX
327 if (callback)
328 {
329 switch (callback(retval, callback_data))
330 {
331 case CALLBACK_ERROR:
332 return NULL;
333 default:
334 break;
335 }
336 }
337#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000338
339 while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000340 && !((*cursor == ARGLISTSEPARATESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000341 || *cursor == ARGLISTCLOSESYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000342 || *cursor == ENUMLISTSEPARATESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000343 || *cursor == ENUMLISTCLOSESYM)
Robert Bieberd1659d62010-06-01 07:11:23 +0000344 && conditional)
345 && !(check_viewport(cursor) && cursor != *document))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000346 {
347 /* Allocating memory if necessary */
348 if(root)
349 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000350 struct skin_element *next = skin_alloc_element();
351 if (!next)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000352 return NULL;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000353 current->next = skin_buffer_to_offset(next);
354 current = next;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000355 }
356 else
357 {
358 current = skin_alloc_element();
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000359 if (!current)
360 return NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000361 root = current;
362 }
363
364 /* Parsing the current element */
365 if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
366 {
367 if(!skin_parse_conditional(current, &cursor))
368 return NULL;
369 }
Robert Bieber999990c2010-06-02 05:45:34 +0000370 else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000371 {
372 if(!skin_parse_tag(current, &cursor))
373 return NULL;
374 }
375 else if(*cursor == COMMENTSYM)
376 {
377 if(!skin_parse_comment(current, &cursor))
378 return NULL;
379 }
380 else
381 {
382 if(!skin_parse_text(current, &cursor, conditional))
383 return NULL;
384 }
385 }
386
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000387 /* Moving up the calling function's pointer */
388 *document = cursor;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000389
Robert Bieber1b613f52010-06-11 08:03:32 +0000390 if(root)
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000391 {
392 children[0] = skin_buffer_to_offset(root);
393 retval->children = skin_buffer_to_offset(children);
394 }
Robert Bieber8ea056d2010-05-27 19:57:15 +0000395 return retval;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000396}
397
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000398static struct skin_element* skin_parse_sublines(const char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000399{
400 return skin_parse_sublines_optional(document, 0);
401}
402
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000403static struct skin_element* skin_parse_sublines_optional(const char** document,
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000404 int conditional)
405{
406 struct skin_element* retval;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000407 OFFSETTYPE(struct skin_element*)* children;
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000408 const char* cursor = *document;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000409 int sublines = 1;
410 int i;
411
412 retval = skin_alloc_element();
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000413 if (!retval)
414 return NULL;
Jonathan Gordondc347852010-07-04 02:04:14 +0000415 retval->type = LINE_ALTERNATOR;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000416 retval->next = skin_buffer_to_offset(NULL);
Robert Bieber8ea056d2010-05-27 19:57:15 +0000417 retval->line = skin_line;
Jonathan Gordon5f387c22012-02-01 21:27:18 +1100418 while (*cursor == '\t')
419 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000420
421 /* First we count the sublines */
422 while(*cursor != '\0' && *cursor != '\n'
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000423 && !((*cursor == ARGLISTSEPARATESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000424 || *cursor == ARGLISTCLOSESYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000425 || *cursor == ENUMLISTSEPARATESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000426 || *cursor == ENUMLISTCLOSESYM)
Robert Bieberd1659d62010-06-01 07:11:23 +0000427 && conditional)
428 && !(check_viewport(cursor) && cursor != *document))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000429 {
430 if(*cursor == COMMENTSYM)
Robert Bieber496bcf32010-06-02 07:48:48 +0000431 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000432 skip_comment(&cursor);
Robert Bieber496bcf32010-06-02 07:48:48 +0000433 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000434 else if(*cursor == TAGSYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000435 {
Jonathan Gordon2d3c43d2012-04-21 23:34:42 +1000436 skip_tag(&cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000437 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000438 else if(*cursor == MULTILINESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000439 {
440 sublines++;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000441 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000442 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000443 else
444 {
445 cursor++;
446 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000447 }
448
449 /* ...and then we parse them */
450 retval->children_count = sublines;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000451 children = skin_alloc_children(sublines);
452 if (!children)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000453 return NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000454
455 cursor = *document;
456 for(i = 0; i < sublines; i++)
457 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000458 children[i] = skin_buffer_to_offset(skin_parse_line_optional(&cursor, conditional));
459 if (children[i] < 0)
460 return NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000461 skip_whitespace(&cursor);
462
463 if(*cursor != MULTILINESYM && i != sublines - 1)
464 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000465 skin_error(MULTILINE_EXPECTED, cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000466 return NULL;
467 }
Robert Bieber8ea056d2010-05-27 19:57:15 +0000468 else if(i != sublines - 1)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000469 {
470 cursor++;
471 }
472 }
473
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000474#ifdef ROCKBOX
475 if (callback)
476 {
477 if (callback(retval, callback_data) == CALLBACK_ERROR)
478 return NULL;
479 }
480#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000481 *document = cursor;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000482 retval->children = skin_buffer_to_offset(children);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000483
484 return retval;
485}
486
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000487static int skin_parse_tag(struct skin_element* element, const char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000488{
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000489 const char* cursor = *document + 1;
490 const char* bookmark;
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100491 char *open_square_bracket = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000492
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100493 char tag_name[MAX_TAG_LENGTH];
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000494 char* tag_args;
Nils Wallméniusc2529c32010-07-31 11:28:37 +0000495 const struct tag_info *tag;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000496 struct skin_tag_parameter* params = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000497
498 int num_args = 1;
499 int i;
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100500 int qmark = 0; /* Flag for the all-or-none option */
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000501
502 int optional = 0;
503
504 /* Checking the tag name */
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100505 for (i=0; cursor[i] && i<MAX_TAG_LENGTH; i++)
506 tag_name[i] = cursor[i];
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000507
508 /* First we check the two characters after the '%', then a single char */
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100509 tag = NULL;
510 i = MAX_TAG_LENGTH;
511 while (!tag && i > 1)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000512 {
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100513 tag_name[i-1] = '\0';
Robert Bieber0a054b22010-06-01 16:44:52 +0000514 tag = find_tag(tag_name);
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100515 i--;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000516 }
517
Robert Bieber0a054b22010-06-01 16:44:52 +0000518 if(!tag)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000519 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000520 skin_error(ILLEGAL_TAG, cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000521 return 0;
522 }
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100523 cursor += i;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000524
525 /* Copying basic tag info */
Robert Bieber1b613f52010-06-11 08:03:32 +0000526 if(element->type != CONDITIONAL && element->type != VIEWPORT)
Robert Bieber64321ad2010-06-10 21:02:44 +0000527 element->type = TAG;
Robert Bieber0a054b22010-06-01 16:44:52 +0000528 element->tag = tag;
529 tag_args = tag->params;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000530 element->line = skin_line;
531
Robert Bieber1937b1b2010-05-25 17:22:39 +0000532 /* Checking for the * flag */
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100533 if(tag_args[0] == '?')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000534 {
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100535 qmark = 1;
Robert Bieber1937b1b2010-05-25 17:22:39 +0000536 tag_args++;
537 }
538
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000539 /* If this tag has no arguments, we can bail out now */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000540 if(strlen(tag_args) == 0
Robert Bieberf02a2442010-06-02 05:27:37 +0000541 || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100542 || (qmark && *cursor != ARGLISTOPENSYM))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000543 {
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000544
545#ifdef ROCKBOX
546 if (callback)
547 {
548 if (callback(element, callback_data) == CALLBACK_ERROR)
549 return 0;
550 }
551#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000552 *document = cursor;
553 return 1;
554 }
555
556 /* Checking the number of arguments and allocating args */
Robert Bieber32ff4e92010-07-04 01:39:57 +0000557 if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000558 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000559 skin_error(ARGLIST_EXPECTED, cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000560 return 0;
561 }
562 else
563 {
564 cursor++;
565 }
566
Robert Bieber06ea93d2010-06-07 23:49:06 +0000567 bookmark = cursor;
568 while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000569 {
570 /* Skipping over escaped characters */
571 if(*cursor == TAGSYM)
572 {
Jonathan Gordon2d3c43d2012-04-21 23:34:42 +1000573 skip_tag(&cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000574 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000575 else if(*cursor == COMMENTSYM)
576 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000577 skip_comment(&cursor);
Robert Bieber06ea93d2010-06-07 23:49:06 +0000578 }
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000579 else if(*cursor == ARGLISTSEPARATESYM)
Robert Bieber06ea93d2010-06-07 23:49:06 +0000580 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000581 num_args++;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000582 cursor++;
583 }
584 else
585 {
586 cursor++;
587 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000588 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000589
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000590 cursor = bookmark; /* Restoring the cursor */
591 element->params_count = num_args;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000592 params = skin_alloc_params(num_args);
593 if (!params)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000594 return 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000595
596 /* Now we have to actually parse each argument */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000597 for(i = 0; i < num_args; i++)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000598 {
Jonathan Gordon863d2392010-08-14 11:17:49 +0000599 char type_code;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000600 /* Making sure we haven't run out of arguments */
Robert Bieber32ff4e92010-07-04 01:39:57 +0000601 if(*tag_args == '\0')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000602 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000603 skin_error(TOO_MANY_ARGS, cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000604 return 0;
605 }
606
607 /* Checking for the optional bar */
608 if(*tag_args == '|')
609 {
610 optional = 1;
611 tag_args++;
612 }
613
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000614 /* Scanning the arguments */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000615 skip_whitespace(&cursor);
616
Robert Bieber1937b1b2010-05-25 17:22:39 +0000617 /* Checking for comments */
618 if(*cursor == COMMENTSYM)
619 skip_comment(&cursor);
Jonathan Gordon863d2392010-08-14 11:17:49 +0000620
621 if (*tag_args == '[')
622 {
623 /* we need to guess which type of param it is.
624 * guess using this priority:
625 * default > decimal/integer > single tag/code > string
626 */
627 int j=0;
628 bool canbedefault = false;
629 bool haspercent = false, number = true, hasdecimal = false;
630 char temp_params[8];
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100631 open_square_bracket = tag_args;
Jonathan Gordon863d2392010-08-14 11:17:49 +0000632 tag_args++;
633 while (*tag_args != ']')
634 {
635 if (*tag_args >= 'a' && *tag_args <= 'z')
636 canbedefault = true;
637 temp_params[j++] = tolower(*tag_args++);
638 }
639 temp_params[j] = '\0';
640 j = 0;
Jonathan Gordon74ec0112010-08-19 13:49:32 +0000641 while (cursor[j] && cursor[j] != ',' && cursor[j] != ')')
Jonathan Gordon863d2392010-08-14 11:17:49 +0000642 {
643 haspercent = haspercent || (cursor[j] == '%');
644 hasdecimal = hasdecimal || (cursor[j] == '.');
Jonathan Gordon74ec0112010-08-19 13:49:32 +0000645 number = number && (isdigit(cursor[j]) ||
646 (cursor[j] == '.') ||
647 (cursor[j] == '-'));
Jonathan Gordon863d2392010-08-14 11:17:49 +0000648 j++;
649 }
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100650 type_code = '?';
Jonathan Gordon863d2392010-08-14 11:17:49 +0000651 if (canbedefault && *cursor == DEFAULTSYM && !isdigit(cursor[1]))
652 {
653 type_code = 'i';
654 }
655 else if (number && hasdecimal && strchr(temp_params, 'd'))
656 {
657 type_code = 'd';
658 }
659 else if (number &&
660 (strchr(temp_params, 'i') || strchr(temp_params, 'd')))
661 {
662 type_code = strchr(temp_params, 'i') ? 'i' : 'd';
663 }
664 else if (haspercent &&
665 (strchr(temp_params, 't') || strchr(temp_params, 'c')))
666 {
667 type_code = strchr(temp_params, 't') ? 't' : 'c';
668 }
669 else if (strchr(temp_params, 's'))
670 {
671 type_code = 's';
672 }
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100673 if (type_code == '?')
Jonathan Gordon863d2392010-08-14 11:17:49 +0000674 {
675 skin_error(INSUFFICIENT_ARGS, cursor);
676 return 0;
677 }
678 }
679 else
680 type_code = *tag_args;
Robert Bieber5943f4c2010-06-01 19:55:20 +0000681 /* Storing the type code */
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000682 params[i].type_code = type_code;
Robert Bieber5943f4c2010-06-01 19:55:20 +0000683
Robert Bieber15488a02010-07-15 06:24:11 +0000684 /* Checking a nullable argument for null. */
Robert Bieberea864be2010-06-02 06:52:17 +0000685 if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000686 {
Jonathan Gordon863d2392010-08-14 11:17:49 +0000687 if(islower(type_code))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000688 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000689 params[i].type = DEFAULT;
Robert Bieber1937b1b2010-05-25 17:22:39 +0000690 cursor++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000691 }
692 else
693 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000694 skin_error(DEFAULT_NOT_ALLOWED, cursor);
Robert Bieber1937b1b2010-05-25 17:22:39 +0000695 return 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000696 }
697 }
Jonathan Gordon863d2392010-08-14 11:17:49 +0000698 else if(tolower(type_code) == 'i')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000699 {
700 /* Scanning an int argument */
Robert Bieberea864be2010-06-02 06:52:17 +0000701 if(!isdigit(*cursor) && *cursor != '-')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000702 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000703 skin_error(INT_EXPECTED, cursor);
Robert Bieber1937b1b2010-05-25 17:22:39 +0000704 return 0;
705 }
706
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000707 params[i].type = INTEGER;
708 params[i].data.number = scan_int(&cursor);
Robert Bieber15488a02010-07-15 06:24:11 +0000709 }
Jonathan Gordon863d2392010-08-14 11:17:49 +0000710 else if(tolower(type_code) == 'd')
Robert Bieber15488a02010-07-15 06:24:11 +0000711 {
712 int val = 0;
713 bool have_point = false;
714 bool have_tenth = false;
715 while ( isdigit(*cursor) || *cursor == '.' )
716 {
717 if (*cursor != '.')
718 {
719 val *= 10;
720 val += *cursor - '0';
721 if (have_point)
722 {
723 have_tenth = true;
724 cursor++;
725 break;
726 }
727 }
728 else
729 have_point = true;
730 cursor++;
731 }
732 if (have_tenth == false)
733 val *= 10;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000734 params[i].type = DECIMAL;
735 params[i].data.number = val;
Robert Bieber1937b1b2010-05-25 17:22:39 +0000736 }
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100737 else if(tolower(type_code) == 's' || tolower(type_code) == 'f')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000738 {
739 /* Scanning a string argument */
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000740 params[i].type = STRING;
741 params[i].data.text = skin_buffer_to_offset(scan_string(&cursor));
Robert Bieber1937b1b2010-05-25 17:22:39 +0000742
743 }
Jonathan Gordon863d2392010-08-14 11:17:49 +0000744 else if(tolower(type_code) == 'c')
Robert Bieber1937b1b2010-05-25 17:22:39 +0000745 {
746 /* Recursively parsing a code argument */
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000747 params[i].type = CODE;
748 params[i].data.code = skin_buffer_to_offset(skin_parse_code_as_arg(&cursor));
749 if(params[i].data.code < 0)
Robert Bieber1937b1b2010-05-25 17:22:39 +0000750 return 0;
751 }
Jonathan Gordon863d2392010-08-14 11:17:49 +0000752 else if (tolower(type_code) == 't')
Jonathan Gordon145571d2010-08-05 11:28:35 +0000753 {
754 struct skin_element* child = skin_alloc_element();
755 child->type = TAG;
756 if (!skin_parse_tag(child, &cursor))
757 return 0;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000758 child->next = skin_buffer_to_offset(NULL);
759 params[i].type = CODE;
760 params[i].data.code = skin_buffer_to_offset(child);
Jonathan Gordon145571d2010-08-05 11:28:35 +0000761 }
762
Robert Bieber1937b1b2010-05-25 17:22:39 +0000763
764 skip_whitespace(&cursor);
765
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000766 if(*cursor != ARGLISTSEPARATESYM && i < num_args - 1)
Robert Bieber1937b1b2010-05-25 17:22:39 +0000767 {
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000768 skin_error(SEPARATOR_EXPECTED, cursor);
Robert Bieber1937b1b2010-05-25 17:22:39 +0000769 return 0;
770 }
771 else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
772 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000773 skin_error(CLOSE_EXPECTED, cursor);
Robert Bieber1937b1b2010-05-25 17:22:39 +0000774 return 0;
775 }
776 else
777 {
778 cursor++;
779 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000780
Jonathan Gordon40ecdf62012-02-02 22:26:16 +1100781 if (*(tag_args + 1) == '*')
782 {
783 if (i+1 == num_args)
784 tag_args += 2;
785 else if (open_square_bracket)
786 {
787 tag_args = open_square_bracket;
788 open_square_bracket = NULL;
789 }
790 }
791 else
Jonathan Gordon9ac4b442010-06-09 16:15:01 +0000792 tag_args++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000793
Robert Bieberd1659d62010-06-01 07:11:23 +0000794 /* Checking for the optional bar */
795 if(*tag_args == '|')
796 {
797 optional = 1;
Robert Bieberd1659d62010-06-01 07:11:23 +0000798 tag_args++;
799 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000800 }
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000801 element->params = skin_buffer_to_offset(params);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000802
Robert Bieber1937b1b2010-05-25 17:22:39 +0000803 /* Checking for a premature end */
Robert Bieber32ff4e92010-07-04 01:39:57 +0000804 if(*tag_args != '\0' && !optional)
Robert Bieber1937b1b2010-05-25 17:22:39 +0000805 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000806 skin_error(INSUFFICIENT_ARGS, cursor);
Robert Bieber1937b1b2010-05-25 17:22:39 +0000807 return 0;
808 }
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000809#ifdef ROCKBOX
810 if (callback)
811 {
812 if (callback(element, callback_data) == CALLBACK_ERROR)
813 return 0;
814 }
815#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000816 *document = cursor;
817
818 return 1;
819}
820
821/*
822 * If the conditional flag is set true, then parsing text will stop at an
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000823 * ARGLISTSEPARATESYM. Only set that flag when parsing within a conditional
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000824 */
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000825static int skin_parse_text(struct skin_element* element, const char** document,
Robert Bieber64321ad2010-06-10 21:02:44 +0000826 int conditional)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000827{
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000828 const char* cursor = *document;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000829 int length = 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000830 int dest;
Robert Bieber64321ad2010-06-10 21:02:44 +0000831 char *text = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000832
833 /* First figure out how much text we're copying */
834 while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
835 && *cursor != COMMENTSYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000836 && !((*cursor == ARGLISTSEPARATESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000837 || *cursor == ARGLISTCLOSESYM
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000838 || *cursor == ENUMLISTSEPARATESYM
Robert Bieber1937b1b2010-05-25 17:22:39 +0000839 || *cursor == ENUMLISTCLOSESYM)
840 && conditional))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000841 {
842 /* Dealing with possibility of escaped characters */
843 if(*cursor == TAGSYM)
844 {
845 if(find_escape_character(cursor[1]))
846 cursor++;
847 else
848 break;
849 }
850
851 length++;
852 cursor++;
853 }
854
855 cursor = *document;
856
857 /* Copying the text into the element struct */
858 element->type = TEXT;
859 element->line = skin_line;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000860 element->next = skin_buffer_to_offset(NULL);
861 text = skin_alloc_string(length);
862 element->data = skin_buffer_to_offset(text);
863 if (element->data < 0)
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000864 return 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000865
866 for(dest = 0; dest < length; dest++)
867 {
868 /* Advancing cursor if we've encountered an escaped character */
869 if(*cursor == TAGSYM)
870 cursor++;
871
Robert Bieber64321ad2010-06-10 21:02:44 +0000872 text[dest] = *cursor;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000873 cursor++;
874 }
Robert Bieber64321ad2010-06-10 21:02:44 +0000875 text[length] = '\0';
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000876
877#ifdef ROCKBOX
878 if (callback)
879 {
880 if (callback(element, callback_data) == CALLBACK_ERROR)
881 return 0;
882 }
883#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000884
885 *document = cursor;
886
887 return 1;
888}
889
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000890static int skin_parse_conditional(struct skin_element* element, const char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000891{
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000892 const char* cursor = *document + 1; /* Starting past the "%" */
893 const char* bookmark;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000894 int children = 1;
895 int i;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000896
897#ifdef ROCKBOX
898 bool feature_available = true;
Nils Wallménius597ccdd2010-07-31 16:25:41 +0000899 const char *false_branch = NULL;
Jonathan Gordon943de3c2010-10-28 11:00:36 +0000900 const char *conditional_end = NULL;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000901#endif
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000902 OFFSETTYPE(struct skin_element*)* children_array = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000903
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000904 /* Some conditional tags allow for target feature checking,
905 * so to handle that call the callback as usual with type == TAG
906 * then call it a second time with type == CONDITIONAL and check the return
907 * value */
908 element->type = TAG;
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000909 element->line = skin_line;
910
911 /* Parsing the tag first */
Robert Bieber64321ad2010-06-10 21:02:44 +0000912 if(!skin_parse_tag(element, &cursor))
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000913 return 0;
914
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000915 element->type = CONDITIONAL;
916#ifdef ROCKBOX
917 if (callback)
918 {
919 switch (callback(element, callback_data))
920 {
921 case FEATURE_NOT_AVAILABLE:
922 feature_available = false;
923 break;
924 case CALLBACK_ERROR:
925 return 0;
926 default:
927 break;
928 }
929 }
930#endif
931
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000932 /* Counting the children */
Robert Bieber1937b1b2010-05-25 17:22:39 +0000933 if(*(cursor++) != ENUMLISTOPENSYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000934 {
Robert Bieber3c4fb8a2010-07-18 00:59:02 +0000935 skin_error(ARGLIST_EXPECTED, cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000936 return 0;
937 }
938 bookmark = cursor;
Jonathan Gordonf4173122012-02-01 21:53:31 +1100939 while(*cursor != ENUMLISTCLOSESYM && *cursor != '\0')
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000940 {
941 if(*cursor == COMMENTSYM)
942 {
943 skip_comment(&cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000944 }
Robert Bieber06ea93d2010-06-07 23:49:06 +0000945 else if(*cursor == TAGSYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000946 {
Jonathan Gordon2d3c43d2012-04-21 23:34:42 +1000947 skip_tag(&cursor);
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000948 }
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +0000949 else if(*cursor == ENUMLISTSEPARATESYM)
Robert Bieber06ea93d2010-06-07 23:49:06 +0000950 {
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000951 children++;
Robert Bieber06ea93d2010-06-07 23:49:06 +0000952 cursor++;
Jonathan Gordonf4173122012-02-01 21:53:31 +1100953 if (*cursor == '\n')
954 cursor++;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000955#ifdef ROCKBOX
956 if (false_branch == NULL && !feature_available)
957 {
958 false_branch = cursor;
959 children--;
960 }
961#endif
Robert Bieber06ea93d2010-06-07 23:49:06 +0000962 }
963 else
964 {
965 cursor++;
966 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000967 }
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000968#ifdef ROCKBOX
969 if (*cursor == ENUMLISTCLOSESYM &&
970 false_branch == NULL && !feature_available)
971 {
972 false_branch = cursor+1;
973 children--;
974 }
Jonathan Gordon943de3c2010-10-28 11:00:36 +0000975 if (element->tag->flags&FEATURE_TAG)
976 {
Jonathan Gordonf6cf2952010-11-03 11:08:55 +0000977 if (feature_available && children > 1)
Jonathan Gordon943de3c2010-10-28 11:00:36 +0000978 children--;
979 }
980 conditional_end = cursor;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000981 /* if we are skipping the true branch fix that up */
982 cursor = false_branch ? false_branch : bookmark;
983#else
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000984 cursor = bookmark;
Jonathan Gordon2d31d772010-07-29 12:37:48 +0000985#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000986 /* Parsing the children */
Jonathan Gordon78a11cf2010-12-24 07:58:26 +0000987
988 /* Feature tags could end up having 0 children which breaks
989 * the render in dangerous ways. Minor hack, but insert an empty
990 * child. (e.g %?xx<foo> when xx isnt available ) */
991
992 if (children == 0)
Robert Bieberd5b24dd2010-05-25 15:19:52 +0000993 {
Nils Wallméniuse52600e2011-10-08 13:54:55 +0000994 const char* emptyline= "";
995 children = 1;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +0000996 children_array = skin_alloc_children(children);
997 if (!children_array)
Nils Wallméniuse52600e2011-10-08 13:54:55 +0000998 return 0;
999 element->children_count = children;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001000 children_array[0] = skin_buffer_to_offset(skin_parse_code_as_arg(&emptyline));
Nils Wallméniuse52600e2011-10-08 13:54:55 +00001001 }
1002 else
1003 {
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001004 children_array = skin_alloc_children(children);
1005 if (!children_array)
Nils Wallméniuse52600e2011-10-08 13:54:55 +00001006 return 0;
1007 element->children_count = children;
Jonathan Gordon78a11cf2010-12-24 07:58:26 +00001008
Nils Wallméniuse52600e2011-10-08 13:54:55 +00001009 for(i = 0; i < children; i++)
1010 {
Jonathan Gordonf4173122012-02-01 21:53:31 +11001011 if (*cursor == '\n')
1012 {
1013 skin_line++;
1014 cursor++;
1015 }
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001016 children_array[i] = skin_buffer_to_offset(skin_parse_code_as_arg(&cursor));
1017 if (children_array[i] < 0)
Nils Wallméniuse52600e2011-10-08 13:54:55 +00001018 return 0;
1019 skip_whitespace(&cursor);
Jonathan Gordon943de3c2010-10-28 11:00:36 +00001020#ifdef ROCKBOX
Nils Wallméniuse52600e2011-10-08 13:54:55 +00001021 if ((element->tag->flags&FEATURE_TAG) && feature_available)
1022 cursor = conditional_end;
Jonathan Gordon943de3c2010-10-28 11:00:36 +00001023#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001024
Nils Wallméniuse52600e2011-10-08 13:54:55 +00001025 if(i < children - 1 && *cursor != ENUMLISTSEPARATESYM)
1026 {
1027 skin_error(SEPARATOR_EXPECTED, cursor);
1028 return 0;
1029 }
1030 else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
1031 {
1032 skin_error(CLOSE_EXPECTED, cursor);
1033 return 0;
1034 }
1035 else
1036 {
1037 cursor++;
1038 }
1039 }
1040 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001041 *document = cursor;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001042 element->children = skin_buffer_to_offset(children_array);
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001043
1044 return 1;
1045}
1046
Nils Wallménius597ccdd2010-07-31 16:25:41 +00001047static int skin_parse_comment(struct skin_element* element, const char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001048{
Nils Wallménius597ccdd2010-07-31 16:25:41 +00001049 const char* cursor = *document;
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +00001050#ifndef ROCKBOX
Robert Bieber64321ad2010-06-10 21:02:44 +00001051 char* text = NULL;
Jonathan Gordonb2ea95e2010-06-17 07:56:51 +00001052#endif
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001053 int length;
1054 /*
1055 * Finding the index of the ending newline or null-terminator
1056 * The length of the string of interest doesn't include the leading #, the
1057 * length we need to reserve is the same as the index of the last character
1058 */
1059 for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
1060
1061 element->type = COMMENT;
1062 element->line = skin_line;
Robert Bieber64321ad2010-06-10 21:02:44 +00001063#ifdef ROCKBOX
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001064 element->data = INVALID_OFFSET;
Robert Bieber64321ad2010-06-10 21:02:44 +00001065#else
1066 element->data = text = skin_alloc_string(length);
Jonathan Gordon2d31d772010-07-29 12:37:48 +00001067 if (!element->data)
1068 return 0;
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001069 /* We copy from one char past cursor to leave out the # */
Robert Bieber64321ad2010-06-10 21:02:44 +00001070 memcpy((void*)text, (void*)(cursor + 1),
Robert Biebera9848ce2010-06-01 21:25:02 +00001071 sizeof(char) * (length-1));
Robert Bieber64321ad2010-06-10 21:02:44 +00001072 text[length - 1] = '\0';
1073#endif
Robert Biebera9848ce2010-06-01 21:25:02 +00001074 if(cursor[length] == '\n')
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001075 skin_line++;
1076
Robert Biebera9848ce2010-06-01 21:25:02 +00001077 *document += (length); /* Move cursor up past # and all text */
1078 if(**document == '\n')
1079 (*document)++;
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001080
1081 return 1;
1082}
1083
Nils Wallménius597ccdd2010-07-31 16:25:41 +00001084static struct skin_element* skin_parse_code_as_arg(const char** document)
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001085{
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001086 int sublines = 0;
Nils Wallménius597ccdd2010-07-31 16:25:41 +00001087 const char* cursor = *document;
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001088
1089 /* Checking for sublines */
Robert Bieber4b77e822010-06-10 21:22:16 +00001090 while(*cursor != '\n' && *cursor != '\0'
Bertrik Sikkenfffbdcc2010-11-05 09:51:19 +00001091 && *cursor != ENUMLISTSEPARATESYM && *cursor != ARGLISTSEPARATESYM
Robert Bieber4b77e822010-06-10 21:22:16 +00001092 && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001093 {
1094 if(*cursor == MULTILINESYM)
1095 {
1096 sublines = 1;
1097 break;
1098 }
1099 else if(*cursor == TAGSYM)
1100 {
Jonathan Gordon2d3c43d2012-04-21 23:34:42 +10001101 skip_tag(&cursor);
Robert Bieber4b77e822010-06-10 21:22:16 +00001102 }
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001103 else
1104 {
1105 /* Advancing the cursor as normal */
1106 cursor++;
1107 }
1108 }
1109
1110 if(sublines)
1111 return skin_parse_sublines_optional(document, 1);
1112 else
1113 return skin_parse_line_optional(document, 1);
1114}
1115
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001116/* Memory management */
1117struct skin_element* skin_alloc_element()
1118{
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001119 struct skin_element* retval = (struct skin_element*)
Jonathan Gordon95b0cef2010-06-17 13:54:09 +00001120 skin_buffer_alloc(sizeof(struct skin_element));
Jonathan Gordon2d31d772010-07-29 12:37:48 +00001121 if (!retval)
1122 return NULL;
Jonathan Gordon35b09cb2010-06-13 03:13:01 +00001123 retval->type = UNKNOWN;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001124 retval->next = skin_buffer_to_offset(NULL);
1125 retval->params = skin_buffer_to_offset(NULL);
Robert Bieber1b613f52010-06-11 08:03:32 +00001126 retval->tag = NULL;
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001127 retval->params_count = 0;
1128 retval->children_count = 0;
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001129 retval->data = INVALID_OFFSET;
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001130
1131 return retval;
1132
1133}
Jonathan Gordon2d31d772010-07-29 12:37:48 +00001134/* On a ROCKBOX build we try to save space as much as possible
1135 * so if we can, use a shared param pool which should be more then large
1136 * enough for any tag. params should be used straight away by the callback
1137 * so this is safe.
1138 */
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001139struct skin_tag_parameter* skin_alloc_params(int count)
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001140{
Robert Bieber64321ad2010-06-10 21:02:44 +00001141 size_t size = sizeof(struct skin_tag_parameter) * count;
Jonathan Gordon95b0cef2010-06-17 13:54:09 +00001142 return (struct skin_tag_parameter*)skin_buffer_alloc(size);
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001143
1144}
1145
1146char* skin_alloc_string(int length)
1147{
Jonathan Gordon95b0cef2010-06-17 13:54:09 +00001148 return (char*)skin_buffer_alloc(sizeof(char) * (length + 1));
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001149}
1150
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001151OFFSETTYPE(struct skin_element*)* skin_alloc_children(int count)
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001152{
Jonathan Gordon9e07ef22011-11-15 14:11:08 +00001153 return (OFFSETTYPE(struct skin_element*)*)
Jonathan Gordon95b0cef2010-06-17 13:54:09 +00001154 skin_buffer_alloc(sizeof(struct skin_element*) * count);
Robert Bieberd5b24dd2010-05-25 15:19:52 +00001155}
Robert Bieber0769fc52010-05-25 22:24:08 +00001156
1157void skin_free_tree(struct skin_element* root)
1158{
Robert Bieber64321ad2010-06-10 21:02:44 +00001159#ifndef ROCKBOX
Robert Bieber0769fc52010-05-25 22:24:08 +00001160 int i;
1161
1162 /* First make the recursive call */
1163 if(!root)
1164 return;
1165 skin_free_tree(root->next);
1166
1167 /* Free any text */
Robert Bieber64321ad2010-06-10 21:02:44 +00001168 if(root->type == TEXT || root->type == COMMENT)
1169 free(root->data);
Robert Bieber0769fc52010-05-25 22:24:08 +00001170
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);
1176
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);
1183
1184 /* Finally, delete root's memory */
1185 free(root);
Robert Bieber64321ad2010-06-10 21:02:44 +00001186#else
1187 (void)root;
1188#endif
Robert Bieber0769fc52010-05-25 22:24:08 +00001189}