]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/fileconf.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / fileconf.cpp
index 79394e5f9b4e915bfd35aae7242b0ccbc64da589..4b632703207d402ca81e7a81a9c48931aa394fc7 100644 (file)
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
-// 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$
 // 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>
 //                       Ballueder@usa.net     <zeitlin@dptmaths.ens-cachan.fr>
-// Licence:     wxWindows license
+// Licence:     wxWindows licence
 ///////////////////////////////////////////////////////////////////////////////
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#ifdef __GNUG__
-#pragma implementation "fileconf.h"
-#endif
-
-// ============================================================================
-// declarations
-// ============================================================================
-
 // ----------------------------------------------------------------------------
 // headers
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // headers
 // ----------------------------------------------------------------------------
+
+// For compilers that support precompilation, includes "wx.h".
 #include  "wx/wxprec.h"
 
 #ifdef    __BORLANDC__
 #include  "wx/wxprec.h"
 
 #ifdef    __BORLANDC__
-  #pragma hdrstop
+    #pragma hdrstop
 #endif  //__BORLANDC__
 
 #endif  //__BORLANDC__
 
+#if wxUSE_CONFIG && wxUSE_FILECONFIG
+
 #ifndef   WX_PRECOMP
 #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
 
 #endif  //WX_PRECOMP
 
-#include  "wx/app.h"
-#include  "wx/dynarray.h"
 #include  "wx/file.h"
 #include  "wx/file.h"
-#include  "wx/log.h"
 #include  "wx/textfile.h"
 #include  "wx/textfile.h"
+#include  "wx/memtext.h"
 #include  "wx/config.h"
 #include  "wx/fileconf.h"
 #include  "wx/config.h"
 #include  "wx/fileconf.h"
+#include  "wx/filefn.h"
 
 
-#include  "wx/utils.h"    // for wxGetHomeDir
+#include "wx/base64.h"
 
 
-// _WINDOWS_ is defined when windows.h is included,
-// __WXMSW__ is defined for MS Windows compilation
-#if       defined(__WXMSW__) && !defined(_WINDOWS_)
-  #include  <windows.h>
+#include  "wx/stdpaths.h"
+
+#if defined(__WINDOWS__)
+    #include "wx/msw/private.h"
 #endif  //windows.h
 #endif  //windows.h
+#if defined(__WXPM__)
+    #define INCL_DOS
+    #include <os2.h>
+#endif
 
 #include  <stdlib.h>
 #include  <ctype.h>
 
 
 #include  <stdlib.h>
 #include  <ctype.h>
 
-// ----------------------------------------------------------------------------
-// macros
-// ----------------------------------------------------------------------------
-#define CONST_CAST ((wxFileConfig *)this)->
-
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
 // constants
 // ----------------------------------------------------------------------------
+
 #ifndef MAX_PATH
 #ifndef MAX_PATH
-  #define MAX_PATH 512
+    #define MAX_PATH 512
 #endif
 
 #endif
 
+#define FILECONF_TRACE_MASK wxT("fileconf")
+
 // ----------------------------------------------------------------------------
 // global functions declarations
 // ----------------------------------------------------------------------------
 
 // ----------------------------------------------------------------------------
 // global functions declarations
 // ----------------------------------------------------------------------------
 
-// is 'c' a valid character in group name?
-// NB: wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR must be valid chars,
-//     but _not_ ']' (group name delimiter)
-inline bool IsValid(char c) { return isalnum(c) || strchr("@_/-!.*%", c); }
-
 // compare functions for sorting the arrays
 // compare functions for sorting the arrays
-static int CompareEntries(wxFileConfig::ConfigEntry *p1,
-                          wxFileConfig::ConfigEntry *p2);
-static int CompareGroups(wxFileConfig::ConfigGroup *p1,
-                         wxFileConfig::ConfigGroup *p2);
+static int LINKAGEMODE CompareEntries(wxFileConfigEntry *p1, wxFileConfigEntry *p2);
+static int LINKAGEMODE CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2);
 
 // filter strings
 
 // filter strings
-static wxString FilterIn(const wxString& str);
-static wxString FilterOut(const wxString& str);
+static wxString FilterInValue(const wxString& str);
+static wxString FilterOutValue(const wxString& str);
+
+static wxString FilterInEntryName(const wxString& str);
+static wxString FilterOutEntryName(const wxString& str);
+
+// get the name to use in wxFileConfig ctor
+static wxString GetAppName(const wxString& appname);
 
 // ============================================================================
 
 // ============================================================================
-// implementation
+// private classes
 // ============================================================================
 
 // ----------------------------------------------------------------------------
 // ============================================================================
 
 // ----------------------------------------------------------------------------
-// static functions
+// "template" array types
 // ----------------------------------------------------------------------------
 // ----------------------------------------------------------------------------
-wxString wxFileConfig::GetGlobalDir()
+
+#ifdef WXMAKINGDLL_BASE
+    WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry *, ArrayEntries,
+                                         WXDLLIMPEXP_BASE);
+    WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup *, ArrayGroups,
+                                         WXDLLIMPEXP_BASE);
+#else
+    WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry *, ArrayEntries);
+    WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup *, ArrayGroups);
+#endif
+
+// ----------------------------------------------------------------------------
+// wxFileConfigLineList
+// ----------------------------------------------------------------------------
+
+// we store all lines of the local config file as a linked list in memory
+class wxFileConfigLineList
 {
 {
-  wxString strDir;
+public:
+  void SetNext(wxFileConfigLineList *pNext)  { m_pNext = pNext; }
+  void SetPrev(wxFileConfigLineList *pPrev)  { m_pPrev = pPrev; }
 
 
-  #ifdef __UNIX__
-    strDir = "/etc/";
-  #elif defined(__WXSTUBS__)
-    wxASSERT_MSG( FALSE, "TODO" ) ;
-  #else // Windows
-    char szWinDir[MAX_PATH];
-    ::GetWindowsDirectory(szWinDir, MAX_PATH);
+  // ctor
+  wxFileConfigLineList(const wxString& str,
+                       wxFileConfigLineList *pNext = NULL) : m_strLine(str)
+    { SetNext(pNext); SetPrev(NULL); }
 
 
-    strDir = szWinDir;
-    strDir << '\\';
-  #endif // Unix/Windows
+  // next/prev nodes in the linked list
+  wxFileConfigLineList *Next() const { return m_pNext;  }
+  wxFileConfigLineList *Prev() const { return m_pPrev;  }
 
 
-  return strDir;
-}
+  // get/change lines text
+  void SetText(const wxString& str) { m_strLine = str;  }
+  const wxString& Text() const { return m_strLine; }
 
 
-wxString wxFileConfig::GetLocalDir()
+private:
+  wxString  m_strLine;                  // line contents
+  wxFileConfigLineList *m_pNext,        // next node
+                       *m_pPrev;        // previous one
+
+    wxDECLARE_NO_COPY_CLASS(wxFileConfigLineList);
+};
+
+// ----------------------------------------------------------------------------
+// wxFileConfigEntry: a name/value pair
+// ----------------------------------------------------------------------------
+
+class wxFileConfigEntry
 {
 {
-  wxString strDir;
-  
-  wxGetHomeDir(&strDir);
-  
-#ifdef  __UNIX__
-  if (strDir.Last() != '/') strDir << '/';
-#else
-  if (strDir.Last() != '\\') strDir << '\\';
-#endif
+private:
+  wxFileConfigGroup *m_pParent; // group that contains us
+
+  wxString      m_strName,      // entry name
+                m_strValue;     //       value
+  bool          m_bImmutable:1, // can be overridden locally?
+                m_bHasValue:1;  // set after first call to SetValue()
+
+  int           m_nLine;        // used if m_pLine == NULL only
+
+  // pointer to our line in the linked list or NULL if it was found in global
+  // file (which we don't modify)
+  wxFileConfigLineList *m_pLine;
+
+public:
+  wxFileConfigEntry(wxFileConfigGroup *pParent,
+                    const wxString& strName, int nLine);
+
+  // simple accessors
+  const wxString& Name()        const { return m_strName;    }
+  const wxString& Value()       const { return m_strValue;   }
+  wxFileConfigGroup *Group()    const { return m_pParent;    }
+  bool            IsImmutable() const { return m_bImmutable; }
+  bool            IsLocal()     const { return m_pLine != 0; }
+  int             Line()        const { return m_nLine;      }
+  wxFileConfigLineList *
+                  GetLine()     const { return m_pLine;      }
+
+  // modify entry attributes
+  void SetValue(const wxString& strValue, bool bUser = true);
+  void SetLine(wxFileConfigLineList *pLine);
+
+    wxDECLARE_NO_COPY_CLASS(wxFileConfigEntry);
+};
+
+// ----------------------------------------------------------------------------
+// wxFileConfigGroup: container of entries and other groups
+// ----------------------------------------------------------------------------
+
+class wxFileConfigGroup
+{
+private:
+  wxFileConfig *m_pConfig;          // config object we belong to
+  wxFileConfigGroup  *m_pParent;    // parent group (NULL for root group)
+  ArrayEntries  m_aEntries;         // entries in this group
+  ArrayGroups   m_aSubgroups;       // subgroups
+  wxString      m_strName;          // group's name
+  wxFileConfigLineList *m_pLine;    // pointer to our line in the linked list
+  wxFileConfigEntry *m_pLastEntry;  // last entry/subgroup of this group in the
+  wxFileConfigGroup *m_pLastGroup;  // local file (we insert new ones after it)
+
+  // DeleteSubgroupByName helper
+  bool DeleteSubgroup(wxFileConfigGroup *pGroup);
+
+  // used by Rename()
+  void UpdateGroupAndSubgroupsLines();
+
+public:
+  // ctor
+  wxFileConfigGroup(wxFileConfigGroup *pParent, const wxString& strName, wxFileConfig *);
+
+  // dtor deletes all entries and subgroups also
+  ~wxFileConfigGroup();
+
+  // simple accessors
+  const wxString& Name()    const { return m_strName; }
+  wxFileConfigGroup    *Parent()  const { return m_pParent; }
+  wxFileConfig   *Config()  const { return m_pConfig; }
+
+  const ArrayEntries& Entries() const { return m_aEntries;   }
+  const ArrayGroups&  Groups()  const { return m_aSubgroups; }
+  bool  IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
+
+  // find entry/subgroup (NULL if not found)
+  wxFileConfigGroup *FindSubgroup(const wxString& name) const;
+  wxFileConfigEntry *FindEntry   (const wxString& name) const;
+
+  // delete entry/subgroup, return false if doesn't exist
+  bool DeleteSubgroupByName(const wxString& name);
+  bool DeleteEntry(const wxString& name);
 
 
-  return strDir;
+  // create new entry/subgroup returning pointer to newly created element
+  wxFileConfigGroup *AddSubgroup(const wxString& strName);
+  wxFileConfigEntry *AddEntry   (const wxString& strName, int nLine = wxNOT_FOUND);
+
+  void SetLine(wxFileConfigLineList *pLine);
+
+  // rename: no checks are done to ensure that the name is unique!
+  void Rename(const wxString& newName);
+
+  //
+  wxString GetFullName() const;
+
+  // 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
+
+  // called by entries/subgroups when they're created/deleted
+  void SetLastEntry(wxFileConfigEntry *pEntry);
+  void SetLastGroup(wxFileConfigGroup *pGroup)
+    { m_pLastGroup = pGroup; }
+
+  wxDECLARE_NO_COPY_CLASS(wxFileConfigGroup);
+};
+
+// ============================================================================
+// implementation
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// static functions
+// ----------------------------------------------------------------------------
+
+// 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() )
+    {
+#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
+    }
 }
 
 }
 
