1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/fileconf.cpp 
   3 // Purpose:     implementation of wxFileConfig derivation of wxConfig 
   4 // Author:      Vadim Zeitlin 
   6 // Created:     07.04.98 (adapted from appconf.cpp) 
   8 // Copyright:   (c) 1997 Karsten Ballüder   &  Vadim Zeitlin 
   9 //                       Ballueder@usa.net     <zeitlin@dptmaths.ens-cachan.fr> 
  10 // Licence:     wxWindows licence 
  11 /////////////////////////////////////////////////////////////////////////////// 
  13 // ---------------------------------------------------------------------------- 
  15 // ---------------------------------------------------------------------------- 
  17 // For compilers that support precompilation, includes "wx.h". 
  18 #include  "wx/wxprec.h" 
  24 #if wxUSE_CONFIG && wxUSE_FILECONFIG 
  27     #include  "wx/dynarray.h" 
  28     #include  "wx/string.h" 
  32     #include  "wx/utils.h"    // for wxGetHomeDir 
  34         #include  "wx/stream.h" 
  35     #endif // wxUSE_STREAMS 
  39 #include  "wx/textfile.h" 
  40 #include  "wx/memtext.h" 
  41 #include  "wx/config.h" 
  42 #include  "wx/fileconf.h" 
  43 #include  "wx/filefn.h" 
  45 #include  "wx/stdpaths.h" 
  47 #if defined(__WXMAC__) 
  48     #include  "wx/mac/private.h"  // includes mac headers 
  51 #if defined(__WXMSW__) 
  52     #include "wx/msw/private.h" 
  62 // ---------------------------------------------------------------------------- 
  64 // ---------------------------------------------------------------------------- 
  65 #define CONST_CAST ((wxFileConfig *)this)-> 
  67 // ---------------------------------------------------------------------------- 
  69 // ---------------------------------------------------------------------------- 
  75 #define FILECONF_TRACE_MASK _T("fileconf") 
  77 // ---------------------------------------------------------------------------- 
  78 // global functions declarations 
  79 // ---------------------------------------------------------------------------- 
  81 // compare functions for sorting the arrays 
  82 static int LINKAGEMODE 
CompareEntries(wxFileConfigEntry 
*p1
, wxFileConfigEntry 
*p2
); 
  83 static int LINKAGEMODE 
CompareGroups(wxFileConfigGroup 
*p1
, wxFileConfigGroup 
*p2
); 
  86 static wxString 
FilterInValue(const wxString
& str
); 
  87 static wxString 
FilterOutValue(const wxString
& str
); 
  89 static wxString 
FilterInEntryName(const wxString
& str
); 
  90 static wxString 
FilterOutEntryName(const wxString
& str
); 
  92 // get the name to use in wxFileConfig ctor 
  93 static wxString 
GetAppName(const wxString
& appname
); 
  95 // ============================================================================ 
  97 // ============================================================================ 
  99 // ---------------------------------------------------------------------------- 
 100 // "template" array types 
 101 // ---------------------------------------------------------------------------- 
 103 #ifdef WXMAKINGDLL_BASE 
 104     WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry 
*, ArrayEntries
, 
 106     WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup 
*, ArrayGroups
, 
 109     WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry 
*, ArrayEntries
); 
 110     WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup 
*, ArrayGroups
); 
 113 // ---------------------------------------------------------------------------- 
 114 // wxFileConfigLineList 
 115 // ---------------------------------------------------------------------------- 
 117 // we store all lines of the local config file as a linked list in memory 
 118 class wxFileConfigLineList
 
 121   void SetNext(wxFileConfigLineList 
*pNext
)  { m_pNext 
= pNext
; } 
 122   void SetPrev(wxFileConfigLineList 
*pPrev
)  { m_pPrev 
= pPrev
; } 
 125   wxFileConfigLineList(const wxString
& str
, 
 126                        wxFileConfigLineList 
*pNext 
= NULL
) : m_strLine(str
) 
 127     { SetNext(pNext
); SetPrev(NULL
); } 
 129   // next/prev nodes in the linked list 
 130   wxFileConfigLineList 
*Next() const { return m_pNext
;  } 
 131   wxFileConfigLineList 
*Prev() const { return m_pPrev
;  } 
 133   // get/change lines text 
 134   void SetText(const wxString
& str
) { m_strLine 
= str
;  } 
 135   const wxString
& Text() const { return m_strLine
; } 
 138   wxString  m_strLine
;                  // line contents 
 139   wxFileConfigLineList 
*m_pNext
,        // next node 
 140                        *m_pPrev
;        // previous one 
 142     DECLARE_NO_COPY_CLASS(wxFileConfigLineList
) 
 145 // ---------------------------------------------------------------------------- 
 146 // wxFileConfigEntry: a name/value pair 
 147 // ---------------------------------------------------------------------------- 
 149 class wxFileConfigEntry
 
 152   wxFileConfigGroup 
*m_pParent
; // group that contains us 
 154   wxString      m_strName
,      // entry name 
 156   bool          m_bImmutable
:1, // can be overriden locally? 
 157                 m_bHasValue
:1;  // set after first call to SetValue() 
 159   int           m_nLine
;        // used if m_pLine == NULL only 
 161   // pointer to our line in the linked list or NULL if it was found in global 
 162   // file (which we don't modify) 
 163   wxFileConfigLineList 
*m_pLine
; 
 166   wxFileConfigEntry(wxFileConfigGroup 
*pParent
, 
 167                     const wxString
& strName
, int nLine
); 
 170   const wxString
& Name()        const { return m_strName
;    } 
 171   const wxString
& Value()       const { return m_strValue
;   } 
 172   wxFileConfigGroup 
*Group()    const { return m_pParent
;    } 
 173   bool            IsImmutable() const { return m_bImmutable
; } 
 174   bool            IsLocal()     const { return m_pLine 
!= 0; } 
 175   int             Line()        const { return m_nLine
;      } 
 176   wxFileConfigLineList 
* 
 177                   GetLine()     const { return m_pLine
;      } 
 179   // modify entry attributes 
 180   void SetValue(const wxString
& strValue
, bool bUser 
= true); 
 181   void SetLine(wxFileConfigLineList 
*pLine
); 
 183     DECLARE_NO_COPY_CLASS(wxFileConfigEntry
) 
 186 // ---------------------------------------------------------------------------- 
 187 // wxFileConfigGroup: container of entries and other groups 
 188 // ---------------------------------------------------------------------------- 
 190 class wxFileConfigGroup
 
 193   wxFileConfig 
*m_pConfig
;          // config object we belong to 
 194   wxFileConfigGroup  
*m_pParent
;    // parent group (NULL for root group) 
 195   ArrayEntries  m_aEntries
;         // entries in this group 
 196   ArrayGroups   m_aSubgroups
;       // subgroups 
 197   wxString      m_strName
;          // group's name 
 198   wxFileConfigLineList 
*m_pLine
;    // pointer to our line in the linked list 
 199   wxFileConfigEntry 
*m_pLastEntry
;  // last entry/subgroup of this group in the 
 200   wxFileConfigGroup 
*m_pLastGroup
;  // local file (we insert new ones after it) 
 202   // DeleteSubgroupByName helper 
 203   bool DeleteSubgroup(wxFileConfigGroup 
*pGroup
); 
 206   void UpdateGroupAndSubgroupsLines(); 
 210   wxFileConfigGroup(wxFileConfigGroup 
*pParent
, const wxString
& strName
, wxFileConfig 
*); 
 212   // dtor deletes all entries and subgroups also 
 213   ~wxFileConfigGroup(); 
 216   const wxString
