1 // ConfigFile.cpp
2
3
4 // ConfigFile.h
5 // Class for reading named values from configuration files
6 // Richard J. Wagner v2.1 24 May 2004 wagnerr@umich.edu
7
8 // Copyright (c) 2004 Richard J. Wagner
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to
12 // deal in the Software without restriction, including without limitation the
13 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
14 // sell copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26 // IN THE SOFTWARE.
27
28
29 #include "ConfigFile.h"
30
31 using std::string;
32
ConfigFile(string filename,string delimiter,string comment,string sentry)33 ConfigFile::ConfigFile( string filename, string delimiter,
34 string comment, string sentry )
35 : myDelimiter(delimiter), myComment(comment), mySentry(sentry)
36 {
37 // Construct a ConfigFile, getting keys and values from given file
38
39 std::ifstream in( filename.c_str() );
40
41 if( !in ) throw file_not_found( filename );
42
43 in >> (*this);
44 }
45
46
ConfigFile()47 ConfigFile::ConfigFile()
48 : myDelimiter( string(1,'=') ), myComment( string(1,'#') )
49 {
50 // Construct a ConfigFile without a file; empty
51 }
52
53
remove(const string & key)54 void ConfigFile::remove( const string& key )
55 {
56 // Remove key and its value
57 myContents.erase( myContents.find( key ) );
58 return;
59 }
60
61
keyExists(const string & key) const62 bool ConfigFile::keyExists( const string& key ) const
63 {
64 // Indicate whether key is found
65 mapci p = myContents.find( key );
66 return ( p != myContents.end() );
67 }
68
69
70 /* static */
trim(string & s)71 void ConfigFile::trim( string& s )
72 {
73 // Remove leading and trailing whitespace
74 static const char whitespace[] = " \n\t\v\r\f";
75 s.erase( 0, s.find_first_not_of(whitespace) );
76 s.erase( s.find_last_not_of(whitespace) + 1U );
77 }
78
79
operator <<(std::ostream & os,const ConfigFile & cf)80 std::ostream& operator<<( std::ostream& os, const ConfigFile& cf )
81 {
82 // Save a ConfigFile to os
83 for( ConfigFile::mapci p = cf.myContents.begin();
84 p != cf.myContents.end();
85 ++p )
86 {
87 os << p->first << " " << cf.myDelimiter << " ";
88 os << p->second << std::endl;
89 }
90 return os;
91 }
92
93
operator >>(std::istream & is,ConfigFile & cf)94 std::istream& operator>>( std::istream& is, ConfigFile& cf )
95 {
96 // Load a ConfigFile from is
97 // Read in keys and values, keeping internal whitespace
98 typedef string::size_type pos;
99 const string& delim = cf.myDelimiter; // separator
100 const string& comm = cf.myComment; // comment
101 const string& sentry = cf.mySentry; // end of file sentry
102 const pos skip = delim.length(); // length of separator
103
104 string nextline = ""; // might need to read ahead to see where value ends
105
106 while( is || nextline.length() > 0 )
107 {
108 // Read an entire line at a time
109 string line;
110 if( nextline.length() > 0 )
111 {
112 line = nextline; // we read ahead; use it now
113 nextline = "";
114 }
115 else
116 {
117 std::getline( is, line );
118 }
119
120 // Ignore comments
121 line = line.substr( 0, line.find(comm) );
122
123 // Check for end of file sentry
124 if( sentry != "" && line.find(sentry) != string::npos ) return is;
125
126 // Parse the line if it contains a delimiter
127 pos delimPos = line.find( delim );
128 if( delimPos < string::npos )
129 {
130 // Extract the key
131 string key = line.substr( 0, delimPos );
132 line.replace( 0, delimPos+skip, "" );
133
134 // See if value continues on the next line
135 // Stop at blank line, next line with a key, end of stream,
136 // or end of file sentry
137 bool terminate = false;
138 while( !terminate && is )
139 {
140 std::getline( is, nextline );
141 terminate = true;
142
143 string nlcopy = nextline;
144 ConfigFile::trim(nlcopy);
145 if( nlcopy == "" ) continue;
146
147 nextline = nextline.substr( 0, nextline.find(comm) );
148 if( nextline.find(delim) != string::npos )
149 continue;
150 if( sentry != "" && nextline.find(sentry) != string::npos )
151 continue;
152
153 nlcopy = nextline;
154 ConfigFile::trim(nlcopy);
155 if( nlcopy != "" ) line += "\n";
156 line += nextline;
157 terminate = false;
158 }
159
160 // Store key and value
161 ConfigFile::trim(key);
162 ConfigFile::trim(line);
163 cf.myContents[key] = line; // overwrites if key is repeated
164 }
165 }
166
167 return is;
168 }
169