-wxString wxFileConfig::GetGlobalFileName(const char *szFile)
+wxString wxFileConfig::GetGlobalDir()
 {
 {
-  wxString str = GetGlobalDir();
-  str << szFile;
+    return wxStandardPaths::Get().GetConfigDir();
+}
 
 
-  if ( strchr(szFile, '.') == NULL )
-  #ifdef  __UNIX__
-    str << ".conf";
-  #else   // Windows
-    str << ".ini";
-  #endif  // UNIX/Win
+wxString wxFileConfig::GetLocalDir(int style)
+{
+    wxUnusedVar(style);
 
 
-  return str;
+    wxStandardPathsBase& stdp = wxStandardPaths::Get();
+
+    // 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 char *szFile)
+wxFileName wxFileConfig::GetGlobalFile(const wxString& szFile)
 {
 {
-  wxString str = GetLocalDir();
+    wxFileName fn(GetGlobalDir(), szFile);
+
+    AddConfFileExtIfNeeded(fn);
 
 
-  #ifdef  __UNIX__
-    str << '.';
-  #endif
+    return fn;
+}
 
 
-  str << szFile;
+wxFileName wxFileConfig::GetLocalFile(const wxString& szFile, int style)
+{
+    wxFileName fn(GetLocalDir(style), szFile);
+
+#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);
+    }
 
 
-  #ifdef __WXMSW__
-    if ( strchr(szFile, '.') == NULL )
-      str << ".ini";
-  #endif
-  
-  return str;
+    return fn;
 }
 
 // ----------------------------------------------------------------------------
 // ctor
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
 // ctor
 // ----------------------------------------------------------------------------
+IMPLEMENT_ABSTRACT_CLASS(wxFileConfig, wxConfigBase)
 
 void wxFileConfig::Init()
 {
 
 void wxFileConfig::Init()
 {
-  m_pCurrentGroup =
-  m_pRootGroup    = new ConfigGroup(NULL, "", this);
+    m_pCurrentGroup =
+    m_pRootGroup    = new wxFileConfigGroup(NULL, wxEmptyString, this);
 
 
-  m_linesHead =
-  m_linesTail = NULL;
+    m_linesHead =
+    m_linesTail = NULL;
 
 
-  // it's not an error if (one of the) file(s) doesn't exist
+    // It's not an error if (one of the) file(s) doesn't exist.
 
 
-  // parse the global file
-  if ( !m_strGlobalFile.IsEmpty() && wxFile::Exists(m_strGlobalFile) ) {
-    wxTextFile fileGlobal(m_strGlobalFile);
+    // parse the global file
+    if ( m_fnGlobalFile.IsOk() && m_fnGlobalFile.FileExists() )
+    {
+        wxTextFile fileGlobal(m_fnGlobalFile.GetFullPath());
 
 
-    if ( fileGlobal.Open() ) {
-      Parse(fileGlobal, FALSE /* global */);
-      SetRootPath();
+        if ( fileGlobal.Open(*m_conv/*ignored in ANSI build*/) )
+        {
+            Parse(fileGlobal, false /* global */);
+            SetRootPath();
+        }
+        else
+        {
+            wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile.GetFullPath().c_str());
+        }
     }
     }
-    else
-      wxLogWarning(_("can't open global configuration file '%s'."),
-                   m_strGlobalFile.c_str());
-  }
 
 
-  // parse the local file
-  if ( !m_strLocalFile.IsEmpty() && wxFile::Exists(m_strLocalFile) ) {
-    wxTextFile fileLocal(m_strLocalFile);
-    if ( fileLocal.Open() ) {
-      Parse(fileLocal, TRUE /* local */);
-      SetRootPath();
+    // parse the local file
+    if ( m_fnLocalFile.IsOk() && m_fnLocalFile.FileExists() )
+    {
+        wxTextFile fileLocal(m_fnLocalFile.GetFullPath());
+        if ( fileLocal.Open(*m_conv/*ignored in ANSI build*/) )
+        {
+            Parse(fileLocal, true /* local */);
+            SetRootPath();
+        }
+        else
+        {
+            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();
+            }
+        }
     }
     }
-    else
-      wxLogWarning(_("can't open user configuration file '%s'."),
-                   m_strLocalFile.c_str());
-  }
+
+    m_isDirty = false;
 }
 
 // constructor supports creation of wxFileConfig objects of any type
 wxFileConfig::wxFileConfig(const wxString& appName, const wxString& vendorName,
                            const wxString& strLocal, const wxString& strGlobal,
 }
 
 // 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)
-            : wxConfigBase(appName, vendorName, strLocal, strGlobal, style),
-              m_strLocalFile(strLocal), m_strGlobalFile(strGlobal)
+                           long style,
+                           const wxMBConv& conv)
+            : wxConfigBase(::GetAppName(appName), vendorName,
+                           strLocal, strGlobal,
+                           style),
+              m_fnLocalFile(strLocal),
+              m_fnGlobalFile(strGlobal),
+              m_conv(conv.Clone())
 {
 {
-  // Make up an application name if not supplied
-  if (appName.IsEmpty() && wxTheApp)
-  {
-    SetAppName(wxTheApp->GetAppName());
-  }
+    // Make up names for files if empty
+    if ( !m_fnLocalFile.IsOk() && (style & wxCONFIG_USE_LOCAL_FILE) )
+        m_fnLocalFile = GetLocalFile(GetAppName(), style);
+
+    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_fnLocalFile.IsOk() )
+        SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
+
+    if ( m_fnGlobalFile.IsOk() )
+        SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE);
+
+    // if the path is not absolute, prepend the standard directory to it
+    // unless explicitly asked not to
+    if ( !(style & wxCONFIG_USE_RELATIVE_PATH) )
+    {
+        if ( m_fnLocalFile.IsOk() )
+            m_fnLocalFile.MakeAbsolute(GetLocalDir(style));
+
+        if ( m_fnGlobalFile.IsOk() )
+            m_fnGlobalFile.MakeAbsolute(GetGlobalDir());
+    }
 
 
-  // Make up names for files if empty
-  if ( m_strLocalFile.IsEmpty() && (style & wxCONFIG_USE_LOCAL_FILE) )
-  {
-    m_strLocalFile = GetLocalFileName(GetAppName());
-  }
+    SetUmask(-1);
 
 
-  if ( m_strGlobalFile.IsEmpty() && (style & wxCONFIG_USE_GLOBAL_FILE) )
-  {
-    m_strGlobalFile = GetGlobalFileName(GetAppName());
-  }
+    Init();
+}
+
+#if wxUSE_STREAMS
 
 
-  // Check if styles are not supplied, but filenames are, in which case
-  // add the correct styles.
-  if ( !m_strLocalFile.IsEmpty() )
+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);
 
     SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
 
-  if ( !m_strGlobalFile.IsEmpty() )
-    SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE);
+    m_pCurrentGroup =
+    m_pRootGroup    = new wxFileConfigGroup(NULL, wxEmptyString, this);
 
 
-  // if the path is not absolute, prepend the standard directory to it
-  if ( !m_strLocalFile.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile) )
-  {
-    wxString strLocal = m_strLocalFile;
-    m_strLocalFile = GetLocalDir();
-    m_strLocalFile << strLocal;
-  }
+    m_linesHead =
+    m_linesTail = NULL;
 
 
-  if ( !m_strGlobalFile.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile) )
-  {
-    wxString strGlobal = m_strGlobalFile;
-    m_strGlobalFile = GetGlobalDir();
-    m_strGlobalFile << strGlobal;
-  }
+    // read the entire stream contents in memory
+    wxWxCharBuffer cbuf;
+    static const size_t chunkLen = 1024;
 
 
-  Init();
+    wxMemoryBuffer buf(chunkLen);
+    do
+    {
+        inStream.Read(buf.GetAppendBuf(chunkLen), chunkLen);
+        buf.UngetAppendBuf(inStream.LastRead());
+
+        const wxStreamError err = inStream.GetLastError();
+
+        if ( err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF )
+        {
+            wxLogError(_("Error reading config options."));
+            break;
+        }
+    }
+    while ( !inStream.Eof() );
+
+#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(), buf.GetDataLen());
+#endif // wxUSE_UNICODE/!wxUSE_UNICODE
+
+    // parse the input contents if there is anything to parse
+    if ( cbuf )
+    {
+        // now break it into lines
+        wxMemoryText memText;
+        for ( const wxChar *s = cbuf; ; ++s )
+        {
+            const wxChar *e = s;
+            while ( *e != '\0' && *e != '\n' && *e != '\r' )
+                ++e;
+
+            // notice that we throw away the original EOL kind here, maybe we
+            // should preserve it?
+            if ( e != s )
+                memText.AddLine(wxString(s, e));
+
+            if ( *e == '\0' )
+                break;
+
+            // skip the second EOL byte if it's a DOS one
+            if ( *e == '\r' && e[1] == '\n' )
+                ++e;
+
+            s = e;
+        }
+
+        // Finally we can parse it all.
+        Parse(memText, true /* local */);
+    }
+
+    SetRootPath();
+    ResetDirty();
 }
 
 }
 
