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::DoReadString(const wxString
& key
, wxString
* pStr
) const
824 wxConfigPathChanger
path(this, key
);
826 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
827 if (pEntry
== NULL
) {
831 *pStr
= pEntry
->Value();
836 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
839 if ( !Read(key
, & str
) )
844 return str
.ToLong(pl
);
847 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
849 wxConfigPathChanger
path(this, key
);
851 wxString strName
= path
.Name();
852 if ( strName
.IsEmpty() ) {
853 // setting the value of a group is an error
854 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
856 // ... except if it's empty in which case it's a way to force it's creation
857 m_pCurrentGroup
->SetDirty();
859 // this will add a line for this group if it didn't have it before
860 (void)m_pCurrentGroup
->GetGroupLine();
865 // check that the name is reasonable
866 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
) {
867 wxLogError(_("Config entry name cannot start with '%c'."),
868 wxCONFIG_IMMUTABLE_PREFIX
);
872 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
873 if ( pEntry
== NULL
)
874 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
876 pEntry
->SetValue(szValue
);
882 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
884 return Write(key
, wxString::Format(_T("%ld"), lValue
));
887 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
889 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
893 // set the umask if needed
897 umaskOld
= umask((mode_t
)m_umask
);
901 wxTempFile
file(m_strLocalFile
);
903 if ( !file
.IsOpened() ) {
904 wxLogError(_("can't open user configuration file."));
908 // write all strings to file
909 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
910 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
911 wxLogError(_("can't write user configuration file."));
916 bool ret
= file
.Commit();
918 #if defined(__WXMAC__)
923 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
925 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
927 finfo
.fdType
= 'TEXT' ;
928 finfo
.fdCreator
= 'ttxt' ;
929 FSpSetFInfo( &spec
, &finfo
) ;
935 // restore the old umask if we changed it
938 (void)umask(umaskOld
);
945 // ----------------------------------------------------------------------------
946 // renaming groups/entries
947 // ----------------------------------------------------------------------------
949 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
950 const wxString
& newName
)
952 // check that the entry exists
953 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
957 // check that the new entry doesn't already exist
958 if ( m_pCurrentGroup
->FindEntry(newName
) )
961 // delete the old entry, create the new one
962 wxString value
= oldEntry
->Value();
963 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
966 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
967 newEntry
->SetValue(value
);
972 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
973 const wxString
& newName
)
975 // check that the group exists
976 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
980 // check that the new group doesn't already exist
981 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
984 group
->Rename(newName
);
989 // ----------------------------------------------------------------------------
990 // delete groups/entries
991 // ----------------------------------------------------------------------------
993 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
995 wxConfigPathChanger
path(this, key
);
997 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1000 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1001 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1002 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1003 SetPath(wxT("..")); // changes m_pCurrentGroup!
1004 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1006 //else: never delete the root group
1012 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1014 wxConfigPathChanger
path(this, key
);
1016 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1019 bool wxFileConfig::DeleteAll()
1023 if ( wxRemove(m_strLocalFile
) == -1 )
1024 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1026 m_strLocalFile
= m_strGlobalFile
= wxT("");
1032 // ----------------------------------------------------------------------------
1033 // linked list functions
1034 // ----------------------------------------------------------------------------
1036 // append a new line to the end of the list
1037 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1039 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1041 if ( m_linesTail
== NULL
) {
1043 m_linesHead
= pLine
;
1047 m_linesTail
->SetNext(pLine
);
1048 pLine
->SetPrev(m_linesTail
);
1051 m_linesTail
= pLine
;
1055 // insert a new line after the given one or in the very beginning if !pLine
1056 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1057 wxFileConfigLineList
*pLine
)
1059 if ( pLine
== m_linesTail
)
1060 return LineListAppend(str
);
1062 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1063 if ( pLine
== NULL
) {
1064 // prepend to the list
1065 pNewLine
->SetNext(m_linesHead
);
1066 m_linesHead
->SetPrev(pNewLine
);
1067 m_linesHead
= pNewLine
;
1070 // insert before pLine
1071 wxFileConfigLineList
*pNext
= pLine
->Next();
1072 pNewLine
->SetNext(pNext
);
1073 pNewLine
->SetPrev(pLine
);
1074 pNext
->SetPrev(pNewLine
);
1075 pLine
->SetNext(pNewLine
);
1081 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1083 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1084 *pNext
= pLine
->Next();
1087 if ( pPrev
== NULL
)
1088 m_linesHead
= pNext
;
1090 pPrev
->SetNext(pNext
);
1093 if ( pNext
== NULL
)
1094 m_linesTail
= pPrev
;
1096 pNext
->SetPrev(pPrev
);
1101 bool wxFileConfig::LineListIsEmpty()
1103 return m_linesHead
== NULL
;
1106 // ============================================================================
1107 // wxFileConfig::wxFileConfigGroup
1108 // ============================================================================
1110 // ----------------------------------------------------------------------------
1112 // ----------------------------------------------------------------------------
1115 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1116 const wxString
& strName
,
1117 wxFileConfig
*pConfig
)
1118 : m_aEntries(CompareEntries
),
1119 m_aSubgroups(CompareGroups
),
1122 m_pConfig
= pConfig
;
1123 m_pParent
= pParent
;
1127 m_pLastEntry
= NULL
;
1128 m_pLastGroup
= NULL
;
1131 // dtor deletes all children
1132 wxFileConfigGroup::~wxFileConfigGroup()
1135 size_t n
, nCount
= m_aEntries
.Count();
1136 for ( n
= 0; n
< nCount
; n
++ )
1137 delete m_aEntries
[n
];
1140 nCount
= m_aSubgroups
.Count();
1141 for ( n
= 0; n
< nCount
; n
++ )
1142 delete m_aSubgroups
[n
];
1145 // ----------------------------------------------------------------------------
1147 // ----------------------------------------------------------------------------
1149 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1151 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
1157 This is a bit complicated, so let me explain it in details. All lines that
1158 were read from the local file (the only one we will ever modify) are stored
1159 in a (doubly) linked list. Our problem is to know at which position in this
1160 list should we insert the new entries/subgroups. To solve it we keep three
1161 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1163 m_pLine points to the line containing "[group_name]"
1164 m_pLastEntry points to the last entry of this group in the local file.
1165 m_pLastGroup subgroup
1167 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1168 from the local file, the corresponding variable is set. However, if the group
1169 was read from the global file and then modified or created by the application
1170 these variables are still NULL and we need to create the corresponding lines.
1171 See the following functions (and comments preceding them) for the details of
1174 Also, when our last entry/group are deleted we need to find the new last
1175 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1176 of lines until it either founds an entry/subgroup (and this is the new last
1177 element) or the m_pLine of the group, in which case there are no more entries
1178 (or subgroups) left and m_pLast<element> becomes NULL.
1180 NB: This last problem could be avoided for entries if we added new entries
1181 immediately after m_pLine, but in this case the entries would appear
1182 backwards in the config file (OTOH, it's not that important) and as we
1183 would still need to do it for the subgroups the code wouldn't have been
1184 significantly less complicated.
1187 // Return the line which contains "[our name]". If we're still not in the list,
1188 // add our line to it immediately after the last line of our parent group if we
1189 // have it or in the very beginning if we're the root group.
1190 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1192 if ( m_pLine
== NULL
) {
1193 wxFileConfigGroup
*pParent
= Parent();
1195 // this group wasn't present in local config file, add it now
1196 if ( pParent
!= NULL
) {
1197 wxString strFullName
;
1198 strFullName
<< wxT("[")
1200 << FilterOutEntryName(GetFullName().c_str() + 1)
1202 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1203 pParent
->GetLastGroupLine());
1204 pParent
->SetLastGroup(this); // we're surely after all the others
1207 // we return NULL, so that LineListInsert() will insert us in the
1215 // Return the last line belonging to the subgroups of this group (after which
1216 // we can add a new subgroup), if we don't have any subgroups or entries our
1217 // last line is the group line (m_pLine) itself.
1218 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1220 // if we have any subgroups, our last line is the last line of the last
1222 if ( m_pLastGroup
!= NULL
) {
1223 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1225 wxASSERT( pLine
!= NULL
); // last group must have !NULL associated line
1229 // no subgroups, so the last line is the line of thelast entry (if any)
1230 return GetLastEntryLine();
1233 // return the last line belonging to the entries of this group (after which
1234 // we can add a new entry), if we don't have any entries we will add the new
1235 // one immediately after the group line itself.
1236 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1238 if ( m_pLastEntry
!= NULL
) {
1239 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1241 wxASSERT( pLine
!= NULL
); // last entry must have !NULL associated line
1245 // no entries: insert after the group header
1246 return GetGroupLine();
1249 // ----------------------------------------------------------------------------
1251 // ----------------------------------------------------------------------------
1253 void wxFileConfigGroup::Rename(const wxString
& newName
)
1255 m_strName
= newName
;
1257 wxFileConfigLineList
*line
= GetGroupLine();
1258 wxString strFullName
;
1259 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1260 line
->SetText(strFullName
);
1265 wxString
wxFileConfigGroup::GetFullName() const
1268 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1273 // ----------------------------------------------------------------------------
1275 // ----------------------------------------------------------------------------
1277 // use binary search because the array is sorted
1279 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1283 hi
= m_aEntries
.Count();
1285 wxFileConfigEntry
*pEntry
;
1289 pEntry
= m_aEntries
[i
];
1291 #if wxCONFIG_CASE_SENSITIVE
1292 res
= wxStrcmp(pEntry
->Name(), szName
);
1294 res
= wxStricmp(pEntry
->Name(), szName
);
1309 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1313 hi
= m_aSubgroups
.Count();
1315 wxFileConfigGroup
*pGroup
;
1319 pGroup
= m_aSubgroups
[i
];
1321 #if wxCONFIG_CASE_SENSITIVE
1322 res
= wxStrcmp(pGroup
->Name(), szName
);
1324 res
= wxStricmp(pGroup
->Name(), szName
);
1338 // ----------------------------------------------------------------------------
1339 // create a new item
1340 // ----------------------------------------------------------------------------
1342 // create a new entry and add it to the current group
1344 wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1346 wxASSERT( FindEntry(strName
) == NULL
);
1348 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1349 m_aEntries
.Add(pEntry
);
1354 // create a new group and add it to the current group
1356 wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1358 wxASSERT( FindSubgroup(strName
) == NULL
);
1360 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1361 m_aSubgroups
.Add(pGroup
);
1366 // ----------------------------------------------------------------------------
1368 // ----------------------------------------------------------------------------
1371 The delete operations are _very_ slow if we delete the last item of this
1372 group (see comments before GetXXXLineXXX functions for more details),
1373 so it's much better to start with the first entry/group if we want to
1374 delete several of them.
1377 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1379 return DeleteSubgroup(FindSubgroup(szName
));
1382 // doesn't delete the subgroup itself, but does remove references to it from
1383 // all other data structures (and normally the returned pointer should be
1384 // deleted a.s.a.p. because there is nothing much to be done with it anyhow)
1385 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1387 wxCHECK( pGroup
!= NULL
, FALSE
); // deleting non existing group?
1389 // delete all entries
1390 size_t nCount
= pGroup
->m_aEntries
.Count();
1391 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ ) {
1392 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1393 if ( pLine
!= NULL
)
1394 m_pConfig
->LineListRemove(pLine
);
1397 // and subgroups of this sungroup
1398 nCount
= pGroup
->m_aSubgroups
.Count();
1399 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ ) {
1400 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1403 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1404 if ( pLine
!= NULL
) {
1405 // notice that we may do this test inside the previous "if" because the
1406 // last entry's line is surely !NULL
1407 if ( pGroup
== m_pLastGroup
) {
1408 // our last entry is being deleted - find the last one which stays
1409 wxASSERT( m_pLine
!= NULL
); // we have a subgroup with !NULL pLine...
1411 // go back until we find a subgroup or reach the group's line
1412 wxFileConfigGroup
*pNewLast
= NULL
;
1413 size_t n
, nSubgroups
= m_aSubgroups
.Count();
1414 wxFileConfigLineList
*pl
;
1415 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1416 // is it our subgroup?
1417 for ( n
= 0; (pNewLast
== NULL
) && (n
< nSubgroups
); n
++ ) {
1418 // do _not_ call GetGroupLine! we don't want to add it to the local
1419 // file if it's not already there
1420 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1421 pNewLast
= m_aSubgroups
[n
];
1424 if ( pNewLast
!= NULL
) // found?
1428 if ( pl
== m_pLine
) {
1429 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1431 // we've reached the group line without finding any subgroups
1432 m_pLastGroup
= NULL
;
1435 m_pLastGroup
= pNewLast
;
1438 m_pConfig
->LineListRemove(pLine
);
1443 m_aSubgroups
.Remove(pGroup
);
1449 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1451 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1452 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1454 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1455 if ( pLine
!= NULL
) {
1456 // notice that we may do this test inside the previous "if" because the
1457 // last entry's line is surely !NULL
1458 if ( pEntry
== m_pLastEntry
) {
1459 // our last entry is being deleted - find the last one which stays
1460 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1462 // go back until we find another entry or reach the group's line
1463 wxFileConfigEntry
*pNewLast
= NULL
;
1464 size_t n
, nEntries
= m_aEntries
.Count();
1465 wxFileConfigLineList
*pl
;
1466 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1467 // is it our subgroup?
1468 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1469 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1470 pNewLast
= m_aEntries
[n
];
1473 if ( pNewLast
!= NULL
) // found?
1477 if ( pl
== m_pLine
) {
1478 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1480 // we've reached the group line without finding any subgroups
1481 m_pLastEntry
= NULL
;
1484 m_pLastEntry
= pNewLast
;
1487 m_pConfig
->LineListRemove(pLine
);
1490 // we must be written back for the changes to be saved
1493 m_aEntries
.Remove(pEntry
);
1499 // ----------------------------------------------------------------------------
1501 // ----------------------------------------------------------------------------
1502 void wxFileConfigGroup::SetDirty()
1505 if ( Parent() != NULL
) // propagate upwards
1506 Parent()->SetDirty();
1509 // ============================================================================
1510 // wxFileConfig::wxFileConfigEntry
1511 // ============================================================================
1513 // ----------------------------------------------------------------------------
1515 // ----------------------------------------------------------------------------
1516 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1517 const wxString
& strName
,
1519 : m_strName(strName
)
1521 wxASSERT( !strName
.IsEmpty() );
1523 m_pParent
= pParent
;
1528 m_bHasValue
= FALSE
;
1530 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1532 m_strName
.erase(0, 1); // remove first character
1535 // ----------------------------------------------------------------------------
1537 // ----------------------------------------------------------------------------
1539 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1541 if ( m_pLine
!= NULL
) {
1542 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1543 Name().c_str(), m_pParent
->GetFullName().c_str());
1547 Group()->SetLastEntry(this);
1550 // second parameter is FALSE if we read the value from file and prevents the
1551 // entry from being marked as 'dirty'
1552 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1554 if ( bUser
&& IsImmutable() ) {
1555 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
1560 // do nothing if it's the same value: but don't test for it if m_bHasValue
1561 // hadn't been set yet or we'd never write empty values to the file
1562 if ( m_bHasValue
&& strValue
== m_strValue
)
1566 m_strValue
= strValue
;
1569 wxString strVal
= FilterOutValue(strValue
);
1571 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strVal
;
1573 if ( m_pLine
!= NULL
) {
1574 // entry was read from the local config file, just modify the line
1575 m_pLine
->SetText(strLine
);
1578 // add a new line to the file
1579 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1581 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1582 Group()->GetLastEntryLine());
1583 Group()->SetLastEntry(this);
1590 void wxFileConfigEntry::SetDirty()
1593 Group()->SetDirty();
1596 // ============================================================================
1598 // ============================================================================
1600 // ----------------------------------------------------------------------------
1601 // compare functions for array sorting
1602 // ----------------------------------------------------------------------------
1604 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1606 #if wxCONFIG_CASE_SENSITIVE
1607 return wxStrcmp(p1
->Name(), p2
->Name());
1609 return wxStricmp(p1
->Name(), p2
->Name());
1613 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1615 #if wxCONFIG_CASE_SENSITIVE
1616 return wxStrcmp(p1
->Name(), p2
->Name());
1618 return wxStricmp(p1
->Name(), p2
->Name());
1622 // ----------------------------------------------------------------------------
1624 // ----------------------------------------------------------------------------
1626 // undo FilterOutValue
1627 static wxString
FilterInValue(const wxString
& str
)
1630 strResult
.Alloc(str
.Len());
1632 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1634 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1635 if ( str
[n
] == wxT('\\') ) {
1636 switch ( str
[++n
] ) {
1638 strResult
+= wxT('\n');
1642 strResult
+= wxT('\r');
1646 strResult
+= wxT('\t');
1650 strResult
+= wxT('\\');
1654 strResult
+= wxT('"');
1659 if ( str
[n
] != wxT('"') || !bQuoted
)
1660 strResult
+= str
[n
];
1661 else if ( n
!= str
.Len() - 1 ) {
1662 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1665 //else: it's the last quote of a quoted string, ok
1672 // quote the string before writing it to file
1673 static wxString
FilterOutValue(const wxString
& str
)
1679 strResult
.Alloc(str
.Len());
1681 // quoting is necessary to preserve spaces in the beginning of the string
1682 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1685 strResult
+= wxT('"');
1688 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1711 //else: fall through
1714 strResult
+= str
[n
];
1715 continue; // nothing special to do
1718 // we get here only for special characters
1719 strResult
<< wxT('\\') << c
;
1723 strResult
+= wxT('"');
1728 // undo FilterOutEntryName
1729 static wxString
FilterInEntryName(const wxString
& str
)
1732 strResult
.Alloc(str
.Len());
1734 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1735 if ( *pc
== wxT('\\') )
1744 // sanitize entry or group name: insert '\\' before any special characters
1745 static wxString
FilterOutEntryName(const wxString
& str
)
1748 strResult
.Alloc(str
.Len());
1750 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1753 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1754 // which will probably never have special meaning
1755 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1756 // should *not* be quoted
1757 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1758 strResult
+= wxT('\\');
1766 // we can't put ?: in the ctor initializer list because it confuses some
1767 // broken compilers (Borland C++)
1768 static wxString
GetAppName(const wxString
& appName
)
1770 if ( !appName
&& wxTheApp
)
1771 return wxTheApp
->GetAppName();
1776 #endif // wxUSE_CONFIG