/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * miav_config.cc * * Sat Feb 19 14:13:19 CET 2005 * Copyright 2005 Bent Bisballe * deva@aasimon.org ****************************************************************************/ /* * This file is part of MIaV. * * MIaV 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. * * MIaV 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 MIaV; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* * $Id$ */ /* * $Log$ * Revision 1.10 2005/06/14 12:29:40 deva * Incorporated the use of the Info object everywhere... also using the log functionality. * * Revision 1.9 2005/06/09 11:00:03 deva * Added daemon code, and cleaned up using -Wall and -Werror * * Revision 1.8 2005/05/03 08:31:59 deva * Removed the error object, and replaced it with a more generic info object. * * Revision 1.7 2005/05/01 09:56:26 deva * Added Id and Log tags to all files */ #include #include "miav_config.h" MiavConfig *config; MiavConfig::MiavConfig(char *file, Info *i) { info = i; configs = NULL; filename = string(file); // Read config file FILE* fp = fopen(file, "r"); if(!fp) { if(info) info->error("Error reading configuration file %s\n", file); else fprintf(stderr, "Error reading configuration file %s\n", file); return; } fseek(fp, 0, SEEK_END); int fsz = ftell(fp); fseek(fp, 0, SEEK_SET); char *raw = (char*)calloc(fsz, 1); fread(raw, 1, fsz, fp); fclose(fp); parse(raw); free(raw); } MiavConfig::~MiavConfig() { _cfg *die = NULL; _cfg *cfg = configs; while(cfg) { if(die) free(die); die = cfg; cfg = cfg->next; } if(die) free(die); } /** * Prints a reasonable error message when a parse error occurres. */ _cfg *MiavConfig::parseError(char* msg, char* line) { if(info) info->error("Error parsing file %s at line:\n\t%s\n\t%s\n", filename.c_str(), line, msg); else fprintf(stderr, "Error parsing file %s at line:\n\t%s\n\t%s\n", filename.c_str(), line, msg); return NULL; } /** * Adds one configuration entry, from a single zero terminated line. */ _cfg *MiavConfig::addConfig(_cfg *parent, char* conf) { // Check for wellformed input: // Check for = if(strstr(conf, "=") == 0) return parseError("Missing '='", conf); /* if(strstr(conf, "\"")) { if(strstr(conf, "=") > strstr(conf, "\"")) return parseError("Missing '=', first occurrence inside string", conf); } */ // Check for nonempty left side if(strstr(conf, "=") == conf) return parseError("Empty left side", conf); // Check for nonempty right side if(strstr(conf, "=") == conf + strlen(conf) - 1) return parseError("Empty right side.", conf); // Parse this wellformed input. _cfg *cfg; cfg = (_cfg*) malloc(sizeof(_cfg)); if(!parent) configs = cfg; int namelen = strchr(conf, '=') - conf; char* name = (char*)calloc(namelen + 1, 1); strncpy(name, conf, namelen); int vallen = conf + strlen(conf) - (strchr(conf, '=') + 1); char* val = (char*)calloc(vallen + 1, 1); strncpy(val, conf + strlen(conf) - vallen, vallen); // TODO: Check valid rightside (true, false, number or "..") cfg->name = new string((const char*)name); free(name); cfg->stringval = new string((const char*)val); cfg->intval = atoi(val); cfg->floatval = atof(val); cfg->boolval = atoi(val) != 0; free(val); cfg->next = NULL; if(parent) parent->next = cfg; return cfg; } /** * Main parse function, iterates the lines of the file. */ int MiavConfig::parse(char* raw) { // Strip the string char *conf = strip(raw); char *conf_end = conf + strlen(conf); char *start = conf; char *p; _cfg *cfg = NULL; // Iterate the lines in the string for(p = conf; p < conf_end; p++) { if(*p == '\n') { *p = '\0'; if(!(cfg = addConfig(cfg, start))) return 1; start = p+1; } } // Allocated in strip free(conf); return 0; } /** * Strip all unwanted data from the string, initial to parsing. */ char* MiavConfig::strip(char* conf) { // Freed in parse!!! char *stripped = (char*)calloc(strlen(conf) + 2, 1); char *r; char *w = stripped; bool instring = false; bool incomment = false; // Iterate over the characters in the input string. for(r = conf; r < conf + strlen(conf); r++) { if(strchr("#", *r)) incomment = true; if(strchr("\n", *r)) incomment = false; if(!incomment) { if(instring) { // When in a string, accept anything, except ". if(*r != '\"') { *w = *r; w++; } } else { // Only copy valid characters if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_,.=", *r)) { // Change comma into dot if(*r == ',') *r = '.'; *w = *r; w++; } // We don't want double newlines and initial newline! if((*r == '\n') && (*(w-1) != '\n') && (w != stripped)) { *w = *r; w++; } } } if(strchr("\"", *r)) instring = !instring; } // If we are not ending on a newline, we better append one if(*(w-1) != '\n') { *w = '\n'; w++; } // Make sure we are nullterminated. *w = '\0'; return stripped; } int MiavConfig::readInt(char *node) { _cfg* n = findNode(node); if(n) return n->intval; else return 0; } bool MiavConfig::readBool(char *node) { _cfg* n = findNode(node); if(n) return n->boolval; else return false; } string *MiavConfig::readString(char *node) { _cfg* n = findNode(node); if(n) return n->stringval; else return &emptyString; } float MiavConfig::readFloat(char *node) { _cfg* n = findNode(node); if(n) return n->floatval; else return 0.0f; } _cfg *MiavConfig::findNode(char* node) { _cfg *cfg = configs; while(cfg) { if(!strcmp(node, cfg->name->c_str())) return cfg; cfg = cfg->next; } if(info) info->error("Request for nonexisting node \"%s\"!\n", node); else fprintf(stderr, "Request for nonexisting node \"%s\"!\n", node); return NULL; }