LogCabin
|
00001 /* 00002 * This file was downloaded from: 00003 * http://www-personal.umich.edu/~wagnerr/ConfigFile.html 00004 * 00005 * Copyright (c) 2004 Richard J. Wagner 00006 * 00007 * Permission is hereby granted, free of charge, to any person obtaining a copy 00008 * of this software and associated documentation files (the "Software"), to 00009 * deal in the Software without restriction, including without limitation the 00010 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 00011 * sell copies of the Software, and to permit persons to whom the Software is 00012 * furnished to do so, subject to the following conditions: 00013 * 00014 * The above copyright notice and this permission notice shall be included in 00015 * all copies or substantial portions of the Software. 00016 * 00017 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 00018 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00019 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 00020 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00021 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00022 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 00023 * IN THE SOFTWARE. 00024 * 00025 * ------------------------------------------------ 00026 * 00027 * It was subsequently modified: 00028 * 00029 * Copyright (c) 2012 Stanford University 00030 * Copyright (c) 2015 Diego Ongaro 00031 * 00032 * Permission to use, copy, modify, and distribute this software for any 00033 * purpose with or without fee is hereby granted, provided that the above 00034 * copyright notice and this permission notice appear in all copies. 00035 * 00036 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES 00037 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 00038 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR 00039 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 00040 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 00041 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 00042 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 00043 */ 00044 00045 #include <cxxabi.h> 00046 #include <fstream> 00047 00048 #include "Core/Config.h" 00049 #include "Core/StringUtil.h" 00050 00051 namespace LogCabin { 00052 namespace Core { 00053 00054 using Core::StringUtil::format; 00055 using std::string; 00056 00057 namespace { 00058 00059 /// The set of whitespace characters. 00060 static const char whitespace[] = " \n\t\v\r\f"; 00061 00062 /// Remove leading whitespace. 00063 void 00064 ltrim(string& s) 00065 { 00066 s.erase(0, s.find_first_not_of(whitespace)); 00067 } 00068 00069 /// Remove trailing whitespace. 00070 void 00071 rtrim(string& s) 00072 { 00073 s.erase(s.find_last_not_of(whitespace) + 1); 00074 } 00075 00076 /// Remove leading and trailing whitespace. 00077 void 00078 trim(string& s) 00079 { 00080 ltrim(s); 00081 rtrim(s); 00082 } 00083 00084 /// Demangle a C++ type name. 00085 string 00086 demangle(const string& name) 00087 { 00088 char* result = abi::__cxa_demangle(name.c_str(), 00089 NULL, NULL, NULL); 00090 if (result == NULL) 00091 return name; 00092 string ret(result); 00093 free(result); 00094 return ret; 00095 } 00096 00097 } // anonymous namespace 00098 00099 00100 // exceptions 00101 00102 Config::Exception::Exception(const std::string& error) 00103 : std::runtime_error(error) 00104 { 00105 } 00106 00107 00108 Config::FileNotFound::FileNotFound(const string& filename) 00109 : Exception(format( 00110 "The config file %s could not be opened", 00111 filename.c_str())) 00112 , filename(filename) 00113 { 00114 } 00115 00116 Config::KeyNotFound::KeyNotFound(const string& key) 00117 : Exception(format( 00118 "The configuration does not specify %s", 00119 key.c_str())) 00120 , key(key) 00121 { 00122 } 00123 00124 Config::ConversionError::ConversionError(const string& key, 00125 const string& value, 00126 const string& typeName) 00127 : Exception(format( 00128 "The value %s for key %s could not be converted to a %s", 00129 key.c_str(), value.c_str(), demangle(typeName).c_str())) 00130 , key(key) 00131 , value(value) 00132 , typeName(typeName) 00133 { 00134 } 00135 00136 // class Config 00137 00138 Config::Config(const string& delimiter, 00139 const string& comment) 00140 : delimiter(delimiter) 00141 , comment(comment) 00142 , contents() 00143 { 00144 } 00145 00146 Config::Config(const std::map<string, string>& options) 00147 : delimiter("=") 00148 , comment("#") 00149 , contents(options) 00150 { 00151 } 00152 00153 00154 void 00155 Config::readFile(const string& filename) 00156 { 00157 std::ifstream in(filename.c_str()); 00158 if (!in) 00159 throw FileNotFound(filename); 00160 in >> (*this); 00161 } 00162 00163 std::istream& 00164 operator>>(std::istream& is, Config& cf) 00165 { 00166 // might need to read ahead to see where value ends 00167 string nextLine; 00168 00169 while (is || !nextLine.empty()) { 00170 // Read an entire line at a time 00171 string line; 00172 if (nextLine.empty()) 00173 line = cf.readLine(is); 00174 else 00175 line.swap(nextLine); // we read ahead; use it now 00176 00177 size_t delimPos = line.find(cf.delimiter); 00178 if (delimPos != string::npos) { 00179 // Extract the key from line 00180 string key = line.substr(0, delimPos); 00181 rtrim(key); 00182 00183 // Extract the value from line 00184 string value; 00185 line.swap(value); 00186 value.erase(0, delimPos + cf.delimiter.length()); 00187 ltrim(value); 00188 00189 // See if value continues on the next line 00190 // Stop at empty line, next line with a key, or end of stream 00191 while (is) { 00192 line = cf.readLine(is); 00193 00194 // Empty lines end multi-line values 00195 if (line.empty()) 00196 break; 00197 00198 // Lines with delimiters end multi-line values 00199 delimPos = line.find(cf.delimiter); 00200 if (delimPos != string::npos) { 00201 nextLine.swap(line); 00202 break; 00203 } 00204 00205 // Append this line to the multi-line value. 00206 value += "\n"; 00207 value += line; 00208 } 00209 00210 // Store key and value 00211 cf.contents[key] = value; // overwrites if key is repeated 00212 } 00213 } 00214 00215 return is; 00216 } 00217 00218 std::ostream& 00219 operator<<(std::ostream& os, const Config& cf) 00220 { 00221 for (auto p = cf.contents.begin(); p != cf.contents.end(); ++p) { 00222 os << p->first << " " 00223 << cf.delimiter << " " 00224 << p->second << std::endl; 00225 } 00226 return os; 00227 } 00228 00229 bool 00230 Config::keyExists(const string& key) const 00231 { 00232 return (contents.find(key) != contents.end()); 00233 } 00234 00235 void 00236 Config::set(const string& key, const string& value) 00237 { 00238 string k = key; 00239 string v = value; 00240 trim(k); 00241 trim(v); 00242 contents[k] = v; 00243 } 00244 00245 void 00246 Config::remove(const string& key) 00247 { 00248 auto it = contents.find(key); 00249 if (it != contents.end()) 00250 contents.erase(it); 00251 } 00252 00253 // private methods 00254 00255 template<> 00256 std::string 00257 Config::fromString<std::string>(const string& key, const string& s) 00258 { 00259 return s; 00260 } 00261 00262 template<> 00263 bool 00264 Config::fromString<bool>(const string& key, const string& s) 00265 { 00266 static const std::map<string, bool> values { 00267 // These must be in sorted ASCII order for binary search to work. All 00268 // alpha entries should appear once in lowercase and once in uppercase. 00269 { "0", false }, 00270 { "1", true }, 00271 { "F", false }, 00272 { "FALSE", false }, 00273 { "N", false }, 00274 { "NO", false }, 00275 { "T", true }, 00276 { "TRUE", true }, 00277 { "Y", true }, 00278 { "YES", true }, 00279 { "f", false }, 00280 { "false", false }, 00281 { "n", false }, 00282 { "no", false }, 00283 { "t", true }, 00284 { "true", true }, 00285 { "y", true }, 00286 { "yes", true }, 00287 }; 00288 auto it = values.find(s); 00289 if (it != values.end()) 00290 return it->second; 00291 throw ConversionError(key, s, "bool"); 00292 } 00293 00294 00295 std::string 00296 Config::readLine(std::istream& is) const 00297 { 00298 string line; 00299 std::getline(is, line); 00300 size_t commentPos = line.find(comment); 00301 if (commentPos != string::npos) 00302 line.erase(commentPos); 00303 trim(line); 00304 return line; 00305 } 00306 00307 } // namespace LogCabin::Core 00308 } // namespace LogCabin