Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 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 Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 24 | #include <stdbool.h> |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 25 | #include <string.h> |
| 26 | #include <ctype.h> |
| 27 | |
Jonathan Gordon | b2ea95e | 2010-06-17 07:56:51 +0000 | [diff] [blame] | 28 | #include "skin_buffer.h" |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 29 | #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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 35 | /* Global variables for the parser */ |
| 36 | int skin_line = 0; |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 37 | char* skin_start = 0; |
Robert Bieber | 4429fb0 | 2010-07-02 08:05:03 +0000 | [diff] [blame] | 38 | int viewport_line = 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 39 | |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 40 | #ifdef ROCKBOX |
| 41 | static skin_callback callback = NULL; |
| 42 | static void* callback_data; |
| 43 | #endif |
| 44 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 45 | /* Auxiliary parsing functions (not visible at global scope) */ |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 46 | static struct skin_element* skin_parse_viewport(const char** document); |
| 47 | static struct skin_element* skin_parse_line(const char** document); |
| 48 | static struct skin_element* skin_parse_line_optional(const char** document, |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 49 | int conditional); |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 50 | static struct skin_element* skin_parse_sublines(const char** document); |
| 51 | static struct skin_element* skin_parse_sublines_optional(const char** document, |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 52 | int conditional); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 53 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 54 | static int skin_parse_tag(struct skin_element* element, const char** document); |
| 55 | static int skin_parse_text(struct skin_element* element, const char** document, |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 56 | int conditional); |
| 57 | static int skin_parse_conditional(struct skin_element* element, |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 58 | const char** document); |
| 59 | static int skin_parse_comment(struct skin_element* element, const char** document); |
| 60 | static struct skin_element* skin_parse_code_as_arg(const char** document); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 61 | |
Jonathan Gordon | 95b0cef | 2010-06-17 13:54:09 +0000 | [diff] [blame] | 62 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 63 | static void skip_whitespace(const char** document) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 64 | { |
| 65 | while(**document == ' ' || **document == '\t') |
| 66 | (*document)++; |
| 67 | } |
Jonathan Gordon | 95b0cef | 2010-06-17 13:54:09 +0000 | [diff] [blame] | 68 | |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 69 | #ifdef ROCKBOX |
| 70 | struct skin_element* skin_parse(const char* document, |
| 71 | skin_callback cb, void* cb_data) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 72 | { |
| 73 | callback = cb; |
| 74 | callback_data = cb_data; |
| 75 | #else |
Robert Bieber | a9848ce | 2010-06-01 21:25:02 +0000 | [diff] [blame] | 76 | struct skin_element* skin_parse(const char* document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 77 | { |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 78 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 79 | struct skin_element* root = NULL; |
| 80 | struct skin_element* last = NULL; |
| 81 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 82 | const char* cursor = document; /*Keeps track of location in the document*/ |
Jonathan Gordon | b2ea95e | 2010-06-17 07:56:51 +0000 | [diff] [blame] | 83 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 84 | skin_line = 1; |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 85 | skin_start = (char*)document; |
Robert Bieber | 4429fb0 | 2010-07-02 08:05:03 +0000 | [diff] [blame] | 86 | viewport_line = 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 87 | |
Robert Bieber | 594d734 | 2010-06-07 21:09:13 +0000 | [diff] [blame] | 88 | skin_clear_errors(); |
| 89 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 90 | while(*cursor != '\0') |
| 91 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 92 | struct skin_element* tree = skin_parse_viewport(&cursor); |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 93 | if(!root) |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 94 | { |
| 95 | root = tree; |
| 96 | last = root; |
| 97 | } |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 98 | else |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 99 | { |
| 100 | last->next = skin_buffer_to_offset(tree); |
| 101 | last = tree; |
| 102 | } |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 103 | |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 104 | if(!last) |
Robert Bieber | 6c9b035 | 2010-06-08 19:34:27 +0000 | [diff] [blame] | 105 | { |
| 106 | skin_free_tree(root); /* Clearing any memory already used */ |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 107 | return NULL; |
Robert Bieber | 6c9b035 | 2010-06-08 19:34:27 +0000 | [diff] [blame] | 108 | } |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 109 | |
| 110 | /* Making sure last is at the end */ |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 111 | while(IS_VALID_OFFSET(last->next)) |
| 112 | last = skin_buffer_from_offset(last->next); |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 113 | |
| 114 | } |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 115 | return root; |
| 116 | |
| 117 | } |
| 118 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 119 | static struct skin_element* skin_parse_viewport(const char** document) |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 120 | { |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 121 | struct skin_element* root = NULL; |
| 122 | struct skin_element* last = NULL; |
| 123 | struct skin_element* retval = NULL; |
| 124 | |
| 125 | retval = skin_alloc_element(); |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 126 | if (!retval) |
| 127 | return NULL; |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 128 | retval->type = VIEWPORT; |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 129 | retval->children_count = 1; |
| 130 | retval->line = skin_line; |
Robert Bieber | 4429fb0 | 2010-07-02 08:05:03 +0000 | [diff] [blame] | 131 | viewport_line = skin_line; |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 132 | |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 133 | OFFSETTYPE(struct skin_element*)* children; |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 134 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 135 | const char* cursor = *document; /* Keeps track of location in the document */ |
| 136 | const char* bookmark; /* Used when we need to look ahead */ |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 137 | |
| 138 | int sublines = 0; /* Flag for parsing sublines */ |
| 139 | |
Robert Bieber | 47cd878 | 2010-06-01 18:31:58 +0000 | [diff] [blame] | 140 | /* Parsing out the viewport tag if there is one */ |
| 141 | if(check_viewport(cursor)) |
| 142 | { |
Thomas Martitz | d98f292 | 2010-11-05 18:46:21 +0000 | [diff] [blame] | 143 | if (!skin_parse_tag(retval, &cursor)) |
| 144 | return NULL; |
Robert Bieber | 7f10b03 | 2010-06-02 07:04:33 +0000 | [diff] [blame] | 145 | if(*cursor == '\n') |
| 146 | { |
| 147 | cursor++; |
| 148 | skin_line++; |
| 149 | } |
Robert Bieber | 47cd878 | 2010-06-01 18:31:58 +0000 | [diff] [blame] | 150 | } |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 151 | #ifdef ROCKBOX |
| 152 | else if (callback) |
| 153 | { |
| 154 | if (callback(retval, callback_data) == CALLBACK_ERROR) |
| 155 | return NULL; |
| 156 | } |
| 157 | #endif |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 158 | |
Jonathan Gordon | d402abe | 2010-10-05 13:02:34 +0000 | [diff] [blame] | 159 | if (check_viewport(cursor)) |
| 160 | { |
| 161 | retval->children_count = 0; |
| 162 | *document = cursor; |
| 163 | return retval; |
| 164 | } |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 165 | retval->children_count = 1; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 166 | children = skin_alloc_children(1); |
| 167 | if (!children) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 168 | return NULL; |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 169 | do |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 170 | { |
| 171 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 172 | /* First, we check to see if this line will contain sublines */ |
| 173 | bookmark = cursor; |
| 174 | sublines = 0; |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 175 | while(*cursor != '\n' && *cursor != '\0' |
| 176 | && !(check_viewport(cursor) && cursor != *document)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 177 | { |
| 178 | if(*cursor == MULTILINESYM) |
| 179 | { |
| 180 | sublines = 1; |
| 181 | break; |
| 182 | } |
| 183 | else if(*cursor == TAGSYM) |
| 184 | { |
Jonathan Gordon | 2d3c43d | 2012-04-21 23:34:42 +1000 | [diff] [blame] | 185 | skip_tag(&cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 186 | } |
Robert Bieber | f9f6f90 | 2010-05-30 01:56:50 +0000 | [diff] [blame] | 187 | else if(*cursor == COMMENTSYM) |
| 188 | { |
| 189 | skip_comment(&cursor); |
| 190 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 191 | else |
| 192 | { |
| 193 | /* Advancing the cursor as normal */ |
| 194 | cursor++; |
| 195 | } |
| 196 | } |
| 197 | cursor = bookmark; |
| 198 | |
Robert Bieber | e25c903 | 2010-06-02 06:41:41 +0000 | [diff] [blame] | 199 | if(sublines) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 200 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 201 | 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 212 | if(!last) |
| 213 | return NULL; |
| 214 | } |
| 215 | else |
| 216 | { |
Jonathan Gordon | d737253 | 2011-09-25 07:43:36 +0000 | [diff] [blame] | 217 | #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 Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 228 | |
| 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 240 | if(!last) |
| 241 | return NULL; |
| 242 | |
| 243 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 244 | /* Making sure last is at the end */ |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 245 | while(IS_VALID_OFFSET(last->next)) |
| 246 | last = skin_buffer_from_offset(last->next); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 247 | |
Robert Bieber | e25c903 | 2010-06-02 06:41:41 +0000 | [diff] [blame] | 248 | if(*cursor == '\n') |
Robert Bieber | 7f10b03 | 2010-06-02 07:04:33 +0000 | [diff] [blame] | 249 | { |
Robert Bieber | e25c903 | 2010-06-02 06:41:41 +0000 | [diff] [blame] | 250 | cursor++; |
Robert Bieber | 7f10b03 | 2010-06-02 07:04:33 +0000 | [diff] [blame] | 251 | skin_line++; |
| 252 | } |
Jonathan Gordon | d737253 | 2011-09-25 07:43:36 +0000 | [diff] [blame] | 253 | #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 Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 265 | } |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 266 | while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document)); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 267 | |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 268 | *document = cursor; |
| 269 | |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 270 | children[0] = skin_buffer_to_offset(root); |
| 271 | retval->children = skin_buffer_to_offset(children); |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 272 | return retval; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | /* Auxiliary Parsing Functions */ |
| 276 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 277 | static struct skin_element* skin_parse_line(const char**document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 278 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 279 | return skin_parse_line_optional(document, 0); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 280 | } |
| 281 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 282 | /* |
| 283 | * If conditional is set to true, then this will break upon encountering |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 284 | * SEPARATESYM. This should only be used when parsing a line inside a |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 285 | * conditional, otherwise just use the wrapper function skin_parse_line() |
| 286 | */ |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 287 | static struct skin_element* skin_parse_line_optional(const char** document, |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 288 | int conditional) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 289 | { |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 290 | const char* cursor = *document; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 291 | |
| 292 | struct skin_element* root = NULL; |
| 293 | struct skin_element* current = NULL; |
Robert Bieber | 8ea056d | 2010-05-27 19:57:15 +0000 | [diff] [blame] | 294 | struct skin_element* retval = NULL; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 295 | OFFSETTYPE(struct skin_element*)* children = NULL; |
Robert Bieber | 8ea056d | 2010-05-27 19:57:15 +0000 | [diff] [blame] | 296 | |
| 297 | /* A wrapper for the line */ |
| 298 | retval = skin_alloc_element(); |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 299 | if (!retval) |
| 300 | return NULL; |
Robert Bieber | 8ea056d | 2010-05-27 19:57:15 +0000 | [diff] [blame] | 301 | retval->type = LINE; |
| 302 | retval->line = skin_line; |
Jonathan Gordon | 5f387c2 | 2012-02-01 21:27:18 +1100 | [diff] [blame] | 303 | while (*cursor == '\t') |
| 304 | cursor++; |
| 305 | |
Robert Bieber | 09db2ae | 2010-06-22 18:51:44 +0000 | [diff] [blame] | 306 | if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 307 | && !(conditional && (*cursor == ARGLISTSEPARATESYM |
Robert Bieber | 3c95dbb | 2010-06-11 19:51:34 +0000 | [diff] [blame] | 308 | || *cursor == ARGLISTCLOSESYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 309 | || *cursor == ENUMLISTSEPARATESYM |
Robert Bieber | 3c95dbb | 2010-06-11 19:51:34 +0000 | [diff] [blame] | 310 | || *cursor == ENUMLISTCLOSESYM))) |
| 311 | { |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 312 | retval->children_count = 1; |
Robert Bieber | 3c95dbb | 2010-06-11 19:51:34 +0000 | [diff] [blame] | 313 | } |
| 314 | else |
| 315 | { |
| 316 | retval->children_count = 0; |
| 317 | } |
| 318 | |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 319 | if(retval->children_count > 0) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 320 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 321 | children = skin_alloc_children(1); |
| 322 | if (!children) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 323 | 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 338 | |
| 339 | while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 340 | && !((*cursor == ARGLISTSEPARATESYM |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 341 | || *cursor == ARGLISTCLOSESYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 342 | || *cursor == ENUMLISTSEPARATESYM |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 343 | || *cursor == ENUMLISTCLOSESYM) |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 344 | && conditional) |
| 345 | && !(check_viewport(cursor) && cursor != *document)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 346 | { |
| 347 | /* Allocating memory if necessary */ |
| 348 | if(root) |
| 349 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 350 | struct skin_element *next = skin_alloc_element(); |
| 351 | if (!next) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 352 | return NULL; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 353 | current->next = skin_buffer_to_offset(next); |
| 354 | current = next; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 355 | } |
| 356 | else |
| 357 | { |
| 358 | current = skin_alloc_element(); |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 359 | if (!current) |
| 360 | return NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 361 | 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 Bieber | 999990c | 2010-06-02 05:45:34 +0000 | [diff] [blame] | 370 | else if(*cursor == TAGSYM && !find_escape_character(cursor[1])) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 371 | { |
| 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 387 | /* Moving up the calling function's pointer */ |
| 388 | *document = cursor; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 389 | |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 390 | if(root) |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 391 | { |
| 392 | children[0] = skin_buffer_to_offset(root); |
| 393 | retval->children = skin_buffer_to_offset(children); |
| 394 | } |
Robert Bieber | 8ea056d | 2010-05-27 19:57:15 +0000 | [diff] [blame] | 395 | return retval; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 396 | } |
| 397 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 398 | static struct skin_element* skin_parse_sublines(const char** document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 399 | { |
| 400 | return skin_parse_sublines_optional(document, 0); |
| 401 | } |
| 402 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 403 | static struct skin_element* skin_parse_sublines_optional(const char** document, |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 404 | int conditional) |
| 405 | { |
| 406 | struct skin_element* retval; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 407 | OFFSETTYPE(struct skin_element*)* children; |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 408 | const char* cursor = *document; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 409 | int sublines = 1; |
| 410 | int i; |
| 411 | |
| 412 | retval = skin_alloc_element(); |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 413 | if (!retval) |
| 414 | return NULL; |
Jonathan Gordon | dc34785 | 2010-07-04 02:04:14 +0000 | [diff] [blame] | 415 | retval->type = LINE_ALTERNATOR; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 416 | retval->next = skin_buffer_to_offset(NULL); |
Robert Bieber | 8ea056d | 2010-05-27 19:57:15 +0000 | [diff] [blame] | 417 | retval->line = skin_line; |
Jonathan Gordon | 5f387c2 | 2012-02-01 21:27:18 +1100 | [diff] [blame] | 418 | while (*cursor == '\t') |
| 419 | cursor++; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 420 | |
| 421 | /* First we count the sublines */ |
| 422 | while(*cursor != '\0' && *cursor != '\n' |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 423 | && !((*cursor == ARGLISTSEPARATESYM |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 424 | || *cursor == ARGLISTCLOSESYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 425 | || *cursor == ENUMLISTSEPARATESYM |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 426 | || *cursor == ENUMLISTCLOSESYM) |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 427 | && conditional) |
| 428 | && !(check_viewport(cursor) && cursor != *document)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 429 | { |
| 430 | if(*cursor == COMMENTSYM) |
Robert Bieber | 496bcf3 | 2010-06-02 07:48:48 +0000 | [diff] [blame] | 431 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 432 | skip_comment(&cursor); |
Robert Bieber | 496bcf3 | 2010-06-02 07:48:48 +0000 | [diff] [blame] | 433 | } |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 434 | else if(*cursor == TAGSYM) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 435 | { |
Jonathan Gordon | 2d3c43d | 2012-04-21 23:34:42 +1000 | [diff] [blame] | 436 | skip_tag(&cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 437 | } |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 438 | else if(*cursor == MULTILINESYM) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 439 | { |
| 440 | sublines++; |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 441 | cursor++; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 442 | } |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 443 | else |
| 444 | { |
| 445 | cursor++; |
| 446 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 447 | } |
| 448 | |
| 449 | /* ...and then we parse them */ |
| 450 | retval->children_count = sublines; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 451 | children = skin_alloc_children(sublines); |
| 452 | if (!children) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 453 | return NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 454 | |
| 455 | cursor = *document; |
| 456 | for(i = 0; i < sublines; i++) |
| 457 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 458 | children[i] = skin_buffer_to_offset(skin_parse_line_optional(&cursor, conditional)); |
| 459 | if (children[i] < 0) |
| 460 | return NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 461 | skip_whitespace(&cursor); |
| 462 | |
| 463 | if(*cursor != MULTILINESYM && i != sublines - 1) |
| 464 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 465 | skin_error(MULTILINE_EXPECTED, cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 466 | return NULL; |
| 467 | } |
Robert Bieber | 8ea056d | 2010-05-27 19:57:15 +0000 | [diff] [blame] | 468 | else if(i != sublines - 1) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 469 | { |
| 470 | cursor++; |
| 471 | } |
| 472 | } |
| 473 | |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 474 | #ifdef ROCKBOX |
| 475 | if (callback) |
| 476 | { |
| 477 | if (callback(retval, callback_data) == CALLBACK_ERROR) |
| 478 | return NULL; |
| 479 | } |
| 480 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 481 | *document = cursor; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 482 | retval->children = skin_buffer_to_offset(children); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 483 | |
| 484 | return retval; |
| 485 | } |
| 486 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 487 | static int skin_parse_tag(struct skin_element* element, const char** document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 488 | { |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 489 | const char* cursor = *document + 1; |
| 490 | const char* bookmark; |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 491 | char *open_square_bracket = NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 492 | |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 493 | char tag_name[MAX_TAG_LENGTH]; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 494 | char* tag_args; |
Nils Wallménius | c2529c3 | 2010-07-31 11:28:37 +0000 | [diff] [blame] | 495 | const struct tag_info *tag; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 496 | struct skin_tag_parameter* params = NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 497 | |
| 498 | int num_args = 1; |
| 499 | int i; |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 500 | int qmark = 0; /* Flag for the all-or-none option */ |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 501 | |
| 502 | int optional = 0; |
| 503 | |
| 504 | /* Checking the tag name */ |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 505 | for (i=0; cursor[i] && i<MAX_TAG_LENGTH; i++) |
| 506 | tag_name[i] = cursor[i]; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 507 | |
| 508 | /* First we check the two characters after the '%', then a single char */ |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 509 | tag = NULL; |
| 510 | i = MAX_TAG_LENGTH; |
| 511 | while (!tag && i > 1) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 512 | { |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 513 | tag_name[i-1] = '\0'; |
Robert Bieber | 0a054b2 | 2010-06-01 16:44:52 +0000 | [diff] [blame] | 514 | tag = find_tag(tag_name); |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 515 | i--; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 516 | } |
| 517 | |
Robert Bieber | 0a054b2 | 2010-06-01 16:44:52 +0000 | [diff] [blame] | 518 | if(!tag) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 519 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 520 | skin_error(ILLEGAL_TAG, cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 521 | return 0; |
| 522 | } |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 523 | cursor += i; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 524 | |
| 525 | /* Copying basic tag info */ |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 526 | if(element->type != CONDITIONAL && element->type != VIEWPORT) |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 527 | element->type = TAG; |
Robert Bieber | 0a054b2 | 2010-06-01 16:44:52 +0000 | [diff] [blame] | 528 | element->tag = tag; |
| 529 | tag_args = tag->params; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 530 | element->line = skin_line; |
| 531 | |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 532 | /* Checking for the * flag */ |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 533 | if(tag_args[0] == '?') |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 534 | { |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 535 | qmark = 1; |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 536 | tag_args++; |
| 537 | } |
| 538 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 539 | /* If this tag has no arguments, we can bail out now */ |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 540 | if(strlen(tag_args) == 0 |
Robert Bieber | f02a244 | 2010-06-02 05:27:37 +0000 | [diff] [blame] | 541 | || (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM) |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 542 | || (qmark && *cursor != ARGLISTOPENSYM)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 543 | { |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 544 | |
| 545 | #ifdef ROCKBOX |
| 546 | if (callback) |
| 547 | { |
| 548 | if (callback(element, callback_data) == CALLBACK_ERROR) |
| 549 | return 0; |
| 550 | } |
| 551 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 552 | *document = cursor; |
| 553 | return 1; |
| 554 | } |
| 555 | |
| 556 | /* Checking the number of arguments and allocating args */ |
Robert Bieber | 32ff4e9 | 2010-07-04 01:39:57 +0000 | [diff] [blame] | 557 | if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|') |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 558 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 559 | skin_error(ARGLIST_EXPECTED, cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 560 | return 0; |
| 561 | } |
| 562 | else |
| 563 | { |
| 564 | cursor++; |
| 565 | } |
| 566 | |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 567 | bookmark = cursor; |
| 568 | while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 569 | { |
| 570 | /* Skipping over escaped characters */ |
| 571 | if(*cursor == TAGSYM) |
| 572 | { |
Jonathan Gordon | 2d3c43d | 2012-04-21 23:34:42 +1000 | [diff] [blame] | 573 | skip_tag(&cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 574 | } |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 575 | else if(*cursor == COMMENTSYM) |
| 576 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 577 | skip_comment(&cursor); |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 578 | } |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 579 | else if(*cursor == ARGLISTSEPARATESYM) |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 580 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 581 | num_args++; |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 582 | cursor++; |
| 583 | } |
| 584 | else |
| 585 | { |
| 586 | cursor++; |
| 587 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 588 | } |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 589 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 590 | cursor = bookmark; /* Restoring the cursor */ |
| 591 | element->params_count = num_args; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 592 | params = skin_alloc_params(num_args); |
| 593 | if (!params) |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 594 | return 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 595 | |
| 596 | /* Now we have to actually parse each argument */ |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 597 | for(i = 0; i < num_args; i++) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 598 | { |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 599 | char type_code; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 600 | /* Making sure we haven't run out of arguments */ |
Robert Bieber | 32ff4e9 | 2010-07-04 01:39:57 +0000 | [diff] [blame] | 601 | if(*tag_args == '\0') |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 602 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 603 | skin_error(TOO_MANY_ARGS, cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 604 | return 0; |
| 605 | } |
| 606 | |
| 607 | /* Checking for the optional bar */ |
| 608 | if(*tag_args == '|') |
| 609 | { |
| 610 | optional = 1; |
| 611 | tag_args++; |
| 612 | } |
| 613 | |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 614 | /* Scanning the arguments */ |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 615 | skip_whitespace(&cursor); |
| 616 | |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 617 | /* Checking for comments */ |
| 618 | if(*cursor == COMMENTSYM) |
| 619 | skip_comment(&cursor); |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 620 | |
| 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 Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 631 | open_square_bracket = tag_args; |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 632 | 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 Gordon | 74ec011 | 2010-08-19 13:49:32 +0000 | [diff] [blame] | 641 | while (cursor[j] && cursor[j] != ',' && cursor[j] != ')') |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 642 | { |
| 643 | haspercent = haspercent || (cursor[j] == '%'); |
| 644 | hasdecimal = hasdecimal || (cursor[j] == '.'); |
Jonathan Gordon | 74ec011 | 2010-08-19 13:49:32 +0000 | [diff] [blame] | 645 | number = number && (isdigit(cursor[j]) || |
| 646 | (cursor[j] == '.') || |
| 647 | (cursor[j] == '-')); |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 648 | j++; |
| 649 | } |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 650 | type_code = '?'; |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 651 | 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 Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 673 | if (type_code == '?') |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 674 | { |
| 675 | skin_error(INSUFFICIENT_ARGS, cursor); |
| 676 | return 0; |
| 677 | } |
| 678 | } |
| 679 | else |
| 680 | type_code = *tag_args; |
Robert Bieber | 5943f4c | 2010-06-01 19:55:20 +0000 | [diff] [blame] | 681 | /* Storing the type code */ |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 682 | params[i].type_code = type_code; |
Robert Bieber | 5943f4c | 2010-06-01 19:55:20 +0000 | [diff] [blame] | 683 | |
Robert Bieber | 15488a0 | 2010-07-15 06:24:11 +0000 | [diff] [blame] | 684 | /* Checking a nullable argument for null. */ |
Robert Bieber | ea864be | 2010-06-02 06:52:17 +0000 | [diff] [blame] | 685 | if(*cursor == DEFAULTSYM && !isdigit(cursor[1])) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 686 | { |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 687 | if(islower(type_code)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 688 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 689 | params[i].type = DEFAULT; |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 690 | cursor++; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 691 | } |
| 692 | else |
| 693 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 694 | skin_error(DEFAULT_NOT_ALLOWED, cursor); |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 695 | return 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 696 | } |
| 697 | } |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 698 | else if(tolower(type_code) == 'i') |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 699 | { |
| 700 | /* Scanning an int argument */ |
Robert Bieber | ea864be | 2010-06-02 06:52:17 +0000 | [diff] [blame] | 701 | if(!isdigit(*cursor) && *cursor != '-') |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 702 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 703 | skin_error(INT_EXPECTED, cursor); |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 704 | return 0; |
| 705 | } |
| 706 | |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 707 | params[i].type = INTEGER; |
| 708 | params[i].data.number = scan_int(&cursor); |
Robert Bieber | 15488a0 | 2010-07-15 06:24:11 +0000 | [diff] [blame] | 709 | } |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 710 | else if(tolower(type_code) == 'd') |
Robert Bieber | 15488a0 | 2010-07-15 06:24:11 +0000 | [diff] [blame] | 711 | { |
| 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 Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 734 | params[i].type = DECIMAL; |
| 735 | params[i].data.number = val; |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 736 | } |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 737 | else if(tolower(type_code) == 's' || tolower(type_code) == 'f') |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 738 | { |
| 739 | /* Scanning a string argument */ |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 740 | params[i].type = STRING; |
| 741 | params[i].data.text = skin_buffer_to_offset(scan_string(&cursor)); |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 742 | |
| 743 | } |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 744 | else if(tolower(type_code) == 'c') |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 745 | { |
| 746 | /* Recursively parsing a code argument */ |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 747 | 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 Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 750 | return 0; |
| 751 | } |
Jonathan Gordon | 863d239 | 2010-08-14 11:17:49 +0000 | [diff] [blame] | 752 | else if (tolower(type_code) == 't') |
Jonathan Gordon | 145571d | 2010-08-05 11:28:35 +0000 | [diff] [blame] | 753 | { |
| 754 | struct skin_element* child = skin_alloc_element(); |
| 755 | child->type = TAG; |
| 756 | if (!skin_parse_tag(child, &cursor)) |
| 757 | return 0; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 758 | child->next = skin_buffer_to_offset(NULL); |
| 759 | params[i].type = CODE; |
| 760 | params[i].data.code = skin_buffer_to_offset(child); |
Jonathan Gordon | 145571d | 2010-08-05 11:28:35 +0000 | [diff] [blame] | 761 | } |
| 762 | |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 763 | |
| 764 | skip_whitespace(&cursor); |
| 765 | |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 766 | if(*cursor != ARGLISTSEPARATESYM && i < num_args - 1) |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 767 | { |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 768 | skin_error(SEPARATOR_EXPECTED, cursor); |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 769 | return 0; |
| 770 | } |
| 771 | else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1) |
| 772 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 773 | skin_error(CLOSE_EXPECTED, cursor); |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 774 | return 0; |
| 775 | } |
| 776 | else |
| 777 | { |
| 778 | cursor++; |
| 779 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 780 | |
Jonathan Gordon | 40ecdf6 | 2012-02-02 22:26:16 +1100 | [diff] [blame] | 781 | 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 Gordon | 9ac4b44 | 2010-06-09 16:15:01 +0000 | [diff] [blame] | 792 | tag_args++; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 793 | |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 794 | /* Checking for the optional bar */ |
| 795 | if(*tag_args == '|') |
| 796 | { |
| 797 | optional = 1; |
Robert Bieber | d1659d6 | 2010-06-01 07:11:23 +0000 | [diff] [blame] | 798 | tag_args++; |
| 799 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 800 | } |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 801 | element->params = skin_buffer_to_offset(params); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 802 | |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 803 | /* Checking for a premature end */ |
Robert Bieber | 32ff4e9 | 2010-07-04 01:39:57 +0000 | [diff] [blame] | 804 | if(*tag_args != '\0' && !optional) |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 805 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 806 | skin_error(INSUFFICIENT_ARGS, cursor); |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 807 | return 0; |
| 808 | } |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 809 | #ifdef ROCKBOX |
| 810 | if (callback) |
| 811 | { |
| 812 | if (callback(element, callback_data) == CALLBACK_ERROR) |
| 813 | return 0; |
| 814 | } |
| 815 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 816 | *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 Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 823 | * ARGLISTSEPARATESYM. Only set that flag when parsing within a conditional |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 824 | */ |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 825 | static int skin_parse_text(struct skin_element* element, const char** document, |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 826 | int conditional) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 827 | { |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 828 | const char* cursor = *document; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 829 | int length = 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 830 | int dest; |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 831 | char *text = NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 832 | |
| 833 | /* First figure out how much text we're copying */ |
| 834 | while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM |
| 835 | && *cursor != COMMENTSYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 836 | && !((*cursor == ARGLISTSEPARATESYM |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 837 | || *cursor == ARGLISTCLOSESYM |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 838 | || *cursor == ENUMLISTSEPARATESYM |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 839 | || *cursor == ENUMLISTCLOSESYM) |
| 840 | && conditional)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 841 | { |
| 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 Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 860 | 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 Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 864 | return 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 865 | |
| 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 Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 872 | text[dest] = *cursor; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 873 | cursor++; |
| 874 | } |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 875 | text[length] = '\0'; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 876 | |
| 877 | #ifdef ROCKBOX |
| 878 | if (callback) |
| 879 | { |
| 880 | if (callback(element, callback_data) == CALLBACK_ERROR) |
| 881 | return 0; |
| 882 | } |
| 883 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 884 | |
| 885 | *document = cursor; |
| 886 | |
| 887 | return 1; |
| 888 | } |
| 889 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 890 | static int skin_parse_conditional(struct skin_element* element, const char** document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 891 | { |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 892 | const char* cursor = *document + 1; /* Starting past the "%" */ |
| 893 | const char* bookmark; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 894 | int children = 1; |
| 895 | int i; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 896 | |
| 897 | #ifdef ROCKBOX |
| 898 | bool feature_available = true; |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 899 | const char *false_branch = NULL; |
Jonathan Gordon | 943de3c | 2010-10-28 11:00:36 +0000 | [diff] [blame] | 900 | const char *conditional_end = NULL; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 901 | #endif |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 902 | OFFSETTYPE(struct skin_element*)* children_array = NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 903 | |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 904 | /* 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 909 | element->line = skin_line; |
| 910 | |
| 911 | /* Parsing the tag first */ |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 912 | if(!skin_parse_tag(element, &cursor)) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 913 | return 0; |
| 914 | |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 915 | 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 932 | /* Counting the children */ |
Robert Bieber | 1937b1b | 2010-05-25 17:22:39 +0000 | [diff] [blame] | 933 | if(*(cursor++) != ENUMLISTOPENSYM) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 934 | { |
Robert Bieber | 3c4fb8a | 2010-07-18 00:59:02 +0000 | [diff] [blame] | 935 | skin_error(ARGLIST_EXPECTED, cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 936 | return 0; |
| 937 | } |
| 938 | bookmark = cursor; |
Jonathan Gordon | f417312 | 2012-02-01 21:53:31 +1100 | [diff] [blame] | 939 | while(*cursor != ENUMLISTCLOSESYM && *cursor != '\0') |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 940 | { |
| 941 | if(*cursor == COMMENTSYM) |
| 942 | { |
| 943 | skip_comment(&cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 944 | } |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 945 | else if(*cursor == TAGSYM) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 946 | { |
Jonathan Gordon | 2d3c43d | 2012-04-21 23:34:42 +1000 | [diff] [blame] | 947 | skip_tag(&cursor); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 948 | } |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 949 | else if(*cursor == ENUMLISTSEPARATESYM) |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 950 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 951 | children++; |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 952 | cursor++; |
Jonathan Gordon | f417312 | 2012-02-01 21:53:31 +1100 | [diff] [blame] | 953 | if (*cursor == '\n') |
| 954 | cursor++; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 955 | #ifdef ROCKBOX |
| 956 | if (false_branch == NULL && !feature_available) |
| 957 | { |
| 958 | false_branch = cursor; |
| 959 | children--; |
| 960 | } |
| 961 | #endif |
Robert Bieber | 06ea93d | 2010-06-07 23:49:06 +0000 | [diff] [blame] | 962 | } |
| 963 | else |
| 964 | { |
| 965 | cursor++; |
| 966 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 967 | } |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 968 | #ifdef ROCKBOX |
| 969 | if (*cursor == ENUMLISTCLOSESYM && |
| 970 | false_branch == NULL && !feature_available) |
| 971 | { |
| 972 | false_branch = cursor+1; |
| 973 | children--; |
| 974 | } |
Jonathan Gordon | 943de3c | 2010-10-28 11:00:36 +0000 | [diff] [blame] | 975 | if (element->tag->flags&FEATURE_TAG) |
| 976 | { |
Jonathan Gordon | f6cf295 | 2010-11-03 11:08:55 +0000 | [diff] [blame] | 977 | if (feature_available && children > 1) |
Jonathan Gordon | 943de3c | 2010-10-28 11:00:36 +0000 | [diff] [blame] | 978 | children--; |
| 979 | } |
| 980 | conditional_end = cursor; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 981 | /* if we are skipping the true branch fix that up */ |
| 982 | cursor = false_branch ? false_branch : bookmark; |
| 983 | #else |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 984 | cursor = bookmark; |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 985 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 986 | /* Parsing the children */ |
Jonathan Gordon | 78a11cf | 2010-12-24 07:58:26 +0000 | [diff] [blame] | 987 | |
| 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 993 | { |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 994 | const char* emptyline= ""; |
| 995 | children = 1; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 996 | children_array = skin_alloc_children(children); |
| 997 | if (!children_array) |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 998 | return 0; |
| 999 | element->children_count = children; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1000 | children_array[0] = skin_buffer_to_offset(skin_parse_code_as_arg(&emptyline)); |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 1001 | } |
| 1002 | else |
| 1003 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1004 | children_array = skin_alloc_children(children); |
| 1005 | if (!children_array) |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 1006 | return 0; |
| 1007 | element->children_count = children; |
Jonathan Gordon | 78a11cf | 2010-12-24 07:58:26 +0000 | [diff] [blame] | 1008 | |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 1009 | for(i = 0; i < children; i++) |
| 1010 | { |
Jonathan Gordon | f417312 | 2012-02-01 21:53:31 +1100 | [diff] [blame] | 1011 | if (*cursor == '\n') |
| 1012 | { |
| 1013 | skin_line++; |
| 1014 | cursor++; |
| 1015 | } |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1016 | children_array[i] = skin_buffer_to_offset(skin_parse_code_as_arg(&cursor)); |
| 1017 | if (children_array[i] < 0) |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 1018 | return 0; |
| 1019 | skip_whitespace(&cursor); |
Jonathan Gordon | 943de3c | 2010-10-28 11:00:36 +0000 | [diff] [blame] | 1020 | #ifdef ROCKBOX |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 1021 | if ((element->tag->flags&FEATURE_TAG) && feature_available) |
| 1022 | cursor = conditional_end; |
Jonathan Gordon | 943de3c | 2010-10-28 11:00:36 +0000 | [diff] [blame] | 1023 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1024 | |
Nils Wallménius | e52600e | 2011-10-08 13:54:55 +0000 | [diff] [blame] | 1025 | 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1041 | *document = cursor; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1042 | element->children = skin_buffer_to_offset(children_array); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1043 | |
| 1044 | return 1; |
| 1045 | } |
| 1046 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 1047 | static int skin_parse_comment(struct skin_element* element, const char** document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1048 | { |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 1049 | const char* cursor = *document; |
Jonathan Gordon | b2ea95e | 2010-06-17 07:56:51 +0000 | [diff] [blame] | 1050 | #ifndef ROCKBOX |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1051 | char* text = NULL; |
Jonathan Gordon | b2ea95e | 2010-06-17 07:56:51 +0000 | [diff] [blame] | 1052 | #endif |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1053 | 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 Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1063 | #ifdef ROCKBOX |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1064 | element->data = INVALID_OFFSET; |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1065 | #else |
| 1066 | element->data = text = skin_alloc_string(length); |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 1067 | if (!element->data) |
| 1068 | return 0; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1069 | /* We copy from one char past cursor to leave out the # */ |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1070 | memcpy((void*)text, (void*)(cursor + 1), |
Robert Bieber | a9848ce | 2010-06-01 21:25:02 +0000 | [diff] [blame] | 1071 | sizeof(char) * (length-1)); |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1072 | text[length - 1] = '\0'; |
| 1073 | #endif |
Robert Bieber | a9848ce | 2010-06-01 21:25:02 +0000 | [diff] [blame] | 1074 | if(cursor[length] == '\n') |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1075 | skin_line++; |
| 1076 | |
Robert Bieber | a9848ce | 2010-06-01 21:25:02 +0000 | [diff] [blame] | 1077 | *document += (length); /* Move cursor up past # and all text */ |
| 1078 | if(**document == '\n') |
| 1079 | (*document)++; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1080 | |
| 1081 | return 1; |
| 1082 | } |
| 1083 | |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 1084 | static struct skin_element* skin_parse_code_as_arg(const char** document) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1085 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1086 | int sublines = 0; |
Nils Wallménius | 597ccdd | 2010-07-31 16:25:41 +0000 | [diff] [blame] | 1087 | const char* cursor = *document; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1088 | |
| 1089 | /* Checking for sublines */ |
Robert Bieber | 4b77e82 | 2010-06-10 21:22:16 +0000 | [diff] [blame] | 1090 | while(*cursor != '\n' && *cursor != '\0' |
Bertrik Sikken | fffbdcc | 2010-11-05 09:51:19 +0000 | [diff] [blame] | 1091 | && *cursor != ENUMLISTSEPARATESYM && *cursor != ARGLISTSEPARATESYM |
Robert Bieber | 4b77e82 | 2010-06-10 21:22:16 +0000 | [diff] [blame] | 1092 | && *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1093 | { |
| 1094 | if(*cursor == MULTILINESYM) |
| 1095 | { |
| 1096 | sublines = 1; |
| 1097 | break; |
| 1098 | } |
| 1099 | else if(*cursor == TAGSYM) |
| 1100 | { |
Jonathan Gordon | 2d3c43d | 2012-04-21 23:34:42 +1000 | [diff] [blame] | 1101 | skip_tag(&cursor); |
Robert Bieber | 4b77e82 | 2010-06-10 21:22:16 +0000 | [diff] [blame] | 1102 | } |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1103 | 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 Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1116 | /* Memory management */ |
| 1117 | struct skin_element* skin_alloc_element() |
| 1118 | { |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1119 | struct skin_element* retval = (struct skin_element*) |
Jonathan Gordon | 95b0cef | 2010-06-17 13:54:09 +0000 | [diff] [blame] | 1120 | skin_buffer_alloc(sizeof(struct skin_element)); |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 1121 | if (!retval) |
| 1122 | return NULL; |
Jonathan Gordon | 35b09cb | 2010-06-13 03:13:01 +0000 | [diff] [blame] | 1123 | retval->type = UNKNOWN; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1124 | retval->next = skin_buffer_to_offset(NULL); |
| 1125 | retval->params = skin_buffer_to_offset(NULL); |
Robert Bieber | 1b613f5 | 2010-06-11 08:03:32 +0000 | [diff] [blame] | 1126 | retval->tag = NULL; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1127 | retval->params_count = 0; |
| 1128 | retval->children_count = 0; |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1129 | retval->data = INVALID_OFFSET; |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1130 | |
| 1131 | return retval; |
| 1132 | |
| 1133 | } |
Jonathan Gordon | 2d31d77 | 2010-07-29 12:37:48 +0000 | [diff] [blame] | 1134 | /* 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 Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1139 | struct skin_tag_parameter* skin_alloc_params(int count) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1140 | { |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1141 | size_t size = sizeof(struct skin_tag_parameter) * count; |
Jonathan Gordon | 95b0cef | 2010-06-17 13:54:09 +0000 | [diff] [blame] | 1142 | return (struct skin_tag_parameter*)skin_buffer_alloc(size); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1143 | |
| 1144 | } |
| 1145 | |
| 1146 | char* skin_alloc_string(int length) |
| 1147 | { |
Jonathan Gordon | 95b0cef | 2010-06-17 13:54:09 +0000 | [diff] [blame] | 1148 | return (char*)skin_buffer_alloc(sizeof(char) * (length + 1)); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1149 | } |
| 1150 | |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1151 | OFFSETTYPE(struct skin_element*)* skin_alloc_children(int count) |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1152 | { |
Jonathan Gordon | 9e07ef2 | 2011-11-15 14:11:08 +0000 | [diff] [blame] | 1153 | return (OFFSETTYPE(struct skin_element*)*) |
Jonathan Gordon | 95b0cef | 2010-06-17 13:54:09 +0000 | [diff] [blame] | 1154 | skin_buffer_alloc(sizeof(struct skin_element*) * count); |
Robert Bieber | d5b24dd | 2010-05-25 15:19:52 +0000 | [diff] [blame] | 1155 | } |
Robert Bieber | 0769fc5 | 2010-05-25 22:24:08 +0000 | [diff] [blame] | 1156 | |
| 1157 | void skin_free_tree(struct skin_element* root) |
| 1158 | { |
Robert Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1159 | #ifndef ROCKBOX |
Robert Bieber | 0769fc5 | 2010-05-25 22:24:08 +0000 | [diff] [blame] | 1160 | 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 Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1168 | if(root->type == TEXT || root->type == COMMENT) |
| 1169 | free(root->data); |
Robert Bieber | 0769fc5 | 2010-05-25 22:24:08 +0000 | [diff] [blame] | 1170 | |
| 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 Bieber | 64321ad | 2010-06-10 21:02:44 +0000 | [diff] [blame] | 1186 | #else |
| 1187 | (void)root; |
| 1188 | #endif |
Robert Bieber | 0769fc5 | 2010-05-25 22:24:08 +0000 | [diff] [blame] | 1189 | } |