/* ====================================================================
* Copyright (c) 2003-2006, Martin Hauner
* http://subcommander.tigris.org
*
* Subcommander is licensed as described in the file doc/COPYING, which
* you should have received as part of this distribution.
* ====================================================================
*/
// sc
#include "ConfigFile.h"
#include "util/apr.h"
#include "util/AprException.h"
// apr
#include <apr_env.h>
#include <apr_strings.h>
#include <apr_file_io.h>
#include <apr_file_info.h>
// qt
#include <qregexp.h>
#include <qstring.h>
// sys
#include <fstream>
const int align = -30; // left align config value keys in a character field of this size.
ConfigFile::ConfigFile( const sc::String& name, const unsigned char* cfgTemplate, int cfgSize,
const sc::String& path ) : _cfgTemplate(cfgTemplate), _cfgSize(cfgSize)
{
_name = name;
if( path == ConfigPathSys )
{
_path = getSystemConfigPath();
}
else
{
_path = path;
}
}
#define MAXCFGLINESIZE 1024
void ConfigFile::get( ConfigValues& cfg )
{
ensureConfig();
std::string ini;
ini = _path.getStr();
ini += "/";
ini += _name.getStr();
std::ifstream in( ini.c_str(), std::ios::binary );
if( !in )
{
// error
}
while( ! in.eof() )
{
char buf[MAXCFGLINESIZE];
// read line without the endline.
in.get( buf, MAXCFGLINESIZE );
if( in.eof() )
{
break;
}
// reset state after an empty line.
if( in.fail() )
{
in.clear();
}
// skip endline.
in.ignore();
insertConfigValue( sc::String(buf), cfg );
}
}
void ConfigFile::set( const ConfigValues& cfg )
{
std::string ini;
ini = _path.getStr();
ini += "/";
ini += _name.getStr();
std::string initmp(ini);
initmp += ".tmp";
std::ofstream out( initmp.c_str(), std::ios::binary );
// write remembered header
for( THeader::iterator it = _header.begin(); it != _header.end(); it++ )
{
const sc::String& s = *it;
if( (const char*)s )
{
out << s;
}
out << '\n';
}
out << '\n';
// write config values
for( ConfigValues::const_iterator it = cfg.begin(); it != cfg.end(); it++ )
{
const ConfigValue& value = *it;
QString line = QString("%1 = %2").arg( QString::fromUtf8(value.getKey()), align ).
arg( QString::fromUtf8((value.getStringValue())) );
const sc::String& s = sc::String(line.utf8());
if( (const char*)s )
{
out << s;
}
out << '\n';
}
out.close();
apr::Pool pool;
apr_status_t status;
status = apr_file_remove( ini.c_str(), pool );
status = apr_file_rename( initmp.c_str(), ini.c_str(), pool );
}
// old, ie. 0.6.0, 0.7.0
static QRegExp reProjectSection( "^\\[Project\\]\\s*$" );
static QRegExp reComment( "^#(?!#).*$" );
// new
static QRegExp reReadKeyValue( "^(\\S+)\\s+=\\s+(.*)\\s*$" );
static QRegExp reHeader( "^##.*$" );
void ConfigFile::insertConfigValue( const sc::String& s, ConfigValues& values )
{
static int prjcnt = -1;
if( reProjectSection.exactMatch(QString::fromUtf8(s)) )
{
prjcnt++;
return;
}
if( reComment.exactMatch(QString::fromUtf8(s)) )
{
// drop old simple comments.
}
else if( reHeader.exactMatch(QString::fromUtf8(s)) )
{
// remember header comments.
_header.push_back(s);
}
else if( reReadKeyValue.exactMatch(QString::fromUtf8(s)) )
{
QString key = reReadKeyValue.cap(1);
QString val = reReadKeyValue.cap(2);
// convert old keys to new keys:
if( key == "name" )
{
key = QString( "project.%1.name" ).arg( prjcnt );
}
else if( key == "guid" )
{
key = QString( "project.%1.guid" ).arg( prjcnt );
}
else if( key == "trunk-name" )
{
key = QString( "project.%1.trunk.name" ).arg( prjcnt );
}
else if( key == "trunk-url" )
{
key = QString( "project.%1.trunk.url" ).arg( prjcnt );
}
else if( key == "branches-name" )
{
key = QString( "project.%1.branches.name" ).arg( prjcnt );
}
else if( key == "branches-url" )
{
key = QString( "project.%1.branches.url" ).arg( prjcnt );
}
else if( key == "tags-name" )
{
key = QString( "project.%1.tags.name" ).arg( prjcnt );
}
else if( key == "tags-url" )
{
key = QString( "project.%1.tags.url" ).arg( prjcnt );
}
else if( key == "wc-name" )
{
key = QString( "project.%1.wc.0.name" ).arg( prjcnt );
QString keyc = QString( "project.%1.wc.current" ).arg( prjcnt );
values.push_back( ConfigValue(sc::String(keyc.utf8()),sc::String("0")) );
}
else if( key == "wc-path" )
{
key = QString( "project.%1.wc.0.path" ).arg( prjcnt );
}
// store key
values.push_back( ConfigValue( sc::String(key.utf8()), sc::String(val.utf8()) ) );
}
}
void ConfigFile::ensureConfig()
{
apr::Pool pool;
apr_status_t status = 0;
apr_finfo_t finfo;
// check configuration directory
status = apr_stat( &finfo, _path.getStr(), APR_FINFO_TYPE, pool );
if( status != APR_SUCCESS )
{
status = apr_dir_make( _path.getStr(), APR_OS_DEFAULT, pool );
if( status != APR_SUCCESS )
{
sc::String msg( "failed to create configuration folder: " );
msg += _path;
throw apr::Exception( status, msg );
}
}
char* cfile = apr_psprintf( pool, "%s/%s", _path.getStr(), _name.getStr() );
status = apr_stat( &finfo, cfile, APR_FINFO_TYPE, pool );
if( status != APR_SUCCESS )
{
apr_file_t* file;
status = apr_file_open( &file, cfile, APR_CREATE|APR_WRITE, APR_OS_DEFAULT, pool );
apr_size_t size = _cfgSize;
status = apr_file_write( file, _cfgTemplate, &size );
status = apr_file_close( file );
}
}
const sc::String& ConfigFile::getConfigPath() const
{
return _path;
}
#ifdef _WIN32
// get HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\AppData
bool getSystemConfigPathFromRegistry( char** appdata, apr_pool_t* pool )
{
bool result = false;
LONG res;
HKEY hShellFolders;
char regAppdata[MAX_PATH] = {};
DWORD regAppdataSize = sizeof(regAppdata);
res = RegOpenKeyEx( HKEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0,
KEY_READ, &hShellFolders );
if( res == ERROR_SUCCESS )
{
result = RegQueryValueEx( hShellFolders, "AppData", NULL, NULL, (LPBYTE)
®Appdata, ®AppdataSize );
if( res == ERROR_SUCCESS )
{
*appdata = apr_psprintf( pool, "%s", regAppdata );
result = true;
}
RegCloseKey(hShellFolders);
}
return result;
}
#endif // _WIN32
sc::String ConfigFile::getSystemConfigPath()
{
apr::Pool pool;
char* confpath = 0;
#ifdef _WIN32
char* appdata = 0;
apr_status_t status = apr_env_get( &appdata, "APPDATA", pool );
if( status != APR_SUCCESS )
{
// if APPDATA is not set try the registry
if( !getSystemConfigPathFromRegistry(&appdata,pool) )
{
// failed => crash
return sc::String();
}
}
confpath = apr_psprintf( pool, "%s/Subcommander", appdata );
#else // ! _WIN32
apr_status_t status;
apr_uid_t uid;
apr_gid_t gid;
char* username = 0;
char* homepath = 0;
status = apr_uid_current( &uid, &gid, pool );
status = apr_uid_name_get( &username, uid, pool );
status = apr_uid_homepath_get( &homepath, username, pool );
confpath = apr_psprintf( pool, "%s/%s", homepath, ".subcommander" );
#endif
return sc::String(confpath);
}
syntax highlighted by Code2HTML, v. 0.9.1