+#endif // wxUSE_STREAMS
+
 void wxFileConfig::CleanUp()
 {
 void wxFileConfig::CleanUp()
 {
-  delete m_pRootGroup;
+    delete m_pRootGroup;
 
 
-  LineList *pCur = m_linesHead;
-  while ( pCur != NULL ) {
-    LineList *pNext = pCur->Next();
-    delete pCur;
-    pCur = pNext;
-  }
+    wxFileConfigLineList *pCur = m_linesHead;
+    while ( pCur != NULL ) {
+        wxFileConfigLineList *pNext = pCur->Next();
+        delete pCur;
+        pCur = pNext;
+    }
 }
 
 wxFileConfig::~wxFileConfig()
 {
 }
 
 wxFileConfig::~wxFileConfig()
 {
-  Flush();
+    Flush();
 
 
-  CleanUp();
+    CleanUp();
+
+    delete m_conv;
 }
 
 // ----------------------------------------------------------------------------
 // parse a config file
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // parse a config file
 // ----------------------------------------------------------------------------
 
-void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
+void wxFileConfig::Parse(const wxTextBuffer& buffer, bool bLocal)
 {
 {
-  const char *pStart;
-  const char *pEnd;
-  wxString strLine;
 
 
-  size_t nLineCount = file.GetLineCount();
-  for ( size_t n = 0; n < nLineCount; n++ ) {
-    strLine = file[n];
+  size_t nLineCount = buffer.GetLineCount();
+
+  for ( size_t n = 0; n < nLineCount; 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);
 
 
     // add the line to linked list
     if ( bLocal )
       LineListAppend(strLine);
 
+
     // skip leading spaces
     // skip leading spaces
-    for ( pStart = strLine; isspace(*pStart); pStart++ )
+    for ( pStart = buf; wxIsspace(*pStart); pStart++ )
       ;
 
     // skip blank/comment lines
       ;
 
     // skip blank/comment lines
-    if ( *pStart == '\0'|| *pStart == ';' || *pStart == '#' )
+    if ( *pStart == wxT('\0')|| *pStart == wxT(';') || *pStart == wxT('#') )
       continue;
 
       continue;
 
-    if ( *pStart == '[' ) {          // a new group
+    if ( *pStart == wxT('[') ) {          // a new group
       pEnd = pStart;
 
       pEnd = pStart;
 
-      while ( *++pEnd != ']' ) {
-        if ( !IsValid(*pEnd) && *pEnd != ' ' )  // allow spaces in group names
-          break;
+      while ( *++pEnd != wxT(']') ) {
+        if ( *pEnd == wxT('\\') ) {
+            // the next char is escaped, so skip it even if it is ']'
+            pEnd++;
+        }
+
+        if ( *pEnd == wxT('\n') || *pEnd == wxT('\0') ) {
+            // we reached the end of line, break out of the loop
+            break;
+        }
       }
 
       }
 
-      if ( *pEnd != ']' ) {
+      if ( *pEnd != wxT(']') ) {
         wxLogError(_("file '%s': unexpected character %c at line %d."),
         wxLogError(_("file '%s': unexpected character %c at line %d."),
-                   file.GetName(), *pEnd, n + 1);
+                   buffer.GetName(), *pEnd, n + 1);
         continue; // skip this line
       }
 
       // group name here is always considered as abs path
       wxString strGroup;
       pStart++;
         continue; // skip this line
       }
 
       // group name here is always considered as abs path
       wxString strGroup;
       pStart++;
-      strGroup << wxCONFIG_PATH_SEPARATOR << wxString(pStart, pEnd - pStart);
+      strGroup << wxCONFIG_PATH_SEPARATOR
+               << FilterInEntryName(wxString(pStart, pEnd - pStart));
 
       // will create it if doesn't yet exist
       SetPath(strGroup);
 
       if ( bLocal )
 
       // will create it if doesn't yet exist
       SetPath(strGroup);
 
       if ( bLocal )
+      {
+        if ( m_pCurrentGroup->Parent() )
+          m_pCurrentGroup->Parent()->SetLastGroup(m_pCurrentGroup);
         m_pCurrentGroup->SetLine(m_linesTail);
         m_pCurrentGroup->SetLine(m_linesTail);
+      }
 
       // check that there is nothing except comments left on this line
 
       // check that there is nothing except comments left on this line
-      bool bCont = TRUE;
-      while ( *++pEnd != '\0' && bCont ) {
+      bool bCont = true;
+      while ( *++pEnd != wxT('\0') && bCont ) {
         switch ( *pEnd ) {
         switch ( *pEnd ) {
-          case '#':
-          case ';':
-            bCont = FALSE;
+          case wxT('#'):
+          case wxT(';'):
+            bCont = false;
             break;
 
             break;
 
-          case ' ':
-          case '\t':
+          case wxT(' '):
+          case wxT('\t'):
             // ignore whitespace ('\n' impossible here)
             break;
 
           default:
             // ignore whitespace ('\n' impossible here)
             break;
 
           default:
-            wxLogWarning(_("file '%s', line %d: '%s' "
-                           "ignored after group header."),
-                         file.GetName(), n + 1, pEnd);
-            bCont = FALSE;
+            wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
+                         buffer.GetName(), n + 1, pEnd);
+            bCont = false;
         }
       }
     }
     else {                        // a key
         }
       }
     }
     else {                        // a key
-      const char *pEnd = pStart;
-      while ( IsValid(*pEnd) )
+      pEnd = pStart;
+      while ( *pEnd && *pEnd != wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
+        if ( *pEnd == wxT('\\') ) {
+          // next character may be space or not - still take it because it's
+          // quoted (unless there is nothing)
+          pEnd++;
+          if ( !*pEnd ) {
+            // the error message will be given below anyhow
+            break;
+          }
+        }
+
         pEnd++;
         pEnd++;
+      }
 
 
-      wxString strKey(pStart, pEnd);
+      wxString strKey(FilterInEntryName(wxString(pStart, pEnd).Trim()));
 
       // skip whitespace
 
       // skip whitespace
-      while ( isspace(*pEnd) )
+      while ( wxIsspace(*pEnd) )
         pEnd++;
 
         pEnd++;
 
-      if ( *pEnd++ != '=' ) {
+      if ( *pEnd++ != wxT('=') ) {
         wxLogError(_("file '%s', line %d: '=' expected."),
         wxLogError(_("file '%s', line %d: '=' expected."),
-                   file.GetName(), n + 1);
+                   buffer.GetName(), n + 1);
       }
       else {
       }
       else {
-        ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strKey);
+        wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strKey);
 
         if ( pEntry == NULL ) {
           // new entry
           pEntry = m_pCurrentGroup->AddEntry(strKey, n);
 
         if ( pEntry == NULL ) {
           // new entry
           pEntry = m_pCurrentGroup->AddEntry(strKey, n);
-
-          if ( bLocal )
-            pEntry->SetLine(m_linesTail);
         }
         else {
           if ( bLocal && pEntry->IsImmutable() ) {
             // immutable keys can't be changed by user
         }
         else {
           if ( bLocal && pEntry->IsImmutable() ) {
             // immutable keys can't be changed by user
-            wxLogWarning(_("file '%s', line %d: value for "
-                           "immutable key '%s' ignored."),
-                         file.GetName(), n + 1, strKey.c_str());
+            wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
+                         buffer.GetName(), n + 1, strKey.c_str());
             continue;
           }
           // the condition below catches the cases (a) and (b) but not (c):
             continue;
           }
           // the condition below catches the cases (a) and (b) but not (c):
@@ -377,20 +660,24 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
           //  (c) key from global file now found in local one
           // which is exactly what we want.
           else if ( !bLocal || pEntry->IsLocal() ) {
           //  (c) key from global file now found in local one
           // which is exactly what we want.
           else if ( !bLocal || pEntry->IsLocal() ) {
-            wxLogWarning(_("file '%s', line %d: key '%s' was first "
-                           "found at line %d."),
-                         file.GetName(), n + 1, strKey.c_str(), pEntry->Line());
+            wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
+                         buffer.GetName(), (int)n + 1, strKey.c_str(), pEntry->Line());
 
 
-            if ( bLocal )
-              pEntry->SetLine(m_linesTail);
           }
         }
 
           }
         }
 
+        if ( bLocal )
+          pEntry->SetLine(m_linesTail);
+
         // skip whitespace
         // skip whitespace
-        while ( isspace(*pEnd) )
+        while ( wxIsspace(*pEnd) )
           pEnd++;
 
           pEnd++;
 
-        pEntry->SetValue(FilterIn(pEnd), FALSE /* read from file */);
+        wxString value = pEnd;
+        if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS) )
+            value = FilterInValue(value);
+
+        pEntry->SetValue(value, false);
       }
     }
   }
       }
     }
   }
@@ -402,45 +689,64 @@ void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
 
 void wxFileConfig::SetRootPath()
 {
 
 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.IsEmpty() ) {
-    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++ ) {
-    ConfigGroup *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;
+
+            pNextGroup = m_pCurrentGroup->AddSubgroup(aParts[n]);
+        }
 
 
-  // recombine path parts in one variable
-  m_strPath.Empty();
-  for ( n = 0; n < aParts.Count(); n++ ) {
-    m_strPath << wxCONFIG_PATH_SEPARATOR << 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 */);
+}
+
+const wxString& wxFileConfig::GetPath() const
+{
+    return m_strPath;
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -449,66 +755,70 @@ void wxFileConfig::SetPath(const wxString& strPath)
 
 bool wxFileConfig::GetFirstGroup(wxString& str, long& lIndex) const
 {
 
 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
 {
 }
 
 bool wxFileConfig::GetNextGroup (wxString& str, long& lIndex) const
 {
-  if ( size_t(lIndex) < m_pCurrentGroup->Groups().Count() ) {
-    str = m_pCurrentGroup->Groups()[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
 {
 }
 
 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
 {
 }
 
 bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex) const
 {
-  if ( size_t(lIndex) < m_pCurrentGroup->Entries().Count() ) {
-    str = m_pCurrentGroup->Entries()[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 wxFileConfig::GetNumberOfEntries(bool bRecursive) const
 {
-  size_t n = m_pCurrentGroup->Entries().Count();
-  if ( bRecursive ) {
-    ConfigGroup *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 ) {
+        wxFileConfig * const self = const_cast<wxFileConfig *>(this);
+
+        wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
+        size_t nSubgroups = m_pCurrentGroup->Groups().GetCount();
+        for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
+            self->m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
+            n += GetNumberOfEntries(true);
+            self->m_pCurrentGroup = pOldCurrentGroup;
+        }
     }
     }
-  }
 
 
-  return n;
+    return n;
 }
 
 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
 {
 }
 
 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
 {
-  size_t n = m_pCurrentGroup->Groups().Count();
-  if ( bRecursive ) {
-    ConfigGroup *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 ) {
+        wxFileConfig * const self = const_cast<wxFileConfig *>(this);
+
+        wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
+        size_t nSubgroups = m_pCurrentGroup->Groups().GetCount();
+        for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
+            self->m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
+            n += GetNumberOfGroups(true);
+            self->m_pCurrentGroup = pOldCurrentGroup;
+        }
     }
     }
-  }
 
 
-  return n;
+    return n;
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -517,141 +827,303 @@ size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
 
 bool wxFileConfig::HasGroup(const wxString& strName) const
 {
 
 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;
+
+    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);
 
 
-  ConfigGroup *pGroup = m_pCurrentGroup->FindSubgroup(path.Name());
-  return pGroup != NULL;
+    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);
 
 
-  ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
-  return pEntry != NULL;
+    // 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;
+
+    // restore the old path if we changed it above
+    if ( !pathOld.empty() )
+    {
+        self->SetPath(pathOld);
+    }
+
+    return exists;
 }
 
 // ----------------------------------------------------------------------------
 // read/write values
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // read/write values
 // ----------------------------------------------------------------------------
 
