/** \file * Key/value parser until we have a proper config */ /* * Copyright (C) 2009 Trammell Hudson * * 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., * 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "dryos.h" #include "config.h" #include "version.h" #include "bmp.h" // Don't use isspace since we don't have it static inline int is_space( char c ) { return c == ' ' || c == '\t' || c == '\r' || c == '\n'; } struct config * config_parse_line( const char * line ) { int name_len = 0; int value_len = 0; static struct config _cfg; struct config * cfg = &_cfg; // Trim any leading whitespace int i = 0; while( line[i] && is_space( line[i] ) ) i++; // Copy the name to the name buffer while( line[i] && !is_space( line[i] ) && line[i] != '=' && name_len < MAX_NAME_LEN ) cfg->name[ name_len++ ] = line[i++]; if( name_len == MAX_NAME_LEN ) goto parse_error; // And nul terminate it cfg->name[ name_len ] = '\0'; // Skip any white space and = signs while( line[i] && is_space( line[i] ) ) i++; if( line[i++] != '=' ) goto parse_error; while( line[i] && is_space( line[i] ) ) i++; // Copy the value to the value buffer while( line[i] && value_len < MAX_VALUE_LEN ) cfg->value[ value_len++ ] = line[ i++ ]; // Back up to trim any white space while( value_len > 0 && is_space( cfg->value[ value_len-1 ] ) ) value_len--; // And nul terminate it cfg->value[ value_len ] = '\0'; DebugMsg( DM_MAGIC, 3, "%s: '%s' => '%s'", __func__, cfg->name, cfg->value ); return cfg; parse_error: DebugMsg( DM_MAGIC, 3, "%s: PARSE ERROR: len=%d,%d string='%s'", __func__, name_len, value_len, line ); bmp_printf(FONT_LARGE, 10, 150, "CONFIG PARSE ERROR"); bmp_printf(FONT_MED, 10, 200, "%s: PARSE ERROR:\nlen=%d,%d\nstring='%s'", __func__, name_len, value_len, line ); msleep(2000); //~ FreeMemory( cfg ); dumpf(); malloc_error: return 0; } char* config_file_buf = 0; int config_file_size = 0; int config_file_pos = 0; int get_char_from_config_file(char* out) { if (config_file_pos >= config_file_size) return 0; *out = config_file_buf[config_file_pos++]; return 1; } int read_line( char * buf, size_t size ) { size_t len = 0; while( len < size ) { int rc = get_char_from_config_file(buf+len); if( rc <= 0 ) return -1; if( buf[len] == '\r' ) continue; if( buf[len] == '\n' ) { buf[len] = '\0'; return len; } len++; } return -1; } extern struct config_var _config_vars_start[]; extern struct config_var _config_vars_end[]; static void config_auto_parse( struct config * cfg ) { struct config_var * var = _config_vars_start; for( ; var < _config_vars_end ; var++ ) { if( !streq( var->name, cfg->name ) ) continue; DebugMsg( DM_MAGIC, 3, "%s: '%s' => '%s'", __func__, cfg->name, cfg->value ); if( var->type == 0 ) { *(unsigned*) var->value = atoi( cfg->value ); } else { *(char **) var->value = cfg->value; } return; } DebugMsg( DM_MAGIC, 3, "%s: '%s' unused?", __func__, cfg->name ); } int config_save_file( const char * filename ) { struct config_var * var = _config_vars_start; int count = 0; DebugMsg( DM_MAGIC, 3, "%s: saving to %s", __func__, filename ); #define MAX_SIZE 10240 char* msg = alloc_dma_memory(MAX_SIZE); char* msgc = CACHEABLE(msg); snprintf( msgc, MAX_SIZE, "# Magic Lantern %s (%s)\n" "# Build on %s by %s\n", build_version, build_id, build_date, build_user ); struct tm now; LoadCalendarFromRTC( &now ); snprintf(msgc + strlen(msgc), MAX_SIZE - strlen(msgc), "# Configuration saved on %04d/%02d/%02d %02d:%02d:%02d\n", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec ); for( ; var < _config_vars_end ; var++ ) { if( var->type == 0 ) snprintf(msgc + strlen(msgc), MAX_SIZE - strlen(msgc), "%s = %d\r\n", var->name, *(unsigned*) var->value ); else snprintf(msgc + strlen(msgc), MAX_SIZE - strlen(msgc), "%s = %s\r\n", var->name, *(const char**) var->value ); count++; } FIO_RemoveFile(filename); FILE * file = FIO_CreateFile( filename ); if( file == INVALID_PTR ) return -1; FIO_WriteFile(file, msg, strlen(msgc)); FIO_CloseFile( file ); return count; } struct config * config_parse() { char line_buf[ 1000 ]; struct config * cfg = 0; int count = 0; while( read_line(line_buf, sizeof(line_buf) ) >= 0 ) { //~ bmp_printf(FONT_SMALL, 0, 0, "cfg line: %s ", line_buf); // Ignore any line that begins with # or is empty if( line_buf[0] == '#' || line_buf[0] == '\0' ) continue; DebugMsg(DM_MAGIC, 3, "cfg line: %s", line_buf); struct config * new_config = config_parse_line( line_buf ); if( !new_config ) goto error; cfg = new_config; count++; config_auto_parse( cfg ); } DebugMsg( DM_MAGIC, 3, "%s: Read %d config values", __func__, count ); return cfg; error: DebugMsg( DM_MAGIC, 3, "%s: ERROR", __func__ ); return NULL; } int config_parse_file( const char * filename ) { config_file_buf = read_entire_file(filename, &config_file_size); config_file_pos = 0; msleep(200); config_parse(); free_dma_memory(config_file_buf); config_file_buf = 0; return 1; } int atoi( const char * s ) { int value = 0; // Only handles base ten for now while( 1 ) { char c = *s++; if( !c || c < '0' || c > '9' ) break; value = value * 10 + c - '0'; } return value; } int config_flag_file_setting_load(char* file) { unsigned size; return ( FIO_GetFileSize( file, &size ) == 0 ); } void config_flag_file_setting_save(char* file, int setting) { FIO_RemoveFile(file); if (setting) { FILE* f = FIO_CreateFile(file); FIO_CloseFile(f); } }