|
@@ -0,0 +1,584 @@
|
|
|
|
+//
|
|
|
|
+// Athena style config parser
|
|
|
|
+// (would be better to have "one" implementation instead of .. 4 :)
|
|
|
|
+//
|
|
|
|
+//
|
|
|
|
+// Author: Florian Wilkemeyer <fw@f-ws.de>
|
|
|
|
+//
|
|
|
|
+// Copyright (c) RAthena Project (www.rathena.org) - Licensed under GNU GPL
|
|
|
|
+// For more information, see LICENCE in the main folder
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+#include <stdio.h>
|
|
|
|
+#include <stdlib.h>
|
|
|
|
+#include <string.h>
|
|
|
|
+
|
|
|
|
+#include "../common/cbasetypes.h"
|
|
|
|
+#include "../common/showmsg.h"
|
|
|
|
+#include "../common/db.h"
|
|
|
|
+#include "../common/malloc.h"
|
|
|
|
+
|
|
|
|
+#include "../common/raconf.h"
|
|
|
|
+
|
|
|
|
+#define SECTION_LEN 32
|
|
|
|
+#define VARNAME_LEN 64
|
|
|
|
+
|
|
|
|
+struct raconf {
|
|
|
|
+ DBMap *db;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+struct conf_value{
|
|
|
|
+ int64 intval;
|
|
|
|
+ bool bval;
|
|
|
|
+ double floatval;
|
|
|
|
+ size_t strval_len; // not includung \0
|
|
|
|
+ char strval[16];
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static struct conf_value *makeValue(const char *key, char *val, size_t val_len){
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+ char *p;
|
|
|
|
+ size_t sz;
|
|
|
|
+
|
|
|
|
+ sz = sizeof(struct conf_value);
|
|
|
|
+ if(val_len >= sizeof(v->strval))
|
|
|
|
+ sz += (val_len - sizeof(v->strval) + 1);
|
|
|
|
+
|
|
|
|
+ v = (struct conf_value*)aCalloc(1, sizeof(struct conf_value));
|
|
|
|
+ if(v == NULL){
|
|
|
|
+ ShowFatalError("raconf: makeValue => Out of Memory while allocating new node.\n");
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memcpy(v->strval, val, val_len);
|
|
|
|
+ v->strval[val_len+1] = '\0';
|
|
|
|
+ v->strval_len = val_len;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // Parse boolean value:
|
|
|
|
+ if((val_len == 4) && (strncmpi("true", val, 4) == 0))
|
|
|
|
+ v->bval = true;
|
|
|
|
+ else if((val_len == 3) && (strncmpi("yes", val, 3) == 0))
|
|
|
|
+ v->bval = true;
|
|
|
|
+ else if((val_len == 3) && (strncmpi("oui", val, 3) == 0))
|
|
|
|
+ v->bval = true;
|
|
|
|
+ else if((val_len == 2) && (strncmpi("si", val, 2) == 0))
|
|
|
|
+ v->bval = true;
|
|
|
|
+ else if((val_len == 2) && (strncmpi("ja", val, 2) == 0))
|
|
|
|
+ v->bval = true;
|
|
|
|
+ else if((val_len == 1) && (*val == '1'))
|
|
|
|
+ v->bval = true;
|
|
|
|
+ else if((val_len == 5) && (strncmpi("false", val, 5) == 0))
|
|
|
|
+ v->bval = false;
|
|
|
|
+ else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
|
|
|
|
+ v->bval = false;
|
|
|
|
+ else if((val_len == 3) && (strncmpi("non", val, 3) == 0))
|
|
|
|
+ v->bval = false;
|
|
|
|
+ else if((val_len == 2) && (strncmpi("no", val, 2) == 0))
|
|
|
|
+ v->bval = false;
|
|
|
|
+ else if((val_len == 4) && (strncmpi("nein", val, 4) == 0))
|
|
|
|
+ v->bval = false;
|
|
|
|
+ else if((val_len == 1) && (*val == '0'))
|
|
|
|
+ v->bval = false;
|
|
|
|
+ else
|
|
|
|
+ v->bval = false; // assume false.
|
|
|
|
+
|
|
|
|
+ // Parse number
|
|
|
|
+ // Supported formats:
|
|
|
|
+ // prefix: 0x hex .
|
|
|
|
+ // postix: h for hex
|
|
|
|
+ // b for bin (dual)
|
|
|
|
+ if( (val_len >= 1 && (val[val_len] == 'h')) || (val_len >= 2 && (val[0] == '0' && val[1] == 'x')) ){//HEX!
|
|
|
|
+ if(val[val_len] == 'h'){
|
|
|
|
+ val[val_len]= '\0';
|
|
|
|
+ v->intval = strtoull(val, NULL, 16);
|
|
|
|
+ val[val_len] = 'h';
|
|
|
|
+ }else
|
|
|
|
+ v->intval = strtoull(&val[2], NULL, 16);
|
|
|
|
+ }else if( val_len >= 1 && (val[val_len] == 'b') ){ //BIN
|
|
|
|
+ val[val_len] = '\0';
|
|
|
|
+ v->intval = strtoull(val, NULL, 2);
|
|
|
|
+ val[val_len] = 'b';
|
|
|
|
+ }else if( *val >='0' && *val <= '9'){ // begins with normal digit, so assume its dec.
|
|
|
|
+ // is it float?
|
|
|
|
+ bool is_float = false;
|
|
|
|
+
|
|
|
|
+ for(p = val; *p != '\0'; p++){
|
|
|
|
+ if(*p == '.'){
|
|
|
|
+ v->floatval = strtod(val, NULL);
|
|
|
|
+ v->intval = (int64) v->floatval;
|
|
|
|
+ is_float = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(is_float == false){
|
|
|
|
+ v->intval = strtoull(val, NULL, 10);
|
|
|
|
+ v->floatval = (double) v->intval;
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ // Everything else: lets use boolean for fallback
|
|
|
|
+ if(v->bval == true)
|
|
|
|
+ v->intval = 1;
|
|
|
|
+ else
|
|
|
|
+ v->intval = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return v;
|
|
|
|
+}//end: makeValue()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static bool configParse(raconf inst, const char *fileName){
|
|
|
|
+ FILE *fp;
|
|
|
|
+ char line[4096];
|
|
|
|
+ char currentSection[SECTION_LEN];
|
|
|
|
+ char *p;
|
|
|
|
+ char c;
|
|
|
|
+ int linecnt;
|
|
|
|
+ size_t linelen;
|
|
|
|
+ size_t currentSection_len;
|
|
|
|
+
|
|
|
|
+ fp = fopen(fileName, "r");
|
|
|
|
+ if(fp == NULL){
|
|
|
|
+ ShowError("configParse: cannot open '%s' for reading.\n", fileName);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // Start with empty section:
|
|
|
|
+ currentSection[0] = '\0';
|
|
|
|
+ currentSection_len = 0;
|
|
|
|
+
|
|
|
|
+ //
|
|
|
|
+ linecnt = 0;
|
|
|
|
+ while(1){
|
|
|
|
+ linecnt++;
|
|
|
|
+
|
|
|
|
+ if(fgets(line, sizeof(line), fp) != line)
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ linelen = strlen(line);
|
|
|
|
+ p = line;
|
|
|
|
+
|
|
|
|
+ // Skip whitespaces from beginning (space and tab)
|
|
|
|
+ _line_begin_skip_whities:
|
|
|
|
+ c = *p;
|
|
|
|
+ if(c == ' ' || c == '\t'){
|
|
|
|
+ p++;
|
|
|
|
+ linelen--;
|
|
|
|
+ goto _line_begin_skip_whities;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Remove linebreaks as (cr or lf) and whitespaces from line end!
|
|
|
|
+ _line_end_skip_whities_and_breaks:
|
|
|
|
+ c = p[linelen-1];
|
|
|
|
+ if(c == '\r' || c == '\n' || c == ' ' || c == '\t'){
|
|
|
|
+ p[--linelen] = '\0';
|
|
|
|
+ goto _line_end_skip_whities_and_breaks;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Empty line?
|
|
|
|
+ // or line starts with comment (commented out)?
|
|
|
|
+ if(linelen == 0 || (p[0] == '/' && p[1] == '/') || p[0] == ';')
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ // Variable names can contain:
|
|
|
|
+ // A-Za-z-_.0-9
|
|
|
|
+ //
|
|
|
|
+ // Sections start with [ .. ] (INI Style)
|
|
|
|
+ //
|
|
|
|
+ c = *p;
|
|
|
|
+
|
|
|
|
+ // check what we have.. :)
|
|
|
|
+ if(c == '['){ // got section!
|
|
|
|
+ // Got Section!
|
|
|
|
+ // Search for ]
|
|
|
|
+ char *start = (p+1);
|
|
|
|
+
|
|
|
|
+ while(1){
|
|
|
|
+ ++p;
|
|
|
|
+ c = *p;
|
|
|
|
+
|
|
|
|
+ if(c == '\0'){
|
|
|
|
+ ShowError("Syntax Error: unterminated Section name in %s:%u (expected ']')\n", fileName, linecnt);
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }else if(c == ']'){ // closing backet (section name termination)
|
|
|
|
+ if( (p - start + 1) > (sizeof(currentSection) ) ){
|
|
|
|
+ ShowError("Syntax Error: Section name in %s:%u is too large (max Supported length: %u chars)\n", fileName, linecnt, sizeof(currentSection)-1);
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Set section!
|
|
|
|
+ *p = '\0'; // add termination here.
|
|
|
|
+ memcpy(currentSection, start, (p-start)+1 ); // we'll copy \0, too! (we replaced the ] backet with \0.)
|
|
|
|
+ currentSection_len = (p-start);
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ }else if( (c >= '0' && c <= '9') || (c == '-') || (c == ' ') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
|
|
|
|
+ // skip .. (allowed char / specifier)
|
|
|
|
+ continue;
|
|
|
|
+ }else{
|
|
|
|
+ ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Section name.\n", c, fileName, linecnt, (p-line));
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }//endwhile: parse section name
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
|
|
|
|
+ // Got variable!
|
|
|
|
+ // Search for '=' or ':' wich termiantes the name
|
|
|
|
+ char *start = p;
|
|
|
|
+ char *valuestart = NULL;
|
|
|
|
+ size_t start_len;
|
|
|
|
+
|
|
|
|
+ while(1){
|
|
|
|
+ ++p;
|
|
|
|
+ c = *p;
|
|
|
|
+
|
|
|
|
+ if(c == '\0'){
|
|
|
|
+ ShowError("Syntax Error: unterminated Variable name in %s:%u\n", fileName, linecnt);
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }else if( (c == '=') || (c == ':') ){
|
|
|
|
+ // got name termination
|
|
|
|
+
|
|
|
|
+ *p = '\0'; // Terminate it so (start) will hold the pointer to the name.
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ }else if( (c >= '0' && c <= '9') || (c == '-') || (c == '_') || (c == '.') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ){
|
|
|
|
+ // skip .. allowed char
|
|
|
|
+ continue;
|
|
|
|
+ }else{
|
|
|
|
+ ShowError("Syntax Error: Invalid Character '%c' in %s:%u (offset %u) for Variable name.\n", c, fileName, linecnt, (p-line));
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }//endwhile: parse var name
|
|
|
|
+
|
|
|
|
+ start_len = (p-start);
|
|
|
|
+ if(start_len >= VARNAME_LEN){
|
|
|
|
+ ShowError("%s:%u Variable length exceeds limit of %u Characters.\n", fileName, linecnt, VARNAME_LEN-1);
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }else if(start_len == 0){
|
|
|
|
+ ShowError("%s:%u Empty Variable name is not allowed.\n", fileName, linecnt);
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ valuestart = (p+1);
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // Skip whitespace from begin of value (tab and space)
|
|
|
|
+ _skip_value_begin_whities:
|
|
|
|
+ c = *valuestart;
|
|
|
|
+ if(c == ' ' || c == '\t'){
|
|
|
|
+ valuestart++;
|
|
|
|
+ goto _skip_value_begin_whities;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Scan for value termination,
|
|
|
|
+ // wich can be \0 or comment start (// or ; (INI) )
|
|
|
|
+ //
|
|
|
|
+ p = valuestart;
|
|
|
|
+ while(1){
|
|
|
|
+ c = *p;
|
|
|
|
+ if(c == '\0'){
|
|
|
|
+ // Terminated by line end.
|
|
|
|
+ break;
|
|
|
|
+ }else if(c == '/' && p[1] == '/'){
|
|
|
|
+ // terminated by c++ style comment.
|
|
|
|
+ *p = '\0';
|
|
|
|
+ break;
|
|
|
|
+ }else if(c == ';'){
|
|
|
|
+ // terminated by ini style comment.
|
|
|
|
+ *p = '\0';
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ p++;
|
|
|
|
+ }//endwhile: search var value end.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // Strip whitespaces from end of value.
|
|
|
|
+ if(valuestart != p){ // not empty!
|
|
|
|
+ p--;
|
|
|
|
+ _strip_value_end_whities:
|
|
|
|
+ c = *p;
|
|
|
|
+ if(c == ' ' || c == '\t'){
|
|
|
|
+ *p = '\0';
|
|
|
|
+ p--;
|
|
|
|
+ goto _strip_value_end_whities;
|
|
|
|
+ }
|
|
|
|
+ p++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ // Buildin Hook:
|
|
|
|
+ if( stricmp(start, "import") == 0){
|
|
|
|
+ if( configParse(inst, valuestart) != true){
|
|
|
|
+ ShowError("%s:%u - Import of '%s' failed!\n", fileName, linecnt, valuestart);
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ // put it to db.
|
|
|
|
+ struct conf_value *v, *o;
|
|
|
|
+ char key[ (SECTION_LEN+VARNAME_LEN+1+1) ]; //+1 for delimiter, +1 for termination.
|
|
|
|
+ size_t section_len;
|
|
|
|
+
|
|
|
|
+ if(*currentSection == '\0'){ // empty / none
|
|
|
|
+ strncpy(key, "<unnamed>",9);
|
|
|
|
+ section_len = 9;
|
|
|
|
+ }else{
|
|
|
|
+ strncpy(key, currentSection, currentSection_len);
|
|
|
|
+ section_len = currentSection_len;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ key[section_len] = '.'; // Delim
|
|
|
|
+
|
|
|
|
+ strncpy(&key[section_len+1], start, start_len);
|
|
|
|
+
|
|
|
|
+ key[section_len + start_len + 1] = '\0';
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ v = makeValue(key, valuestart, (p-valuestart) );
|
|
|
|
+
|
|
|
|
+ // Try to get the old one before
|
|
|
|
+ o = strdb_get(inst->db, key);
|
|
|
|
+ if(o != NULL){
|
|
|
|
+ strdb_remove(inst->db, key);
|
|
|
|
+ aFree(o); //
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ strdb_put( inst->db, key, v);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }else{
|
|
|
|
+ ShowError("Syntax Error: unexpected Character '%c' in %s:%u (offset %u)\n", c, fileName, linecnt, (p-line) );
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ fclose(fp);
|
|
|
|
+ return true;
|
|
|
|
+}//end: configParse()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+#define MAKEKEY(dest, section, key) { size_t section_len, key_len; \
|
|
|
|
+ if(section == NULL || *section == '\0'){ \
|
|
|
|
+ strncpy(dest, "<unnamed>", 9); \
|
|
|
|
+ section_len = 9; \
|
|
|
|
+ }else{ \
|
|
|
|
+ section_len = strlen(section); \
|
|
|
|
+ strncpy(dest, section, section_len); \
|
|
|
|
+ } \
|
|
|
|
+ \
|
|
|
|
+ dest[section_len] = '.'; \
|
|
|
|
+ \
|
|
|
|
+ key_len = strlen(key); \
|
|
|
|
+ strncpy(&dest[section_len+1], key, key_len); \
|
|
|
|
+ dest[section_len + key_len + 1] = '\0'; \
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+raconf raconf_parse(const char *file_name){
|
|
|
|
+ struct raconf *rc;
|
|
|
|
+
|
|
|
|
+ rc = aCalloc(1, sizeof(struct raconf) );
|
|
|
|
+ if(rc == NULL){
|
|
|
|
+ ShowFatalError("raconf_parse: failed to allocate memory for new handle\n");
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rc->db = strdb_alloc(DB_OPT_BASE | DB_OPT_DUP_KEY, 98);
|
|
|
|
+ //
|
|
|
|
+
|
|
|
|
+ if(configParse(rc, file_name) != true){
|
|
|
|
+ ShowError("Failed to Parse Configuration file '%s'\n", file_name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}//end: raconf_parse()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+void raconf_destroy(raconf rc){
|
|
|
|
+ DBIterator *iter;
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ // Clear all entrys in db.
|
|
|
|
+ iter = db_iterator(rc->db);
|
|
|
|
+ for( v = (struct conf_value*)dbi_first(iter); dbi_exists(iter); v = (struct conf_value*)dbi_next(iter) ){
|
|
|
|
+ aFree(v);
|
|
|
|
+ }
|
|
|
|
+ dbi_destroy(iter);
|
|
|
|
+
|
|
|
|
+ db_destroy(rc->db);
|
|
|
|
+
|
|
|
|
+ aFree(rc);
|
|
|
|
+
|
|
|
|
+}//end: raconf_destroy()
|
|
|
|
+
|
|
|
|
+bool raconf_getbool(raconf rc, const char *section, const char *key, bool _default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL)
|
|
|
|
+ return _default;
|
|
|
|
+ else
|
|
|
|
+ return v->bval;
|
|
|
|
+}//end: raconf_getbool()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+float raconf_getfloat(raconf rc,const char *section, const char *key, float _default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL)
|
|
|
|
+ return _default;
|
|
|
|
+ else
|
|
|
|
+ return (float)v->floatval;
|
|
|
|
+}//end: raconf_getfloat()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int64 raconf_getint(raconf rc, const char *section, const char *key, int64 _default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL)
|
|
|
|
+ return _default;
|
|
|
|
+ else
|
|
|
|
+ return v->intval;
|
|
|
|
+
|
|
|
|
+}//end: raconf_getint()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const char* raconf_getstr(raconf rc, const char *section, const char *key, const char *_default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL)
|
|
|
|
+ return _default;
|
|
|
|
+ else
|
|
|
|
+ return v->strval;
|
|
|
|
+}//end: raconf_getstr()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+bool raconf_getboolEx(raconf rc, const char *section, const char *fallback_section, const char *key, bool _default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, fallback_section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+ return _default;
|
|
|
|
+ }else{
|
|
|
|
+ return v->bval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }else{
|
|
|
|
+ return v->bval;
|
|
|
|
+ }
|
|
|
|
+}//end: raconf_getboolEx()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+float raconf_getfloatEx(raconf rc,const char *section, const char *fallback_section, const char *key, float _default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, fallback_section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+ return _default;
|
|
|
|
+ }else{
|
|
|
|
+ return (float)v->floatval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }else{
|
|
|
|
+ return (float)v->floatval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}//end: raconf_getfloatEx()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+int64 raconf_getintEx(raconf rc, const char *section, const char *fallback_section, const char *key, int64 _default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, fallback_section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+ return _default;
|
|
|
|
+ }else{
|
|
|
|
+ return v->intval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }else{
|
|
|
|
+ return v->intval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}//end: raconf_getintEx()
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+const char* raconf_getstrEx(raconf rc, const char *section, const char *fallback_section, const char *key, const char *_default){
|
|
|
|
+ char keystr[SECTION_LEN + VARNAME_LEN + 1 + 1];
|
|
|
|
+ struct conf_value *v;
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+
|
|
|
|
+ MAKEKEY(keystr, fallback_section, key);
|
|
|
|
+ v = strdb_get(rc->db, keystr);
|
|
|
|
+ if(v == NULL){
|
|
|
|
+ return _default;
|
|
|
|
+ }else{
|
|
|
|
+ return v->strval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }else{
|
|
|
|
+ return v->strval;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}//end: raconf_getstrEx()
|