-bool wxFileConfig::Read(const wxString& key,
-                        wxString* pStr) const
+bool wxFileConfig::DoReadString(const wxString& key, wxString* pStr) const
 {
 {
-  wxConfigPathChanger path(this, key);
+    wxConfigPathChanger path(this, key);
 
 
-  ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
-  if (pEntry == NULL) {
-    return FALSE;
-  }
-  else {
-    *pStr = ExpandEnvVars(pEntry->Value());
-    return TRUE;
-  }
+    wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
+    if (pEntry == NULL) {
+        return false;
+    }
+
+    *pStr = pEntry->Value();
+
+    return true;
 }
 
 }
 
-bool wxFileConfig::Read(const wxString& key,
-                        wxString* pStr, const wxString& defVal) const
+bool wxFileConfig::DoReadLong(const wxString& key, long *pl) const
 {
 {
-  wxConfigPathChanger path(this, key);
+    wxString str;
+    if ( !Read(key, &str) )
+        return false;
 
 
-  ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
-  if (pEntry == NULL) {
-    if( IsRecordingDefaults() )
-      ((wxFileConfig *)this)->Write(key,defVal);
-    *pStr = ExpandEnvVars(defVal);
-    return FALSE;
-  }
-  else {
-    *pStr = ExpandEnvVars(pEntry->Value());
-    return TRUE;
-  }
+    // extra spaces shouldn't prevent us from reading numeric values
+    str.Trim();
+
+    return str.ToLong(pl);
 }
 
 }
 
-bool wxFileConfig::Read(const wxString& key, long *pl) const
+#if wxUSE_BASE64
+
+bool wxFileConfig::DoReadBinary(const wxString& key, wxMemoryBuffer* buf) const
 {
 {
-  wxString str;
-  if ( Read(key, & str) ) {
-    *pl = atol(str);
-    return TRUE;
-  }
-  else {
-    return FALSE;
-  }
+    wxCHECK_MSG( buf, false, wxT("NULL buffer") );
+
+    wxString str;
+    if ( !Read(key, &str) )
+        return false;
+
+    *buf = wxBase64Decode(str);
+    return true;
 }
 
 }
 
-bool wxFileConfig::Write(const wxString& key, const wxString& szValue)
+#endif // wxUSE_BASE64
+
+bool wxFileConfig::DoWriteString(const wxString& key, const wxString& szValue)
 {
 {
-  wxConfigPathChanger path(this, key);
+    wxConfigPathChanger     path(this, key);
+    wxString                strName = path.Name();
 
 
-  wxString strName = path.Name();
-  if ( strName.IsEmpty() ) {
-    // setting the value of a group is an error
-    wxASSERT_MSG( IsEmpty(szValue), _("can't set value of a group!") );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("  Writing String '%s' = '%s' to Group '%s'"),
+                strName.c_str(),
+                szValue.c_str(),
+                GetPath().c_str() );
 
 
-    // ... except if it's empty in which case it's a way to force it's creation
-    m_pCurrentGroup->SetDirty();
+    if ( strName.empty() )
+    {
+            // setting the value of a group is an error
 
 
-    // this will add a line for this group if it didn't have it before
-    (void)m_pCurrentGroup->GetGroupLine();
-  }
-  else {
-    // writing an entry
-
-    // check that the name is reasonable
-    if ( strName[0u] == wxCONFIG_IMMUTABLE_PREFIX ) {
-      wxLogError(_("Entry name can't start with '%c'."),
-                 wxCONFIG_IMMUTABLE_PREFIX);
-      return FALSE;
-    }
+        wxASSERT_MSG( szValue.empty(), wxT("can't set value of a group!") );
 
 
-    for ( const char *pc = strName; *pc != '\0'; pc++ ) {
-      if ( !IsValid(*pc) ) {
-        wxLogError(_("Character '%c' is invalid in a config entry name."),
-                   *pc);
-        return FALSE;
-      }
+            // ... except if it's empty in which case it's a way to force it's creation
+
+        wxLogTrace( FILECONF_TRACE_MASK,
+                    wxT("  Creating group %s"),
+                    m_pCurrentGroup->Name().c_str() );
+
+        SetDirty();
+
+        // 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
+    {
+        // writing an entry check that the name is reasonable
+        if ( strName[0u] == wxCONFIG_IMMUTABLE_PREFIX )
+        {
+            wxLogError( _("Config entry name cannot start with '%c'."),
+                        wxCONFIG_IMMUTABLE_PREFIX);
+            return false;
+        }
 
 
-    ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strName);
-    if ( pEntry == NULL )
-      pEntry = m_pCurrentGroup->AddEntry(strName);
+        wxFileConfigEntry   *pEntry = m_pCurrentGroup->FindEntry(strName);
 
 
-    pEntry->SetValue(szValue);
-  }
+        if ( pEntry == 0 )
+        {
+            wxLogTrace( FILECONF_TRACE_MASK,
+                        wxT("  Adding Entry %s"),
+                        strName.c_str() );
+            pEntry = m_pCurrentGroup->AddEntry(strName);
+        }
+
+        wxLogTrace( FILECONF_TRACE_MASK,
+                    wxT("  Setting value %s"),
+                    szValue.c_str() );
+        pEntry->SetValue(szValue);
+
+        SetDirty();
+    }
 
 
-  return TRUE;
+    return true;
 }
 
 }
 
-bool wxFileConfig::Write(const wxString& key, long lValue)
+bool wxFileConfig::DoWriteLong(const wxString& key, long lValue)
 {
 {
-  // ltoa() is not ANSI :-(
-  wxString buf;
-  buf.Printf("%ld", lValue);
-  return Write(key, buf);
+  return Write(key, wxString::Format(wxT("%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 */)
 {
 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
 {
-  if ( LineListIsEmpty() || !m_pRootGroup->IsDirty() )
-    return TRUE;
+  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() ) {
+  if ( !file.IsOpened() )
+  {
     wxLogError(_("can't open user configuration file."));
     wxLogError(_("can't open user configuration file."));
-    return FALSE;
+    return false;
   }
 
   // write all strings to file
   }
 
   // write all strings to file
-  for ( LineList *p = m_linesHead; p != NULL; p = p->Next() ) {
-    if ( !file.Write(p->Text() + wxTextFile::GetEOL()) ) {
-      wxLogError(_("can't write user configuration file."));
-      return FALSE;
-    }
+  wxString filetext;
+  filetext.reserve(4096);
+  for ( wxFileConfigLineList *p = m_linesHead; p != NULL; p = p->Next() )
+  {
+    filetext << p->Text() << wxTextFile::GetEOL();
+  }
+
+  if ( !file.Write(filetext, *m_conv) )
+  {
+    wxLogError(_("can't write user configuration file."));
+    return false;
+  }
+
+  if ( !file.Commit() )
+  {
+      wxLogError(_("Failed to update user configuration file."));
+
+      return false;
   }
 
   }
 
-  return file.Commit();
+  ResetDirty();
+
+#if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
+  m_fnLocalFile.MacSetTypeAndCreator('TEXT', 'ttxt');
+#endif // __WXMAC__
+
+  return true;
+}
+
+#if wxUSE_STREAMS
+
+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();
+
+        wxCharBuffer buf(line.mb_str(conv));
+        if ( !os.Write(buf, strlen(buf)) )
+        {
+            wxLogError(_("Error saving user configuration data."));
+
+            return false;
+        }
+    }
+
+    ResetDirty();
+
+    return true;
+}
+
+#endif // wxUSE_STREAMS
+
+// ----------------------------------------------------------------------------
+// renaming groups/entries
+// ----------------------------------------------------------------------------
+
+bool wxFileConfig::RenameEntry(const wxString& oldName,
+                               const wxString& newName)
+{
+    wxASSERT_MSG( oldName.find(wxCONFIG_PATH_SEPARATOR) == wxString::npos,
+                   wxT("RenameEntry(): paths are not supported") );
+
+    // check that the entry exists
+    wxFileConfigEntry *oldEntry = m_pCurrentGroup->FindEntry(oldName);
+    if ( !oldEntry )
+        return false;
+
+    // check that the new entry doesn't already exist
+    if ( m_pCurrentGroup->FindEntry(newName) )
+        return false;
+
+    // delete the old entry, create the new one
+    wxString value = oldEntry->Value();
+    if ( !m_pCurrentGroup->DeleteEntry(oldName) )
+        return false;
+
+    SetDirty();
+
+    wxFileConfigEntry *newEntry = m_pCurrentGroup->AddEntry(newName);
+    newEntry->SetValue(value);
+
+    return true;
+}
+
+bool wxFileConfig::RenameGroup(const wxString& oldName,
+                               const wxString& newName)
+{
+    // check that the group exists
+    wxFileConfigGroup *group = m_pCurrentGroup->FindSubgroup(oldName);
+    if ( !group )
+        return false;
+
+    // check that the new group doesn't already exist
+    if ( m_pCurrentGroup->FindSubgroup(newName) )
+        return false;
+
+    group->Rename(newName);
+
+    SetDirty();
+
+    return true;
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -663,118 +1135,205 @@ bool wxFileConfig::DeleteEntry(const wxString& key, bool bGroupIfEmptyAlso)
   wxConfigPathChanger path(this, key);
 
   if ( !m_pCurrentGroup->DeleteEntry(path.Name()) )
   wxConfigPathChanger path(this, key);
 
   if ( !m_pCurrentGroup->DeleteEntry(path.Name()) )
-    return FALSE;
+    return false;
+
+  SetDirty();
 
   if ( bGroupIfEmptyAlso && m_pCurrentGroup->IsEmpty() ) {
     if ( m_pCurrentGroup != m_pRootGroup ) {
 
   if ( bGroupIfEmptyAlso && m_pCurrentGroup->IsEmpty() ) {
     if ( m_pCurrentGroup != m_pRootGroup ) {
-      ConfigGroup *pGroup = m_pCurrentGroup;
-      SetPath("..");  // changes m_pCurrentGroup!
+      wxFileConfigGroup *pGroup = m_pCurrentGroup;
+      SetPath(wxT(".."));  // changes m_pCurrentGroup!
       m_pCurrentGroup->DeleteSubgroupByName(pGroup->Name());
     }
     //else: never delete the root group
   }
 
       m_pCurrentGroup->DeleteSubgroupByName(pGroup->Name());
     }
     //else: never delete the root group
   }
 
-  return TRUE;
+  return true;
 }
 
 bool wxFileConfig::DeleteGroup(const wxString& key)
 {
 }
 
 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();
 
 
-  return m_pCurrentGroup->DeleteSubgroupByName(path.Name());
+  SetDirty();
+
+  return true;
 }
 
 bool wxFileConfig::DeleteAll()
 {
   CleanUp();
 
 }
 
 bool wxFileConfig::DeleteAll()
 {
   CleanUp();
 
-  const char *szFile = m_strLocalFile;
-
-  if ( remove(szFile) == -1 )
-    wxLogSysError(_("can't delete user configuration file '%s'"), szFile);
+  if ( m_fnLocalFile.IsOk() )
+  {
+      if ( m_fnLocalFile.FileExists() &&
+           !wxRemoveFile(m_fnLocalFile.GetFullPath()) )
+      {
+          wxLogSysError(_("can't delete user configuration file '%s'"),
+                        m_fnLocalFile.GetFullPath().c_str());
+          return false;
+      }
+  }
 
 
-  m_strLocalFile = m_strGlobalFile = "";
   Init();
 
   Init();
 
-  return TRUE;
+  return true;
 }
 
 // ----------------------------------------------------------------------------
 // linked list functions
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // linked list functions
 // ----------------------------------------------------------------------------
 
-// append a new line to the end of the list
-wxFileConfig::LineList *wxFileConfig::LineListAppend(const wxString& str)
+    // append a new line to the end of the list
+
+wxFileConfigLineList *wxFileConfig::LineListAppend(const wxString& str)
 {
 {
-  LineList *pLine = new LineList(str);
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("    ** Adding Line '%s'"),
+                str.c_str() );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        head: %s"),
+                ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+                               : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        tail: %s"),
+                ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+                               : wxEmptyString) );
+
+    wxFileConfigLineList *pLine = new wxFileConfigLineList(str);
+
+    if ( m_linesTail == NULL )
+    {
+        // list is empty
+        m_linesHead = pLine;
+    }
+    else
+    {
+        // adjust pointers
+        m_linesTail->SetNext(pLine);
+        pLine->SetPrev(m_linesTail);
+    }
 
 
-  if ( m_linesTail == NULL ) {
-    // list is empty
-    m_linesHead = pLine;
-  }
-  else {
-    // adjust pointers
-    m_linesTail->SetNext(pLine);
-    pLine->SetPrev(m_linesTail);
-  }
+    m_linesTail = pLine;
 
 
-  m_linesTail = pLine;
-  return m_linesTail;
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        head: %s"),
+                ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+                               : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        tail: %s"),
+                ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+                               : wxEmptyString) );
+
+    return m_linesTail;
 }
 
 // insert a new line after the given one or in the very beginning if !pLine
 }
 
 // insert a new line after the given one or in the very beginning if !pLine
