2010-05-25 15:19:52 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2010 Robert Bieber
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
2010-06-17 07:56:51 +00:00
|
|
|
#include "skin_buffer.h"
|
2010-05-25 15:19:52 +00:00
|
|
|
#include "skin_parser.h"
|
|
|
|
#include "skin_debug.h"
|
|
|
|
#include "tag_table.h"
|
|
|
|
#include "symbols.h"
|
|
|
|
#include "skin_scan.h"
|
|
|
|
|
|
|
|
/* Global variables for the parser */
|
|
|
|
int skin_line = 0;
|
|
|
|
|
|
|
|
/* Auxiliary parsing functions (not visible at global scope) */
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_viewport(char** document);
|
|
|
|
static struct skin_element* skin_parse_line(char** document);
|
|
|
|
static struct skin_element* skin_parse_line_optional(char** document,
|
|
|
|
int conditional);
|
|
|
|
static struct skin_element* skin_parse_sublines(char** document);
|
|
|
|
static struct skin_element* skin_parse_sublines_optional(char** document,
|
|
|
|
int conditional);
|
|
|
|
|
|
|
|
static int skin_parse_tag(struct skin_element* element, char** document);
|
|
|
|
static int skin_parse_text(struct skin_element* element, char** document,
|
|
|
|
int conditional);
|
|
|
|
static int skin_parse_conditional(struct skin_element* element,
|
|
|
|
char** document);
|
|
|
|
static int skin_parse_comment(struct skin_element* element, char** document);
|
|
|
|
static struct skin_element* skin_parse_code_as_arg(char** document);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
2010-06-17 13:54:09 +00:00
|
|
|
|
|
|
|
|
2010-06-01 21:25:02 +00:00
|
|
|
struct skin_element* skin_parse(const char* document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
2010-06-01 07:11:23 +00:00
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
struct skin_element* root = NULL;
|
|
|
|
struct skin_element* last = NULL;
|
|
|
|
|
|
|
|
struct skin_element** to_write = 0;
|
|
|
|
|
2010-06-01 21:25:02 +00:00
|
|
|
char* cursor = (char*)document; /*Keeps track of location in the document*/
|
2010-06-17 07:56:51 +00:00
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
skin_line = 1;
|
|
|
|
|
2010-06-07 21:09:13 +00:00
|
|
|
skin_clear_errors();
|
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
while(*cursor != '\0')
|
2010-06-01 07:11:23 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
if(!root)
|
|
|
|
to_write = &root;
|
|
|
|
else
|
|
|
|
to_write = &(last->next);
|
|
|
|
|
|
|
|
|
|
|
|
*to_write = skin_parse_viewport(&cursor);
|
|
|
|
last = *to_write;
|
|
|
|
if(!last)
|
2010-06-08 19:34:27 +00:00
|
|
|
{
|
|
|
|
skin_free_tree(root); /* Clearing any memory already used */
|
2010-06-01 07:11:23 +00:00
|
|
|
return NULL;
|
2010-06-08 19:34:27 +00:00
|
|
|
}
|
2010-06-01 07:11:23 +00:00
|
|
|
|
|
|
|
/* Making sure last is at the end */
|
|
|
|
while(last->next)
|
|
|
|
last = last->next;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return root;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_viewport(char** document)
|
2010-06-01 07:11:23 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
struct skin_element* root = NULL;
|
|
|
|
struct skin_element* last = NULL;
|
|
|
|
struct skin_element* retval = NULL;
|
|
|
|
|
|
|
|
retval = skin_alloc_element();
|
|
|
|
retval->type = VIEWPORT;
|
|
|
|
retval->children_count = 1;
|
|
|
|
retval->line = skin_line;
|
|
|
|
|
|
|
|
struct skin_element** to_write = 0;
|
|
|
|
|
|
|
|
char* cursor = *document; /* Keeps track of location in the document */
|
|
|
|
char* bookmark; /* Used when we need to look ahead */
|
|
|
|
|
|
|
|
int sublines = 0; /* Flag for parsing sublines */
|
|
|
|
|
2010-06-01 18:31:58 +00:00
|
|
|
/* Parsing out the viewport tag if there is one */
|
|
|
|
if(check_viewport(cursor))
|
|
|
|
{
|
2010-06-11 08:03:32 +00:00
|
|
|
skin_parse_tag(retval, &cursor);
|
2010-06-02 07:04:33 +00:00
|
|
|
if(*cursor == '\n')
|
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
skin_line++;
|
|
|
|
}
|
2010-06-01 18:31:58 +00:00
|
|
|
}
|
2010-06-11 08:03:32 +00:00
|
|
|
|
|
|
|
retval->children_count = 1;
|
|
|
|
retval->children = skin_alloc_children(1);
|
2010-06-01 18:31:58 +00:00
|
|
|
|
|
|
|
|
2010-06-11 08:03:32 +00:00
|
|
|
do
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/* First, we check to see if this line will contain sublines */
|
|
|
|
bookmark = cursor;
|
|
|
|
sublines = 0;
|
2010-06-01 07:11:23 +00:00
|
|
|
while(*cursor != '\n' && *cursor != '\0'
|
|
|
|
&& !(check_viewport(cursor) && cursor != *document))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
if(*cursor == MULTILINESYM)
|
|
|
|
{
|
|
|
|
sublines = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if(*cursor == TAGSYM)
|
|
|
|
{
|
|
|
|
/* A ';' directly after a '%' doesn't count */
|
|
|
|
cursor ++;
|
|
|
|
|
|
|
|
if(*cursor == '\0')
|
|
|
|
break;
|
|
|
|
|
|
|
|
cursor++;
|
|
|
|
}
|
2010-05-30 01:56:50 +00:00
|
|
|
else if(*cursor == COMMENTSYM)
|
|
|
|
{
|
|
|
|
skip_comment(&cursor);
|
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == ARGLISTOPENSYM)
|
|
|
|
{
|
|
|
|
skip_arglist(&cursor);
|
|
|
|
}
|
|
|
|
else if(*cursor == ENUMLISTOPENSYM)
|
|
|
|
{
|
2010-06-10 21:22:16 +00:00
|
|
|
skip_enumlist(&cursor);
|
2010-06-07 23:49:06 +00:00
|
|
|
}
|
2010-05-25 15:19:52 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Advancing the cursor as normal */
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cursor = bookmark;
|
|
|
|
|
|
|
|
if(!root)
|
|
|
|
to_write = &root;
|
|
|
|
else
|
|
|
|
to_write = &(last->next);
|
|
|
|
|
2010-06-02 06:41:41 +00:00
|
|
|
if(sublines)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
*to_write = skin_parse_sublines(&cursor);
|
|
|
|
last = *to_write;
|
|
|
|
if(!last)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
|
|
|
|
*to_write = skin_parse_line(&cursor);
|
|
|
|
last = *to_write;
|
|
|
|
if(!last)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Making sure last is at the end */
|
|
|
|
while(last->next)
|
|
|
|
last = last->next;
|
|
|
|
|
2010-06-02 06:41:41 +00:00
|
|
|
if(*cursor == '\n')
|
2010-06-02 07:04:33 +00:00
|
|
|
{
|
2010-06-02 06:41:41 +00:00
|
|
|
cursor++;
|
2010-06-02 07:04:33 +00:00
|
|
|
skin_line++;
|
|
|
|
}
|
2010-06-01 07:11:23 +00:00
|
|
|
}
|
2010-06-11 08:03:32 +00:00
|
|
|
while(*cursor != '\0' && !(check_viewport(cursor) && cursor != *document));
|
2010-06-01 07:11:23 +00:00
|
|
|
|
|
|
|
*document = cursor;
|
|
|
|
|
2010-06-11 08:03:32 +00:00
|
|
|
retval->children[0] = root;
|
2010-06-01 07:11:23 +00:00
|
|
|
return retval;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Auxiliary Parsing Functions */
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_line(char**document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
return skin_parse_line_optional(document, 0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If conditional is set to true, then this will break upon encountering
|
|
|
|
* SEPERATESYM. This should only be used when parsing a line inside a
|
|
|
|
* conditional, otherwise just use the wrapper function skin_parse_line()
|
|
|
|
*/
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_line_optional(char** document,
|
|
|
|
int conditional)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
char* cursor = *document;
|
|
|
|
|
|
|
|
struct skin_element* root = NULL;
|
|
|
|
struct skin_element* current = NULL;
|
2010-05-27 19:57:15 +00:00
|
|
|
struct skin_element* retval = NULL;
|
|
|
|
|
|
|
|
/* A wrapper for the line */
|
|
|
|
retval = skin_alloc_element();
|
|
|
|
retval->type = LINE;
|
|
|
|
retval->line = skin_line;
|
2010-06-22 18:51:44 +00:00
|
|
|
if(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
|
2010-06-11 19:51:34 +00:00
|
|
|
&& !(conditional && (*cursor == ARGLISTSEPERATESYM
|
|
|
|
|| *cursor == ARGLISTCLOSESYM
|
|
|
|
|| *cursor == ENUMLISTSEPERATESYM
|
|
|
|
|| *cursor == ENUMLISTCLOSESYM)))
|
|
|
|
{
|
2010-06-11 08:03:32 +00:00
|
|
|
retval->children_count = 1;
|
2010-06-11 19:51:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
retval->children_count = 0;
|
|
|
|
}
|
|
|
|
|
2010-06-11 08:03:32 +00:00
|
|
|
if(retval->children_count > 0)
|
|
|
|
retval->children = skin_alloc_children(1);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
while(*cursor != '\n' && *cursor != '\0' && *cursor != MULTILINESYM
|
2010-05-25 17:22:39 +00:00
|
|
|
&& !((*cursor == ARGLISTSEPERATESYM
|
|
|
|
|| *cursor == ARGLISTCLOSESYM
|
|
|
|
|| *cursor == ENUMLISTSEPERATESYM
|
|
|
|
|| *cursor == ENUMLISTCLOSESYM)
|
2010-06-01 07:11:23 +00:00
|
|
|
&& conditional)
|
|
|
|
&& !(check_viewport(cursor) && cursor != *document))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
/* Allocating memory if necessary */
|
|
|
|
if(root)
|
|
|
|
{
|
|
|
|
current->next = skin_alloc_element();
|
|
|
|
current = current->next;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
current = skin_alloc_element();
|
|
|
|
root = current;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parsing the current element */
|
|
|
|
if(*cursor == TAGSYM && cursor[1] == CONDITIONSYM)
|
|
|
|
{
|
|
|
|
if(!skin_parse_conditional(current, &cursor))
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-06-02 05:45:34 +00:00
|
|
|
else if(*cursor == TAGSYM && !find_escape_character(cursor[1]))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
if(!skin_parse_tag(current, &cursor))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else if(*cursor == COMMENTSYM)
|
|
|
|
{
|
|
|
|
if(!skin_parse_comment(current, &cursor))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(!skin_parse_text(current, &cursor, conditional))
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Moving up the calling function's pointer */
|
|
|
|
*document = cursor;
|
|
|
|
|
2010-06-11 08:03:32 +00:00
|
|
|
if(root)
|
|
|
|
retval->children[0] = root;
|
2010-05-27 19:57:15 +00:00
|
|
|
return retval;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_sublines(char** document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
return skin_parse_sublines_optional(document, 0);
|
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_sublines_optional(char** document,
|
2010-05-25 15:19:52 +00:00
|
|
|
int conditional)
|
|
|
|
{
|
|
|
|
struct skin_element* retval;
|
|
|
|
char* cursor = *document;
|
|
|
|
int sublines = 1;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
retval = skin_alloc_element();
|
|
|
|
retval->type = SUBLINES;
|
|
|
|
retval->next = NULL;
|
2010-05-27 19:57:15 +00:00
|
|
|
retval->line = skin_line;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
/* First we count the sublines */
|
|
|
|
while(*cursor != '\0' && *cursor != '\n'
|
2010-05-25 17:22:39 +00:00
|
|
|
&& !((*cursor == ARGLISTSEPERATESYM
|
|
|
|
|| *cursor == ARGLISTCLOSESYM
|
|
|
|
|| *cursor == ENUMLISTSEPERATESYM
|
|
|
|
|| *cursor == ENUMLISTCLOSESYM)
|
2010-06-01 07:11:23 +00:00
|
|
|
&& conditional)
|
|
|
|
&& !(check_viewport(cursor) && cursor != *document))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
if(*cursor == COMMENTSYM)
|
2010-06-02 07:48:48 +00:00
|
|
|
{
|
2010-05-25 15:19:52 +00:00
|
|
|
skip_comment(&cursor);
|
2010-06-02 07:48:48 +00:00
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == ENUMLISTOPENSYM)
|
2010-06-02 07:48:48 +00:00
|
|
|
{
|
2010-06-07 23:49:06 +00:00
|
|
|
skip_enumlist(&cursor);
|
2010-06-02 07:48:48 +00:00
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == ARGLISTOPENSYM)
|
|
|
|
{
|
|
|
|
skip_arglist(&cursor);
|
|
|
|
}
|
|
|
|
else if(*cursor == TAGSYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
if(*cursor == '\0' || *cursor == '\n')
|
|
|
|
break;
|
2010-06-07 23:49:06 +00:00
|
|
|
cursor++;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == MULTILINESYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
sublines++;
|
2010-06-07 23:49:06 +00:00
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor++;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ...and then we parse them */
|
|
|
|
retval->children_count = sublines;
|
|
|
|
retval->children = skin_alloc_children(sublines);
|
|
|
|
|
|
|
|
cursor = *document;
|
|
|
|
for(i = 0; i < sublines; i++)
|
|
|
|
{
|
|
|
|
retval->children[i] = skin_parse_line_optional(&cursor, conditional);
|
|
|
|
skip_whitespace(&cursor);
|
|
|
|
|
|
|
|
if(*cursor != MULTILINESYM && i != sublines - 1)
|
|
|
|
{
|
|
|
|
skin_error(MULTILINE_EXPECTED);
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-27 19:57:15 +00:00
|
|
|
else if(i != sublines - 1)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*document = cursor;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static int skin_parse_tag(struct skin_element* element, char** document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
char* cursor = *document + 1;
|
|
|
|
char* bookmark;
|
|
|
|
|
|
|
|
char tag_name[3];
|
|
|
|
char* tag_args;
|
2010-06-01 16:44:52 +00:00
|
|
|
struct tag_info *tag;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
int num_args = 1;
|
|
|
|
int i;
|
2010-05-25 17:22:39 +00:00
|
|
|
int star = 0; /* Flag for the all-or-none option */
|
|
|
|
int req_args; /* To mark when we enter optional arguments */
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
int optional = 0;
|
|
|
|
|
|
|
|
/* Checking the tag name */
|
|
|
|
tag_name[0] = cursor[0];
|
|
|
|
tag_name[1] = cursor[1];
|
|
|
|
tag_name[2] = '\0';
|
|
|
|
|
|
|
|
/* First we check the two characters after the '%', then a single char */
|
2010-06-01 16:44:52 +00:00
|
|
|
tag = find_tag(tag_name);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
2010-06-01 16:44:52 +00:00
|
|
|
if(!tag)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
tag_name[1] = '\0';
|
2010-06-01 16:44:52 +00:00
|
|
|
tag = find_tag(tag_name);
|
2010-05-25 15:19:52 +00:00
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor += 2;
|
|
|
|
}
|
|
|
|
|
2010-06-01 16:44:52 +00:00
|
|
|
if(!tag)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
skin_error(ILLEGAL_TAG);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copying basic tag info */
|
2010-06-11 08:03:32 +00:00
|
|
|
if(element->type != CONDITIONAL && element->type != VIEWPORT)
|
2010-06-10 21:02:44 +00:00
|
|
|
element->type = TAG;
|
2010-06-01 16:44:52 +00:00
|
|
|
element->tag = tag;
|
|
|
|
tag_args = tag->params;
|
2010-05-25 15:19:52 +00:00
|
|
|
element->line = skin_line;
|
|
|
|
|
2010-05-25 17:22:39 +00:00
|
|
|
/* Checking for the * flag */
|
|
|
|
if(tag_args[0] == '*')
|
|
|
|
{
|
|
|
|
star = 1;
|
|
|
|
tag_args++;
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
/* If this tag has no arguments, we can bail out now */
|
2010-05-25 17:22:39 +00:00
|
|
|
if(strlen(tag_args) == 0
|
2010-06-02 05:27:37 +00:00
|
|
|
|| (tag_args[0] == '|' && *cursor != ARGLISTOPENSYM)
|
|
|
|
|| (star && *cursor != ARGLISTOPENSYM))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
*document = cursor;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-07-01 08:45:49 +00:00
|
|
|
/* Eating a newline if necessary */
|
|
|
|
if(tag_args[0] == '\n')
|
|
|
|
{
|
2010-07-01 10:17:41 +00:00
|
|
|
while (*cursor && *cursor != '\n')
|
|
|
|
cursor++;
|
|
|
|
if (*cursor)
|
2010-07-01 08:45:49 +00:00
|
|
|
cursor++;
|
|
|
|
*document = cursor;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
/* Checking the number of arguments and allocating args */
|
2010-07-01 08:45:49 +00:00
|
|
|
if(*cursor != ARGLISTOPENSYM && tag_args[0] != '|'
|
|
|
|
&& tag_args[0] != '\n')
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
skin_error(ARGLIST_EXPECTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
|
2010-06-07 23:49:06 +00:00
|
|
|
bookmark = cursor;
|
|
|
|
while(*cursor != '\n' && *cursor != '\0' && *cursor != ARGLISTCLOSESYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
/* Skipping over escaped characters */
|
|
|
|
if(*cursor == TAGSYM)
|
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
if(*cursor == '\0')
|
|
|
|
break;
|
2010-06-07 23:49:06 +00:00
|
|
|
cursor++;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == COMMENTSYM)
|
|
|
|
{
|
2010-05-25 15:19:52 +00:00
|
|
|
skip_comment(&cursor);
|
2010-06-07 23:49:06 +00:00
|
|
|
}
|
|
|
|
else if(*cursor == ARGLISTOPENSYM)
|
|
|
|
{
|
|
|
|
skip_arglist(&cursor);
|
|
|
|
}
|
|
|
|
else if(*cursor == ARGLISTSEPERATESYM)
|
|
|
|
{
|
2010-05-25 15:19:52 +00:00
|
|
|
num_args++;
|
2010-06-07 23:49:06 +00:00
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
}
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
cursor = bookmark; /* Restoring the cursor */
|
|
|
|
element->params_count = num_args;
|
|
|
|
element->params = skin_alloc_params(num_args);
|
|
|
|
|
|
|
|
/* Now we have to actually parse each argument */
|
2010-05-25 17:22:39 +00:00
|
|
|
for(i = 0; i < num_args; i++)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
/* Making sure we haven't run out of arguments */
|
2010-07-01 08:45:49 +00:00
|
|
|
if(*tag_args == '\0' || *tag_args == '\n')
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
skin_error(TOO_MANY_ARGS);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Checking for the optional bar */
|
|
|
|
if(*tag_args == '|')
|
|
|
|
{
|
|
|
|
optional = 1;
|
2010-06-01 07:11:23 +00:00
|
|
|
req_args = i;
|
2010-05-25 15:19:52 +00:00
|
|
|
tag_args++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Scanning the arguments */
|
2010-05-25 17:22:39 +00:00
|
|
|
skip_whitespace(&cursor);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
|
2010-05-25 17:22:39 +00:00
|
|
|
/* Checking for comments */
|
|
|
|
if(*cursor == COMMENTSYM)
|
|
|
|
skip_comment(&cursor);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
2010-06-01 19:55:20 +00:00
|
|
|
/* Storing the type code */
|
|
|
|
element->params[i].type_code = *tag_args;
|
|
|
|
|
2010-05-25 17:22:39 +00:00
|
|
|
/* Checking a nullable argument for null */
|
2010-06-02 06:52:17 +00:00
|
|
|
if(*cursor == DEFAULTSYM && !isdigit(cursor[1]))
|
2010-05-25 17:22:39 +00:00
|
|
|
{
|
|
|
|
if(islower(*tag_args))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
2010-05-25 17:22:39 +00:00
|
|
|
element->params[i].type = DEFAULT;
|
|
|
|
cursor++;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
2010-05-25 17:22:39 +00:00
|
|
|
else
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
2010-05-25 17:22:39 +00:00
|
|
|
skin_error(DEFAULT_NOT_ALLOWED);
|
|
|
|
return 0;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
2010-05-25 17:22:39 +00:00
|
|
|
}
|
|
|
|
else if(tolower(*tag_args) == 'i')
|
|
|
|
{
|
|
|
|
/* Scanning an int argument */
|
2010-06-02 06:52:17 +00:00
|
|
|
if(!isdigit(*cursor) && *cursor != '-')
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
2010-05-25 17:22:39 +00:00
|
|
|
skin_error(INT_EXPECTED);
|
|
|
|
return 0;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
|
2010-05-25 17:22:39 +00:00
|
|
|
element->params[i].type = NUMERIC;
|
|
|
|
element->params[i].data.numeric = scan_int(&cursor);
|
|
|
|
}
|
2010-06-09 16:15:01 +00:00
|
|
|
else if(tolower(*tag_args) == 'n' ||
|
|
|
|
tolower(*tag_args) == 's' || tolower(*tag_args) == 'f')
|
2010-05-25 17:22:39 +00:00
|
|
|
{
|
|
|
|
/* Scanning a string argument */
|
|
|
|
element->params[i].type = STRING;
|
|
|
|
element->params[i].data.text = scan_string(&cursor);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
2010-05-25 17:22:39 +00:00
|
|
|
}
|
|
|
|
else if(tolower(*tag_args) == 'c')
|
|
|
|
{
|
|
|
|
/* Recursively parsing a code argument */
|
|
|
|
element->params[i].type = CODE;
|
|
|
|
element->params[i].data.code = skin_parse_code_as_arg(&cursor);
|
|
|
|
if(!element->params[i].data.code)
|
2010-05-25 15:19:52 +00:00
|
|
|
return 0;
|
2010-05-25 17:22:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
skip_whitespace(&cursor);
|
|
|
|
|
|
|
|
if(*cursor != ARGLISTSEPERATESYM && i < num_args - 1)
|
|
|
|
{
|
|
|
|
skin_error(SEPERATOR_EXPECTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else if(*cursor != ARGLISTCLOSESYM && i == num_args - 1)
|
|
|
|
{
|
|
|
|
skin_error(CLOSE_EXPECTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor++;
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
|
2010-06-09 16:15:01 +00:00
|
|
|
if (*tag_args != 'N')
|
|
|
|
tag_args++;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
2010-06-01 07:11:23 +00:00
|
|
|
/* Checking for the optional bar */
|
|
|
|
if(*tag_args == '|')
|
|
|
|
{
|
|
|
|
optional = 1;
|
|
|
|
req_args = i + 1;
|
|
|
|
tag_args++;
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
|
2010-05-25 17:22:39 +00:00
|
|
|
/* Checking for a premature end */
|
2010-07-01 08:45:49 +00:00
|
|
|
if(*tag_args != '\0' && *tag_args != '\n' && !optional)
|
2010-05-25 17:22:39 +00:00
|
|
|
{
|
|
|
|
skin_error(INSUFFICIENT_ARGS);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-07-01 08:45:49 +00:00
|
|
|
if(*tag_args == '\n')
|
2010-07-01 10:17:41 +00:00
|
|
|
{
|
|
|
|
while (*cursor && *cursor != '\n')
|
2010-07-01 08:45:49 +00:00
|
|
|
cursor++;
|
2010-07-01 10:17:41 +00:00
|
|
|
if (*cursor)
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
|
2010-05-25 15:19:52 +00:00
|
|
|
*document = cursor;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the conditional flag is set true, then parsing text will stop at an
|
|
|
|
* ARGLISTSEPERATESYM. Only set that flag when parsing within a conditional
|
|
|
|
*/
|
2010-06-10 21:02:44 +00:00
|
|
|
static int skin_parse_text(struct skin_element* element, char** document,
|
|
|
|
int conditional)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
char* cursor = *document;
|
|
|
|
int length = 0;
|
|
|
|
int dest;
|
2010-06-10 21:02:44 +00:00
|
|
|
char *text = NULL;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
/* First figure out how much text we're copying */
|
|
|
|
while(*cursor != '\0' && *cursor != '\n' && *cursor != MULTILINESYM
|
|
|
|
&& *cursor != COMMENTSYM
|
2010-05-25 17:22:39 +00:00
|
|
|
&& !((*cursor == ARGLISTSEPERATESYM
|
|
|
|
|| *cursor == ARGLISTCLOSESYM
|
|
|
|
|| *cursor == ENUMLISTSEPERATESYM
|
|
|
|
|| *cursor == ENUMLISTCLOSESYM)
|
|
|
|
&& conditional))
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
/* Dealing with possibility of escaped characters */
|
|
|
|
if(*cursor == TAGSYM)
|
|
|
|
{
|
|
|
|
if(find_escape_character(cursor[1]))
|
|
|
|
cursor++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
length++;
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor = *document;
|
|
|
|
|
|
|
|
/* Copying the text into the element struct */
|
|
|
|
element->type = TEXT;
|
|
|
|
element->line = skin_line;
|
|
|
|
element->next = NULL;
|
2010-06-10 21:02:44 +00:00
|
|
|
element->data = text = skin_alloc_string(length);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
for(dest = 0; dest < length; dest++)
|
|
|
|
{
|
|
|
|
/* Advancing cursor if we've encountered an escaped character */
|
|
|
|
if(*cursor == TAGSYM)
|
|
|
|
cursor++;
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
text[dest] = *cursor;
|
2010-05-25 15:19:52 +00:00
|
|
|
cursor++;
|
|
|
|
}
|
2010-06-10 21:02:44 +00:00
|
|
|
text[length] = '\0';
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
*document = cursor;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static int skin_parse_conditional(struct skin_element* element, char** document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
char* cursor = *document + 1; /* Starting past the "%" */
|
|
|
|
char* bookmark;
|
|
|
|
int children = 1;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
element->type = CONDITIONAL;
|
|
|
|
element->line = skin_line;
|
|
|
|
|
|
|
|
/* Parsing the tag first */
|
2010-06-10 21:02:44 +00:00
|
|
|
if(!skin_parse_tag(element, &cursor))
|
2010-05-25 15:19:52 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Counting the children */
|
2010-05-25 17:22:39 +00:00
|
|
|
if(*(cursor++) != ENUMLISTOPENSYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
skin_error(ARGLIST_EXPECTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
bookmark = cursor;
|
2010-05-25 17:22:39 +00:00
|
|
|
while(*cursor != ENUMLISTCLOSESYM && *cursor != '\n' && *cursor != '\0')
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
if(*cursor == COMMENTSYM)
|
|
|
|
{
|
|
|
|
skip_comment(&cursor);
|
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == ENUMLISTOPENSYM)
|
2010-06-02 07:48:48 +00:00
|
|
|
{
|
2010-06-07 23:49:06 +00:00
|
|
|
skip_enumlist(&cursor);
|
2010-06-02 07:48:48 +00:00
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == TAGSYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
if(*cursor == '\0' || *cursor == '\n')
|
|
|
|
break;
|
|
|
|
cursor++;
|
|
|
|
}
|
2010-06-07 23:49:06 +00:00
|
|
|
else if(*cursor == ENUMLISTSEPERATESYM)
|
|
|
|
{
|
2010-05-25 15:19:52 +00:00
|
|
|
children++;
|
2010-06-07 23:49:06 +00:00
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
}
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
cursor = bookmark;
|
|
|
|
|
|
|
|
/* Parsing the children */
|
2010-06-10 21:02:44 +00:00
|
|
|
element->children = skin_alloc_children(children);
|
|
|
|
element->children_count = children;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
for(i = 0; i < children; i++)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
element->children[i] = skin_parse_code_as_arg(&cursor);
|
|
|
|
skip_whitespace(&cursor);
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
if(i < children - 1 && *cursor != ENUMLISTSEPERATESYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
skin_error(SEPERATOR_EXPECTED);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-06-10 21:02:44 +00:00
|
|
|
else if(i == children - 1 && *cursor != ENUMLISTCLOSESYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
skin_error(CLOSE_EXPECTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*document = cursor;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static int skin_parse_comment(struct skin_element* element, char** document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
char* cursor = *document;
|
2010-06-17 07:56:51 +00:00
|
|
|
#ifndef ROCKBOX
|
2010-06-10 21:02:44 +00:00
|
|
|
char* text = NULL;
|
2010-06-17 07:56:51 +00:00
|
|
|
#endif
|
2010-05-25 15:19:52 +00:00
|
|
|
int length;
|
|
|
|
/*
|
|
|
|
* Finding the index of the ending newline or null-terminator
|
|
|
|
* The length of the string of interest doesn't include the leading #, the
|
|
|
|
* length we need to reserve is the same as the index of the last character
|
|
|
|
*/
|
|
|
|
for(length = 0; cursor[length] != '\n' && cursor[length] != '\0'; length++);
|
|
|
|
|
|
|
|
element->type = COMMENT;
|
|
|
|
element->line = skin_line;
|
2010-06-10 21:02:44 +00:00
|
|
|
#ifdef ROCKBOX
|
|
|
|
element->data = NULL;
|
|
|
|
#else
|
|
|
|
element->data = text = skin_alloc_string(length);
|
2010-05-25 15:19:52 +00:00
|
|
|
/* We copy from one char past cursor to leave out the # */
|
2010-06-10 21:02:44 +00:00
|
|
|
memcpy((void*)text, (void*)(cursor + 1),
|
2010-06-01 21:25:02 +00:00
|
|
|
sizeof(char) * (length-1));
|
2010-06-10 21:02:44 +00:00
|
|
|
text[length - 1] = '\0';
|
|
|
|
#endif
|
2010-06-01 21:25:02 +00:00
|
|
|
if(cursor[length] == '\n')
|
2010-05-25 15:19:52 +00:00
|
|
|
skin_line++;
|
|
|
|
|
2010-06-01 21:25:02 +00:00
|
|
|
*document += (length); /* Move cursor up past # and all text */
|
|
|
|
if(**document == '\n')
|
|
|
|
(*document)++;
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-06-10 21:02:44 +00:00
|
|
|
static struct skin_element* skin_parse_code_as_arg(char** document)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
int sublines = 0;
|
|
|
|
char* cursor = *document;
|
|
|
|
|
|
|
|
/* Checking for sublines */
|
2010-06-10 21:22:16 +00:00
|
|
|
while(*cursor != '\n' && *cursor != '\0'
|
|
|
|
&& *cursor != ENUMLISTSEPERATESYM && *cursor != ARGLISTSEPERATESYM
|
|
|
|
&& *cursor != ENUMLISTCLOSESYM && *cursor != ARGLISTCLOSESYM)
|
2010-05-25 15:19:52 +00:00
|
|
|
{
|
|
|
|
if(*cursor == MULTILINESYM)
|
|
|
|
{
|
|
|
|
sublines = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if(*cursor == TAGSYM)
|
|
|
|
{
|
|
|
|
/* A ';' directly after a '%' doesn't count */
|
|
|
|
cursor ++;
|
|
|
|
|
|
|
|
if(*cursor == '\0')
|
|
|
|
break;
|
|
|
|
|
|
|
|
cursor++;
|
|
|
|
}
|
2010-06-10 21:22:16 +00:00
|
|
|
else if(*cursor == ARGLISTOPENSYM)
|
|
|
|
{
|
|
|
|
skip_arglist(&cursor);
|
|
|
|
}
|
|
|
|
else if(*cursor == ENUMLISTOPENSYM)
|
|
|
|
{
|
|
|
|
skip_enumlist(&cursor);
|
|
|
|
}
|
2010-05-25 15:19:52 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Advancing the cursor as normal */
|
|
|
|
cursor++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(sublines)
|
|
|
|
return skin_parse_sublines_optional(document, 1);
|
|
|
|
else
|
|
|
|
return skin_parse_line_optional(document, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Memory management */
|
2010-06-10 21:02:44 +00:00
|
|
|
struct skin_element* skin_alloc_element()
|
|
|
|
{
|
2010-05-25 15:19:52 +00:00
|
|
|
struct skin_element* retval = (struct skin_element*)
|
2010-06-17 13:54:09 +00:00
|
|
|
skin_buffer_alloc(sizeof(struct skin_element));
|
2010-06-13 03:13:01 +00:00
|
|
|
retval->type = UNKNOWN;
|
2010-05-25 15:19:52 +00:00
|
|
|
retval->next = NULL;
|
2010-06-11 08:03:32 +00:00
|
|
|
retval->tag = NULL;
|
2010-05-25 15:19:52 +00:00
|
|
|
retval->params_count = 0;
|
|
|
|
retval->children_count = 0;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
struct skin_tag_parameter* skin_alloc_params(int count)
|
|
|
|
{
|
2010-06-10 21:02:44 +00:00
|
|
|
size_t size = sizeof(struct skin_tag_parameter) * count;
|
2010-06-17 13:54:09 +00:00
|
|
|
return (struct skin_tag_parameter*)skin_buffer_alloc(size);
|
2010-05-25 15:19:52 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
char* skin_alloc_string(int length)
|
|
|
|
{
|
2010-06-17 13:54:09 +00:00
|
|
|
return (char*)skin_buffer_alloc(sizeof(char) * (length + 1));
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct skin_element** skin_alloc_children(int count)
|
|
|
|
{
|
2010-06-10 21:02:44 +00:00
|
|
|
return (struct skin_element**)
|
2010-06-17 13:54:09 +00:00
|
|
|
skin_buffer_alloc(sizeof(struct skin_element*) * count);
|
2010-05-25 15:19:52 +00:00
|
|
|
}
|
2010-05-25 22:24:08 +00:00
|
|
|
|
|
|
|
void skin_free_tree(struct skin_element* root)
|
|
|
|
{
|
2010-06-10 21:02:44 +00:00
|
|
|
#ifndef ROCKBOX
|
2010-05-25 22:24:08 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* First make the recursive call */
|
|
|
|
if(!root)
|
|
|
|
return;
|
|
|
|
skin_free_tree(root->next);
|
|
|
|
|
|
|
|
/* Free any text */
|
2010-06-10 21:02:44 +00:00
|
|
|
if(root->type == TEXT || root->type == COMMENT)
|
|
|
|
free(root->data);
|
2010-05-25 22:24:08 +00:00
|
|
|
|
|
|
|
/* Then recursively free any children, before freeing their pointers */
|
|
|
|
for(i = 0; i < root->children_count; i++)
|
|
|
|
skin_free_tree(root->children[i]);
|
|
|
|
if(root->children_count > 0)
|
|
|
|
free(root->children);
|
|
|
|
|
|
|
|
/* Free any parameters, making sure to deallocate strings */
|
|
|
|
for(i = 0; i < root->params_count; i++)
|
|
|
|
if(root->params[i].type == STRING)
|
|
|
|
free(root->params[i].data.text);
|
|
|
|
if(root->params_count > 0)
|
|
|
|
free(root->params);
|
|
|
|
|
|
|
|
/* Finally, delete root's memory */
|
|
|
|
free(root);
|
2010-06-10 21:02:44 +00:00
|
|
|
#else
|
|
|
|
(void)root;
|
|
|
|
#endif
|
2010-05-25 22:24:08 +00:00
|
|
|
}
|