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/config.h"
40 #include "wx/fileconf.h"
42 #include "wx/utils.h" // for wxGetHomeDir
44 // _WINDOWS_ is defined when windows.h is included,
45 // __WXMSW__ is defined for MS Windows compilation
46 #if defined(__WXMSW__) && !defined(_WINDOWS_)
57 // headers needed for umask()
59 #include <sys/types.h>
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
66 #define CONST_CAST ((wxFileConfig *)this)->
68 // ----------------------------------------------------------------------------
70 // ----------------------------------------------------------------------------
76 // ----------------------------------------------------------------------------
77 // global functions declarations
78 // ----------------------------------------------------------------------------
80 // compare functions for sorting the arrays
81 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
82 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
85 static wxString
FilterInValue(const wxString
& str
);
86 static wxString
FilterOutValue(const wxString
& str
);
88 static wxString
FilterInEntryName(const wxString
& str
);
89 static wxString
FilterOutEntryName(const wxString
& str
);
91 // get the name to use in wxFileConfig ctor
92 static wxString
GetAppName(const wxString
& appname
);
94 // ============================================================================
96 // ============================================================================
98 // ----------------------------------------------------------------------------
99 // "template" array types
100 // ----------------------------------------------------------------------------
102 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
103 WX_DEFINE_SORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
);
105 // ----------------------------------------------------------------------------
106 // wxFileConfigLineList
107 // ----------------------------------------------------------------------------
109 // we store all lines of the local config file as a linked list in memory
110 class wxFileConfigLineList
113 void SetNext(wxFileConfigLineList
*pNext
) { m_pNext
= pNext
; }
114 void SetPrev(wxFileConfigLineList
*pPrev
) { m_pPrev
= pPrev
; }
117 wxFileConfigLineList(const wxString
& str
,
118 wxFileConfigLineList
*pNext
= NULL
) : m_strLine(str
)
119 { SetNext(pNext
); SetPrev(NULL
); }
121 // next/prev nodes in the linked list
122 wxFileConfigLineList
*Next() const { return m_pNext
; }
123 wxFileConfigLineList
*Prev() const { return m_pPrev
; }
125 // get/change lines text
126 void SetText(const wxString
& str
) { m_strLine
= str
; }
127 const wxString
& Text() const { return m_strLine
; }
130 wxString m_strLine
; // line contents
131 wxFileConfigLineList
*m_pNext
, // next node
132 *m_pPrev
; // previous one
135 // ----------------------------------------------------------------------------
136 // wxFileConfigEntry: a name/value pair
137 // ----------------------------------------------------------------------------
139 class wxFileConfigEntry
142 wxFileConfigGroup
*m_pParent
; // group that contains us
144 wxString m_strName
, // entry name
146 bool m_bDirty
:1, // changed since last read?
147 m_bImmutable
:1, // can be overriden locally?
148 m_bHasValue
:1; // set after first call to SetValue()
150 int m_nLine
; // used if m_pLine == NULL only
152 // pointer to our line in the linked list or NULL if it was found in global
153 // file (which we don't modify)
154 wxFileConfigLineList
*m_pLine
;
157 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
158 const wxString
& strName
, int nLine
);
161 const wxString
& Name() const { return m_strName
; }
162 const wxString
& Value() const { return m_strValue
; }
163 wxFileConfigGroup
*Group() const { return m_pParent
; }
164 bool IsDirty() const { return m_bDirty
; }
165 bool IsImmutable() const { return m_bImmutable
; }
166 bool IsLocal() const { return m_pLine
!= 0; }
167 int Line() const { return m_nLine
; }
168 wxFileConfigLineList
*
169 GetLine() const { return m_pLine
; }
171 // modify entry attributes
172 void SetValue(const wxString
& strValue
, bool bUser
= TRUE
);
174 void SetLine(wxFileConfigLineList
*pLine
);
177 // ----------------------------------------------------------------------------
178 // wxFileConfigGroup: container of entries and other groups
179 // ----------------------------------------------------------------------------
181 class wxFileConfigGroup
184 wxFileConfig
*m_pConfig
; // config object we belong to
185 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
186 ArrayEntries m_aEntries
; // entries in this group
187 ArrayGroups m_aSubgroups
; // subgroups
188 wxString m_strName
; // group's name
189 bool m_bDirty
; // if FALSE => all subgroups are not dirty
190 wxFileConfigLineList
*m_pLine
; // pointer to our line in the linked list
191 wxFileConfigEntry
*m_pLastEntry
; // last entry/subgroup of this group in the
192 wxFileConfigGroup
*m_pLastGroup
; // local file (we insert new ones after it)
194 // DeleteSubgroupByName helper
195 bool DeleteSubgroup(wxFileConfigGroup
*pGroup
);
199 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
201 // dtor deletes all entries and subgroups also
202 ~wxFileConfigGroup();
205 const wxString
& Name() const { return m_strName
; }
206 wxFileConfigGroup
*Parent() const { return m_pParent
; }
207 wxFileConfig
*Config() const { return m_pConfig
; }
208 bool IsDirty() const { return m_bDirty
; }
210 const ArrayEntries
& Entries() const { return m_aEntries
; }
211 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
212 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
214 // find entry/subgroup (NULL if not found)
215 wxFileConfigGroup
*FindSubgroup(const wxChar
*szName
) const;
216 wxFileConfigEntry
*FindEntry (const wxChar
*szName
) const;
218 // delete entry/subgroup, return FALSE if doesn't exist
219 bool DeleteSubgroupByName(const wxChar
*szName
);
220 bool DeleteEntry(const wxChar
*szName
);
222 // create new entry/subgroup returning pointer to newly created element
223 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
224 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
226 // will also recursively set parent's dirty flag
228 void SetLine(wxFileConfigLineList
*pLine
);
230 // rename: no checks are done to ensure that the name is unique!
231 void Rename(const wxString
& newName
);
234 wxString
GetFullName() const;
236 // get the last line belonging to an entry/subgroup of this group
237 wxFileConfigLineList
*GetGroupLine(); // line which contains [group]
238 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
239 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
241 // called by entries/subgroups when they're created/deleted
242 void SetLastEntry(wxFileConfigEntry
*pEntry
) { m_pLastEntry
= pEntry
; }
243 void SetLastGroup(wxFileConfigGroup
*pGroup
) { m_pLastGroup
= pGroup
; }
246 // ============================================================================
248 // ============================================================================
250 // ----------------------------------------------------------------------------
252 // ----------------------------------------------------------------------------
253 wxString
wxFileConfig::GetGlobalDir()
257 #ifdef __VMS__ // Note if __VMS is defined __UNIX is also defined
258 strDir
= wxT("sys$manager:");
259 #elif defined( __UNIX__ )
260 strDir
= wxT("/etc/");
261 #elif defined(__WXPM__)
262 ULONG aulSysInfo
[QSV_MAX
] = {0};
266 rc
= DosQuerySysInfo( 1L, QSV_MAX
, (PVOID
)aulSysInfo
, sizeof(ULONG
)*QSV_MAX
);
269 drive
= aulSysInfo
[QSV_BOOT_DRIVE
- 1];
273 strDir
= "A:\\OS2\\";
276 strDir
= "B:\\OS2\\";
279 strDir
= "C:\\OS2\\";
282 strDir
= "D:\\OS2\\";
285 strDir
= "E:\\OS2\\";
288 strDir
= "F:\\OS2\\";
291 strDir
= "G:\\OS2\\";
294 strDir
= "H:\\OS2\\";
297 strDir
= "I:\\OS2\\";
300 strDir
= "J:\\OS2\\";
303 strDir
= "K:\\OS2\\";
306 strDir
= "L:\\OS2\\";
309 strDir
= "M:\\OS2\\";
312 strDir
= "N:\\OS2\\";
315 strDir
= "O:\\OS2\\";
318 strDir
= "P:\\OS2\\";
321 strDir
= "Q:\\OS2\\";
324 strDir
= "R:\\OS2\\";
327 strDir
= "S:\\OS2\\";
330 strDir
= "T:\\OS2\\";
333 strDir
= "U:\\OS2\\";
336 strDir
= "V:\\OS2\\";
339 strDir
= "W:\\OS2\\";
342 strDir
= "X:\\OS2\\";
345 strDir
= "Y:\\OS2\\";
348 strDir
= "Z:\\OS2\\";
352 #elif defined(__WXSTUBS__)
353 wxASSERT_MSG( FALSE
, wxT("TODO") ) ;
354 #elif defined(__WXMAC__)
355 strDir
= wxMacFindFolder( (short) kOnSystemDisk
, kPreferencesFolderType
, kDontCreateFolder
) ;
357 wxChar szWinDir
[MAX_PATH
];
358 ::GetWindowsDirectory(szWinDir
, MAX_PATH
);
362 #endif // Unix/Windows
367 wxString
wxFileConfig::GetLocalDir()
372 wxGetHomeDir(&strDir
);
376 if (strDir
.Last() != wxT(']'))
378 if (strDir
.Last() != wxT('/')) strDir
<< wxT('/');
380 if (strDir
.Last() != wxT('\\')) strDir
<< wxT('\\');
383 // no local dir concept on mac
384 return GetGlobalDir() ;
390 wxString
wxFileConfig::GetGlobalFileName(const wxChar
*szFile
)
392 wxString str
= GetGlobalDir();
395 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
398 #elif defined( __WXMAC__ )
399 str
<< " Preferences";
407 wxString
wxFileConfig::GetLocalFileName(const wxChar
*szFile
)
409 #ifdef __VMS__ // On VMS I saw the problem that the home directory was appended
410 // twice for the configuration file. Does that also happen for other
412 wxString str
= wxT( '.' );
414 wxString str
= GetLocalDir();
417 #if defined( __UNIX__ ) && !defined( __VMS )
424 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
430 str
<< " Preferences";
435 // ----------------------------------------------------------------------------
437 // ----------------------------------------------------------------------------
439 void wxFileConfig::Init()
442 m_pRootGroup
= new wxFileConfigGroup(NULL
, "", this);
447 // it's not an error if (one of the) file(s) doesn't exist
449 // parse the global file
450 if ( !m_strGlobalFile
.IsEmpty() && wxFile::Exists(m_strGlobalFile
) ) {
451 wxTextFile
fileGlobal(m_strGlobalFile
);
453 if ( fileGlobal
.Open() ) {
454 Parse(fileGlobal
, FALSE
/* global */);
458 wxLogWarning(_("can't open global configuration file '%s'."),
459 m_strGlobalFile
.c_str());
462 // parse the local file
463 if ( !m_strLocalFile
.IsEmpty() && wxFile::Exists(m_strLocalFile
) ) {
464 wxTextFile
fileLocal(m_strLocalFile
);
465 if ( fileLocal
.Open() ) {
466 Parse(fileLocal
, TRUE
/* local */);
470 wxLogWarning(_("can't open user configuration file '%s'."),
471 m_strLocalFile
.c_str());
475 // constructor supports creation of wxFileConfig objects of any type
476 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
477 const wxString
& strLocal
, const wxString
& strGlobal
,
479 : wxConfigBase(::GetAppName(appName
), vendorName
,
482 m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
)
484 // Make up names for files if empty
485 if ( m_strLocalFile
.IsEmpty() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
487 m_strLocalFile
= GetLocalFileName(GetAppName());
490 if ( m_strGlobalFile
.IsEmpty() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
492 m_strGlobalFile
= GetGlobalFileName(GetAppName());
495 // Check if styles are not supplied, but filenames are, in which case
496 // add the correct styles.
497 if ( !m_strLocalFile
.IsEmpty() )
498 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
500 if ( !m_strGlobalFile
.IsEmpty() )
501 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
503 // if the path is not absolute, prepend the standard directory to it
504 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
505 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
507 if ( !m_strLocalFile
.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile
) )
509 wxString strLocal
= m_strLocalFile
;
510 m_strLocalFile
= GetLocalDir();
511 m_strLocalFile
<< strLocal
;
514 if ( !m_strGlobalFile
.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile
) )
516 wxString strGlobal
= m_strGlobalFile
;
517 m_strGlobalFile
= GetGlobalDir();
518 m_strGlobalFile
<< strGlobal
;
527 void wxFileConfig::CleanUp()
531 wxFileConfigLineList
*pCur
= m_linesHead
;
532 while ( pCur
!= NULL
) {
533 wxFileConfigLineList
*pNext
= pCur
->Next();
539 wxFileConfig::~wxFileConfig()
546 // ----------------------------------------------------------------------------
547 // parse a config file
548 // ----------------------------------------------------------------------------
550 void wxFileConfig::Parse(wxTextFile
& file
, bool bLocal
)
552 const wxChar
*pStart
;
556 size_t nLineCount
= file
.GetLineCount();
557 for ( size_t n
= 0; n
< nLineCount
; n
++ ) {
560 // add the line to linked list
562 LineListAppend(strLine
);
564 // skip leading spaces
565 for ( pStart
= strLine
; wxIsspace(*pStart
); pStart
++ )
568 // skip blank/comment lines
569 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
572 if ( *pStart
== wxT('[') ) { // a new group
575 while ( *++pEnd
!= wxT(']') ) {
576 if ( *pEnd
== wxT('\\') ) {
577 // the next char is escaped, so skip it even if it is ']'
581 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
582 // we reached the end of line, break out of the loop
587 if ( *pEnd
!= wxT(']') ) {
588 wxLogError(_("file '%s': unexpected character %c at line %d."),
589 file
.GetName(), *pEnd
, n
+ 1);
590 continue; // skip this line
593 // group name here is always considered as abs path
596 strGroup
<< wxCONFIG_PATH_SEPARATOR
597 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
599 // will create it if doesn't yet exist
603 m_pCurrentGroup
->SetLine(m_linesTail
);
605 // check that there is nothing except comments left on this line
607 while ( *++pEnd
!= wxT('\0') && bCont
) {
616 // ignore whitespace ('\n' impossible here)
620 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
621 file
.GetName(), n
+ 1, pEnd
);
627 const wxChar
*pEnd
= pStart
;
628 while ( *pEnd
&& *pEnd
!= wxT('=') && !wxIsspace(*pEnd
) ) {
629 if ( *pEnd
== wxT('\\') ) {
630 // next character may be space or not - still take it because it's
631 // quoted (unless there is nothing)
634 // the error message will be given below anyhow
642 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
)));
645 while ( wxIsspace(*pEnd
) )
648 if ( *pEnd
++ != wxT('=') ) {
649 wxLogError(_("file '%s', line %d: '=' expected."),
650 file
.GetName(), n
+ 1);
653 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
655 if ( pEntry
== NULL
) {
657 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
660 pEntry
->SetLine(m_linesTail
);
663 if ( bLocal
&& pEntry
->IsImmutable() ) {
664 // immutable keys can't be changed by user
665 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
666 file
.GetName(), n
+ 1, strKey
.c_str());
669 // the condition below catches the cases (a) and (b) but not (c):
670 // (a) global key found second time in global file
671 // (b) key found second (or more) time in local file
672 // (c) key from global file now found in local one
673 // which is exactly what we want.
674 else if ( !bLocal
|| pEntry
->IsLocal() ) {
675 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
676 file
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
679 pEntry
->SetLine(m_linesTail
);
684 while ( wxIsspace(*pEnd
) )
687 pEntry
->SetValue(FilterInValue(pEnd
), FALSE
/* read from file */);
693 // ----------------------------------------------------------------------------
695 // ----------------------------------------------------------------------------
697 void wxFileConfig::SetRootPath()
700 m_pCurrentGroup
= m_pRootGroup
;
703 void wxFileConfig::SetPath(const wxString
& strPath
)
705 wxArrayString aParts
;
707 if ( strPath
.IsEmpty() ) {
712 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
714 wxSplitPath(aParts
, strPath
);
717 // relative path, combine with current one
718 wxString strFullPath
= m_strPath
;
719 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
720 wxSplitPath(aParts
, strFullPath
);
723 // change current group
725 m_pCurrentGroup
= m_pRootGroup
;
726 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
727 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
728 if ( pNextGroup
== NULL
)
729 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
730 m_pCurrentGroup
= pNextGroup
;
733 // recombine path parts in one variable
735 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
736 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
740 // ----------------------------------------------------------------------------
742 // ----------------------------------------------------------------------------
744 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
747 return GetNextGroup(str
, lIndex
);
750 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
752 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
753 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
760 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
763 return GetNextEntry(str
, lIndex
);
766 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
768 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
769 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
776 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
778 size_t n
= m_pCurrentGroup
->Entries().Count();
780 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
781 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
782 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
783 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
784 n
+= GetNumberOfEntries(TRUE
);
785 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
792 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
794 size_t n
= m_pCurrentGroup
->Groups().Count();
796 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
797 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
798 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
799 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
800 n
+= GetNumberOfGroups(TRUE
);
801 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
808 // ----------------------------------------------------------------------------
809 // tests for existence
810 // ----------------------------------------------------------------------------
812 bool wxFileConfig::HasGroup(const wxString
& strName
) const
814 wxConfigPathChanger
path(this, strName
);
816 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
->FindSubgroup(path
.Name());
817 return pGroup
!= NULL
;
820 bool wxFileConfig::HasEntry(const wxString
& strName
) const
822 wxConfigPathChanger
path(this, strName
);
824 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
825 return pEntry
!= NULL
;
828 // ----------------------------------------------------------------------------
830 // ----------------------------------------------------------------------------
832 bool wxFileConfig::Read(const wxString
& key
,
833 wxString
* pStr
) const
835 wxConfigPathChanger
path(this, key
);
837 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
838 if (pEntry
== NULL
) {
842 *pStr
= ExpandEnvVars(pEntry
->Value());
846 bool wxFileConfig::Read(const wxString
& key
,
847 wxString
* pStr
, const wxString
& defVal
) const
849 wxConfigPathChanger
path(this, key
);
851 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
853 if (pEntry
== NULL
) {
854 if( IsRecordingDefaults() )
855 ((wxFileConfig
*)this)->Write(key
,defVal
);
856 *pStr
= ExpandEnvVars(defVal
);
860 *pStr
= ExpandEnvVars(pEntry
->Value());
867 bool wxFileConfig::Read(const wxString
& key
, long *pl
) const
870 if ( !Read(key
, & str
) )
879 bool wxFileConfig::Write(const wxString
& key
, const wxString
& szValue
)
881 wxConfigPathChanger
path(this, key
);
883 wxString strName
= path
.Name();
884 if ( strName
.IsEmpty() ) {
885 // setting the value of a group is an error
886 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
888 // ... except if it's empty in which case it's a way to force it's creation
889 m_pCurrentGroup
->SetDirty();
891 // this will add a line for this group if it didn't have it before
892 (void)m_pCurrentGroup
->GetGroupLine();
897 // check that the name is reasonable
898 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
) {
899 wxLogError(_("Config entry name cannot start with '%c'."),
900 wxCONFIG_IMMUTABLE_PREFIX
);
904 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
905 if ( pEntry
== NULL
)
906 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
908 pEntry
->SetValue(szValue
);
914 bool wxFileConfig::Write(const wxString
& key
, long lValue
)
916 // ltoa() is not ANSI :-(
918 buf
.Printf(wxT("%ld"), lValue
);
919 return Write(key
, buf
);
922 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
924 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
928 // set the umask if needed
932 umaskOld
= umask((mode_t
)m_umask
);
936 wxTempFile
file(m_strLocalFile
);
938 if ( !file
.IsOpened() ) {
939 wxLogError(_("can't open user configuration file."));
943 // write all strings to file
944 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
945 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
946 wxLogError(_("can't write user configuration file."));
951 bool ret
= file
.Commit();
953 #if defined(__WXMAC__) && !defined(__UNIX__)
958 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
960 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
962 finfo
.fdType
= 'TEXT' ;
963 finfo
.fdCreator
= 'ttxt' ;
964 FSpSetFInfo( &spec
, &finfo
) ;
967 #endif // __WXMAC__ && !__UNIX__
970 // restore the old umask if we changed it
973 (void)umask(umaskOld
);
980 // ----------------------------------------------------------------------------
981 // renaming groups/entries
982 // ----------------------------------------------------------------------------
984 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
985 const wxString
& newName
)
987 // check that the entry exists
988 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
992 // check that the new entry doesn't already exist
993 if ( m_pCurrentGroup
->FindEntry(newName
) )
996 // delete the old entry, create the new one
997 wxString value
= oldEntry
->Value();
998 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1001 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1002 newEntry
->SetValue(value
);
1007 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1008 const wxString
& newName
)
1010 // check that the group exists
1011 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1015 // check that the new group doesn't already exist
1016 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1019 group
->Rename(newName
);
1024 // ----------------------------------------------------------------------------
1025 // delete groups/entries
1026 // ----------------------------------------------------------------------------
1028 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1030 wxConfigPathChanger
path(this, key
);
1032 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1035 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1036 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1037 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1038 SetPath(wxT("..")); // changes m_pCurrentGroup!
1039 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1041 //else: never delete the root group
1047 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1049 wxConfigPathChanger
path(this, key
);
1051 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1054 bool wxFileConfig::DeleteAll()
1058 if ( wxRemove(m_strLocalFile
) == -1 )
1059 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1061 m_strLocalFile
= m_strGlobalFile
= wxT("");
1067 // ----------------------------------------------------------------------------
1068 // linked list functions
1069 // ----------------------------------------------------------------------------
1071 // append a new line to the end of the list
1072 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1074 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1076 if ( m_linesTail
== NULL
) {
1078 m_linesHead
= pLine
;
1082 m_linesTail
->SetNext(pLine
);
1083 pLine
->SetPrev(m_linesTail
);
1086 m_linesTail
= pLine
;
1090 // insert a new line after the given one or in the very beginning if !pLine
1091 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1092 wxFileConfigLineList
*pLine
)
1094 if ( pLine
== m_linesTail
)
1095 return LineListAppend(str
);
1097 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1098 if ( pLine
== NULL
) {
1099 // prepend to the list
1100 pNewLine
->SetNext(m_linesHead
);
1101 m_linesHead
->SetPrev(pNewLine
);
1102 m_linesHead
= pNewLine
;
1105 // insert before pLine
1106 wxFileConfigLineList
*pNext
= pLine
->Next();
1107 pNewLine
->SetNext(pNext
);
1108 pNewLine
->SetPrev(pLine
);
1109 pNext
->SetPrev(pNewLine
);
1110 pLine
->SetNext(pNewLine
);
1116 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1118 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1119 *pNext
= pLine
->Next();
1122 if ( pPrev
== NULL
)
1123 m_linesHead
= pNext
;
1125 pPrev
->SetNext(pNext
);
1128 if ( pNext
== NULL
)
1129 m_linesTail
= pPrev
;
1131 pNext
->SetPrev(pPrev
);
1136 bool wxFileConfig::LineListIsEmpty()
1138 return m_linesHead
== NULL
;
1141 // ============================================================================
1142 // wxFileConfig::wxFileConfigGroup
1143 // ============================================================================
1145 // ----------------------------------------------------------------------------
1147 // ----------------------------------------------------------------------------
1150 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1151 const wxString
& strName
,
1152 wxFileConfig
*pConfig
)
1153 : m_aEntries(CompareEntries
),
1154 m_aSubgroups(CompareGroups
),
1157 m_pConfig
= pConfig
;
1158 m_pParent
= pParent
;
1162 m_pLastEntry
= NULL
;
1163 m_pLastGroup
= NULL
;
1166 // dtor deletes all children
1167 wxFileConfigGroup::~wxFileConfigGroup()
1170 size_t n
, nCount
= m_aEntries
.Count();
1171 for ( n
= 0; n
< nCount
; n
++ )
1172 delete m_aEntries
[n
];
1175 nCount
= m_aSubgroups
.Count();
1176 for ( n
= 0; n
< nCount
; n
++ )
1177 delete m_aSubgroups
[n
];
1180 // ----------------------------------------------------------------------------
1182 // ----------------------------------------------------------------------------
1184 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1186 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
1192 This is a bit complicated, so let me explain it in details. All lines that
1193 were read from the local file (the only one we will ever modify) are stored
1194 in a (doubly) linked list. Our problem is to know at which position in this
1195 list should we insert the new entries/subgroups. To solve it we keep three
1196 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1198 m_pLine points to the line containing "[group_name]"
1199 m_pLastEntry points to the last entry of this group in the local file.
1200 m_pLastGroup subgroup
1202 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1203 from the local file, the corresponding variable is set. However, if the group
1204 was read from the global file and then modified or created by the application
1205 these variables are still NULL and we need to create the corresponding lines.
1206 See the following functions (and comments preceding them) for the details of
1209 Also, when our last entry/group are deleted we need to find the new last
1210 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1211 of lines until it either founds an entry/subgroup (and this is the new last
1212 element) or the m_pLine of the group, in which case there are no more entries
1213 (or subgroups) left and m_pLast<element> becomes NULL.
1215 NB: This last problem could be avoided for entries if we added new entries
1216 immediately after m_pLine, but in this case the entries would appear
1217 backwards in the config file (OTOH, it's not that important) and as we
1218 would still need to do it for the subgroups the code wouldn't have been
1219 significantly less complicated.
1222 // Return the line which contains "[our name]". If we're still not in the list,
1223 // add our line to it immediately after the last line of our parent group if we
1224 // have it or in the very beginning if we're the root group.
1225 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1227 if ( m_pLine
== NULL
) {
1228 wxFileConfigGroup
*pParent
= Parent();
1230 // this group wasn't present in local config file, add it now
1231 if ( pParent
!= NULL
) {
1232 wxString strFullName
;
1233 strFullName
<< wxT("[")
1235 << FilterOutEntryName(GetFullName().c_str() + 1)
1237 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1238 pParent
->GetLastGroupLine());
1239 pParent
->SetLastGroup(this); // we're surely after all the others
1242 // we return NULL, so that LineListInsert() will insert us in the
1250 // Return the last line belonging to the subgroups of this group (after which
1251 // we can add a new subgroup), if we don't have any subgroups or entries our
1252 // last line is the group line (m_pLine) itself.
1253 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1255 // if we have any subgroups, our last line is the last line of the last
1257 if ( m_pLastGroup
!= NULL
) {
1258 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1260 wxASSERT( pLine
!= NULL
); // last group must have !NULL associated line
1264 // no subgroups, so the last line is the line of thelast entry (if any)
1265 return GetLastEntryLine();
1268 // return the last line belonging to the entries of this group (after which
1269 // we can add a new entry), if we don't have any entries we will add the new
1270 // one immediately after the group line itself.
1271 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1273 if ( m_pLastEntry
!= NULL
) {
1274 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1276 wxASSERT( pLine
!= NULL
); // last entry must have !NULL associated line
1280 // no entries: insert after the group header
1281 return GetGroupLine();
1284 // ----------------------------------------------------------------------------
1286 // ----------------------------------------------------------------------------
1288 void wxFileConfigGroup::Rename(const wxString
& newName
)
1290 m_strName
= newName
;
1292 wxFileConfigLineList
*line
= GetGroupLine();
1293 wxString strFullName
;
1294 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1295 line
->SetText(strFullName
);
1300 wxString
wxFileConfigGroup::GetFullName() const
1303 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1308 // ----------------------------------------------------------------------------
1310 // ----------------------------------------------------------------------------
1312 // use binary search because the array is sorted
1314 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1318 hi
= m_aEntries
.Count();
1320 wxFileConfigEntry
*pEntry
;
1324 pEntry
= m_aEntries
[i
];
1326 #if wxCONFIG_CASE_SENSITIVE
1327 res
= wxStrcmp(pEntry
->Name(), szName
);
1329 res
= wxStricmp(pEntry
->Name(), szName
);
1344 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1348 hi
= m_aSubgroups
.Count();
1350 wxFileConfigGroup
*pGroup
;
1354 pGroup
= m_aSubgroups
[i
];
1356 #if wxCONFIG_CASE_SENSITIVE
1357 res
= wxStrcmp(pGroup
->Name(), szName
);
1359 res
= wxStricmp(pGroup
->Name(), szName
);
1373 // ----------------------------------------------------------------------------
1374 // create a new item
1375 // ----------------------------------------------------------------------------
1377 // create a new entry and add it to the current group
1379 wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1381 wxASSERT( FindEntry(strName
) == NULL
);
1383 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1384 m_aEntries
.Add(pEntry
);
1389 // create a new group and add it to the current group
1391 wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1393 wxASSERT( FindSubgroup(strName
) == NULL
);
1395 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1396 m_aSubgroups
.Add(pGroup
);
1401 // ----------------------------------------------------------------------------
1403 // ----------------------------------------------------------------------------
1406 The delete operations are _very_ slow if we delete the last item of this
1407 group (see comments before GetXXXLineXXX functions for more details),
1408 so it's much better to start with the first entry/group if we want to
1409 delete several of them.
1412 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1414 return DeleteSubgroup(FindSubgroup(szName
));
1417 // doesn't delete the subgroup itself, but does remove references to it from
1418 // all other data structures (and normally the returned pointer should be
1419 // deleted a.s.a.p. because there is nothing much to be done with it anyhow)
1420 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1422 wxCHECK( pGroup
!= NULL
, FALSE
); // deleting non existing group?
1424 // delete all entries
1425 size_t nCount
= pGroup
->m_aEntries
.Count();
1426 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ ) {
1427 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1428 if ( pLine
!= NULL
)
1429 m_pConfig
->LineListRemove(pLine
);
1432 // and subgroups of this sungroup
1433 nCount
= pGroup
->m_aSubgroups
.Count();
1434 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ ) {
1435 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1438 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1439 if ( pLine
!= NULL
) {
1440 // notice that we may do this test inside the previous "if" because the
1441 // last entry's line is surely !NULL
1442 if ( pGroup
== m_pLastGroup
) {
1443 // our last entry is being deleted - find the last one which stays
1444 wxASSERT( m_pLine
!= NULL
); // we have a subgroup with !NULL pLine...
1446 // go back until we find a subgroup or reach the group's line
1447 wxFileConfigGroup
*pNewLast
= NULL
;
1448 size_t n
, nSubgroups
= m_aSubgroups
.Count();
1449 wxFileConfigLineList
*pl
;
1450 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1451 // is it our subgroup?
1452 for ( n
= 0; (pNewLast
== NULL
) && (n
< nSubgroups
); n
++ ) {
1453 // do _not_ call GetGroupLine! we don't want to add it to the local
1454 // file if it's not already there
1455 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1456 pNewLast
= m_aSubgroups
[n
];
1459 if ( pNewLast
!= NULL
) // found?
1463 if ( pl
== m_pLine
) {
1464 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1466 // we've reached the group line without finding any subgroups
1467 m_pLastGroup
= NULL
;
1470 m_pLastGroup
= pNewLast
;
1473 m_pConfig
->LineListRemove(pLine
);
1478 m_aSubgroups
.Remove(pGroup
);
1484 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1486 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1487 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1489 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1490 if ( pLine
!= NULL
) {
1491 // notice that we may do this test inside the previous "if" because the
1492 // last entry's line is surely !NULL
1493 if ( pEntry
== m_pLastEntry
) {
1494 // our last entry is being deleted - find the last one which stays
1495 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1497 // go back until we find another entry or reach the group's line
1498 wxFileConfigEntry
*pNewLast
= NULL
;
1499 size_t n
, nEntries
= m_aEntries
.Count();
1500 wxFileConfigLineList
*pl
;
1501 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1502 // is it our subgroup?
1503 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1504 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1505 pNewLast
= m_aEntries
[n
];
1508 if ( pNewLast
!= NULL
) // found?
1512 if ( pl
== m_pLine
) {
1513 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1515 // we've reached the group line without finding any subgroups
1516 m_pLastEntry
= NULL
;
1519 m_pLastEntry
= pNewLast
;
1522 m_pConfig
->LineListRemove(pLine
);
1525 // we must be written back for the changes to be saved
1528 m_aEntries
.Remove(pEntry
);
1534 // ----------------------------------------------------------------------------
1536 // ----------------------------------------------------------------------------
1537 void wxFileConfigGroup::SetDirty()
1540 if ( Parent() != NULL
) // propagate upwards
1541 Parent()->SetDirty();
1544 // ============================================================================
1545 // wxFileConfig::wxFileConfigEntry
1546 // ============================================================================
1548 // ----------------------------------------------------------------------------
1550 // ----------------------------------------------------------------------------
1551 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1552 const wxString
& strName
,
1554 : m_strName(strName
)
1556 wxASSERT( !strName
.IsEmpty() );
1558 m_pParent
= pParent
;
1563 m_bHasValue
= FALSE
;
1565 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1567 m_strName
.erase(0, 1); // remove first character
1570 // ----------------------------------------------------------------------------
1572 // ----------------------------------------------------------------------------
1574 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1576 if ( m_pLine
!= NULL
) {
1577 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1578 Name().c_str(), m_pParent
->GetFullName().c_str());
1582 Group()->SetLastEntry(this);
1585 // second parameter is FALSE if we read the value from file and prevents the
1586 // entry from being marked as 'dirty'
1587 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1589 if ( bUser
&& IsImmutable() ) {
1590 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
1595 // do nothing if it's the same value: but don't test for it if m_bHasValue
1596 // hadn't been set yet or we'd never write empty values to the file
1597 if ( m_bHasValue
&& strValue
== m_strValue
)
1601 m_strValue
= strValue
;
1604 wxString strVal
= FilterOutValue(strValue
);
1606 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strVal
;
1608 if ( m_pLine
!= NULL
) {
1609 // entry was read from the local config file, just modify the line
1610 m_pLine
->SetText(strLine
);
1613 // add a new line to the file
1614 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1616 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1617 Group()->GetLastEntryLine());
1618 Group()->SetLastEntry(this);
1625 void wxFileConfigEntry::SetDirty()
1628 Group()->SetDirty();
1631 // ============================================================================
1633 // ============================================================================
1635 // ----------------------------------------------------------------------------
1636 // compare functions for array sorting
1637 // ----------------------------------------------------------------------------
1639 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1641 #if wxCONFIG_CASE_SENSITIVE
1642 return wxStrcmp(p1
->Name(), p2
->Name());
1644 return wxStricmp(p1
->Name(), p2
->Name());
1648 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1650 #if wxCONFIG_CASE_SENSITIVE
1651 return wxStrcmp(p1
->Name(), p2
->Name());
1653 return wxStricmp(p1
->Name(), p2
->Name());
1657 // ----------------------------------------------------------------------------
1659 // ----------------------------------------------------------------------------
1661 // undo FilterOutValue
1662 static wxString
FilterInValue(const wxString
& str
)
1665 strResult
.Alloc(str
.Len());
1667 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1669 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1670 if ( str
[n
] == wxT('\\') ) {
1671 switch ( str
[++n
] ) {
1673 strResult
+= wxT('\n');
1677 strResult
+= wxT('\r');
1681 strResult
+= wxT('\t');
1685 strResult
+= wxT('\\');
1689 strResult
+= wxT('"');
1694 if ( str
[n
] != wxT('"') || !bQuoted
)
1695 strResult
+= str
[n
];
1696 else if ( n
!= str
.Len() - 1 ) {
1697 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1700 //else: it's the last quote of a quoted string, ok
1707 // quote the string before writing it to file
1708 static wxString
FilterOutValue(const wxString
& str
)
1714 strResult
.Alloc(str
.Len());
1716 // quoting is necessary to preserve spaces in the beginning of the string
1717 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1720 strResult
+= wxT('"');
1723 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1746 //else: fall through
1749 strResult
+= str
[n
];
1750 continue; // nothing special to do
1753 // we get here only for special characters
1754 strResult
<< wxT('\\') << c
;
1758 strResult
+= wxT('"');
1763 // undo FilterOutEntryName
1764 static wxString
FilterInEntryName(const wxString
& str
)
1767 strResult
.Alloc(str
.Len());
1769 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1770 if ( *pc
== wxT('\\') )
1779 // sanitize entry or group name: insert '\\' before any special characters
1780 static wxString
FilterOutEntryName(const wxString
& str
)
1783 strResult
.Alloc(str
.Len());
1785 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1788 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1789 // which will probably never have special meaning
1790 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1791 // should *not* be quoted
1792 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1793 strResult
+= wxT('\\');
1801 // we can't put ?: in the ctor initializer list because it confuses some
1802 // broken compilers (Borland C++)
1803 static wxString
GetAppName(const wxString
& appName
)
1805 if ( !appName
&& wxTheApp
)
1806 return wxTheApp
->GetAppName();
1811 #endif // wxUSE_CONFIG