-wxFileConfig::LineList *wxFileConfig::LineListInsert(const wxString& str,
-                                                     LineList *pLine)
-{
-  if ( pLine == m_linesTail )
-    return LineListAppend(str);
-
-  LineList *pNewLine = new LineList(str);
-  if ( pLine == NULL ) {
-    // prepend to the list
-    pNewLine->SetNext(m_linesHead);
-    m_linesHead->SetPrev(pNewLine);
-    m_linesHead = pNewLine;
-  }
-  else {
-    // insert before pLine
-    LineList *pNext = pLine->Next();
-    pNewLine->SetNext(pNext);
-    pNewLine->SetPrev(pLine);
-    pNext->SetPrev(pNewLine);
-    pLine->SetNext(pNewLine);
-  }
+wxFileConfigLineList *wxFileConfig::LineListInsert(const wxString& str,
+                                                   wxFileConfigLineList *pLine)
+{
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("    ** Inserting Line '%s' after '%s'"),
+                str.c_str(),
+                ((pLine) ? (const wxChar*)pLine->Text().c_str()
+                         : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        head: %s"),
+                ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+                               : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        tail: %s"),
+                ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+                               : wxEmptyString) );
+
+    if ( pLine == m_linesTail )
+        return LineListAppend(str);
+
+    wxFileConfigLineList *pNewLine = new wxFileConfigLineList(str);
+    if ( pLine == NULL )
+    {
+        // prepend to the list
+        pNewLine->SetNext(m_linesHead);
+        m_linesHead->SetPrev(pNewLine);
+        m_linesHead = pNewLine;
+    }
+    else
+    {
+        // insert before pLine
+        wxFileConfigLineList *pNext = pLine->Next();
+        pNewLine->SetNext(pNext);
+        pNewLine->SetPrev(pLine);
+        pNext->SetPrev(pNewLine);
+        pLine->SetNext(pNewLine);
+    }
 
 
-  return pNewLine;
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        head: %s"),
+                ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+                               : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        tail: %s"),
+                ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+                               : wxEmptyString) );
+
+    return pNewLine;
 }
 
 }
 
-void wxFileConfig::LineListRemove(LineList *pLine)
+void wxFileConfig::LineListRemove(wxFileConfigLineList *pLine)
 {
 {
-  LineList *pPrev = pLine->Prev(),
-           *pNext = pLine->Next();
-
-  // first entry?
-  if ( pPrev == NULL )
-    m_linesHead = pNext;
-  else
-    pPrev->SetNext(pNext);
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("    ** Removing Line '%s'"),
+                pLine->Text().c_str() );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        head: %s"),
+                ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+                               : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        tail: %s"),
+                ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+                               : wxEmptyString) );
+
+    wxFileConfigLineList    *pPrev = pLine->Prev(),
+                            *pNext = pLine->Next();
+
+        // first entry?
+
+    if ( pPrev == NULL )
+        m_linesHead = pNext;
+    else
+        pPrev->SetNext(pNext);
 
 
-  // last entry?
-  if ( pNext == NULL )
-    m_linesTail = pPrev;
-  else    
-    pNext->SetPrev(pPrev);
+        // last entry?
 
 
-  delete pLine;
+    if ( pNext == NULL )
+        m_linesTail = pPrev;
+    else
+        pNext->SetPrev(pPrev);
+
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        head: %s"),
+                ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
+                               : wxEmptyString) );
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("        tail: %s"),
+                ((m_linesTail) ? (const wxChar*)m_linesTail->Text().c_str()
+                               : wxEmptyString) );
+
+    delete pLine;
 }
 
 bool wxFileConfig::LineListIsEmpty()
 {
 }
 
 bool wxFileConfig::LineListIsEmpty()
 {
-  return m_linesHead == NULL;
+    return m_linesHead == NULL;
 }
 
 // ============================================================================
 }
 
 // ============================================================================
-// wxFileConfig::ConfigGroup
+// wxFileConfig::wxFileConfigGroup
 // ============================================================================
 
 // ----------------------------------------------------------------------------
 // ============================================================================
 
 // ----------------------------------------------------------------------------
@@ -782,7 +1341,7 @@ bool wxFileConfig::LineListIsEmpty()
 // ----------------------------------------------------------------------------
 
 // ctor
 // ----------------------------------------------------------------------------
 
 // ctor
-wxFileConfig::ConfigGroup::ConfigGroup(wxFileConfig::ConfigGroup *pParent,
+wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup *pParent,
                                        const wxString& strName,
                                        wxFileConfig *pConfig)
                          : m_aEntries(CompareEntries),
                                        const wxString& strName,
                                        wxFileConfig *pConfig)
                          : m_aEntries(CompareEntries),
@@ -791,7 +1350,6 @@ wxFileConfig::ConfigGroup::ConfigGroup(wxFileConfig::ConfigGroup *pParent,
 {
   m_pConfig = pConfig;
   m_pParent = pParent;
 {
   m_pConfig = pConfig;
   m_pParent = pParent;
-  m_bDirty  = FALSE;
   m_pLine   = NULL;
 
   m_pLastEntry = NULL;
   m_pLine   = NULL;
 
   m_pLastEntry = NULL;
@@ -799,15 +1357,15 @@ wxFileConfig::ConfigGroup::ConfigGroup(wxFileConfig::ConfigGroup *pParent,
 }
 
 // dtor deletes all children
 }
 
 // dtor deletes all children
-wxFileConfig::ConfigGroup::~ConfigGroup()
+wxFileConfigGroup::~wxFileConfigGroup()
 {
   // entries
 {
   // 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
   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];
 }
   for ( n = 0; n < nCount; n++ )
     delete m_aSubgroups[n];
 }
@@ -816,11 +1374,14 @@ wxFileConfig::ConfigGroup::~ConfigGroup()
 // line
 // ----------------------------------------------------------------------------
 
 // line
 // ----------------------------------------------------------------------------
 
-void wxFileConfig::ConfigGroup::SetLine(LineList *pLine)
+void wxFileConfigGroup::SetLine(wxFileConfigLineList *pLine)
 {
 {
-  wxASSERT( m_pLine == NULL ); // 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,
+                   wxT("changing line for a non-root group?") );
 
 
-  m_pLine = pLine;
+    m_pLine = pLine;
 }
 
 /*
 }
 
 /*
@@ -852,77 +1413,154 @@ void wxFileConfig::ConfigGroup::SetLine(LineList *pLine)
       backwards in the config file (OTOH, it's not that important) and as we
       would still need to do it for the subgroups the code wouldn't have been
       significantly less complicated.
       backwards in the config file (OTOH, it's not that important) and as we
       would still need to do it for the subgroups the code wouldn't have been
       significantly less complicated.
- */
+*/
 
 // Return the line which contains "[our name]". If we're still not in the list,
 // add our line to it immediately after the last line of our parent group if we
 // have it or in the very beginning if we're the root group.
 
 // Return the line which contains "[our name]". If we're still not in the list,
 // add our line to it immediately after the last line of our parent group if we
 // have it or in the very beginning if we're the root group.
-wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetGroupLine()
-{
-  if ( m_pLine == NULL ) {
-    ConfigGroup *pParent = Parent();
-
-    // this group wasn't present in local config file, add it now
-    if ( pParent != NULL ) {
-      wxString strFullName;
-      strFullName << "[" << (GetFullName().c_str() + 1) << "]"; // +1: no '/'
-      m_pLine = m_pConfig->LineListInsert(strFullName,
-                                          pParent->GetLastGroupLine());
-      pParent->SetLastGroup(this);  // we're surely after all the others
-    }
-    else {
-      // we return NULL, so that LineListInsert() will insert us in the
-      // very beginning
+wxFileConfigLineList *wxFileConfigGroup::GetGroupLine()
+{
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("  GetGroupLine() for Group '%s'"),
+                Name().c_str() );
+
+    if ( !m_pLine )
+    {
+        wxLogTrace( FILECONF_TRACE_MASK,
+                    wxT("    Getting Line item pointer") );
+
+        wxFileConfigGroup   *pParent = Parent();
+
+        // this group wasn't present in local config file, add it now
+        if ( pParent )
+        {
+            wxLogTrace( FILECONF_TRACE_MASK,
+                        wxT("    checking parent '%s'"),
+                        pParent->Name().c_str() );
+
+            wxString    strFullName;
+
+            // add 1 to the name because we don't want to start with '/'
+            strFullName << wxT("[")
+                        << FilterOutEntryName(GetFullName().c_str() + 1)
+                        << wxT("]");
+            m_pLine = m_pConfig->LineListInsert(strFullName,
+                                                pParent->GetLastGroupLine());
+            pParent->SetLastGroup(this);  // we're surely after all the others
+        }
+        //else: this is the root group and so we return NULL because we don't
+        //      have any group line
     }
     }
-  }
 
 
-  return m_pLine;
+    return m_pLine;
 }
 
 // Return the last line belonging to the subgroups of this group (after which
 // we can add a new subgroup), if we don't have any subgroups or entries our
 // last line is the group line (m_pLine) itself.
 }
 
 // Return the last line belonging to the subgroups of this group (after which
 // we can add a new subgroup), if we don't have any subgroups or entries our
 // last line is the group line (m_pLine) itself.
-wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetLastGroupLine()
+wxFileConfigLineList *wxFileConfigGroup::GetLastGroupLine()
 {
 {
-  // if we have any subgroups, our last line is the last line of the last
-  // subgroup
-  if ( m_pLastGroup != NULL ) {
-    wxFileConfig::LineList *pLine = m_pLastGroup->GetLastGroupLine();
+    // if we have any subgroups, our last line is the last line of the last
+    // subgroup
+    if ( m_pLastGroup )
+    {
+        wxFileConfigLineList *pLine = m_pLastGroup->GetLastGroupLine();
 
 
-    wxASSERT( pLine != NULL );  // last group must have !NULL associated line
-    return pLine;
-  }
+        wxASSERT_MSG( pLine, wxT("last group must have !NULL associated line") );
+
+        return pLine;
+    }
 
 
-  // no subgroups, so the last line is the line of thelast entry (if any)
-  return GetLastEntryLine();
+    // no subgroups, so the last line is the line of thelast entry (if any)
+    return GetLastEntryLine();
 }
 
 // return the last line belonging to the entries of this group (after which
 // we can add a new entry), if we don't have any entries we will add the new
 // one immediately after the group line itself.
 }
 
 // return the last line belonging to the entries of this group (after which
 // we can add a new entry), if we don't have any entries we will add the new
 // one immediately after the group line itself.
-wxFileConfig::LineList *wxFileConfig::ConfigGroup::GetLastEntryLine()
+wxFileConfigLineList *wxFileConfigGroup::GetLastEntryLine()
 {
 {
-  if ( m_pLastEntry != NULL ) {
-    wxFileConfig::LineList *pLine = m_pLastEntry->GetLine();
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("  GetLastEntryLine() for Group '%s'"),
+                Name().c_str() );
 
 
-    wxASSERT( pLine != NULL );  // last entry must have !NULL associated line
-    return pLine;
-  }
+    if ( m_pLastEntry )
+    {
+        wxFileConfigLineList    *pLine = m_pLastEntry->GetLine();
+
+        wxASSERT_MSG( pLine, wxT("last entry must have !NULL associated line") );
+
+        return pLine;
+    }
 
 
-  // no entries: insert after the group header
-  return GetGroupLine();
+    // no entries: insert after the group header, if any
+    return GetGroupLine();
+}
+
+void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry *pEntry)
+{
+    m_pLastEntry = pEntry;
+
+    if ( !m_pLine )
+    {
+        // the only situation in which a group without its own line can have
+        // an entry is when the first entry is added to the initially empty
+        // root pseudo-group
+        wxASSERT_MSG( !m_pParent, wxT("unexpected for non root group") );
+
+        // let the group know that it does have a line in the file now
+        m_pLine = pEntry->GetLine();
+    }
 }
 
 // ----------------------------------------------------------------------------
 // group name
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // group name
 // ----------------------------------------------------------------------------
 
