Files
wxWidgets/src/common/config.cpp
Vadim Zeitlin 1af292a686 Use single BeforeLast() call in wxConfigPathChanger ctor.
Use a single BeforeLast() call with the "rest" argument and avoid calling
AfterLast() laster in wxConfigPathChanger ctor.

This is a small optimization which may count because wxConfigPathChanger is
used in a lot of wxFileConfig functions but, even more importantly, this works
around a bug in g++ 4 optimized build when the name was not filled by
AfterLast() call correctly as apparently the optimizer decided it was not
used. The real cause of this compiler bug was difficult to find as it couldn't
be reproduced in a simple test case but this change avoids it and fixes
wxFileConfig unit test in optimized build.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@65863 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
2010-10-22 14:17:42 +00:00

543 lines
17 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/common/config.cpp
// Purpose: implementation of wxConfigBase class
// Author: Vadim Zeitlin
// Modified by:
// Created: 07.04.98
// RCS-ID: $Id$
// Copyright: (c) 1997 Karsten Ballueder Ballueder@usa.net
// Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif //__BORLANDC__
#ifndef wxUSE_CONFIG_NATIVE
#define wxUSE_CONFIG_NATIVE 1
#endif
#include "wx/config.h"
#ifndef WX_PRECOMP
#include "wx/intl.h"
#include "wx/log.h"
#include "wx/app.h"
#include "wx/utils.h"
#include "wx/arrstr.h"
#include "wx/math.h"
#endif //WX_PRECOMP
#if wxUSE_CONFIG && ((wxUSE_FILE && wxUSE_TEXTFILE) || wxUSE_CONFIG_NATIVE)
#include "wx/apptrait.h"
#include "wx/file.h"
#include <stdlib.h>
#include <ctype.h>
#include <limits.h> // for INT_MAX
#include <float.h> // for FLT_MAX
// ----------------------------------------------------------------------------
// global and class static variables
// ----------------------------------------------------------------------------
wxConfigBase *wxConfigBase::ms_pConfig = NULL;
bool wxConfigBase::ms_bAutoCreate = true;
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// wxAppTraitsBase
// ----------------------------------------------------------------------------
wxConfigBase *wxAppTraitsBase::CreateConfig()
{
return new
#if defined(__WXMSW__) && wxUSE_CONFIG_NATIVE
wxRegConfig(wxTheApp->GetAppName(), wxTheApp->GetVendorName());
#elif defined(__WXPALMOS__) && wxUSE_CONFIG_NATIVE
wxPrefConfig(wxTheApp->GetAppName());
#else // either we're under Unix or wish to use files even under Windows
wxFileConfig(wxTheApp->GetAppName());
#endif
}
// ----------------------------------------------------------------------------
// wxConfigBase
// ----------------------------------------------------------------------------
IMPLEMENT_ABSTRACT_CLASS(wxConfigBase, wxObject)
// Not all args will always be used by derived classes, but including them all
// in each class ensures compatibility.
wxConfigBase::wxConfigBase(const wxString& appName,
const wxString& vendorName,
const wxString& WXUNUSED(localFilename),
const wxString& WXUNUSED(globalFilename),
long style)
: m_appName(appName), m_vendorName(vendorName), m_style(style)
{
m_bExpandEnvVars = true;
m_bRecordDefaults = false;
}
wxConfigBase::~wxConfigBase()
{
// required here for Darwin
}
wxConfigBase *wxConfigBase::Set(wxConfigBase *pConfig)
{
wxConfigBase *pOld = ms_pConfig;
ms_pConfig = pConfig;
return pOld;
}
wxConfigBase *wxConfigBase::Create()
{
if ( ms_bAutoCreate && ms_pConfig == NULL ) {
wxAppTraits * const traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
wxCHECK_MSG( traits, NULL, wxT("create wxApp before calling this") );
ms_pConfig = traits->CreateConfig();
}
return ms_pConfig;
}
// ----------------------------------------------------------------------------
// wxConfigBase reading entries
// ----------------------------------------------------------------------------
// implement both Read() overloads for the given type in terms of DoRead()
#define IMPLEMENT_READ_FOR_TYPE(name, type, deftype, extra) \
bool wxConfigBase::Read(const wxString& key, type *val) const \
{ \
wxCHECK_MSG( val, false, wxT("wxConfig::Read(): NULL parameter") ); \
\
if ( !DoRead##name(key, val) ) \
return false; \
\
*val = extra(*val); \
\
return true; \
} \
\
bool wxConfigBase::Read(const wxString& key, \
type *val, \
deftype defVal) const \
{ \
wxCHECK_MSG( val, false, wxT("wxConfig::Read(): NULL parameter") ); \
\
bool read = DoRead##name(key, val); \
if ( !read ) \
{ \
if ( IsRecordingDefaults() ) \
{ \
((wxConfigBase *)this)->DoWrite##name(key, defVal); \
} \
\
*val = defVal; \
} \
\
*val = extra(*val); \
\
return read; \
}
IMPLEMENT_READ_FOR_TYPE(String, wxString, const wxString&, ExpandEnvVars)
IMPLEMENT_READ_FOR_TYPE(Long, long, long, long)
IMPLEMENT_READ_FOR_TYPE(Double, double, double, double)
IMPLEMENT_READ_FOR_TYPE(Bool, bool, bool, bool)
#undef IMPLEMENT_READ_FOR_TYPE
// int is stored as long
bool wxConfigBase::Read(const wxString& key, int *pi) const
{
long l = *pi;
bool r = Read(key, &l);
wxASSERT_MSG( l < INT_MAX, wxT("int overflow in wxConfig::Read") );
*pi = (int)l;
return r;
}
bool wxConfigBase::Read(const wxString& key, int *pi, int defVal) const
{
long l = *pi;
bool r = Read(key, &l, defVal);
wxASSERT_MSG( l < INT_MAX, wxT("int overflow in wxConfig::Read") );
*pi = (int)l;
return r;
}
// Read floats as doubles then just type cast it down.
bool wxConfigBase::Read(const wxString& key, float* val) const
{
wxCHECK_MSG( val, false, wxT("wxConfig::Read(): NULL parameter") );
double temp;
if ( !Read(key, &temp) )
return false;
wxCHECK_MSG( fabs(temp) <= FLT_MAX, false,
wxT("float overflow in wxConfig::Read") );
wxCHECK_MSG( (temp == 0.0) || (fabs(temp) >= FLT_MIN), false,
wxT("float underflow in wxConfig::Read") );
*val = static_cast<float>(temp);
return true;
}
bool wxConfigBase::Read(const wxString& key, float* val, float defVal) const
{
wxCHECK_MSG( val, false, wxT("wxConfig::Read(): NULL parameter") );
if ( Read(key, val) )
return true;
*val = defVal;
return false;
}
// the DoReadXXX() for the other types have implementation in the base class
// but can be overridden in the derived ones
bool wxConfigBase::DoReadBool(const wxString& key, bool* val) const
{
wxCHECK_MSG( val, false, wxT("wxConfig::Read(): NULL parameter") );
long l;
if ( !DoReadLong(key, &l) )
return false;
wxASSERT_MSG( l == 0 || l == 1, wxT("bad bool value in wxConfig::DoReadInt") );
*val = l != 0;
return true;
}
bool wxConfigBase::DoReadDouble(const wxString& key, double* val) const
{
wxString str;
if ( Read(key, &str) )
{
if ( str.ToCDouble(val) )
return true;
// Previous versions of wxFileConfig wrote the numbers out using the
// current locale and not the C one as now, so attempt to parse the
// string as a number in the current locale too, for compatibility.
if ( str.ToDouble(val) )
return true;
}
return false;
}
// string reading helper
wxString wxConfigBase::ExpandEnvVars(const wxString& str) const
{
wxString tmp; // Required for BC++
if (IsExpandingEnvVars())
tmp = wxExpandEnvVars(str);
else
tmp = str;
return tmp;
}
// ----------------------------------------------------------------------------
// wxConfigBase writing
// ----------------------------------------------------------------------------
bool wxConfigBase::DoWriteDouble(const wxString& key, double val)
{
// Notice that we always write out the numbers in C locale and not the
// current one. This makes the config files portable between machines using
// different locales.
return DoWriteString(key, wxString::FromCDouble(val));
}
bool wxConfigBase::DoWriteBool(const wxString& key, bool value)
{
return DoWriteLong(key, value ? 1l : 0l);
}
// ----------------------------------------------------------------------------
// wxConfigPathChanger
// ----------------------------------------------------------------------------
wxConfigPathChanger::wxConfigPathChanger(const wxConfigBase *pContainer,
const wxString& strEntry)
{
m_bChanged = false;
m_pContainer = const_cast<wxConfigBase *>(pContainer);
// the path is everything which precedes the last slash and the name is
// everything after it -- and this works correctly if there is no slash too
wxString strPath = strEntry.BeforeLast(wxCONFIG_PATH_SEPARATOR, &m_strName);
// except in the special case of "/keyname" when there is nothing before "/"
if ( strPath.empty() &&
((!strEntry.empty()) && strEntry[0] == wxCONFIG_PATH_SEPARATOR) )
{
strPath = wxCONFIG_PATH_SEPARATOR;
}
if ( !strPath.empty() )
{
if ( m_pContainer->GetPath() != strPath )
{
// we do change the path so restore it later
m_bChanged = true;
/* JACS: work around a memory bug that causes an assert
when using wxRegConfig, related to reference-counting.
Can be reproduced by removing .wc_str() below and
adding the following code to the config sample OnInit under
Windows:
pConfig->SetPath(wxT("MySettings"));
pConfig->SetPath(wxT(".."));
int value;
pConfig->Read(wxT("MainWindowX"), & value);
*/
m_strOldPath = m_pContainer->GetPath().wc_str();
if ( *m_strOldPath.c_str() != wxCONFIG_PATH_SEPARATOR )
m_strOldPath += wxCONFIG_PATH_SEPARATOR;
m_pContainer->SetPath(strPath);
}
}
}
void wxConfigPathChanger::UpdateIfDeleted()
{
// we don't have to do anything at all if we didn't change the path
if ( !m_bChanged )
return;
// find the deepest still existing parent path of the original path
while ( !m_pContainer->HasGroup(m_strOldPath) )
{
m_strOldPath = m_strOldPath.BeforeLast(wxCONFIG_PATH_SEPARATOR);
if ( m_strOldPath.empty() )
m_strOldPath = wxCONFIG_PATH_SEPARATOR;
}
}
wxConfigPathChanger::~wxConfigPathChanger()
{
// only restore path if it was changed
if ( m_bChanged ) {
m_pContainer->SetPath(m_strOldPath);
}
}
// this is a wxConfig method but it's mainly used with wxConfigPathChanger
/* static */
wxString wxConfigBase::RemoveTrailingSeparator(const wxString& key)
{
wxString path(key);
// don't remove the only separator from a root group path!
while ( path.length() > 1 )
{
if ( *path.rbegin() != wxCONFIG_PATH_SEPARATOR )
break;
path.erase(path.end() - 1);
}
return path;
}
#endif // wxUSE_CONFIG
// ----------------------------------------------------------------------------
// static & global functions
// ----------------------------------------------------------------------------
// understands both Unix and Windows (but only under Windows) environment
// variables expansion: i.e. $var, $(var) and ${var} are always understood
// and in addition under Windows %var% is also.
// don't change the values the enum elements: they must be equal
// to the matching [closing] delimiter.
enum Bracket
{
Bracket_None,
Bracket_Normal = ')',
Bracket_Curly = '}',
#ifdef __WXMSW__
Bracket_Windows = '%', // yeah, Windows people are a bit strange ;-)
#endif
Bracket_Max
};
wxString wxExpandEnvVars(const wxString& str)
{
wxString strResult;
strResult.Alloc(str.length());
size_t m;
for ( size_t n = 0; n < str.length(); n++ ) {
switch ( str[n].GetValue() ) {
#ifdef __WXMSW__
case wxT('%'):
#endif //WINDOWS
case wxT('$'):
{
Bracket bracket;
#ifdef __WXMSW__
if ( str[n] == wxT('%') )
bracket = Bracket_Windows;
else
#endif //WINDOWS
if ( n == str.length() - 1 ) {
bracket = Bracket_None;
}
else {
switch ( str[n + 1].GetValue() ) {
case wxT('('):
bracket = Bracket_Normal;
n++; // skip the bracket
break;
case wxT('{'):
bracket = Bracket_Curly;
n++; // skip the bracket
break;
default:
bracket = Bracket_None;
}
}
m = n + 1;
while ( m < str.length() && (wxIsalnum(str[m]) || str[m] == wxT('_')) )
m++;
wxString strVarName(str.c_str() + n + 1, m - n - 1);
#ifdef __WXWINCE__
const bool expanded = false;
#else
// NB: use wxGetEnv instead of wxGetenv as otherwise variables
// set through wxSetEnv may not be read correctly!
bool expanded = false;
wxString tmp;
if (wxGetEnv(strVarName, &tmp))
{
strResult += tmp;
expanded = true;
}
else
#endif
{
// variable doesn't exist => don't change anything
#ifdef __WXMSW__
if ( bracket != Bracket_Windows )
#endif
if ( bracket != Bracket_None )
strResult << str[n - 1];
strResult << str[n] << strVarName;
}
// check the closing bracket
if ( bracket != Bracket_None ) {
if ( m == str.length() || str[m] != (wxChar)bracket ) {
// under MSW it's common to have '%' characters in the registry
// and it's annoying to have warnings about them each time, so
// ignroe them silently if they are not used for env vars
//
// under Unix, OTOH, this warning could be useful for the user to
// understand why isn't the variable expanded as intended
#ifndef __WXMSW__
wxLogWarning(_("Environment variables expansion failed: missing '%c' at position %u in '%s'."),
(char)bracket, (unsigned int) (m + 1), str.c_str());
#endif // __WXMSW__
}
else {
// skip closing bracket unless the variables wasn't expanded
if ( !expanded )
strResult << (wxChar)bracket;
m++;
}
}
n = m - 1; // skip variable name
}
break;
case wxT('\\'):
// backslash can be used to suppress special meaning of % and $
if ( n != str.length() - 1 &&
(str[n + 1] == wxT('%') || str[n + 1] == wxT('$')) ) {
strResult += str[++n];
break;
}
//else: fall through
default:
strResult += str[n];
}
}
return strResult;
}
// this function is used to properly interpret '..' in path
void wxSplitPath(wxArrayString& aParts, const wxString& path)
{
aParts.clear();
wxString strCurrent;
wxString::const_iterator pc = path.begin();
for ( ;; ) {
if ( pc == path.end() || *pc == wxCONFIG_PATH_SEPARATOR ) {
if ( strCurrent == wxT(".") ) {
// ignore
}
else if ( strCurrent == wxT("..") ) {
// go up one level
if ( aParts.size() == 0 )
{
wxLogWarning(_("'%s' has extra '..', ignored."), path);
}
else
{
aParts.erase(aParts.end() - 1);
}
strCurrent.Empty();
}
else if ( !strCurrent.empty() ) {
aParts.push_back(strCurrent);
strCurrent.Empty();
}
//else:
// could log an error here, but we prefer to ignore extra '/'
if ( pc == path.end() )
break;
}
else
strCurrent += *pc;
++pc;
}
}