Files
OpenCellular/parse.c
Allen Martin a5e1002501 Remove extra path element.
Remove extra path element that came as part of the git I cloned
this from.
2011-01-25 12:56:10 -08:00

528 lines
12 KiB
C

/**
* Copyright (c) 2011 NVIDIA Corporation. All rights reserved.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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 program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
/*
* parse.c - Parsing support for the cbootimage tool
*/
/*
* TODO / Notes
* - Add doxygen commentary
* - Do we have endian issues to deal with?
* - Add support for device configuration data
* - Add support for bad blocks
* - Add support for different allocation modes/strategies
* - Add support for multiple BCTs in journal block
* - Add support for other missing features.
*/
#include "parse.h"
#include "cbootimage.h"
#include "data_layout.h"
#include "crypto.h"
#include "set.h"
/*
* Function prototypes
*
* ParseXXX() parses XXX in the input
* SetXXX() sets state based on the parsing results but does not perform
* any parsing of its own
* A ParseXXX() function may call other parse functions and set functions.
* A SetXXX() function may not call any parseing functions.
*/
static char *parse_u32(char *statement, u_int32_t *val);
static char *parse_filename(char *statement, char *name, int chars_remaining);
static int
parse_array(build_image_context *context, parse_token token, char *rest);
static int
parse_bootloader(build_image_context *context, parse_token token, char *rest);
static int
parse_value_u32(build_image_context *context, parse_token token, char *rest);
static int
parse_bct_file(build_image_context *context, parse_token token, char *rest);
static int
parse_addon(build_image_context *context, parse_token token, char *rest);
static char *parse_string(char *statement, char *uname, int chars_remaining);
static char
*parse_end_state(char *statement, char *uname, int chars_remaining);
static int process_statement(build_image_context *context, char *statement);
static parse_item s_top_level_items[] =
{
{ "Bctfile=", token_bct_file, parse_bct_file },
{ "Attribute=", token_attribute, parse_value_u32 },
{ "Attribute[", token_attribute, parse_array },
{ "BootLoader=", token_bootloader, parse_bootloader },
{ "Redundancy=", token_redundancy, parse_value_u32 },
{ "Version=", token_version, parse_value_u32 },
{ "AddOn[", token_addon, parse_addon },
{ NULL, 0, NULL } /* Must be last */
};
/* Macro to simplify parser code a bit. */
#define PARSE_COMMA(x) if (*rest != ',') return (x); rest++
/* This parsing code was initially borrowed from nvcamera_config_parse.c. */
/* Returns the address of the character after the parsed data. */
static char *
parse_u32(char *statement, u_int32_t *val)
{
u_int32_t value = 0;
while (*statement=='0') {
statement++;
}
if (*statement=='x' || *statement=='X') {
statement++;
while (((*statement >= '0') && (*statement <= '9')) ||
((*statement >= 'a') && (*statement <= 'f')) ||
((*statement >= 'A') && (*statement <= 'F'))) {
value *= 16;
if ((*statement >= '0') && (*statement <= '9')) {
value += (*statement - '0');
} else if ((*statement >= 'A') &&
(*statement <= 'F')) {
value += ((*statement - 'A')+10);
} else {
value += ((*statement - 'a')+10);
}
statement++;
}
} else {
while (*statement >= '0' && *statement <= '9') {
value = value*10 + (*statement - '0');
statement++;
}
}
*val = value;
return statement;
}
/* This parsing code was initially borrowed from nvcamera_config_parse.c. */
/* Returns the address of the character after the parsed data. */
static char *
parse_filename(char *statement, char *name, int chars_remaining)
{
while (((*statement >= '0') && (*statement <= '9')) ||
((*statement >= 'a') && (*statement <= 'z')) ||
((*statement >= 'A') && (*statement <= 'Z')) ||
(*statement == '\\') ||
(*statement == '/' ) ||
(*statement == '~' ) ||
(*statement == '_' ) ||
(*statement == '-' ) ||
(*statement == '+' ) ||
(*statement == ':' ) ||
(*statement == '.' )) {
/* Check if the filename buffer is out of space, preserving one
* character to null terminate the string.
*/
chars_remaining--;
if (chars_remaining < 1)
return NULL;
*name++ = *statement++;
}
/* Null terminate the filename. */
*name = '\0';
return statement;
}
/*
* parse_bootloader(): Processes commands to set a bootloader.
*/
static int parse_bootloader(build_image_context *context,
parse_token token,
char *rest)
{
char filename[MAX_BUFFER];
char e_state[MAX_STR_LEN];
u_int32_t load_addr;
u_int32_t entry_point;
assert(context != NULL);
assert(rest != NULL);
/* Parse the file name. */
rest = parse_filename(rest, filename, MAX_BUFFER);
if (rest == NULL)
return 1;
PARSE_COMMA(1);
/* Parse the load address. */
rest = parse_u32(rest, &load_addr);
if (rest == NULL)
return 1;
PARSE_COMMA(1);
/* Parse the entry point. */
rest = parse_u32(rest, &entry_point);
if (rest == NULL)
return 1;
PARSE_COMMA(1);
/* Parse the end state. */
rest = parse_end_state(rest, e_state, MAX_STR_LEN);
if (rest == NULL)
return 1;
if (strncmp(e_state, "Complete", strlen("Complete")))
return 1;
/* Parsing has finished - set the bootloader */
return set_bootloader(context, filename, load_addr, entry_point);
}
/*
* parse_array(): Processes commands to set an array value.
*/
static int
parse_array(build_image_context *context, parse_token token, char *rest)
{
u_int32_t index;
u_int32_t value;
assert(context != NULL);
assert(rest != NULL);
/* Parse the index. */
rest = parse_u32(rest, &index);
if (rest == NULL)
return 1;
/* Parse the closing bracket. */
if (*rest != ']')
return 1;
rest++;
/* Parse the equals sign.*/
if (*rest != '=')
return 1;
rest++;
/* Parse the value based on the field table. */
switch(token) {
case token_attribute:
rest = parse_u32(rest, &value);
break;
default:
/* Unknown token */
return 1;
}
if (rest == NULL)
return 1;
/* Store the result. */
return context_set_array(context, index, token, value);
}
/*
* parse_value_u32(): General handler for setting u_int32_t values in config files.
*/
static int parse_value_u32(build_image_context *context,
parse_token token,
char *rest)
{
u_int32_t value;
assert(context != NULL);
assert(rest != NULL);
rest = parse_u32(rest, &value);
if (rest == NULL)
return 1;
return context_set_value(context, token, value);
}
static int
parse_bct_file(build_image_context *context, parse_token token, char *rest)
{
char filename[MAX_BUFFER];
assert(context != NULL);
assert(rest != NULL);
/* Parse the file name. */
rest = parse_filename(rest, filename, MAX_BUFFER);
if (rest == NULL)
return 1;
/* Parsing has finished - set the bctfile */
context->bct_filename = filename;
/* Read the bct file to buffer */
read_bct_file(context);
return 0;
}
static char *
parse_string(char *statement, char *uname, int chars_remaining)
{
memset(uname, 0, chars_remaining);
while (((*statement >= '0') && (*statement <= '9')) ||
((*statement >= 'A') && (*statement <= 'Z')) ||
((*statement >= 'a') && (*statement <= 'z'))) {
*uname++ = *statement++;
if (--chars_remaining < 0) {
printf("String length beyond the boundary!!!");
return NULL;
}
}
*uname = '\0';
return statement;
}
static char *
parse_end_state(char *statement, char *uname, int chars_remaining)
{
while (((*statement >= 'a') && (*statement <= 'z')) ||
((*statement >= 'A') && (*statement <= 'Z'))) {
*uname++ = *statement++;
if (--chars_remaining < 0)
return NULL;
}
*uname = '\0';
return statement;
}
/* Parse the addon component */
static int
parse_addon(build_image_context *context, parse_token token, char *rest)
{
char filename[MAX_BUFFER];
char u_name[4];
char e_state[MAX_STR_LEN];
u_int32_t index;
u_int32_t item_attr;
u_int32_t others;
char other_str[MAX_STR_LEN];
assert(context != NULL);
assert(rest != NULL);
/* Parse the index. */
rest = parse_u32(rest, &index);
if (rest == NULL)
return 1;
/* Parse the closing bracket. */
if (*rest != ']')
return 1;
rest++;
/* Parse the equals sign.*/
if (*rest != '=')
return 1;
rest++;
rest = parse_filename(rest, filename, MAX_BUFFER);
if (rest == NULL)
return 1;
if (set_addon_filename(context, filename, index) != 0)
return 1;
PARSE_COMMA(1);
rest = parse_string(rest, u_name, 3);
if (rest == NULL) {
printf("Unique name should be 3 characters.\n");
return 1;
}
if (set_unique_name(context, u_name, index) != 0)
return 1;
PARSE_COMMA(1);
rest = parse_u32(rest, &item_attr);
if (rest == NULL)
return 1;
if (set_addon_attr(context, item_attr, index) != 0)
return 1;
PARSE_COMMA(1);
if (*rest == '0' && (*(rest + 1) == 'x' ||*(rest + 1) == 'X')) {
rest = parse_u32(rest, &others);
if (set_other_field(context, NULL, others, index) != 0)
return 1;
} else {
rest = parse_string(rest, other_str, 16);
if (set_other_field(context, other_str, 0, index) != 0)
return 1;
}
if (rest == NULL)
return 1;
PARSE_COMMA(1);
rest = parse_end_state(rest, e_state, MAX_STR_LEN);
if (rest == NULL)
return 1;
if (strncmp(e_state, "Complete", strlen("Complete")))
return 1;
return 0;
}
/* Return 0 on success, 1 on error */
static int
process_statement(build_image_context *context, char *statement)
{
int i;
char *rest;
for (i = 0; s_top_level_items[i].prefix != NULL; i++) {
if (!strncmp(s_top_level_items[i].prefix, statement,
strlen(s_top_level_items[i].prefix))) {
rest = statement + strlen(s_top_level_items[i].prefix);
return s_top_level_items[i].process(context,
s_top_level_items[i].token,
rest);
}
}
/* If this point was reached, there was a processing error. */
return 1;
}
/* Note: Basic parsing borrowed from nvcamera_config.c */
void process_config_file(build_image_context *context)
{
char buffer[MAX_BUFFER];
int space = 0;
char current;
u_int8_t c_eol_comment_start = 0; // True after first slash
u_int8_t comment = 0;
u_int8_t string = 0;
u_int8_t equal_encounter = 0;
assert(context != NULL);
assert(context->config_file != NULL);
while ((current = fgetc(context->config_file)) !=EOF) {
if (space >= (MAX_BUFFER-1)) {
/* if we exceeded the max buffer size, it is likely
due to a missing semi-colon at the end of a line */
printf("Config file parsing error!");
exit(1);
}
/* Handle failure to complete "//" comment token.
Insert the '/' into the busffer and proceed with
processing the current character. */
if (c_eol_comment_start && current != '/') {
c_eol_comment_start = 0;
buffer[space++] = '/';
}
switch (current) {
case '\"': /* " indicates start or end of a string */
if (!comment) {
string ^= 1;
buffer[space++] = current;
}
break;
case ';':
if (!string && !comment) {
buffer[space++] = '\0';
/* Process a statement. */
if (process_statement(context, buffer)) {
goto error;
}
space = 0;
equal_encounter = 0;
} else if (string) {
buffer[space++] = current;
}
break;
case '/':
if (!string && !comment) {
if (c_eol_comment_start) {
/* EOL comment started. */
comment = 1;
c_eol_comment_start = 0;
} else {
/* Potential start of eol comment. */
c_eol_comment_start = 1;
}
} else if (!comment) {
buffer[space++] = current;
}
break;
/* ignore whitespace. uses fallthrough */
case '\n':
case '\r': /* carriage returns end comments */
string = 0;
comment = 0;
c_eol_comment_start = 0;
case ' ':
case '\t':
if (string) {
buffer[space++] = current;
}
break;
case '#':
if (!string) {
comment = 1;
} else {
buffer[space++] = current;
}
break;
default:
if (!comment) {
buffer[space++] = current;
if (current == '=') {
if (!equal_encounter) {
equal_encounter = 1;
} else {
goto error;
}
}
}
break;
}
}
return;
error:
printf("Error parsing: %s\n", buffer);
exit(1);
}