E-MailRelay

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

gmapfile.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2001-2018 Graeme Walker <graeme_walker@users.sourceforge.net>
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gmapfile.cpp
19 //
20
21 #include "gdef.h"
22 #include "gmapfile.h"
23 #include "gstr.h"
24 #include "gpath.h"
25 #include "gprocess.h"
26 #include "gdatetime.h"
27 #include "gdate.h"
28 #include "gtime.h"
29 #include "gfile.h"
30 #include "glog.h"
31 #include "gassert.h"
32 #include <algorithm> // std::find
33 #include <iterator>
34 #include <stdexcept>
35
36 G::MapFile::MapFile() :
37 m_logging(true)
38 {
39 }
40
41 G::MapFile::MapFile( const G::Path & path ) :
42 m_logging(true)
43 {
44 if( path != Path() )
45 readFrom( path ) ;
46 }
47
48 G::MapFile::MapFile( std::istream & stream ) :
49 m_logging(true)
50 {
51 G_LOG( "MapFile::read: start" ) ;
52 readFrom( stream ) ;
53 }
54
55 G::MapFile::MapFile( const G::StringMap & map ) :
56 m_logging(true) ,
57 m_map(map)
58 {
59 m_keys.reserve( m_map.size() ) ;
60 for( StringMap::iterator p = m_map.begin() ; p != m_map.end() ; ++p )
61 m_keys.push_back( (*p).first ) ;
62 }
63
64 G::MapFile::MapFile( const OptionMap & map , const std::string & yes ) :
65 m_logging(true)
66 {
67 for( OptionMap::const_iterator p = map.begin() ; p != map.end() ; )
68 {
69 std::string key = (*p).first ;
70 if( map.contains(key) ) // ie. ignore "--foo=off"
71 {
72 std::string value = (*p).second.valued() ? map.value(key) : yes ;
73 log( key , value ) ;
74 add( key , value ) ;
75 }
76 while( p != map.end() && (*p).first == key ) // since we used OptionMap::value()
77 ++p ;
78 }
79 }
80
81 void G::MapFile::readFrom( const G::Path & path )
82 {
83 std::ifstream stream( path.str().c_str() ) ;
84 if( !stream.good() )
85 throw ReadError( path.str() ) ;
86 G_LOG( "MapFile::read: reading [" << path.str() << "]" ) ;
87 readFrom( stream ) ;
88 }
89
90 void G::MapFile::readFrom( std::istream & ss )
91 {
92 std::string line ;
93 while( ss.good() )
94 {
95 Str::readLineFrom( ss , "\n" , line ) ;
96 if( line.empty() )
97 continue ;
98 if( !ss )
99 break ;
100 if( ignore(line) )
101 continue ;
102
103 // no escaping here -- just strip quotes if the value starts and ends with them
104 std::string key ;
105 std::string value ;
106 {
107 StringArray part ;
108 Str::splitIntoTokens( line , part , " =\t" ) ;
109 if( part.size() == 0U )
110 continue ;
111 key = part[0] ;
112
113 value = G::Str::tail( line , line.find(key)+key.size() , std::string() ) ;
114 G::Str::trimLeft( value , " =\t" ) ;
115 G::Str::trimRight( value , G::Str::ws() ) ;
116
117 if( value.length() >= 2U && value.at(0U) == '"' && value.at(value.length()-1U) == '"' )
118 value = value.substr(1U,value.length()-2U) ;
119 }
120
121 log( key , value ) ;
122 add( key , value ) ;
123 }
124 }
125
126 bool G::MapFile::ignore( const std::string & line ) const
127 {
128 std::string::size_type pos_interesting = line.find_first_not_of(" \t\r#") ;
129 if( pos_interesting == std::string::npos )
130 return true ;
131
132 std::string::size_type pos_hash = line.find("#") ;
133 if( pos_hash != std::string::npos && pos_hash < pos_interesting )
134 return true ;
135
136 return false ;
137 }
138
139 void G::MapFile::check( const G::Path & path )
140 {
141 MapFile tmp ;
142 tmp.m_logging = false ;
143 tmp.readFrom( path ) ;
144 }
145
146 void G::MapFile::log() const
147 {
148 for( StringArray::const_iterator p = m_keys.begin() ; p != m_keys.end() ; ++p )
149 log( (*(m_map.find(*p))).first , (*(m_map.find(*p))).second ) ;
150 }
151
152 void G::MapFile::log( const std::string & key , const std::string & value ) const
153 {
154 log( m_logging , key , value ) ;
155 }
156
157 void G::MapFile::log( bool logging , const std::string & key , const std::string & value )
158 {
159 if( logging )
160 G_LOG( "MapFile::item: " << key << "=[" <<
161 ( key.find("password") == std::string::npos ?
162 Str::printable(value) :
163 std::string("<not-logged>")
164 ) << "]" ) ;
165 }
166
167 void G::MapFile::writeItem( std::ostream & stream , const std::string & key ) const
168 {
169 std::string value = m_map.find(key) == m_map.end() ? std::string() : (*m_map.find(key)).second ;
170 writeItem( stream , key , value ) ;
171 }
172
173 void G::MapFile::writeItem( std::ostream & stream , const std::string & key , const std::string & value )
174 {
175 log( true , key , value ) ;
176 const char * qq = value.find(' ') == std::string::npos ? "" : "\"" ;
177 stream << key << "=" << qq << value << qq << "\n" ;
178 }
179
180 std::string G::MapFile::quote( const std::string & s )
181 {
182 return s.find_first_of(" \t") == std::string::npos ? s : (std::string()+"\""+s+"\"") ;
183 }
184
185 void G::MapFile::editInto( const G::Path & path , bool make_backup , bool allow_read_error , bool allow_write_error ) const
186 {
187 typedef std::list<std::string> List ;
188 List lines = read( path , allow_read_error ) ;
189 commentOut( lines ) ;
190 replace( lines ) ;
191 if( make_backup ) backup( path ) ;
192 save( path , lines , allow_write_error ) ;
193 }
194
195 G::MapFile::List G::MapFile::read( const G::Path & path , bool allow_read_error ) const
196 {
197 List line_list ;
198 std::ifstream file_in( path.str().c_str() ) ;
199 if( !file_in.good() && !allow_read_error )
200 throw ReadError( path.str() ) ;
201 while( file_in.good() )
202 {
203 std::string line = Str::readLineFrom( file_in , "\n" ) ;
204 if( !file_in ) break ;
205 line_list.push_back( line ) ;
206 }
207 return line_list ;
208 }
209
210 void G::MapFile::commentOut( List & line_list ) const
211 {
212 for( List::iterator line_p = line_list.begin() ; line_p != line_list.end() ; ++line_p )
213 {
214 std::string & line = *line_p ;
215 if( line.empty() || line.at(0U) == '#' )
216 continue ;
217 line = std::string(1U,'#') + line ;
218 }
219 }
220
221 void G::MapFile::replace( List & line_list ) const
222 {
223 for( StringMap::const_iterator map_p = m_map.begin() ; map_p != m_map.end() ; ++map_p )
224 {
225 bool found = false ;
226 for( List::iterator line_p = line_list.begin() ; line_p != line_list.end() ; ++line_p )
227 {
228 std::string & line = (*line_p) ;
229 if( line.empty() ) continue ;
230 StringArray part ;
231 Str::splitIntoTokens( line , part , Str::ws()+"=#" ) ;
232 if( part.size() == 0U ) continue ;
233 if( part.at(0U) == (*map_p).first )
234 {
235 std::string value = (*map_p).second ;
236 line = Str::trimmed( (*map_p).first + " " + quote(value) , Str::ws() ) ;
237 found = true ;
238 break ;
239 }
240 }
241
242 if( !found )
243 {
244 std::string value = (*map_p).second ;
245 line_list.push_back( Str::trimmed( (*map_p).first + " " + quote(value) , Str::ws() ) ) ;
246 }
247 }
248 }
249
250 void G::MapFile::backup( const G::Path & path )
251 {
252 // ignore errors
253 DateTime::BrokenDownTime now = DateTime::local( DateTime::now() ) ;
254 std::string timestamp = Date(now).string(Date::yyyy_mm_dd) + Time(now).hhmmss() ;
255 Path backup( path.dirname() , path.basename() + "." + timestamp ) ;
256 Process::Umask umask( Process::Umask::Tightest ) ;
257 File::copy( path , backup , File::NoThrow() ) ;
258 }
259
260 void G::MapFile::save( const G::Path & path , List & line_list , bool allow_write_error )
261 {
262 std::ofstream file_out( path.str().c_str() , std::ios_base::out | std::ios_base::trunc ) ;
263 std::copy( line_list.begin() , line_list.end() , std::ostream_iterator<std::string>(file_out,"\n") ) ;
264 file_out.close() ;
265 if( file_out.fail() && !allow_write_error )
266 throw WriteError( path.str() ) ;
267 }
268
269 bool G::MapFile::booleanValue( const std::string & key , bool default_ ) const
270 {
271 StringMap::const_iterator p = m_map.find( key ) ;
272 if( p == m_map.end() )
273 {
274 return default_ ;
275 }
276 else if( (*p).second.empty() )
277 {
278 return true ;
279 }
280 else
281 {
282 return G::Str::isPositive( (*p).second ) ;
283 }
284 }
285
286 std::string G::MapFile::value( const std::string & key , const std::string & default_ ) const
287 {
288 StringMap::const_iterator p = m_map.find( key ) ;
289 std::string result = ( p == m_map.end() || (*p).second.empty() ) ? default_ : (*p).second ;
290 return result ;
291 }
292
293 std::string G::MapFile::value( const std::string & key , const char * default_ ) const
294 {
295 return value( key , std::string(default_) ) ;
296 }
297
298 std::string G::MapFile::mandatoryValue( const std::string & key ) const
299 {
300 if( m_map.find(key) == m_map.end() )
301 throw Missing( key ) ;
302 return value( key ) ;
303 }
304
305 G::Path G::MapFile::pathValue( const std::string & key ) const
306 {
307 return Path( mandatoryValue(key) ) ;
308 }
309
310 G::Path G::MapFile::expandedPathValue( const std::string & key ) const
311 {
312 return Path( expand(mandatoryValue(key)) ) ;
313 }
314
315 G::Path G::MapFile::pathValue( const std::string & key , const G::Path & default_ ) const
316 {
317 return Path( value(key,default_.str()) ) ;
318 }
319
320 G::Path G::MapFile::expandedPathValue( const std::string & key , const G::Path & default_ ) const
321 {
322 return Path( expand(value(key,default_.str())) ) ;
323 }
324
325 unsigned int G::MapFile::numericValue( const std::string & key , unsigned int default_ ) const
326 {
327 std::string s = value( key , std::string() ) ;
328 return !s.empty() && Str::isUInt(s) ? Str::toUInt( s ) : default_ ;
329 }
330
331 void G::MapFile::remove( const std::string & key )
332 {
333 StringMap::iterator p = m_map.find( key ) ;
334 if( p != m_map.end() )
335 {
336 m_map.erase( p ) ;
337 G_ASSERT( std::find(m_keys.begin(),m_keys.end(),key) != m_keys.end() ) ;
338 m_keys.erase( std::find(m_keys.begin(),m_keys.end(),key) ) ;
339 }
340 }
341
342 std::string G::MapFile::expand( const std::string & value_in ) const
343 {
344 std::string value( value_in ) ;
345 expand_( value ) ;
346 return value ;
347 }
348
349 namespace
350 {
351 size_t find_single( std::string & s , char c , size_t start_pos )
352 {
353 char cc[] = { c , '\0' } ;
354 size_t pos = start_pos ;
355 for(;;)
356 {
357 pos = s.find( cc , pos ) ;
358 if( pos == std::string::npos )
359 {
360 break ; // not found
361 }
362 else if( (pos+1U) < s.length() && s.at(pos+1U) == c )
363 {
364 s.erase( pos , 1U ) ;
365 if( (pos+1U) == s.length() )
366 {
367 pos = std::string::npos ;
368 break ;
369 }
370 pos++ ;
371 }
372 else
373 {
374 break ; // found
375 }
376 }
377 return pos ;
378 }
379 }
380
381 bool G::MapFile::expand_( std::string & value ) const
382 {
383 bool changed = false ;
384 size_t start = 0U ;
385 size_t end = 0U ;
386 size_t const npos = std::string::npos ;
387 while( end < value.length() )
388 {
389 start = find_single( value , '%' , end ) ;
390 if( start == npos ) break ;
391 end = value.find( "%" , start+1U ) ;
392 if( end == npos ) break ;
393 end++ ;
394 std::string key = value.substr( start+1U , end-start-2U ) ;
395 StringMap::const_iterator p = m_map.find( key ) ;
396 if( p != m_map.end() )
397 {
398 size_t old = end - start ;
399 size_t new_ = (*p).second.length() ;
400 value.replace( start , old , (*p).second ) ;
401 end += new_ ;
402 end -= old ;
403 changed = true ;
404 }
405 }
406 return changed ;
407 }
408
409 void G::MapFile::add( const std::string & key , const std::string & value )
410 {
411 if( m_map.find(key) == m_map.end() )
412 m_keys.push_back( key ) ;
413 m_map[key] = value ;
414 }
415
416 bool G::MapFile::contains( const std::string & key ) const
417 {
418 return m_map.find( key ) != m_map.end() ;
419 }
420
421 const G::StringMap & G::MapFile::map() const
422 {
423 return m_map ;
424 }
425
426 const G::StringArray & G::MapFile::keys() const
427 {
428 return m_keys ;
429 }
430
431 /// \file gmapfile.cpp
G::Path::str
std::string str() const
Returns the path string.
Definition: gpath.cpp:290
G::Str::printable
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:701
G::Str::tail
static std::string tail(const std::string &in, size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1102
G::File::copy
static bool copy(const Path &from, const Path &to, const NoThrow &)
Copies a file. Returns false on error.
Definition: gfile.cpp:79
G::MapFile::value
std::string value(const std::string &key, const std::string &default_=std::string()) const
Returns a string value from the map.
Definition: gmapfile.cpp:286
glog.h
G::Path::basename
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Definition: gpath.cpp:310
G::MapFile::keys
const G::StringArray & keys() const
Returns a reference to the ordered list of keys.
Definition: gmapfile.cpp:426
gdate.h
G::DateTime::local
static BrokenDownTime local(EpochTime epoch_time)
Converts from epoch time to local broken-down-time.
Definition: gdatetime.cpp:131
G::Str::isPositive
static bool isPositive(const std::string &)
Returns true if the string has a positive meaning, such as "1", "true", "yes".
Definition: gstr.cpp:1182
gtime.h
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:908
std
STL namespace.
G::MapFile::editInto
void editInto(const G::Path &path, bool make_backup, bool allow_read_error, bool allow_write_error) const
Edits an existing file so that its contents reflect this map.
Definition: gmapfile.cpp:185
G::OptionMap::contains
bool contains(const std::string &) const
Returns true if the map contains the given key, but ignoring un-valued() 'off' options.
Definition: goptionmap.cpp:58
G::OptionMap::begin
const_iterator begin() const
Returns the begin iterator.
Definition: goptionmap.cpp:33
G::MapFile::check
static void check(const G::Path &)
Throws if the file is invalid.
Definition: gmapfile.cpp:139
G::MapFile::remove
void remove(const std::string &key)
Removes a value (if it exists).
Definition: gmapfile.cpp:331
G::DateTime::now
static EpochTime now()
Returns the current epoch time.
Definition: gdatetime_unix.cpp:26
G::Str::trimLeft
static void trimLeft(std::string &s, const std::string &ws, size_type limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:224
G::OptionMap
A multimap-like container for command-line options and their values.
Definition: goptionmap.h:39
G::MapFile::pathValue
G::Path pathValue(const std::string &key) const
Returns a mandatory path value from the map.
Definition: gmapfile.cpp:305
G::MapFile::contains
bool contains(const std::string &key) const
Returns true if the map contains the given key.
Definition: gmapfile.cpp:416
G::MapFile::map
const G::StringMap & map() const
Returns a reference to the internal map.
Definition: gmapfile.cpp:421
G::Str::toUInt
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:488
gmapfile.h
G::MapFile::expandedPathValue
G::Path expandedPathValue(const std::string &key) const
Returns a mandatory path value from the map with expand().
Definition: gmapfile.cpp:310
gstr.h
gassert.h
G::Str::trimRight
static void trimRight(std::string &s, const std::string &ws, size_type limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:235
G::MapFile
A class for reading and editing key=value files, supporting comments, creation of backup files...
Definition: gmapfile.h:45
G::MapFile::numericValue
unsigned int numericValue(const std::string &key, unsigned int default_) const
Returns a numeric value from the map.
Definition: gmapfile.cpp:325
G::MapFile::add
void add(const std::string &key, const std::string &value)
Adds or updates a single item in the map.
Definition: gmapfile.cpp:409
gfile.h
G::MapFile::expand
std::string expand(const std::string &value) const
Does one-pass variable substitution for the given string.
Definition: gmapfile.cpp:342
gprocess.h
G::OptionMap::end
const_iterator end() const
Returns the off-the-end iterator.
Definition: goptionmap.cpp:38
G::Str::trimmed
static std::string trimmed(const std::string &s, const std::string &ws)
Returns a trim()med version of s.
Definition: gstr.cpp:251
G::Str::readLineFrom
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:733
G::Path::dirname
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
Definition: gpath.cpp:318
G::MapFile::MapFile
MapFile()
Constructor for an empty map.
Definition: gmapfile.cpp:36
G::MapFile::booleanValue
bool booleanValue(const std::string &key, bool default_) const
Returns a boolean value from the map.
Definition: gmapfile.cpp:269
gdatetime.h
G::MapFile::writeItem
void writeItem(std::ostream &, const std::string &key) const
Writes a single item from this map to the stream.
Definition: gmapfile.cpp:167
gpath.h
G::StringMap
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstrings.h:34
G::Str::isUInt
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception...
Definition: gstr.cpp:304
G::OptionMap::value
std::string value(const std::string &key, const std::string &default_=std::string()) const
Returns the value of the valued() option identified by the given key.
Definition: goptionmap.cpp:83
G::Path
A Path object represents a file system path.
Definition: gpath.h:72
G::Str::ws
static std::string ws()
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1078
G::MapFile::log
void log() const
Logs the contents.
Definition: gmapfile.cpp:146