]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/fileconf.cpp
added vendor display name (for consistency with app display name &c) (patch 1831303)
[wxWidgets.git] / src / common / fileconf.cpp
index 1503ece182d4b439820cd78570c3c5576298dbe7..09a66cf6b1bd96bb36cd85dd13463caf20a31d20 100644 (file)
@@ -42,6 +42,8 @@
 #include  "wx/fileconf.h"
 #include  "wx/filefn.h"
 
+#include "wx/base64.h"
+
 #include  "wx/stdpaths.h"
 
 #if defined(__WXMAC__)
@@ -222,12 +224,12 @@ public:
   bool  IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
 
   // find entry/subgroup (NULL if not found)
-  wxFileConfigGroup *FindSubgroup(const wxChar *szName) const;
-  wxFileConfigEntry *FindEntry   (const wxChar *szName) const;
+  wxFileConfigGroup *FindSubgroup(const wxString& name) const;
+  wxFileConfigEntry *FindEntry   (const wxString& name) const;
 
   // delete entry/subgroup, return false if doesn't exist
-  bool DeleteSubgroupByName(const wxChar *szName);
-  bool DeleteEntry(const wxChar *szName);
+  bool DeleteSubgroupByName(const wxString& name);
+  bool DeleteEntry(const wxString& name);
 
   // create new entry/subgroup returning pointer to newly created element
   wxFileConfigGroup *AddSubgroup(const wxString& strName);
@@ -243,6 +245,7 @@ public:
 
   // 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
 
@@ -344,7 +347,7 @@ void wxFileConfig::Init()
     // It's not an error if (one of the) file(s) doesn't exist.
 
     // parse the global file
-    if ( m_fnGlobalFile.FileExists() )
+    if ( m_fnGlobalFile.IsOk() && m_fnGlobalFile.FileExists() )
     {
         wxTextFile fileGlobal(m_fnGlobalFile.GetFullPath());
 
@@ -360,7 +363,7 @@ void wxFileConfig::Init()
     }
 
     // parse the local file
-    if ( m_fnLocalFile.FileExists() )
+    if ( m_fnLocalFile.IsOk() && m_fnLocalFile.FileExists() )
     {
         wxTextFile fileLocal(m_fnLocalFile.GetFullPath());
         if ( fileLocal.Open(*m_conv/*ignored in ANSI build*/) )
@@ -370,7 +373,16 @@ void wxFileConfig::Init()
         }
         else
         {
-            wxLogWarning(_("can't open user configuration file '%s'."),  m_fnLocalFile.GetFullPath().c_str() );
+            const wxString path = m_fnLocalFile.GetFullPath();
+            wxLogWarning(_("can't open user configuration file '%s'."),
+                         path.c_str());
+
+            if ( m_fnLocalFile.FileExists() )
+            {
+                wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
+                             path.c_str());
+                m_fnLocalFile.Clear();
+            }
         }
     }
 
@@ -435,71 +447,61 @@ wxFileConfig::wxFileConfig(wxInputStream &inStream, const wxMBConv& conv)
     m_linesTail = NULL;
 
     // read the entire stream contents in memory
