40 m_kind(sv_to_string(kind))
43 readFromFile( path , kind ,
true ) ;
48 m_kind(sv_to_string(kind))
51 readFromFile( path , kind ,
false ) ;
56 readFromStream( stream ) ;
63 m_keys.reserve( m_map.size() ) ;
64 for(
auto & p : m_map )
65 m_keys.push_back( p.first ) ;
70 for(
auto p = map.begin() ; p != map.end() ; )
72 const std::string & key = (*p).first ;
73 if( !(*p).second.isOff() )
75 std::string value = (*p).second.isOn() ? sv_to_string(yes) : map.value(key) ;
78 while( p != map.end() && (*p).first == key )
83void G::MapFile::readFromFile(
const Path & path , std::string_view kind ,
bool do_throw )
85 std::ifstream stream ;
89 if( !do_throw ) return ;
90 throw readError( path , kind ) ;
93 readFromStream( stream ) ;
95 if( stream.bad() && do_throw )
96 throw readError( path , sv_to_string(kind) ) ;
99G::MapFile::List G::MapFile::readLines(
const Path & path , std::string_view kind ,
bool do_throw )
const
101 std::ifstream stream ;
105 if( !do_throw )
return {} ;
106 throw readError( path , kind ) ;
112 line_list.push_back( line ) ;
116void G::MapFile::readFromStream( std::istream & stream )
119 while( stream.good() )
131 auto pair = split( line ) ;
132 if( !pair.first.empty() )
133 add( pair.first , pair.second ) ;
137std::pair<std::string_view,std::string_view> G::MapFile::split( std::string_view line )
139 StringTokenView t( line , std::string_view(
" =\t",3U) ) ;
143 std::string_view key = t() ;
144 auto pos = line.find( key.data() , 0U , key.size() ) + key.size() ;
150 if( value.size() >= 2U && value.at(0U) ==
'"' && value.at(value.size()-1U) ==
'"' )
151 value = value.substr( 1U , value.length() - 2U ) ;
156std::string G::MapFile::join( std::string_view key , std::string_view value )
158 std::string line = sv_to_string(key).append(1U,
' ').append(quote(sv_to_string(value))) ;
162std::string G::MapFile::quote(
const std::string & s )
164 return s.find_first_of(
" \t") == std::string::npos ? s : (
"\""+s+
"\"") ;
167bool G::MapFile::valued(
const std::string & line )
169 auto pos_letter = line.find_first_not_of(
" \t\r#" ) ;
170 auto pos_hash = line.find(
'#' ) ;
171 if( pos_letter == std::string::npos )
173 else if( pos_hash == std::string::npos )
176 return pos_hash >= pos_letter ;
179bool G::MapFile::commentedOut(
const std::string & line )
181 auto pos_letter = line.find_first_not_of(
" \t\r#" ) ;
182 auto pos_hash = line.find(
'#' ) ;
183 if( pos_letter == std::string::npos )
185 else if( pos_hash == std::string::npos )
188 return pos_hash == 0U && pos_letter == 1U ;
193 std::string prefix = prefix_in.empty() ? std::string() : ( prefix_in +
": " ) ;
194 for(
const auto & key : m_keys )
196 auto p = find( key ) ;
197 if( p == m_map.end() ) continue ;
198 std::string value = (*p).second ;
199 G_LOG(
"MapFile::item: " << prefix << key <<
"=[" <<
200 (
Str::ifind(key,
"password") == std::string::npos ?
202 std::string(
"<not-logged>")
209 auto p = find( key ) ;
210 if( p == m_map.end() )
211 writeItem( stream , key , {} ) ;
213 writeItem( stream , key , (*p).second ) ;
218 const char * qq = value.find(
' ') == std::string::npos ?
"" :
"\"" ;
219 stream << key <<
"=" << qq << value << qq <<
"\n" ;
225 List lines = readLines( path , m_kind , do_throw ) ;
232 std::for_each( lines.begin() , lines.end() , [](std::string & line_){
233 if( valued(line_) ) line_.insert(0U,1U,
'\0') ;
234 else if( commentedOut(line_) ) line_.at(0U) =
'\0';
238 for(
const auto & map_item : m_map )
241 for(
auto & line : lines )
243 if( !line.empty() && line[0] ==
'\0' )
245 auto pair = split( std::string_view(line).substr(1U) ) ;
246 if( !pair.first.empty() && pair.first == map_item.first )
248 line = join( pair.first , map_item.second ) ;
256 lines.push_back( join(map_item.first,map_item.second) ) ;
261 std::for_each( lines.begin() , lines.end() , [](std::string & line_){
262 if( !line_.empty() && line_[0U] ==
'\0' ) line_[0] =
'#';} ) ;
266 if( make_backup && lines != old_lines )
270 std::ofstream file_out ;
272 std::copy( lines.begin() , lines.end() , std::ostream_iterator<std::string>(file_out,
"\n") ) ;
274 if( file_out.fail() && do_throw )
275 throw writeError( path ) ;
282 auto p = find( key ) ;
283 if( p == m_map.end() )
287 else if( (*p).second.empty() )
299 auto p = find( key ) ;
300 return ( p == m_map.end() || (*p).second.empty() ) ? sv_to_string(default_) : (*p).second ;
305 const std::string s = value( key , default_ ) ;
306 const std::string_view sv = s ;
315std::string G::MapFile::mandatoryValue( std::string_view key )
const
317 if( find(key) == m_map.end() )
318 throw missingValueError( m_path , m_kind , sv_to_string(key) ) ;
319 return value( key ) ;
324 return toPath( expand(value(key,default_.
str())) ) ;
329 return toPath( expand(mandatoryValue(key)) ) ;
334 return toPath( value(key,default_.
str()) ) ;
339 return toPath( mandatoryValue(key) ) ;
342G::Path G::MapFile::toPath( std::string_view path_in )
345 Path path1( path_in ) ;
359 auto p = find( key ) ;
360 if( p != m_map.end() )
363 G_ASSERT( std::find(m_keys.begin(),m_keys.end(),key) != m_keys.end() ) ;
364 m_keys.erase( std::find(m_keys.begin(),m_keys.end(),key) ) ;
375 std::string value = sv_to_string(value_in) ;
384 std::size_t find_single( std::string & s ,
char c , std::size_t start_pos )
386 std::array<char,2U> cc {{ c ,
'\0' }} ;
387 std::size_t pos = start_pos ;
390 pos = s.find( &cc[0] , pos ) ;
391 if( pos == std::string::npos )
395 else if( (pos+1U) < s.length() && s.at(pos+1U) == c )
397 s.erase( pos , 1U ) ;
398 if( (pos+1U) == s.length() )
400 pos = std::string::npos ;
415bool G::MapFile::expand_( std::string & value )
const
417 bool changed = false ;
418 std::size_t start = 0U ;
419 std::size_t end = 0U ;
420 std::size_t
const npos = std::string::npos ;
421 while( end < value.length() )
423 start = MapFileImp::find_single( value ,
'%' , end ) ;
424 if( start == npos ) break ;
425 end = value.find(
'%' , start+1U ) ;
426 if( end == npos ) break ;
428 std::string key = value.substr( start+1U , end-start-2U ) ;
429 auto p = find( key ) ;
430 if( p != m_map.end() )
432 std::size_t old = end - start ;
433 std::size_t new_ = (*p).second.length() ;
434 value.replace( start , old , (*p).second ) ;
445 auto p = find( key ) ;
446 if( p == m_map.end() )
448 m_keys.push_back( sv_to_string(key) ) ;
449 m_map[sv_to_string(key)] = sv_to_string(value) ;
453 m_map[sv_to_string(key)] = sv_to_string(value) ;
457 (*p).second.append(1U,
',').append( value.data() , value.size() ) ;
463 auto p = find( key ) ;
464 if( p == m_map.end() )
470 (*p).second = sv_to_string(value) ;
475G::StringMap::iterator G::MapFile::find( std::string_view key )
477 return m_map.find( sv_to_string(key) ) ;
480G::StringMap::const_iterator G::MapFile::find( std::string_view key )
const
482 return m_map.find( sv_to_string(key) ) ;
487 return find( key ) != m_map.end() ;
500std::string G::MapFile::strkind( std::string_view kind )
502 return kind.empty() ? std::string(
"map") : sv_to_string(kind) ;
505std::string G::MapFile::strpath(
const Path & path_in )
507 return path_in.empty() ? std::string() : (
" ["+path_in.str()+
"]") ;
510G::MapFile::Error G::MapFile::readError(
const Path & path , std::string_view kind )
512 std::string description =
"cannot read " + strkind(kind) +
" file" + strpath(path) ;
513 return Error( description ) ;
516G::MapFile::Error G::MapFile::writeError(
const Path & path , std::string_view kind )
518 return Error( std::string(
"cannot create ").append(strkind(kind)).append(
" file ").append(strpath(path)) ) ;
521G::MapFile::Error G::MapFile::missingValueError(
const Path & path ,
const std::string & kind ,
522 const std::string & key )
524 return Error( std::string(
"no item [").append(key).append(
"] in ").append(strkind(kind)).append(
" file ").append(strpath(path)) ) ;
An overload discriminator for G::File::open().
static void open(std::ofstream &, const Path &)
Calls open() on the given output file stream.
static bool isDirectory(const Path &path, std::nothrow_t)
Returns true if the path exists() and is a directory.
static Path backup(const Path &from, std::nothrow_t)
Creates a backup copy of the given file in the same directory and with a lightly-mangled filename.
bool remove(std::string_view key)
Removes a value (if it exists).
unsigned int numericValue(std::string_view key, unsigned int default_) const
Returns a numeric value from the map.
Path expandedPathValue(std::string_view key) const
Returns a mandatory path value from the map with expand().
std::string value(std::string_view key, std::string_view default_={}) const
Returns a string value from the map.
bool valueContains(std::string_view key, std::string_view token, std::string_view default_={}) const
Returns true if value(key,default_) contains the given comma-separated token.
bool update(std::string_view key, std::string_view value)
Updates an existing value.
std::string expand(std::string_view value) const
Does one-pass variable substitution for the given string.
bool booleanValue(std::string_view key, bool default_) const
Returns a boolean value from the map.
const StringMap & map() const
Returns a reference to the internal map.
Path editInto(const Path &path, bool make_backup, bool do_throw=true) const
Edits an existing file so that its contents reflect this map.
void add(std::string_view key, std::string_view value, bool clear=false)
Adds or updates a single item in the map.
bool contains(std::string_view key) const
Returns true if the map contains the given key.
MapFile()
Constructor for an empty map.
const StringArray & keys() const
Returns a reference to the internal ordered list of keys.
void writeItem(std::ostream &, std::string_view key) const
Writes a single item from this map to the stream.
void log(const std::string &prefix={}) const
Logs the contents.
Path pathValue(std::string_view key) const
Returns a mandatory path 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 str() const
Returns the path string.
bool empty() const noexcept
Returns true if the path is empty.
static std::string & trimRight(std::string &s, std::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_view trimLeftView(std::string_view, std::string_view ws, std::size_t limit=0U) noexcept
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
static std::string_view trimRightView(std::string_view sv, std::string_view ws, std::size_t limit=0U) noexcept
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
static bool isPositive(std::string_view) noexcept
Returns true if the string has a positive meaning, such as "1", "true", "yes".
static std::istream & readLine(std::istream &stream, std::string &result, std::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 printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static std::size_t ifind(std::string_view s, std::string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
static std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
static unsigned int toUInt(std::string_view s)
Converts string 's' to an unsigned int.
static std::string_view ws() noexcept
Returns a string of standard whitespace characters.
static std::string trimmed(const std::string &s, std::string_view ws)
Returns a trim()med version of s.
A zero-copy string token iterator where the token separators are runs of whitespace characters,...
std::string fromCodePageAnsi(std::string_view)
Converts from the active OEM codepage (see GetACP(), 1252 on unix) to UTF-8.
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.