42 m_kind(sv_to_string(kind))
47 readFrom( path , kind ) ;
59 m_keys.reserve( m_map.size() ) ;
60 for(
auto & p : m_map )
61 m_keys.push_back( p.first ) ;
66 for(
auto p = map.begin() ; p != map.end() ; )
68 const std::string & key = (*p).first ;
69 if( !(*p).second.isOff() )
71 std::string value = (*p).second.isOn() ? sv_to_string(yes) : map.value(key) ;
74 while( p != map.end() && (*p).first == key )
81 std::ifstream stream ;
84 throw readError( path , sv_to_string(kind) ) ;
85 G_LOG(
"MapFile::read: reading [" << path.
str() <<
"]" ) ;
88 throw readError( path , sv_to_string(kind) ) ;
91void G::MapFile::readFrom( std::istream & stream )
94 while( stream.good() )
108 StringTokenView t( line_sv ,
" =\t"_sv ) ;
112 string_view key = t() ;
113 auto pos = line.find( key.data() , 0U , key.size() ) + key.size() ;
117 if( value.size() >= 2U && value.at(0U) ==
'"' && value.at(value.size()-1U) ==
'"' )
118 value = value.substr(1U,value.length()-2U) ;
124bool G::MapFile::ignore(
const std::string & line )
const
126 std::string::size_type pos_interesting = line.find_first_not_of(
" \t\r#") ;
127 if( pos_interesting == std::string::npos )
130 std::string::size_type pos_hash = line.find(
'#') ;
131 return pos_hash != std::string::npos && pos_hash < pos_interesting ;
137 tmp.readFrom( path , kind ) ;
142 std::string prefix = prefix_in.empty() ? std::string() : ( prefix_in +
": " ) ;
143 for(
const auto & key : m_keys )
145 auto p = find( key ) ;
146 if( p == m_map.end() ) continue ;
147 std::string value = (*p).second ;
148 G_LOG(
"MapFile::item: " << prefix << key <<
"=[" <<
149 (
Str::ifind(key,
"password") == std::string::npos ?
151 std::string(
"<not-logged>")
158 auto p = find( key ) ;
159 if( p == m_map.end() )
160 writeItem( stream , key , {} ) ;
162 writeItem( stream , key , (*p).second ) ;
167 const char * qq = value.find(
' ') == std::string::npos ?
"" :
"\"" ;
168 stream << key <<
"=" << qq << value << qq <<
"\n" ;
171std::string G::MapFile::quote(
const std::string & s )
173 return s.find_first_of(
" \t") == std::string::npos ? s : (
"\""+s+
"\"") ;
177 bool allow_read_error ,
bool allow_write_error )
const
179 List lines = read( path , m_kind , allow_read_error ) ;
180 commentOut( lines ) ;
182 if( make_backup ) backup( path ) ;
183 save( path , lines , allow_write_error ) ;
186G::MapFile::List G::MapFile::read(
const Path & path ,
string_view kind ,
bool allow_read_error )
const
189 std::ifstream file_in ;
191 if( !file_in.good() && !allow_read_error )
192 throw readError( path , kind ) ;
193 while( file_in.good() )
197 if( !file_in ) break ;
198 line_list.push_back( line ) ;
203void G::MapFile::commentOut( List & line_list )
const
205 for(
auto & line : line_list )
207 if( line.empty() || line.at(0U) ==
'#' )
209 line.insert( 0U , 1U ,
'#' ) ;
213void G::MapFile::replace( List & line_list )
const
215 for(
const auto & map_item : m_map )
218 for(
auto & line : line_list )
220 if( line.empty() ) continue ;
222 StringTokenView t( line_sv ,
" \r\n\t=#"_sv ) ;
226 const std::string & value = map_item.second ;
227 line =
Str::trimmed( std::string(map_item.first).append(1U,
' ').append(quote(value)) ,
Str::ws() ) ;
235 const std::string & value = map_item.second ;
236 line_list.push_back(
Str::trimmed( std::string(map_item.first).append(1U,
' ').append(quote(value)) ,
Str::ws() ) ) ;
241void G::MapFile::backup(
const Path & path )
245 std::string timestamp = Date(now).
str(Date::Format::yyyy_mm_dd) + Time(now).hhmmss() ;
246 Path backup( path.dirname() , path.basename() +
"." + timestamp ) ;
247 Process::Umask umask( Process::Umask::Mode::Tightest ) ;
251void G::MapFile::save(
const Path & path , List & line_list ,
bool allow_write_error )
253 std::ofstream file_out ;
254 File::open( file_out , path , File::Text() ) ;
255 std::copy( line_list.begin() , line_list.end() , std::ostream_iterator<std::string>(file_out,
"\n") ) ;
257 if( file_out.fail() && !allow_write_error )
258 throw writeError( path ) ;
263 auto p = find( key ) ;
264 if( p == m_map.end() )
268 else if( (*p).second.empty() )
280 auto p = find( key ) ;
281 return ( p == m_map.end() || (*p).second.empty() ) ? sv_to_string(default_) : (*p).second ;
284std::string G::MapFile::mandatoryValue(
string_view key )
const
286 if( find(key) == m_map.end() )
287 throw missingValueError( m_path , m_kind , sv_to_string(key) ) ;
288 return value( key ) ;
293 return { expand(value(key,default_.
str())) } ;
298 return { expand(mandatoryValue(key)) } ;
303 return { value(key,default_.
str()) } ;
308 return { mandatoryValue(key) } ;
318 auto p = find( key ) ;
319 if( p != m_map.end() )
322 G_ASSERT( std::find(m_keys.begin(),m_keys.end(),key) != m_keys.end() ) ;
323 m_keys.erase( std::find(m_keys.begin(),m_keys.end(),key) ) ;
329 std::string value = sv_to_string(value_in) ;
338 std::size_t find_single( std::string & s ,
char c , std::size_t start_pos )
340 std::array<char,2U> cc {{ c ,
'\0' }} ;
341 std::size_t pos = start_pos ;
344 pos = s.find( &cc[0] , pos ) ;
345 if( pos == std::string::npos )
349 else if( (pos+1U) < s.length() && s.at(pos+1U) == c )
351 s.erase( pos , 1U ) ;
352 if( (pos+1U) == s.length() )
354 pos = std::string::npos ;
369bool G::MapFile::expand_( std::string & value )
const
371 bool changed = false ;
372 std::size_t start = 0U ;
373 std::size_t end = 0U ;
374 std::size_t
const npos = std::string::npos ;
375 while( end < value.length() )
377 start = MapFileImp::find_single( value ,
'%' , end ) ;
378 if( start == npos ) break ;
379 end = value.find(
'%' , start+1U ) ;
380 if( end == npos ) break ;
382 std::string key = value.substr( start+1U , end-start-2U ) ;
383 auto p = find( key ) ;
384 if( p != m_map.end() )
386 std::size_t old = end - start ;
387 std::size_t new_ = (*p).second.length() ;
388 value.replace( start , old , (*p).second ) ;
399 std::string key = sv_to_string( key_in ) ;
400 if( find(key) == m_map.end() )
402 m_keys.push_back( key ) ;
403 m_map[key] = sv_to_string(value) ;
407 m_map[key] = sv_to_string(value) ;
411 m_map[key].append( 1U ,
',' ) ;
412 m_map[key].append( value.data() , value.size() ) ;
416G::StringMap::iterator G::MapFile::find(
string_view key )
418 return m_map.find( sv_to_string(key) ) ;
421G::StringMap::const_iterator G::MapFile::find( string_view key )
const
423 return m_map.find( sv_to_string(key) ) ;
428 return find( key ) != m_map.end() ;
443 return kind.empty() ? std::string(
"map") : sv_to_string(kind) ;
446std::string G::MapFile::epath(
const Path & path_in )
448 return path_in.empty() ? std::string() : (
" ["+path_in.str()+
"]") ;
453 std::string description =
"cannot read " + ekind(kind) +
" file" + epath(path) ;
454 return Error( description ) ;
459 return Error( std::string(
"cannot create ").append(ekind(kind)).append(
" file ").append(epath(path)) ) ;
462G::MapFile::Error G::MapFile::missingValueError(
const Path & path ,
const std::string & kind ,
463 const std::string & key )
465 return Error( std::string(
"no item [").append(key).append(
"] in ").append(ekind(kind)).append(
" file ").append(epath(path)) ) ;
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 Path &, string_view kind={})
Throws if the file is invalid.
void writeItem(std::ostream &, string_view key) const
Writes a single item from this map to the stream.
std::string value(string_view key, string_view default_={}) const
Returns a string value from the map.
bool booleanValue(string_view key, bool default_) const
Returns a boolean value from the map.
void editInto(const 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.
void add(string_view key, string_view value, bool clear=false)
Adds or updates a single item in the map.
unsigned int numericValue(string_view key, unsigned int default_) const
Returns a numeric value from the map.
const StringMap & map() const
Returns a reference to the internal map.
bool contains(string_view key) const
Returns true if the map contains the given key.
std::string expand(string_view value) const
Does one-pass variable substitution for the given string.
Path expandedPathValue(string_view key) const
Returns a mandatory path value from the map with expand().
void remove(string_view key)
Removes a value (if it exists).
MapFile()
Constructor for an empty map.
Path pathValue(string_view key) const
Returns a mandatory path value from the map.
const StringArray & keys() const
Returns a reference to the internal ordered list of keys.
void log(const std::string &prefix={}) const
Logs the contents.
A multimap-like container for command-line options and their values.
A Path object represents a file system path.
std::string str() const
Returns the path string.
bool empty() const noexcept
Returns true if size() is zero.
static std::size_t ifind(string_view s, string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
static bool match(string_view, string_view) noexcept
Returns true if the two strings are the same.
static string_view tailView(string_view in, std::size_t pos, string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static unsigned int toUInt(string_view s)
Converts string 's' to an unsigned int.
static std::istream & readLine(std::istream &stream, std::string &result, string_view eol={}, bool pre_erase_result=true, std::size_t limit=0U)
Reads a line from the stream using the given line terminator, which may be multi-character.
static std::string readLineFrom(std::istream &stream, string_view eol={})
Reads a line from the stream using the given line terminator.
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 bool isPositive(string_view) noexcept
Returns true if the string has a positive meaning, such as "1", "true", "yes".
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
static string_view trimLeftView(string_view, string_view ws, std::size_t limit=0U) noexcept
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
static string_view ws() noexcept
Returns a string of standard whitespace characters.
static string_view trimRightView(string_view sv, string_view ws, std::size_t limit=0U) noexcept
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
static SystemTime now()
Factory function for the current time.
BrokenDownTime local() const
Returns the locale-dependent local broken-down time.
A class like c++17's std::string_view.
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.