& Name()    const { return m_strName
; } 
 217   wxFileConfigGroup    
*Parent()  const { return m_pParent
; } 
 218   wxFileConfig   
*Config()  const { return m_pConfig
; } 
 220   const ArrayEntries
& Entries() const { return m_aEntries
;   } 
 221   const ArrayGroups
&  Groups()  const { return m_aSubgroups
; } 
 222   bool  IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); } 
 224   // find entry/subgroup (NULL if not found) 
 225   wxFileConfigGroup 
*FindSubgroup(const wxChar 
*szName
) const; 
 226   wxFileConfigEntry 
*FindEntry   (const wxChar 
*szName
) const; 
 228   // delete entry/subgroup, return false if doesn't exist 
 229   bool DeleteSubgroupByName(const wxChar 
*szName
); 
 230   bool DeleteEntry(const wxChar 
*szName
); 
 232   // create new entry/subgroup returning pointer to newly created element 
 233   wxFileConfigGroup 
*AddSubgroup(const wxString
& strName
); 
 234   wxFileConfigEntry 
*AddEntry   (const wxString
& strName
, int nLine 
= wxNOT_FOUND
); 
 236   void SetLine(wxFileConfigLineList 
*pLine
); 
 238   // rename: no checks are done to ensure that the name is unique! 
 239   void Rename(const wxString
& newName
); 
 242   wxString 
GetFullName() const; 
 244   // get the last line belonging to an entry/subgroup of this group 
 245   wxFileConfigLineList 
*GetGroupLine();     // line which contains [group] 
 246   wxFileConfigLineList 
*GetLastEntryLine(); // after which our subgroups start 
 247   wxFileConfigLineList 
*GetLastGroupLine(); // after which the next group starts 
 249   // called by entries/subgroups when they're created/deleted 
 250   void SetLastEntry(wxFileConfigEntry 
*pEntry
); 
 251   void SetLastGroup(wxFileConfigGroup 
*pGroup
) 
 252     { m_pLastGroup 
= pGroup
; } 
 254   DECLARE_NO_COPY_CLASS(wxFileConfigGroup
) 
 257 // ============================================================================ 
 259 // ============================================================================ 
 261 // ---------------------------------------------------------------------------- 
 263 // ---------------------------------------------------------------------------- 
 265 // this function modifies in place the given wxFileName object if it doesn't 
 266 // already have an extension 
 268 // note that it's slightly misnamed under Mac as there it doesn't add an 
 269 // extension but modifies the file name instead, so you shouldn't suppose that 
 270 // fn.HasExt() is true after it returns 
 271 static void AddConfFileExtIfNeeded(wxFileName
& fn
) 
 275 #if defined( __WXMAC__ ) 
 276         fn
.SetName(fn
.GetName() + wxT(" Preferences")); 
 277 #elif defined( __UNIX__ ) 
 278         fn
.SetExt(wxT(".conf")); 
 280         fn
.SetExt(wxT(".ini")); 
 285 wxString 
wxFileConfig::GetGlobalDir() 
 287     return wxStandardPaths::Get().GetConfigDir(); 
 290 wxString 
wxFileConfig::GetLocalDir(int style
) 
 294     wxStandardPathsBase
& stdp 
= wxStandardPaths::Get(); 
 296     // it so happens that user data directory is a subdirectory of user config 
 297     // directory on all supported platforms, which explains why we use it here 
 298     return style 
& wxCONFIG_USE_SUBDIR 
? stdp
.GetUserDataDir() 
 299                                        : stdp
.GetUserConfigDir(); 
 302 wxFileName 
wxFileConfig::GetGlobalFile(const wxString
& szFile
) 
 304     wxFileName 
fn(GetGlobalDir(), szFile
); 
 306     AddConfFileExtIfNeeded(fn
); 
 311 wxFileName 
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
) 
 313     wxFileName 
fn(GetLocalDir(style
), szFile
); 
 316     if ( !(style 
& wxCONFIG_USE_SUBDIR
) ) 
 318         // dot-files under Unix start with, well, a dot (but OTOH they usually 
 319         // don't have any specific extension) 
 320         fn
.SetName(wxT('.') + fn
.GetName()); 
 322     else // we do append ".conf" extension to config files in subdirectories 
 325         AddConfFileExtIfNeeded(fn
); 
 331 // ---------------------------------------------------------------------------- 
 333 // ---------------------------------------------------------------------------- 
 334 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
) 
 336 void wxFileConfig::Init() 
 339     m_pRootGroup    
= new wxFileConfigGroup(NULL
, wxEmptyString
, this); 
 344     // It's not an error if (one of the) file(s) doesn't exist. 
 346     // parse the global file 
 347     if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() ) 
 349         wxTextFile 
fileGlobal(m_fnGlobalFile
.GetFullPath()); 
 351         if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) ) 
 353             Parse(fileGlobal
, false /* global */); 
 358             wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str()); 
 362     // parse the local file 
 363     if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() ) 
 365         wxTextFile 
fileLocal(m_fnLocalFile
.GetFullPath()); 
 366         if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) ) 
 368             Parse(fileLocal
, true /* local */); 
 373             wxLogWarning(_("can't open user configuration file '%s'."),  m_fnLocalFile
.GetFullPath().c_str() ); 
 380 // constructor supports creation of wxFileConfig objects of any type 
 381 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
, 
 382                            const wxString
& strLocal
, const wxString
& strGlobal
, 
 384                            const wxMBConv
& conv
) 
 385             : wxConfigBase(::GetAppName(appName
), vendorName
, 
 388               m_fnLocalFile(strLocal
), 
 389               m_fnGlobalFile(strGlobal
), 
 392     // Make up names for files if empty 
 393     if ( !m_fnLocalFile
.IsOk() && (style 
& wxCONFIG_USE_LOCAL_FILE
) ) 
 394         m_fnLocalFile 
= GetLocalFile(GetAppName(), style
); 
 396     if ( !m_fnGlobalFile
.IsOk() && (style 
& wxCONFIG_USE_GLOBAL_FILE
) ) 
 397         m_fnGlobalFile 
= GetGlobalFile(GetAppName()); 
 399     // Check if styles are not supplied, but filenames are, in which case 
 400     // add the correct styles. 
 401     if ( m_fnLocalFile
.IsOk() ) 
 402         SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
); 
 404     if ( m_fnGlobalFile
.IsOk() ) 
 405         SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
); 
 407     // if the path is not absolute, prepend the standard directory to it 
 408     // unless explicitly asked not to 
 409     if ( !(style 
& wxCONFIG_USE_RELATIVE_PATH
) ) 
 411         if ( m_fnLocalFile
.IsOk() ) 
 412             m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
)); 
 414         if ( m_fnGlobalFile
.IsOk() ) 
 415             m_fnGlobalFile
.MakeAbsolute(GetGlobalDir()); 
 425 wxFileConfig::wxFileConfig(wxInputStream 
&inStream
, const wxMBConv
& conv
) 
 426             : m_conv(conv
.Clone()) 
 428     // always local_file when this constructor is called (?) 
 429     SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
); 
 432     m_pRootGroup    
= new wxFileConfigGroup(NULL
, wxEmptyString
, this); 
 437     // read the entire stream contents in memory 
 440         static const size_t chunkLen 
= 1024; 
 442         wxMemoryBuffer 
