LogCabin
Core/Config.cc
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines