1 /////////////////////////////////////////////////////////////////////////////// 
   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 license 
  11 /////////////////////////////////////////////////////////////////////////////// 
  14 #pragma implementation "fileconf.h" 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  21 #include  "wx/wxprec.h" 
  30   #include  "wx/string.h" 
  35 #include  "wx/dynarray.h" 
  38 #include  "wx/textfile.h" 
  39 #include  "wx/memtext.h" 
  40 #include  "wx/config.h" 
  41 #include  "wx/fileconf.h" 
  44     #include  "wx/stream.h" 
  45 #endif // wxUSE_STREAMS 
  47 #include  "wx/utils.h"    // for wxGetHomeDir 
  49 // _WINDOWS_ is defined when windows.h is included, 
  50 // __WXMSW__ is defined for MS Windows compilation 
  51 #if       defined(__WXMSW__) && !defined(_WINDOWS_) 
  62 // headers needed for umask() 
  64     #include <sys/types.h> 
  68 // ---------------------------------------------------------------------------- 
  70 // ---------------------------------------------------------------------------- 
  71 #define CONST_CAST ((wxFileConfig *)this)-> 
  73 // ---------------------------------------------------------------------------- 
  75 // ---------------------------------------------------------------------------- 
  81 // ---------------------------------------------------------------------------- 
  82 // global functions declarations 
  83 // ---------------------------------------------------------------------------- 
  85 // compare functions for sorting the arrays 
  86 static int LINKAGEMODE 
CompareEntries(wxFileConfigEntry 
*p1
, wxFileConfigEntry 
*p2
); 
  87 static int LINKAGEMODE 
CompareGroups(wxFileConfigGroup 
*p1
, wxFileConfigGroup 
*p2
); 
  90 static wxString 
FilterInValue(const wxString
& str
); 
  91 static wxString 
FilterOutValue(const wxString
& str
); 
  93 static wxString 
FilterInEntryName(const wxString
& str
); 
  94 static wxString 
FilterOutEntryName(const wxString
& str
); 
  96 // get the name to use in wxFileConfig ctor 
  97 static wxString 
GetAppName(const wxString
& appname
); 
  99 // ============================================================================ 
 101 // ============================================================================ 
 103 // ---------------------------------------------------------------------------- 
 104 // "template" array types 
 105 // ---------------------------------------------------------------------------- 
 107 WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigEntry 
*, ArrayEntries
); 
 108 WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigGroup 
*, ArrayGroups
); 
 110 // ---------------------------------------------------------------------------- 
 111 // wxFileConfigLineList 
 112 // ---------------------------------------------------------------------------- 
 114 // we store all lines of the local config file as a linked list in memory 
 115 class wxFileConfigLineList
 
 118   void SetNext(wxFileConfigLineList 
*pNext
)  { m_pNext 
= pNext
; } 
 119   void SetPrev(wxFileConfigLineList 
*pPrev
)  { m_pPrev 
= pPrev
; } 
 122   wxFileConfigLineList(const wxString
& str
, 
 123                        wxFileConfigLineList 
*pNext 
= NULL
) : m_strLine(str
) 
 124     { SetNext(pNext
); SetPrev(NULL
); } 
 126   // next/prev nodes in the linked list 
 127   wxFileConfigLineList 
*Next() const { return m_pNext
;  } 
 128   wxFileConfigLineList 
*Prev() const { return m_pPrev
;  } 
 130   // get/change lines text 
 131   void SetText(const wxString
& str
) { m_strLine 
= str
;  } 
 132   const wxString
& Text() const { return m_strLine
; } 
 135   wxString  m_strLine
;                  // line contents 
 136   wxFileConfigLineList 
*m_pNext
,        // next node 
 137                        *m_pPrev
;        // previous one 
 140 // ---------------------------------------------------------------------------- 
 141 // wxFileConfigEntry: a name/value pair 
 142 // ---------------------------------------------------------------------------- 
 144 class wxFileConfigEntry
 
 147   wxFileConfigGroup 
*m_pParent
; // group that contains us 
 149   wxString      m_strName
,      // entry name 
 151   bool          m_bDirty
:1,     // changed since last read? 
 152                 m_bImmutable
:1, // can be overriden locally? 
 153                 m_bHasValue
:1;  // set after first call to SetValue() 
 155   int           m_nLine
;        // used if m_pLine == NULL only 
 157   // pointer to our line in the linked list or NULL if it was found in global 
 158   // file (which we don't modify) 
 159   wxFileConfigLineList 
*m_pLine
; 
 162   wxFileConfigEntry(wxFileConfigGroup 
*pParent
, 
 163                     const wxString
& strName
, int nLine
); 
 166   const wxString
& Name()        const { return m_strName
;    } 
 167   const wxString
& Value()       const { return m_strValue
;   } 
 168   wxFileConfigGroup 
*Group()    const { return m_pParent
;    } 
 169   bool            IsDirty()     const { return m_bDirty
;     } 
 170   bool            IsImmutable() const { return m_bImmutable
; } 
 171   bool            IsLocal()     const { return m_pLine 
!= 0; } 
 172   int             Line()        const { return m_nLine
;      } 
 173   wxFileConfigLineList 
* 
 174                   GetLine()     const { return m_pLine
;      } 
 176   // modify entry attributes 
 177   void SetValue(const wxString
& strValue
, bool bUser 
= TRUE
); 
 179   void SetLine(wxFileConfigLineList 
*pLine
); 
 182 // ---------------------------------------------------------------------------- 
 183 // wxFileConfigGroup: container of entries and other groups 
 184 // ---------------------------------------------------------------------------- 
 186 class wxFileConfigGroup
 
 189   wxFileConfig 
*m_pConfig
;          // config object we belong to 
 190   wxFileConfigGroup  
*m_pParent
;    // parent group (NULL for root group) 
 191   ArrayEntries  m_aEntries
;         // entries in this group 
 192   ArrayGroups   m_aSubgroups
;       // subgroups 
 193   wxString      m_strName
;          // group's name 
 194   bool          m_bDirty
;           // if FALSE => all subgroups are not dirty 
 195   wxFileConfigLineList 
*m_pLine
;    // pointer to our line in the linked list 
 196   wxFileConfigEntry 
*m_pLastEntry
;  // last entry/subgroup of this group in the 
 197   wxFileConfigGroup 
*m_pLastGroup
;  // local file (we insert new ones after it) 
 199   // DeleteSubgroupByName helper 
 200   bool DeleteSubgroup(wxFileConfigGroup 
*pGroup
); 
 204   wxFileConfigGroup(wxFileConfigGroup 
*pParent
, const wxString
& strName
, wxFileConfig 
*); 
 206   // dtor deletes all entries and subgroups also 
 207   ~wxFileConfigGroup(); 
 210   const wxString
& Name()    const { return m_strName
; } 
 211   wxFileConfigGroup    
*Parent()  const { return m_pParent
; } 
 212   wxFileConfig   
*Config()  const { return m_pConfig
; } 
 213   bool            IsDirty() const { return m_bDirty
;  } 
 215   const ArrayEntries
& Entries() const { return m_aEntries
;   } 
 216   const ArrayGroups
&  Groups()  const { return m_aSubgroups
; } 
 217   bool  IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); } 
 219   // find entry/subgroup (NULL if not found) 
 220   wxFileConfigGroup 
*FindSubgroup(const wxChar 
*szName
) const; 
 221   wxFileConfigEntry 
*FindEntry   (const wxChar 
*szName
) const; 
 223   // delete entry/subgroup, return FALSE if doesn't exist 
 224   bool DeleteSubgroupByName(const wxChar 
*szName
); 
 225   bool DeleteEntry(const wxChar 
*szName
); 
 227   // create new entry/subgroup returning pointer to newly created element 
 228   wxFileConfigGroup 
*AddSubgroup(const wxString
& strName
); 
 229   wxFileConfigEntry 
*AddEntry   (const wxString
& strName
, int nLine 
= wxNOT_FOUND
); 
 231   // will also recursively set parent's dirty flag 
 233   void SetLine(wxFileConfigLineList 
*pLine
); 
 235   // rename: no checks are done to ensure that the name is unique! 
 236   void Rename(const wxString
& newName
); 
 239   wxString 
GetFullName() const; 
 241   // get the last line belonging to an entry/subgroup of this group 
 242   wxFileConfigLineList 
*GetGroupLine();     // line which contains [group] 
 243   wxFileConfigLineList 
*GetLastEntryLine(); // after which our subgroups start 
 244   wxFileConfigLineList 
*GetLastGroupLine(); // after which the next group starts 
 246   // called by entries/subgroups when they're created/deleted 
 247   void SetLastEntry(wxFileConfigEntry 
*pEntry
) { m_pLastEntry 
= pEntry
; } 
 248   void SetLastGroup(wxFileConfigGroup 
*pGroup
) { m_pLastGroup 
= pGroup
; } 
 251 // ============================================================================ 
 253 // ============================================================================ 
 255 // ---------------------------------------------------------------------------- 
 257 // ---------------------------------------------------------------------------- 
 258 wxString 
wxFileConfig::GetGlobalDir() 
 262   #ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined 
 263     strDir 
= wxT("sys$manager:"); 
 264   #elif defined(__WXMAC__) 
 265     strDir 
= wxMacFindFolder(  (short) kOnSystemDisk
, kPreferencesFolderType
, kDontCreateFolder 
) ; 
 266   #elif defined( __UNIX__ ) 
 267     strDir 
= wxT("/etc/"); 
 268   #elif defined(__WXPM__) 
 269     ULONG                           aulSysInfo
[QSV_MAX
] = {0}; 
 273     rc 
= DosQuerySysInfo( 1L, QSV_MAX
, (PVOID
)aulSysInfo
, sizeof(ULONG
)*QSV_MAX
); 
 276         drive 
= aulSysInfo
[QSV_BOOT_DRIVE 
- 1]; 
 277         strDir
.Printf(wxT("%c:\\OS2\\"), 'A'+drive
-1); 
 279   #elif defined(__WXSTUBS__) 
 280     wxASSERT_MSG( FALSE
, wxT("TODO") ) ; 
 281   #elif defined(__DOS__) 
 282     // There's no such thing as global cfg dir in MS-DOS, let's return 
 283     // current directory (FIXME_MGL?) 
 286     wxChar szWinDir
[MAX_PATH
]; 
 287     ::GetWindowsDirectory(szWinDir
, MAX_PATH
); 
 291   #endif // Unix/Windows 
 296 wxString 
wxFileConfig::GetLocalDir() 
 300 #if defined(__WXMAC__) 
 301   // no local dir concept on Mac OS 9 
 302   return GetGlobalDir() ; 
 304   wxGetHomeDir(&strDir
); 
 308   if (strDir
.Last() != wxT(']')) 
 310       if (strDir
.Last() != wxT('/')) strDir 
<< wxT('/'); 
 312   if (strDir
.Last() != wxT('\\')) strDir 
<< wxT('\\'); 
 319 wxString 
wxFileConfig::GetGlobalFileName(const wxChar 
*szFile
) 
 321   wxString str 
= GetGlobalDir(); 
 324   if ( wxStrchr(szFile
, wxT('.')) == NULL 
) 
 325   #if defined( __WXMAC__ ) 
 326      str 
<< " Preferences"; 
 327   #elif defined( __UNIX__ ) 
 336 wxString 
wxFileConfig::GetLocalFileName(const wxChar 
*szFile
) 
 338 #ifdef __VMS__ // On VMS I saw the problem that the home directory was appended 
 339    // twice for the configuration file. Does that also happen for other 
 341    wxString str 
= wxT( '.' );  
 343    wxString str 
= GetLocalDir(); 
 346   #if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ ) 
 353     if ( wxStrchr(szFile
, wxT('.')) == NULL 
) 
 358      str 
<< " Preferences"; 
 363 // ---------------------------------------------------------------------------- 
 365 // ---------------------------------------------------------------------------- 
 367 void wxFileConfig::Init() 
 370   m_pRootGroup    
