/* ==================================================================== * 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 #include #include #include // qt #include #include // sys #include 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); }