/* cfgfile.c - handle configuration file. * * King of the Hill (KOTH) Copyright (C) 1999 Peter Amstutz * KOTH comes with ABSOLUTELY NO WARRANTY * This is free software, and you are welcome to redistribute it * under the conditions of the GNU GPL * * Created by Ariel Eizenberg (arielez@cs.huji.ac.il) * Configuration is read from a file into an add-only hashtable. * Default configuration file name is DEFAULT_CONFIGFILE * * See .h file for more info. */ #include #include #include #include #include "cfgfile.h" #include "log.h" Configuration_cfg *cfg_configuration = NULL; struct Category_cfg { char *name; ConfigItem_cfg *items; int numitems, maxitems; Category_cfg *next; }; #define HASH_BUCKETS 10 struct Configuration_cfg { Category_cfg *items[HASH_BUCKETS]; }; #define HASH_NUM 30031 #define HASH_BASE 211 void cfgParserError(int, const char *, const char *); void cfgAddToHash(Configuration_cfg *, Category_cfg *); const Category_cfg *cfgGetCategory(Configuration_cfg *, const char *); int cfgLoadConfiguration(Configuration_cfg *, FILE *, const char *); Category_cfg *cfgParseCategory(char *, Configuration_cfg *, int, const char *); void cfgParseField(char *, Category_cfg *, int, const char *); inline int cfgHash(const char *string) { int result = 0; while(*string) result = (result * HASH_BASE + *(string++)) % HASH_NUM; return result; } inline char *cfgClearWS(char *string) { char *ret = string, *temp; while(isspace(*ret)) ret++; temp = ret; while(!isspace(*temp)) temp++; temp = '\0'; return ret; } void cfgParserError(int linenum, const char *fname, const char *msg) { logPrintf(CRITICAL, "Error in %s(%i): %s.", fname, linenum, msg); /* fprintf(stderr, "Error in %s(%i): %s.",fname,linenum,msg); */ exit(-1); } void cfgAddToHash(Configuration_cfg * config, Category_cfg * item) { int hvalue = cfgHash(item->name); hvalue %= HASH_BUCKETS; if(config->items[hvalue] != NULL) item->next = config->items[hvalue]; config->items[hvalue] = item; } const Category_cfg *cfgGetCategory(Configuration_cfg * config, const char *cat) { Category_cfg *category = NULL; int hvalue; hvalue = cfgHash(cat) % HASH_BUCKETS; for(category = config->items[hvalue]; category && strcmp(category->name, cat); category = category->next) ; return category; } void cfgAddToCategory(Category_cfg * category, char *name, char *value) { ConfigItem_cfg *item; if(category->numitems == category->maxitems) { ConfigItem_cfg *newitems; category->maxitems = category->maxitems * 2 + 1; newitems = (ConfigItem_cfg *) malloc(sizeof(ConfigItem_cfg) * category->maxitems); if(category->items != NULL) { memcpy(newitems, category->items, sizeof(ConfigItem_cfg) * category->numitems); free(category->items); } category->items = newitems; } item = &category->items[category->numitems++]; item->name = (char *) malloc(sizeof(char *) * (strlen(name) + 1)); item->value = (char *) malloc(sizeof(char *) * (strlen(value) + 1)); strcpy(item->name, name); strcpy(item->value, value); } #define BUFFER_SIZE 100 int cfgLoadConfiguration(Configuration_cfg * config, FILE * cfgfile, const char *fname) { Category_cfg *category = NULL; char buffer[BUFFER_SIZE]; int i, linenum = 0; while(!feof(cfgfile)) { linenum++; if(!fgets(buffer, BUFFER_SIZE, cfgfile)) break; for(i = 0; buffer[i] && !isspace(buffer[i]) && buffer[i] != '#'; i++) ; buffer[i] = '\0'; if(buffer[0] != '\0') { if(buffer[0] == '[') category = cfgParseCategory(buffer, config, linenum, fname); else cfgParseField(buffer, category, linenum, fname); } } return 0; } Category_cfg *cfgParseCategory(char *buffer, Configuration_cfg * config, int linenum, const char *fname) { int length; char *name; Category_cfg *category; length = strlen(buffer); buffer[length - 1] = '\0'; /* clear the ']' */ name = cfgClearWS(buffer); if(strlen(name) < 1) cfgParserError(linenum, fname, "Empty category name"); category = (Category_cfg *) cfgGetCategory(config, name); if(category == NULL) { category = (Category_cfg *) malloc(sizeof(Category_cfg)); category->name = (char *) malloc(strlen(name) + 1); strcpy(category->name, &buffer[1]); category->items = NULL; category->numitems = category->maxitems = 0; category->next = NULL; } else { sprintf(buffer, "Category %s redefined", name); cfgParserError(linenum, fname, buffer); } cfgAddToHash(config, category); return category; } void cfgParseField(char *buffer, Category_cfg * category, int linenum, const char *fname) { char *value, *name; name = &buffer[0]; value = strchr(name, '='); if(!value) cfgParserError(linenum, fname, "'=' expected"); *(value++) = '\0'; name = cfgClearWS(name); value = cfgClearWS(value); if(strlen(name) == 0) cfgParserError(linenum, fname, "name expected before '='"); cfgAddToCategory(category, name, value); } Configuration_cfg *cfgReadConfiguration(const char *filename) { Configuration_cfg *config; FILE *cfgfile; int i; config = (Configuration_cfg *) malloc(sizeof(Configuration_cfg)); for(i = 0; i < HASH_BUCKETS; i++) config->items[i] = NULL; if(filename == NULL) filename = DEFAULT_CONFIGFILE; cfgfile = fopen(filename, "r"); if(!cfgfile) { cfgfile = fopen(CONFIGFILE_STUB, "r"); if(!cfgfile) { logPrintf(CRITICAL, "Couldn't open configuration file %s: \n", filename); return NULL; } } if(cfgLoadConfiguration(config, cfgfile, filename)) return NULL; fclose(cfgfile); return config; } void cfgFreeConfiguration(Configuration_cfg * config) { int i; Category_cfg *category; for(i = 0; i < HASH_BUCKETS; i++) { category = config->items[i]; while(category != NULL) { Category_cfg *next = category->next; if(category->items != NULL) free(category->items); free(category); category = next; } } free(config); } const ConfigItem_cfg *cfgGetConfigItem(Configuration_cfg * config, const char *option) { char buffer[BUFFER_SIZE]; char *cat = &buffer[0], *field; int i; const Category_cfg *category; strcpy(buffer, option); field = strchr(buffer, '.'); if(field == NULL) return NULL; *(field++) = '\0'; if(!(category = cfgGetCategory(config, cat))) return NULL; for(i = 0; i < category->numitems; i++) if(strcmp(category->items[i].name, field) == 0) { return &category->items[i]; } return NULL; } int cfgLoadConfigItemStrBuf(Configuration_cfg * config, const char *option, char *buf) { const ConfigItem_cfg *item; if((item = cfgGetConfigItem(config, option)) != NULL) { strcpy(buf, item->value); return 1; } return 0; } int cfgLoadConfigItemStr(Configuration_cfg * config, const char *option, char **buf) { const ConfigItem_cfg *item; if((item = cfgGetConfigItem(config, option)) != NULL) { *buf = item->value; return 1; } return 0; } int cfgLoadConfigItemInt(Configuration_cfg * config, const char *option, int *buf) { const ConfigItem_cfg *item; if((item = cfgGetConfigItem(config, option)) != NULL) { *buf = atoi(item->value); return 1; } return 0; } int cfgLoadConfigItemIntD(Configuration_cfg * config, const char *option, int *buf, int def) { if(config == NULL) *buf = def; else if(!cfgLoadConfigItemInt(config, option, buf)) *buf = def; return 1; } int cfgLoadConfigItemDouble(Configuration_cfg * config, const char *option, double *buf) { const ConfigItem_cfg *item; if((item = cfgGetConfigItem(config, option)) != NULL) { *buf = strtod(item->value, NULL); return 1; } return 0; } int cfgLoadConfigItemDoubleD(Configuration_cfg * config, const char *option, double *buf, double def) { if(config == NULL) *buf = def; else if(!cfgLoadConfigItemDouble(config, option, buf)) *buf = def; return 1; } int cfgLoadConfigItemOption(Configuration_cfg * config, const char *option, const char **possible, const int *values, int *buf) { char *buffer; int i; if(!cfgLoadConfigItemStr(config, option, &buffer)) return 0; for(i = 0; possible[i] != NULL && strcmp(possible[i], buffer); i++) ; if(possible[i]) { *buf = values[i]; return 1; } return 0; } int cfgLoadConfigItemOptionD(Configuration_cfg * config, const char *option, const char **possible, const int *values, int *buf, int def) { if(config == NULL) *buf = def; else if(!cfgLoadConfigItemOption(config, option, possible, values, buf)) *buf = def; return 1; }