= new wxFileConfigGroup(NULL
, "", this); 
 375   // it's not an error if (one of the) file(s) doesn't exist 
 377   // parse the global file 
 378   if ( !m_strGlobalFile
.IsEmpty() && wxFile::Exists(m_strGlobalFile
) ) { 
 379     wxTextFile 
fileGlobal(m_strGlobalFile
); 
 381     if ( fileGlobal
.Open() ) { 
 382       Parse(fileGlobal
, FALSE 
/* global */); 
 386       wxLogWarning(_("can't open global configuration file '%s'."), 
 387                    m_strGlobalFile
.c_str()); 
 390   // parse the local file 
 391   if ( !m_strLocalFile
.IsEmpty() && wxFile::Exists(m_strLocalFile
) ) { 
 392     wxTextFile 
fileLocal(m_strLocalFile
); 
 393     if ( fileLocal
.Open() ) { 
 394       Parse(fileLocal
, TRUE 
/* local */); 
 398       wxLogWarning(_("can't open user configuration file '%s'."), 
 399                    m_strLocalFile
.c_str()); 
 403 // constructor supports creation of wxFileConfig objects of any type 
 404 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
, 
 405                            const wxString
& strLocal
, const wxString
& strGlobal
, 
 407             : wxConfigBase(::GetAppName(appName
), vendorName
, 
 410               m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
) 
 412   // Make up names for files if empty 
 413   if ( m_strLocalFile
.IsEmpty() && (style 
& wxCONFIG_USE_LOCAL_FILE
) ) 
 415     m_strLocalFile 
= GetLocalFileName(GetAppName()); 
 418   if ( m_strGlobalFile
.IsEmpty() && (style 
& wxCONFIG_USE_GLOBAL_FILE
) ) 
 420     m_strGlobalFile 
= GetGlobalFileName(GetAppName()); 
 423   // Check if styles are not supplied, but filenames are, in which case 
 424   // add the correct styles. 
 425   if ( !m_strLocalFile
.IsEmpty() ) 
 426     SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
); 
 428   if ( !m_strGlobalFile
.IsEmpty() ) 
 429     SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
); 
 431   // if the path is not absolute, prepend the standard directory to it 
 432   // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set 
 433   if ( !(style 
& wxCONFIG_USE_RELATIVE_PATH
) ) 
 435       if ( !m_strLocalFile
.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile
) ) 
 437           wxString strLocal 
= m_strLocalFile
; 
 438           m_strLocalFile 
= GetLocalDir(); 
 439           m_strLocalFile 
<< strLocal
; 
 442       if ( !m_strGlobalFile
.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile
) ) 
 444           wxString strGlobal 
= m_strGlobalFile
; 
 445           m_strGlobalFile 
= GetGlobalDir(); 
 446           m_strGlobalFile 
<< strGlobal
; 
 457 wxFileConfig::wxFileConfig(wxInputStream 
&inStream
) 
 459     // always local_file when this constructor is called (?) 
 460     SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
); 
 463     m_pRootGroup    
= new wxFileConfigGroup(NULL
, "", this); 
 468     // translate everything to the current (platform-dependent) line 
 469     // termination character 
 475         while ( !inStream
.Read(buf
, WXSIZEOF(buf
)).Eof() ) 
 476             strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead()); 
 478         strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead()); 
 480         strTrans 
= wxTextBuffer::Translate(strTmp
); 
 483     wxMemoryText memText
; 
 485     // Now we can add the text to the memory text. To do this we extract line 
 486     // by line from the translated string, until we've reached the end. 
 488     // VZ: all this is horribly inefficient, we should do the translation on 
 489     //     the fly in one pass saving both memory and time (TODO) 
 491     const wxChar 
*pEOL 
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
); 
 492     const size_t EOLLen 
= wxStrlen(pEOL
); 
 494     int posLineStart 
= strTrans
.Find(pEOL
); 
 495     while ( posLineStart 
!= -1 ) 
 497         wxString 
line(strTrans
.Left(posLineStart
)); 
 499         memText
.AddLine(line
); 
 501         strTrans 
= strTrans
.Mid(posLineStart 
+ EOLLen
); 
 503         posLineStart 
= strTrans
.Find(pEOL
); 
 506     // also add whatever we have left in the translated string. 
 507     memText
.AddLine(strTrans
); 
 509     // Finally we can parse it all. 
 510     Parse(memText
, TRUE 
/* local */); 
 515 #endif // wxUSE_STREAMS 
 517 void wxFileConfig::CleanUp() 
 521   wxFileConfigLineList 
