46 readFrom( path , kind ) ;
58 m_keys.reserve( m_map.size() ) ;
59 for(
auto & p : m_map )
60 m_keys.push_back( p.first ) ;
65 for(
auto p = map.begin() ; p != map.end() ; )
67 std::string key = (*p).first ;
68 if( !(*p).second.isOff() )
70 std::string value = (*p).second.isOn() ? yes : map.value(key) ;
73 while( p != map.end() && (*p).first == key )
78void G::MapFile::readFrom(
const G::Path & path ,
const std::string & kind )
80 std::ifstream stream ;
83 throw readError( path , kind ) ;
84 G_LOG(
"MapFile::read: reading [" << path.
str() <<
"]" ) ;
87 throw readError( path , kind ) ;
90void G::MapFile::readFrom( std::istream & stream )
93 while( stream.good() )
114 value =
Str::tail( line , line.find(key)+key.size() , std::string() ) ;
118 if( value.length() >= 2U && value.at(0U) ==
'"' && value.at(value.length()-1U) ==
'"' )
119 value = value.substr(1U,value.length()-2U) ;
125bool G::MapFile::ignore(
const std::string & line )
const
127 std::string::size_type pos_interesting = line.find_first_not_of(
" \t\r#") ;
128 if( pos_interesting == std::string::npos )
131 std::string::size_type pos_hash = line.find(
'#') ;
132 if( pos_hash != std::string::npos && pos_hash < pos_interesting )
141 tmp.readFrom( path , kind ) ;
146 std::string prefix = prefix_in.empty() ? std::string() : ( prefix_in +
": " ) ;
147 for(
const auto & key : m_keys )
149 auto p = m_map.find( key ) ;
150 if( p == m_map.end() )
continue ;
151 std::string value = (*p).second ;
152 G_LOG(
"MapFile::item: " << prefix << key <<
"=[" <<
153 (
Str::ifind(key,
"password") == std::string::npos ?
155 std::string(
"<not-logged>")
162 std::string value = m_map.find(key) == m_map.end() ? std::string() : (*m_map.find(key)).second ;
163 writeItem( stream , key , value ) ;
168 const char * qq = value.find(
' ') == std::string::npos ?
"" :
"\"" ;
169 stream << key <<
"=" << qq << value << qq <<
"\n" ;
172std::string G::MapFile::quote(
const std::string & s )
174 return s.find_first_of(
" \t") == std::string::npos ? s : (
"\""+s+
"\"") ;
178 bool allow_read_error ,
bool allow_write_error )
const
180 List lines = read( path , m_kind , allow_read_error ) ;
181 commentOut( lines ) ;
183 if( make_backup ) backup( path ) ;
184 save( path , lines , allow_write_error ) ;
187G::MapFile::List G::MapFile::read(
const G::Path & path ,
const std::string & kind ,
bool allow_read_error )
const
190 std::ifstream file_in ;
192 if( !file_in.good() && !allow_read_error )
193 throw readError( path , kind ) ;
194 while( file_in.good() )
198 if( !file_in ) break ;
199 line_list.push_back( line ) ;
204void G::MapFile::commentOut( List & line_list )
const
206 for(
auto & line : line_list )
208 if( line.empty() || line.at(0U) ==
'#' )
210 line.insert( 0U , 1U ,
'#' ) ;
214void G::MapFile::replace( List & line_list )
const
216 for(
const auto & map_item : m_map )
219 for(
auto & line : line_list )
221 if( line.empty() ) continue ;
224 if( parts.empty() ) continue ;
225 if( parts.at(0U) == map_item.first )
227 std::string value = map_item.second ;
236 std::string value = map_item.second ;
242void G::MapFile::backup(
const G::Path & path )
246 std::string timestamp = Date(now).
str(Date::Format::yyyy_mm_dd) + Time(now).hhmmss() ;
248 Process::Umask umask( Process::Umask::Mode::Tightest ) ;
252void G::MapFile::save(
const G::Path & path , List & line_list ,
bool allow_write_error )
254 std::ofstream file_out ;
255 File::open( file_out , path , File::Text() ) ;
256 std::copy( line_list.begin() , line_list.end() , std::ostream_iterator<std::string>(file_out,
"\n") ) ;
258 if( file_out.fail() && !allow_write_error )
259 throw writeError( path ) ;
264 auto p = m_map.find( key ) ;
265 if( p == m_map.end() )
269 else if( (*p).second.empty() )
281 auto p = m_map.find( key ) ;
282 std::string result = ( p == m_map.end() || (*p).second.empty() ) ? default_ : (*p).second ;
288 return value( key , std::string(default_) ) ;
291std::string G::MapFile::mandatoryValue(
const std::string & key )
const
293 if( m_map.find(key) == m_map.end() )
294 throw missingValueError( m_path , m_kind , key ) ;
295 return value( key ) ;
300 return Path( mandatoryValue(key) ) ;
305 return Path( expand(mandatoryValue(key)) ) ;
310 return Path( value(key,default_.
str()) ) ;
315 return Path( expand(value(key,default_.
str())) ) ;
320 std::string s = value( key , std::string() ) ;
326 auto p = m_map.find( key ) ;
327 if( p != m_map.end() )
330 G_ASSERT( std::find(m_keys.begin(),m_keys.end(),key) != m_keys.end() ) ;
331 m_keys.erase( std::find(m_keys.begin(),m_keys.end(),key) ) ;
337 std::string value( value_in ) ;
346 std::size_t find_single( std::string & s ,
char c , std::size_t start_pos )
348 std::array<char,2U> cc {{ c ,
'\0' }} ;
349 std::size_t pos = start_pos ;
352 pos = s.find( &cc[0] , pos ) ;
353 if( pos == std::string::npos )
357 else if( (pos+1U) < s.length() && s.at(pos+1U) == c )
359 s.erase( pos , 1U ) ;
360 if( (pos+1U) == s.length() )
362 pos = std::string::npos ;
377bool G::MapFile::expand_( std::string & value )
const
379 bool changed = false ;
380 std::size_t start = 0U ;
381 std::size_t end = 0U ;
382 std::size_t
const npos = std::string::npos ;
383 while( end < value.length() )
385 start = MapFileImp::find_single( value ,
'%' , end ) ;
386 if( start == npos ) break ;
387 end = value.find(
'%' , start+1U ) ;
388 if( end == npos ) break ;
390 std::string key = value.substr( start+1U , end-start-2U ) ;
391 auto p = m_map.find( key ) ;
392 if( p != m_map.end() )
394 std::size_t old = end - start ;
395 std::size_t new_ = (*p).second.length() ;
396 value.replace( start , old , (*p).second ) ;
405void G::MapFile::add(
const std::string & key ,
const std::string & value ,
bool clear )
407 if( m_map.find(key) == m_map.end() )
409 m_keys.push_back( key ) ;
418 m_map[key].append( 1U ,
',' ) ;
419 m_map[key].append( value ) ;
425 return m_map.find( key ) != m_map.end() ;
438std::string G::MapFile::ekind(
const std::string & kind_in )
440 return kind_in.empty() ? std::string(
"map") : kind_in ;
443std::string G::MapFile::epath(
const G::Path & path_in )
445 return path_in.
empty() ? std::string() : (
" ["+path_in.str()+
"]") ;
448G::MapFile::Error G::MapFile::readError(
const Path & path ,
const std::string & kind_in )
450 std::string description =
"cannot read " + ekind(kind_in) +
" file" + epath(path) ;
451 return Error( description ) ;
454G::MapFile::Error G::MapFile::writeError(
const Path & path ,
const std::string & kind_in )
456 std::string description =
"cannot create " + ekind(kind_in) +
" file" + epath(path) ;
457 return Error( description ) ;
460G::MapFile::Error G::MapFile::missingValueError(
const Path & path ,
const std::string & kind ,
461 const std::string & key )
463 std::string description =
"no item [" + key +
"] in " + ekind(kind) +
" file" + epath(path) ;
464 return Error( description ) ;
std::string str(const char *fmt) const
Returns the formatted date, with the same restrictions as format().
An overload discriminator for G::File::open().
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
static bool copy(const Path &from, const Path &to, std::nothrow_t)
Copies a file. Returns false on error.
A class for reading, writing and editing key=value files, supporting variable expansion of percent-ke...
static void check(const G::Path &, const std::string &kind=std::string())
Throws if the file is invalid.
std::string expand(const std::string &value) const
Does one-pass variable substitution for the given string.
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.
bool booleanValue(const std::string &key, bool default_) const
Returns a boolean value from the map.
unsigned int numericValue(const std::string &key, unsigned int default_) const
Returns a numeric value from the map.
void add(const std::string &key, const std::string &value, bool clear=false)
Adds or updates a single item in the map.
const G::StringMap & map() const
Returns a reference to the internal map.
void remove(const std::string &key)
Removes a value (if it exists).
G::Path pathValue(const std::string &key) const
Returns a mandatory path value from the map.
MapFile()
Constructor for an empty map.
bool contains(const std::string &key) const
Returns true if the map contains the given key.
G::Path expandedPathValue(const std::string &key) const
Returns a mandatory path value from the map with expand().
const G::StringArray & keys() const
Returns a reference to the internal ordered list of keys.
void log(const std::string &prefix=std::string()) const
Logs the contents.
void writeItem(std::ostream &, const std::string &key) const
Writes a single item from this map to the stream.
std::string value(const std::string &key, const std::string &default_=std::string()) const
Returns a string value from the map.
A multimap-like container for command-line options and their values.
A Path object represents a file system path.
std::string basename() const
Returns the rightmost part of the path, ignoring "." parts.
Path dirname() const
Returns the path without the rightmost part, ignoring "." parts.
std::string str() const
Returns the path string.
bool empty() const noexcept
Returns true if size() is zero.
static bool isPositive(const std::string &)
Returns true if the string has a positive meaning, such as "1", "true", "yes".
static string_view ws()
Returns a string of standard whitespace characters.
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
static std::string & trimLeft(std::string &s, string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
static std::string & trimRight(std::string &s, string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception.
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
static std::size_t ifind(const std::string &s, const std::string &key, std::size_t pos=0U)
Returns the position of the key in 's' using a Latin-1 case-insensitive search.
static SystemTime now()
Factory function for the current time.
BrokenDownTime local() const
Returns the locale-dependent local broken-down time.
std::vector< std::string > StringArray
A std::vector of std::strings.
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Exception class for G::MapFile.