buf(chunkLen
); 
 445             inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
); 
 446             buf
.UngetAppendBuf(inStream
.LastRead()); 
 448             const wxStreamError err 
= inStream
.GetLastError(); 
 450             if ( err 
!= wxSTREAM_NO_ERROR 
&& err 
!= wxSTREAM_EOF 
) 
 452                 wxLogError(_("Error reading config options.")); 
 456         while ( !inStream
.Eof() ); 
 460         str 
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen(), &len
); 
 461         if ( !len 
&& buf
.GetDataLen() ) 
 463             wxLogError(_("Failed to read config options.")); 
 465 #else // !wxUSE_UNICODE 
 466         // no need for conversion 
 467         str
.assign((char *)buf
.GetData(), buf
.GetDataLen()); 
 468 #endif // wxUSE_UNICODE/!wxUSE_UNICODE 
 472     // translate everything to the current (platform-dependent) line 
 473     // termination character 
 474     str 
= wxTextBuffer::Translate(str
); 
 476     wxMemoryText memText
; 
 478     // Now we can add the text to the memory text. To do this we extract line 
 479     // by line from the translated string, until we've reached the end. 
 481     // VZ: all this is horribly inefficient, we should do the translation on 
 482     //     the fly in one pass saving both memory and time (TODO) 
 484     const wxChar 
*pEOL 
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
); 
 485     const size_t EOLLen 
= wxStrlen(pEOL
); 
 487     int posLineStart 
= str
.Find(pEOL
); 
 488     while ( posLineStart 
!= -1 ) 
 490         wxString 
line(str
.Left(posLineStart
)); 
 492         memText
.AddLine(line
); 
 494         str 
= str
.Mid(posLineStart 
+ EOLLen
); 
 496         posLineStart 
= str
.Find(pEOL
); 
 499     // also add whatever we have left in the translated string. 
 501         memText
.AddLine(str
); 
 503     // Finally we can parse it all. 
 504     Parse(memText
, true /* local */); 
 510 #endif // wxUSE_STREAMS 
 512 void wxFileConfig::CleanUp() 
 516     wxFileConfigLineList 
*pCur 
= m_linesHead
; 
 517     while ( pCur 
!= NULL 
) { 
 518         wxFileConfigLineList 
*pNext 
= pCur
->Next(); 
 524 wxFileConfig::~wxFileConfig() 
 533 // ---------------------------------------------------------------------------- 
 534 // parse a config file 
 535 // ---------------------------------------------------------------------------- 
 537 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
) 
 539   const wxChar 
*pStart
; 
 543   size_t nLineCount 
= buffer
.GetLineCount(); 
 545   for ( size_t n 
= 0; n 
< nLineCount
; n
++ ) 
 549     // add the line to linked list 
 552       LineListAppend(strLine
); 
 554       // let the root group have its start line as well 
 557         m_pCurrentGroup
->SetLine(m_linesTail
); 
 562     // skip leading spaces 
 563     for ( pStart 
= strLine
; wxIsspace(*pStart
); pStart
++ ) 
 566     // skip blank/comment lines 
 567     if ( *pStart 
== wxT('\0')|| *pStart 
== wxT(';') || *pStart 
== wxT('#') ) 
 570     if ( *pStart 
== wxT('[') ) {          // a new group 
 573       while ( *++pEnd 
!= wxT(']') ) { 
 574         if ( *pEnd 
== wxT('\\') ) { 
 575             // the next char is escaped, so skip it even if it is ']' 
 579         if ( *pEnd 
== wxT('\n') || *pEnd 
== wxT('\0') ) { 
 580             // we reached the end of line, break out of the loop 
 585       if ( *pEnd 
!= wxT(']') ) { 
 586         wxLogError(_("file '%s': unexpected character %c at line %d."), 
 587                    buffer
.GetName(), *pEnd
, n 
+ 1); 
 588         continue; // skip this line 
 591       // group name here is always considered as abs path 
 594       strGroup 
<< wxCONFIG_PATH_SEPARATOR
 
 595                << FilterInEntryName(wxString(pStart
, pEnd 
- pStart
)); 
 597       // will create it if doesn't yet exist 
 602         if ( m_pCurrentGroup
->Parent() ) 
 603           m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
); 
 604         m_pCurrentGroup
->SetLine(m_linesTail
); 
 607       // check that there is nothing except comments left on this line 
 609       while ( *++pEnd 
!= wxT('\0') && bCont 
) { 
 618             // ignore whitespace ('\n' impossible here) 
 622             wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."), 
 623                          buffer
.GetName(), n 
+ 1, pEnd
); 
 630       while ( *pEnd 
&& *pEnd 
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) { 
 631         if ( *pEnd 
== wxT('\\') ) { 
 632           // next character may be space or not - still take it because it's 
 633           // quoted (unless there is nothing) 
 636             // the error message will be given below anyhow 
 644       wxString 
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim())); 
 647       while ( wxIsspace(*pEnd
) ) 
 650       if ( *pEnd
++ != wxT('=') ) { 
 651         wxLogError(_("file '%s', line %d: '=' expected."), 
 652                    buffer
.GetName(), n 
+ 1); 
 655         wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(strKey
); 
 657         if ( pEntry 
== NULL 
) { 
 659           pEntry 
= m_pCurrentGroup
->AddEntry(strKey
, n
); 
 662           if ( bLocal 
&& pEntry
->IsImmutable() ) { 
 663             // immutable keys can't be changed by user 
 664             wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."), 
 665                          buffer
.GetName(), n 
+ 1, strKey
.c_str()); 
 668           // the condition below catches the cases (a) and (b) but not (c): 
 669           //  (a) global key found second time in global file 
 670           //  (b) key found second (or more) time in local file 
 671           //  (c) key from global file now found in local one 
 672           // which is exactly what we want. 
 673           else if ( !bLocal 
|| pEntry
->IsLocal() ) { 
 674             wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."), 
 675                          buffer
.GetName(), n 
+ 1, strKey
.c_str(), pEntry
->Line()); 
 681           pEntry
->SetLine(m_linesTail
); 
 684         while ( wxIsspace(*pEnd
) ) 
 687         wxString value 
= pEnd
; 
 688         if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) ) 
 689             value 
= FilterInValue(value
); 
 691         pEntry
->SetValue(value
, false); 
 697 // ---------------------------------------------------------------------------- 
 699 // ---------------------------------------------------------------------------- 
 701 void wxFileConfig::SetRootPath() 
 704     m_pCurrentGroup 
= m_pRootGroup
; 
 708 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
) 
 710     wxArrayString aParts
; 
 712     if ( strPath
.empty() ) { 
 717     if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR 
) { 
 719         wxSplitPath(aParts
, strPath
); 
 722         // relative path, combine with current one 
 723         wxString strFullPath 
= m_strPath
; 
 724         strFullPath 
<< wxCONFIG_PATH_SEPARATOR 
<< strPath
; 
 725         wxSplitPath(aParts
, strFullPath
); 
 728     // change current group 
 730     m_pCurrentGroup 
= m_pRootGroup
; 
 731     for ( n 
= 0; n 
< aParts
.GetCount(); n
++ ) { 
 732         wxFileConfigGroup 
*pNextGroup 
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]); 
 733         if ( pNextGroup 
== NULL 
) 
 735             if ( !createMissingComponents 
) 
 738             pNextGroup 
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]); 
 741         m_pCurrentGroup 
= pNextGroup
; 
 744     // recombine path parts in one variable 
 746     for ( n 
= 0; n 
< aParts
.GetCount(); n
++ ) { 
 747         m_strPath 
<< wxCONFIG_PATH_SEPARATOR 
<< aParts
[n
]; 
 753 void wxFileConfig::SetPath(const wxString
& strPath
) 
 755     DoSetPath(strPath
, true /* create missing path components */); 
 758 // ---------------------------------------------------------------------------- 
 760 // ---------------------------------------------------------------------------- 
 762 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const 
 765     return GetNextGroup(str
, lIndex
); 
 768 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const 
 770     if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) { 
 771         str 
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name(); 
 778 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const 
 781     return GetNextEntry(str
, lIndex
); 
 784 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const 
 786     if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) { 
 787         str 
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name(); 
 794 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const 
 796     size_t n 
= m_pCurrentGroup
->Entries().GetCount(); 
 798         wxFileConfigGroup 
*pOldCurrentGroup 
= m_pCurrentGroup
; 
 799         size_t nSubgroups 
= m_pCurrentGroup
->Groups().GetCount(); 
 800         for ( size_t nGroup 
= 0; nGroup 
< nSubgroups
; nGroup
++ ) { 
 801             CONST_CAST m_pCurrentGroup 
= m_pCurrentGroup
->Groups()[nGroup
]; 
 802             n 
+= GetNumberOfEntries(true); 
 803             CONST_CAST m_pCurrentGroup 
= pOldCurrentGroup
; 
 810 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const 
 812     size_t n 
= m_pCurrentGroup
->Groups().GetCount(); 
 814         wxFileConfigGroup 
*pOldCurrentGroup 
= m_pCurrentGroup
; 
 815         size_t nSubgroups 
= m_pCurrentGroup
->Groups().GetCount(); 
 816         for ( size_t nGroup 
= 0; nGroup 
< nSubgroups
; nGroup
++ ) { 
 817             CONST_CAST m_pCurrentGroup 
= m_pCurrentGroup
->Groups()[nGroup
]; 
 818             n 
+= GetNumberOfGroups(true); 
 819             CONST_CAST m_pCurrentGroup 
= pOldCurrentGroup
; 
 826 // ---------------------------------------------------------------------------- 
 827 // tests for existence 
 828 // ---------------------------------------------------------------------------- 
 830 bool wxFileConfig::HasGroup(const wxString
& strName
) const 
 832     // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/") 
 833     // but there is no group with empty name so treat this separately 
 834     if ( strName
.empty() ) 
 837     const wxString pathOld 
= GetPath(); 
 839     wxFileConfig 
*self 
= wx_const_cast(wxFileConfig 
*, this); 
 841         rc 
= self
->DoSetPath(strName
, false /* don't create missing components */); 
 843     self
->SetPath(pathOld
); 
 848 bool wxFileConfig::HasEntry(const wxString
& entry
) const 
 850     // path is the part before the last "/" 
 851     wxString path 
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
); 
 853     // except in the special case of "/keyname" when there is nothing before "/" 
 854     if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR 
) 
 856         path 
= wxCONFIG_PATH_SEPARATOR
; 
 859     // change to the path of the entry if necessary and remember the old path 
 860     // to restore it later 
 862     wxFileConfig 
* const self 
= wx_const_cast(wxFileConfig 
*, this); 
 866         if ( pathOld
.empty() ) 
 867             pathOld 
= wxCONFIG_PATH_SEPARATOR
; 
 869         if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) ) 
 875     // check if the entry exists in this group 
 876     const bool exists 
