/** @file
$ Id $
$ Date $
$ Author $
@ copy & copy ; Marc W & auml ; ckerlin
@ license LGPL , see file < a href = " license.html " > COPYING < / a >
$ Log $
Revision 1.3 2005 / 11 / 29 12 : 39 : 42 marc
make it compilable with gcc 4.0 .2 and newer doxygen
Revision 1.2 2005 / 01 / 28 07 : 49 : 32 marc
Save configuration using file . hxx
Revision 1.1 2005 / 01 / 07 00 : 31 : 38 marc
initial version
1 2 3 4 5 6 7 8
5678901234567890123456789012345678901234567890123456789012345678901234567890
*/
# include <mrw/configfile.hxx>
# include <mrw/exception.hxx>
# include <mrw/string.hxx>
# include <mrw/file.hxx>
# include <fstream>
mrw : : ConfigFileReader : : Value &
mrw : : ConfigFileReader : : operator ( ) ( const std : : string & section ,
const std : : string & name ,
const std : : string & default_ )
throw ( std : : bad_exception ) {
_values [ section ] . insert ( std : : make_pair ( name , Value ( default_ ) ) ) ;
return _values [ section ] . find ( name ) - > second ;
}
mrw : : ConfigFileReader & mrw : : ConfigFileReader : : load ( const std : : string & filename )
throw ( std : : exception ) {
_filename = filename ;
return reload ( ) ;
}
mrw : : ConfigFileReader & mrw : : ConfigFileReader : : reload ( ) throw ( std : : exception ) {
return parse ( mrw : : File : : read ( _filename ) ) ;
}
mrw : : ConfigFileReader & mrw : : ConfigFileReader : : parse ( const std : : string & file )
throw ( std : : exception ) {
_values . erase ( _values . begin ( ) , _values . end ( ) ) ;
std : : string section ;
for ( std : : string : : size_type pos ( 0 ) ;
( pos = file . find_first_of ( " [=# " , pos ) ) ! = std : : string : : npos ; + + pos ) {
switch ( file [ pos ] ) {
case ' [ ' : _sections [ section ] = pos ; section = parseSection ( file , pos ) ; break ;
case ' = ' : _values [ section ] . insert ( parseValue ( file , pos ) ) ; break ;
case ' # ' : parseComment ( file , pos ) ; break ;
} ;
}
_sections [ section ] = file . size ( ) ;
return * this ;
}
std : : string mrw : : ConfigFileReader : : parseSection ( const std : : string & file ,
std : : string : : size_type & pos )
const throw ( std : : exception ) {
std : : string : : size_type start ( pos ) ;
if ( ( pos = file . find ( ' ] ' , pos ) ) = = std : : string : : npos )
throw mrw : : invalid_argument
( " section not terminated in file ' " + _filename + " ' \n "
" environment is: " + file . substr ( start - 10 > 0 ? start - 10 : 0 ) ) ;
return file . substr ( start + 1 , pos - start - 1 ) ;
}
mrw : : ConfigFileReader : : Values : : mapped_type : : value_type
mrw : : ConfigFileReader : : parseValue ( const std : : string & file ,
std : : string : : size_type & pos )
const throw ( std : : exception ) {
// find the begin of the variable name
std : : string : : size_type startName ( file . rfind ( ' \n ' , pos ) ) ;
if ( startName = = std : : string : : npos ) startName = 0 ;
startName = file . find_first_not_of ( " \n \t " , startName ) ;
if ( startName = = pos )
throw mrw : : invalid_argument
( " empty variable name in file ' " + _filename + " ' \n "
" environment is: " + file . substr ( startName - 10 > 0 ? startName - 10 : 0 , pos + 10 ) ) ;
// find the end of the variable name
std : : string : : size_type endName ( file . find_last_not_of ( " \n \t " , pos - 1 ) + 1 ) ;
// find the begin of the variable contents
std : : string : : size_type startVal ( file . find_first_not_of ( " \n \t " , + + pos ) ) ;
if ( startVal = = std : : string : : npos )
return std : : make_pair // end of file, empty value
( file . substr ( startName , endName - startName ) , Value ( " " , pos , pos ) ) ;
// find the end of the variable contents
std : : string : : size_type realStart ( startVal ) ; // doesn't cut the first quote
std : : string : : size_type endVal ( file [ startVal ] = = ' " ' ? // quoted with "
file . find ( ' " ' , + + startVal ) :
file [ startVal ] = = ' \' ' ? // quoted with '
file . find ( ' \' ' , + + startVal ) :
file . find_first_of ( " [=# " , startVal ) ) ; // normal
if ( endVal ! = std : : string : : npos & & file [ endVal ] = = ' = ' )
endVal = file . rfind ( ' \n ' , endVal ) ; // jump before following variable name
endVal = file . find_last_not_of ( " \n \t " , // remove trailing white spaces
endVal ! = std : : string : : npos ? endVal - 1 : endVal ) + 1 ;
std : : string : : size_type realEnd ( endVal ! = std : : string : : npos & & // quotes?
( ( file [ realStart ] = = ' " ' & & file [ endVal ] = = ' " ' ) | |
( file [ realStart ] = = ' \' ' & & file [ endVal ] = = ' \' ' ) )
? endVal + 1 : endVal ) ;
if ( endVal < = startVal )
return std : : make_pair // empty value
( file . substr ( startName , endName - startName ) , Value ( " " , pos , pos ) ) ;
return std : : make_pair // normal case
( file . substr ( startName , endName - startName ) ,
Value ( file . substr ( startVal , endVal - startVal ) , realStart , pos = realEnd ) ) ;
}
std : : string mrw : : ConfigFileReader : : parseComment ( const std : : string & file ,
std : : string : : size_type & pos )
const throw ( std : : bad_exception ) {
std : : string : : size_type start ( pos ) ;
pos = file . find ( ' \n ' , pos ) ;
return file . substr ( start , pos - start ) ;
}
// GNU g++ prior to 3.4 does not implement covariant returns
# if __GNUC__ == 3 && __GNUC_MINOR__ < 4
mrw : : ConfigFileReader &
# else
mrw : : ConfigFileWriter &
# endif
mrw : : ConfigFileWriter : : reload ( ) throw ( std : : exception ) {
# if __GNUC__ == 3 && __GNUC_MINOR__ < 4
return parse ( _file = mrw : : File : : read ( _filename ) ) ;
# else
return dynamic_cast < mrw : : ConfigFileWriter & >
( parse ( _file = mrw : : File : : read ( _filename ) ) ) ;
# endif
}
mrw : : ConfigFileWriter & mrw : : ConfigFileWriter : : save ( ) throw ( std : : exception ) {
if ( _filename = = " " ) return * this ;
// work in changes
bool changed ( false ) ;
for ( Values : : iterator sec ( _values . begin ( ) ) ; sec ! = _values . end ( ) ; + + sec )
for ( Values : : mapped_type : : iterator var ( sec - > second . begin ( ) ) ;
var ! = sec - > second . end ( ) ; + + var )
if ( var - > second . changed ) {
var - > second . changed = false ;
changed = true ;
// check if we need quoting
std : : string value ( var - > second . value ) ;
if ( value . find_first_of ( " [=# " ) ! = std : : string : : npos ) {
if ( value . find ( ' " ' ) = = std : : string : : npos ) {
value = ' " ' + value + ' " ' ;
} else if ( value . find ( ' \' ' ) = = std : : string : : npos ) {
value = ' \' ' + value + ' \' ' ;
} else {
throw mrw : : invalid_argument ( " Configuration value is not quotable: "
+ value ) ;
}
}
// recalculate all positions
if ( var - > second . start & &
value . size ( ) ! = var - > second . end - var - > second . start ) {
std : : string : : size_type diff
( value . size ( ) - var - > second . end + var - > second . start ) ;
for ( Values : : iterator sec2 ( _values . begin ( ) ) ;
sec2 ! = _values . end ( ) ; + + sec2 )
for ( Values : : mapped_type : : iterator var2 ( sec2 - > second . begin ( ) ) ;
var2 ! = sec2 - > second . end ( ) ; + + var2 )
if ( var2 - > second . start > var - > second . end ) {
var2 - > second . start + = diff ;
var2 - > second . end + = diff ;
}
for ( Sections : : iterator sec2 ( _sections . begin ( ) ) ;
sec2 ! = _sections . end ( ) ; + + sec2 )
if ( sec2 - > second > = var - > second . end ) sec2 - > second + = diff ;
}
// set the new value
if ( var - > second . start ) // simple change
_file . replace ( var - > second . start , var - > second . end - var - > second . start ,
value ) ;
else if ( _sections . find ( sec - > first ) ! = _sections . end ( ) ) // new value
_file . insert ( _sections [ sec - > first ] , " \n " + var - > first + " = " + value
+ ( _file [ _sections [ sec - > first ] ] = = ' \n ' ? " " : " \n " ) ) ;
else { // even a new section
_file + = " \n [ " + sec - > first + " ] \n " + var - > first + " = " + value ;
_sections [ sec - > first ] = _file . size ( ) ;
}
}
if ( changed ) mrw : : File : : save ( _filename , _file ) ;
return * this ;
}