*pCur 
= m_linesHead
; 
 522   while ( pCur 
!= NULL 
) { 
 523     wxFileConfigLineList 
*pNext 
= pCur
->Next(); 
 529 wxFileConfig::~wxFileConfig() 
 536 // ---------------------------------------------------------------------------- 
 537 // parse a config file 
 538 // ---------------------------------------------------------------------------- 
 540 void wxFileConfig::Parse(wxTextBuffer
& buffer
, bool bLocal
) 
 542   const wxChar 
*pStart
; 
 546   size_t nLineCount 
= buffer
.GetLineCount(); 
 547   for ( size_t n 
= 0; n 
< nLineCount
; n
++ ) { 
 550     // add the line to linked list 
 552       LineListAppend(strLine
); 
 554     // skip leading spaces 
 555     for ( pStart 
= strLine
; wxIsspace(*pStart
); pStart
++ ) 
 558     // skip blank/comment lines 
 559     if ( *pStart 
== wxT('\0')|| *pStart 
== wxT(';') || *pStart 
== wxT('#') ) 
 562     if ( *pStart 
== wxT('[') ) {          // a new group 
 565       while ( *++pEnd 
!= wxT(']') ) { 
 566         if ( *pEnd 
== wxT('\\') ) { 
 567             // the next char is escaped, so skip it even if it is ']' 
 571         if ( *pEnd 
== wxT('\n') || *pEnd 
== wxT('\0') ) { 
 572             // we reached the end of line, break out of the loop 
 577       if ( *pEnd 
!= wxT(']') ) { 
 578         wxLogError(_("file '%s': unexpected character %c at line %d."), 
 579                    buffer
.GetName(), *pEnd
, n 
+ 1); 
 580         continue; // skip this line 
 583       // group name here is always considered as abs path 
 586       strGroup 
<< wxCONFIG_PATH_SEPARATOR
 
 587                << FilterInEntryName(wxString(pStart
, pEnd 
- pStart
)); 
 589       // will create it if doesn't yet exist 
 593         m_pCurrentGroup
->SetLine(m_linesTail
); 
 595       // check that there is nothing except comments left on this line 
 597       while ( *++pEnd 
!= wxT('\0') && bCont 
) { 
 606             // ignore whitespace ('\n' impossible here) 
 610             wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."), 
 611                          buffer
.GetName(), n 
+ 1, pEnd
); 
 617       const wxChar 
*pEnd 
= pStart
; 
 618       while ( *pEnd 
&& *pEnd 
!= wxT('=') && !wxIsspace(*pEnd
) ) { 
 619         if ( *pEnd 
== wxT('\\') ) { 
 620           // next character may be space or not - still take it because it's 
 621           // quoted (unless there is nothing) 
 624             // the error message will be given below anyhow 
 632       wxString 
strKey(FilterInEntryName(wxString(pStart
, pEnd
))); 
 635       while ( wxIsspace(*pEnd
) ) 
 638       if ( *pEnd
++ != wxT('=') ) { 
 639         wxLogError(_("file '%s', line %d: '=' expected."), 
 640                    buffer
.GetName(), n 
+ 1); 
 643         wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(strKey
); 
 645         if ( pEntry 
== NULL 
) { 
 647           pEntry 
= m_pCurrentGroup
->AddEntry(strKey
, n
); 
 650             pEntry
->SetLine(m_linesTail
); 
 653           if ( bLocal 
&& pEntry
->IsImmutable() ) { 
 654             // immutable keys can't be changed by user 
 655             wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."), 
 656                          buffer
.GetName(), n 
+ 1, strKey
.c_str()); 
 659           // the condition below catches the cases (a) and (b) but not (c): 
 660           //  (a) global key found second time in global file 
 661           //  (b) key found second (or more) time in local file 
 662           //  (c) key from global file now found in local one 
 663           // which is exactly what we want. 
 664           else if ( !bLocal 
|| pEntry
->IsLocal() ) { 
 665             wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."), 
 666                          buffer
.GetName(), n 
+ 1, strKey
.c_str(), pEntry
->Line()); 
 669               pEntry
->SetLine(m_linesTail
); 
 674         while ( wxIsspace(*pEnd
) ) 
 677         pEntry
->SetValue(FilterInValue(pEnd
), FALSE 
/* read from file */); 
 683 // ---------------------------------------------------------------------------- 
 685 // ---------------------------------------------------------------------------- 
 687 void wxFileConfig::SetRootPath() 
 690   m_pCurrentGroup 
= m_pRootGroup
; 
 693 void wxFileConfig::SetPath(const wxString
& strPath
) 
 695   wxArrayString aParts
; 
 697   if ( strPath
.IsEmpty() ) { 
 702   if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR 
) { 
 704     wxSplitPath(aParts
, strPath
); 
 707     // relative path, combine with current one 
 708     wxString strFullPath 
= m_strPath
; 
 709     strFullPath 
<< wxCONFIG_PATH_SEPARATOR 
<< strPath
; 
 710     wxSplitPath(aParts
, strFullPath
); 
 713   // change current group 
 715   m_pCurrentGroup 
= m_pRootGroup
; 
 716   for ( n 
= 0; n 
< aParts
.Count(); n
++ ) { 
 717     wxFileConfigGroup 
*pNextGroup 
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]); 
 718     if ( pNextGroup 
== NULL 
) 
 719       pNextGroup 
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]); 
 720     m_pCurrentGroup 
= pNextGroup
; 
 723   // recombine path parts in one variable 
 725   for ( n 
= 0; n 
< aParts
.Count(); n
++ ) { 
 726     m_strPath 
<< wxCONFIG_PATH_SEPARATOR 
<< aParts
[n
]; 
 730 // ---------------------------------------------------------------------------- 
 732 // ---------------------------------------------------------------------------- 
 734 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const 
 737   return GetNextGroup(str
, lIndex
); 
 740 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const 
 742   if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) { 
 743     str 
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name(); 
 750 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const 
 753   return GetNextEntry(str
, lIndex
); 
 756 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const 
 758   if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) { 
 759     str 
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name(); 
 766 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const 
 768   size_t n 
= m_pCurrentGroup
->Entries().Count(); 
 770     wxFileConfigGroup 
*pOldCurrentGroup 
= m_pCurrentGroup
; 
 771     size_t nSubgroups 
= m_pCurrentGroup
->Groups().Count(); 
 772     for ( size_t nGroup 
= 0; nGroup 
< nSubgroups
; nGroup
++ ) { 
 773       CONST_CAST m_pCurrentGroup 
= m_pCurrentGroup
->Groups()[nGroup
]; 
 774       n 
+= GetNumberOfEntries(TRUE
); 
 775       CONST_CAST m_pCurrentGroup 
= pOldCurrentGroup
; 
 782 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const 
 784   size_t n 
= m_pCurrentGroup
->Groups().Count(); 
 786     wxFileConfigGroup 
*pOldCurrentGroup 
= m_pCurrentGroup
; 
 787     size_t nSubgroups 
= m_pCurrentGroup
->Groups().Count(); 
 788     for ( size_t nGroup 
= 0; nGroup 
< nSubgroups
; nGroup
++ ) { 
 789       CONST_CAST m_pCurrentGroup 
= m_pCurrentGroup
->Groups()[nGroup
]; 
 790       n 
+= GetNumberOfGroups(TRUE
); 
 791       CONST_CAST m_pCurrentGroup 
= pOldCurrentGroup
; 
 798 // ---------------------------------------------------------------------------- 
 799 // tests for existence 
 800 // ---------------------------------------------------------------------------- 
 802 bool wxFileConfig::HasGroup(const wxString
& strName
) const 
 804   wxConfigPathChanger 
path(this, strName
); 
 806   wxFileConfigGroup 
*pGroup 
= m_pCurrentGroup
->FindSubgroup(path
.Name()); 
 807   return pGroup 
!= NULL
; 
 810 bool wxFileConfig::HasEntry(const wxString
& strName
) const 
 812   wxConfigPathChanger 
path(this, strName
); 
 814   wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(path
.Name()); 
 815   return pEntry 
!= NULL
; 
 818 // ---------------------------------------------------------------------------- 
 820 // ---------------------------------------------------------------------------- 
 822 bool wxFileConfig::Read(const wxString
& key
, 
 823                         wxString
* pStr
) const 
 825   wxConfigPathChanger 
path(this, key
); 
 827   wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(path
.Name()); 
 828   if (pEntry 
== NULL
) { 
 832   *pStr 
= ExpandEnvVars(pEntry
->Value()); 
 836 bool wxFileConfig::Read(const wxString
& key
, 
 837                         wxString
* pStr
, const wxString
& defVal
) const 
 839   wxConfigPathChanger 
path(this, key
); 
 841   wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(path
.Name()); 
 843   if (pEntry 
== NULL
) { 
 844     if( IsRecordingDefaults() ) 
 845       ((wxFileConfig 
*)this)->Write(key
,defVal
); 
 846     *pStr 
= ExpandEnvVars(defVal
); 
 850     *pStr 
= ExpandEnvVars(pEntry
->Value()); 
 857 bool wxFileConfig::Read(const wxString
& key
, long *pl
) const 
 860   if ( !Read(key
, & str
) ) 
 869 bool wxFileConfig::Write(const wxString
& key
, const wxString
& szValue
) 
 871   wxConfigPathChanger 
path(this, key
); 
 873   wxString strName 
= path
.Name(); 
 874   if ( strName
.IsEmpty() ) { 
 875     // setting the value of a group is an error 
 876     wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") ); 
 878     // ... except if it's empty in which case it's a way to force it's creation 
 879     m_pCurrentGroup
->SetDirty(); 
 881     // this will add a line for this group if it didn't have it before 
 882     (void)m_pCurrentGroup
->GetGroupLine(); 
 887     // check that the name is reasonable 
 888     if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX 
) { 
 889       wxLogError(_("Config entry name cannot start with '%c'."), 
 890                  wxCONFIG_IMMUTABLE_PREFIX
); 
 894     wxFileConfigEntry 
*pEntry 
= m_pCurrentGroup
->FindEntry(strName
); 
 895     if ( pEntry 
== NULL 
) 
 896       pEntry 
= m_pCurrentGroup
->AddEntry(strName
); 
 898     pEntry
->SetValue(szValue
); 
 904 bool wxFileConfig::Write(const wxString
& key
, long lValue
) 
 906   // ltoa() is not ANSI :-( 
 908   buf
.Printf(wxT("%ld"), lValue
); 
 909   return Write(key
, buf
); 
 912 bool wxFileConfig::Flush(bool /* bCurrentOnly */) 
 914   if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile 
) 
 918   // set the umask if needed 
 922       umaskOld 
= umask((mode_t
)m_umask
); 
 926   wxTempFile 
file(m_strLocalFile
); 
 928   if ( !file
.IsOpened() ) { 
 929     wxLogError(_("can't open user configuration file.")); 
 933   // write all strings to file 
 934   for ( wxFileConfigLineList 
*p 
= m_linesHead
; p 
!= NULL
; p 
= p
->Next() ) { 
 935     if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) { 
 936       wxLogError(_("can't write user configuration file.")); 
 941   bool ret 
= file
.Commit(); 
 943 #if defined(__WXMAC__) 
 948         wxMacFilename2FSSpec( m_strLocalFile 
, &spec 
) ; 
 950         if ( FSpGetFInfo( &spec 
, &finfo 
) == noErr 
) 
 952                 finfo
.fdType 
= 'TEXT' ; 
 953                 finfo
.fdCreator 
= 'ttxt' ; 
 954                 FSpSetFInfo( &spec 
, &finfo 
) ; 
 960   // restore the old umask if we changed it 
 963       (void)umask(umaskOld
); 
 970 // ---------------------------------------------------------------------------- 
 971 // renaming groups/entries 
 972 // ---------------------------------------------------------------------------- 
 974 bool wxFileConfig::RenameEntry(const wxString
& oldName
, 
 975                                const wxString
& newName
) 
 977     // check that the entry exists 
 978     wxFileConfigEntry 
*oldEntry 
= m_pCurrentGroup
->FindEntry(oldName
); 
 982     // check that the new entry doesn't already exist 
 983     if ( m_pCurrentGroup
->FindEntry(newName
) ) 
 986     // delete the old entry, create the new one 
 987     wxString value 
= oldEntry
->Value(); 
 988     if ( !m_pCurrentGroup
->DeleteEntry(oldName
) ) 
 991     wxFileConfigEntry 
*newEntry 
= m_pCurrentGroup
->AddEntry(newName
); 
 992     newEntry
->SetValue(value
); 
 997 bool wxFileConfig::RenameGroup(const wxString
& oldName
, 
 998                                const wxString
& newName
) 
1000     // check that the group exists 
1001     wxFileConfigGroup 
*group 
= m_pCurrentGroup
->FindSubgroup(oldName
); 
1005     // check that the new group doesn't already exist 
1006     if ( m_pCurrentGroup
->FindSubgroup(newName
) ) 
1009     group
->Rename(newName
); 
1014 // ---------------------------------------------------------------------------- 
1015 // delete groups/entries 
1016 // ---------------------------------------------------------------------------- 
1018 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
) 
1020   wxConfigPathChanger 
path(this, key
); 
1022   if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) ) 
1025   if ( bGroupIfEmptyAlso 
&& m_pCurrentGroup
->IsEmpty() ) { 
1026     if ( m_pCurrentGroup 
!= m_pRootGroup 
) { 
1027       wxFileConfigGroup 
*pGroup 
= m_pCurrentGroup
; 
1028       SetPath(wxT(".."));  // changes m_pCurrentGroup! 
1029       m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name()); 
1031     //else: never delete the root group 
1037 bool wxFileConfig::DeleteGroup(const wxString
& key
) 
1039   wxConfigPathChanger 
path(this, key
); 
1041   return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()); 
1044 bool wxFileConfig::DeleteAll() 
1048   if ( wxRemove(m_strLocalFile
) == -1 ) 
1049     wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str()); 
1051   m_strLocalFile 
= m_strGlobalFile 
= wxT(""); 
1057 // ---------------------------------------------------------------------------- 
1058 // linked list functions 
1059 // ---------------------------------------------------------------------------- 
1061 // append a new line to the end of the list 
1062 wxFileConfigLineList 
*wxFileConfig::LineListAppend(const wxString
& str
) 
1064   wxFileConfigLineList 
*pLine 
= new wxFileConfigLineList(str
); 
1066   if ( m_linesTail 
== NULL 
) { 
1068     m_linesHead 
= pLine
; 
1072     m_linesTail
->SetNext(pLine
); 
1073     pLine
->SetPrev(m_linesTail
); 
1076   m_linesTail 
= pLine
; 
1080 // insert a new line after the given one or in the very beginning if !pLine 
1081 wxFileConfigLineList 
*wxFileConfig::LineListInsert(const wxString
& str
, 
1082                                                      wxFileConfigLineList 
*pLine
) 
1084   if ( pLine 
== m_linesTail 
) 
1085     return LineListAppend(str
); 
1087   wxFileConfigLineList 
*pNewLine 
= new wxFileConfigLineList(str
); 
1088   if ( pLine 
== NULL 
) { 
1089     // prepend to the list 
1090     pNewLine
->SetNext(m_linesHead
); 
1091     m_linesHead
->SetPrev(pNewLine
); 
1092     m_linesHead 
= pNewLine
; 
1095     // insert before pLine 
1096     wxFileConfigLineList 
*pNext 
= pLine
->Next(); 
1097     pNewLine
->SetNext(pNext
); 
1098     pNewLine
->SetPrev(pLine
); 
1099     pNext
->SetPrev(pNewLine
); 
1100     pLine
->SetNext(pNewLine
); 
1106 void wxFileConfig::LineListRemove(wxFileConfigLineList 
*pLine
) 
1108   wxFileConfigLineList 
*pPrev 
= pLine
->Prev(), 
1109            *pNext 
= pLine
->Next(); 
1112   if ( pPrev 
== NULL 
) 
1113     m_linesHead 
= pNext
; 
1115     pPrev
->SetNext(pNext
); 
1118   if ( pNext 
== NULL 
) 
1119     m_linesTail 
= pPrev
; 
1121     pNext
->SetPrev(pPrev
); 
1126 bool wxFileConfig::LineListIsEmpty() 
1128   return m_linesHead 
== NULL
; 
1131 // ============================================================================ 
1132 // wxFileConfig::wxFileConfigGroup 
1133 // ============================================================================ 
1135 // ---------------------------------------------------------------------------- 
1137 // ---------------------------------------------------------------------------- 
1140 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup 
*pParent
, 
1141                                        const wxString
& strName
, 
1142                                        wxFileConfig 
*pConfig
) 
1143                          : m_aEntries(CompareEntries
), 
1144                            m_aSubgroups(CompareGroups
), 
1147   m_pConfig 
= pConfig
; 
1148   m_pParent 
= pParent
; 
1152   m_pLastEntry 
= NULL
; 
1153   m_pLastGroup 
= NULL
; 
1156 // dtor deletes all children 
1157 wxFileConfigGroup::~wxFileConfigGroup() 
1160   size_t n
, nCount 
= m_aEntries
.Count(); 
1161   for ( n 
= 0; n 
< nCount
; n
++ ) 
1162     delete m_aEntries
[n
]; 
1165   nCount 
= m_aSubgroups
.Count(); 
1166   for ( n 
= 0; n 
< nCount
; n
++ ) 
1167     delete m_aSubgroups
[n
]; 
1170 // ---------------------------------------------------------------------------- 
1172 // ---------------------------------------------------------------------------- 
1174 void wxFileConfigGroup::SetLine(wxFileConfigLineList 
*pLine
) 
1176   wxASSERT( m_pLine 
== NULL 
); // shouldn't be called twice 
1182   This is a bit complicated, so let me explain it in details. All lines that 
1183   were read from the local file (the only one we will ever modify) are stored 
1184   in a (doubly) linked list. Our problem is to know at which position in this 
1185   list should we insert the new entries/subgroups. To solve it we keep three 
1186   variables for each group: m_pLine, m_pLastEntry and m_pLastGroup. 
1188   m_pLine points to the line containing "[group_name]" 
1189   m_pLastEntry points to the last entry of this group in the local file. 
1190   m_pLastGroup                   subgroup 
1192   Initially, they're NULL all three. When the group (an entry/subgroup) is read 
1193   from the local file, the corresponding variable is set. However, if the group 
1194   was read from the global file and then modified or created by the application 
1195   these variables are still NULL and we need to create the corresponding lines. 
1196   See the following functions (and comments preceding them) for the details of 
1199   Also, when our last entry/group are deleted we need to find the new last 
1200   element - the code in DeleteEntry/Subgroup does this by backtracking the list 
1201   of lines until it either founds an entry/subgroup (and this is the new last 
1202   element) or the m_pLine of the group, in which case there are no more entries 
1203   (or subgroups) left and m_pLast<element> becomes NULL. 
1205   NB: This last problem could be avoided for entries if we added new entries 
1206       immediately after m_pLine, but in this case the entries would appear 
1207       backwards in the config file (OTOH, it's not that important) and as we 
1208       would still need to do it for the subgroups the code wouldn't have been 
1209       significantly less complicated. 
1212 // Return the line which contains "[our name]". If we're still not in the list, 
1213 // add our line to it immediately after the last line of our parent group if we 
1214 // have it or in the very beginning if we're the root group. 
1215 wxFileConfigLineList 
*wxFileConfigGroup::GetGroupLine() 
1217   if ( m_pLine 
== NULL 
) { 
1218     wxFileConfigGroup 
*pParent 
= Parent(); 
1220     // this group wasn't present in local config file, add it now 
1221     if ( pParent 
!= NULL 
) { 
1222       wxString strFullName
; 
1223       strFullName 
<< wxT("[") 
1225                   << FilterOutEntryName(GetFullName().c_str() + 1) 
1227       m_pLine 
= m_pConfig
->LineListInsert(strFullName
, 
1228                                           pParent
->GetLastGroupLine()); 
1229       pParent
->SetLastGroup(this);  // we're surely after all the others 
1232       // we return NULL, so that LineListInsert() will insert us in the 
1240 // Return the last line belonging to the subgroups of this group (after which 
1241 // we can add a new subgroup), if we don't have any subgroups or entries our 
1242 // last line is the group line (m_pLine) itself. 
1243 wxFileConfigLineList 
*wxFileConfigGroup::GetLastGroupLine() 
1245   // if we have any subgroups, our last line is the last line of the last 
1247   if ( m_pLastGroup 
!= NULL 
) { 
1248     wxFileConfigLineList 
*pLine 
= m_pLastGroup
->GetLastGroupLine(); 
1250     wxASSERT( pLine 
!= NULL 
);  // last group must have !NULL associated line 
1254   // no subgroups, so the last line is the line of thelast entry (if any) 
1255   return GetLastEntryLine(); 
1258 // return the last line belonging to the entries of this group (after which 
1259 // we can add a new entry), if we don't have any entries we will add the new 
1260 // one immediately after the group line itself. 
1261 wxFileConfigLineList 
*wxFileConfigGroup::GetLastEntryLine() 
1263   if ( m_pLastEntry 
!= NULL 
) { 
1264     wxFileConfigLineList 
*pLine 
= m_pLastEntry
->GetLine(); 
1266     wxASSERT( pLine 
!= NULL 
);  // last entry must have !NULL associated line 
1270   // no entries: insert after the group header 
1271   return GetGroupLine(); 
1274 // ---------------------------------------------------------------------------- 
1276 // ---------------------------------------------------------------------------- 
1278 void wxFileConfigGroup::Rename(const wxString
& newName
) 
1280     m_strName 
= newName
; 
1282     wxFileConfigLineList 
*line 
= GetGroupLine(); 
1283     wxString strFullName
; 
1284     strFullName 
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/' 
1285     line
->SetText(strFullName
); 
1290 wxString 
wxFileConfigGroup::GetFullName() const 
1293     return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR 
+ Name(); 
1298 // ---------------------------------------------------------------------------- 
1300 // ---------------------------------------------------------------------------- 
1302 // use binary search because the array is sorted 
1304 wxFileConfigGroup::FindEntry(const wxChar 
*szName
) const 
1308        hi 
= m_aEntries
.Count(); 
1310   wxFileConfigEntry 
*pEntry
; 
1314     pEntry 
= m_aEntries
[i
]; 
1316     #if wxCONFIG_CASE_SENSITIVE 
1317       res 
= wxStrcmp(pEntry
->Name(), szName
); 
1319       res 
= wxStricmp(pEntry
->Name(), szName
); 
1334 wxFileConfigGroup::FindSubgroup(const wxChar 
*szName
) const 
1338        hi 
= m_aSubgroups
.Count(); 
1340   wxFileConfigGroup 
*pGroup
; 
1344     pGroup 
= m_aSubgroups
[i
]; 
1346     #if wxCONFIG_CASE_SENSITIVE 
1347       res 
= wxStrcmp(pGroup
->Name(), szName
); 
1349       res 
= wxStricmp(pGroup
->Name(), szName
); 
1363 // ---------------------------------------------------------------------------- 
1364 // create a new item 
1365 // ---------------------------------------------------------------------------- 
1367 // create a new entry and add it to the current group 
1369 wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
) 
1371   wxASSERT( FindEntry(strName
) == NULL 
); 
1373   wxFileConfigEntry 
*pEntry 
= new wxFileConfigEntry(this, strName
, nLine
); 
1374   m_aEntries
.Add(pEntry
); 
1379 // create a new group and add it to the current group 
1381 wxFileConfigGroup::AddSubgroup(const wxString
& strName
) 
1383   wxASSERT( FindSubgroup(strName
) == NULL 
); 
1385   wxFileConfigGroup 
*pGroup 
= new wxFileConfigGroup(this, strName
, m_pConfig
); 
1386   m_aSubgroups
.Add(pGroup
); 
1391 // ---------------------------------------------------------------------------- 
1393 // ---------------------------------------------------------------------------- 
1396   The delete operations are _very_ slow if we delete the last item of this 
1397   group (see comments before GetXXXLineXXX functions for more details), 
1398   so it's much better to start with the first entry/group if we want to 
1399   delete several of them. 
1402 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar 
*szName
) 
1404   return DeleteSubgroup(FindSubgroup(szName
)); 
1407 // doesn't delete the subgroup itself, but does remove references to it from 
1408 // all other data structures (and normally the returned pointer should be 
1409 // deleted a.s.a.p. because there is nothing much to be done with it anyhow) 
1410 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup 
*pGroup
) 
1412   wxCHECK( pGroup 
!= NULL
, FALSE 
); // deleting non existing group? 
1414   // delete all entries 
1415   size_t nCount 
= pGroup
->m_aEntries
.Count(); 
1416   for ( size_t nEntry 
= 0; nEntry 
< nCount
; nEntry
++ ) { 
1417     wxFileConfigLineList 
*pLine 
= pGroup
->m_aEntries
[nEntry
]->GetLine(); 
1418     if ( pLine 
!= NULL 
) 
1419       m_pConfig
->LineListRemove(pLine
); 
1422   // and subgroups of this sungroup 
1423   nCount 
= pGroup
->m_aSubgroups
.Count(); 
1424   for ( size_t nGroup 
= 0; nGroup 
< nCount
; nGroup
++ ) { 
1425     pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]); 
1428   wxFileConfigLineList 
*pLine 
= pGroup
->m_pLine
; 
1429   if ( pLine 
!= NULL 
) { 
1430     // notice that we may do this test inside the previous "if" because the 
1431     // last entry's line is surely !NULL 
1432     if ( pGroup 
== m_pLastGroup 
) { 
1433       // our last entry is being deleted - find the last one which stays 
1434       wxASSERT( m_pLine 
!= NULL 
);  // we have a subgroup with !NULL pLine... 
1436       // go back until we find a subgroup or reach the group's line 
1437       wxFileConfigGroup 
*pNewLast 
= NULL
; 
1438       size_t n
, nSubgroups 
= m_aSubgroups
.Count(); 
1439       wxFileConfigLineList 
*pl
; 
1440       for ( pl 
= pLine
->Prev(); pl 
!= m_pLine
; pl 
= pl
->Prev() ) { 
1441         // is it our subgroup? 
1442         for ( n 
= 0; (pNewLast 
== NULL
) && (n 
< nSubgroups
); n
++ ) { 
1443           // do _not_ call GetGroupLine! we don't want to add it to the local 
1444           // file if it's not already there 
1445           if ( m_aSubgroups
[n
]->m_pLine 
== m_pLine 
) 
1446             pNewLast 
= m_aSubgroups
[n
]; 
1449         if ( pNewLast 
!= NULL 
) // found? 
1453       if ( pl 
== m_pLine 
) { 
1454         wxASSERT( !pNewLast 
);  // how comes it has the same line as we? 
1456         // we've reached the group line without finding any subgroups 
1457         m_pLastGroup 
= NULL
; 
1460         m_pLastGroup 
= pNewLast
; 
1463     m_pConfig
->LineListRemove(pLine
); 
1468   m_aSubgroups
.Remove(pGroup
); 
1474 bool wxFileConfigGroup::DeleteEntry(const wxChar 
*szName
) 
1476   wxFileConfigEntry 
*pEntry 
= FindEntry(szName
); 
1477   wxCHECK( pEntry 
!= NULL
, FALSE 
);  // deleting non existing item? 
1479   wxFileConfigLineList 
*pLine 
= pEntry
->GetLine(); 
1480   if ( pLine 
!= NULL 
) { 
1481     // notice that we may do this test inside the previous "if" because the 
1482     // last entry's line is surely !NULL 
1483     if ( pEntry 
== m_pLastEntry 
) { 
1484       // our last entry is being deleted - find the last one which stays 
1485       wxASSERT( m_pLine 
!= NULL 
);  // if we have an entry with !NULL pLine... 
1487       // go back until we find another entry or reach the group's line 
1488       wxFileConfigEntry 
*pNewLast 
= NULL
; 
1489       size_t n
, nEntries 
= m_aEntries
.Count(); 
1490       wxFileConfigLineList 
*pl
; 
1491       for ( pl 
= pLine
->Prev(); pl 
!= m_pLine
; pl 
= pl
->Prev() ) { 
1492         // is it our subgroup? 
1493         for ( n 
= 0; (pNewLast 
== NULL
) && (n 
< nEntries
); n
++ ) { 
1494           if ( m_aEntries
[n
]->GetLine() == m_pLine 
) 
1495             pNewLast 
= m_aEntries
[n
]; 
1498         if ( pNewLast 
!= NULL 
) // found? 
1502       if ( pl 
== m_pLine 
) { 
1503         wxASSERT( !pNewLast 
);  // how comes it has the same line as we? 
1505         // we've reached the group line without finding any subgroups 
1506         m_pLastEntry 
= NULL
; 
1509         m_pLastEntry 
= pNewLast
; 
1512     m_pConfig
->LineListRemove(pLine
); 
1515   // we must be written back for the changes to be saved 
1518   m_aEntries
.Remove(pEntry
); 
1524 // ---------------------------------------------------------------------------- 
1526 // ---------------------------------------------------------------------------- 
1527 void wxFileConfigGroup::SetDirty() 
1530   if ( Parent() != NULL 
)             // propagate upwards 
1531     Parent()->SetDirty(); 
1534 // ============================================================================ 
1535 // wxFileConfig::wxFileConfigEntry 
1536 // ============================================================================ 
1538 // ---------------------------------------------------------------------------- 
1540 // ---------------------------------------------------------------------------- 
1541 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup 
*pParent
, 
1542                                        const wxString
& strName
, 
1544                          : m_strName(strName
) 
1546   wxASSERT( !strName
.IsEmpty() ); 
1548   m_pParent 
= pParent
; 
1553   m_bHasValue 
= FALSE
; 
1555   m_bImmutable 
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
; 
1557     m_strName
.erase(0, 1);  // remove first character 
1560 // ---------------------------------------------------------------------------- 
1562 // ---------------------------------------------------------------------------- 
1564 void wxFileConfigEntry::SetLine(wxFileConfigLineList 
*pLine
) 
1566   if ( m_pLine 
!= NULL 
) { 
1567     wxLogWarning(_("entry '%s' appears more than once in group '%s'"), 
1568                  Name().c_str(), m_pParent
->GetFullName().c_str()); 
1572   Group()->SetLastEntry(this); 
1575 // second parameter is FALSE if we read the value from file and prevents the 
1576 // entry from being marked as 'dirty' 
1577 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
) 
1579   if ( bUser 
&& IsImmutable() ) { 
1580     wxLogWarning(_("attempt to change immutable key '%s' ignored."), 
1585   // do nothing if it's the same value: but don't test for it if m_bHasValue 
1586   // hadn't been set yet or we'd never write empty values to the file 
1587   if ( m_bHasValue 
&& strValue 
== m_strValue 
) 
1591   m_strValue 
= strValue
; 
1594     wxString strVal 
= FilterOutValue(strValue
); 
1596     strLine 
<< FilterOutEntryName(m_strName
) << wxT('=') << strVal
; 
1598     if ( m_pLine 
!= NULL 
) { 
1599       // entry was read from the local config file, just modify the line 
1600       m_pLine
->SetText(strLine
); 
1603       // add a new line to the file 
1604       wxASSERT( m_nLine 
== wxNOT_FOUND 
);   // consistency check 
1606       m_pLine 
= Group()->Config()->LineListInsert(strLine
, 
1607                                                   Group()->GetLastEntryLine()); 
1608       Group()->SetLastEntry(this); 
1615 void wxFileConfigEntry::SetDirty() 
1618   Group()->SetDirty(); 
1621 // ============================================================================ 
1623 // ============================================================================ 
1625 // ---------------------------------------------------------------------------- 
1626 // compare functions for array sorting 
1627 // ---------------------------------------------------------------------------- 
1629 int CompareEntries(wxFileConfigEntry 
*p1
, wxFileConfigEntry 
*p2
) 
1631   #if wxCONFIG_CASE_SENSITIVE 
1632     return wxStrcmp(p1
->Name(), p2
->Name()); 
1634     return wxStricmp(p1
->Name(), p2
->Name()); 
1638 int CompareGroups(wxFileConfigGroup 
*p1
, wxFileConfigGroup 
*p2
) 
1640   #if wxCONFIG_CASE_SENSITIVE 
1641     return wxStrcmp(p1
->Name(), p2
->Name()); 
1643     return wxStricmp(p1
->Name(), p2
->Name()); 
1647 // ---------------------------------------------------------------------------- 
1649 // ---------------------------------------------------------------------------- 
1651 // undo FilterOutValue 
1652 static wxString 
FilterInValue(const wxString
& str
) 
1655   strResult
.Alloc(str
.Len()); 
1657   bool bQuoted 
= !str
.IsEmpty() && str
[0] == '"'; 
1659   for ( size_t n 
= bQuoted 
? 1 : 0; n 
< str
.Len(); n
++ ) { 
1660     if ( str
[n
] == wxT('\\') ) { 
1661       switch ( str
[++n
] ) { 
1663           strResult 
+= wxT('\n'); 
1667           strResult 
+= wxT('\r'); 
1671           strResult 
+= wxT('\t'); 
1675           strResult 
+= wxT('\\'); 
1679           strResult 
+= wxT('"'); 
1684       if ( str
[n
] != wxT('"') || !bQuoted 
) 
1685         strResult 
+= str
[n
]; 
1686       else if ( n 
!= str
.Len() - 1 ) { 
1687         wxLogWarning(_("unexpected \" at position %d in '%s'."), 
1690       //else: it's the last quote of a quoted string, ok 
1697 // quote the string before writing it to file 
1698 static wxString 
FilterOutValue(const wxString
& str
) 
1704   strResult
.Alloc(str
.Len()); 
1706   // quoting is necessary to preserve spaces in the beginning of the string 
1707   bool bQuote 
= wxIsspace(str
[0]) || str
[0] == wxT('"'); 
1710     strResult 
+= wxT('"'); 
1713   for ( size_t n 
= 0; n 
< str
.Len(); n
++ ) { 
1736         //else: fall through 
1739         strResult 
+= str
[n
]; 
1740         continue;   // nothing special to do 
1743     // we get here only for special characters 
1744     strResult 
<< wxT('\\') << c
; 
1748     strResult 
+= wxT('"'); 
1753 // undo FilterOutEntryName 
1754 static wxString 
FilterInEntryName(const wxString
& str
) 
1757   strResult
.Alloc(str
.Len()); 
1759   for ( const wxChar 
*pc 
= str
.c_str(); *pc 
!= '\0'; pc
++ ) { 
1760     if ( *pc 
== wxT('\\') ) 
1769 // sanitize entry or group name: insert '\\' before any special characters 
1770 static wxString 
FilterOutEntryName(const wxString
& str
) 
1773   strResult
.Alloc(str
.Len()); 
1775   for ( const wxChar 
*pc 
= str
.c_str(); *pc 
!= wxT('\0'); pc
++ ) { 
1778     // we explicitly allow some of "safe" chars and 8bit ASCII characters 
1779     // which will probably never have special meaning 
1780     // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR 
1781     //     should *not* be quoted 
1782     if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c 
& 0x80) == 0) ) 
1783       strResult 
+= wxT('\\'); 
1791 // we can't put ?: in the ctor initializer list because it confuses some 
1792 // broken compilers (Borland C++) 
1793 static wxString 
GetAppName(const wxString
& appName
) 
1795     if ( !appName 
&& wxTheApp 
) 
1796         return wxTheApp
->GetAppName(); 
1801 #endif // wxUSE_CONFIG