///////////////////////////////////////////////////////////////////////////////
-// Name: fileconf.cpp
+// Name: src/common/fileconf.cpp
// Purpose: implementation of wxFileConfig derivation of wxConfig
// Author: Vadim Zeitlin
// Modified by:
// Created: 07.04.98 (adapted from appconf.cpp)
// RCS-ID: $Id$
-// Copyright: (c) 1997 Karsten Ballüder & Vadim Zeitlin
+// Copyright: (c) 1997 Karsten Ballueder & Vadim Zeitlin
// Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
-#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
-#pragma implementation "fileconf.h"
-#endif
-
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
+// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
- #pragma hdrstop
+ #pragma hdrstop
#endif //__BORLANDC__
#if wxUSE_CONFIG && wxUSE_FILECONFIG
#ifndef WX_PRECOMP
- #include "wx/string.h"
- #include "wx/intl.h"
+ #include "wx/dynarray.h"
+ #include "wx/string.h"
+ #include "wx/intl.h"
+ #include "wx/log.h"
+ #include "wx/app.h"
+ #include "wx/utils.h" // for wxGetHomeDir
+ #if wxUSE_STREAMS
+ #include "wx/stream.h"
+ #endif // wxUSE_STREAMS
#endif //WX_PRECOMP
-#include "wx/app.h"
-#include "wx/dynarray.h"
#include "wx/file.h"
-#include "wx/log.h"
#include "wx/textfile.h"
#include "wx/memtext.h"
#include "wx/config.h"
#include "wx/fileconf.h"
#include "wx/filefn.h"
-#if wxUSE_STREAMS
- #include "wx/stream.h"
-#endif // wxUSE_STREAMS
+#include "wx/base64.h"
-#include "wx/utils.h" // for wxGetHomeDir
-
-#if defined(__WXMAC__)
- #include "wx/mac/private.h" // includes mac headers
- #include "wx/filename.h" // for MacSetTypeAndCreator
-#endif
+#include "wx/stdpaths.h"
#if defined(__WXMSW__)
- #include "wx/msw/private.h"
+ #include "wx/msw/private.h"
#endif //windows.h
#if defined(__WXPM__)
- #define INCL_DOS
- #include <os2.h>
+ #define INCL_DOS
+ #include <os2.h>
#endif
#include <stdlib.h>
// ----------------------------------------------------------------------------
#ifndef MAX_PATH
- #define MAX_PATH 512
+ #define MAX_PATH 512
#endif
#define FILECONF_TRACE_MASK _T("fileconf")
bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
// find entry/subgroup (NULL if not found)
- wxFileConfigGroup *FindSubgroup(const wxChar *szName) const;
- wxFileConfigEntry *FindEntry (const wxChar *szName) const;
+ wxFileConfigGroup *FindSubgroup(const wxString& name) const;
+ wxFileConfigEntry *FindEntry (const wxString& name) const;
// delete entry/subgroup, return false if doesn't exist
- bool DeleteSubgroupByName(const wxChar *szName);
- bool DeleteEntry(const wxChar *szName);
+ bool DeleteSubgroupByName(const wxString& name);
+ bool DeleteEntry(const wxString& name);
// create new entry/subgroup returning pointer to newly created element
wxFileConfigGroup *AddSubgroup(const wxString& strName);
// get the last line belonging to an entry/subgroup of this group
wxFileConfigLineList *GetGroupLine(); // line which contains [group]
+ // may be NULL for "/" only
wxFileConfigLineList *GetLastEntryLine(); // after which our subgroups start
wxFileConfigLineList *GetLastGroupLine(); // after which the next group starts
// ----------------------------------------------------------------------------
// static functions
// ----------------------------------------------------------------------------
-wxString wxFileConfig::GetGlobalDir()
-{
- wxString strDir;
-#ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined
- strDir = wxT("sys$manager:");
-#elif defined(__WXMAC__)
- strDir = wxMacFindFolder( (short) kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder ) ;
-#elif defined( __UNIX__ )
- strDir = wxT("/etc/");
-#elif defined(__WXPM__)
- ULONG aulSysInfo[QSV_MAX] = {0};
- UINT drive;
- APIRET rc;
-
- rc = DosQuerySysInfo( 1L, QSV_MAX, (PVOID)aulSysInfo, sizeof(ULONG)*QSV_MAX);
- if (rc == 0)
+// this function modifies in place the given wxFileName object if it doesn't
+// already have an extension
+//
+// note that it's slightly misnamed under Mac as there it doesn't add an
+// extension but modifies the file name instead, so you shouldn't suppose that
+// fn.HasExt() is true after it returns
+static void AddConfFileExtIfNeeded(wxFileName& fn)
+{
+ if ( !fn.HasExt() )
{
- drive = aulSysInfo[QSV_BOOT_DRIVE - 1];
- strDir.Printf(wxT("%c:\\OS2\\"), 'A'+drive-1);
+#if defined( __WXMAC__ )
+ fn.SetName(fn.GetName() + wxT(" Preferences"));
+#elif defined( __UNIX__ )
+ fn.SetExt(wxT("conf"));
+#else // Windows
+ fn.SetExt(wxT("ini"));
+#endif // UNIX/Win
}
-#elif defined(__WXSTUBS__)
- wxASSERT_MSG( false, wxT("TODO") ) ;
-#elif defined(__DOS__)
- // There's no such thing as global cfg dir in MS-DOS, let's return
- // current directory (FIXME_MGL?)
- return wxT(".\\");
-#else // Windows
- wxChar szWinDir[MAX_PATH];
- ::GetWindowsDirectory(szWinDir, MAX_PATH);
-
- strDir = szWinDir;
- strDir << wxT('\\');
-#endif // Unix/Windows
-
- return strDir;
}
-wxString wxFileConfig::GetLocalDir()
+wxString wxFileConfig::GetGlobalDir()
{
- wxString strDir;
-
-#if defined(__WXMAC__) || defined(__DOS__)
- // no local dir concept on Mac OS 9 or MS-DOS
- return GetGlobalDir() ;
-#else
- wxGetHomeDir(&strDir);
-
-#ifdef __UNIX__
-#ifdef __VMS
- if (strDir.Last() != wxT(']'))
-#endif
- if (strDir.Last() != wxT('/')) strDir << wxT('/');
-#else
- if (strDir.Last() != wxT('\\')) strDir << wxT('\\');
-#endif
-#endif
-
- return strDir;
+ return wxStandardPaths::Get().GetConfigDir();
}
-wxString wxFileConfig::GetGlobalFileName(const wxChar *szFile)
+wxString wxFileConfig::GetLocalDir(int style)
{
- wxString str = GetGlobalDir();
- str << szFile;
+ wxUnusedVar(style);
- if ( wxStrchr(szFile, wxT('.')) == NULL )
-#if defined( __WXMAC__ )
- str << wxT(" Preferences") ;
-#elif defined( __UNIX__ )
- str << wxT(".conf");
-#else // Windows
- str << wxT(".ini");
-#endif // UNIX/Win
+ wxStandardPathsBase& stdp = wxStandardPaths::Get();
- return str;
+ // it so happens that user data directory is a subdirectory of user config
+ // directory on all supported platforms, which explains why we use it here
+ return style & wxCONFIG_USE_SUBDIR ? stdp.GetUserDataDir()
+ : stdp.GetUserConfigDir();
}
-wxString wxFileConfig::GetLocalFileName(const wxChar *szFile)
+wxFileName wxFileConfig::GetGlobalFile(const wxString& szFile)
{
-#ifdef __VMS__
- // On VMS I saw the problem that the home directory was appended
- // twice for the configuration file. Does that also happen for
- // other platforms?
- wxString str = wxT( '.' );
-#else
- wxString str = GetLocalDir();
-#endif
+ wxFileName fn(GetGlobalDir(), szFile);
-#if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ )
- str << wxT('.');
-#endif
+ AddConfFileExtIfNeeded(fn);
- str << szFile;
+ return fn;
+}
-#if defined(__WINDOWS__) || defined(__DOS__)
- if ( wxStrchr(szFile, wxT('.')) == NULL )
- str << wxT(".ini");
-#endif
+wxFileName wxFileConfig::GetLocalFile(const wxString& szFile, int style)
+{
+ wxFileName fn(GetLocalDir(style), szFile);
-#ifdef __WXMAC__
- str << wxT(" Preferences") ;
-#endif
+#if defined( __UNIX__ ) && !defined( __WXMAC__ )
+ if ( !(style & wxCONFIG_USE_SUBDIR) )
+ {
+ // dot-files under Unix start with, well, a dot (but OTOH they usually
+ // don't have any specific extension)
+ fn.SetName(wxT('.') + fn.GetName());
+ }
+ else // we do append ".conf" extension to config files in subdirectories
+#endif // defined( __UNIX__ ) && !defined( __WXMAC__ )
+ {
+ AddConfFileExtIfNeeded(fn);
+ }
- return str;
+ return fn;
}
// ----------------------------------------------------------------------------
// ctor
// ----------------------------------------------------------------------------
+IMPLEMENT_ABSTRACT_CLASS(wxFileConfig, wxConfigBase)
void wxFileConfig::Init()
{
// It's not an error if (one of the) file(s) doesn't exist.
// parse the global file
- if ( !m_strGlobalFile.empty() && wxFile::Exists(m_strGlobalFile) )
+ if ( m_fnGlobalFile.IsOk() && m_fnGlobalFile.FileExists() )
{
- wxTextFile fileGlobal(m_strGlobalFile);
+ wxTextFile fileGlobal(m_fnGlobalFile.GetFullPath());
- if ( fileGlobal.Open(m_conv/*ignored in ANSI build*/) )
+ if ( fileGlobal.Open(*m_conv/*ignored in ANSI build*/) )
{
Parse(fileGlobal, false /* global */);
SetRootPath();
}
else
{
- wxLogWarning(_("can't open global configuration file '%s'."), m_strGlobalFile.c_str());
+ wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile.GetFullPath().c_str());
}
}
// parse the local file
- if ( !m_strLocalFile.empty() && wxFile::Exists(m_strLocalFile) )
+ if ( m_fnLocalFile.IsOk() && m_fnLocalFile.FileExists() )
{
- wxTextFile fileLocal(m_strLocalFile);
- if ( fileLocal.Open(m_conv/*ignored in ANSI build*/) )
+ wxTextFile fileLocal(m_fnLocalFile.GetFullPath());
+ if ( fileLocal.Open(*m_conv/*ignored in ANSI build*/) )
{
Parse(fileLocal, true /* local */);
SetRootPath();
}
else
{
- wxLogWarning(_("can't open user configuration file '%s'."), m_strLocalFile.c_str() );
+ const wxString path = m_fnLocalFile.GetFullPath();
+ wxLogWarning(_("can't open user configuration file '%s'."),
+ path.c_str());
+
+ if ( m_fnLocalFile.FileExists() )
+ {
+ wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
+ path.c_str());
+ m_fnLocalFile.Clear();
+ }
}
}
// constructor supports creation of wxFileConfig objects of any type
wxFileConfig::wxFileConfig(const wxString& appName, const wxString& vendorName,
const wxString& strLocal, const wxString& strGlobal,
- long style, wxMBConv& conv)
+ long style,
+ const wxMBConv& conv)
: wxConfigBase(::GetAppName(appName), vendorName,
strLocal, strGlobal,
style),
- m_strLocalFile(strLocal), m_strGlobalFile(strGlobal),
- m_conv(conv)
+ m_fnLocalFile(strLocal),
+ m_fnGlobalFile(strGlobal),
+ m_conv(conv.Clone())
{
// Make up names for files if empty
- if ( m_strLocalFile.empty() && (style & wxCONFIG_USE_LOCAL_FILE) )
- m_strLocalFile = GetLocalFileName(GetAppName());
+ if ( !m_fnLocalFile.IsOk() && (style & wxCONFIG_USE_LOCAL_FILE) )
+ m_fnLocalFile = GetLocalFile(GetAppName(), style);
- if ( m_strGlobalFile.empty() && (style & wxCONFIG_USE_GLOBAL_FILE) )
- m_strGlobalFile = GetGlobalFileName(GetAppName());
+ if ( !m_fnGlobalFile.IsOk() && (style & wxCONFIG_USE_GLOBAL_FILE) )
+ m_fnGlobalFile = GetGlobalFile(GetAppName());
// Check if styles are not supplied, but filenames are, in which case
// add the correct styles.
- if ( !m_strLocalFile.empty() )
+ if ( m_fnLocalFile.IsOk() )
SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
- if ( !m_strGlobalFile.empty() )
+ if ( m_fnGlobalFile.IsOk() )
SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE);
// if the path is not absolute, prepend the standard directory to it
- // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
+ // unless explicitly asked not to
if ( !(style & wxCONFIG_USE_RELATIVE_PATH) )
{
- if ( !m_strLocalFile.empty() && !wxIsAbsolutePath(m_strLocalFile) )
- {
- wxString strLocal = m_strLocalFile;
- m_strLocalFile = GetLocalDir();
- m_strLocalFile << strLocal;
- }
+ if ( m_fnLocalFile.IsOk() )
+ m_fnLocalFile.MakeAbsolute(GetLocalDir(style));
- if ( !m_strGlobalFile.empty() && !wxIsAbsolutePath(m_strGlobalFile) )
- {
- wxString strGlobal = m_strGlobalFile;
- m_strGlobalFile = GetGlobalDir();
- m_strGlobalFile << strGlobal;
- }
+ if ( m_fnGlobalFile.IsOk() )
+ m_fnGlobalFile.MakeAbsolute(GetGlobalDir());
}
SetUmask(-1);
#if wxUSE_STREAMS
-wxFileConfig::wxFileConfig(wxInputStream &inStream, wxMBConv& conv)
- : m_conv(conv)
+wxFileConfig::wxFileConfig(wxInputStream &inStream, const wxMBConv& conv)
+ : m_conv(conv.Clone())
{
// always local_file when this constructor is called (?)
SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
m_linesHead =
m_linesTail = NULL;
- // translate everything to the current (platform-dependent) line
- // termination character
- wxString strTrans;
- {
- wxString strTmp;
-
- char buf[1024];
- do
- {
- inStream.Read(buf, WXSIZEOF(buf));
+ // read the entire stream contents in memory
+ wxWxCharBuffer cbuf;
+ static const size_t chunkLen = 1024;
- const wxStreamError err = inStream.GetLastError();
+ wxMemoryBuffer buf(chunkLen);
+ do
+ {
+ inStream.Read(buf.GetAppendBuf(chunkLen), chunkLen);
+ buf.UngetAppendBuf(inStream.LastRead());
- if ( err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF )
- {
- wxLogError(_("Error reading config options."));
- break;
- }
+ const wxStreamError err = inStream.GetLastError();
- strTmp.append(wxConvertMB2WX(buf), inStream.LastRead());
+ if ( err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF )
+ {
+ wxLogError(_("Error reading config options."));
+ break;
}
- while ( !inStream.Eof() );
-
- strTrans = wxTextBuffer::Translate(strTmp);
}
+ while ( !inStream.Eof() );
- wxMemoryText memText;
-
- // Now we can add the text to the memory text. To do this we extract line
- // by line from the translated string, until we've reached the end.
- //
- // VZ: all this is horribly inefficient, we should do the translation on
- // the fly in one pass saving both memory and time (TODO)
+#if wxUSE_UNICODE
+ size_t len;
+ cbuf = conv.cMB2WC((char *)buf.GetData(), buf.GetDataLen() + 1, &len);
+ if ( !len && buf.GetDataLen() )
+ {
+ wxLogError(_("Failed to read config options."));
+ }
+#else // !wxUSE_UNICODE
+ // no need for conversion
+ cbuf = wxCharBuffer::CreateNonOwned((char *)buf.GetData());
+#endif // wxUSE_UNICODE/!wxUSE_UNICODE
- const wxChar *pEOL = wxTextBuffer::GetEOL(wxTextBuffer::typeDefault);
- const size_t EOLLen = wxStrlen(pEOL);
- int posLineStart = strTrans.Find(pEOL);
- while ( posLineStart != -1 )
+ // now break it into lines
+ wxMemoryText memText;
+ for ( const wxChar *s = cbuf; ; ++s )
{
- wxString line(strTrans.Left(posLineStart));
+ const wxChar *e = s;
+ while ( *e != '\0' && *e != '\n' && *e != '\r' )
+ ++e;
- memText.AddLine(line);
+ // notice that we throw away the original EOL kind here, maybe we
+ // should preserve it?
+ if ( e != s )
+ memText.AddLine(wxString(s, e));
- strTrans = strTrans.Mid(posLineStart + EOLLen);
+ if ( *e == '\0' )
+ break;
- posLineStart = strTrans.Find(pEOL);
- }
+ // skip the second EOL byte if it's a DOS one
+ if ( *e == '\r' && e[1] == '\n' )
+ ++e;
- // also add whatever we have left in the translated string.
- if ( !strTrans.empty() )
- memText.AddLine(strTrans);
+ s = e;
+ }
// Finally we can parse it all.
Parse(memText, true /* local */);
SetRootPath();
+ ResetDirty();
}
#endif // wxUSE_STREAMS
void wxFileConfig::CleanUp()
{
- delete m_pRootGroup;
+ delete m_pRootGroup;
- wxFileConfigLineList *pCur = m_linesHead;
- while ( pCur != NULL ) {
- wxFileConfigLineList *pNext = pCur->Next();
- delete pCur;
- pCur = pNext;
- }
+ wxFileConfigLineList *pCur = m_linesHead;
+ while ( pCur != NULL ) {
+ wxFileConfigLineList *pNext = pCur->Next();
+ delete pCur;
+ pCur = pNext;
+ }
}
wxFileConfig::~wxFileConfig()
{
- Flush();
+ Flush();
- CleanUp();
+ CleanUp();
+
+ delete m_conv;
}
// ----------------------------------------------------------------------------
// parse a config file
// ----------------------------------------------------------------------------
-void wxFileConfig::Parse(wxTextBuffer& buffer, bool bLocal)
+void wxFileConfig::Parse(const wxTextBuffer& buffer, bool bLocal)
{
- const wxChar *pStart;
- const wxChar *pEnd;
- wxString strLine;
size_t nLineCount = buffer.GetLineCount();
for ( size_t n = 0; n < nLineCount; n++ )
{
- strLine = buffer[n];
+ wxString strLine = buffer[n];
+ // FIXME-UTF8: rewrite using iterators, without this buffer
+ wxWxCharBuffer buf(strLine.c_str());
+ const wxChar *pStart;
+ const wxChar *pEnd;
// add the line to linked list
if ( bLocal )
- {
LineListAppend(strLine);
- // let the root group have it start line as well
- if ( !n )
- {
- m_pCurrentGroup->SetLine(m_linesTail);
- }
- }
-
// skip leading spaces
- for ( pStart = strLine; wxIsspace(*pStart); pStart++ )
+ for ( pStart = buf; wxIsspace(*pStart); pStart++ )
;
// skip blank/comment lines
}
}
else { // a key
- const wxChar *pEnd = pStart;
+ pEnd = pStart;
while ( *pEnd && *pEnd != wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
if ( *pEnd == wxT('\\') ) {
// next character may be space or not - still take it because it's
void wxFileConfig::SetRootPath()
{
- m_strPath.Empty();
- m_pCurrentGroup = m_pRootGroup;
+ m_strPath.Empty();
+ m_pCurrentGroup = m_pRootGroup;
}
-void wxFileConfig::SetPath(const wxString& strPath)
+bool
+wxFileConfig::DoSetPath(const wxString& strPath, bool createMissingComponents)
{
- wxArrayString aParts;
+ wxArrayString aParts;
- if ( strPath.empty() ) {
- SetRootPath();
- return;
- }
+ if ( strPath.empty() ) {
+ SetRootPath();
+ return true;
+ }
- if ( strPath[0] == wxCONFIG_PATH_SEPARATOR ) {
- // absolute path
- wxSplitPath(aParts, strPath);
- }
- else {
- // relative path, combine with current one
- wxString strFullPath = m_strPath;
- strFullPath << wxCONFIG_PATH_SEPARATOR << strPath;
- wxSplitPath(aParts, strFullPath);
- }
+ if ( strPath[0] == wxCONFIG_PATH_SEPARATOR ) {
+ // absolute path
+ wxSplitPath(aParts, strPath);
+ }
+ else {
+ // relative path, combine with current one
+ wxString strFullPath = m_strPath;
+ strFullPath << wxCONFIG_PATH_SEPARATOR << strPath;
+ wxSplitPath(aParts, strFullPath);
+ }
- // change current group
- size_t n;
- m_pCurrentGroup = m_pRootGroup;
- for ( n = 0; n < aParts.Count(); n++ ) {
- wxFileConfigGroup *pNextGroup = m_pCurrentGroup->FindSubgroup(aParts[n]);
- if ( pNextGroup == NULL )
- pNextGroup = m_pCurrentGroup->AddSubgroup(aParts[n]);
- m_pCurrentGroup = pNextGroup;
- }
+ // change current group
+ size_t n;
+ m_pCurrentGroup = m_pRootGroup;
+ for ( n = 0; n < aParts.GetCount(); n++ ) {
+ wxFileConfigGroup *pNextGroup = m_pCurrentGroup->FindSubgroup(aParts[n]);
+ if ( pNextGroup == NULL )
+ {
+ if ( !createMissingComponents )
+ return false;
- // recombine path parts in one variable
- m_strPath.Empty();
- for ( n = 0; n < aParts.Count(); n++ ) {
- m_strPath << wxCONFIG_PATH_SEPARATOR << aParts[n];
- }
+ pNextGroup = m_pCurrentGroup->AddSubgroup(aParts[n]);
+ }
+
+ m_pCurrentGroup = pNextGroup;
+ }
+
+ // recombine path parts in one variable
+ m_strPath.Empty();
+ for ( n = 0; n < aParts.GetCount(); n++ ) {
+ m_strPath << wxCONFIG_PATH_SEPARATOR << aParts[n];
+ }
+
+ return true;
+}
+
+void wxFileConfig::SetPath(const wxString& strPath)
+{
+ DoSetPath(strPath, true /* create missing path components */);
}
// ----------------------------------------------------------------------------
bool wxFileConfig::GetFirstGroup(wxString& str, long& lIndex) const
{
- lIndex = 0;
- return GetNextGroup(str, lIndex);
+ lIndex = 0;
+ return GetNextGroup(str, lIndex);
}
bool wxFileConfig::GetNextGroup (wxString& str, long& lIndex) const
{
- if ( size_t(lIndex) < m_pCurrentGroup->Groups().Count() ) {
- str = m_pCurrentGroup->Groups()[(size_t)lIndex++]->Name();
- return true;
- }
- else
- return false;
+ if ( size_t(lIndex) < m_pCurrentGroup->Groups().GetCount() ) {
+ str = m_pCurrentGroup->Groups()[(size_t)lIndex++]->Name();
+ return true;
+ }
+ else
+ return false;
}
bool wxFileConfig::GetFirstEntry(wxString& str, long& lIndex) const
{
- lIndex = 0;
- return GetNextEntry(str, lIndex);
+ lIndex = 0;
+ return GetNextEntry(str, lIndex);
}
bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex) const
{
- if ( size_t(lIndex) < m_pCurrentGroup->Entries().Count() ) {
- str = m_pCurrentGroup->Entries()[(size_t)lIndex++]->Name();
- return true;
- }
- else
- return false;
+ if ( size_t(lIndex) < m_pCurrentGroup->Entries().GetCount() ) {
+ str = m_pCurrentGroup->Entries()[(size_t)lIndex++]->Name();
+ return true;
+ }
+ else
+ return false;
}
size_t wxFileConfig::GetNumberOfEntries(bool bRecursive) const
{
- size_t n = m_pCurrentGroup->Entries().Count();
- if ( bRecursive ) {
- wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
- size_t nSubgroups = m_pCurrentGroup->Groups().Count();
- for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
- CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
- n += GetNumberOfEntries(true);
- CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
+ size_t n = m_pCurrentGroup->Entries().GetCount();
+ if ( bRecursive ) {
+ wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
+ size_t nSubgroups = m_pCurrentGroup->Groups().GetCount();
+ for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
+ CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
+ n += GetNumberOfEntries(true);
+ CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
+ }
}
- }
- return n;
+ return n;
}
size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
{
- size_t n = m_pCurrentGroup->Groups().Count();
- if ( bRecursive ) {
- wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
- size_t nSubgroups = m_pCurrentGroup->Groups().Count();
- for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
- CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
- n += GetNumberOfGroups(true);
- CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
+ size_t n = m_pCurrentGroup->Groups().GetCount();
+ if ( bRecursive ) {
+ wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
+ size_t nSubgroups = m_pCurrentGroup->Groups().GetCount();
+ for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
+ CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
+ n += GetNumberOfGroups(true);
+ CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
+ }
}
- }
- return n;
+ return n;
}
// ----------------------------------------------------------------------------
bool wxFileConfig::HasGroup(const wxString& strName) const
{
- wxConfigPathChanger path(this, strName);
+ // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
+ // but there is no group with empty name so treat this separately
+ if ( strName.empty() )
+ return false;
- wxFileConfigGroup *pGroup = m_pCurrentGroup->FindSubgroup(path.Name());
- return pGroup != NULL;
+ const wxString pathOld = GetPath();
+
+ wxFileConfig *self = const_cast<wxFileConfig *>(this);
+ const bool
+ rc = self->DoSetPath(strName, false /* don't create missing components */);
+
+ self->SetPath(pathOld);
+
+ return rc;
}
-bool wxFileConfig::HasEntry(const wxString& strName) const
+bool wxFileConfig::HasEntry(const wxString& entry) const
{
- wxConfigPathChanger path(this, strName);
+ // path is the part before the last "/"
+ wxString path = entry.BeforeLast(wxCONFIG_PATH_SEPARATOR);
+
+ // except in the special case of "/keyname" when there is nothing before "/"
+ if ( path.empty() && *entry.c_str() == wxCONFIG_PATH_SEPARATOR )
+ {
+ path = wxCONFIG_PATH_SEPARATOR;
+ }
+
+ // change to the path of the entry if necessary and remember the old path
+ // to restore it later
+ wxString pathOld;
+ wxFileConfig * const self = const_cast<wxFileConfig *>(this);
+ if ( !path.empty() )
+ {
+ pathOld = GetPath();
+ if ( pathOld.empty() )
+ pathOld = wxCONFIG_PATH_SEPARATOR;
+
+ if ( !self->DoSetPath(path, false /* don't create if doesn't exist */) )
+ {
+ return false;
+ }
+ }
+
+ // check if the entry exists in this group
+ const bool exists = m_pCurrentGroup->FindEntry(
+ entry.AfterLast(wxCONFIG_PATH_SEPARATOR)) != NULL;
- wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
- return pEntry != NULL;
+ // restore the old path if we changed it above
+ if ( !pathOld.empty() )
+ {
+ self->SetPath(pathOld);
+ }
+
+ return exists;
}
// ----------------------------------------------------------------------------
bool wxFileConfig::DoReadString(const wxString& key, wxString* pStr) const
{
- wxConfigPathChanger path(this, key);
+ wxConfigPathChanger path(this, key);
- wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
- if (pEntry == NULL) {
- return false;
- }
+ wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
+ if (pEntry == NULL) {
+ return false;
+ }
- *pStr = pEntry->Value();
+ *pStr = pEntry->Value();
- return true;
+ return true;
}
bool wxFileConfig::DoReadLong(const wxString& key, long *pl) const
return str.ToLong(pl);
}
+#if wxUSE_BASE64
+
+bool wxFileConfig::DoReadBinary(const wxString& key, wxMemoryBuffer* buf) const
+{
+ wxCHECK_MSG( buf, false, _T("NULL buffer") );
+
+ wxString str;
+ if ( !Read(key, &str) )
+ return false;
+
+ *buf = wxBase64Decode(str);
+ return true;
+}
+
+#endif // wxUSE_BASE64
+
bool wxFileConfig::DoWriteString(const wxString& key, const wxString& szValue)
{
wxConfigPathChanger path(this, key);
SetDirty();
- // this will add a line for this group if it didn't have it before
-
+ // this will add a line for this group if it didn't have it before (or
+ // do nothing for the root but it's ok as it always exists anyhow)
(void)m_pCurrentGroup->GetGroupLine();
}
else
return Write(key, wxString::Format(_T("%ld"), lValue));
}
+#if wxUSE_BASE64
+
+bool wxFileConfig::DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf)
+{
+ return Write(key, wxBase64Encode(buf));
+}
+
+#endif // wxUSE_BASE64
+
bool wxFileConfig::Flush(bool /* bCurrentOnly */)
{
- if ( !IsDirty() || !m_strLocalFile )
+ if ( !IsDirty() || !m_fnLocalFile.GetFullPath() )
return true;
// set the umask if needed
wxCHANGE_UMASK(m_umask);
- wxTempFile file(m_strLocalFile);
+ wxTempFile file(m_fnLocalFile.GetFullPath());
if ( !file.IsOpened() )
{
}
// write all strings to file
+ wxString filetext;
+ filetext.reserve(4096);
for ( wxFileConfigLineList *p = m_linesHead; p != NULL; p = p->Next() )
{
- wxString line = p->Text();
- line += wxTextFile::GetEOL();
- if ( !file.Write(line, m_conv) )
- {
- wxLogError(_("can't write user configuration file."));
- return false;
- }
+ filetext << p->Text() << wxTextFile::GetEOL();
+ }
+
+ if ( !file.Write(filetext, *m_conv) )
+ {
+ wxLogError(_("can't write user configuration file."));
+ return false;
}
if ( !file.Commit() )
ResetDirty();
-#if defined(__WXMAC__)
- wxFileName(m_strLocalFile).MacSetTypeAndCreator('TEXT', 'ttxt');
+#if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
+ m_fnLocalFile.MacSetTypeAndCreator('TEXT', 'ttxt');
#endif // __WXMAC__
return true;
#if wxUSE_STREAMS
-bool wxFileConfig::Save(wxOutputStream& os, wxMBConv& conv)
+bool wxFileConfig::Save(wxOutputStream& os, const wxMBConv& conv)
{
// save unconditionally, even if not dirty
for ( wxFileConfigLineList *p = m_linesHead; p != NULL; p = p->Next() )
{
wxString line = p->Text();
line += wxTextFile::GetEOL();
- if ( !os.Write(line.mb_str(conv), line.length()) )
+
+ wxCharBuffer buf(line.mb_str(conv));
+ if ( !os.Write(buf, strlen(buf)) )
{
wxLogError(_("Error saving user configuration data."));
bool wxFileConfig::RenameEntry(const wxString& oldName,
const wxString& newName)
{
- wxASSERT_MSG( !wxStrchr(oldName, wxCONFIG_PATH_SEPARATOR),
+ wxASSERT_MSG( oldName.find(wxCONFIG_PATH_SEPARATOR) == wxString::npos,
_T("RenameEntry(): paths are not supported") );
// check that the entry exists
if ( !m_pCurrentGroup->DeleteEntry(path.Name()) )
return false;
+ SetDirty();
+
if ( bGroupIfEmptyAlso && m_pCurrentGroup->IsEmpty() ) {
if ( m_pCurrentGroup != m_pRootGroup ) {
wxFileConfigGroup *pGroup = m_pCurrentGroup;
SetPath(wxT("..")); // changes m_pCurrentGroup!
- if ( m_pCurrentGroup->DeleteSubgroupByName(pGroup->Name()) )
- SetDirty();
+ m_pCurrentGroup->DeleteSubgroupByName(pGroup->Name());
}
//else: never delete the root group
}
bool wxFileConfig::DeleteGroup(const wxString& key)
{
- wxConfigPathChanger path(this, key);
+ wxConfigPathChanger path(this, RemoveTrailingSeparator(key));
if ( !m_pCurrentGroup->DeleteSubgroupByName(path.Name()) )
return false;
+ path.UpdateIfDeleted();
+
SetDirty();
return true;
{
CleanUp();
- if ( !m_strLocalFile.empty() )
+ if ( m_fnLocalFile.IsOk() )
{
- if ( wxFile::Exists(m_strLocalFile) && wxRemove(m_strLocalFile) == -1 )
+ if ( m_fnLocalFile.FileExists() &&
+ !wxRemoveFile(m_fnLocalFile.GetFullPath()) )
{
wxLogSysError(_("can't delete user configuration file '%s'"),
- m_strLocalFile.c_str());
+ m_fnLocalFile.GetFullPath().c_str());
return false;
}
-
- m_strLocalFile =
- m_strGlobalFile = wxEmptyString;
}
Init();
str.c_str() );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" head: %s"),
- ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
+ ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" tail: %s"),
- ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
+ ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+ : wxEmptyString) );
wxFileConfigLineList *pLine = new wxFileConfigLineList(str);
wxLogTrace( FILECONF_TRACE_MASK,
_T(" head: %s"),
- ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
+ ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" tail: %s"),
- ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
+ ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+ : wxEmptyString) );
return m_linesTail;
}
wxLogTrace( FILECONF_TRACE_MASK,
_T(" ** Inserting Line '%s' after '%s'"),
str.c_str(),
- ((pLine) ? pLine->Text().c_str() : wxEmptyString) );
+ ((pLine) ? (const wxChar*)pLine->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" head: %s"),
- ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
+ ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" tail: %s"),
- ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
+ ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+ : wxEmptyString) );
if ( pLine == m_linesTail )
return LineListAppend(str);
wxLogTrace( FILECONF_TRACE_MASK,
_T(" head: %s"),
- ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
+ ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" tail: %s"),
- ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
+ ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+ : wxEmptyString) );
return pNewLine;
}
pLine->Text().c_str() );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" head: %s"),
- ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
+ ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" tail: %s"),
- ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
+ ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+ : wxEmptyString) );
wxFileConfigLineList *pPrev = pLine->Prev(),
*pNext = pLine->Next();
wxLogTrace( FILECONF_TRACE_MASK,
_T(" head: %s"),
- ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
+ ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+ : wxEmptyString) );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" tail: %s"),
- ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
+ ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+ : wxEmptyString) );
delete pLine;
}
wxFileConfigGroup::~wxFileConfigGroup()
{
// entries
- size_t n, nCount = m_aEntries.Count();
+ size_t n, nCount = m_aEntries.GetCount();
for ( n = 0; n < nCount; n++ )
delete m_aEntries[n];
// subgroups
- nCount = m_aSubgroups.Count();
+ nCount = m_aSubgroups.GetCount();
for ( n = 0; n < nCount; n++ )
delete m_aSubgroups[n];
}
void wxFileConfigGroup::SetLine(wxFileConfigLineList *pLine)
{
- wxASSERT( m_pLine == 0 ); // shouldn't be called twice
+ // for a normal (i.e. not root) group this method shouldn't be called twice
+ // unless we are resetting the line
+ wxASSERT_MSG( !m_pParent || !m_pLine || !pLine,
+ _T("changing line for a non-root group?") );
+
m_pLine = pLine;
}
// also update all subgroups as they have this groups name in their lines
- const size_t nCount = m_aSubgroups.Count();
+ const size_t nCount = m_aSubgroups.GetCount();
for ( size_t n = 0; n < nCount; n++ )
{
m_aSubgroups[n]->UpdateGroupAndSubgroupsLines();
{
wxCHECK_RET( m_pParent, _T("the root group can't be renamed") );
+ if ( newName == m_strName )
+ return;
+
+ // we need to remove the group from the parent and it back under the new
+ // name to keep the parents array of subgroups alphabetically sorted
+ m_pParent->m_aSubgroups.Remove(this);
+
m_strName = newName;
+ m_pParent->m_aSubgroups.Add(this);
+
// update the group lines recursively
UpdateGroupAndSubgroupsLines();
}
// use binary search because the array is sorted
wxFileConfigEntry *
-wxFileConfigGroup::FindEntry(const wxChar *szName) const
+wxFileConfigGroup::FindEntry(const wxString& name) const
{
size_t i,
lo = 0,
- hi = m_aEntries.Count();
+ hi = m_aEntries.GetCount();
int res;
wxFileConfigEntry *pEntry;
pEntry = m_aEntries[i];
#if wxCONFIG_CASE_SENSITIVE
- res = wxStrcmp(pEntry->Name(), szName);
+ res = pEntry->Name().compare(name);
#else
- res = wxStricmp(pEntry->Name(), szName);
+ res = pEntry->Name().CmpNoCase(name);
#endif
if ( res > 0 )
}
wxFileConfigGroup *
-wxFileConfigGroup::FindSubgroup(const wxChar *szName) const
+wxFileConfigGroup::FindSubgroup(const wxString& name) const
{
size_t i,
lo = 0,
- hi = m_aSubgroups.Count();
+ hi = m_aSubgroups.GetCount();
int res;
wxFileConfigGroup *pGroup;
pGroup = m_aSubgroups[i];
#if wxCONFIG_CASE_SENSITIVE
- res = wxStrcmp(pGroup->Name(), szName);
+ res = pGroup->Name().compare(name);
#else
- res = wxStricmp(pGroup->Name(), szName);
+ res = pGroup->Name().CmpNoCase(name);
#endif
if ( res > 0 )
delete several of them.
*/
-bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar *szName)
+bool wxFileConfigGroup::DeleteSubgroupByName(const wxString& name)
{
- wxFileConfigGroup * const pGroup = FindSubgroup(szName);
+ wxFileConfigGroup * const pGroup = FindSubgroup(name);
return pGroup ? DeleteSubgroup(pGroup) : false;
}
wxLogTrace( FILECONF_TRACE_MASK,
_T(" (m_pLine) = prev: %p, this %p, next %p"),
- ((m_pLine) ? m_pLine->Prev() : 0),
- m_pLine,
- ((m_pLine) ? m_pLine->Next() : 0) );
+ m_pLine ? static_cast<void*>(m_pLine->Prev()) : 0,
+ static_cast<void*>(m_pLine),
+ m_pLine ? static_cast<void*>(m_pLine->Next()) : 0 );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" text: '%s'"),
- ((m_pLine) ? m_pLine->Text().c_str() : wxEmptyString) );
+ m_pLine ? (const wxChar*)m_pLine->Text().c_str()
+ : wxEmptyString );
// delete all entries...
- size_t nCount = pGroup->m_aEntries.Count();
+ size_t nCount = pGroup->m_aEntries.GetCount();
wxLogTrace(FILECONF_TRACE_MASK,
_T("Removing %lu entries"), (unsigned long)nCount );
}
// ...and subgroups of this subgroup
- nCount = pGroup->m_aSubgroups.Count();
+ nCount = pGroup->m_aSubgroups.GetCount();
wxLogTrace( FILECONF_TRACE_MASK,
_T("Removing %lu subgroups"), (unsigned long)nCount );
wxLogTrace( FILECONF_TRACE_MASK,
_T(" Removing from group '%s' : '%s'"),
Name().c_str(),
- ((m_pLine) ? m_pLine->Text().c_str() : wxEmptyString) );
+ ((m_pLine) ? (const wxChar*)m_pLine->Text().c_str()
+ : wxEmptyString) );
// notice that we may do this test inside the previous "if"
// because the last entry's line is surely !NULL
// our last entry is being deleted, so find the last one which
// stays by going back until we find a subgroup or reach the
// group line
- const size_t nSubgroups = m_aSubgroups.Count();
+ const size_t nSubgroups = m_aSubgroups.GetCount();
m_pLastGroup = NULL;
for ( wxFileConfigLineList *pl = pLine->Prev();
- pl && pl != m_pLine && !m_pLastGroup;
+ pl && !m_pLastGroup;
pl = pl->Prev() )
{
// does this line belong to our subgroup?
break;
}
}
+
+ if ( pl == m_pLine )
+ break;
}
}
return true;
}
-bool wxFileConfigGroup::DeleteEntry(const wxChar *szName)
+bool wxFileConfigGroup::DeleteEntry(const wxString& name)
{
- wxFileConfigEntry *pEntry = FindEntry(szName);
+ wxFileConfigEntry *pEntry = FindEntry(name);
if ( !pEntry )
{
// entry doesn't exist, nothing to do
// our last entry is being deleted - find the last one which stays
wxASSERT( m_pLine != NULL ); // if we have an entry with !NULL pLine...
- // go back until we find another entry or reach the group's line
+ // find the previous entry (if any)
wxFileConfigEntry *pNewLast = NULL;
- size_t n, nEntries = m_aEntries.Count();
- wxFileConfigLineList *pl;
- for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() ) {
- // is it our subgroup?
- for ( n = 0; (pNewLast == NULL) && (n < nEntries); n++ ) {
- if ( m_aEntries[n]->GetLine() == m_pLine )
- pNewLast = m_aEntries[n];
- }
-
- if ( pNewLast != NULL ) // found?
+ const wxFileConfigLineList * const
+ pNewLastLine = m_pLastEntry->GetLine()->Prev();
+ const size_t nEntries = m_aEntries.GetCount();
+ for ( size_t n = 0; n < nEntries; n++ ) {
+ if ( m_aEntries[n]->GetLine() == pNewLastLine ) {
+ pNewLast = m_aEntries[n];
break;
+ }
}
- if ( pl == m_pLine ) {
- wxASSERT( !pNewLast ); // how comes it has the same line as we?
-
- // we've reached the group line without finding any subgroups
- m_pLastEntry = NULL;
- }
- else
- m_pLastEntry = pNewLast;
+ // pNewLast can be NULL here -- it's ok and can happen if we have no
+ // entries left
+ m_pLastEntry = pNewLast;
}
m_pConfig->LineListRemove(pLine);
}
else // this entry didn't exist in the local file
{
- // add a new line to the file: note the hack for the root group
- // which is special in that it doesn't have its own group line
- // (something like "[/]") and so the line we get for it may be not
- // its line at all if it doesn't have any entries
- //
- // this is definitely not the right place to fix it but changing
- // the root group to have NULL m_pLine will probably break too
- // much stuff elsewhere so I don't dare to do it...
+ // add a new line to the file: note that line returned by
+ // GetLastEntryLine() may be NULL if we're in the root group and it
+ // doesn't have any entries yet, but this is ok as passing NULL
+ // line to LineListInsert() means to prepend new line to the list
wxFileConfigLineList *line = Group()->GetLastEntryLine();
- if ( !Group()->Parent() && line == Group()->GetGroupLine() )
- {
- // prepend the first root group entry to the head of the list
- line = NULL;
- }
m_pLine = Group()->Config()->LineListInsert(strLine, line);
Group()->SetLastEntry(this);
int CompareEntries(wxFileConfigEntry *p1, wxFileConfigEntry *p2)
{
- #if wxCONFIG_CASE_SENSITIVE
- return wxStrcmp(p1->Name(), p2->Name());
- #else
- return wxStricmp(p1->Name(), p2->Name());
- #endif
+#if wxCONFIG_CASE_SENSITIVE
+ return p1->Name().compare(p2->Name());
+#else
+ return p1->Name().CmpNoCase(p2->Name());
+#endif
}
int CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2)
{
- #if wxCONFIG_CASE_SENSITIVE
- return wxStrcmp(p1->Name(), p2->Name());
- #else
- return wxStricmp(p1->Name(), p2->Name());
- #endif
+#if wxCONFIG_CASE_SENSITIVE
+ return p1->Name().compare(p2->Name());
+#else
+ return p1->Name().CmpNoCase(p2->Name());
+#endif
}
// ----------------------------------------------------------------------------
// undo FilterOutValue
static wxString FilterInValue(const wxString& str)
{
- wxString strResult;
- strResult.Alloc(str.Len());
+ wxString strResult;
+ if ( str.empty() )
+ return strResult;
- bool bQuoted = !str.empty() && str[0] == '"';
+ strResult.reserve(str.length());
- for ( size_t n = bQuoted ? 1 : 0; n < str.Len(); n++ ) {
- if ( str[n] == wxT('\\') ) {
- switch ( str[++n] ) {
- case wxT('n'):
- strResult += wxT('\n');
- break;
+ wxString::const_iterator i = str.begin();
+ const bool bQuoted = *i == '"';
+ if ( bQuoted )
+ ++i;
- case wxT('r'):
- strResult += wxT('\r');
- break;
+ for ( const wxString::const_iterator end = str.end(); i != end; ++i )
+ {
+ if ( *i == wxT('\\') )
+ {
+ if ( ++i == end )
+ {
+ wxLogWarning(_("trailing backslash ignored in '%s'"), str.c_str());
+ break;
+ }
- case wxT('t'):
- strResult += wxT('\t');
- break;
+ switch ( (*i).GetValue() )
+ {
+ case wxT('n'):
+ strResult += wxT('\n');
+ break;
- case wxT('\\'):
- strResult += wxT('\\');
- break;
+ case wxT('r'):
+ strResult += wxT('\r');
+ break;
- case wxT('"'):
- strResult += wxT('"');
- break;
- }
- }
- else {
- if ( str[n] != wxT('"') || !bQuoted )
- strResult += str[n];
- else if ( n != str.Len() - 1 ) {
- wxLogWarning(_("unexpected \" at position %d in '%s'."),
- n, str.c_str());
- }
- //else: it's the last quote of a quoted string, ok
+ case wxT('t'):
+ strResult += wxT('\t');
+ break;
+
+ case wxT('\\'):
+ strResult += wxT('\\');
+ break;
+
+ case wxT('"'):
+ strResult += wxT('"');
+ break;
+ }
+ }
+ else // not a backslash
+ {
+ if ( *i != wxT('"') || !bQuoted )
+ {
+ strResult += *i;
+ }
+ else if ( i != end - 1 )
+ {
+ wxLogWarning(_("unexpected \" at position %d in '%s'."),
+ i - str.begin(), str.c_str());
+ }
+ //else: it's the last quote of a quoted string, ok
+ }
}
- }
- return strResult;
+ return strResult;
}
// quote the string before writing it to file
wxChar c;
for ( size_t n = 0; n < str.Len(); n++ ) {
- switch ( str[n] ) {
+ switch ( str[n].GetValue() ) {
case wxT('\n'):
c = wxT('n');
break;
strResult.Alloc(str.Len());
for ( const wxChar *pc = str.c_str(); *pc != '\0'; pc++ ) {
- if ( *pc == wxT('\\') )
- pc++;
+ if ( *pc == wxT('\\') ) {
+ // we need to test it here or we'd skip past the NUL in the loop line
+ if ( *++pc == _T('\0') )
+ break;
+ }
strResult += *pc;
}
}
#endif // wxUSE_CONFIG
-