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 return str
.ToLong(pl
) ;
850 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
852 wxConfigPathChanger
path(this, key
);
854 wxString strName
= path
.Name();
855 if ( strName
.IsEmpty() ) {
856 // setting the value of a group is an error
857 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
859 // ... except if it's empty in which case it's a way to force it's creation
860 m_pCurrentGroup
->SetDirty();
862 // this will add a line for this group if it didn't have it before
863 (void)m_pCurrentGroup
->GetGroupLine();
868 // check that the name is reasonable
869 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
) {
870 wxLogError(_("Config entry name cannot start with '%c'."),
871 wxCONFIG_IMMUTABLE_PREFIX
);
875 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
876 if ( pEntry
== NULL
)
877 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
879 pEntry
->SetValue(szValue
);
885 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
887 return Write(key
, wxString::Format(_T("%ld"), lValue
));
890 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
892 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
896 // set the umask if needed
900 umaskOld
= umask((mode_t
)m_umask
);
904 wxTempFile
file(m_strLocalFile
);
906 if ( !file
.IsOpened() ) {
907 wxLogError(_("can't open user configuration file."));
911 // write all strings to file
912 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
913 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
914 wxLogError(_("can't write user configuration file."));
919 bool ret
= file
.Commit();
921 #if defined(__WXMAC__)
926 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
928 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
930 finfo
.fdType
= 'TEXT' ;
931 finfo
.fdCreator
= 'ttxt' ;
932 FSpSetFInfo( &spec
, &finfo
) ;
938 // restore the old umask if we changed it
941 (void)umask(umaskOld
);
948 // ----------------------------------------------------------------------------
949 // renaming groups/entries
950 // ----------------------------------------------------------------------------
952 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
953 const wxString
& newName
)
955 // check that the entry exists
956 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
960 // check that the new entry doesn't already exist
961 if ( m_pCurrentGroup
->FindEntry(newName
) )
964 // delete the old entry, create the new one
965 wxString value
= oldEntry
->Value();
966 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
969 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
970 newEntry
->SetValue(value
);
975 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
976 const wxString
& newName
)
978 // check that the group exists
979 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
983 // check that the new group doesn't already exist
984 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
987 group
->Rename(newName
);
992 // ----------------------------------------------------------------------------
993 // delete groups/entries
994 // ----------------------------------------------------------------------------
996 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
998 wxConfigPathChanger
path(this, key
);
1000 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1003 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1004 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1005 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1006 SetPath(wxT("..")); // changes m_pCurrentGroup!
1007 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1009 //else: never delete the root group
1015 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1017 wxConfigPathChanger
path(this, key
);
1019 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1022 bool wxFileConfig::DeleteAll()
1026 if ( wxRemove(m_strLocalFile
) == -1 )
1027 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1029 m_strLocalFile
= m_strGlobalFile
= wxT("");
1035 // ----------------------------------------------------------------------------
1036 // linked list functions
1037 // ----------------------------------------------------------------------------
1039 // append a new line to the end of the list
1040 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1042 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1044 if ( m_linesTail
== NULL
) {
1046 m_linesHead
= pLine
;
1050 m_linesTail
->SetNext(pLine
);
1051 pLine
->SetPrev(m_linesTail
);
1054 m_linesTail
= pLine
;
1058 // insert a new line after the given one or in the very beginning if !pLine
1059 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1060 wxFileConfigLineList
*pLine
)
1062 if ( pLine
== m_linesTail
)
1063 return LineListAppend(str
);
1065 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1066 if ( pLine
== NULL
) {
1067 // prepend to the list
1068 pNewLine
->SetNext(m_linesHead
);
1069 m_linesHead
->SetPrev(pNewLine
);
1070 m_linesHead
= pNewLine
;
1073 // insert before pLine
1074 wxFileConfigLineList
*pNext
= pLine
->Next();
1075 pNewLine
->SetNext(pNext
);
1076 pNewLine
->SetPrev(pLine
);
1077 pNext
->SetPrev(pNewLine
);
1078 pLine
->SetNext(pNewLine
);
1084 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1086 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1087 *pNext
= pLine
->Next();
1090 if ( pPrev
== NULL
)
1091 m_linesHead
= pNext
;
1093 pPrev
->SetNext(pNext
);
1096 if ( pNext
== NULL
)
1097 m_linesTail
= pPrev
;
1099 pNext
->SetPrev(pPrev
);
1104 bool wxFileConfig::LineListIsEmpty()
1106 return m_linesHead
== NULL
;
1109 // ============================================================================
1110 // wxFileConfig::wxFileConfigGroup
1111 // ============================================================================
1113 // ----------------------------------------------------------------------------
1115 // ----------------------------------------------------------------------------
1118 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1119 const wxString
& strName
,
1120 wxFileConfig
*pConfig
)
1121 : m_aEntries(CompareEntries
),
1122 m_aSubgroups(CompareGroups
),
1125 m_pConfig
= pConfig
;
1126 m_pParent
= pParent
;
1130 m_pLastEntry
= NULL
;
1131 m_pLastGroup
= NULL
;
1134 // dtor deletes all children
1135 wxFileConfigGroup::~wxFileConfigGroup()
1138 size_t n
, nCount
= m_aEntries
.Count();
1139 for ( n
= 0; n
< nCount
; n
++ )
1140 delete m_aEntries
[n
];
1143 nCount
= m_aSubgroups
.Count();
1144 for ( n
= 0; n
< nCount
; n
++ )
1145 delete m_aSubgroups
[n
];
1148 // ----------------------------------------------------------------------------
1150 // ----------------------------------------------------------------------------
1152 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1154 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
1160 This is a bit complicated, so let me explain it in details. All lines that
1161 were read from the local file (the only one we will ever modify) are stored
1162 in a (doubly) linked list. Our problem is to know at which position in this
1163 list should we insert the new entries/subgroups. To solve it we keep three
1164 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1166 m_pLine points to the line containing "[group_name]"
1167 m_pLastEntry points to the last entry of this group in the local file.
1168 m_pLastGroup subgroup
1170 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1171 from the local file, the corresponding variable is set. However, if the group
1172 was read from the global file and then modified or created by the application
1173 these variables are still NULL and we need to create the corresponding lines.
1174 See the following functions (and comments preceding them) for the details of
1177 Also, when our last entry/group are deleted we need to find the new last
1178 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1179 of lines until it either founds an entry/subgroup (and this is the new last
1180 element) or the m_pLine of the group, in which case there are no more entries
1181 (or subgroups) left and m_pLast<element> becomes NULL.
1183 NB: This last problem could be avoided for entries if we added new entries
1184 immediately after m_pLine, but in this case the entries would appear
1185 backwards in the config file (OTOH, it's not that important) and as we
1186 would still need to do it for the subgroups the code wouldn't have been
1187 significantly less complicated.
1190 // Return the line which contains "[our name]". If we're still not in the list,
1191 // add our line to it immediately after the last line of our parent group if we
1192 // have it or in the very beginning if we're the root group.
1193 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1195 if ( m_pLine
== NULL
) {
1196 wxFileConfigGroup
*pParent
= Parent();
1198 // this group wasn't present in local config file, add it now
1199 if ( pParent
!= NULL
) {
1200 wxString strFullName
;
1201 strFullName
<< wxT("[")
1203 << FilterOutEntryName(GetFullName().c_str() + 1)
1205 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1206 pParent
->GetLastGroupLine());
1207 pParent
->SetLastGroup(this); // we're surely after all the others
1210 // we return NULL, so that LineListInsert() will insert us in the
1218 // Return the last line belonging to the subgroups of this group (after which
1219 // we can add a new subgroup), if we don't have any subgroups or entries our
1220 // last line is the group line (m_pLine) itself.
1221 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1223 // if we have any subgroups, our last line is the last line of the last
1225 if ( m_pLastGroup
!= NULL
) {
1226 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1228 wxASSERT( pLine
!= NULL
); // last group must have !NULL associated line
1232 // no subgroups, so the last line is the line of thelast entry (if any)
1233 return GetLastEntryLine();
1236 // return the last line belonging to the entries of this group (after which
1237 // we can add a new entry), if we don't have any entries we will add the new
1238 // one immediately after the group line itself.
1239 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1241 if ( m_pLastEntry
!= NULL
) {
1242 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1244 wxASSERT( pLine
!= NULL
); // last entry must have !NULL associated line
1248 // no entries: insert after the group header
1249 return GetGroupLine();
1252 // ----------------------------------------------------------------------------
1254 // ----------------------------------------------------------------------------
1256 void wxFileConfigGroup::Rename(const wxString
& newName
)
1258 m_strName
= newName
;
1260 wxFileConfigLineList
*line
= GetGroupLine();
1261 wxString strFullName
;
1262 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1263 line
->SetText(strFullName
);
1268 wxString
wxFileConfigGroup::GetFullName() const
1271 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1276 // ----------------------------------------------------------------------------
1278 // ----------------------------------------------------------------------------
1280 // use binary search because the array is sorted
1282 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1286 hi
= m_aEntries
.Count();
1288 wxFileConfigEntry
*pEntry
;
1292 pEntry
= m_aEntries
[i
];
1294 #if wxCONFIG_CASE_SENSITIVE
1295 res
= wxStrcmp(pEntry
->Name(), szName
);
1297 res
= wxStricmp(pEntry
->Name(), szName
);
1312 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1316 hi
= m_aSubgroups
.Count();
1318 wxFileConfigGroup
*pGroup
;
1322 pGroup
= m_aSubgroups
[i
];
1324 #if wxCONFIG_CASE_SENSITIVE
1325 res
= wxStrcmp(pGroup
->Name(), szName
);
1327 res
= wxStricmp(pGroup
->Name(), szName
);
1341 // ----------------------------------------------------------------------------
1342 // create a new item
1343 // ----------------------------------------------------------------------------
1345 // create a new entry and add it to the current group
1347 wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1349 wxASSERT( FindEntry(strName
) == NULL
);
1351 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1352 m_aEntries
.Add(pEntry
);
1357 // create a new group and add it to the current group
1359 wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1361 wxASSERT( FindSubgroup(strName
) == NULL
);
1363 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1364 m_aSubgroups
.Add(pGroup
);
1369 // ----------------------------------------------------------------------------
1371 // ----------------------------------------------------------------------------
1374 The delete operations are _very_ slow if we delete the last item of this
1375 group (see comments before GetXXXLineXXX functions for more details),
1376 so it's much better to start with the first entry/group if we want to
1377 delete several of them.
1380 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1382 return DeleteSubgroup(FindSubgroup(szName
));
1385 // doesn't delete the subgroup itself, but does remove references to it from
1386 // all other data structures (and normally the returned pointer should be
1387 // deleted a.s.a.p. because there is nothing much to be done with it anyhow)
1388 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1390 wxCHECK( pGroup
!= NULL
, FALSE
); // deleting non existing group?
1392 // delete all entries
1393 size_t nCount
= pGroup
->m_aEntries
.Count();
1394 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ ) {
1395 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1396 if ( pLine
!= NULL
)
1397 m_pConfig
->LineListRemove(pLine
);
1400 // and subgroups of this sungroup
1401 nCount
= pGroup
->m_aSubgroups
.Count();
1402 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ ) {
1403 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1406 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1407 if ( pLine
!= NULL
) {
1408 // notice that we may do this test inside the previous "if" because the
1409 // last entry's line is surely !NULL
1410 if ( pGroup
== m_pLastGroup
) {
1411 // our last entry is being deleted - find the last one which stays
1412 wxASSERT( m_pLine
!= NULL
); // we have a subgroup with !NULL pLine...
1414 // go back until we find a subgroup or reach the group's line
1415 wxFileConfigGroup
*pNewLast
= NULL
;
1416 size_t n
, nSubgroups
= m_aSubgroups
.Count();
1417 wxFileConfigLineList
*pl
;
1418 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1419 // is it our subgroup?
1420 for ( n
= 0; (pNewLast
== NULL
) && (n
< nSubgroups
); n
++ ) {
1421 // do _not_ call GetGroupLine! we don't want to add it to the local
1422 // file if it's not already there
1423 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1424 pNewLast
= m_aSubgroups
[n
];
1427 if ( pNewLast
!= NULL
) // found?
1431 if ( pl
== m_pLine
) {
1432 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1434 // we've reached the group line without finding any subgroups
1435 m_pLastGroup
= NULL
;
1438 m_pLastGroup
= pNewLast
;
1441 m_pConfig
->LineListRemove(pLine
);
1446 m_aSubgroups
.Remove(pGroup
);
1452 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1454 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1455 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1457 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1458 if ( pLine
!= NULL
) {
1459 // notice that we may do this test inside the previous "if" because the
1460 // last entry's line is surely !NULL
1461 if ( pEntry
== m_pLastEntry
) {
1462 // our last entry is being deleted - find the last one which stays
1463 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1465 // go back until we find another entry or reach the group's line
1466 wxFileConfigEntry
*pNewLast
= NULL
;
1467 size_t n
, nEntries
= m_aEntries
.Count();
1468 wxFileConfigLineList
*pl
;
1469 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1470 // is it our subgroup?
1471 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1472 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1473 pNewLast
= m_aEntries
[n
];
1476 if ( pNewLast
!= NULL
) // found?
1480 if ( pl
== m_pLine
) {
1481 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1483 // we've reached the group line without finding any subgroups
1484 m_pLastEntry
= NULL
;
1487 m_pLastEntry
= pNewLast
;
1490 m_pConfig
->LineListRemove(pLine
);
1493 // we must be written back for the changes to be saved
1496 m_aEntries
.Remove(pEntry
);
1502 // ----------------------------------------------------------------------------
1504 // ----------------------------------------------------------------------------
1505 void wxFileConfigGroup::SetDirty()
1508 if ( Parent() != NULL
) // propagate upwards
1509 Parent()->SetDirty();
1512 // ============================================================================
1513 // wxFileConfig::wxFileConfigEntry
1514 // ============================================================================
1516 // ----------------------------------------------------------------------------
1518 // ----------------------------------------------------------------------------
1519 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1520 const wxString
& strName
,
1522 : m_strName(strName
)
1524 wxASSERT( !strName
.IsEmpty() );
1526 m_pParent
= pParent
;
1531 m_bHasValue
= FALSE
;
1533 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1535 m_strName
.erase(0, 1); // remove first character
1538 // ----------------------------------------------------------------------------
1540 // ----------------------------------------------------------------------------
1542 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1544 if ( m_pLine
!= NULL
) {
1545 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1546 Name().c_str(), m_pParent
->GetFullName().c_str());
1550 Group()->SetLastEntry(this);
1553 // second parameter is FALSE if we read the value from file and prevents the
1554 // entry from being marked as 'dirty'
1555 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1557 if ( bUser
&& IsImmutable() ) {
1558 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
1563 // do nothing if it's the same value: but don't test for it if m_bHasValue
1564 // hadn't been set yet or we'd never write empty values to the file
1565 if ( m_bHasValue
&& strValue
== m_strValue
)
1569 m_strValue
= strValue
;
1572 wxString strVal
= FilterOutValue(strValue
);
1574 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strVal
;
1576 if ( m_pLine
!= NULL
) {
1577 // entry was read from the local config file, just modify the line
1578 m_pLine
->SetText(strLine
);
1581 // add a new line to the file
1582 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1584 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1585 Group()->GetLastEntryLine());
1586 Group()->SetLastEntry(this);
1593 void wxFileConfigEntry::SetDirty()
1596 Group()->SetDirty();
1599 // ============================================================================
1601 // ============================================================================
1603 // ----------------------------------------------------------------------------
1604 // compare functions for array sorting
1605 // ----------------------------------------------------------------------------
1607 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1609 #if wxCONFIG_CASE_SENSITIVE
1610 return wxStrcmp(p1
->Name(), p2
->Name());
1612 return wxStricmp(p1
->Name(), p2
->Name());
1616 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1618 #if wxCONFIG_CASE_SENSITIVE
1619 return wxStrcmp(p1
->Name(), p2
->Name());
1621 return wxStricmp(p1
->Name(), p2
->Name());
1625 // ----------------------------------------------------------------------------
1627 // ----------------------------------------------------------------------------
1629 // undo FilterOutValue
1630 static wxString
FilterInValue(const wxString
& str
)
1633 strResult
.Alloc(str
.Len());
1635 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1637 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1638 if ( str
[n
] == wxT('\\') ) {
1639 switch ( str
[++n
] ) {
1641 strResult
+= wxT('\n');
1645 strResult
+= wxT('\r');
1649 strResult
+= wxT('\t');
1653 strResult
+= wxT('\\');
1657 strResult
+= wxT('"');
1662 if ( str
[n
] != wxT('"') || !bQuoted
)
1663 strResult
+= str
[n
];
1664 else if ( n
!= str
.Len() - 1 ) {
1665 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1668 //else: it's the last quote of a quoted string, ok
1675 // quote the string before writing it to file
1676 static wxString
FilterOutValue(const wxString
& str
)
1682 strResult
.Alloc(str
.Len());
1684 // quoting is necessary to preserve spaces in the beginning of the string
1685 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1688 strResult
+= wxT('"');
1691 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1714 //else: fall through
1717 strResult
+= str
[n
];
1718 continue; // nothing special to do
1721 // we get here only for special characters
1722 strResult
<< wxT('\\') << c
;
1726 strResult
+= wxT('"');
1731 // undo FilterOutEntryName
1732 static wxString
FilterInEntryName(const wxString
& str
)
1735 strResult
.Alloc(str
.Len());
1737 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1738 if ( *pc
== wxT('\\') )
1747 // sanitize entry or group name: insert '\\' before any special characters
1748 static wxString
FilterOutEntryName(const wxString
& str
)
1751 strResult
.Alloc(str
.Len());
1753 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1756 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1757 // which will probably never have special meaning
1758 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1759 // should *not* be quoted
1760 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1761 strResult
+= wxT('\\');
1769 // we can't put ?: in the ctor initializer list because it confuses some
1770 // broken compilers (Borland C++)
1771 static wxString
GetAppName(const wxString
& appName
)
1773 if ( !appName
&& wxTheApp
)
1774 return wxTheApp
->GetAppName();
1779 #endif // wxUSE_CONFIG