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 #if defined(__WXMAC__)
50 #include "wx/mac/private.h" // includes mac headers
53 // _WINDOWS_ is defined when windows.h is included,
54 // __WXMSW__ is defined for MS Windows compilation
55 #if defined(__WXMSW__) && !defined(_WINDOWS_)
66 // headers needed for umask()
68 #include <sys/types.h>
72 // ----------------------------------------------------------------------------
74 // ----------------------------------------------------------------------------
75 #define CONST_CAST ((wxFileConfig *)this)->
77 // ----------------------------------------------------------------------------
79 // ----------------------------------------------------------------------------
85 // ----------------------------------------------------------------------------
86 // global functions declarations
87 // ----------------------------------------------------------------------------
89 // compare functions for sorting the arrays
90 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
91 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
94 static wxString
FilterInValue(const wxString
& str
);
95 static wxString
FilterOutValue(const wxString
& str
);
97 static wxString
FilterInEntryName(const wxString
& str
);
98 static wxString
FilterOutEntryName(const wxString
& str
);
100 // get the name to use in wxFileConfig ctor
101 static wxString
GetAppName(const wxString
& appname
);
103 // ============================================================================
105 // ============================================================================
107 // ----------------------------------------------------------------------------
108 // "template" array types
109 // ----------------------------------------------------------------------------
111 WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
112 WX_DEFINE_SORTED_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
114 // ----------------------------------------------------------------------------
115 // wxFileConfigLineList
116 // ----------------------------------------------------------------------------
118 // we store all lines of the local config file as a linked list in memory
119 class wxFileConfigLineList
122 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
123 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
126 wxFileConfigLineList(const wxString
& str
,
127 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
128 { SetNext(pNext
); SetPrev(NULL
); }
130 // next/prev nodes in the linked list
131 wxFileConfigLineList
*Next() const { return m_pNext
; }
132 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
134 // get/change lines text
135 void SetText(const wxString
& str
) { m_strLine
= str
; }
136 const wxString
& Text() const { return m_strLine
; }
139 wxString m_strLine
; // line contents
140 wxFileConfigLineList
*m_pNext
, // next node
141 *m_pPrev
; // previous one
144 // ----------------------------------------------------------------------------
145 // wxFileConfigEntry: a name/value pair
146 // ----------------------------------------------------------------------------
148 class wxFileConfigEntry
151 wxFileConfigGroup
*m_pParent
; // group that contains us
153 wxString m_strName
, // entry name
155 bool m_bDirty
:1, // changed since last read?
156 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 IsDirty() const { return m_bDirty
; }
174 bool IsImmutable() const { return m_bImmutable
; }
175 bool IsLocal() const { return m_pLine
!= 0; }
176 int Line() const { return m_nLine
; }
177 wxFileConfigLineList
*
178 GetLine() const { return m_pLine
; }
180 // modify entry attributes
181 void SetValue(const wxString
& strValue
, bool bUser
= TRUE
);
183 void SetLine(wxFileConfigLineList
*pLine
);
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 bool m_bDirty
; // if FALSE => all subgroups are not dirty
199 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
200 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
201 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
203 // DeleteSubgroupByName helper
204 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
208 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
210 // dtor deletes all entries and subgroups also
211 ~wxFileConfigGroup();
214 const wxString
& Name() const { return m_strName
; }
215 wxFileConfigGroup
*Parent() const { return m_pParent
; }
216 wxFileConfig
*Config() const { return m_pConfig
; }
217 bool IsDirty() const { return m_bDirty
; }
219 const ArrayEntries
& Entries() const { return m_aEntries
; }
220 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
221 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
223 // find entry/subgroup (NULL if not found)
224 wxFileConfigGroup
*FindSubgroup(const wxChar
*szName
) const;
225 wxFileConfigEntry
*FindEntry (const wxChar
*szName
) const;
227 // delete entry/subgroup, return FALSE if doesn't exist
228 bool DeleteSubgroupByName(const wxChar
*szName
);
229 bool DeleteEntry(const wxChar
*szName
);
231 // create new entry/subgroup returning pointer to newly created element
232 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
233 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
235 // will also recursively set parent's dirty flag
237 void SetLine(wxFileConfigLineList
*pLine
);
239 // rename: no checks are done to ensure that the name is unique!
240 void Rename(const wxString
& newName
);
243 wxString
GetFullName() const;
245 // get the last line belonging to an entry/subgroup of this group
246 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
247 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
248 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
250 // called by entries/subgroups when they're created/deleted
251 void SetLastEntry(wxFileConfigEntry
*pEntry
) { m_pLastEntry
= pEntry
; }
252 void SetLastGroup(wxFileConfigGroup
*pGroup
) { m_pLastGroup
= pGroup
; }
255 // ============================================================================
257 // ============================================================================
259 // ----------------------------------------------------------------------------
261 // ----------------------------------------------------------------------------
262 wxString
wxFileConfig::GetGlobalDir()
266 #ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined
267 strDir
= wxT("sys$manager:");
268 #elif defined(__WXMAC__)
269 strDir
= wxMacFindFolder( (short) kOnSystemDisk
, kPreferencesFolderType
, kDontCreateFolder
) ;
270 #elif defined( __UNIX__ )
271 strDir
= wxT("/etc/");
272 #elif defined(__WXPM__)
273 ULONG aulSysInfo
[QSV_MAX
] = {0};
277 rc
= DosQuerySysInfo( 1L, QSV_MAX
, (PVOID
)aulSysInfo
, sizeof(ULONG
)*QSV_MAX
);
280 drive
= aulSysInfo
[QSV_BOOT_DRIVE
- 1];
281 strDir
.Printf(wxT("%c:\\OS2\\"), 'A'+drive
-1);
283 #elif defined(__WXSTUBS__)
284 wxASSERT_MSG( FALSE
, wxT("TODO") ) ;
285 #elif defined(__DOS__)
286 // There's no such thing as global cfg dir in MS-DOS, let's return
287 // current directory (FIXME_MGL?)
290 wxChar szWinDir
[MAX_PATH
];
291 ::GetWindowsDirectory(szWinDir
, MAX_PATH
);
295 #endif // Unix/Windows
300 wxString
wxFileConfig::GetLocalDir()
304 #if defined(__WXMAC__) || defined(__DOS__)
305 // no local dir concept on Mac OS 9 or MS-DOS
306 return GetGlobalDir() ;
308 wxGetHomeDir(&strDir
);
312 if (strDir
.Last() != wxT(']'))
314 if (strDir
.Last() != wxT('/')) strDir
<< wxT('/');
316 if (strDir
.Last() != wxT('\\')) strDir
<< wxT('\\');
323 wxString
wxFileConfig::GetGlobalFileName(const wxChar
*szFile
)
325 wxString str
= GetGlobalDir();
328 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
329 #if defined( __WXMAC__ )
330 str
<< " Preferences";
331 #elif defined( __UNIX__ )
340 wxString
wxFileConfig::GetLocalFileName(const wxChar
*szFile
)
342 #ifdef __VMS__ // On VMS I saw the problem that the home directory was appended
343 // twice for the configuration file. Does that also happen for other
345 wxString str
= wxT( '.' );
347 wxString str
= GetLocalDir();
350 #if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ )
356 #if defined(__WINDOWS__) || defined(__DOS__)
357 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
362 str
<< " Preferences";
367 // ----------------------------------------------------------------------------
369 // ----------------------------------------------------------------------------
371 void wxFileConfig::Init()
374 m_pRootGroup
= new wxFileConfigGroup(NULL
, "", this);
379 // it's not an error if (one of the) file(s) doesn't exist
381 // parse the global file
382 if ( !m_strGlobalFile
.IsEmpty() && wxFile::Exists(m_strGlobalFile
) ) {
383 wxTextFile
fileGlobal(m_strGlobalFile
);
385 if ( fileGlobal
.Open() ) {
386 Parse(fileGlobal
, FALSE
/* global */);
390 wxLogWarning(_("can't open global configuration file '%s'."),
391 m_strGlobalFile
.c_str());
394 // parse the local file
395 if ( !m_strLocalFile
.IsEmpty() && wxFile::Exists(m_strLocalFile
) ) {
396 wxTextFile
fileLocal(m_strLocalFile
);
397 if ( fileLocal
.Open() ) {
398 Parse(fileLocal
, TRUE
/* local */);
402 wxLogWarning(_("can't open user configuration file '%s'."),
403 m_strLocalFile
.c_str());
407 // constructor supports creation of wxFileConfig objects of any type
408 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
409 const wxString
& strLocal
, const wxString
& strGlobal
,
411 : wxConfigBase(::GetAppName(appName
), vendorName
,
414 m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
)
416 // Make up names for files if empty
417 if ( m_strLocalFile
.IsEmpty() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
419 m_strLocalFile
= GetLocalFileName(GetAppName());
422 if ( m_strGlobalFile
.IsEmpty() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
424 m_strGlobalFile
= GetGlobalFileName(GetAppName());
427 // Check if styles are not supplied, but filenames are, in which case
428 // add the correct styles.
429 if ( !m_strLocalFile
.IsEmpty() )
430 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
432 if ( !m_strGlobalFile
.IsEmpty() )
433 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
435 // if the path is not absolute, prepend the standard directory to it
436 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
437 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
439 if ( !m_strLocalFile
.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile
) )
441 wxString strLocal
= m_strLocalFile
;
442 m_strLocalFile
= GetLocalDir();
443 m_strLocalFile
<< strLocal
;
446 if ( !m_strGlobalFile
.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile
) )
448 wxString strGlobal
= m_strGlobalFile
;
449 m_strGlobalFile
= GetGlobalDir();
450 m_strGlobalFile
<< strGlobal
;
461 wxFileConfig::wxFileConfig(wxInputStream
&inStream
)
463 // always local_file when this constructor is called (?)
464 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
467 m_pRootGroup
= new wxFileConfigGroup(NULL
, "", this);
472 // translate everything to the current (platform-dependent) line
473 // termination character
479 while ( !inStream
.Read(buf
, WXSIZEOF(buf
)).Eof() )
480 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
482 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
484 strTrans
= wxTextBuffer::Translate(strTmp
);
487 wxMemoryText memText
;
489 // Now we can add the text to the memory text. To do this we extract line
490 // by line from the translated string, until we've reached the end.
492 // VZ: all this is horribly inefficient, we should do the translation on
493 // the fly in one pass saving both memory and time (TODO)
495 const wxChar
*pEOL
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
);
496 const size_t EOLLen
= wxStrlen(pEOL
);
498 int posLineStart
= strTrans
.Find(pEOL
);
499 while ( posLineStart
!= -1 )
501 wxString
line(strTrans
.Left(posLineStart
));
503 memText
.AddLine(line
);
505 strTrans
= strTrans
.Mid(posLineStart
+ EOLLen
);
507 posLineStart
= strTrans
.Find(pEOL
);
510 // also add whatever we have left in the translated string.
511 memText
.AddLine(strTrans
);
513 // Finally we can parse it all.
514 Parse(memText
, TRUE
/* local */);
519 #endif // wxUSE_STREAMS
521 void wxFileConfig::CleanUp()
525 wxFileConfigLineList
*pCur
= m_linesHead
;
526 while ( pCur
!= NULL
) {
527 wxFileConfigLineList
*pNext
= pCur
->Next();
533 wxFileConfig::~wxFileConfig()
540 // ----------------------------------------------------------------------------
541 // parse a config file
542 // ----------------------------------------------------------------------------
544 void wxFileConfig::Parse(wxTextBuffer
& buffer
, bool bLocal
)
546 const wxChar
*pStart
;
550 size_t nLineCount
= buffer
.GetLineCount();
551 for ( size_t n
= 0; n
< nLineCount
; n
++ ) {
554 // add the line to linked list
556 LineListAppend(strLine
);
558 // skip leading spaces
559 for ( pStart
= strLine
; wxIsspace(*pStart
); pStart
++ )
562 // skip blank/comment lines
563 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
566 if ( *pStart
== wxT('[') ) { // a new group
569 while ( *++pEnd
!= wxT(']') ) {
570 if ( *pEnd
== wxT('\\') ) {
571 // the next char is escaped, so skip it even if it is ']'
575 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
576 // we reached the end of line, break out of the loop
581 if ( *pEnd
!= wxT(']') ) {
582 wxLogError(_("file '%s': unexpected character %c at line %d."),
583 buffer
.GetName(), *pEnd
, n
+ 1);
584 continue; // skip this line
587 // group name here is always considered as abs path
590 strGroup
<< wxCONFIG_PATH_SEPARATOR
591 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
593 // will create it if doesn't yet exist
597 m_pCurrentGroup
->SetLine(m_linesTail
);
599 // check that there is nothing except comments left on this line
601 while ( *++pEnd
!= wxT('\0') && bCont
) {
610 // ignore whitespace ('\n' impossible here)
614 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
615 buffer
.GetName(), n
+ 1, pEnd
);
621 const wxChar
*pEnd
= pStart
;
622 while ( *pEnd
&& *pEnd
!= wxT('=') && !wxIsspace(*pEnd
) ) {
623 if ( *pEnd
== wxT('\\') ) {
624 // next character may be space or not - still take it because it's
625 // quoted (unless there is nothing)
628 // the error message will be given below anyhow
636 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
)));
639 while ( wxIsspace(*pEnd
) )
642 if ( *pEnd
++ != wxT('=') ) {
643 wxLogError(_("file '%s', line %d: '=' expected."),
644 buffer
.GetName(), n
+ 1);
647 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
649 if ( pEntry
== NULL
) {
651 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
654 pEntry
->SetLine(m_linesTail
);
657 if ( bLocal
&& pEntry
->IsImmutable() ) {
658 // immutable keys can't be changed by user
659 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
660 buffer
.GetName(), n
+ 1, strKey
.c_str());
663 // the condition below catches the cases (a) and (b) but not (c):
664 // (a) global key found second time in global file
665 // (b) key found second (or more) time in local file
666 // (c) key from global file now found in local one
667 // which is exactly what we want.
668 else if ( !bLocal
|| pEntry
->IsLocal() ) {
669 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
670 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
673 pEntry
->SetLine(m_linesTail
);
678 while ( wxIsspace(*pEnd
) )
681 pEntry
->SetValue(FilterInValue(pEnd
), FALSE
/* read from file */);
687 // ----------------------------------------------------------------------------
689 // ----------------------------------------------------------------------------
691 void wxFileConfig::SetRootPath()
694 m_pCurrentGroup
= m_pRootGroup
;
697 void wxFileConfig::SetPath(const wxString
& strPath
)
699 wxArrayString aParts
;
701 if ( strPath
.IsEmpty() ) {
706 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
708 wxSplitPath(aParts
, strPath
);
711 // relative path, combine with current one
712 wxString strFullPath
= m_strPath
;
713 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
714 wxSplitPath(aParts
, strFullPath
);
717 // change current group
719 m_pCurrentGroup
= m_pRootGroup
;
720 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
721 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
722 if ( pNextGroup
== NULL
)
723 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
724 m_pCurrentGroup
= pNextGroup
;
727 // recombine path parts in one variable
729 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
730 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
734 // ----------------------------------------------------------------------------
736 // ----------------------------------------------------------------------------
738 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
741 return GetNextGroup(str
, lIndex
);
744 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
746 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
747 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
754 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
757 return GetNextEntry(str
, lIndex
);
760 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
762 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
763 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
770 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
772 size_t n
= m_pCurrentGroup
->Entries().Count();
774 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
775 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
776 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
777 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
778 n
+= GetNumberOfEntries(TRUE
);
779 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
786 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
788 size_t n
= m_pCurrentGroup
->Groups().Count();
790 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
791 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
792 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
793 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
794 n
+= GetNumberOfGroups(TRUE
);
795 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
802 // ----------------------------------------------------------------------------
803 // tests for existence
804 // ----------------------------------------------------------------------------
806 bool wxFileConfig::HasGroup(const wxString
& strName
) const
808 wxConfigPathChanger
path(this, strName
);
810 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
->FindSubgroup(path
.Name());
811 return pGroup
!= NULL
;
814 bool wxFileConfig::HasEntry(const wxString
& strName
) const
816 wxConfigPathChanger
path(this, strName
);
818 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
819 return pEntry
!= NULL
;
822 // ----------------------------------------------------------------------------
824 // ----------------------------------------------------------------------------
826 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
828 wxConfigPathChanger
path(this, key
);
830 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
831 if (pEntry
== NULL
) {
835 *pStr
= pEntry
->Value();
840 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
843 if ( !Read(key
, & str
) )
847 *pl
= str
.ToLong(pl
) ;
851 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
853 wxConfigPathChanger
path(this, key
);
855 wxString strName
= path
.Name();
856 if ( strName
.IsEmpty() ) {
857 // setting the value of a group is an error
858 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
860 // ... except if it's empty in which case it's a way to force it's creation
861 m_pCurrentGroup
->SetDirty();
863 // this will add a line for this group if it didn't have it before
864 (void)m_pCurrentGroup
->GetGroupLine();
869 // check that the name is reasonable
870 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
) {
871 wxLogError(_("Config entry name cannot start with '%c'."),
872 wxCONFIG_IMMUTABLE_PREFIX
);
876 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
877 if ( pEntry
== NULL
)
878 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
880 pEntry
->SetValue(szValue
);
886 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
888 return Write(key
, wxString::Format(_T("%ld"), lValue
));
891 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
893 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
897 // set the umask if needed
901 umaskOld
= umask((mode_t
)m_umask
);
905 wxTempFile
file(m_strLocalFile
);
907 if ( !file
.IsOpened() ) {
908 wxLogError(_("can't open user configuration file."));
912 // write all strings to file
913 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
914 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
915 wxLogError(_("can't write user configuration file."));
920 bool ret
= file
.Commit();
922 #if defined(__WXMAC__)
927 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
929 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
931 finfo
.fdType
= 'TEXT' ;
932 finfo
.fdCreator
= 'ttxt' ;
933 FSpSetFInfo( &spec
, &finfo
) ;
939 // restore the old umask if we changed it
942 (void)umask(umaskOld
);
949 // ----------------------------------------------------------------------------
950 // renaming groups/entries
951 // ----------------------------------------------------------------------------
953 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
954 const wxString
& newName
)
956 // check that the entry exists
957 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
961 // check that the new entry doesn't already exist
962 if ( m_pCurrentGroup
->FindEntry(newName
) )
965 // delete the old entry, create the new one
966 wxString value
= oldEntry
->Value();
967 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
970 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
971 newEntry
->SetValue(value
);
976 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
977 const wxString
& newName
)
979 // check that the group exists
980 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
984 // check that the new group doesn't already exist
985 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
988 group
->Rename(newName
);
993 // ----------------------------------------------------------------------------
994 // delete groups/entries
995 // ----------------------------------------------------------------------------
997 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
999 wxConfigPathChanger
path(this, key
);
1001 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1004 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1005 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1006 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1007 SetPath(wxT("..")); // changes m_pCurrentGroup!
1008 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1010 //else: never delete the root group
1016 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1018 wxConfigPathChanger
path(this, key
);
1020 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1023 bool wxFileConfig::DeleteAll()
1027 if ( wxRemove(m_strLocalFile
) == -1 )
1028 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1030 m_strLocalFile
= m_strGlobalFile
= wxT("");
1036 // ----------------------------------------------------------------------------
1037 // linked list functions
1038 // ----------------------------------------------------------------------------
1040 // append a new line to the end of the list
1041 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1043 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1045 if ( m_linesTail
== NULL
) {
1047 m_linesHead
= pLine
;
1051 m_linesTail
->SetNext(pLine
);
1052 pLine
->SetPrev(m_linesTail
);
1055 m_linesTail
= pLine
;
1059 // insert a new line after the given one or in the very beginning if !pLine
1060 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1061 wxFileConfigLineList
*pLine
)
1063 if ( pLine
== m_linesTail
)
1064 return LineListAppend(str
);
1066 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1067 if ( pLine
== NULL
) {
1068 // prepend to the list
1069 pNewLine
->SetNext(m_linesHead
);
1070 m_linesHead
->SetPrev(pNewLine
);
1071 m_linesHead
= pNewLine
;
1074 // insert before pLine
1075 wxFileConfigLineList
*pNext
= pLine
->Next();
1076 pNewLine
->SetNext(pNext
);
1077 pNewLine
->SetPrev(pLine
);
1078 pNext
->SetPrev(pNewLine
);
1079 pLine
->SetNext(pNewLine
);
1085 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1087 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1088 *pNext
= pLine
->Next();
1091 if ( pPrev
== NULL
)
1092 m_linesHead
= pNext
;
1094 pPrev
->SetNext(pNext
);
1097 if ( pNext
== NULL
)
1098 m_linesTail
= pPrev
;
1100 pNext
->SetPrev(pPrev
);
1105 bool wxFileConfig::LineListIsEmpty()
1107 return m_linesHead
== NULL
;
1110 // ============================================================================
1111 // wxFileConfig::wxFileConfigGroup
1112 // ============================================================================
1114 // ----------------------------------------------------------------------------
1116 // ----------------------------------------------------------------------------
1119 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1120 const wxString
& strName
,
1121 wxFileConfig
*pConfig
)
1122 : m_aEntries(CompareEntries
),
1123 m_aSubgroups(CompareGroups
),
1126 m_pConfig
= pConfig
;
1127 m_pParent
= pParent
;
1131 m_pLastEntry
= NULL
;
1132 m_pLastGroup
= NULL
;
1135 // dtor deletes all children
1136 wxFileConfigGroup::~wxFileConfigGroup()
1139 size_t n
, nCount
= m_aEntries
.Count();
1140 for ( n
= 0; n
< nCount
; n
++ )
1141 delete m_aEntries
[n
];
1144 nCount
= m_aSubgroups
.Count();
1145 for ( n
= 0; n
< nCount
; n
++ )
1146 delete m_aSubgroups
[n
];
1149 // ----------------------------------------------------------------------------
1151 // ----------------------------------------------------------------------------
1153 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1155 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
1161 This is a bit complicated, so let me explain it in details. All lines that
1162 were read from the local file (the only one we will ever modify) are stored
1163 in a (doubly) linked list. Our problem is to know at which position in this
1164 list should we insert the new entries/subgroups. To solve it we keep three
1165 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1167 m_pLine points to the line containing "[group_name]"
1168 m_pLastEntry points to the last entry of this group in the local file.
1169 m_pLastGroup subgroup
1171 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1172 from the local file, the corresponding variable is set. However, if the group
1173 was read from the global file and then modified or created by the application
1174 these variables are still NULL and we need to create the corresponding lines.
1175 See the following functions (and comments preceding them) for the details of
1178 Also, when our last entry/group are deleted we need to find the new last
1179 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1180 of lines until it either founds an entry/subgroup (and this is the new last
1181 element) or the m_pLine of the group, in which case there are no more entries
1182 (or subgroups) left and m_pLast<element> becomes NULL.
1184 NB: This last problem could be avoided for entries if we added new entries
1185 immediately after m_pLine, but in this case the entries would appear
1186 backwards in the config file (OTOH, it's not that important) and as we
1187 would still need to do it for the subgroups the code wouldn't have been
1188 significantly less complicated.
1191 // Return the line which contains "[our name]". If we're still not in the list,
1192 // add our line to it immediately after the last line of our parent group if we
1193 // have it or in the very beginning if we're the root group.
1194 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1196 if ( m_pLine
== NULL
) {
1197 wxFileConfigGroup
*pParent
= Parent();
1199 // this group wasn't present in local config file, add it now
1200 if ( pParent
!= NULL
) {
1201 wxString strFullName
;
1202 strFullName
<< wxT("[")
1204 << FilterOutEntryName(GetFullName().c_str() + 1)
1206 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1207 pParent
->GetLastGroupLine());
1208 pParent
->SetLastGroup(this); // we're surely after all the others
1211 // we return NULL, so that LineListInsert() will insert us in the
1219 // Return the last line belonging to the subgroups of this group (after which
1220 // we can add a new subgroup), if we don't have any subgroups or entries our
1221 // last line is the group line (m_pLine) itself.
1222 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1224 // if we have any subgroups, our last line is the last line of the last
1226 if ( m_pLastGroup
!= NULL
) {
1227 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1229 wxASSERT( pLine
!= NULL
); // last group must have !NULL associated line
1233 // no subgroups, so the last line is the line of thelast entry (if any)
1234 return GetLastEntryLine();
1237 // return the last line belonging to the entries of this group (after which
1238 // we can add a new entry), if we don't have any entries we will add the new
1239 // one immediately after the group line itself.
1240 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1242 if ( m_pLastEntry
!= NULL
) {
1243 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1245 wxASSERT( pLine
!= NULL
); // last entry must have !NULL associated line
1249 // no entries: insert after the group header
1250 return GetGroupLine();
1253 // ----------------------------------------------------------------------------
1255 // ----------------------------------------------------------------------------
1257 void wxFileConfigGroup::Rename(const wxString
& newName
)
1259 m_strName
= newName
;
1261 wxFileConfigLineList
*line
= GetGroupLine();
1262 wxString strFullName
;
1263 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1264 line
->SetText(strFullName
);
1269 wxString
wxFileConfigGroup::GetFullName() const
1272 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1277 // ----------------------------------------------------------------------------
1279 // ----------------------------------------------------------------------------
1281 // use binary search because the array is sorted
1283 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1287 hi
= m_aEntries
.Count();
1289 wxFileConfigEntry
*pEntry
;
1293 pEntry
= m_aEntries
[i
];
1295 #if wxCONFIG_CASE_SENSITIVE
1296 res
= wxStrcmp(pEntry
->Name(), szName
);
1298 res
= wxStricmp(pEntry
->Name(), szName
);
1313 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1317 hi
= m_aSubgroups
.Count();
1319 wxFileConfigGroup
*pGroup
;
1323 pGroup
= m_aSubgroups
[i
];
1325 #if wxCONFIG_CASE_SENSITIVE
1326 res
= wxStrcmp(pGroup
->Name(), szName
);
1328 res
= wxStricmp(pGroup
->Name(), szName
);
1342 // ----------------------------------------------------------------------------
1343 // create a new item
1344 // ----------------------------------------------------------------------------
1346 // create a new entry and add it to the current group
1348 wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1350 wxASSERT( FindEntry(strName
) == NULL
);
1352 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1353 m_aEntries
.Add(pEntry
);
1358 // create a new group and add it to the current group
1360 wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1362 wxASSERT( FindSubgroup(strName
) == NULL
);
1364 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1365 m_aSubgroups
.Add(pGroup
);
1370 // ----------------------------------------------------------------------------
1372 // ----------------------------------------------------------------------------
1375 The delete operations are _very_ slow if we delete the last item of this
1376 group (see comments before GetXXXLineXXX functions for more details),
1377 so it's much better to start with the first entry/group if we want to
1378 delete several of them.
1381 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1383 return DeleteSubgroup(FindSubgroup(szName
));
1386 // doesn't delete the subgroup itself, but does remove references to it from
1387 // all other data structures (and normally the returned pointer should be
1388 // deleted a.s.a.p. because there is nothing much to be done with it anyhow)
1389 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1391 wxCHECK( pGroup
!= NULL
, FALSE
); // deleting non existing group?
1393 // delete all entries
1394 size_t nCount
= pGroup
->m_aEntries
.Count();
1395 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ ) {
1396 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1397 if ( pLine
!= NULL
)
1398 m_pConfig
->LineListRemove(pLine
);
1401 // and subgroups of this sungroup
1402 nCount
= pGroup
->m_aSubgroups
.Count();
1403 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ ) {
1404 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1407 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1408 if ( pLine
!= NULL
) {
1409 // notice that we may do this test inside the previous "if" because the
1410 // last entry's line is surely !NULL
1411 if ( pGroup
== m_pLastGroup
) {
1412 // our last entry is being deleted - find the last one which stays
1413 wxASSERT( m_pLine
!= NULL
); // we have a subgroup with !NULL pLine...
1415 // go back until we find a subgroup or reach the group's line
1416 wxFileConfigGroup
*pNewLast
= NULL
;
1417 size_t n
, nSubgroups
= m_aSubgroups
.Count();
1418 wxFileConfigLineList
*pl
;
1419 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1420 // is it our subgroup?
1421 for ( n
= 0; (pNewLast
== NULL
) && (n
< nSubgroups
); n
++ ) {
1422 // do _not_ call GetGroupLine! we don't want to add it to the local
1423 // file if it's not already there
1424 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1425 pNewLast
= m_aSubgroups
[n
];
1428 if ( pNewLast
!= NULL
) // found?
1432 if ( pl
== m_pLine
) {
1433 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1435 // we've reached the group line without finding any subgroups
1436 m_pLastGroup
= NULL
;
1439 m_pLastGroup
= pNewLast
;
1442 m_pConfig
->LineListRemove(pLine
);
1447 m_aSubgroups
.Remove(pGroup
);
1453 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1455 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1456 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1458 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1459 if ( pLine
!= NULL
) {
1460 // notice that we may do this test inside the previous "if" because the
1461 // last entry's line is surely !NULL
1462 if ( pEntry
== m_pLastEntry
) {
1463 // our last entry is being deleted - find the last one which stays
1464 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1466 // go back until we find another entry or reach the group's line
1467 wxFileConfigEntry
*pNewLast
= NULL
;
1468 size_t n
, nEntries
= m_aEntries
.Count();
1469 wxFileConfigLineList
*pl
;
1470 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1471 // is it our subgroup?
1472 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1473 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1474 pNewLast
= m_aEntries
[n
];
1477 if ( pNewLast
!= NULL
) // found?
1481 if ( pl
== m_pLine
) {
1482 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1484 // we've reached the group line without finding any subgroups
1485 m_pLastEntry
= NULL
;
1488 m_pLastEntry
= pNewLast
;
1491 m_pConfig
->LineListRemove(pLine
);
1494 // we must be written back for the changes to be saved
1497 m_aEntries
.Remove(pEntry
);
1503 // ----------------------------------------------------------------------------
1505 // ----------------------------------------------------------------------------
1506 void wxFileConfigGroup::SetDirty()
1509 if ( Parent() != NULL
) // propagate upwards
1510 Parent()->SetDirty();
1513 // ============================================================================
1514 // wxFileConfig::wxFileConfigEntry
1515 // ============================================================================
1517 // ----------------------------------------------------------------------------
1519 // ----------------------------------------------------------------------------
1520 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1521 const wxString
& strName
,
1523 : m_strName(strName
)
1525 wxASSERT( !strName
.IsEmpty() );
1527 m_pParent
= pParent
;
1532 m_bHasValue
= FALSE
;
1534 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1536 m_strName
.erase(0, 1); // remove first character
1539 // ----------------------------------------------------------------------------
1541 // ----------------------------------------------------------------------------
1543 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1545 if ( m_pLine
!= NULL
) {
1546 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1547 Name().c_str(), m_pParent
->GetFullName().c_str());
1551 Group()->SetLastEntry(this);
1554 // second parameter is FALSE if we read the value from file and prevents the
1555 // entry from being marked as 'dirty'
1556 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1558 if ( bUser
&& IsImmutable() ) {
1559 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
1564 // do nothing if it's the same value: but don't test for it if m_bHasValue
1565 // hadn't been set yet or we'd never write empty values to the file
1566 if ( m_bHasValue
&& strValue
== m_strValue
)
1570 m_strValue
= strValue
;
1573 wxString strVal
= FilterOutValue(strValue
);
1575 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strVal
;
1577 if ( m_pLine
!= NULL
) {
1578 // entry was read from the local config file, just modify the line
1579 m_pLine
->SetText(strLine
);
1582 // add a new line to the file
1583 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1585 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1586 Group()->GetLastEntryLine());
1587 Group()->SetLastEntry(this);
1594 void wxFileConfigEntry::SetDirty()
1597 Group()->SetDirty();
1600 // ============================================================================
1602 // ============================================================================
1604 // ----------------------------------------------------------------------------
1605 // compare functions for array sorting
1606 // ----------------------------------------------------------------------------
1608 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1610 #if wxCONFIG_CASE_SENSITIVE
1611 return wxStrcmp(p1
->Name(), p2
->Name());
1613 return wxStricmp(p1
->Name(), p2
->Name());
1617 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1619 #if wxCONFIG_CASE_SENSITIVE
1620 return wxStrcmp(p1
->Name(), p2
->Name());
1622 return wxStricmp(p1
->Name(), p2
->Name());
1626 // ----------------------------------------------------------------------------
1628 // ----------------------------------------------------------------------------
1630 // undo FilterOutValue
1631 static wxString
FilterInValue(const wxString
& str
)
1634 strResult
.Alloc(str
.Len());
1636 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1638 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1639 if ( str
[n
] == wxT('\\') ) {
1640 switch ( str
[++n
] ) {
1642 strResult
+= wxT('\n');
1646 strResult
+= wxT('\r');
1650 strResult
+= wxT('\t');
1654 strResult
+= wxT('\\');
1658 strResult
+= wxT('"');
1663 if ( str
[n
] != wxT('"') || !bQuoted
)
1664 strResult
+= str
[n
];
1665 else if ( n
!= str
.Len() - 1 ) {
1666 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1669 //else: it's the last quote of a quoted string, ok
1676 // quote the string before writing it to file
1677 static wxString
FilterOutValue(const wxString
& str
)
1683 strResult
.Alloc(str
.Len());
1685 // quoting is necessary to preserve spaces in the beginning of the string
1686 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1689 strResult
+= wxT('"');
1692 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1715 //else: fall through
1718 strResult
+= str
[n
];
1719 continue; // nothing special to do
1722 // we get here only for special characters
1723 strResult
<< wxT('\\') << c
;
1727 strResult
+= wxT('"');
1732 // undo FilterOutEntryName
1733 static wxString
FilterInEntryName(const wxString
& str
)
1736 strResult
.Alloc(str
.Len());
1738 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1739 if ( *pc
== wxT('\\') )
1748 // sanitize entry or group name: insert '\\' before any special characters
1749 static wxString
FilterOutEntryName(const wxString
& str
)
1752 strResult
.Alloc(str
.Len());
1754 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1757 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1758 // which will probably never have special meaning
1759 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1760 // should *not* be quoted
1761 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1762 strResult
+= wxT('\\');
1770 // we can't put ?: in the ctor initializer list because it confuses some
1771 // broken compilers (Borland C++)
1772 static wxString
GetAppName(const wxString
& appName
)
1774 if ( !appName
&& wxTheApp
)
1775 return wxTheApp
->GetAppName();
1780 #endif // wxUSE_CONFIG