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__) || defined(__DOS__)
301 // no local dir concept on Mac OS 9 or MS-DOS
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__ )
352 #if defined(__WINDOWS__) || defined(__DOS__)
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