/* -*- 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. */ #include "miav_config.h" #include #include #include #include MiavConfig *config; MiavConfig::MiavConfig() { } MiavConfig::MiavConfig(const char *file) { configs = NULL; filename = string(file); // Read config file FILE* fp = fopen(file, "r"); if(!fp) { ERR(config, "Error reading configuration file %s\n", file); return; } fseek(fp, 0, SEEK_END); int fsz = ftell(fp) + 1; fseek(fp, 0, SEEK_SET); char *raw = (char*)calloc(fsz, 1); size_t rsz = fread(raw, 1, fsz, fp); rsz = rsz; fclose(fp); configs = 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. */ void MiavConfig::parseError(const char* msg, _cfg* cfg) { ERR(config, "Error parsing file %s at line %d:\n\t%s\n\t%s\n", filename.c_str(), cfg->line, cfg->orig, msg); } _cfg* MiavConfig::readLines(char* raw) { int line = 1; _cfg *first = (_cfg*)calloc(1, sizeof(_cfg)); _cfg *current = first; _cfg *next = NULL; char *nl = strchr(raw, '\n'); while(nl != NULL) { int len = nl - raw; current->line = line; current->orig = (char*) calloc(len + 1, 1); strncpy(current->orig, raw, len); // Find next newline raw = nl+1; nl = strchr(raw, '\n'); line++; // Add _cfg if(nl != NULL) { next = (_cfg*)calloc(1, sizeof(_cfg)); current->next = next; current = next; } else { current->next = NULL; } } return first; } _cfg* MiavConfig::parseLines(_cfg *cfg) { if(cfg == NULL) return NULL; char *l = cfg->left = (char*)calloc(1, strlen(cfg->orig)); char *r = cfg->right = (char*)calloc(1, strlen(cfg->orig)); char *p = cfg->orig; // Skip leftmost whitespace while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { p++; } // Empty line, with whitespaces if(p == cfg->orig + strlen(cfg->orig)) { _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } // Parse left side while(p < cfg->orig + strlen(cfg->orig) && !strchr("\t ", *p)) { if(strchr("#", *p)) { if(l != cfg->left) parseError("Incomplete line.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } if(strchr("=", *p)) break; if(strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_", *p)) { *l = *p; l++; } else { char buf[256]; sprintf(buf, "Invalid left hand side character at [%s].", p); parseError(buf, cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } p++; } // Skip whitespace while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { p++; } if(*p != '=') { parseError("Expected '='.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } p++; // Get past the '=' // Skip whitespace while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { p++; } // Parse right hand side int instring = 0; while(p < cfg->orig + strlen(cfg->orig) && !(strchr("\t ", *p) && instring != 1)) { if(*p == '\"') instring++; if(instring > 2) { parseError("Too many '\"'.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } if(instring == 1) { // Accept all chars *r= *p; r++; } else { // Accept only those chars valid for the data types. if(strchr("truefalseyesnoTRUEFALSEYESNO1234567890\",.-", *p)) { if(*p == ',') *r= '.'; *r = *p; r++; } else if(!strchr("\n", *p)) { char buf[256]; sprintf(buf, "Invalid right hand side character at [%s].", p); parseError(buf, cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } if(*p == '#') break; } p++; } // Skip whitespace while(p < cfg->orig + strlen(cfg->orig) && strchr("\t ", *p)) { p++; } // Detect if whitespace ocurred inside righthand value. if(p != cfg->orig + strlen(cfg->orig)) { parseError("Invalid use of whitespace.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } // Check for instring (string not ended) if(instring == 1) { parseError("String not closed.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } // Check for empty line if(l == cfg->left && r == cfg->right) { _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } // Check for empty left side. if(l == cfg->left) { parseError("Empty left side.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } // Check for empty right side. if(r == cfg->right) { parseError("Empty right side.", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return parseLines(next); } cfg->next = parseLines(cfg->next); return cfg; } _cfg *MiavConfig::createSemantics(_cfg *cfg) { if(cfg == NULL) return NULL; cfg->type = CONFIG_UNKNOWN; // Boolean - true if(strcasecmp(cfg->right, "yes") == 0 || strcasecmp(cfg->right, "true") == 0) { cfg->type = CONFIG_BOOL; cfg->boolval = true; } // Boolean - false if(strcasecmp(cfg->right, "no") == 0 || strcasecmp(cfg->right, "false") == 0) { cfg->type = CONFIG_BOOL; cfg->boolval = false; } // String if(cfg->right[0] == '\"') { cfg->type = CONFIG_STRING; cfg->right[strlen(cfg->right) - 1] = '\0'; cfg->stringval = new string(cfg->right + 1); } // Number bool number = true; char *p = cfg->right; while(p < cfg->right + strlen(cfg->right)) { if(!strchr("01234567890.-", *p)) number = false; p++; } // Integer if(number && strstr(cfg->right, ".") == NULL ) { cfg->type = CONFIG_INT; cfg->intval = atoi(cfg->right); } // Float if(number && strstr(cfg->right, ".") != NULL) { cfg->type = CONFIG_FLOAT; cfg->floatval = atof(cfg->right); } if(cfg->type == CONFIG_UNKNOWN) { parseError("Unknown type (see 'man miav.conf' for valid right hand sides).", cfg); _cfg* next = cfg->next; free(cfg->orig); free(cfg->left); free(cfg->right); free(cfg); return createSemantics(next); } // Create name cfg->name = new string(cfg->left); cfg->next = createSemantics(cfg->next); return cfg; } _cfg* MiavConfig::parse(char* raw) { _cfg *first = readLines(raw); first = parseLines(first); first = createSemantics(first); /* _cfg* cfg = first; while(cfg) { printf("Node:\n"); printf("\tLine: [%d]\n", cfg->line); printf("\tOrig: [%s]\n", cfg->orig); printf("\tLeft: [%s]\n", cfg->left); printf("\tRight: [%s]\n", cfg->right); switch(cfg->type) { case CONFIG_INT: printf("\tInt value: %d\n", cfg->intval); break; case CONFIG_BOOL: printf("\tBool value: %d\n", cfg->boolval); break; case CONFIG_FLOAT: printf("\tFloat value: %f\n", cfg->floatval); break; case CONFIG_STRING: printf("\tString value: %s\n", cfg->stringval->c_str()); break; case CONFIG_UNKNOWN: printf("\tUnknown type: %s\n", cfg->right); break; } cfg= cfg->next; } */ return first; } int MiavConfig::readInt(const char *node) { _cfg* n = findNode(node); if(n) { if(n->type == CONFIG_INT) return n->intval; parseError("Expected integer.", n); } return 0; } bool MiavConfig::readBool(const char *node) { _cfg* n = findNode(node); if(n) { if(n->type == CONFIG_BOOL) return n->boolval; if(n->type == CONFIG_INT) return (n->intval != 0); parseError("Expected boolean.", n); } return false; } string *MiavConfig::readString(const char *node) { _cfg* n = findNode(node); if(n) { if(n->type == CONFIG_STRING) return n->stringval; parseError("Expected string.", n); } return &emptyString; } float MiavConfig::readFloat(const char *node) { _cfg* n = findNode(node); if(n) { if(n->type == CONFIG_FLOAT) return n->floatval; if(n->type == CONFIG_INT) return (float)n->intval; parseError("Expected float.", n); } return 0.0f; } _cfg *MiavConfig::findNode(const char* node) { _cfg *cfg = configs; while(cfg) { if(!strcmp(node, cfg->name->c_str())) return cfg; cfg = cfg->next; } ERR(config, "Missing line in configuration file: \"%s\"!\n", node); return NULL; } #ifdef __TEST_MIAV_CONFIG int main(int argc, char *argv[]) { if(argc < 2) { fprintf(stderr, "usage:\n\tmiav_config [filename]\n"); return 1; } MiavConfig cfg(argv[1]); printf("Server user: [%s]\n", cfg.readString("server_user")->c_str()); printf("Resolution: [%f]\n", cfg.readFloat("screensize")); printf("Resolution (as int): [%d]\n", cfg.readInt("screensize")); printf("Width: [%d]\n", cfg.readInt("pixel_width")); printf("Width (as float): [%f]\n", cfg.readFloat("pixel_width")); printf("Frame quality: [%d]\n", cfg.readInt("frame_quality")); printf("Skip frames: [%d]\n", cfg.readBool("player_skip_frames")); printf("Skip frames (as int): [%d]\n", cfg.readInt("player_skip_frames")); printf("Frame quality (as bool): [%d]\n", cfg.readBool("frame_quality")); } #endif/* __TEST_MIAV_CONFIG*/