-    wxString str;
-    {
-        static const size_t chunkLen = 1024;
+    wxWxCharBuffer cbuf;
+    static const size_t chunkLen = 1024;
 
-        wxMemoryBuffer buf(chunkLen);
-        do
-        {
-            inStream.Read(buf.GetAppendBuf(chunkLen), chunkLen);
-            buf.UngetAppendBuf(inStream.LastRead());
+    wxMemoryBuffer buf(chunkLen);
+    do
+    {
+        inStream.Read(buf.GetAppendBuf(chunkLen), chunkLen);
+        buf.UngetAppendBuf(inStream.LastRead());
 
-            const wxStreamError err = inStream.GetLastError();
+        const wxStreamError err = inStream.GetLastError();
 
-            if ( err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF )
-            {
-                wxLogError(_("Error reading config options."));
-                break;
-            }
+        if ( err != wxSTREAM_NO_ERROR && err != wxSTREAM_EOF )
+        {
+            wxLogError(_("Error reading config options."));
+            break;
         }
-        while ( !inStream.Eof() );
+    }
+    while ( !inStream.Eof() );
 
 #if wxUSE_UNICODE
-        size_t len;
-        str = conv.cMB2WC((char *)buf.GetData(), buf.GetDataLen(), &len);
-        if ( !len && buf.GetDataLen() )
-        {
-            wxLogError(_("Failed to read config options."));
-        }
+    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
-        str.assign((char *)buf.GetData(), buf.GetDataLen());
+    // no need for conversion
+    cbuf = wxCharBuffer::CreateNonOwned((char *)buf.GetData());
 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
-    }
-
 
-    // translate everything to the current (platform-dependent) line
-    // termination character
-    str = wxTextBuffer::Translate(str);
 
+    // now break it into lines
     wxMemoryText memText;
-
-    // Now we can add the text to the memory text. To do this we extract line
-    // by line from the translated string, until we've reached the end.
-    //
-    // VZ: all this is horribly inefficient, we should do the translation on
-    //     the fly in one pass saving both memory and time (TODO)
-
-    const wxChar *pEOL = wxTextBuffer::GetEOL(wxTextBuffer::typeDefault);
-    const size_t EOLLen = wxStrlen(pEOL);
-
-    int posLineStart = str.Find(pEOL);
-    while ( posLineStart != -1 )
+    for ( const wxChar *s = cbuf; ; ++s )
     {
-        wxString line(str.Left(posLineStart));
+        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));
 
-        memText.AddLine(line);
+        if ( *e == '\0' )
+            break;
 
-        str = str.Mid(posLineStart + EOLLen);
+        // skip the second EOL byte if it's a DOS one
+        if ( *e == '\r' && e[1] == '\n' )
+            ++e;
 
-        posLineStart = str.Find(pEOL);
+        s = e;
     }
 
-    // also add whatever we have left in the translated string.
-    if ( !str.empty() )
-        memText.AddLine(str);
-
     // Finally we can parse it all.
     Parse(memText, true /* local */);
 
