/* * userconf.cpp * * User configuration goop * * Copyright (c) 2004 Jack Lloyd * */ #include #include #include #include #include #include namespace { /* 99% of this crap stolen from Botan and horribly hacked up */ class Config_Error : public std::exception { public: Config_Error(const std::string err, unsigned int line_no = 0) { val = err; } ~Config_Error() throw() {} const char* what() const throw() { return val.c_str(); } private: std::string val; }; bool is_space(char c) { if(c == ' ' || c == '\t' || c == '\n' || c == '\r') return true; return false; } /************************************************* * Strip comments and whitespace from line * *************************************************/ std::string strip_whitespace(const std::string& line) { bool is_escaped = false, in_quote = false, in_string = false; std::string new_line; for(std::string::const_iterator j = line.begin(); j != line.end(); j++) { const char c = *j; if(c == '"' && !is_escaped && !in_string) { in_quote = !in_quote; continue; } if(c == '\'' && !is_escaped && !in_quote) { in_string = !in_string; continue; } if(c == '#' && !is_escaped && !in_quote && !in_string) return new_line; if(c == '\\' && !is_escaped) { is_escaped = true; continue; } if(is_space(c) && !in_quote && !in_string && !is_escaped) continue; new_line += c; is_escaped = false; } return new_line; } char digit2char(unsigned char b) { if(b == 0) return '0'; if(b == 1) return '1'; if(b == 2) return '2'; if(b == 3) return '3'; if(b == 4) return '4'; if(b == 5) return '5'; if(b == 6) return '6'; if(b == 7) return '7'; if(b == 8) return '8'; if(b == 9) return '9'; throw Config_Error("digit2char: Input is not a digit"); } unsigned char char2digit(char c) { if(c == '0') return 0; if(c == '1') return 1; if(c == '2') return 2; if(c == '3') return 3; if(c == '4') return 4; if(c == '5') return 5; if(c == '6') return 6; if(c == '7') return 7; if(c == '8') return 8; if(c == '9') return 9; throw Config_Error("char2digit: Invalid decimal char " + c); } std::string to_string(int n) { std::string lenstr; if(n) { while(n > 0) { lenstr = digit2char(n % 10) + lenstr; n /= 10; } } else lenstr = "0"; return lenstr; } int to_int(const std::string& number) { int n = 0; for(std::string::const_iterator j = number.begin(); j != number.end(); j++) { const int OVERFLOW_MARK = 0x7FFFFFFF / 10; unsigned char digit = char2digit(*j); if((n > OVERFLOW_MARK) || (n == OVERFLOW_MARK && digit > 5)) throw Config_Error("to_u32bit: Integer overflow"); n *= 10; n += digit; } return n; } /* Only split on the first delim */ std::vector split_on(const std::string& str, char delim) { std::vector elems; if(str == "") return elems; std::string substr; for(std::string::const_iterator j = str.begin(); j != str.end(); j++) { if(*j == delim && elems.size() == 0) { elems.push_back(substr); substr = ""; } else substr += *j; } if(substr == "") throw Config_Error("Unable to split string: " + str); elems.push_back(substr); return elems; } std::map do_load_options(const std::string& fsname) { std::ifstream config(fsname.c_str()); if(!config) throw Config_Error("Could not open config file " + fsname); unsigned int line_no = 0; std::string line; std::map results; while(std::getline(config, line)) { line_no++; line = strip_whitespace(line); if(line == "") continue; std::vector name_and_value; name_and_value = split_on(line, '='); if(name_and_value.size() != 2) throw Config_Error("Bad line: " + line, line_no); const std::string name = name_and_value[0]; const std::string value = name_and_value[1]; results[name] = value; } return results; } int do_save_options(const std::string& fsname, const std::map& opts) { std::ofstream out(fsname.c_str()); if(!out) return -1; std::map::const_iterator p = opts.begin(); while(p != opts.end()) { out << p->first << " = " << p->second << std::endl; p++; } return 0; } const char* str_cache_of(const std::map& options, std::map& cache, const std::string& option) { if(cache.find(option) != cache.end()) /* already cached */ return cache[option]; if(options.find(option) == options.end()) /* not set */ return NULL; cache[option] = strdup(options.find(option)->second.c_str()); return cache[option]; } } extern "C" { typedef std::map option_map; typedef std::map str_cache; cutlass_options* new_options(void) { option_map* new_map = new option_map(); str_cache* cache = new str_cache(); cutlass_options* opt = new cutlass_options; opt->val = new_map; opt->strcache = cache; pthread_mutex_init(&(opt->lock), 0); return opt; } cutlass_options* load_options(const char* filename) { option_map* new_map = new option_map(); str_cache* cache = new str_cache(); try { *new_map = do_load_options(filename); } catch(std::exception& e) { /* cutlass_sysmsg(NULL, CUT_DEBUG, "%s\n", e.what()); */ delete new_map; return NULL; } cutlass_options* opt = new cutlass_options; opt->val = new_map; opt->strcache = cache; pthread_mutex_init(&(opt->lock), 0); return opt; } int save_options(cutlass_options* opt, const char* filename) { pthread_mutex_lock(&(opt->lock)); option_map* x = static_cast(opt->val); int result = do_save_options(filename, *x); pthread_mutex_unlock(&(opt->lock)); return result; } void delete_options(cutlass_options* opt) { pthread_mutex_lock(&(opt->lock)); option_map* vals = static_cast(opt->val); str_cache* cache = static_cast(opt->val); /* clear out the c_str cache; allocated with strdup, so use free() */ str_cache::iterator p = cache->begin(); while(p != cache->end()) { free(p->second); p++; } delete vals; delete cache; pthread_mutex_unlock(&(opt->lock)); pthread_mutex_destroy(&(opt->lock)); delete opt; } void set_int_option(cutlass_options* opt, const char* option_name, int value) { pthread_mutex_lock(&(opt->lock)); option_map* x = static_cast(opt->val); (*x)[option_name] = to_string(value); pthread_mutex_unlock(&(opt->lock)); } void set_str_option(cutlass_options* opt, const char* option_name, const char* value) { pthread_mutex_lock(&(opt->lock)); option_map* vals = static_cast(opt->val); (*vals)[option_name] = value; pthread_mutex_unlock(&(opt->lock)); } int get_int_option(cutlass_options* opt, const char* option_name) { pthread_mutex_lock(&(opt->lock)); option_map* vals = static_cast(opt->val); if(vals->find(option_name) == vals->end()) /* not set */ { pthread_mutex_unlock(&(opt->lock)); return -1; } int result = to_int(vals->find(option_name)->second); pthread_mutex_unlock(&(opt->lock)); return result; } const char* get_str_option(cutlass_options* opt, const char* option_name) { pthread_mutex_lock(&(opt->lock)); option_map* vals = static_cast(opt->val); str_cache* cache = static_cast(opt->strcache); const char* result = str_cache_of(*vals, *cache, option_name); pthread_mutex_unlock(&(opt->lock)); return result; } }