diff options
Diffstat (limited to 'libmiav/miav_config.cc')
-rw-r--r-- | libmiav/miav_config.cc | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/libmiav/miav_config.cc b/libmiav/miav_config.cc new file mode 100644 index 0000000..adfa5c5 --- /dev/null +++ b/libmiav/miav_config.cc @@ -0,0 +1,492 @@ +/* -*- 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 <config.h> +#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) + 1; + fseek(fp, 0, SEEK_SET); + + char *raw = (char*)calloc(fsz, 1); + fread(raw, 1, fsz, fp); + + 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(char* msg, _cfg* cfg) +{ + if(info) info->error("Error parsing file %s at line %d:\n\t%s\n\t%s\n", + filename.c_str(), + cfg->line, + cfg->orig, + msg); + else fprintf(stderr, "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(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(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(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(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(char* node) +{ + _cfg *cfg = configs; + + while(cfg) { + if(!strcmp(node, cfg->name->c_str())) return cfg; + cfg = cfg->next; + } + if(info) info->error("Missing line in configuration file: \"%s\"!\n", node); + else fprintf(stderr, "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*/ |