home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / common / fileconf.cpp < prev    next >
C/C++ Source or Header  |  2002-11-04  |  61KB  |  2,005 lines

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Name:        fileconf.cpp
  3. // Purpose:     implementation of wxFileConfig derivation of wxConfig
  4. // Author:      Vadim Zeitlin
  5. // Modified by:
  6. // Created:     07.04.98 (adapted from appconf.cpp)
  7. // RCS-ID:      $Id: fileconf.cpp,v 1.90.2.5 2002/10/29 21:47:44 RR Exp $
  8. // Copyright:   (c) 1997 Karsten Ballⁿder   &  Vadim Zeitlin
  9. //                       Ballueder@usa.net     <zeitlin@dptmaths.ens-cachan.fr>
  10. // Licence:     wxWindows license
  11. ///////////////////////////////////////////////////////////////////////////////
  12.  
  13. #ifdef __GNUG__
  14. #pragma implementation "fileconf.h"
  15. #endif
  16.  
  17. // ----------------------------------------------------------------------------
  18. // headers
  19. // ----------------------------------------------------------------------------
  20.  
  21. #include  "wx/wxprec.h"
  22.  
  23. #ifdef    __BORLANDC__
  24.   #pragma hdrstop
  25. #endif  //__BORLANDC__
  26.  
  27. #if wxUSE_CONFIG
  28.  
  29. #ifndef   WX_PRECOMP
  30.   #include  "wx/string.h"
  31.   #include  "wx/intl.h"
  32. #endif  //WX_PRECOMP
  33.  
  34. #include  "wx/app.h"
  35. #include  "wx/dynarray.h"
  36. #include  "wx/file.h"
  37. #include  "wx/log.h"
  38. #include  "wx/textfile.h"
  39. #include  "wx/memtext.h"
  40. #include  "wx/config.h"
  41. #include  "wx/fileconf.h"
  42.  
  43. #if wxUSE_STREAMS
  44.     #include  "wx/stream.h"
  45. #endif // wxUSE_STREAMS
  46.  
  47. #include  "wx/utils.h"    // for wxGetHomeDir
  48.  
  49. #if defined(__WXMAC__)
  50.   #include  "wx/mac/private.h"  // includes mac headers
  51. #endif
  52.  
  53. #if defined(__WXMSW__)
  54.   #include "wx/msw/private.h"
  55. #endif  //windows.h
  56. #if defined(__WXPM__)
  57.   #define INCL_DOS
  58.   #include <os2.h>
  59. #endif
  60.  
  61. #include  <stdlib.h>
  62. #include  <ctype.h>
  63.  
  64. // headers needed for umask()
  65. #ifdef __UNIX__
  66.     #include <sys/types.h>
  67.     #include <sys/stat.h>
  68. #endif // __UNIX__
  69.  
  70. // ----------------------------------------------------------------------------
  71. // macros
  72. // ----------------------------------------------------------------------------
  73. #define CONST_CAST ((wxFileConfig *)this)->
  74.  
  75. // ----------------------------------------------------------------------------
  76. // constants
  77. // ----------------------------------------------------------------------------
  78.  
  79. #ifndef MAX_PATH
  80.   #define MAX_PATH 512
  81. #endif
  82.  
  83. // ----------------------------------------------------------------------------
  84. // global functions declarations
  85. // ----------------------------------------------------------------------------
  86.  
  87. // compare functions for sorting the arrays
  88. static int LINKAGEMODE CompareEntries(wxFileConfigEntry *p1, wxFileConfigEntry *p2);
  89. static int LINKAGEMODE CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2);
  90.  
  91. // filter strings
  92. static wxString FilterInValue(const wxString& str);
  93. static wxString FilterOutValue(const wxString& str);
  94.  
  95. static wxString FilterInEntryName(const wxString& str);
  96. static wxString FilterOutEntryName(const wxString& str);
  97.  
  98. // get the name to use in wxFileConfig ctor
  99. static wxString GetAppName(const wxString& appname);
  100.  
  101. // ============================================================================
  102. // private classes
  103. // ============================================================================
  104.  
  105. // ----------------------------------------------------------------------------
  106. // "template" array types
  107. // ----------------------------------------------------------------------------
  108.  
  109. WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigEntry *, ArrayEntries);
  110. WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigGroup *, ArrayGroups);
  111.  
  112. // ----------------------------------------------------------------------------
  113. // wxFileConfigLineList
  114. // ----------------------------------------------------------------------------
  115.  
  116. // we store all lines of the local config file as a linked list in memory
  117. class wxFileConfigLineList
  118. {
  119. public:
  120.   void SetNext(wxFileConfigLineList *pNext)  { m_pNext = pNext; }
  121.   void SetPrev(wxFileConfigLineList *pPrev)  { m_pPrev = pPrev; }
  122.  
  123.   // ctor
  124.   wxFileConfigLineList(const wxString& str,
  125.                        wxFileConfigLineList *pNext = NULL) : m_strLine(str)
  126.     { SetNext(pNext); SetPrev(NULL); }
  127.  
  128.   // next/prev nodes in the linked list
  129.   wxFileConfigLineList *Next() const { return m_pNext;  }
  130.   wxFileConfigLineList *Prev() const { return m_pPrev;  }
  131.  
  132.   // get/change lines text
  133.   void SetText(const wxString& str) { m_strLine = str;  }
  134.   const wxString& Text() const { return m_strLine; }
  135.  
  136. private:
  137.   wxString  m_strLine;                  // line contents
  138.   wxFileConfigLineList *m_pNext,        // next node
  139.                        *m_pPrev;        // previous one
  140. };
  141.  
  142. // ----------------------------------------------------------------------------
  143. // wxFileConfigEntry: a name/value pair
  144. // ----------------------------------------------------------------------------
  145.  
  146. class wxFileConfigEntry
  147. {
  148. private:
  149.   wxFileConfigGroup *m_pParent; // group that contains us
  150.  
  151.   wxString      m_strName,      // entry name
  152.                 m_strValue;     //       value
  153.   bool          m_bDirty:1,     // changed since last read?
  154.                 m_bImmutable:1, // can be overriden locally?
  155.                 m_bHasValue:1;  // set after first call to SetValue()
  156.  
  157.   int           m_nLine;        // used if m_pLine == NULL only
  158.  
  159.   // pointer to our line in the linked list or NULL if it was found in global
  160.   // file (which we don't modify)
  161.   wxFileConfigLineList *m_pLine;
  162.  
  163. public:
  164.   wxFileConfigEntry(wxFileConfigGroup *pParent,
  165.                     const wxString& strName, int nLine);
  166.  
  167.   // simple accessors
  168.   const wxString& Name()        const { return m_strName;    }
  169.   const wxString& Value()       const { return m_strValue;   }
  170.   wxFileConfigGroup *Group()    const { return m_pParent;    }
  171.   bool            IsDirty()     const { return m_bDirty;     }
  172.   bool            IsImmutable() const { return m_bImmutable; }
  173.   bool            IsLocal()     const { return m_pLine != 0; }
  174.   int             Line()        const { return m_nLine;      }
  175.   wxFileConfigLineList *
  176.                   GetLine()     const { return m_pLine;      }
  177.  
  178.   // modify entry attributes
  179.   void SetValue(const wxString& strValue, bool bUser = TRUE);
  180.   void SetDirty();
  181.   void SetLine(wxFileConfigLineList *pLine);
  182. };
  183.  
  184. // ----------------------------------------------------------------------------
  185. // wxFileConfigGroup: container of entries and other groups
  186. // ----------------------------------------------------------------------------
  187.  
  188. class wxFileConfigGroup
  189. {
  190. private:
  191.   wxFileConfig *m_pConfig;          // config object we belong to
  192.   wxFileConfigGroup  *m_pParent;    // parent group (NULL for root group)
  193.   ArrayEntries  m_aEntries;         // entries in this group
  194.   ArrayGroups   m_aSubgroups;       // subgroups
  195.   wxString      m_strName;          // group's name
  196.   bool          m_bDirty;           // if FALSE => all subgroups are not dirty
  197.   wxFileConfigLineList *m_pLine;    // pointer to our line in the linked list
  198.   wxFileConfigEntry *m_pLastEntry;  // last entry/subgroup of this group in the
  199.   wxFileConfigGroup *m_pLastGroup;  // local file (we insert new ones after it)
  200.  
  201.   // DeleteSubgroupByName helper
  202.   bool DeleteSubgroup(wxFileConfigGroup *pGroup);
  203.  
  204. public:
  205.   // ctor
  206.   wxFileConfigGroup(wxFileConfigGroup *pParent, const wxString& strName, wxFileConfig *);
  207.  
  208.   // dtor deletes all entries and subgroups also
  209.   ~wxFileConfigGroup();
  210.  
  211.   // simple accessors
  212.   const wxString& Name()    const { return m_strName; }
  213.   wxFileConfigGroup    *Parent()  const { return m_pParent; }
  214.   wxFileConfig   *Config()  const { return m_pConfig; }
  215.   bool            IsDirty() const { return m_bDirty;  }
  216.  
  217.   const ArrayEntries& Entries() const { return m_aEntries;   }
  218.   const ArrayGroups&  Groups()  const { return m_aSubgroups; }
  219.   bool  IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
  220.  
  221.   // find entry/subgroup (NULL if not found)
  222.   wxFileConfigGroup *FindSubgroup(const wxChar *szName) const;
  223.   wxFileConfigEntry *FindEntry   (const wxChar *szName) const;
  224.  
  225.   // delete entry/subgroup, return FALSE if doesn't exist
  226.   bool DeleteSubgroupByName(const wxChar *szName);
  227.   bool DeleteEntry(const wxChar *szName);
  228.  
  229.   // create new entry/subgroup returning pointer to newly created element
  230.   wxFileConfigGroup *AddSubgroup(const wxString& strName);
  231.   wxFileConfigEntry *AddEntry   (const wxString& strName, int nLine = wxNOT_FOUND);
  232.  
  233.   // will also recursively set parent's dirty flag
  234.   void SetDirty();
  235.   void SetLine(wxFileConfigLineList *pLine);
  236.  
  237.   // rename: no checks are done to ensure that the name is unique!
  238.   void Rename(const wxString& newName);
  239.  
  240.   //
  241.   wxString GetFullName() const;
  242.  
  243.   // get the last line belonging to an entry/subgroup of this group
  244.   wxFileConfigLineList *GetGroupLine();     // line which contains [group]
  245.   wxFileConfigLineList *GetLastEntryLine(); // after which our subgroups start
  246.   wxFileConfigLineList *GetLastGroupLine(); // after which the next group starts
  247.  
  248.   // called by entries/subgroups when they're created/deleted
  249.   void SetLastEntry(wxFileConfigEntry *pEntry) { m_pLastEntry = pEntry; }
  250.   void SetLastGroup(wxFileConfigGroup *pGroup) { m_pLastGroup = pGroup; }
  251. };
  252.  
  253. // ============================================================================
  254. // implementation
  255. // ============================================================================
  256.  
  257. // ----------------------------------------------------------------------------
  258. // static functions
  259. // ----------------------------------------------------------------------------
  260. wxString wxFileConfig::GetGlobalDir()
  261. {
  262.   wxString strDir;
  263.  
  264. #ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined
  265.     strDir = wxT("sys$manager:");
  266. #elif defined(__WXMAC__)
  267.     strDir = wxMacFindFolder(  (short) kOnSystemDisk, kPreferencesFolderType, kDontCreateFolder ) ;
  268. #elif defined( __UNIX__ )
  269.     strDir = wxT("/etc/");
  270. #elif defined(__WXPM__)
  271.     ULONG aulSysInfo[QSV_MAX] = {0};
  272.     UINT drive;
  273.     APIRET rc;
  274.  
  275.     rc = DosQuerySysInfo( 1L, QSV_MAX, (PVOID)aulSysInfo, sizeof(ULONG)*QSV_MAX);
  276.     if (rc == 0)
  277.     {
  278.         drive = aulSysInfo[QSV_BOOT_DRIVE - 1];
  279.         strDir.Printf(wxT("%c:\\OS2\\"), 'A'+drive-1);
  280.     }
  281. #elif defined(__WXSTUBS__)
  282.     wxASSERT_MSG( FALSE, wxT("TODO") ) ;
  283. #elif defined(__DOS__)
  284.     // There's no such thing as global cfg dir in MS-DOS, let's return
  285.     // current directory (FIXME_MGL?)
  286.     return wxT(".\\");
  287. #else // Windows
  288.     wxChar szWinDir[MAX_PATH];
  289.     ::GetWindowsDirectory(szWinDir, MAX_PATH);
  290.  
  291.     strDir = szWinDir;
  292.     strDir << wxT('\\');
  293. #endif // Unix/Windows
  294.  
  295.     return strDir;
  296. }
  297.  
  298. wxString wxFileConfig::GetLocalDir()
  299. {
  300.     wxString strDir;
  301.  
  302. #if defined(__WXMAC__) || defined(__DOS__)
  303.     // no local dir concept on Mac OS 9 or MS-DOS
  304.     return GetGlobalDir() ;
  305. #else
  306.     wxGetHomeDir(&strDir);
  307.  
  308. #ifdef  __UNIX__
  309. #ifdef __VMS
  310.     if (strDir.Last() != wxT(']'))
  311. #endif
  312.     if (strDir.Last() != wxT('/')) strDir << wxT('/');
  313. #else
  314.     if (strDir.Last() != wxT('\\')) strDir << wxT('\\');
  315. #endif
  316. #endif
  317.  
  318.     return strDir;
  319. }
  320.  
  321. wxString wxFileConfig::GetGlobalFileName(const wxChar *szFile)
  322. {
  323.     wxString str = GetGlobalDir();
  324.     str << szFile;
  325.  
  326.     if ( wxStrchr(szFile, wxT('.')) == NULL )
  327. #if defined( __WXMAC__ )
  328.         str << " Preferences";
  329. #elif defined( __UNIX__ )
  330.         str << wxT(".conf");
  331. #else   // Windows
  332.         str << wxT(".ini");
  333. #endif  // UNIX/Win
  334.  
  335.     return str;
  336. }
  337.  
  338. wxString wxFileConfig::GetLocalFileName(const wxChar *szFile)
  339. {
  340. #ifdef __VMS__ 
  341.     // On VMS I saw the problem that the home directory was appended
  342.     // twice for the configuration file. Does that also happen for
  343.     // other platforms?
  344.     wxString str = wxT( '.' );
  345. #else
  346.     wxString str = GetLocalDir();
  347. #endif
  348.  
  349. #if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ )
  350.     str << wxT('.');
  351. #endif
  352.  
  353.     str << szFile;
  354.  
  355. #if defined(__WINDOWS__) || defined(__DOS__)
  356.     if ( wxStrchr(szFile, wxT('.')) == NULL )
  357.         str << wxT(".ini");
  358. #endif
  359.  
  360. #ifdef __WXMAC__
  361.     str << " Preferences";
  362. #endif
  363.  
  364.     return str;
  365. }
  366.  
  367. // ----------------------------------------------------------------------------
  368. // ctor
  369. // ----------------------------------------------------------------------------
  370.  
  371. void wxFileConfig::Init()
  372. {
  373.     m_pCurrentGroup =
  374.     m_pRootGroup    = new wxFileConfigGroup(NULL, wxT(""), this);
  375.  
  376.     m_linesHead =
  377.     m_linesTail = NULL;
  378.  
  379.     // It's not an error if (one of the) file(s) doesn't exist.
  380.  
  381.     // parse the global file
  382.     if ( !m_strGlobalFile.IsEmpty() && wxFile::Exists(m_strGlobalFile) )
  383.     {
  384.         wxTextFile fileGlobal(m_strGlobalFile);
  385.  
  386. #if defined(__WXGTK20__) && wxUSE_UNICODE
  387.         if ( fileGlobal.Open( wxConvUTF8 ) ) 
  388. #else
  389.         if ( fileGlobal.Open() ) 
  390. #endif
  391.         {
  392.             Parse(fileGlobal, FALSE /* global */);
  393.             SetRootPath();
  394.         }
  395.         else
  396.         {
  397.             wxLogWarning(_("can't open global configuration file '%s'."), m_strGlobalFile.c_str());
  398.         }
  399.     }
  400.  
  401.     // parse the local file
  402.     if ( !m_strLocalFile.IsEmpty() && wxFile::Exists(m_strLocalFile) )
  403.     {
  404.         wxTextFile fileLocal(m_strLocalFile);
  405. #if defined(__WXGTK20__) && wxUSE_UNICODE
  406.         if ( fileLocal.Open( wxConvUTF8 ) ) 
  407. #else
  408.         if ( fileLocal.Open() ) 
  409. #endif
  410.         {
  411.             Parse(fileLocal, TRUE /* local */);
  412.             SetRootPath();
  413.         }
  414.         else
  415.         {
  416.             wxLogWarning(_("can't open user configuration file '%s'."),  m_strLocalFile.c_str() );
  417.         }
  418.     }
  419. }
  420.  
  421. // constructor supports creation of wxFileConfig objects of any type
  422. wxFileConfig::wxFileConfig(const wxString& appName, const wxString& vendorName,
  423.                            const wxString& strLocal, const wxString& strGlobal,
  424.                            long style)
  425.             : wxConfigBase(::GetAppName(appName), vendorName,
  426.                            strLocal, strGlobal,
  427.                            style),
  428.               m_strLocalFile(strLocal), m_strGlobalFile(strGlobal)
  429. {
  430.     // Make up names for files if empty
  431.     if ( m_strLocalFile.IsEmpty() && (style & wxCONFIG_USE_LOCAL_FILE) )
  432.         m_strLocalFile = GetLocalFileName(GetAppName());
  433.  
  434.     if ( m_strGlobalFile.IsEmpty() && (style & wxCONFIG_USE_GLOBAL_FILE) )
  435.         m_strGlobalFile = GetGlobalFileName(GetAppName());
  436.  
  437.     // Check if styles are not supplied, but filenames are, in which case
  438.     // add the correct styles.
  439.     if ( !m_strLocalFile.IsEmpty() )
  440.         SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
  441.  
  442.     if ( !m_strGlobalFile.IsEmpty() )
  443.         SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE);
  444.  
  445.     // if the path is not absolute, prepend the standard directory to it
  446.     // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
  447.     if ( !(style & wxCONFIG_USE_RELATIVE_PATH) )
  448.     {
  449.         if ( !m_strLocalFile.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile) )
  450.         {
  451.             wxString strLocal = m_strLocalFile;
  452.             m_strLocalFile = GetLocalDir();
  453.             m_strLocalFile << strLocal;
  454.         }
  455.  
  456.         if ( !m_strGlobalFile.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile) )
  457.         {
  458.             wxString strGlobal = m_strGlobalFile;
  459.             m_strGlobalFile = GetGlobalDir();
  460.             m_strGlobalFile << strGlobal;
  461.         }
  462.     }
  463.  
  464.     SetUmask(-1);
  465.     
  466.     Init();
  467. }
  468.  
  469. #if wxUSE_STREAMS
  470.  
  471. wxFileConfig::wxFileConfig(wxInputStream &inStream)
  472. {
  473.     // always local_file when this constructor is called (?)
  474.     SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
  475.  
  476.     m_pCurrentGroup =
  477.     m_pRootGroup    = new wxFileConfigGroup(NULL, wxT(""), this);
  478.  
  479.     m_linesHead =
  480.     m_linesTail = NULL;
  481.  
  482.     // translate everything to the current (platform-dependent) line
  483.     // termination character
  484.     wxString strTrans;
  485.     {
  486.         wxString strTmp;
  487.  
  488.         char buf[1024];
  489.         while ( !inStream.Read(buf, WXSIZEOF(buf)).Eof() )
  490.             strTmp.append(wxConvertMB2WX(buf), inStream.LastRead());
  491.  
  492.         strTmp.append(wxConvertMB2WX(buf), inStream.LastRead());
  493.  
  494.         strTrans = wxTextBuffer::Translate(strTmp);
  495.     }
  496.  
  497.     wxMemoryText memText;
  498.  
  499.     // Now we can add the text to the memory text. To do this we extract line
  500.     // by line from the translated string, until we've reached the end.
  501.     //
  502.     // VZ: all this is horribly inefficient, we should do the translation on
  503.     //     the fly in one pass saving both memory and time (TODO)
  504.  
  505.     const wxChar *pEOL = wxTextBuffer::GetEOL(wxTextBuffer::typeDefault);
  506.     const size_t EOLLen = wxStrlen(pEOL);
  507.  
  508.     int posLineStart = strTrans.Find(pEOL);
  509.     while ( posLineStart != -1 )
  510.     {
  511.         wxString line(strTrans.Left(posLineStart));
  512.  
  513.         memText.AddLine(line);
  514.  
  515.         strTrans = strTrans.Mid(posLineStart + EOLLen);
  516.  
  517.         posLineStart = strTrans.Find(pEOL);
  518.     }
  519.  
  520.     // also add whatever we have left in the translated string.
  521.     memText.AddLine(strTrans);
  522.  
  523.     // Finally we can parse it all.
  524.     Parse(memText, TRUE /* local */);
  525.  
  526.     SetRootPath();
  527. }
  528.  
  529. #endif // wxUSE_STREAMS
  530.  
  531. void wxFileConfig::CleanUp()
  532. {
  533.   delete m_pRootGroup;
  534.  
  535.   wxFileConfigLineList *pCur = m_linesHead;
  536.   while ( pCur != NULL ) {
  537.     wxFileConfigLineList *pNext = pCur->Next();
  538.     delete pCur;
  539.     pCur = pNext;
  540.   }
  541. }
  542.  
  543. wxFileConfig::~wxFileConfig()
  544. {
  545.   Flush();
  546.  
  547.   CleanUp();
  548. }
  549.  
  550. // ----------------------------------------------------------------------------
  551. // parse a config file
  552. // ----------------------------------------------------------------------------
  553.  
  554. void wxFileConfig::Parse(wxTextBuffer& buffer, bool bLocal)
  555. {
  556.   const wxChar *pStart;
  557.   const wxChar *pEnd;
  558.   wxString strLine;
  559.  
  560.   size_t nLineCount = buffer.GetLineCount();
  561.   
  562.   for ( size_t n = 0; n < nLineCount; n++ )
  563.   {
  564.     strLine = buffer[n];
  565.  
  566.     // add the line to linked list
  567.     if ( bLocal )
  568.       LineListAppend(strLine);
  569.  
  570.     // skip leading spaces
  571.     for ( pStart = strLine; wxIsspace(*pStart); pStart++ )
  572.       ;
  573.  
  574.     // skip blank/comment lines
  575.     if ( *pStart == wxT('\0')|| *pStart == wxT(';') || *pStart == wxT('#') )
  576.       continue;
  577.  
  578.     if ( *pStart == wxT('[') ) {          // a new group
  579.       pEnd = pStart;
  580.  
  581.       while ( *++pEnd != wxT(']') ) {
  582.         if ( *pEnd == wxT('\\') ) {
  583.             // the next char is escaped, so skip it even if it is ']'
  584.             pEnd++;
  585.         }
  586.  
  587.         if ( *pEnd == wxT('\n') || *pEnd == wxT('\0') ) {
  588.             // we reached the end of line, break out of the loop
  589.             break;
  590.         }
  591.       }
  592.  
  593.       if ( *pEnd != wxT(']') ) {
  594.         wxLogError(_("file '%s': unexpected character %c at line %d."),
  595.                    buffer.GetName(), *pEnd, n + 1);
  596.         continue; // skip this line
  597.       }
  598.  
  599.       // group name here is always considered as abs path
  600.       wxString strGroup;
  601.       pStart++;
  602.       strGroup << wxCONFIG_PATH_SEPARATOR
  603.                << FilterInEntryName(wxString(pStart, pEnd - pStart));
  604.  
  605.       // will create it if doesn't yet exist
  606.       SetPath(strGroup);
  607.  
  608.       if ( bLocal )
  609.         m_pCurrentGroup->SetLine(m_linesTail);
  610.  
  611.       // check that there is nothing except comments left on this line
  612.       bool bCont = TRUE;
  613.       while ( *++pEnd != wxT('\0') && bCont ) {
  614.         switch ( *pEnd ) {
  615.           case wxT('#'):
  616.           case wxT(';'):
  617.             bCont = FALSE;
  618.             break;
  619.  
  620.           case wxT(' '):
  621.           case wxT('\t'):
  622.             // ignore whitespace ('\n' impossible here)
  623.             break;
  624.  
  625.           default:
  626.             wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
  627.                          buffer.GetName(), n + 1, pEnd);
  628.             bCont = FALSE;
  629.         }
  630.       }
  631.     }
  632.     else {                        // a key
  633.       const wxChar *pEnd = pStart;
  634.       while ( *pEnd && *pEnd != wxT('=') && !wxIsspace(*pEnd) ) {
  635.         if ( *pEnd == wxT('\\') ) {
  636.           // next character may be space or not - still take it because it's
  637.           // quoted (unless there is nothing)
  638.           pEnd++;
  639.           if ( !*pEnd ) {
  640.             // the error message will be given below anyhow
  641.             break;
  642.           }
  643.         }
  644.  
  645.         pEnd++;
  646.       }
  647.  
  648.       wxString strKey(FilterInEntryName(wxString(pStart, pEnd)));
  649.  
  650.       // skip whitespace
  651.       while ( wxIsspace(*pEnd) )
  652.         pEnd++;
  653.  
  654.       if ( *pEnd++ != wxT('=') ) {
  655.         wxLogError(_("file '%s', line %d: '=' expected."),
  656.                    buffer.GetName(), n + 1);
  657.       }
  658.       else {
  659.         wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strKey);
  660.  
  661.         if ( pEntry == NULL ) {
  662.           // new entry
  663.           pEntry = m_pCurrentGroup->AddEntry(strKey, n);
  664.  
  665.           if ( bLocal )
  666.             pEntry->SetLine(m_linesTail);
  667.         }
  668.         else {
  669.           if ( bLocal && pEntry->IsImmutable() ) {
  670.             // immutable keys can't be changed by user
  671.             wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
  672.                          buffer.GetName(), n + 1, strKey.c_str());
  673.             continue;
  674.           }
  675.           // the condition below catches the cases (a) and (b) but not (c):
  676.           //  (a) global key found second time in global file
  677.           //  (b) key found second (or more) time in local file
  678.           //  (c) key from global file now found in local one
  679.           // which is exactly what we want.
  680.           else if ( !bLocal || pEntry->IsLocal() ) {
  681.             wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
  682.                          buffer.GetName(), n + 1, strKey.c_str(), pEntry->Line());
  683.  
  684.             if ( bLocal )
  685.               pEntry->SetLine(m_linesTail);
  686.           }
  687.         }
  688.  
  689.         // skip whitespace
  690.         while ( wxIsspace(*pEnd) )
  691.           pEnd++;
  692.  
  693.         wxString value = pEnd;
  694.         if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS) )
  695.             value = FilterInValue(value);
  696.  
  697.         pEntry->SetValue(value, FALSE);
  698.       }
  699.     }
  700.   }
  701. }
  702.  
  703. // ----------------------------------------------------------------------------
  704. // set/retrieve path
  705. // ----------------------------------------------------------------------------
  706.  
  707. void wxFileConfig::SetRootPath()
  708. {
  709.   m_strPath.Empty();
  710.   m_pCurrentGroup = m_pRootGroup;
  711. }
  712.  
  713. void wxFileConfig::SetPath(const wxString& strPath)
  714. {
  715.   wxArrayString aParts;
  716.  
  717.   if ( strPath.IsEmpty() ) {
  718.     SetRootPath();
  719.     return;
  720.   }
  721.  
  722.   if ( strPath[0] == wxCONFIG_PATH_SEPARATOR ) {
  723.     // absolute path
  724.     wxSplitPath(aParts, strPath);
  725.   }
  726.   else {
  727.     // relative path, combine with current one
  728.     wxString strFullPath = m_strPath;
  729.     strFullPath << wxCONFIG_PATH_SEPARATOR << strPath;
  730.     wxSplitPath(aParts, strFullPath);
  731.   }
  732.  
  733.   // change current group
  734.   size_t n;
  735.   m_pCurrentGroup = m_pRootGroup;
  736.   for ( n = 0; n < aParts.Count(); n++ ) {
  737.     wxFileConfigGroup *pNextGroup = m_pCurrentGroup->FindSubgroup(aParts[n]);
  738.     if ( pNextGroup == NULL )
  739.       pNextGroup = m_pCurrentGroup->AddSubgroup(aParts[n]);
  740.     m_pCurrentGroup = pNextGroup;
  741.   }
  742.  
  743.   // recombine path parts in one variable
  744.   m_strPath.Empty();
  745.   for ( n = 0; n < aParts.Count(); n++ ) {
  746.     m_strPath << wxCONFIG_PATH_SEPARATOR << aParts[n];
  747.   }
  748. }
  749.  
  750. // ----------------------------------------------------------------------------
  751. // enumeration
  752. // ----------------------------------------------------------------------------
  753.  
  754. bool wxFileConfig::GetFirstGroup(wxString& str, long& lIndex) const
  755. {
  756.   lIndex = 0;
  757.   return GetNextGroup(str, lIndex);
  758. }
  759.  
  760. bool wxFileConfig::GetNextGroup (wxString& str, long& lIndex) const
  761. {
  762.   if ( size_t(lIndex) < m_pCurrentGroup->Groups().Count() ) {
  763.     str = m_pCurrentGroup->Groups()[(size_t)lIndex++]->Name();
  764.     return TRUE;
  765.   }
  766.   else
  767.     return FALSE;
  768. }
  769.  
  770. bool wxFileConfig::GetFirstEntry(wxString& str, long& lIndex) const
  771. {
  772.   lIndex = 0;
  773.   return GetNextEntry(str, lIndex);
  774. }
  775.  
  776. bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex) const
  777. {
  778.   if ( size_t(lIndex) < m_pCurrentGroup->Entries().Count() ) {
  779.     str = m_pCurrentGroup->Entries()[(size_t)lIndex++]->Name();
  780.     return TRUE;
  781.   }
  782.   else
  783.     return FALSE;
  784. }
  785.  
  786. size_t wxFileConfig::GetNumberOfEntries(bool bRecursive) const
  787. {
  788.   size_t n = m_pCurrentGroup->Entries().Count();
  789.   if ( bRecursive ) {
  790.     wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
  791.     size_t nSubgroups = m_pCurrentGroup->Groups().Count();
  792.     for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
  793.       CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
  794.       n += GetNumberOfEntries(TRUE);
  795.       CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
  796.     }
  797.   }
  798.  
  799.   return n;
  800. }
  801.  
  802. size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
  803. {
  804.   size_t n = m_pCurrentGroup->Groups().Count();
  805.   if ( bRecursive ) {
  806.     wxFileConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
  807.     size_t nSubgroups = m_pCurrentGroup->Groups().Count();
  808.     for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
  809.       CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
  810.       n += GetNumberOfGroups(TRUE);
  811.       CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
  812.     }
  813.   }
  814.  
  815.   return n;
  816. }
  817.  
  818. // ----------------------------------------------------------------------------
  819. // tests for existence
  820. // ----------------------------------------------------------------------------
  821.  
  822. bool wxFileConfig::HasGroup(const wxString& strName) const
  823. {
  824.   wxConfigPathChanger path(this, strName);
  825.  
  826.   wxFileConfigGroup *pGroup = m_pCurrentGroup->FindSubgroup(path.Name());
  827.   return pGroup != NULL;
  828. }
  829.  
  830. bool wxFileConfig::HasEntry(const wxString& strName) const
  831. {
  832.   wxConfigPathChanger path(this, strName);
  833.  
  834.   wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
  835.   return pEntry != NULL;
  836. }
  837.  
  838. // ----------------------------------------------------------------------------
  839. // read/write values
  840. // ----------------------------------------------------------------------------
  841.  
  842. bool wxFileConfig::DoReadString(const wxString& key, wxString* pStr) const
  843. {
  844.   wxConfigPathChanger path(this, key);
  845.  
  846.   wxFileConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
  847.   if (pEntry == NULL) {
  848.     return FALSE;
  849.   }
  850.  
  851.   *pStr = pEntry->Value();
  852.  
  853.   return TRUE;
  854. }
  855.  
  856. bool wxFileConfig::DoReadLong(const wxString& key, long *pl) const
  857. {
  858.   wxString str;
  859.   if ( !Read(key, & str) )
  860.   {
  861.     return FALSE;
  862.   }
  863.   return str.ToLong(pl) ;
  864. }
  865.  
  866. bool wxFileConfig::DoWriteString(const wxString& key, const wxString& szValue)
  867. {
  868.     wxConfigPathChanger     path(this, key);
  869.     wxString                strName = path.Name();
  870.   
  871.     wxLogTrace( _T("wxFileConfig"),
  872.                 _T("  Writing String '%s' = '%s' to Group '%s'"),
  873.                 strName.c_str(),
  874.                 szValue.c_str(),
  875.                 GetPath().c_str() );
  876.  
  877.     if ( strName.IsEmpty() )
  878.     {
  879.             // setting the value of a group is an error
  880.  
  881.         wxASSERT_MSG( wxIsEmpty(szValue), wxT("can't set value of a group!") );
  882.  
  883.             // ... except if it's empty in which case it's a way to force it's creation
  884.  
  885.         wxLogTrace( _T("wxFileConfig"),
  886.                     _T("  Creating group %s"),
  887.                     m_pCurrentGroup->Name().c_str() );
  888.  
  889.         m_pCurrentGroup->SetDirty();
  890.  
  891.             // this will add a line for this group if it didn't have it before
  892.  
  893.         (void)m_pCurrentGroup->GetGroupLine();
  894.     }
  895.     else
  896.     {
  897.             // writing an entry
  898.             // check that the name is reasonable
  899.  
  900.         if ( strName[0u] == wxCONFIG_IMMUTABLE_PREFIX )
  901.         {
  902.             wxLogError( _("Config entry name cannot start with '%c'."),
  903.                         wxCONFIG_IMMUTABLE_PREFIX);
  904.             return FALSE;
  905.         }
  906.  
  907.         wxFileConfigEntry   *pEntry = m_pCurrentGroup->FindEntry(strName);
  908.  
  909.         if ( pEntry == 0 )
  910.         {
  911.             wxLogTrace( _T("wxFileConfig"),
  912.                         _T("  Adding Entry %s"),
  913.                         strName.c_str() );
  914.             pEntry = m_pCurrentGroup->AddEntry(strName);
  915.         }
  916.  
  917.         wxLogTrace( _T("wxFileConfig"),
  918.                     _T("  Setting value %s"),
  919.                     szValue.c_str() );
  920.         pEntry->SetValue(szValue);
  921.     }
  922.  
  923.     return TRUE;
  924. }
  925.  
  926. bool wxFileConfig::DoWriteLong(const wxString& key, long lValue)
  927. {
  928.   return Write(key, wxString::Format(_T("%ld"), lValue));
  929. }
  930.  
  931. bool wxFileConfig::Flush(bool /* bCurrentOnly */)
  932. {
  933.   if ( LineListIsEmpty() || !m_pRootGroup->IsDirty() || !m_strLocalFile )
  934.     return TRUE;
  935.  
  936. #ifdef __UNIX__
  937.   // set the umask if needed
  938.   mode_t umaskOld = 0;
  939.   if ( m_umask != -1 )
  940.   {
  941.       umaskOld = umask((mode_t)m_umask);
  942.   }
  943. #endif // __UNIX__
  944.  
  945.   wxTempFile file(m_strLocalFile);
  946.  
  947.   if ( !file.IsOpened() )
  948.   {
  949.     wxLogError(_("can't open user configuration file."));
  950.     return FALSE;
  951.   }
  952.  
  953.   // write all strings to file
  954.   for ( wxFileConfigLineList *p = m_linesHead; p != NULL; p = p->Next() )
  955.   {
  956.     wxString line = p->Text();
  957.     line += wxTextFile::GetEOL();
  958. #if wxUSE_UNICODE
  959.     wxCharBuffer buf = wxConvLocal.cWX2MB( line );
  960.     if ( !file.Write( (const char*)buf, strlen( (const char*) buf ) ) )
  961. #else
  962.     if ( !file.Write( line.c_str(), line.Len() ) )
  963. #endif
  964.     {
  965.       wxLogError(_("can't write user configuration file."));
  966.       return FALSE;
  967.     }
  968.   }
  969.  
  970.   bool ret = file.Commit();
  971.  
  972. #if defined(__WXMAC__)
  973.   if ( ret )
  974.   {
  975.       FSSpec spec ;
  976.  
  977.       wxMacFilename2FSSpec( m_strLocalFile , &spec ) ;
  978.       FInfo finfo ;
  979.       if ( FSpGetFInfo( &spec , &finfo ) == noErr )
  980.       {
  981.           finfo.fdType = 'TEXT' ;
  982.           finfo.fdCreator = 'ttxt' ;
  983.           FSpSetFInfo( &spec , &finfo ) ;
  984.       }
  985.   }
  986. #endif // __WXMAC__
  987.  
  988. #ifdef __UNIX__
  989.   // restore the old umask if we changed it
  990.   if ( m_umask != -1 )
  991.   {
  992.       (void)umask(umaskOld);
  993.   }
  994. #endif // __UNIX__
  995.  
  996.   return ret;
  997. }
  998.  
  999. // ----------------------------------------------------------------------------
  1000. // renaming groups/entries
  1001. // ----------------------------------------------------------------------------
  1002.  
  1003. bool wxFileConfig::RenameEntry(const wxString& oldName,
  1004.                                const wxString& newName)
  1005. {
  1006.     // check that the entry exists
  1007.     wxFileConfigEntry *oldEntry = m_pCurrentGroup->FindEntry(oldName);
  1008.     if ( !oldEntry )
  1009.         return FALSE;
  1010.  
  1011.     // check that the new entry doesn't already exist
  1012.     if ( m_pCurrentGroup->FindEntry(newName) )
  1013.         return FALSE;
  1014.  
  1015.     // delete the old entry, create the new one
  1016.     wxString value = oldEntry->Value();
  1017.     if ( !m_pCurrentGroup->DeleteEntry(oldName) )
  1018.         return FALSE;
  1019.  
  1020.     wxFileConfigEntry *newEntry = m_pCurrentGroup->AddEntry(newName);
  1021.     newEntry->SetValue(value);
  1022.  
  1023.     return TRUE;
  1024. }
  1025.  
  1026. bool wxFileConfig::RenameGroup(const wxString& oldName,
  1027.                                const wxString& newName)
  1028. {
  1029.     // check that the group exists
  1030.     wxFileConfigGroup *group = m_pCurrentGroup->FindSubgroup(oldName);
  1031.     if ( !group )
  1032.         return FALSE;
  1033.  
  1034.     // check that the new group doesn't already exist
  1035.     if ( m_pCurrentGroup->FindSubgroup(newName) )
  1036.         return FALSE;
  1037.  
  1038.     group->Rename(newName);
  1039.  
  1040.     return TRUE;
  1041. }
  1042.  
  1043. // ----------------------------------------------------------------------------
  1044. // delete groups/entries
  1045. // ----------------------------------------------------------------------------
  1046.  
  1047. bool wxFileConfig::DeleteEntry(const wxString& key, bool bGroupIfEmptyAlso)
  1048. {
  1049.   wxConfigPathChanger path(this, key);
  1050.  
  1051.   if ( !m_pCurrentGroup->DeleteEntry(path.Name()) )
  1052.     return FALSE;
  1053.  
  1054.   if ( bGroupIfEmptyAlso && m_pCurrentGroup->IsEmpty() ) {
  1055.     if ( m_pCurrentGroup != m_pRootGroup ) {
  1056.       wxFileConfigGroup *pGroup = m_pCurrentGroup;
  1057.       SetPath(wxT(".."));  // changes m_pCurrentGroup!
  1058.       m_pCurrentGroup->DeleteSubgroupByName(pGroup->Name());
  1059.     }
  1060.     //else: never delete the root group
  1061.   }
  1062.  
  1063.   return TRUE;
  1064. }
  1065.  
  1066. bool wxFileConfig::DeleteGroup(const wxString& key)
  1067. {
  1068.   wxConfigPathChanger path(this, key);
  1069.  
  1070.   return m_pCurrentGroup->DeleteSubgroupByName(path.Name());
  1071. }
  1072.  
  1073. bool wxFileConfig::DeleteAll()
  1074. {
  1075.   CleanUp();
  1076.  
  1077.   if ( wxRemove(m_strLocalFile) == -1 )
  1078.     wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile.c_str());
  1079.  
  1080.   m_strLocalFile = m_strGlobalFile = wxT("");
  1081.   Init();
  1082.  
  1083.   return TRUE;
  1084. }
  1085.  
  1086. // ----------------------------------------------------------------------------
  1087. // linked list functions
  1088. // ----------------------------------------------------------------------------
  1089.  
  1090.     // append a new line to the end of the list
  1091.  
  1092. wxFileConfigLineList *wxFileConfig::LineListAppend(const wxString& str)
  1093. {
  1094.     wxLogTrace( _T("wxFileConfig"),
  1095.                 _T("    ** Adding Line '%s'"),
  1096.                 str.c_str() );
  1097.     wxLogTrace( _T("wxFileConfig"),
  1098.                 _T("        head: %s"),
  1099.                 ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
  1100.     wxLogTrace( _T("wxFileConfig"),
  1101.                 _T("        tail: %s"),
  1102.                 ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
  1103.  
  1104.     wxFileConfigLineList *pLine = new wxFileConfigLineList(str);
  1105.  
  1106.     if ( m_linesTail == NULL )
  1107.     {
  1108.         // list is empty
  1109.         m_linesHead = pLine;
  1110.     }
  1111.     else
  1112.     {
  1113.         // adjust pointers
  1114.         m_linesTail->SetNext(pLine);
  1115.         pLine->SetPrev(m_linesTail);
  1116.     }
  1117.  
  1118.     m_linesTail = pLine;
  1119.  
  1120.     wxLogTrace( _T("wxFileConfig"),
  1121.                 _T("        head: %s"),
  1122.                 ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
  1123.     wxLogTrace( _T("wxFileConfig"),
  1124.                 _T("        tail: %s"),
  1125.                 ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
  1126.  
  1127.     return m_linesTail;
  1128. }
  1129.  
  1130.     // insert a new line after the given one or in the very beginning if !pLine
  1131.  
  1132. wxFileConfigLineList *wxFileConfig::LineListInsert(const wxString& str,
  1133.                                                    wxFileConfigLineList *pLine)
  1134. {
  1135.     wxLogTrace( _T("wxFileConfig"),
  1136.                 _T("    ** Inserting Line '%s' after '%s'"),
  1137.                 str.c_str(),
  1138.                 ((pLine) ? pLine->Text().c_str() : wxEmptyString) );
  1139.     wxLogTrace( _T("wxFileConfig"),
  1140.                 _T("        head: %s"),
  1141.                 ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
  1142.     wxLogTrace( _T("wxFileConfig"),
  1143.                 _T("        tail: %s"),
  1144.                 ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
  1145.  
  1146.     if ( pLine == m_linesTail )
  1147.         return LineListAppend(str);
  1148.  
  1149.     wxFileConfigLineList *pNewLine = new wxFileConfigLineList(str);
  1150.     if ( pLine == NULL )
  1151.     {
  1152.         // prepend to the list
  1153.         pNewLine->SetNext(m_linesHead);
  1154.         m_linesHead->SetPrev(pNewLine);
  1155.         m_linesHead = pNewLine;
  1156.     }
  1157.     else
  1158.     {
  1159.         // insert before pLine
  1160.         wxFileConfigLineList *pNext = pLine->Next();
  1161.         pNewLine->SetNext(pNext);
  1162.         pNewLine->SetPrev(pLine);
  1163.         pNext->SetPrev(pNewLine);
  1164.         pLine->SetNext(pNewLine);
  1165.     }
  1166.  
  1167.     wxLogTrace( _T("wxFileConfig"),
  1168.                 _T("        head: %s"),
  1169.                 ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
  1170.     wxLogTrace( _T("wxFileConfig"),
  1171.                 _T("        tail: %s"),
  1172.                 ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
  1173.  
  1174.     return pNewLine;
  1175. }
  1176.  
  1177. void wxFileConfig::LineListRemove(wxFileConfigLineList *pLine)
  1178. {
  1179.     wxLogTrace( _T("wxFileConfig"),
  1180.                 _T("    ** Removing Line '%s'"),
  1181.                 pLine->Text().c_str() );
  1182.     wxLogTrace( _T("wxFileConfig"),
  1183.                 _T("        head: %s"),
  1184.                 ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
  1185.     wxLogTrace( _T("wxFileConfig"),
  1186.                 _T("        tail: %s"),
  1187.                 ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
  1188.  
  1189.     wxFileConfigLineList    *pPrev = pLine->Prev(),
  1190.                             *pNext = pLine->Next();
  1191.  
  1192.         // first entry?
  1193.  
  1194.     if ( pPrev == NULL )
  1195.         m_linesHead = pNext;
  1196.     else
  1197.         pPrev->SetNext(pNext);
  1198.  
  1199.         // last entry?
  1200.  
  1201.     if ( pNext == NULL )
  1202.         m_linesTail = pPrev;
  1203.     else
  1204.         pNext->SetPrev(pPrev);
  1205.  
  1206.     wxLogTrace( _T("wxFileConfig"),
  1207.                 _T("        head: %s"),
  1208.                 ((m_linesHead) ? m_linesHead->Text().c_str() : wxEmptyString) );
  1209.     wxLogTrace( _T("wxFileConfig"),
  1210.                 _T("        tail: %s"),
  1211.                 ((m_linesTail) ? m_linesTail->Text().c_str() : wxEmptyString) );
  1212.  
  1213.     delete pLine;
  1214. }
  1215.  
  1216. bool wxFileConfig::LineListIsEmpty()
  1217. {
  1218.     return m_linesHead == NULL;
  1219. }
  1220.  
  1221. // ============================================================================
  1222. // wxFileConfig::wxFileConfigGroup
  1223. // ============================================================================
  1224.  
  1225. // ----------------------------------------------------------------------------
  1226. // ctor/dtor
  1227. // ----------------------------------------------------------------------------
  1228.  
  1229. // ctor
  1230. wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup *pParent,
  1231.                                        const wxString& strName,
  1232.                                        wxFileConfig *pConfig)
  1233.                          : m_aEntries(CompareEntries),
  1234.                            m_aSubgroups(CompareGroups),
  1235.                            m_strName(strName)
  1236. {
  1237.   m_pConfig = pConfig;
  1238.   m_pParent = pParent;
  1239.   m_bDirty  = FALSE;
  1240.   m_pLine   = NULL;
  1241.  
  1242.   m_pLastEntry = NULL;
  1243.   m_pLastGroup = NULL;
  1244. }
  1245.  
  1246. // dtor deletes all children
  1247. wxFileConfigGroup::~wxFileConfigGroup()
  1248. {
  1249.   // entries
  1250.   size_t n, nCount = m_aEntries.Count();
  1251.   for ( n = 0; n < nCount; n++ )
  1252.     delete m_aEntries[n];
  1253.  
  1254.   // subgroups
  1255.   nCount = m_aSubgroups.Count();
  1256.   for ( n = 0; n < nCount; n++ )
  1257.     delete m_aSubgroups[n];
  1258. }
  1259.  
  1260. // ----------------------------------------------------------------------------
  1261. // line
  1262. // ----------------------------------------------------------------------------
  1263.  
  1264. void wxFileConfigGroup::SetLine(wxFileConfigLineList *pLine)
  1265. {
  1266.     wxASSERT( m_pLine == 0 ); // shouldn't be called twice
  1267.     m_pLine = pLine;
  1268. }
  1269.  
  1270. /*
  1271.   This is a bit complicated, so let me explain it in details. All lines that
  1272.   were read from the local file (the only one we will ever modify) are stored
  1273.   in a (doubly) linked list. Our problem is to know at which position in this
  1274.   list should we insert the new entries/subgroups. To solve it we keep three
  1275.   variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
  1276.  
  1277.   m_pLine points to the line containing "[group_name]"
  1278.   m_pLastEntry points to the last entry of this group in the local file.
  1279.   m_pLastGroup                   subgroup
  1280.  
  1281.   Initially, they're NULL all three. When the group (an entry/subgroup) is read
  1282.   from the local file, the corresponding variable is set. However, if the group
  1283.   was read from the global file and then modified or created by the application
  1284.   these variables are still NULL and we need to create the corresponding lines.
  1285.   See the following functions (and comments preceding them) for the details of
  1286.   how we do it.
  1287.  
  1288.   Also, when our last entry/group are deleted we need to find the new last
  1289.   element - the code in DeleteEntry/Subgroup does this by backtracking the list
  1290.   of lines until it either founds an entry/subgroup (and this is the new last
  1291.   element) or the m_pLine of the group, in which case there are no more entries
  1292.   (or subgroups) left and m_pLast<element> becomes NULL.
  1293.  
  1294.   NB: This last problem could be avoided for entries if we added new entries
  1295.       immediately after m_pLine, but in this case the entries would appear
  1296.       backwards in the config file (OTOH, it's not that important) and as we
  1297.       would still need to do it for the subgroups the code wouldn't have been
  1298.       significantly less complicated.
  1299. */
  1300.  
  1301. // Return the line which contains "[our name]". If we're still not in the list,
  1302. // add our line to it immediately after the last line of our parent group if we
  1303. // have it or in the very beginning if we're the root group.
  1304. wxFileConfigLineList *wxFileConfigGroup::GetGroupLine()
  1305. {
  1306.     wxLogTrace( _T("wxFileConfig"),
  1307.                 _T("  GetGroupLine() for Group '%s'"),
  1308.                 Name().c_str() );
  1309.  
  1310.     if ( m_pLine == 0 )
  1311.     {
  1312.         wxLogTrace( _T("wxFileConfig"),
  1313.                     _T("    Getting Line item pointer") );
  1314.  
  1315.         wxFileConfigGroup   *pParent = Parent();
  1316.  
  1317.             // this group wasn't present in local config file, add it now
  1318.  
  1319.         if ( pParent != 0 )
  1320.         {
  1321.             wxLogTrace( _T("wxFileConfig"),
  1322.                         _T("    checking parent '%s'"),
  1323.                         pParent->Name().c_str() );
  1324.  
  1325.             wxString    strFullName;
  1326.  
  1327.             strFullName << wxT("[")         // +1: no '/'
  1328.                         << FilterOutEntryName(GetFullName().c_str() + 1)
  1329.                         << wxT("]");
  1330.             m_pLine = m_pConfig->LineListInsert(strFullName,
  1331.                                                 pParent->GetLastGroupLine());
  1332.             pParent->SetLastGroup(this);  // we're surely after all the others
  1333.         }
  1334.         else
  1335.         {
  1336.             // we return NULL, so that LineListInsert() will insert us in the
  1337.             // very beginning
  1338.         }
  1339.     }
  1340.  
  1341.     return m_pLine;
  1342. }
  1343.  
  1344. // Return the last line belonging to the subgroups of this group (after which
  1345. // we can add a new subgroup), if we don't have any subgroups or entries our
  1346. // last line is the group line (m_pLine) itself.
  1347. wxFileConfigLineList *wxFileConfigGroup::GetLastGroupLine()
  1348. {
  1349.         // if we have any subgroups, our last line is
  1350.         // the last line of the last subgroup
  1351.  
  1352.     if ( m_pLastGroup != 0 )
  1353.     {
  1354.         wxFileConfigLineList *pLine = m_pLastGroup->GetLastGroupLine();
  1355.  
  1356.         wxASSERT( pLine != 0 );  // last group must have !NULL associated line
  1357.         return pLine;
  1358.     }
  1359.  
  1360.         // no subgroups, so the last line is the line of thelast entry (if any)
  1361.  
  1362.     return GetLastEntryLine();
  1363. }
  1364.  
  1365. // return the last line belonging to the entries of this group (after which
  1366. // we can add a new entry), if we don't have any entries we will add the new
  1367. // one immediately after the group line itself.
  1368. wxFileConfigLineList *wxFileConfigGroup::GetLastEntryLine()
  1369. {
  1370.     wxLogTrace( _T("wxFileConfig"),
  1371.                 _T("  GetLastEntryLine() for Group '%s'"),
  1372.                 Name().c_str() );
  1373.  
  1374.     if ( m_pLastEntry != 0 )
  1375.     {
  1376.         wxFileConfigLineList    *pLine = m_pLastEntry->GetLine();
  1377.  
  1378.         wxASSERT( pLine != 0 );  // last entry must have !NULL associated line
  1379.         return pLine;
  1380.     }
  1381.  
  1382.         // no entries: insert after the group header
  1383.  
  1384.     return GetGroupLine();
  1385. }
  1386.  
  1387. // ----------------------------------------------------------------------------
  1388. // group name
  1389. // ----------------------------------------------------------------------------
  1390.  
  1391. void wxFileConfigGroup::Rename(const wxString& newName)
  1392. {
  1393.     m_strName = newName;
  1394.  
  1395.     wxFileConfigLineList *line = GetGroupLine();
  1396.     wxString strFullName;
  1397.     strFullName << wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
  1398.     line->SetText(strFullName);
  1399.  
  1400.     SetDirty();
  1401. }
  1402.  
  1403. wxString wxFileConfigGroup::GetFullName() const
  1404. {
  1405.   if ( Parent() )
  1406.     return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR + Name();
  1407.   else
  1408.     return wxT("");
  1409. }
  1410.  
  1411. // ----------------------------------------------------------------------------
  1412. // find an item
  1413. // ----------------------------------------------------------------------------
  1414.  
  1415. // use binary search because the array is sorted
  1416. wxFileConfigEntry *
  1417. wxFileConfigGroup::FindEntry(const wxChar *szName) const
  1418. {
  1419.   size_t i,
  1420.        lo = 0,
  1421.        hi = m_aEntries.Count();
  1422.   int res;
  1423.   wxFileConfigEntry *pEntry;
  1424.  
  1425.   while ( lo < hi ) {
  1426.     i = (lo + hi)/2;
  1427.     pEntry = m_aEntries[i];
  1428.  
  1429.     #if wxCONFIG_CASE_SENSITIVE
  1430.       res = wxStrcmp(pEntry->Name(), szName);
  1431.     #else
  1432.       res = wxStricmp(pEntry->Name(), szName);
  1433.     #endif
  1434.  
  1435.     if ( res > 0 )
  1436.       hi = i;
  1437.     else if ( res < 0 )
  1438.       lo = i + 1;
  1439.     else
  1440.       return pEntry;
  1441.   }
  1442.  
  1443.   return NULL;
  1444. }
  1445.  
  1446. wxFileConfigGroup *
  1447. wxFileConfigGroup::FindSubgroup(const wxChar *szName) const
  1448. {
  1449.   size_t i,
  1450.        lo = 0,
  1451.        hi = m_aSubgroups.Count();
  1452.   int res;
  1453.   wxFileConfigGroup *pGroup;
  1454.  
  1455.   while ( lo < hi ) {
  1456.     i = (lo + hi)/2;
  1457.     pGroup = m_aSubgroups[i];
  1458.  
  1459.     #if wxCONFIG_CASE_SENSITIVE
  1460.       res = wxStrcmp(pGroup->Name(), szName);
  1461.     #else
  1462.       res = wxStricmp(pGroup->Name(), szName);
  1463.     #endif
  1464.  
  1465.     if ( res > 0 )
  1466.       hi = i;
  1467.     else if ( res < 0 )
  1468.       lo = i + 1;
  1469.     else
  1470.       return pGroup;
  1471.   }
  1472.  
  1473.   return NULL;
  1474. }
  1475.  
  1476. // ----------------------------------------------------------------------------
  1477. // create a new item
  1478. // ----------------------------------------------------------------------------
  1479.  
  1480. // create a new entry and add it to the current group
  1481. wxFileConfigEntry *wxFileConfigGroup::AddEntry(const wxString& strName, int nLine)
  1482. {
  1483.     wxASSERT( FindEntry(strName) == 0 );
  1484.  
  1485.     wxFileConfigEntry   *pEntry = new wxFileConfigEntry(this, strName, nLine);
  1486.  
  1487.     m_aEntries.Add(pEntry);
  1488.     return pEntry;
  1489. }
  1490.  
  1491. // create a new group and add it to the current group
  1492. wxFileConfigGroup *wxFileConfigGroup::AddSubgroup(const wxString& strName)
  1493. {
  1494.     wxASSERT( FindSubgroup(strName) == 0 );
  1495.  
  1496.     wxFileConfigGroup   *pGroup = new wxFileConfigGroup(this, strName, m_pConfig);
  1497.  
  1498.     m_aSubgroups.Add(pGroup);
  1499.     return pGroup;
  1500. }
  1501.  
  1502. // ----------------------------------------------------------------------------
  1503. // delete an item
  1504. // ----------------------------------------------------------------------------
  1505.  
  1506. /*
  1507.   The delete operations are _very_ slow if we delete the last item of this
  1508.   group (see comments before GetXXXLineXXX functions for more details),
  1509.   so it's much better to start with the first entry/group if we want to
  1510.   delete several of them.
  1511.  */
  1512.  
  1513. bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar *szName)
  1514. {
  1515.   return DeleteSubgroup(FindSubgroup(szName));
  1516. }
  1517.  
  1518. // Delete the subgroup and remove all references to it from
  1519. // other data structures.
  1520. bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup *pGroup)
  1521. {
  1522.     wxLogTrace( _T("wxFileConfig"),
  1523.                 _T("Deleting group '%s' from '%s'"),
  1524.                 pGroup->Name().c_str(),
  1525.                 Name().c_str() );
  1526.  
  1527.     wxLogTrace( _T("wxFileConfig"),
  1528.                 _T("  (m_pLine) = prev: %p, this %p, next %p"),
  1529.                 ((m_pLine) ? m_pLine->Prev() : 0),
  1530.                 m_pLine,
  1531.                 ((m_pLine) ? m_pLine->Next() : 0) );
  1532.     wxLogTrace( _T("wxFileConfig"),
  1533.                 _T("  text: '%s'"),
  1534.                 ((m_pLine) ? m_pLine->Text().c_str() : wxEmptyString) );
  1535.  
  1536.     wxCHECK_MSG( pGroup != 0, FALSE, _T("deleting non existing group?") );
  1537.  
  1538.         // delete all entries
  1539.  
  1540.     size_t  nCount = pGroup->m_aEntries.Count();
  1541.  
  1542.     wxLogTrace(_T("wxFileConfig"),
  1543.                _T("Removing %lu Entries"),
  1544.                (unsigned long)nCount );
  1545.  
  1546.     for ( size_t nEntry = 0; nEntry < nCount; nEntry++ )
  1547.     {
  1548.         wxFileConfigLineList    *pLine = pGroup->m_aEntries[nEntry]->GetLine();
  1549.  
  1550.         if ( pLine != 0 )
  1551.         {
  1552.             wxLogTrace( _T("wxFileConfig"),
  1553.                         _T("    '%s'"),
  1554.                         pLine->Text().c_str() );
  1555.             m_pConfig->LineListRemove(pLine);
  1556.         }
  1557.     }
  1558.  
  1559.         // and subgroups of this subgroup
  1560.  
  1561.     nCount = pGroup->m_aSubgroups.Count();
  1562.  
  1563.     wxLogTrace( _T("wxFileConfig"),
  1564.                 _T("Removing %lu SubGroups"),
  1565.                 (unsigned long)nCount );
  1566.  
  1567.     for ( size_t nGroup = 0; nGroup < nCount; nGroup++ )
  1568.     {
  1569.         pGroup->DeleteSubgroup(pGroup->m_aSubgroups[0]);
  1570.     }
  1571.  
  1572.         // finally the group itself
  1573.  
  1574.     wxFileConfigLineList    *pLine = pGroup->m_pLine;
  1575.  
  1576.     if ( pLine != 0 )
  1577.     {
  1578.         wxLogTrace( _T("wxFileConfig"),
  1579.                     _T("  Removing line entry for Group '%s' : '%s'"),
  1580.                     pGroup->Name().c_str(),
  1581.                     pLine->Text().c_str() );
  1582.         wxLogTrace( _T("wxFileConfig"),
  1583.                     _T("  Removing from Group '%s' : '%s'"),
  1584.                     Name().c_str(),
  1585.                     ((m_pLine) ? m_pLine->Text().c_str() : wxEmptyString) );
  1586.  
  1587.             // notice that we may do this test inside the previous "if"
  1588.             // because the last entry's line is surely !NULL
  1589.  
  1590.         if ( pGroup == m_pLastGroup )
  1591.         {
  1592.             wxLogTrace( _T("wxFileConfig"),
  1593.                         _T("  ------- Removing last group -------") );
  1594.  
  1595.                 // our last entry is being deleted, so find the last one which stays.
  1596.                 // go back until we find a subgroup or reach the group's line, unless
  1597.                 // we are the root group, which we'll notice shortly.
  1598.  
  1599.             wxFileConfigGroup       *pNewLast = 0;
  1600.             size_t                   nSubgroups = m_aSubgroups.Count();
  1601.             wxFileConfigLineList    *pl;
  1602.  
  1603.             for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() )
  1604.             {
  1605.                     // is it our subgroup?
  1606.  
  1607.                 for ( size_t n = 0; (pNewLast == 0) && (n < nSubgroups); n++ )
  1608.                 {
  1609.                     // do _not_ call GetGroupLine! we don't want to add it to the local
  1610.                     // file if it's not already there
  1611.  
  1612.                     if ( m_aSubgroups[n]->m_pLine == m_pLine )
  1613.                         pNewLast = m_aSubgroups[n];
  1614.                 }
  1615.  
  1616.                 if ( pNewLast != 0 ) // found?
  1617.                     break;
  1618.             }
  1619.  
  1620.             if ( pl == m_pLine || m_pParent == 0 )
  1621.             {
  1622.                 wxLogTrace( _T("wxFileConfig"),
  1623.                             _T("  ------- No previous group found -------") );
  1624.                 
  1625.                 wxASSERT_MSG( !pNewLast || m_pLine == 0,
  1626.                               _T("how comes it has the same line as we?") );
  1627.  
  1628.                     // we've reached the group line without finding any subgroups,
  1629.                     // or realised we removed the last group from the root.
  1630.  
  1631.                 m_pLastGroup = 0;
  1632.             }
  1633.             else
  1634.             {
  1635.                 wxLogTrace( _T("wxFileConfig"),
  1636.                             _T("  ------- Last Group set to '%s' -------"),
  1637.                             pNewLast->Name().c_str() );
  1638.  
  1639.                 m_pLastGroup = pNewLast;
  1640.             }
  1641.         }
  1642.  
  1643.         m_pConfig->LineListRemove(pLine);
  1644.     }
  1645.     else
  1646.     {
  1647.         wxLogTrace( _T("wxFileConfig"),
  1648.                     _T("  No line entry for Group '%s'?"),
  1649.                     pGroup->Name().c_str() );
  1650.     }
  1651.  
  1652.     SetDirty();
  1653.  
  1654.     m_aSubgroups.Remove(pGroup);
  1655.     delete pGroup;
  1656.  
  1657.     return TRUE;
  1658. }
  1659.  
  1660. bool wxFileConfigGroup::DeleteEntry(const wxChar *szName)
  1661. {
  1662.   wxFileConfigEntry *pEntry = FindEntry(szName);
  1663.   wxCHECK( pEntry != NULL, FALSE );  // deleting non existing item?
  1664.  
  1665.   wxFileConfigLineList *pLine = pEntry->GetLine();
  1666.   if ( pLine != NULL ) {
  1667.     // notice that we may do this test inside the previous "if" because the
  1668.     // last entry's line is surely !NULL
  1669.     if ( pEntry == m_pLastEntry ) {
  1670.       // our last entry is being deleted - find the last one which stays
  1671.       wxASSERT( m_pLine != NULL );  // if we have an entry with !NULL pLine...
  1672.  
  1673.       // go back until we find another entry or reach the group's line
  1674.       wxFileConfigEntry *pNewLast = NULL;
  1675.       size_t n, nEntries = m_aEntries.Count();
  1676.       wxFileConfigLineList *pl;
  1677.       for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() ) {
  1678.         // is it our subgroup?
  1679.         for ( n = 0; (pNewLast == NULL) && (n < nEntries); n++ ) {
  1680.           if ( m_aEntries[n]->GetLine() == m_pLine )
  1681.             pNewLast = m_aEntries[n];
  1682.         }
  1683.  
  1684.         if ( pNewLast != NULL ) // found?
  1685.           break;
  1686.       }
  1687.  
  1688.       if ( pl == m_pLine ) {
  1689.         wxASSERT( !pNewLast );  // how comes it has the same line as we?
  1690.  
  1691.         // we've reached the group line without finding any subgroups
  1692.         m_pLastEntry = NULL;
  1693.       }
  1694.       else
  1695.         m_pLastEntry = pNewLast;
  1696.     }
  1697.  
  1698.     m_pConfig->LineListRemove(pLine);
  1699.   }
  1700.  
  1701.   // we must be written back for the changes to be saved
  1702.   SetDirty();
  1703.  
  1704.   m_aEntries.Remove(pEntry);
  1705.   delete pEntry;
  1706.  
  1707.   return TRUE;
  1708. }
  1709.  
  1710. // ----------------------------------------------------------------------------
  1711. //
  1712. // ----------------------------------------------------------------------------
  1713. void wxFileConfigGroup::SetDirty()
  1714. {
  1715.   m_bDirty = TRUE;
  1716.   if ( Parent() != NULL )             // propagate upwards
  1717.     Parent()->SetDirty();
  1718. }
  1719.  
  1720. // ============================================================================
  1721. // wxFileConfig::wxFileConfigEntry
  1722. // ============================================================================
  1723.  
  1724. // ----------------------------------------------------------------------------
  1725. // ctor
  1726. // ----------------------------------------------------------------------------
  1727. wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup *pParent,
  1728.                                        const wxString& strName,
  1729.                                        int nLine)
  1730.                          : m_strName(strName)
  1731. {
  1732.   wxASSERT( !strName.IsEmpty() );
  1733.  
  1734.   m_pParent = pParent;
  1735.   m_nLine   = nLine;
  1736.   m_pLine   = NULL;
  1737.  
  1738.   m_bDirty =
  1739.   m_bHasValue = FALSE;
  1740.  
  1741.   m_bImmutable = strName[0] == wxCONFIG_IMMUTABLE_PREFIX;
  1742.   if ( m_bImmutable )
  1743.     m_strName.erase(0, 1);  // remove first character
  1744. }
  1745.  
  1746. // ----------------------------------------------------------------------------
  1747. // set value
  1748. // ----------------------------------------------------------------------------
  1749.  
  1750. void wxFileConfigEntry::SetLine(wxFileConfigLineList *pLine)
  1751. {
  1752.   if ( m_pLine != NULL ) {
  1753.     wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
  1754.                  Name().c_str(), m_pParent->GetFullName().c_str());
  1755.   }
  1756.  
  1757.   m_pLine = pLine;
  1758.   Group()->SetLastEntry(this);
  1759. }
  1760.  
  1761. // second parameter is FALSE if we read the value from file and prevents the
  1762. // entry from being marked as 'dirty'
  1763. void wxFileConfigEntry::SetValue(const wxString& strValue, bool bUser)
  1764. {
  1765.     if ( bUser && IsImmutable() )
  1766.     {
  1767.         wxLogWarning( _("attempt to change immutable key '%s' ignored."),
  1768.                       Name().c_str());
  1769.         return;
  1770.     }
  1771.  
  1772.         // do nothing if it's the same value: but don't test for it
  1773.         // if m_bHasValue hadn't been set yet or we'd never write
  1774.         // empty values to the file
  1775.  
  1776.     if ( m_bHasValue && strValue == m_strValue )
  1777.         return;
  1778.  
  1779.     m_bHasValue = TRUE;
  1780.     m_strValue = strValue;
  1781.  
  1782.     if ( bUser )
  1783.     {
  1784.         wxString    strValFiltered;
  1785.  
  1786.         if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS )
  1787.         {
  1788.             strValFiltered = strValue;
  1789.         }
  1790.         else {
  1791.             strValFiltered = FilterOutValue(strValue);
  1792.         }
  1793.  
  1794.         wxString    strLine;
  1795.         strLine << FilterOutEntryName(m_strName) << wxT('=') << strValFiltered;
  1796.  
  1797.         if ( m_pLine != 0 )
  1798.         {
  1799.             // entry was read from the local config file, just modify the line
  1800.             m_pLine->SetText(strLine);
  1801.         }
  1802.         else {
  1803.             // add a new line to the file
  1804.             wxASSERT( m_nLine == wxNOT_FOUND );   // consistency check
  1805.  
  1806.             m_pLine = Group()->Config()->LineListInsert(strLine,
  1807.                                                         Group()->GetLastEntryLine());
  1808.             Group()->SetLastEntry(this);
  1809.         }
  1810.  
  1811.         SetDirty();
  1812.     }
  1813. }
  1814.  
  1815. void wxFileConfigEntry::SetDirty()
  1816. {
  1817.   m_bDirty = TRUE;
  1818.   Group()->SetDirty();
  1819. }
  1820.  
  1821. // ============================================================================
  1822. // global functions
  1823. // ============================================================================
  1824.  
  1825. // ----------------------------------------------------------------------------
  1826. // compare functions for array sorting
  1827. // ----------------------------------------------------------------------------
  1828.  
  1829. int CompareEntries(wxFileConfigEntry *p1, wxFileConfigEntry *p2)
  1830. {
  1831.   #if wxCONFIG_CASE_SENSITIVE
  1832.     return wxStrcmp(p1->Name(), p2->Name());
  1833.   #else
  1834.     return wxStricmp(p1->Name(), p2->Name());
  1835.   #endif
  1836. }
  1837.  
  1838. int CompareGroups(wxFileConfigGroup *p1, wxFileConfigGroup *p2)
  1839. {
  1840.   #if wxCONFIG_CASE_SENSITIVE
  1841.     return wxStrcmp(p1->Name(), p2->Name());
  1842.   #else
  1843.     return wxStricmp(p1->Name(), p2->Name());
  1844.   #endif
  1845. }
  1846.  
  1847. // ----------------------------------------------------------------------------
  1848. // filter functions
  1849. // ----------------------------------------------------------------------------
  1850.  
  1851. // undo FilterOutValue
  1852. static wxString FilterInValue(const wxString& str)
  1853. {
  1854.   wxString strResult;
  1855.   strResult.Alloc(str.Len());
  1856.  
  1857.   bool bQuoted = !str.IsEmpty() && str[0] == '"';
  1858.  
  1859.   for ( size_t n = bQuoted ? 1 : 0; n < str.Len(); n++ ) {
  1860.     if ( str[n] == wxT('\\') ) {
  1861.       switch ( str[++n] ) {
  1862.         case wxT('n'):
  1863.           strResult += wxT('\n');
  1864.           break;
  1865.  
  1866.         case wxT('r'):
  1867.           strResult += wxT('\r');
  1868.           break;
  1869.  
  1870.         case wxT('t'):
  1871.           strResult += wxT('\t');
  1872.           break;
  1873.  
  1874.         case wxT('\\'):
  1875.           strResult += wxT('\\');
  1876.           break;
  1877.  
  1878.         case wxT('"'):
  1879.           strResult += wxT('"');
  1880.           break;
  1881.       }
  1882.     }
  1883.     else {
  1884.       if ( str[n] != wxT('"') || !bQuoted )
  1885.         strResult += str[n];
  1886.       else if ( n != str.Len() - 1 ) {
  1887.         wxLogWarning(_("unexpected \" at position %d in '%s'."),
  1888.                      n, str.c_str());
  1889.       }
  1890.       //else: it's the last quote of a quoted string, ok
  1891.     }
  1892.   }
  1893.  
  1894.   return strResult;
  1895. }
  1896.  
  1897. // quote the string before writing it to file
  1898. static wxString FilterOutValue(const wxString& str)
  1899. {
  1900.    if ( !str )
  1901.       return str;
  1902.  
  1903.   wxString strResult;
  1904.   strResult.Alloc(str.Len());
  1905.  
  1906.   // quoting is necessary to preserve spaces in the beginning of the string
  1907.   bool bQuote = wxIsspace(str[0]) || str[0] == wxT('"');
  1908.  
  1909.   if ( bQuote )
  1910.     strResult += wxT('"');
  1911.  
  1912.   wxChar c;
  1913.   for ( size_t n = 0; n < str.Len(); n++ ) {
  1914.     switch ( str[n] ) {
  1915.       case wxT('\n'):
  1916.         c = wxT('n');
  1917.         break;
  1918.  
  1919.       case wxT('\r'):
  1920.         c = wxT('r');
  1921.         break;
  1922.  
  1923.       case wxT('\t'):
  1924.         c = wxT('t');
  1925.         break;
  1926.  
  1927.       case wxT('\\'):
  1928.         c = wxT('\\');
  1929.         break;
  1930.  
  1931.       case wxT('"'):
  1932.         if ( bQuote ) {
  1933.           c = wxT('"');
  1934.           break;
  1935.         }
  1936.         //else: fall through
  1937.  
  1938.       default:
  1939.         strResult += str[n];
  1940.         continue;   // nothing special to do
  1941.     }
  1942.  
  1943.     // we get here only for special characters
  1944.     strResult << wxT('\\') << c;
  1945.   }
  1946.  
  1947.   if ( bQuote )
  1948.     strResult += wxT('"');
  1949.  
  1950.   return strResult;
  1951. }
  1952.  
  1953. // undo FilterOutEntryName
  1954. static wxString FilterInEntryName(const wxString& str)
  1955. {
  1956.   wxString strResult;
  1957.   strResult.Alloc(str.Len());
  1958.  
  1959.   for ( const wxChar *pc = str.c_str(); *pc != '\0'; pc++ ) {
  1960.     if ( *pc == wxT('\\') )
  1961.       pc++;
  1962.  
  1963.     strResult += *pc;
  1964.   }
  1965.  
  1966.   return strResult;
  1967. }
  1968.  
  1969. // sanitize entry or group name: insert '\\' before any special characters
  1970. static wxString FilterOutEntryName(const wxString& str)
  1971. {
  1972.   wxString strResult;
  1973.   strResult.Alloc(str.Len());
  1974.  
  1975.   for ( const wxChar *pc = str.c_str(); *pc != wxT('\0'); pc++ ) {
  1976.     wxChar c = *pc;
  1977.  
  1978.     // we explicitly allow some of "safe" chars and 8bit ASCII characters
  1979.     // which will probably never have special meaning
  1980.     // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
  1981.     //     should *not* be quoted
  1982.     if ( !wxIsalnum(c) && !wxStrchr(wxT("@_/-!.*%"), c) && ((c & 0x80) == 0) )
  1983.       strResult += wxT('\\');
  1984.  
  1985.     strResult += c;
  1986.   }
  1987.  
  1988.   return strResult;
  1989. }
  1990.  
  1991. // we can't put ?: in the ctor initializer list because it confuses some
  1992. // broken compilers (Borland C++)
  1993. static wxString GetAppName(const wxString& appName)
  1994. {
  1995.     if ( !appName && wxTheApp )
  1996.         return wxTheApp->GetAppName();
  1997.     else
  1998.         return appName;
  1999. }
  2000.  
  2001. #endif // wxUSE_CONFIG
  2002.  
  2003.  
  2004. // vi:sts=4:sw=4:et
  2005.