@@ -536,31 +538,24 @@ wxFileConfig::~wxFileConfig()
 
 void wxFileConfig::Parse(const wxTextBuffer& buffer, bool bLocal)
 {
-  const wxChar *pStart;
-  const wxChar *pEnd;
-  wxString strLine;
 
   size_t nLineCount = buffer.GetLineCount();
 
   for ( size_t n = 0; n < nLineCount; n++ )
   {
-    strLine = buffer[n];
+    wxString strLine = buffer[n];
+    // FIXME-UTF8: rewrite using iterators, without this buffer
+    wxWxCharBuffer buf(strLine.c_str());
+    const wxChar *pStart;
+    const wxChar *pEnd;
 
     // add the line to linked list
     if ( bLocal )
-    {
       LineListAppend(strLine);
 
-      // let the root group have its start line as well
-      if ( !n )
-      {
-        m_pCurrentGroup->SetLine(m_linesTail);
-      }
-    }
-
 
     // skip leading spaces
-    for ( pStart = strLine; wxIsspace(*pStart); pStart++ )
+    for ( pStart = buf; wxIsspace(*pStart); pStart++ )
       ;
 
     // skip blank/comment lines
@@ -728,7 +723,7 @@ wxFileConfig::DoSetPath(const wxString& strPath, bool createMissingComponents)
     // change current group
     size_t n;
     m_pCurrentGroup = m_pRootGroup;
-    for ( n = 0; n < aParts.Count(); n++ ) {
+    for ( n = 0; n < aParts.GetCount(); n++ ) {
         wxFileConfigGroup *pNextGroup = m_pCurrentGroup->FindSubgroup(aParts[n]);
         if ( pNextGroup == NULL )
         {
@@ -743,7 +738,7 @@ wxFileConfig::DoSetPath(const wxString& strPath, bool createMissingComponents)
 
     // recombine path parts in one variable
     m_strPath.Empty();
-    for ( n = 0; n < aParts.Count(); n++ ) {
+    for ( n = 0; n < aParts.GetCount(); n++ ) {
         m_strPath << wxCONFIG_PATH_SEPARATOR << aParts[n];
     }
 
@@ -767,7 +762,7 @@ bool wxFileConfig::GetFirstGroup(wxString& str, long& lIndex) const
 
 bool wxFileConfig::GetNextGroup (wxString& str, long& lIndex) const
 {
-    if ( size_t(lIndex) < m_pCurrentGroup->Groups().Count() ) {
+    if ( size_t(lIndex) < m_pCurrentGroup->Groups().GetCount() ) {
         str = m_pCurrentGroup->Groups()[(size_t)lIndex++]->Name();
         return true;
     }
@@ -783,7 +778,7 @@ bool wxFileConfig::GetFirstEntry(wxString& str, long& lIndex) const
 
 bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex) const
 {
-    if ( size_t(lIndex) < m_pCurrentGroup->Entries().Count() ) {
+    if ( size_t(lIndex) < m_pCurrentGroup->Entries().GetCount() ) {
         str = m_pCurrentGroup->Entries()[(size_t)lIndex++]->Name();
         return true;
     }
@@ -793,10 +788,10 @@ bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex) const
 
 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive) const
 {
-    size_t n = m_pCurrentGroup->Entries().Count();
+    size_t n = m_pCurrentGroup->Entries().GetCount();
     if ( bRecursive ) {
         wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
-        size_t nSubgroups = m_pCurrentGroup->Groups().Count();
+        size_t nSubgroups = m_pCurrentGroup->Groups().GetCount();
         for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
             CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
             n += GetNumberOfEntries(true);
@@ -809,10 +804,10 @@ size_t wxFileConfig::GetNumberOfEntries(bool bRecursive) const
 
 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
 {
-    size_t n = m_pCurrentGroup->Groups().Count();
+    size_t n = m_pCurrentGroup->Groups().GetCount();
     if ( bRecursive ) {
         wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
-        size_t nSubgroups = m_pCurrentGroup->Groups().Count();
+        size_t nSubgroups = m_pCurrentGroup->Groups().GetCount();
         for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
             CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
             n += GetNumberOfGroups(true);
@@ -915,6 +910,22 @@ bool wxFileConfig::DoReadLong(const wxString& key, long *pl) const
     return str.ToLong(pl);
 }
 
+#if wxUSE_BASE64
+
+bool wxFileConfig::DoReadBinary(const wxString& key, wxMemoryBuffer* buf) const
+{
+    wxCHECK_MSG( buf, false, _T("NULL buffer") );
+
+    wxString str;
+    if ( !Read(key, &str) )
+        return false;
+
+    *buf = wxBase64Decode(str);
+    return true;
+}
+
+#endif // wxUSE_BASE64
+
 bool wxFileConfig::DoWriteString(const wxString& key, const wxString& szValue)
 {
     wxConfigPathChanger     path(this, key);
@@ -940,8 +951,8 @@ bool wxFileConfig::DoWriteString(const wxString& key, const wxString& szValue)
 
         SetDirty();
 
-            // this will add a line for this group if it didn't have it before
-
+        // this will add a line for this group if it didn't have it before (or
+        // do nothing for the root but it's ok as it always exists anyhow)
         (void)m_pCurrentGroup->GetGroupLine();
     }
     else
@@ -980,6 +991,15 @@ bool wxFileConfig::DoWriteLong(const wxString& key, long lValue)
   return Write(key, wxString::Format(_T("%ld"), lValue));
 }
 
+#if wxUSE_BASE64
+
+bool wxFileConfig::DoWriteBinary(const wxString& key, const wxMemoryBuffer& buf)
+{
+  return Write(key, wxBase64Encode(buf));
+}
+
+#endif // wxUSE_BASE64
+
 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
 {
   if ( !IsDirty() || !m_fnLocalFile.GetFullPath() )
@@ -1059,7 +1079,7 @@ bool wxFileConfig::Save(wxOutputStream& os, const wxMBConv& conv)
 bool wxFileConfig::RenameEntry(const wxString& oldName,
                                const wxString& newName)
 {
-    wxASSERT_MSG( !wxStrchr(oldName, wxCONFIG_PATH_SEPARATOR),
+    wxASSERT_MSG( oldName.find(wxCONFIG_PATH_SEPARATOR) == wxString::npos,
                    _T("RenameEntry(): paths are not supported") );
 
     // check that the entry exists
@@ -1148,7 +1168,8 @@ bool wxFileConfig::DeleteAll()
 
   if ( m_fnLocalFile.IsOk() )
   {
-      if ( m_fnLocalFile.FileExists() && wxRemove(m_fnLocalFile.GetFullPath()) == -1 )
+      if ( m_fnLocalFile.FileExists() &&
+           !wxRemoveFile(m_fnLocalFile.GetFullPath()) )
       {
           wxLogSysError(_("can't delete user configuration file '%s'"),
                         m_fnLocalFile.GetFullPath().c_str());
@@ -1291,9 +1312,6 @@ void wxFileConfig::LineListRemove(wxFileConfigLineList *pLine)
     else
         pNext->SetPrev(pPrev);
 
-    if ( m_pRootGroup->GetGroupLine() == pLine )
-        m_pRootGroup->SetLine(m_linesHead);
-
     wxLogTrace( FILECONF_TRACE_MASK,
                 _T("        head: %s"),
                 ((m_linesHead) ? (const wxChar*)m_linesHead->Text().c_str()
@@ -1339,12 +1357,12 @@ wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup *pParent,
 wxFileConfigGroup::~wxFileConfigGroup()
 {
   // entries
-  size_t n, nCount = m_aEntries.Count();
+  size_t n, nCount = m_aEntries.GetCount();
   for ( n = 0; n < nCount; n++ )
     delete m_aEntries[n];
 
   // subgroups
-  nCount = m_aSubgroups.Count();
+  nCount = m_aSubgroups.GetCount();
   for ( n = 0; n < nCount; n++ )
     delete m_aSubgroups[n];
 }
@@ -1507,7 +1525,7 @@ void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
 
 
     // also update all subgroups as they have this groups name in their lines
-    const size_t nCount = m_aSubgroups.Count();
+    const size_t nCount = m_aSubgroups.GetCount();
     for ( size_t n = 0; n < nCount; n++ )
     {
         m_aSubgroups[n]->UpdateGroupAndSubgroupsLines();
@@ -1548,11 +1566,11 @@ wxString wxFileConfigGroup::GetFullName() const
 
 // use binary search because the array is sorted
 wxFileConfigEntry *
-wxFileConfigGroup::FindEntry(const wxChar *szName) const
+wxFileConfigGroup::FindEntry(const wxString& name) const
 {
   size_t i,
        lo = 0,
-       hi = m_aEntries.Count();
+       hi = m_aEntries.GetCount();
   int res;
   wxFileConfigEntry *pEntry;
 
@@ -1561,9 +1579,9 @@ wxFileConfigGroup::FindEntry(const wxChar *szName) const
     pEntry = m_aEntries[i];
 
     #if wxCONFIG_CASE_SENSITIVE
-      res = wxStrcmp(pEntry->Name(), szName);
+      res = pEntry->Name().compare(name);
     #else
-      res = wxStricmp(pEntry->Name(), szName);
+      res = pEntry->Name().CmpNoCase(name);
     #endif
 
     if ( res > 0 )
@@ -1578,11 +1596,11 @@ wxFileConfigGroup::FindEntry(const wxChar *szName) const
 }
 
 wxFileConfigGroup *
-wxFileConfigGroup::FindSubgroup(const wxChar *szName) const
+wxFileConfigGroup::FindSubgroup(const wxString& name) const
 {
   size_t i,
        lo = 0,
-       hi = m_aSubgroups.Count();
+       hi = m_aSubgroups.GetCount();
   int res;
   wxFileConfigGroup *pGroup;
 
@@ -1591,9 +1609,9 @@ wxFileConfigGroup::FindSubgroup(const wxChar *szName) const
     pGroup = m_aSubgroups[i];
 
     #if wxCONFIG_CASE_SENSITIVE
-      res = wxStrcmp(pGroup->Name(), szName);
+      res = pGroup->Name().compare(name);
     #else
-      res = wxStricmp(pGroup->Name(), szName);
+      res = pGroup->Name().CmpNoCase(name);
     #endif
 
     if ( res > 0 )
@@ -1644,9 +1662,9 @@ wxFileConfigGroup *wxFileConfigGroup::AddSubgroup(const wxString& strName)
   delete several of them.
  */
 
-bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar *szName)
+bool wxFileConfigGroup::DeleteSubgroupByName(const wxString& name)
 {
-    wxFileConfigGroup * const pGroup = FindSubgroup(szName);
+    wxFileConfigGroup * const pGroup = FindSubgroup(name);
 
     return pGroup ? DeleteSubgroup(pGroup) : false;
 }
@@ -1673,7 +1691,7 @@ bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
                         : wxEmptyString );
 
     // delete all entries...
-    size_t nCount = pGroup->m_aEntries.Count();
+    size_t nCount = pGroup->m_aEntries.GetCount();
 
     wxLogTrace(FILECONF_TRACE_MASK,
                _T("Removing %lu entries"), (unsigned long)nCount );
@@ -1692,7 +1710,7 @@ bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
     }
 
     // ...and subgroups of this subgroup
-    nCount = pGroup->m_aSubgroups.Count();
+    nCount = pGroup->m_aSubgroups.GetCount();
 
     wxLogTrace( FILECONF_TRACE_MASK,
                 _T("Removing %lu subgroups"), (unsigned long)nCount );
@@ -1726,11 +1744,11 @@ bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
             // our last entry is being deleted, so find the last one which
             // stays by going back until we find a subgroup or reach the
             // group line
-            const size_t nSubgroups = m_aSubgroups.Count();
+            const size_t nSubgroups = m_aSubgroups.GetCount();
 
             m_pLastGroup = NULL;
             for ( wxFileConfigLineList *pl = pLine->Prev();
-                  pl && pl != m_pLine && !m_pLastGroup;
+                  pl && !m_pLastGroup;
                   pl = pl->Prev() )
             {
                 // does this line belong to our subgroup?
@@ -1744,6 +1762,9 @@ bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
                         break;
                     }
                 }
+
+                if ( pl == m_pLine )
+                    break;
             }
         }
 
@@ -1762,9 +1783,9 @@ bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
     return true;
 }
 
-bool wxFileConfigGroup::DeleteEntry(const wxChar *szName)
+bool wxFileConfigGroup::DeleteEntry(const wxString& name)
 {
-  wxFileConfigEntry *pEntry = FindEntry(szName);
+  wxFileConfigEntry *pEntry = FindEntry(name);
   if ( !pEntry )
   {
       // entry doesn't exist, nothing to do
@@ -1779,29 +1800,21 @@ bool wxFileConfigGroup::DeleteEntry(const wxChar *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...
 
-      // go back until we find another entry or reach the group's line
+      // find the previous entry (if any)
       wxFileConfigEntry *pNewLast = NULL;
-      size_t n, nEntries = m_aEntries.Count();
-      wxFileConfigLineList *pl;
-      for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() ) {
-        // is it our subgroup?
-        for ( n = 0; (pNewLast == NULL) && (n < nEntries); n++ ) {
-          if ( m_aEntries[n]->GetLine() == m_pLine )
-            pNewLast = m_aEntries[n];
-        }
-
-        if ( pNewLast != NULL ) // found?
+      const wxFileConfigLineList * const
+        pNewLastLine = m_pLastEntry->GetLine()->Prev();
+      const size_t nEntries = m_aEntries.GetCount();
+      for ( size_t n = 0; n < nEntries; n++ ) {
+        if ( m_aEntries[n]->GetLine() == pNewLastLine ) {
+          pNewLast = m_aEntries[n];
           break;
+        }
       }
 
-      if ( pl == m_pLine ) {
-        wxASSERT( !pNewLast );  // how comes it has the same line as we?
-
-        // we've reached the group line without finding any subgroups
-        m_pLastEntry = NULL;
-      }
-      else
-        m_pLastEntry = pNewLast;
+      // pNewLast can be NULL here -- it's ok and can happen if we have no
+      // entries left
+      m_pLastEntry = pNewLast;
     }
 
     m_pConfig->LineListRemove(pLine);
@@ -1917,18 +1930,18 @@ void wxFileConfigEntry::SetValue(const wxString& strValue, bool bUser)
 int CompareEntries(wxFileConfigEntry *p1, wxFileConfigEntry *p2)
 {
 #if wxCONFIG_CASE_SENSITIVE
-    return wxStrcmp(p1->Name(), p2->Name());
+    return p1->Name().compare(p2->Name());
 #else
-    return wxStricmp(p1->Name(), p2->Name());
+    return p1->Name().CmpNoCase(p2->Name());
 #endif
 }
 
 int CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2)
 {
 #if wxCONFIG_CASE_SENSITIVE
-    return wxStrcmp(p1->Name(), p2->Name());
+    return p1->Name().compare(p2->Name());
 #else
-    return wxStricmp(p1->Name(), p2->Name());
+    return p1->Name().CmpNoCase(p2->Name());
 #endif
 }
 
@@ -1939,47 +1952,66 @@ int CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2)
 // undo FilterOutValue
 static wxString FilterInValue(const wxString& str)
 {
-  wxString strResult;
-  strResult.Alloc(str.Len());
+    wxString strResult;
+    if ( str.empty() )
+        return strResult;
 
-  bool bQuoted = !str.empty() && str[0] == '"';
+    strResult.reserve(str.length());
 
-  for ( size_t n = bQuoted ? 1 : 0; n < str.Len(); n++ ) {
-    if ( str[n] == wxT('\\') ) {
-      switch ( str[++n].GetValue() ) {
-        case wxT('n'):
-          strResult += wxT('\n');
-          break;
+    wxString::const_iterator i = str.begin();
+    const bool bQuoted = *i == '"';
+    if ( bQuoted )
+        ++i;
 
-        case wxT('r'):
-          strResult += wxT('\r');
-          break;
+    for ( const wxString::const_iterator end = str.end(); i != end; ++i )
+    {
+        if ( *i == wxT('\\') )
+        {
+            if ( ++i == end )
+            {
+                wxLogWarning(_("trailing backslash ignored in '%s'"), str.c_str());
+                break;
+            }
 
-        case wxT('t'):
-          strResult += wxT('\t');
-          break;
+            switch ( (*i).GetValue() )
+            {
+                case wxT('n'):
+                    strResult += wxT('\n');
+                    break;
 
-        case wxT('\\'):
-          strResult += wxT('\\');
-          break;
+                case wxT('r'):
+                    strResult += wxT('\r');
+                    break;
 
-        case wxT('"'):
-          strResult += wxT('"');
-          break;
-      }
-    }
-    else {
-      if ( str[n] != wxT('"') || !bQuoted )
-        strResult += str[n];
-      else if ( n != str.Len() - 1 ) {
-        wxLogWarning(_("unexpected \" at position %d in '%s'."),
-                     n, str.c_str());
-      }
-      //else: it's the last quote of a quoted string, ok
+                case wxT('t'):
+                    strResult += wxT('\t');
+                    break;
+
+                case wxT('\\'):
+                    strResult += wxT('\\');
+                    break;
+
+                case wxT('"'):
+                    strResult += wxT('"');
+                    break;
+            }
+        }
+        else // not a backslash
+        {
+            if ( *i != wxT('"') || !bQuoted )
+            {
+                strResult += *i;
+            }
+            else if ( i != end - 1 )
+            {
+                wxLogWarning(_("unexpected \" at position %d in '%s'."),
+                             i - str.begin(), str.c_str());
+            }
+            //else: it's the last quote of a quoted string, ok
+        }
     }
-  }
 
-  return strResult;
+    return strResult;
 }
 
 // quote the string before writing it to file