blob: e2b5e09595178291769ea5c18e54bd5e11e7f5c7 [file] [log] [blame]
/*
* Copyright 2012, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* symbol.c: symbol handling
*/
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lib/string_builder.h"
#include "mosys/alloc.h"
#include "mosys/list.h"
#include "mosys/log.h"
#include "mosys/globals.h"
#include "symbol.h"
static struct ll_node *symbols;
/* for debugging */
#if 0
static void print_symbol(struct ll_node *node)
{
struct vpd_symbol *symbol;
if (node->data)
symbol = node->data;
else
return;
lprintf(LOG_DEBUG, "symbol->name: \"%s\", symbol->value: "
"\"%s\"\n", symbol->name, symbol->value);
}
#endif
void free_vpd_symbol(struct vpd_symbol *symbol)
{
free(symbol->name);
free(symbol->value);
free(symbol);
symbol = NULL;
}
void free_vpd_symbol_node(struct ll_node *node)
{
struct vpd_symbol *symbol;
if (!node->data)
return;
symbol = (struct vpd_symbol *)node->data;
free_vpd_symbol(symbol);
}
void cleanup_symtab()
{
list_foreach(symbols, free_vpd_symbol_node);
list_cleanup(&symbols);
}
/*
* parse_symbol - parse a buffer and generate a symbol, if possible
*
* @str: string to parse
* @len: length
*
* Parse two substrings delimited by equal ('=') sign:
* First string: The symbol name, beginning with CONFIG_. No spaces,
* quotes, or special symbols allowed.
* Second string: The symbol value, interpreted as follows:
* - If enclosed in quotes, value is a string.
* - If no quotes and the character is 'y' or 'n', value is
* assumed to be booloean.
* - If no quotes and character is a number, value is assumed
* to be numerical.
*
* Example:
* CONFIG_SYMBOL_STRING="symbol value"
* CONFIG_SYMBOL_BOOLEAN=y
* CONFIG_SYMBOL_NUMERICAL=0xdeadbeef
*
* returns a newly allocated vpd_symbol if successful
* returns NULL otherwise
*/
static struct vpd_symbol *parse_symbol(const char *str, size_t len)
{
struct vpd_symbol *symbol = NULL;
char *p;
struct string_builder *sb;
int index = 0;
if (!str) {
lprintf(LOG_DEBUG, "%s: string is NULL\n", __func__);
}
if (len < strlen("CONFIG_")) {
lprintf(LOG_DEBUG, "%s: too few bytes: %lu\n", __func__, len);
return NULL;
}
if (strncmp(str , "CONFIG_", 7)) {
lprintf(LOG_DEBUG, "%s: string does not begin with "
"CONFIG_\n", __func__);
return NULL;
}
p = strchr(str, '=');
if (!p) {
lprintf(LOG_DEBUG, "%s: string \"%s\" is not a symbol=value "
"pair\n", __func__, str);
return NULL;
}
index = p - str;
symbol = mosys_zalloc(sizeof(*symbol));
symbol->name = mosys_malloc(index + 1);
snprintf(symbol->name, index + 1, "%s", str);
/* iterate past the '=' and copy the symbol's
value one character at a time */
p++;
index += sizeof(char);
sb = new_string_builder();
if (*p == '"') {
/* symbol is a string, so we will stop parsing when
we encounter the end quote */
p++; /* iterate past open quote */
while (*p != '"' && index < len) {
string_builder_add_char(sb, *p);
p++;
index += sizeof(char);
}
if (*p != '"') {
lprintf(LOG_ERR, "Error parsing \"%s\": "
"no end quote found\n", symbol->name);
free_vpd_symbol(symbol);
free_string_builder(sb);
symbol = NULL;
goto parse_symbol_exit;
}
symbol->type = STRING;
} else if (isdigit(*p)) {
char *endptr;
long long int u;
u = strtoll(p, &endptr, 0);
/* finish parsing the string for error checking */
while (!isspace(*p) && index < len) {
p++;
index += sizeof(char);
}
if (endptr != p) {
lprintf(LOG_ERR, "Error parsing \"%s\": "
"value is non-numeric\n", symbol->name);
free_vpd_symbol(symbol);
free_string_builder(sb);
symbol = NULL;
goto parse_symbol_exit;
}
string_builder_sprintf(sb, "%lld", u);
symbol->type = NUMERIC;
} else if (*p == 'y' || *p == 'n') {
/* symbol is boolean, so we'll stop parsing after
this character is read */
string_builder_add_char(sb, *p);
/* FIXME: Add sanity checking */
symbol->type = BOOLEAN;
}
symbol->value = mosys_strdup(string_builder_get_string(sb));
free_string_builder(sb);
lprintf(LOG_DEBUG, "%s: symbol->name: %s, symbol->value: %s\n",
__func__, symbol->name, symbol->value);
parse_symbol_exit:
return symbol;
}
/*
* gen_symtab - Parse config file and insert symbols into a table (linked list)
*
* Assumes file is in the format:
* CONFIG_SYMBOL_STRING=value
*
* where the value may be a numeric value, a boolean 'y' or 'n' (for yes / no),
* or a string enclosed in quotation marks ("")
*
* returns 0 to indicate success
* returns <0 to indicate failure
*/
int gen_symtab(const char *file)
{
FILE *fp;
char *line = NULL;
size_t read;
size_t len = 0;
fp = fopen(file, "r");
if (fp == NULL) {
fprintf(stderr, "unable to open fp file \"%s\": %s\n",
file, strerror(errno));
return -1;
}
while ((read = getline(&line, &len, fp)) != -1) {
struct vpd_symbol *symbol = NULL;
/* line is commented */
if (line[0] == '#')
continue;
if (isspace(line[0]))
continue;
symbol = parse_symbol(line, read);
if (symbol)
symbols = list_insert_before(symbols, symbol);
}
if (line)
free(line);
fclose(fp);
return 0;
}
struct vpd_symbol *lookup_symbol(const char *symbol)
{
struct ll_node *node;
struct vpd_symbol *ret = NULL;
if (symbols)
node = list_head(symbols);
else
return NULL;
while (node) {
struct vpd_symbol *sym = node->data;
if (!sym) {
lprintf(LOG_DEBUG, "%s: no data for this node???\n");
continue;
}
if (!strcmp(symbol, sym->name)) {
ret = sym;
break;
}
node = node->next;
}
return ret;
}
char *sym2str(const char *symbol)
{
struct vpd_symbol *s;
s = lookup_symbol(symbol);
if (!s)
return NULL;
return s->value;
}
int sym2bool(const char *symbol)
{
struct vpd_symbol *s;
unsigned long int val = 0;
s = lookup_symbol(symbol);
if (!s)
return 0;
if (s->type != BOOLEAN)
return 0;
if (s->value[0] == 'y') {
val = 1;
} else if (s->value[0] == 'n') {
val = 0;
} else {
lprintf(LOG_DEBUG, "%s: Non-boolean value, s->value[0]: %c\n",
__func__, s->value[0]);
}
return val;
}
struct vpd_symbol *update_symbol(const char *str)
{
struct vpd_symbol *input, *s = NULL;
/* process input string as a symbol */
input = parse_symbol(str, strlen(str));
if (!input)
return NULL;
s = lookup_symbol(input->name);
if (!s) { /* insert new symbol */
symbols = list_insert_before(symbols, input);
} else { /* edit existing symbol */
switch(s->type){
case BOOLEAN:
s->value = input->value;
break;
case NUMERIC:
s->value = input->value;
break;
case STRING:
free(s->value);
s->value = mosys_strdup(input->value);
break;
default:
lprintf(LOG_ERR, "cannot insert symbol: %s\n", str);
break;
}
free(input->name);
free(input->value);
free(input);
}
return s;
}