= m_pCurrentGroup
->FindEntry( 
 877                             entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
; 
 879     // restore the old path if we changed it above 
 880     if ( !pathOld
.empty() ) 
 882         self
->SetPath(pathOld
); 
 888 // ---------------------------------------------------------------------------- 
 890 // ---------------------------------------------------------------------------- 
 892 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const 
 894     wxConfigPathChanger 
path(this, key
); 
 896     wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(path
.Name()); 
 897     if (pEntry 
== NULL
) { 
 901     *pStr 
= pEntry
->Value(); 
 906 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const 
 909     if ( !Read(key
, &str
) ) 
 912     // extra spaces shouldn't prevent us from reading numeric values 
 915     return str
.ToLong(pl
); 
 918 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
) 
 920     wxConfigPathChanger     
path(this, key
); 
 921     wxString                strName 
= path
.Name(); 
 923     wxLogTrace( FILECONF_TRACE_MASK
, 
 924                 _T("  Writing String '%s' = '%s' to Group '%s'"), 
 929     if ( strName
.empty() ) 
 931             // setting the value of a group is an error 
 933         wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") ); 
 935             // ... except if it's empty in which case it's a way to force it's creation 
 937         wxLogTrace( FILECONF_TRACE_MASK
, 
 938                     _T("  Creating group %s"), 
 939                     m_pCurrentGroup
->Name().c_str() ); 
 943             // this will add a line for this group if it didn't have it before 
 945         (void)m_pCurrentGroup
->GetGroupLine(); 
 949         // writing an entry check that the name is reasonable 
 950         if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX 
) 
 952             wxLogError( _("Config entry name cannot start with '%c'."), 
 953                         wxCONFIG_IMMUTABLE_PREFIX
); 
 957         wxFileConfigEntry   
*pEntry 
= m_pCurrentGroup
->FindEntry(strName
); 
 961             wxLogTrace( FILECONF_TRACE_MASK
, 
 962                         _T("  Adding Entry %s"), 
 964             pEntry 
= m_pCurrentGroup
->AddEntry(strName
); 
 967         wxLogTrace( FILECONF_TRACE_MASK
, 
 968                     _T("  Setting value %s"), 
 970         pEntry
->SetValue(szValue
); 
 978 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
) 
 980   return Write(key
, wxString::Format(_T("%ld"), lValue
)); 
 983 bool wxFileConfig::Flush(bool /* bCurrentOnly */) 
 985   if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() ) 
 988   // set the umask if needed 
 989   wxCHANGE_UMASK(m_umask
); 
 991   wxTempFile 
file(m_fnLocalFile
.GetFullPath()); 
 993   if ( !file
.IsOpened() ) 
 995     wxLogError(_("can't open user configuration file.")); 
 999   // write all strings to file 
1001   filetext
.reserve(4096); 
1002   for ( wxFileConfigLineList 
*p 
= m_linesHead
; p 
!= NULL
; p 
= p
->Next() ) 
1004     filetext 
<< p
->Text() << wxTextFile::GetEOL(); 
1007   if ( !file
.Write(filetext
, *m_conv
) ) 
1009     wxLogError(_("can't write user configuration file.")); 
1013   if ( !file
.Commit() ) 
1015       wxLogError(_("Failed to update user configuration file.")); 
1022 #if defined(__WXMAC__) 
1023   m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt'); 
1031 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
) 
1033     // save unconditionally, even if not dirty 
1034     for ( wxFileConfigLineList 
*p 
= m_linesHead
; p 
!= NULL
; p 
= p
->Next() ) 
1036         wxString line 
= p
->Text(); 
1037         line 
+= wxTextFile::GetEOL(); 
1039         wxCharBuffer 
buf(line
.mb_str(conv
)); 
1040         if ( !os
.Write(buf
, strlen(buf
)) ) 
1042             wxLogError(_("Error saving user configuration data.")); 
1053 #endif // wxUSE_STREAMS 
1055 // ---------------------------------------------------------------------------- 
1056 // renaming groups/entries 
1057 // ---------------------------------------------------------------------------- 
1059 bool wxFileConfig::RenameEntry(const wxString
& oldName
, 
1060                                const wxString
& newName
) 
1062     wxASSERT_MSG( !wxStrchr(oldName
, wxCONFIG_PATH_SEPARATOR
), 
1063                    _T("RenameEntry(): paths are not supported") ); 
1065     // check that the entry exists 
1066     wxFileConfigEntry 
*oldEntry 
= m_pCurrentGroup
->FindEntry(oldName
); 
1070     // check that the new entry doesn't already exist 
1071     if ( m_pCurrentGroup
->FindEntry(newName
) ) 
1074     // delete the old entry, create the new one 
1075     wxString value 
= oldEntry
->Value(); 
1076     if ( !m_pCurrentGroup
->DeleteEntry(oldName
) ) 
1081     wxFileConfigEntry 
*newEntry 
= m_pCurrentGroup
->AddEntry(newName
); 
1082     newEntry
->SetValue(value
); 
1087 bool wxFileConfig::RenameGroup(const wxString
& oldName
, 
1088                                const wxString
& newName
) 
1090     // check that the group exists 
1091     wxFileConfigGroup 
*group 
= m_pCurrentGroup
->FindSubgroup(oldName
); 
1095     // check that the new group doesn't already exist 
1096     if ( m_pCurrentGroup
->FindSubgroup(newName
) ) 
1099     group
->Rename(newName
); 
1106 // ---------------------------------------------------------------------------- 
1107 // delete groups/entries 
1108 // ---------------------------------------------------------------------------- 
1110 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
) 
1112   wxConfigPathChanger 
path(this, key
); 
1114   if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) ) 
1119   if ( bGroupIfEmptyAlso 
&& m_pCurrentGroup
->IsEmpty() ) { 
1120     if ( m_pCurrentGroup 
!= m_pRootGroup 
) { 
1121       wxFileConfigGroup 
*pGroup 
= m_pCurrentGroup
; 
1122       SetPath(wxT(".."));  // changes m_pCurrentGroup! 
1123       m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name()); 
1125     //else: never delete the root group 
1131 bool wxFileConfig::DeleteGroup(const wxString
& key
) 
1133   wxConfigPathChanger 
path(this, RemoveTrailingSeparator(key
)); 
1135   if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) ) 
1138   path
.UpdateIfDeleted(); 
1145 bool wxFileConfig::DeleteAll() 
1149   if ( m_fnLocalFile
.IsOk() ) 
1151       if ( m_fnLocalFile
.FileExists() && wxRemove(m_fnLocalFile
.GetFullPath()) == -1 ) 
1153           wxLogSysError(_("can't delete user configuration file '%s'"), 
1154                         m_fnLocalFile
.GetFullPath().c_str()); 
1164 // ---------------------------------------------------------------------------- 
1165 // linked list functions 
1166 // ---------------------------------------------------------------------------- 
1168     // append a new line to the end of the list 
1170 wxFileConfigLineList 
*wxFileConfig::LineListAppend(const wxString
& str
) 
1172     wxLogTrace( FILECONF_TRACE_MASK
, 
1173                 _T("    ** Adding Line '%s'"), 
1175     wxLogTrace( FILECONF_TRACE_MASK
, 
1177                 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str() 
1179     wxLogTrace( FILECONF_TRACE_MASK
, 
1181                 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str() 
1184     wxFileConfigLineList 
*pLine 
= new wxFileConfigLineList(str
); 
1186     if ( m_linesTail 
== NULL 
) 
1189         m_linesHead 
= pLine
; 
1194         m_linesTail
->SetNext(pLine
); 
1195         pLine
->SetPrev(m_linesTail
); 
1198     m_linesTail 
= pLine
; 
1200     wxLogTrace( FILECONF_TRACE_MASK
, 
1202                 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str() 
1204     wxLogTrace( FILECONF_TRACE_MASK
, 
1206                 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str() 
1212 // insert a new line after the given one or in the very beginning if !pLine 
1213 wxFileConfigLineList 
*wxFileConfig::LineListInsert(const wxString
& str
, 
1214                                                    wxFileConfigLineList 
*pLine
) 
1216     wxLogTrace( FILECONF_TRACE_MASK
, 
1217                 _T("    ** Inserting Line '%s' after '%s'"), 
1219                 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str() 
1221     wxLogTrace( FILECONF_TRACE_MASK
, 
1223                 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str() 
1225     wxLogTrace( FILECONF_TRACE_MASK
, 
1227                 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str() 
1230     if ( pLine 
== m_linesTail 
) 
1231         return LineListAppend(str
); 
1233     wxFileConfigLineList 
*pNewLine 
= new wxFileConfigLineList(str
); 
1234     if ( pLine 
== NULL 
) 
1236         // prepend to the list 
1237         pNewLine
->SetNext(m_linesHead
); 
1238         m_linesHead
->SetPrev(pNewLine
); 
1239         m_linesHead 
= pNewLine
; 
1243         // insert before pLine 
1244         wxFileConfigLineList 
*pNext 
= pLine
->Next(); 
1245         pNewLine
->SetNext(pNext
); 
1246         pNewLine
->SetPrev(pLine
); 
1247         pNext
->SetPrev(pNewLine
); 
1248         pLine
->SetNext(pNewLine
); 
1251     wxLogTrace( FILECONF_TRACE_MASK
, 
1253                 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str() 
1255     wxLogTrace( FILECONF_TRACE_MASK
, 
1257                 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str() 
1263 void wxFileConfig::LineListRemove(wxFileConfigLineList 
*pLine
) 
1265     wxLogTrace( FILECONF_TRACE_MASK
, 
1266                 _T("    ** Removing Line '%s'"), 
1267                 pLine
->Text().c_str() ); 
1268     wxLogTrace( FILECONF_TRACE_MASK
, 
1270                 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str() 
1272     wxLogTrace( FILECONF_TRACE_MASK
, 
1274                 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str() 
1277     wxFileConfigLineList    
*pPrev 
= pLine
->Prev(), 
1278                             *pNext 
= pLine
->Next(); 
1282     if ( pPrev 
== NULL 
) 
1283         m_linesHead 
= pNext
; 
1285         pPrev
->SetNext(pNext
); 
1289     if ( pNext 
== NULL 
) 
1290         m_linesTail 
= pPrev
; 
1292         pNext
->SetPrev(pPrev
); 
1294     if ( m_pRootGroup
->GetGroupLine() == pLine 
) 
1295         m_pRootGroup
->SetLine(m_linesHead
); 
1297     wxLogTrace( FILECONF_TRACE_MASK
, 
1299                 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str() 
1301     wxLogTrace( FILECONF_TRACE_MASK
, 
1303                 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str() 
1309 bool wxFileConfig::LineListIsEmpty() 
1311     return m_linesHead 
== NULL
; 
1314 // ============================================================================ 
1315 // wxFileConfig::wxFileConfigGroup 
1316 // ============================================================================ 
1318 // ---------------------------------------------------------------------------- 
1320 // ---------------------------------------------------------------------------- 
1323 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup 
*pParent
, 
1324                                        const wxString
& strName
, 
1325                                        wxFileConfig 
*pConfig
) 
1326                          : m_aEntries(CompareEntries
), 
1327                            m_aSubgroups(CompareGroups
), 
1330   m_pConfig 
= pConfig
; 
1331   m_pParent 
= pParent
; 
1334   m_pLastEntry 
= NULL
; 
1335   m_pLastGroup 
= NULL
; 
1338 // dtor deletes all children 
1339 wxFileConfigGroup::~wxFileConfigGroup() 
1342   size_t n
, nCount 
= m_aEntries
.GetCount(); 
1343   for ( n 
= 0; n 
< nCount
; n
++ ) 
1344     delete m_aEntries
[n
]; 
1347   nCount 
= m_aSubgroups
.GetCount(); 
1348   for ( n 
= 0; n 
< nCount
; n
++ ) 
1349     delete m_aSubgroups
[n
]; 
1352 // ---------------------------------------------------------------------------- 
1354 // ---------------------------------------------------------------------------- 
1356 void wxFileConfigGroup::SetLine(wxFileConfigLineList 
*pLine
) 
1358     // for a normal (i.e. not root) group this method shouldn't be called twice 
1359     // unless we are resetting the line 
1360     wxASSERT_MSG( !m_pParent 
|| !m_pLine 
|| !pLine
, 
1361                    _T("changing line for a non-root group?") ); 
1367   This is a bit complicated, so let me explain it in details. All lines that 
1368   were read from the local file (the only one we will ever modify) are stored 
1369   in a (doubly) linked list. Our problem is to know at which position in this 
1370   list should we insert the new entries/subgroups. To solve it we keep three 
1371   variables for each group: m_pLine, m_pLastEntry and m_pLastGroup. 
1373   m_pLine points to the line containing "[group_name]" 
1374   m_pLastEntry points to the last entry of this group in the local file. 
1375   m_pLastGroup                   subgroup 
1377   Initially, they're NULL all three. When the group (an entry/subgroup) is read 
1378   from the local file, the corresponding variable is set. However, if the group 
1379   was read from the global file and then modified or created by the application 
1380   these variables are still NULL and we need to create the corresponding lines. 
1381   See the following functions (and comments preceding them) for the details of 
1384   Also, when our last entry/group are deleted we need to find the new last 
1385   element - the code in DeleteEntry/Subgroup does this by backtracking the list 
1386   of lines until it either founds an entry/subgroup (and this is the new last 
1387   element) or the m_pLine of the group, in which case there are no more entries 
1388   (or subgroups) left and m_pLast<element> becomes NULL. 
1390   NB: This last problem could be avoided for entries if we added new entries 
1391       immediately after m_pLine, but in this case the entries would appear 
1392       backwards in the config file (OTOH, it's not that important) and as we 
1393       would still need to do it for the subgroups the code wouldn't have been 
1394       significantly less complicated. 
1397 // Return the line which contains "[our name]". If we're still not in the list, 
1398 // add our line to it immediately after the last line of our parent group if we 
1399 // have it or in the very beginning if we're the root group. 
1400 wxFileConfigLineList 
*wxFileConfigGroup::GetGroupLine() 
1402     wxLogTrace( FILECONF_TRACE_MASK
, 
1403                 _T("  GetGroupLine() for Group '%s'"), 
1408         wxLogTrace( FILECONF_TRACE_MASK
, 
1409                     _T("    Getting Line item pointer") ); 
1411         wxFileConfigGroup   
*pParent 
= Parent(); 
1413         // this group wasn't present in local config file, add it now 
1416             wxLogTrace( FILECONF_TRACE_MASK
, 
1417                         _T("    checking parent '%s'"), 
1418                         pParent
->Name().c_str() ); 
1420             wxString    strFullName
; 
1422             // add 1 to the name because we don't want to start with '/' 
1423             strFullName 
<< wxT("[") 
1424                         << FilterOutEntryName(GetFullName().c_str() + 1) 
1426             m_pLine 
= m_pConfig
->LineListInsert(strFullName
, 
1427                                                 pParent
->GetLastGroupLine()); 
1428             pParent
->SetLastGroup(this);  // we're surely after all the others 
1430         //else: this is the root group and so we return NULL because we don't 
1431         //      have any group line 
1437 // Return the last line belonging to the subgroups of this group (after which 
1438 // we can add a new subgroup), if we don't have any subgroups or entries our 
1439 // last line is the group line (m_pLine) itself. 
1440 wxFileConfigLineList 
*wxFileConfigGroup::GetLastGroupLine() 
1442     // if we have any subgroups, our last line is the last line of the last 
1446         wxFileConfigLineList 
*pLine 
= m_pLastGroup
->GetLastGroupLine(); 
1448         wxASSERT_MSG( pLine
, _T("last group must have !NULL associated line") ); 
1453     // no subgroups, so the last line is the line of thelast entry (if any) 
1454     return GetLastEntryLine(); 
1457 // return the last line belonging to the entries of this group (after which 
1458 // we can add a new entry), if we don't have any entries we will add the new 
1459 // one immediately after the group line itself. 
1460 wxFileConfigLineList 
*wxFileConfigGroup::GetLastEntryLine() 
1462     wxLogTrace( FILECONF_TRACE_MASK
, 
1463                 _T("  GetLastEntryLine() for Group '%s'"), 
1468         wxFileConfigLineList    
*pLine 
= m_pLastEntry
->GetLine(); 
1470         wxASSERT_MSG( pLine
, _T("last entry must have !NULL associated line") ); 
1475     // no entries: insert after the group header, if any 
1476     return GetGroupLine(); 
1479 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry 
*pEntry
) 
1481     m_pLastEntry 
= pEntry
; 
1485         // the only situation in which a group without its own line can have 
1486         // an entry is when the first entry is added to the initially empty 
1487         // root pseudo-group 
1488         wxASSERT_MSG( !m_pParent
, _T("unexpected for non root group") ); 
1490         // let the group know that it does have a line in the file now 
1491         m_pLine 
= pEntry
->GetLine(); 
1495 // ---------------------------------------------------------------------------- 
1497 // ---------------------------------------------------------------------------- 
1499 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines() 
1501     // update the line of this group 
1502     wxFileConfigLineList 
*line 
= GetGroupLine(); 
1503     wxCHECK_RET( line
, _T("a non root group must have a corresponding line!") ); 
1505     // +1: skip the leading '/' 
1506     line
->SetText(wxString::Format(_T("[%s]"), GetFullName().c_str() + 1)); 
1509     // also update all subgroups as they have this groups name in their lines 
1510     const size_t nCount 
= m_aSubgroups
.GetCount(); 
1511     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
1513         m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines(); 
1517 void wxFileConfigGroup::Rename(const wxString
& newName
) 
1519     wxCHECK_RET( m_pParent
, _T("the root group can't be renamed") ); 
1521     if ( newName 
== m_strName 
) 
1524     // we need to remove the group from the parent and it back under the new 
1525     // name to keep the parents array of subgroups alphabetically sorted 
1526     m_pParent
->m_aSubgroups
.Remove(this); 
1528     m_strName 
= newName
; 
1530     m_pParent
->m_aSubgroups
.Add(this); 
1532     // update the group lines recursively 
1533     UpdateGroupAndSubgroupsLines(); 
1536 wxString 
wxFileConfigGroup::GetFullName() const 
1540         fullname 
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR 
+ Name(); 
1545 // ---------------------------------------------------------------------------- 
1547 // ---------------------------------------------------------------------------- 
1549 // use binary search because the array is sorted 
1551 wxFileConfigGroup::FindEntry(const wxChar 
*szName
) const 
1555        hi 
= m_aEntries
.GetCount(); 
1557   wxFileConfigEntry 
*pEntry
; 
1561     pEntry 
= m_aEntries
[i
]; 
1563     #if wxCONFIG_CASE_SENSITIVE 
1564       res 
= wxStrcmp(pEntry
->Name(), szName
); 
1566       res 
= wxStricmp(pEntry
->Name(), szName
); 
1581 wxFileConfigGroup::FindSubgroup(const wxChar 
*szName
) const 
1585        hi 
= m_aSubgroups
.GetCount(); 
1587   wxFileConfigGroup 
*pGroup
; 
1591     pGroup 
= m_aSubgroups
[i
]; 
1593     #if wxCONFIG_CASE_SENSITIVE 
1594       res 
= wxStrcmp(pGroup
->Name(), szName
); 
1596       res 
= wxStricmp(pGroup
->Name(), szName
); 
1610 // ---------------------------------------------------------------------------- 
1611 // create a new item 
1612 // ---------------------------------------------------------------------------- 
1614 // create a new entry and add it to the current group 
1615 wxFileConfigEntry 
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
) 
1617     wxASSERT( FindEntry(strName
) == 0 ); 
1619     wxFileConfigEntry   
*pEntry 
= new wxFileConfigEntry(this, strName
, nLine
); 
1621     m_aEntries
.Add(pEntry
); 
1625 // create a new group and add it to the current group 
1626 wxFileConfigGroup 
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
) 
1628     wxASSERT( FindSubgroup(strName
) == 0 ); 
1630     wxFileConfigGroup   
*pGroup 
= new wxFileConfigGroup(this, strName
, m_pConfig
); 
1632     m_aSubgroups
.Add(pGroup
); 
1636 // ---------------------------------------------------------------------------- 
1638 // ---------------------------------------------------------------------------- 
1641   The delete operations are _very_ slow if we delete the last item of this 
1642   group (see comments before GetXXXLineXXX functions for more details), 
1643   so it's much better to start with the first entry/group if we want to 
1644   delete several of them. 
1647 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar 
*szName
) 
1649     wxFileConfigGroup 
* const pGroup 
= FindSubgroup(szName
); 
1651     return pGroup 
? DeleteSubgroup(pGroup
) : false; 
1654 // Delete the subgroup and remove all references to it from 
1655 // other data structures. 
1656 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup 
*pGroup
) 
1658     wxCHECK_MSG( pGroup
, false, _T("deleting non existing group?") ); 
1660     wxLogTrace( FILECONF_TRACE_MASK
, 
1661                 _T("Deleting group '%s' from '%s'"), 
1662                 pGroup
->Name().c_str(), 
1665     wxLogTrace( FILECONF_TRACE_MASK
, 
1666                 _T("  (m_pLine) = prev: %p, this %p, next %p"), 
1667                 m_pLine 
? wx_static_cast(void*, m_pLine
->Prev()) : 0, 
1668                 wx_static_cast(void*, m_pLine
), 
1669                 m_pLine 
? wx_static_cast(void*, m_pLine
->Next()) : 0 ); 
1670     wxLogTrace( FILECONF_TRACE_MASK
, 
1672                 m_pLine 
? (const wxChar
*)m_pLine
->Text().c_str() 
1675     // delete all entries... 
1676     size_t nCount 
= pGroup
->m_aEntries
.GetCount(); 
1678     wxLogTrace(FILECONF_TRACE_MASK
, 
1679                _T("Removing %lu entries"), (unsigned long)nCount 
); 
1681     for ( size_t nEntry 
= 0; nEntry 
< nCount
; nEntry
++ ) 
1683         wxFileConfigLineList 
*pLine 
= pGroup
->m_aEntries
[nEntry
]->GetLine(); 
1687             wxLogTrace( FILECONF_TRACE_MASK
, 
1689                         pLine
->Text().c_str() ); 
1690             m_pConfig
->LineListRemove(pLine
); 
1694     // ...and subgroups of this subgroup 
1695     nCount 
= pGroup
->m_aSubgroups
.GetCount(); 
1697     wxLogTrace( FILECONF_TRACE_MASK
, 
1698                 _T("Removing %lu subgroups"), (unsigned long)nCount 
); 
1700     for ( size_t nGroup 
= 0; nGroup 
< nCount
; nGroup
++ ) 
1702         pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]); 
1705     // and then finally the group itself 
1706     wxFileConfigLineList 
*pLine 
= pGroup
->m_pLine
; 
1709         wxLogTrace( FILECONF_TRACE_MASK
, 
1710                     _T("  Removing line for group '%s' : '%s'"), 
1711                     pGroup
->Name().c_str(), 
1712                     pLine
->Text().c_str() ); 
1713         wxLogTrace( FILECONF_TRACE_MASK
, 
1714                     _T("  Removing from group '%s' : '%s'"), 
1716                     ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str() 
1719         // notice that we may do this test inside the previous "if" 
1720         // because the last entry's line is surely !NULL 
1721         if ( pGroup 
== m_pLastGroup 
) 
1723             wxLogTrace( FILECONF_TRACE_MASK
, 
1724                         _T("  Removing last group") ); 
1726             // our last entry is being deleted, so find the last one which 
1727             // stays by going back until we find a subgroup or reach the 
1729             const size_t nSubgroups 
= m_aSubgroups
.GetCount(); 
1731             m_pLastGroup 
= NULL
; 
1732             for ( wxFileConfigLineList 
*pl 
= pLine
->Prev(); 
1733                   pl 
&& pl 
!= m_pLine 
&& !m_pLastGroup
; 
1736                 // does this line belong to our subgroup? 
1737                 for ( size_t n 
= 0; n 
< nSubgroups
; n
++ ) 
1739                     // do _not_ call GetGroupLine! we don't want to add it to 
1740                     // the local file if it's not already there 
1741                     if ( m_aSubgroups
[n
]->m_pLine 
== pl 
) 
1743                         m_pLastGroup 
= m_aSubgroups
[n
]; 
1750         m_pConfig
->LineListRemove(pLine
); 
1754         wxLogTrace( FILECONF_TRACE_MASK
, 
1755                     _T("  No line entry for Group '%s'?"), 
1756                     pGroup
->Name().c_str() ); 
1759     m_aSubgroups
.Remove(pGroup
); 
1765 bool wxFileConfigGroup::DeleteEntry(const wxChar 
*szName
) 
1767   wxFileConfigEntry 
*pEntry 
= FindEntry(szName
); 
1770       // entry doesn't exist, nothing to do 
1774   wxFileConfigLineList 
*pLine 
= pEntry
->GetLine(); 
1775   if ( pLine 
!= NULL 
) { 
1776     // notice that we may do this test inside the previous "if" because the 
1777     // last entry's line is surely !NULL 
1778     if ( pEntry 
== m_pLastEntry 
) { 
1779       // our last entry is being deleted - find the last one which stays 
1780       wxASSERT( m_pLine 
!= NULL 
);  // if we have an entry with !NULL pLine... 
1782       // go back until we find another entry or reach the group's line 
1783       wxFileConfigEntry 
*pNewLast 
= NULL
; 
1784       size_t n
, nEntries 
= m_aEntries
.GetCount(); 
1785       wxFileConfigLineList 
*pl
; 
1786       for ( pl 
= pLine
->Prev(); pl 
!= m_pLine
; pl 
= pl
->Prev() ) { 
1787         // is it our subgroup? 
1788         for ( n 
= 0; (pNewLast 
== NULL
) && (n 
< nEntries
); n
++ ) { 
1789           if ( m_aEntries
[n
]->GetLine() == m_pLine 
) 
1790             pNewLast 
= m_aEntries
[n
]; 
1793         if ( pNewLast 
!= NULL 
) // found? 
1797       if ( pl 
== m_pLine 
) { 
1798         wxASSERT( !pNewLast 
);  // how comes it has the same line as we? 
1800         // we've reached the group line without finding any subgroups 
1801         m_pLastEntry 
= NULL
; 
1804         m_pLastEntry 
= pNewLast
; 
1807     m_pConfig
->LineListRemove(pLine
); 
1810   m_aEntries
.Remove(pEntry
); 
1816 // ============================================================================ 
1817 // wxFileConfig::wxFileConfigEntry 
1818 // ============================================================================ 
1820 // ---------------------------------------------------------------------------- 
1822 // ---------------------------------------------------------------------------- 
1823 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup 
*pParent
, 
1824                                        const wxString
& strName
, 
1826                          : m_strName(strName
) 
1828   wxASSERT( !strName
.empty() ); 
1830   m_pParent 
= pParent
; 
1834   m_bHasValue 
= false; 
1836   m_bImmutable 
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
; 
1838     m_strName
.erase(0, 1);  // remove first character 
1841 // ---------------------------------------------------------------------------- 
1843 // ---------------------------------------------------------------------------- 
1845 void wxFileConfigEntry::SetLine(wxFileConfigLineList 
*pLine
) 
1847   if ( m_pLine 
!= NULL 
) { 
1848     wxLogWarning(_("entry '%s' appears more than once in group '%s'"), 
1849                  Name().c_str(), m_pParent
->GetFullName().c_str()); 
1853   Group()->SetLastEntry(this); 
1856 // second parameter is false if we read the value from file and prevents the 
1857 // entry from being marked as 'dirty' 
1858 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
) 
1860     if ( bUser 
&& IsImmutable() ) 
1862         wxLogWarning( _("attempt to change immutable key '%s' ignored."), 
1867     // do nothing if it's the same value: but don't test for it if m_bHasValue 
1868     // hadn't been set yet or we'd never write empty values to the file 
1869     if ( m_bHasValue 
&& strValue 
== m_strValue 
) 
1873     m_strValue 
= strValue
; 
1877         wxString strValFiltered
; 
1879         if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS 
) 
1881             strValFiltered 
= strValue
; 
1884             strValFiltered 
= FilterOutValue(strValue
); 
1888         strLine 
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
; 
1892             // entry was read from the local config file, just modify the line 
1893             m_pLine
->SetText(strLine
); 
1895         else // this entry didn't exist in the local file 
1897             // add a new line to the file: note that line returned by 
1898             // GetLastEntryLine() may be NULL if we're in the root group and it 
1899             // doesn't have any entries yet, but this is ok as passing NULL 
1900             // line to LineListInsert() means to prepend new line to the list 
1901             wxFileConfigLineList 
*line 
= Group()->GetLastEntryLine(); 
1902             m_pLine 
= Group()->Config()->LineListInsert(strLine
, line
); 
1904             Group()->SetLastEntry(this); 
1909 // ============================================================================ 
1911 // ============================================================================ 
1913 // ---------------------------------------------------------------------------- 
1914 // compare functions for array sorting 
1915 // ---------------------------------------------------------------------------- 
1917 int CompareEntries(wxFileConfigEntry 
*p1
, wxFileConfigEntry 
*p2
) 
1919 #if wxCONFIG_CASE_SENSITIVE 
1920     return wxStrcmp(p1
->Name(), p2
->Name()); 
1922     return wxStricmp(p1
->Name(), p2
->Name()); 
1926 int CompareGroups(wxFileConfigGroup 
*p1
, wxFileConfigGroup 
*p2
) 
1928 #if wxCONFIG_CASE_SENSITIVE 
1929     return wxStrcmp(p1
->Name(), p2
->Name()); 
1931     return wxStricmp(p1
->Name(), p2
->Name()); 
1935 // ---------------------------------------------------------------------------- 
1937 // ---------------------------------------------------------------------------- 
1939 // undo FilterOutValue 
1940 static wxString 
FilterInValue(const wxString
& str
) 
1943   strResult
.Alloc(str
.Len()); 
1945   bool bQuoted 
= !str
.empty() && str
[0] == '"'; 
1947   for ( size_t n 
= bQuoted 
? 1 : 0; n 
< str
.Len(); n
++ ) { 
1948     if ( str
[n
] == wxT('\\') ) { 
1949       switch ( str
[++n
].GetValue() ) { 
1951           strResult 
+= wxT('\n'); 
1955           strResult 
+= wxT('\r'); 
1959           strResult 
+= wxT('\t'); 
1963           strResult 
+= wxT('\\'); 
1967           strResult 
+= wxT('"'); 
1972       if ( str
[n
] != wxT('"') || !bQuoted 
) 
1973         strResult 
+= str
[n
]; 
1974       else if ( n 
!= str
.Len() - 1 ) { 
1975         wxLogWarning(_("unexpected \" at position %d in '%s'."), 
1978       //else: it's the last quote of a quoted string, ok 
1985 // quote the string before writing it to file 
1986 static wxString 
FilterOutValue(const wxString
& str
) 
1992   strResult
.Alloc(str
.Len()); 
1994   // quoting is necessary to preserve spaces in the beginning of the string 
1995   bool bQuote 
= wxIsspace(str
[0]) || str
[0] == wxT('"'); 
1998     strResult 
+= wxT('"'); 
2001   for ( size_t n 
= 0; n 
< str
.Len(); n
++ ) { 
2002     switch ( str
[n
].GetValue() ) { 
2024         //else: fall through 
2027         strResult 
+= str
[n
]; 
2028         continue;   // nothing special to do 
2031     // we get here only for special characters 
2032     strResult 
<< wxT('\\') << c
; 
2036     strResult 
+= wxT('"'); 
2041 // undo FilterOutEntryName 
2042 static wxString 
FilterInEntryName(const wxString
& str
) 
2045   strResult
.Alloc(str
.Len()); 
2047   for ( const wxChar 
*pc 
= str
.c_str(); *pc 
!= '\0'; pc
++ ) { 
2048     if ( *pc 
== wxT('\\') ) { 
2049       // we need to test it here or we'd skip past the NUL in the loop line 
2050       if ( *++pc 
== _T('\0') ) 
2060 // sanitize entry or group name: insert '\\' before any special characters 
2061 static wxString 
FilterOutEntryName(const wxString
& str
) 
2064   strResult
.Alloc(str
.Len()); 
2066   for ( const wxChar 
*pc 
= str
.c_str(); *pc 
!= wxT('\0'); pc
++ ) { 
2067     const wxChar c 
= *pc
; 
2069     // we explicitly allow some of "safe" chars and 8bit ASCII characters 
2070     // which will probably never have special meaning and with which we can't 
2071     // use isalnum() anyhow (in ASCII built, in Unicode it's just fine) 
2073     // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR 
2074     //     should *not* be quoted 
2077             ((unsigned char)c 
< 127) && 
2079          !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) ) 
2081       strResult 
+= wxT('\\'); 
2090 // we can't put ?: in the ctor initializer list because it confuses some 
2091 // broken compilers (Borland C++) 
2092 static wxString 
GetAppName(const wxString
& appName
) 
2094     if ( !appName 
&& wxTheApp 
) 
2095         return wxTheApp
->GetAppName(); 
2100 #endif // wxUSE_CONFIG