-wxString wxFileConfig::ConfigGroup::GetFullName() const
+void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
+{
+    // update the line of this group
+    wxFileConfigLineList *line = GetGroupLine();
+    wxCHECK_RET( line, wxT("a non root group must have a corresponding line!") );
+
+    // +1: skip the leading '/'
+    line->SetText(wxString::Format(wxT("[%s]"), GetFullName().c_str() + 1));
+
+
+    // also update all subgroups as they have this groups name in their lines
+    const size_t nCount = m_aSubgroups.GetCount();
+    for ( size_t n = 0; n < nCount; n++ )
+    {
+        m_aSubgroups[n]->UpdateGroupAndSubgroupsLines();
+    }
+}
+
+void wxFileConfigGroup::Rename(const wxString& newName)
+{
+    wxCHECK_RET( m_pParent, wxT("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();
+}
+
+wxString wxFileConfigGroup::GetFullName() const
 {
 {
-  if ( Parent() )
-    return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR + Name();
-  else
-    return "";
+    wxString fullname;
+    if ( Parent() )
+        fullname = Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR + Name();
+
+    return fullname;
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -930,23 +1568,23 @@ wxString wxFileConfig::ConfigGroup::GetFullName() const
 // ----------------------------------------------------------------------------
 
 // use binary search because the array is sorted
 // ----------------------------------------------------------------------------
 
 // use binary search because the array is sorted
-wxFileConfig::ConfigEntry *
-wxFileConfig::ConfigGroup::FindEntry(const char *szName) const
+wxFileConfigEntry *
+wxFileConfigGroup::FindEntry(const wxString& name) const
 {
   size_t i,
        lo = 0,
 {
   size_t i,
        lo = 0,
-       hi = m_aEntries.Count();
+       hi = m_aEntries.GetCount();
   int res;
   int res;
-  wxFileConfig::ConfigEntry *pEntry;
+  wxFileConfigEntry *pEntry;
 
   while ( lo < hi ) {
     i = (lo + hi)/2;
     pEntry = m_aEntries[i];
 
     #if wxCONFIG_CASE_SENSITIVE
 
   while ( lo < hi ) {
     i = (lo + hi)/2;
     pEntry = m_aEntries[i];
 
     #if wxCONFIG_CASE_SENSITIVE
-      res = strcmp(pEntry->Name(), szName);
+      res = pEntry->Name().compare(name);
     #else
     #else
-      res = Stricmp(pEntry->Name(), szName);
+      res = pEntry->Name().CmpNoCase(name);
     #endif
 
     if ( res > 0 )
     #endif
 
     if ( res > 0 )
@@ -960,23 +1598,23 @@ wxFileConfig::ConfigGroup::FindEntry(const char *szName) const
   return NULL;
 }
 
   return NULL;
 }
 
-wxFileConfig::ConfigGroup *
-wxFileConfig::ConfigGroup::FindSubgroup(const char *szName) const
+wxFileConfigGroup *
+wxFileConfigGroup::FindSubgroup(const wxString& name) const
 {
   size_t i,
        lo = 0,
 {
   size_t i,
        lo = 0,
-       hi = m_aSubgroups.Count();
+       hi = m_aSubgroups.GetCount();
   int res;
   int res;
-  wxFileConfig::ConfigGroup *pGroup;
+  wxFileConfigGroup *pGroup;
 
   while ( lo < hi ) {
     i = (lo + hi)/2;
     pGroup = m_aSubgroups[i];
 
     #if wxCONFIG_CASE_SENSITIVE
 
   while ( lo < hi ) {
     i = (lo + hi)/2;
     pGroup = m_aSubgroups[i];
 
     #if wxCONFIG_CASE_SENSITIVE
-      res = strcmp(pGroup->Name(), szName);
+      res = pGroup->Name().compare(name);
     #else
     #else
-      res = Stricmp(pGroup->Name(), szName);
+      res = pGroup->Name().CmpNoCase(name);
     #endif
 
     if ( res > 0 )
     #endif
 
     if ( res > 0 )
@@ -995,27 +1633,25 @@ wxFileConfig::ConfigGroup::FindSubgroup(const char *szName) const
 // ----------------------------------------------------------------------------
 
 // create a new entry and add it to the current group
 // ----------------------------------------------------------------------------
 
 // create a new entry and add it to the current group
-wxFileConfig::ConfigEntry *
-wxFileConfig::ConfigGroup::AddEntry(const wxString& strName, int nLine)
+wxFileConfigEntry *wxFileConfigGroup::AddEntry(const wxString& strName, int nLine)
 {
 {
-  wxASSERT( FindEntry(strName) == NULL );
+    wxASSERT( FindEntry(strName) == 0 );
 
 
-  ConfigEntry *pEntry = new ConfigEntry(this, strName, nLine);
-  m_aEntries.Add(pEntry);
+    wxFileConfigEntry   *pEntry = new wxFileConfigEntry(this, strName, nLine);
 
 
-  return pEntry;
+    m_aEntries.Add(pEntry);
+    return pEntry;
 }
 
 // create a new group and add it to the current group
 }
 
 // create a new group and add it to the current group
-wxFileConfig::ConfigGroup *
-wxFileConfig::ConfigGroup::AddSubgroup(const wxString& strName)
+wxFileConfigGroup *wxFileConfigGroup::AddSubgroup(const wxString& strName)
 {
 {
-  wxASSERT( FindSubgroup(strName) == NULL );
+    wxASSERT( FindSubgroup(strName) == 0 );
 
 
-  ConfigGroup *pGroup = new ConfigGroup(this, strName, m_pConfig);
-  m_aSubgroups.Add(pGroup);
+    wxFileConfigGroup   *pGroup = new wxFileConfigGroup(this, strName, m_pConfig);
 
 
-  return pGroup;
+    m_aSubgroups.Add(pGroup);
+    return pGroup;
 }
 
 // ----------------------------------------------------------------------------
 }
 
 // ----------------------------------------------------------------------------
@@ -1029,84 +1665,137 @@ wxFileConfig::ConfigGroup::AddSubgroup(const wxString& strName)
   delete several of them.
  */
 
   delete several of them.
  */
 
-bool wxFileConfig::ConfigGroup::DeleteSubgroupByName(const char *szName)
+bool wxFileConfigGroup::DeleteSubgroupByName(const wxString& name)
 {
 {
-  return DeleteSubgroup(FindSubgroup(szName));
+    wxFileConfigGroup * const pGroup = FindSubgroup(name);
+
+    return pGroup ? DeleteSubgroup(pGroup) : false;
 }
 
 }
 
-// doesn't delete the subgroup itself, but does remove references to it from
-// all other data structures (and normally the returned pointer should be
-// deleted a.s.a.p. because there is nothing much to be done with it anyhow)
-bool wxFileConfig::ConfigGroup::DeleteSubgroup(ConfigGroup *pGroup)
+// Delete the subgroup and remove all references to it from
+// other data structures.
+bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
 {
 {
-  wxCHECK( pGroup != NULL, FALSE ); // deleting non existing group?
-
-  // delete all entries
-  size_t nCount = pGroup->m_aEntries.Count();
-  for ( size_t nEntry = 0; nEntry < nCount; nEntry++ ) {
-    LineList *pLine = pGroup->m_aEntries[nEntry]->GetLine();
-    if ( pLine != NULL )
-      m_pConfig->LineListRemove(pLine);
-  }
-
-  // and subgroups of this sungroup
-  nCount = pGroup->m_aSubgroups.Count();
-  for ( size_t nGroup = 0; nGroup < nCount; nGroup++ ) {
-    pGroup->DeleteSubgroup(pGroup->m_aSubgroups[nGroup]);
-  }
-
-  LineList *pLine = pGroup->m_pLine;
-  if ( pLine != NULL ) {
-    // notice that we may do this test inside the previous "if" because the
-    // last entry's line is surely !NULL
-    if ( pGroup == m_pLastGroup ) {
-      // our last entry is being deleted - find the last one which stays
-      wxASSERT( m_pLine != NULL );  // we have a subgroup with !NULL pLine...
-
-      // go back until we find a subgroup or reach the group's line
-      ConfigGroup *pNewLast = NULL;
-      size_t n, nSubgroups = m_aSubgroups.Count();
-      LineList *pl;
-      for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() ) {
-        // is it our subgroup?
-        for ( n = 0; (pNewLast == NULL) && (n < nSubgroups); n++ ) {
-          // do _not_ call GetGroupLine! we don't want to add it to the local
-          // file if it's not already there
-          if ( m_aSubgroups[n]->m_pLine == m_pLine )
-            pNewLast = m_aSubgroups[n];
+    wxCHECK_MSG( pGroup, false, wxT("deleting non existing group?") );
+
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("Deleting group '%s' from '%s'"),
+                pGroup->Name().c_str(),
+                Name().c_str() );
+
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("  (m_pLine) = prev: %p, this %p, next %p"),
+                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,
+                wxT("  text: '%s'"),
+                m_pLine ? (const wxChar*)m_pLine->Text().c_str()
+                        : wxEmptyString );
+
+    // delete all entries...
+    size_t nCount = pGroup->m_aEntries.GetCount();
+
+    wxLogTrace(FILECONF_TRACE_MASK,
+               wxT("Removing %lu entries"), (unsigned long)nCount );
+
+    for ( size_t nEntry = 0; nEntry < nCount; nEntry++ )
+    {
+        wxFileConfigLineList *pLine = pGroup->m_aEntries[nEntry]->GetLine();
+
+        if ( pLine )
+        {
+            wxLogTrace( FILECONF_TRACE_MASK,
+                        wxT("    '%s'"),
+                        pLine->Text().c_str() );
+            m_pConfig->LineListRemove(pLine);
         }
         }
+    }
 
 
-        if ( pNewLast != NULL ) // found?
-          break;
-      }
+    // ...and subgroups of this subgroup
+    nCount = pGroup->m_aSubgroups.GetCount();
 
 
-      if ( pl == m_pLine ) {
-        wxASSERT( !pNewLast );  // how comes it has the same line as we?
+    wxLogTrace( FILECONF_TRACE_MASK,
+                wxT("Removing %lu subgroups"), (unsigned long)nCount );
 
 
-        // we've reached the group line without finding any subgroups
-        m_pLastGroup = NULL;
-      }
-      else
-        m_pLastGroup = pNewLast;
+    for ( size_t nGroup = 0; nGroup < nCount; nGroup++ )
+    {
+        pGroup->DeleteSubgroup(pGroup->m_aSubgroups[0]);
     }
 
     }
 
-    m_pConfig->LineListRemove(pLine);
-  }
+    // and then finally the group itself
+    wxFileConfigLineList *pLine = pGroup->m_pLine;
+    if ( pLine )
+    {
+        wxLogTrace( FILECONF_TRACE_MASK,
+                    wxT("  Removing line for group '%s' : '%s'"),
+                    pGroup->Name().c_str(),
+                    pLine->Text().c_str() );
+        wxLogTrace( FILECONF_TRACE_MASK,
+                    wxT("  Removing from group '%s' : '%s'"),
+                    Name().c_str(),
+                    ((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
+        if ( pGroup == m_pLastGroup )
+        {
+            wxLogTrace( FILECONF_TRACE_MASK,
+                        wxT("  Removing last group") );
+
+            // 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.GetCount();
+
+            m_pLastGroup = NULL;
+            for ( wxFileConfigLineList *pl = pLine->Prev();
+                  pl && !m_pLastGroup;
+                  pl = pl->Prev() )
+            {
+                // does this line belong to our subgroup?
+                for ( size_t n = 0; n < nSubgroups; n++ )
+                {
+                    // do _not_ call GetGroupLine! we don't want to add it to
+                    // the local file if it's not already there
+                    if ( m_aSubgroups[n]->m_pLine == pl )
+                    {
+                        m_pLastGroup = m_aSubgroups[n];
+                        break;
+                    }
+                }
+
+                if ( pl == m_pLine )
+                    break;
+            }
+        }
 
 
-  SetDirty();
+        m_pConfig->LineListRemove(pLine);
+    }
+    else
+    {
+        wxLogTrace( FILECONF_TRACE_MASK,
+                    wxT("  No line entry for Group '%s'?"),
+                    pGroup->Name().c_str() );
+    }
 
 
-  m_aSubgroups.Remove(pGroup);
-  delete pGroup;
+    m_aSubgroups.Remove(pGroup);
+    delete pGroup;
 
 
-  return TRUE;
+    return true;
 }
 
 }
 
-bool wxFileConfig::ConfigGroup::DeleteEntry(const char *szName)
+bool wxFileConfigGroup::DeleteEntry(const wxString& name)
 {
 {
-  ConfigEntry *pEntry = FindEntry(szName);
-  wxCHECK( pEntry != NULL, FALSE );  // deleting non existing item?
+  wxFileConfigEntry *pEntry = FindEntry(name);
+  if ( !pEntry )
+  {
+      // entry doesn't exist, nothing to do
+      return false;
+  }
 
 
-  LineList *pLine = pEntry->GetLine();
+  wxFileConfigLineList *pLine = pEntry->GetLine();
   if ( pLine != NULL ) {
     // notice that we may do this test inside the previous "if" because the
     // last entry's line is surely !NULL
   if ( pLine != NULL ) {
     // notice that we may do this test inside the previous "if" because the
     // last entry's line is surely !NULL
@@ -1114,72 +1803,56 @@ bool wxFileConfig::ConfigGroup::DeleteEntry(const char *szName)
       // our last entry is being deleted - find the last one which stays
       wxASSERT( m_pLine != NULL );  // if we have an entry with !NULL pLine...
 
       // 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
-      ConfigEntry *pNewLast = NULL;
-      size_t n, nEntries = m_aEntries.Count();
-      LineList *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?
+      // find the previous entry (if any)
+      wxFileConfigEntry *pNewLast = NULL;
+      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;
           break;
+        }
       }
 
       }
 
-      if ( pl == m_pLine ) {
-        wxASSERT( !pNewLast );  // how comes it has the same line as we?
+      // pNewLast can be NULL here -- it's ok and can happen if we have no
+      // entries left
+      m_pLastEntry = pNewLast;
 
 
-        // we've reached the group line without finding any subgroups
-        m_pLastEntry = NULL;
-      }
-      else
-        m_pLastEntry = pNewLast;
+      // For the root group only, we could be removing the first group line
+      // here, so update m_pLine to avoid keeping a dangling pointer.
+      if ( pLine == m_pLine )
+          SetLine(NULL);
     }
 
     m_pConfig->LineListRemove(pLine);
   }
 
     }
 
     m_pConfig->LineListRemove(pLine);
   }
 
-  // we must be written back for the changes to be saved
-  SetDirty();
-
   m_aEntries.Remove(pEntry);
   delete pEntry;
 
   m_aEntries.Remove(pEntry);
   delete pEntry;
 
-  return TRUE;
-}
-
-// ----------------------------------------------------------------------------
-//
-// ----------------------------------------------------------------------------
-void wxFileConfig::ConfigGroup::SetDirty()
-{
-  m_bDirty = TRUE;
-  if ( Parent() != NULL )             // propagate upwards
-    Parent()->SetDirty();
+  return true;
 }
 
 // ============================================================================
 }
 
 // ============================================================================
-// wxFileConfig::ConfigEntry
+// wxFileConfig::wxFileConfigEntry
 // ============================================================================
 
 // ----------------------------------------------------------------------------
 // ctor
 // ----------------------------------------------------------------------------
 // ============================================================================
 
 // ----------------------------------------------------------------------------
 // ctor
 // ----------------------------------------------------------------------------
-wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup *pParent,
+wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup *pParent,
                                        const wxString& strName,
                                        int nLine)
                          : m_strName(strName)
 {
                                        const wxString& strName,
                                        int nLine)
                          : m_strName(strName)
 {
-  wxASSERT( !strName.IsEmpty() );
+  wxASSERT( !strName.empty() );
 
   m_pParent = pParent;
   m_nLine   = nLine;
   m_pLine   = NULL;
 
 
   m_pParent = pParent;
   m_nLine   = nLine;
   m_pLine   = NULL;
 
-  m_bDirty  = FALSE;
+  m_bHasValue = false;
 
   m_bImmutable = strName[0] == wxCONFIG_IMMUTABLE_PREFIX;
   if ( m_bImmutable )
 
   m_bImmutable = strName[0] == wxCONFIG_IMMUTABLE_PREFIX;
   if ( m_bImmutable )
@@ -1190,7 +1863,7 @@ wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup *pParent,
 // set value
 // ----------------------------------------------------------------------------
 
 // set value
 // ----------------------------------------------------------------------------
 
-void wxFileConfig::ConfigEntry::SetLine(LineList *pLine)
+void wxFileConfigEntry::SetLine(wxFileConfigLineList *pLine)
 {
   if ( m_pLine != NULL ) {
     wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
 {
   if ( m_pLine != NULL ) {
     wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
@@ -1201,48 +1874,57 @@ void wxFileConfig::ConfigEntry::SetLine(LineList *pLine)
   Group()->SetLastEntry(this);
 }
 
   Group()->SetLastEntry(this);
 }
 
-// second parameter is FALSE if we read the value from file and prevents the
+// second parameter is false if we read the value from file and prevents the
 // entry from being marked as 'dirty'
 // entry from being marked as 'dirty'
-void wxFileConfig::ConfigEntry::SetValue(const wxString& strValue, bool bUser)
+void wxFileConfigEntry::SetValue(const wxString& strValue, bool bUser)
 {
 {
-  if ( bUser && IsImmutable() ) {
-    wxLogWarning(_("attempt to change immutable key '%s' ignored."),
-                 Name().c_str());
-    return;
-  }
+    if ( bUser && IsImmutable() )
+    {
+        wxLogWarning( _("attempt to change immutable key '%s' ignored."),
+                      Name().c_str());
+        return;
+    }
 
 
-  // do nothing if it's the same value
-  if ( strValue == m_strValue )
-    return;
+    // do nothing if it's the same value: but don't test for it if m_bHasValue
+    // hadn't been set yet or we'd never write empty values to the file
+    if ( m_bHasValue && strValue == m_strValue )
+        return;
 
 
-  m_strValue = strValue;
+    m_bHasValue = true;
+    m_strValue = strValue;
 
 
-  if ( bUser ) {
-    wxString strVal = FilterOut(strValue);
-    wxString strLine;
-    strLine << m_strName << " = " << strVal;
+    if ( bUser )
+    {
+        wxString strValFiltered;
 
 
-    if ( m_pLine != NULL ) {
-      // entry was read from the local config file, just modify the line
-      m_pLine->SetText(strLine);
-    }
-    else {
-      // add a new line to the file
-      wxASSERT( m_nLine == NOT_FOUND );   // consistency check
+        if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS )
+        {
+            strValFiltered = strValue;
+        }
+        else {
+            strValFiltered = FilterOutValue(strValue);
+        }
 
 
-      m_pLine = Group()->Config()->LineListInsert(strLine,
-                                                  Group()->GetLastEntryLine());
-      Group()->SetLastEntry(this);
-    }
+        wxString    strLine;
+        strLine << FilterOutEntryName(m_strName) << wxT('=') << strValFiltered;
 
 
-    SetDirty();
-  }
-}
-
-void wxFileConfig::ConfigEntry::SetDirty()
-{
-  m_bDirty = TRUE;
-  Group()->SetDirty();
+        if ( m_pLine )
+        {
+            // entry was read from the local config file, just modify the line
+            m_pLine->SetText(strLine);
+        }
+        else // this entry didn't exist in the local file
+        {
+            // 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();
+            m_pLine = Group()->Config()->LineListInsert(strLine, line);
+
+            Group()->SetLastEntry(this);
+        }
+    }
 }
 
 // ============================================================================
 }
 
 // ============================================================================
@@ -1253,113 +1935,130 @@ void wxFileConfig::ConfigEntry::SetDirty()
 // compare functions for array sorting
 // ----------------------------------------------------------------------------
 
 // compare functions for array sorting
 // ----------------------------------------------------------------------------
 
-int CompareEntries(wxFileConfig::ConfigEntry *p1,
-                   wxFileConfig::ConfigEntry *p2)
+int CompareEntries(wxFileConfigEntry *p1, wxFileConfigEntry *p2)
 {
 {
-  #if wxCONFIG_CASE_SENSITIVE
-    return strcmp(p1->Name(), p2->Name());
-  #else
-    return Stricmp(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(wxFileConfig::ConfigGroup *p1,
-                  wxFileConfig::ConfigGroup *p2)
+int CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2)
 {
 {
-  #if wxCONFIG_CASE_SENSITIVE
-    return strcmp(p1->Name(), p2->Name());
-  #else
-    return Stricmp(p1->Name(), p2->Name());
-  #endif
+#if wxCONFIG_CASE_SENSITIVE
+    return p1->Name().compare(p2->Name());
+#else
+    return p1->Name().CmpNoCase(p2->Name());
+#endif
 }
 
 // ----------------------------------------------------------------------------
 // filter functions
 // ----------------------------------------------------------------------------
 
 }
 
 // ----------------------------------------------------------------------------
 // filter functions
 // ----------------------------------------------------------------------------
 
-// undo FilterOut
-wxString FilterIn(const wxString& str)
+// undo FilterOutValue
+static wxString FilterInValue(const wxString& str)
 {
 {
-  wxString strResult;
-  strResult.Alloc(str.Len());
-
-  bool bQuoted = !str.IsEmpty() && str[0] == '"';
-
-  for ( size_t n = bQuoted ? 1 : 0; n < str.Len(); n++ ) {
-    if ( str[n] == '\\' ) {
-      switch ( str[++n] ) {
-        case 'n':
-          strResult += '\n';
-          break;
-
-        case 'r':
-          strResult += '\r';
-          break;
-
-        case 't':
-          strResult += '\t';
-          break;
-
-        case '\\':
-          strResult += '\\';
-          break;
-
-        case '"':
-          strResult += '"';
-          break;
-      }
-    }
-    else {
-      if ( str[n] != '"' || !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
+    wxString strResult;
+    if ( str.empty() )
+        return strResult;
+
+    strResult.reserve(str.length());
+
+    wxString::const_iterator i = str.begin();
+    const bool bQuoted = *i == '"';
+    if ( bQuoted )
+        ++i;
+
+    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;
+            }
+
+            switch ( (*i).GetValue() )
+            {
+                case wxT('n'):
+                    strResult += wxT('\n');
+                    break;
+
+                case wxT('r'):
+                    strResult += wxT('\r');
+                    break;
+
+                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
 }
 
 // quote the string before writing it to file
-wxString FilterOut(const wxString& str)
+static wxString FilterOutValue(const wxString& str)
 {
 {
-   if(str.IsEmpty())
+   if ( !str )
       return str;
       return str;
-   
+
   wxString strResult;
   strResult.Alloc(str.Len());
 
   // quoting is necessary to preserve spaces in the beginning of the string
   wxString strResult;
   strResult.Alloc(str.Len());
 
   // quoting is necessary to preserve spaces in the beginning of the string
-  bool bQuote = isspace(str[0]) || str[0] == '"';
+  bool bQuote = wxIsspace(str[0]) || str[0] == wxT('"');
 
   if ( bQuote )
 
   if ( bQuote )
-    strResult += '"';
+    strResult += wxT('"');
 
 
-  char c;
+  wxChar c;
   for ( size_t n = 0; n < str.Len(); n++ ) {
   for ( size_t n = 0; n < str.Len(); n++ ) {
-    switch ( str[n] ) {
-      case '\n':
-        c = 'n';
+    switch ( str[n].GetValue() ) {
+      case wxT('\n'):
+        c = wxT('n');
         break;
 
         break;
 
-      case '\r':
-        c = 'r';
+      case wxT('\r'):
+        c = wxT('r');
         break;
 
         break;
 
-      case '\t':
-        c = 't';
+      case wxT('\t'):
+        c = wxT('t');
         break;
 
         break;
 
-      case '\\':
-        c = '\\';
+      case wxT('\\'):
+        c = wxT('\\');
         break;
 
         break;
 
-      case '"':
+      case wxT('"'):
         if ( bQuote ) {
         if ( bQuote ) {
-          c = '"';
+          c = wxT('"');
           break;
         }
         //else: fall through
           break;
         }
         //else: fall through
@@ -1370,11 +2069,72 @@ wxString FilterOut(const wxString& str)
     }
 
     // we get here only for special characters
     }
 
     // we get here only for special characters
-    strResult << '\\' << c;
+    strResult << wxT('\\') << c;
   }
 
   if ( bQuote )
   }
 
   if ( bQuote )
-    strResult += '"';
+    strResult += wxT('"');
+
+  return strResult;
+}
+
+// undo FilterOutEntryName
+static wxString FilterInEntryName(const wxString& str)
+{
+  wxString strResult;
+  strResult.Alloc(str.Len());
+
+  for ( const wxChar *pc = str.c_str(); *pc != '\0'; pc++ ) {
+    if ( *pc == wxT('\\') ) {
+      // we need to test it here or we'd skip past the NUL in the loop line
+      if ( *++pc == wxT('\0') )
+        break;
+    }
+
+    strResult += *pc;
+  }
 
   return strResult;
 }
 
   return strResult;
 }
+
+// sanitize entry or group name: insert '\\' before any special characters
+static wxString FilterOutEntryName(const wxString& str)
+{
+  wxString strResult;
+  strResult.Alloc(str.Len());
+
+  for ( const wxChar *pc = str.c_str(); *pc != wxT('\0'); pc++ ) {
+    const wxChar c = *pc;
+
+    // we explicitly allow some of "safe" chars and 8bit ASCII characters
+    // which will probably never have special meaning and with which we can't
+    // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
+    //
+    // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
+    //     should *not* be quoted
+    if (
+#if !wxUSE_UNICODE
+            ((unsigned char)c < 127) &&
+#endif // ANSI
+         !wxIsalnum(c) && !wxStrchr(wxT("@_/-!.*%()"), c) )
+    {
+      strResult += wxT('\\');
+    }
+
+    strResult += c;
+  }
+
+  return strResult;
+}
+
+// we can't put ?: in the ctor initializer list because it confuses some
+// broken compilers (Borland C++)
+static wxString GetAppName(const wxString& appName)
+{
+    if ( !appName && wxTheApp )
+        return wxTheApp->GetAppName();
+    else
+        return appName;
+}
+
+#endif // wxUSE_CONFIG