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];
280 strDir
= "A:\\OS2\\";
283 strDir
= "B:\\OS2\\";
286 strDir
= "C:\\OS2\\";
289 strDir
= "D:\\OS2\\";
292 strDir
= "E:\\OS2\\";
295 strDir
= "F:\\OS2\\";
298 strDir
= "G:\\OS2\\";
301 strDir
= "H:\\OS2\\";
304 strDir
= "I:\\OS2\\";
307 strDir
= "J:\\OS2\\";
310 strDir
= "K:\\OS2\\";
313 strDir
= "L:\\OS2\\";
316 strDir
= "M:\\OS2\\";
319 strDir
= "N:\\OS2\\";
322 strDir
= "O:\\OS2\\";
325 strDir
= "P:\\OS2\\";
328 strDir
= "Q:\\OS2\\";
331 strDir
= "R:\\OS2\\";
334 strDir
= "S:\\OS2\\";
337 strDir
= "T:\\OS2\\";
340 strDir
= "U:\\OS2\\";
343 strDir
= "V:\\OS2\\";
346 strDir
= "W:\\OS2\\";
349 strDir
= "X:\\OS2\\";
352 strDir
= "Y:\\OS2\\";
355 strDir
= "Z:\\OS2\\";
359 #elif defined(__WXSTUBS__)
360 wxASSERT_MSG( FALSE
, wxT("TODO") ) ;
362 wxChar szWinDir
[MAX_PATH
];
363 ::GetWindowsDirectory(szWinDir
, MAX_PATH
);
367 #endif // Unix/Windows
372 wxString
wxFileConfig::GetLocalDir()
376 #if defined(__WXMAC__)
377 // no local dir concept on Mac OS 9
378 return GetGlobalDir() ;
380 wxGetHomeDir(&strDir
);
384 if (strDir
.Last() != wxT(']'))
386 if (strDir
.Last() != wxT('/')) strDir
<< wxT('/');
388 if (strDir
.Last() != wxT('\\')) strDir
<< wxT('\\');
395 wxString
wxFileConfig::GetGlobalFileName(const wxChar
*szFile
)
397 wxString str
= GetGlobalDir();
400 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
401 #if defined( __WXMAC__ )
402 str
<< " Preferences";
403 #elif defined( __UNIX__ )
412 wxString
wxFileConfig::GetLocalFileName(const wxChar
*szFile
)
414 #ifdef __VMS__ // On VMS I saw the problem that the home directory was appended
415 // twice for the configuration file. Does that also happen for other
417 wxString str
= wxT( '.' );
419 wxString str
= GetLocalDir();
422 #if defined( __UNIX__ ) && !defined( __VMS ) && !defined( __WXMAC__ )
429 if ( wxStrchr(szFile
, wxT('.')) == NULL
)
434 str
<< " Preferences";
439 // ----------------------------------------------------------------------------
441 // ----------------------------------------------------------------------------
443 void wxFileConfig::Init()
446 m_pRootGroup
= new wxFileConfigGroup(NULL
, "", this);
451 // it's not an error if (one of the) file(s) doesn't exist
453 // parse the global file
454 if ( !m_strGlobalFile
.IsEmpty() && wxFile::Exists(m_strGlobalFile
) ) {
455 wxTextFile
fileGlobal(m_strGlobalFile
);
457 if ( fileGlobal
.Open() ) {
458 Parse(fileGlobal
, FALSE
/* global */);
462 wxLogWarning(_("can't open global configuration file '%s'."),
463 m_strGlobalFile
.c_str());
466 // parse the local file
467 if ( !m_strLocalFile
.IsEmpty() && wxFile::Exists(m_strLocalFile
) ) {
468 wxTextFile
fileLocal(m_strLocalFile
);
469 if ( fileLocal
.Open() ) {
470 Parse(fileLocal
, TRUE
/* local */);
474 wxLogWarning(_("can't open user configuration file '%s'."),
475 m_strLocalFile
.c_str());
479 // constructor supports creation of wxFileConfig objects of any type
480 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
481 const wxString
& strLocal
, const wxString
& strGlobal
,
483 : wxConfigBase(::GetAppName(appName
), vendorName
,
486 m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
)
488 // Make up names for files if empty
489 if ( m_strLocalFile
.IsEmpty() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
491 m_strLocalFile
= GetLocalFileName(GetAppName());
494 if ( m_strGlobalFile
.IsEmpty() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
496 m_strGlobalFile
= GetGlobalFileName(GetAppName());
499 // Check if styles are not supplied, but filenames are, in which case
500 // add the correct styles.
501 if ( !m_strLocalFile
.IsEmpty() )
502 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
504 if ( !m_strGlobalFile
.IsEmpty() )
505 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
507 // if the path is not absolute, prepend the standard directory to it
508 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
509 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
511 if ( !m_strLocalFile
.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile
) )
513 wxString strLocal
= m_strLocalFile
;
514 m_strLocalFile
= GetLocalDir();
515 m_strLocalFile
<< strLocal
;
518 if ( !m_strGlobalFile
.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile
) )
520 wxString strGlobal
= m_strGlobalFile
;
521 m_strGlobalFile
= GetGlobalDir();
522 m_strGlobalFile
<< strGlobal
;
533 wxFileConfig::wxFileConfig(wxInputStream
&inStream
)
535 // always local_file when this constructor is called (?)
536 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
539 m_pRootGroup
= new wxFileConfigGroup(NULL
, "", this);
544 // translate everything to the current (platform-dependent) line
545 // termination character
551 while ( !inStream
.Read(buf
, WXSIZEOF(buf
)).Eof() )
552 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
554 strTmp
.append(wxConvertMB2WX(buf
), inStream
.LastRead());
556 strTrans
= wxTextBuffer::Translate(strTmp
);
559 wxMemoryText memText
;
561 // Now we can add the text to the memory text. To do this we extract line
562 // by line from the translated string, until we've reached the end.
564 // VZ: all this is horribly inefficient, we should do the translation on
565 // the fly in one pass saving both memory and time (TODO)
567 const wxChar
*pEOL
= wxTextBuffer::GetEOL(wxTextBuffer::typeDefault
);
568 const size_t EOLLen
= wxStrlen(pEOL
);
570 int posLineStart
= strTrans
.Find(pEOL
);
571 while ( posLineStart
!= -1 )
573 wxString
line(strTrans
.Left(posLineStart
));
575 memText
.AddLine(line
);
577 strTrans
= strTrans
.Mid(posLineStart
+ EOLLen
);
579 posLineStart
= strTrans
.Find(pEOL
);
582 // also add whatever we have left in the translated string.
583 memText
.AddLine(strTrans
);
585 // Finally we can parse it all.
586 Parse(memText
, TRUE
/* local */);
591 #endif // wxUSE_STREAMS
593 void wxFileConfig::CleanUp()
597 wxFileConfigLineList
*pCur
= m_linesHead
;
598 while ( pCur
!= NULL
) {
599 wxFileConfigLineList
*pNext
= pCur
->Next();
605 wxFileConfig::~wxFileConfig()
612 // ----------------------------------------------------------------------------
613 // parse a config file
614 // ----------------------------------------------------------------------------
616 void wxFileConfig::Parse(wxTextBuffer
& buffer
, bool bLocal
)
618 const wxChar
*pStart
;
622 size_t nLineCount
= buffer
.GetLineCount();
623 for ( size_t n
= 0; n
< nLineCount
; n
++ ) {
626 // add the line to linked list
628 LineListAppend(strLine
);
630 // skip leading spaces
631 for ( pStart
= strLine
; wxIsspace(*pStart
); pStart
++ )
634 // skip blank/comment lines
635 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
638 if ( *pStart
== wxT('[') ) { // a new group
641 while ( *++pEnd
!= wxT(']') ) {
642 if ( *pEnd
== wxT('\\') ) {
643 // the next char is escaped, so skip it even if it is ']'
647 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
648 // we reached the end of line, break out of the loop
653 if ( *pEnd
!= wxT(']') ) {
654 wxLogError(_("file '%s': unexpected character %c at line %d."),
655 buffer
.GetName(), *pEnd
, n
+ 1);
656 continue; // skip this line
659 // group name here is always considered as abs path
662 strGroup
<< wxCONFIG_PATH_SEPARATOR
663 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
665 // will create it if doesn't yet exist
669 m_pCurrentGroup
->SetLine(m_linesTail
);
671 // check that there is nothing except comments left on this line
673 while ( *++pEnd
!= wxT('\0') && bCont
) {
682 // ignore whitespace ('\n' impossible here)
686 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
687 buffer
.GetName(), n
+ 1, pEnd
);
693 const wxChar
*pEnd
= pStart
;
694 while ( *pEnd
&& *pEnd
!= wxT('=') && !wxIsspace(*pEnd
) ) {
695 if ( *pEnd
== wxT('\\') ) {
696 // next character may be space or not - still take it because it's
697 // quoted (unless there is nothing)
700 // the error message will be given below anyhow
708 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
)));
711 while ( wxIsspace(*pEnd
) )
714 if ( *pEnd
++ != wxT('=') ) {
715 wxLogError(_("file '%s', line %d: '=' expected."),
716 buffer
.GetName(), n
+ 1);
719 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
721 if ( pEntry
== NULL
) {
723 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
726 pEntry
->SetLine(m_linesTail
);
729 if ( bLocal
&& pEntry
->IsImmutable() ) {
730 // immutable keys can't be changed by user
731 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
732 buffer
.GetName(), n
+ 1, strKey
.c_str());
735 // the condition below catches the cases (a) and (b) but not (c):
736 // (a) global key found second time in global file
737 // (b) key found second (or more) time in local file
738 // (c) key from global file now found in local one
739 // which is exactly what we want.
740 else if ( !bLocal
|| pEntry
->IsLocal() ) {
741 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
742 buffer
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
745 pEntry
->SetLine(m_linesTail
);
750 while ( wxIsspace(*pEnd
) )
753 pEntry
->SetValue(FilterInValue(pEnd
), FALSE
/* read from file */);
759 // ----------------------------------------------------------------------------
761 // ----------------------------------------------------------------------------
763 void wxFileConfig::SetRootPath()
766 m_pCurrentGroup
= m_pRootGroup
;
769 void wxFileConfig::SetPath(const wxString
& strPath
)
771 wxArrayString aParts
;
773 if ( strPath
.IsEmpty() ) {
778 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
780 wxSplitPath(aParts
, strPath
);
783 // relative path, combine with current one
784 wxString strFullPath
= m_strPath
;
785 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
786 wxSplitPath(aParts
, strFullPath
);
789 // change current group
791 m_pCurrentGroup
= m_pRootGroup
;
792 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
793 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
794 if ( pNextGroup
== NULL
)
795 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
796 m_pCurrentGroup
= pNextGroup
;
799 // recombine path parts in one variable
801 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
802 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
806 // ----------------------------------------------------------------------------
808 // ----------------------------------------------------------------------------
810 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
813 return GetNextGroup(str
, lIndex
);
816 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
818 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
819 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
826 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
829 return GetNextEntry(str
, lIndex
);
832 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
834 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
835 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
842 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
844 size_t n
= m_pCurrentGroup
->Entries().Count();
846 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
847 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
848 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
849 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
850 n
+= GetNumberOfEntries(TRUE
);
851 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
858 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
860 size_t n
= m_pCurrentGroup
->Groups().Count();
862 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
863 size_t nSubgroups
= m_pCurrentGroup
->Groups().Count();
864 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
865 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
866 n
+= GetNumberOfGroups(TRUE
);
867 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
874 // ----------------------------------------------------------------------------
875 // tests for existence
876 // ----------------------------------------------------------------------------
878 bool wxFileConfig::HasGroup(const wxString
& strName
) const
880 wxConfigPathChanger
path(this, strName
);
882 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
->FindSubgroup(path
.Name());
883 return pGroup
!= NULL
;
886 bool wxFileConfig::HasEntry(const wxString
& strName
) const
888 wxConfigPathChanger
path(this, strName
);
890 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
891 return pEntry
!= NULL
;
894 // ----------------------------------------------------------------------------
896 // ----------------------------------------------------------------------------
898 bool wxFileConfig::Read(const wxString
& key
,
899 wxString
* pStr
) const
901 wxConfigPathChanger
path(this, key
);
903 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
904 if (pEntry
== NULL
) {
908 *pStr
= ExpandEnvVars(pEntry
->Value());
912 bool wxFileConfig::Read(const wxString
& key
,
913 wxString
* pStr
, const wxString
& defVal
) const
915 wxConfigPathChanger
path(this, key
);
917 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
919 if (pEntry
== NULL
) {
920 if( IsRecordingDefaults() )
921 ((wxFileConfig
*)this)->Write(key
,defVal
);
922 *pStr
= ExpandEnvVars(defVal
);
926 *pStr
= ExpandEnvVars(pEntry
->Value());
933 bool wxFileConfig::Read(const wxString
& key
, long *pl
) const
936 if ( !Read(key
, & str
) )
945 bool wxFileConfig::Write(const wxString
& key
, const wxString
& szValue
)
947 wxConfigPathChanger
path(this, key
);
949 wxString strName
= path
.Name();
950 if ( strName
.IsEmpty() ) {
951 // setting the value of a group is an error
952 wxASSERT_MSG( wxIsEmpty(szValue
), wxT("can't set value of a group!") );
954 // ... except if it's empty in which case it's a way to force it's creation
955 m_pCurrentGroup
->SetDirty();
957 // this will add a line for this group if it didn't have it before
958 (void)m_pCurrentGroup
->GetGroupLine();
963 // check that the name is reasonable
964 if ( strName
[0u] == wxCONFIG_IMMUTABLE_PREFIX
) {
965 wxLogError(_("Config entry name cannot start with '%c'."),
966 wxCONFIG_IMMUTABLE_PREFIX
);
970 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strName
);
971 if ( pEntry
== NULL
)
972 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
974 pEntry
->SetValue(szValue
);
980 bool wxFileConfig::Write(const wxString
& key
, long lValue
)
982 // ltoa() is not ANSI :-(
984 buf
.Printf(wxT("%ld"), lValue
);
985 return Write(key
, buf
);
988 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
990 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() || !m_strLocalFile
)
994 // set the umask if needed
998 umaskOld
= umask((mode_t
)m_umask
);
1002 wxTempFile
file(m_strLocalFile
);
1004 if ( !file
.IsOpened() ) {
1005 wxLogError(_("can't open user configuration file."));
1009 // write all strings to file
1010 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
1011 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
1012 wxLogError(_("can't write user configuration file."));
1017 bool ret
= file
.Commit();
1019 #if defined(__WXMAC__)
1024 wxMacFilename2FSSpec( m_strLocalFile
, &spec
) ;
1026 if ( FSpGetFInfo( &spec
, &finfo
) == noErr
)
1028 finfo
.fdType
= 'TEXT' ;
1029 finfo
.fdCreator
= 'ttxt' ;
1030 FSpSetFInfo( &spec
, &finfo
) ;
1036 // restore the old umask if we changed it
1037 if ( m_umask
!= -1 )
1039 (void)umask(umaskOld
);
1046 // ----------------------------------------------------------------------------
1047 // renaming groups/entries
1048 // ----------------------------------------------------------------------------
1050 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1051 const wxString
& newName
)
1053 // check that the entry exists
1054 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1058 // check that the new entry doesn't already exist
1059 if ( m_pCurrentGroup
->FindEntry(newName
) )
1062 // delete the old entry, create the new one
1063 wxString value
= oldEntry
->Value();
1064 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1067 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1068 newEntry
->SetValue(value
);
1073 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1074 const wxString
& newName
)
1076 // check that the group exists
1077 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1081 // check that the new group doesn't already exist
1082 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1085 group
->Rename(newName
);
1090 // ----------------------------------------------------------------------------
1091 // delete groups/entries
1092 // ----------------------------------------------------------------------------
1094 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1096 wxConfigPathChanger
path(this, key
);
1098 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1101 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1102 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1103 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1104 SetPath(wxT("..")); // changes m_pCurrentGroup!
1105 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1107 //else: never delete the root group
1113 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1115 wxConfigPathChanger
path(this, key
);
1117 return m_pCurrentGroup
->DeleteSubgroupByName(path
.Name());
1120 bool wxFileConfig::DeleteAll()
1124 if ( wxRemove(m_strLocalFile
) == -1 )
1125 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile
.c_str());
1127 m_strLocalFile
= m_strGlobalFile
= wxT("");
1133 // ----------------------------------------------------------------------------
1134 // linked list functions
1135 // ----------------------------------------------------------------------------
1137 // append a new line to the end of the list
1138 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1140 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1142 if ( m_linesTail
== NULL
) {
1144 m_linesHead
= pLine
;
1148 m_linesTail
->SetNext(pLine
);
1149 pLine
->SetPrev(m_linesTail
);
1152 m_linesTail
= pLine
;
1156 // insert a new line after the given one or in the very beginning if !pLine
1157 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1158 wxFileConfigLineList
*pLine
)
1160 if ( pLine
== m_linesTail
)
1161 return LineListAppend(str
);
1163 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1164 if ( pLine
== NULL
) {
1165 // prepend to the list
1166 pNewLine
->SetNext(m_linesHead
);
1167 m_linesHead
->SetPrev(pNewLine
);
1168 m_linesHead
= pNewLine
;
1171 // insert before pLine
1172 wxFileConfigLineList
*pNext
= pLine
->Next();
1173 pNewLine
->SetNext(pNext
);
1174 pNewLine
->SetPrev(pLine
);
1175 pNext
->SetPrev(pNewLine
);
1176 pLine
->SetNext(pNewLine
);
1182 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1184 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1185 *pNext
= pLine
->Next();
1188 if ( pPrev
== NULL
)
1189 m_linesHead
= pNext
;
1191 pPrev
->SetNext(pNext
);
1194 if ( pNext
== NULL
)
1195 m_linesTail
= pPrev
;
1197 pNext
->SetPrev(pPrev
);
1202 bool wxFileConfig::LineListIsEmpty()
1204 return m_linesHead
== NULL
;
1207 // ============================================================================
1208 // wxFileConfig::wxFileConfigGroup
1209 // ============================================================================
1211 // ----------------------------------------------------------------------------
1213 // ----------------------------------------------------------------------------
1216 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1217 const wxString
& strName
,
1218 wxFileConfig
*pConfig
)
1219 : m_aEntries(CompareEntries
),
1220 m_aSubgroups(CompareGroups
),
1223 m_pConfig
= pConfig
;
1224 m_pParent
= pParent
;
1228 m_pLastEntry
= NULL
;
1229 m_pLastGroup
= NULL
;
1232 // dtor deletes all children
1233 wxFileConfigGroup::~wxFileConfigGroup()
1236 size_t n
, nCount
= m_aEntries
.Count();
1237 for ( n
= 0; n
< nCount
; n
++ )
1238 delete m_aEntries
[n
];
1241 nCount
= m_aSubgroups
.Count();
1242 for ( n
= 0; n
< nCount
; n
++ )
1243 delete m_aSubgroups
[n
];
1246 // ----------------------------------------------------------------------------
1248 // ----------------------------------------------------------------------------
1250 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1252 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
1258 This is a bit complicated, so let me explain it in details. All lines that
1259 were read from the local file (the only one we will ever modify) are stored
1260 in a (doubly) linked list. Our problem is to know at which position in this
1261 list should we insert the new entries/subgroups. To solve it we keep three
1262 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1264 m_pLine points to the line containing "[group_name]"
1265 m_pLastEntry points to the last entry of this group in the local file.
1266 m_pLastGroup subgroup
1268 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1269 from the local file, the corresponding variable is set. However, if the group
1270 was read from the global file and then modified or created by the application
1271 these variables are still NULL and we need to create the corresponding lines.
1272 See the following functions (and comments preceding them) for the details of
1275 Also, when our last entry/group are deleted we need to find the new last
1276 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1277 of lines until it either founds an entry/subgroup (and this is the new last
1278 element) or the m_pLine of the group, in which case there are no more entries
1279 (or subgroups) left and m_pLast<element> becomes NULL.
1281 NB: This last problem could be avoided for entries if we added new entries
1282 immediately after m_pLine, but in this case the entries would appear
1283 backwards in the config file (OTOH, it's not that important) and as we
1284 would still need to do it for the subgroups the code wouldn't have been
1285 significantly less complicated.
1288 // Return the line which contains "[our name]". If we're still not in the list,
1289 // add our line to it immediately after the last line of our parent group if we
1290 // have it or in the very beginning if we're the root group.
1291 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1293 if ( m_pLine
== NULL
) {
1294 wxFileConfigGroup
*pParent
= Parent();
1296 // this group wasn't present in local config file, add it now
1297 if ( pParent
!= NULL
) {
1298 wxString strFullName
;
1299 strFullName
<< wxT("[")
1301 << FilterOutEntryName(GetFullName().c_str() + 1)
1303 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1304 pParent
->GetLastGroupLine());
1305 pParent
->SetLastGroup(this); // we're surely after all the others
1308 // we return NULL, so that LineListInsert() will insert us in the
1316 // Return the last line belonging to the subgroups of this group (after which
1317 // we can add a new subgroup), if we don't have any subgroups or entries our
1318 // last line is the group line (m_pLine) itself.
1319 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1321 // if we have any subgroups, our last line is the last line of the last
1323 if ( m_pLastGroup
!= NULL
) {
1324 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1326 wxASSERT( pLine
!= NULL
); // last group must have !NULL associated line
1330 // no subgroups, so the last line is the line of thelast entry (if any)
1331 return GetLastEntryLine();
1334 // return the last line belonging to the entries of this group (after which
1335 // we can add a new entry), if we don't have any entries we will add the new
1336 // one immediately after the group line itself.
1337 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1339 if ( m_pLastEntry
!= NULL
) {
1340 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1342 wxASSERT( pLine
!= NULL
); // last entry must have !NULL associated line
1346 // no entries: insert after the group header
1347 return GetGroupLine();
1350 // ----------------------------------------------------------------------------
1352 // ----------------------------------------------------------------------------
1354 void wxFileConfigGroup::Rename(const wxString
& newName
)
1356 m_strName
= newName
;
1358 wxFileConfigLineList
*line
= GetGroupLine();
1359 wxString strFullName
;
1360 strFullName
<< wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1361 line
->SetText(strFullName
);
1366 wxString
wxFileConfigGroup::GetFullName() const
1369 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1374 // ----------------------------------------------------------------------------
1376 // ----------------------------------------------------------------------------
1378 // use binary search because the array is sorted
1380 wxFileConfigGroup::FindEntry(const wxChar
*szName
) const
1384 hi
= m_aEntries
.Count();
1386 wxFileConfigEntry
*pEntry
;
1390 pEntry
= m_aEntries
[i
];
1392 #if wxCONFIG_CASE_SENSITIVE
1393 res
= wxStrcmp(pEntry
->Name(), szName
);
1395 res
= wxStricmp(pEntry
->Name(), szName
);
1410 wxFileConfigGroup::FindSubgroup(const wxChar
*szName
) const
1414 hi
= m_aSubgroups
.Count();
1416 wxFileConfigGroup
*pGroup
;
1420 pGroup
= m_aSubgroups
[i
];
1422 #if wxCONFIG_CASE_SENSITIVE
1423 res
= wxStrcmp(pGroup
->Name(), szName
);
1425 res
= wxStricmp(pGroup
->Name(), szName
);
1439 // ----------------------------------------------------------------------------
1440 // create a new item
1441 // ----------------------------------------------------------------------------
1443 // create a new entry and add it to the current group
1445 wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1447 wxASSERT( FindEntry(strName
) == NULL
);
1449 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1450 m_aEntries
.Add(pEntry
);
1455 // create a new group and add it to the current group
1457 wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1459 wxASSERT( FindSubgroup(strName
) == NULL
);
1461 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1462 m_aSubgroups
.Add(pGroup
);
1467 // ----------------------------------------------------------------------------
1469 // ----------------------------------------------------------------------------
1472 The delete operations are _very_ slow if we delete the last item of this
1473 group (see comments before GetXXXLineXXX functions for more details),
1474 so it's much better to start with the first entry/group if we want to
1475 delete several of them.
1478 bool wxFileConfigGroup::DeleteSubgroupByName(const wxChar
*szName
)
1480 return DeleteSubgroup(FindSubgroup(szName
));
1483 // doesn't delete the subgroup itself, but does remove references to it from
1484 // all other data structures (and normally the returned pointer should be
1485 // deleted a.s.a.p. because there is nothing much to be done with it anyhow)
1486 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1488 wxCHECK( pGroup
!= NULL
, FALSE
); // deleting non existing group?
1490 // delete all entries
1491 size_t nCount
= pGroup
->m_aEntries
.Count();
1492 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ ) {
1493 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1494 if ( pLine
!= NULL
)
1495 m_pConfig
->LineListRemove(pLine
);
1498 // and subgroups of this sungroup
1499 nCount
= pGroup
->m_aSubgroups
.Count();
1500 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ ) {
1501 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1504 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1505 if ( pLine
!= NULL
) {
1506 // notice that we may do this test inside the previous "if" because the
1507 // last entry's line is surely !NULL
1508 if ( pGroup
== m_pLastGroup
) {
1509 // our last entry is being deleted - find the last one which stays
1510 wxASSERT( m_pLine
!= NULL
); // we have a subgroup with !NULL pLine...
1512 // go back until we find a subgroup or reach the group's line
1513 wxFileConfigGroup
*pNewLast
= NULL
;
1514 size_t n
, nSubgroups
= m_aSubgroups
.Count();
1515 wxFileConfigLineList
*pl
;
1516 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1517 // is it our subgroup?
1518 for ( n
= 0; (pNewLast
== NULL
) && (n
< nSubgroups
); n
++ ) {
1519 // do _not_ call GetGroupLine! we don't want to add it to the local
1520 // file if it's not already there
1521 if ( m_aSubgroups
[n
]->m_pLine
== m_pLine
)
1522 pNewLast
= m_aSubgroups
[n
];
1525 if ( pNewLast
!= NULL
) // found?
1529 if ( pl
== m_pLine
) {
1530 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1532 // we've reached the group line without finding any subgroups
1533 m_pLastGroup
= NULL
;
1536 m_pLastGroup
= pNewLast
;
1539 m_pConfig
->LineListRemove(pLine
);
1544 m_aSubgroups
.Remove(pGroup
);
1550 bool wxFileConfigGroup::DeleteEntry(const wxChar
*szName
)
1552 wxFileConfigEntry
*pEntry
= FindEntry(szName
);
1553 wxCHECK( pEntry
!= NULL
, FALSE
); // deleting non existing item?
1555 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1556 if ( pLine
!= NULL
) {
1557 // notice that we may do this test inside the previous "if" because the
1558 // last entry's line is surely !NULL
1559 if ( pEntry
== m_pLastEntry
) {
1560 // our last entry is being deleted - find the last one which stays
1561 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1563 // go back until we find another entry or reach the group's line
1564 wxFileConfigEntry
*pNewLast
= NULL
;
1565 size_t n
, nEntries
= m_aEntries
.Count();
1566 wxFileConfigLineList
*pl
;
1567 for ( pl
= pLine
->Prev(); pl
!= m_pLine
; pl
= pl
->Prev() ) {
1568 // is it our subgroup?
1569 for ( n
= 0; (pNewLast
== NULL
) && (n
< nEntries
); n
++ ) {
1570 if ( m_aEntries
[n
]->GetLine() == m_pLine
)
1571 pNewLast
= m_aEntries
[n
];
1574 if ( pNewLast
!= NULL
) // found?
1578 if ( pl
== m_pLine
) {
1579 wxASSERT( !pNewLast
); // how comes it has the same line as we?
1581 // we've reached the group line without finding any subgroups
1582 m_pLastEntry
= NULL
;
1585 m_pLastEntry
= pNewLast
;
1588 m_pConfig
->LineListRemove(pLine
);
1591 // we must be written back for the changes to be saved
1594 m_aEntries
.Remove(pEntry
);
1600 // ----------------------------------------------------------------------------
1602 // ----------------------------------------------------------------------------
1603 void wxFileConfigGroup::SetDirty()
1606 if ( Parent() != NULL
) // propagate upwards
1607 Parent()->SetDirty();
1610 // ============================================================================
1611 // wxFileConfig::wxFileConfigEntry
1612 // ============================================================================
1614 // ----------------------------------------------------------------------------
1616 // ----------------------------------------------------------------------------
1617 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1618 const wxString
& strName
,
1620 : m_strName(strName
)
1622 wxASSERT( !strName
.IsEmpty() );
1624 m_pParent
= pParent
;
1629 m_bHasValue
= FALSE
;
1631 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1633 m_strName
.erase(0, 1); // remove first character
1636 // ----------------------------------------------------------------------------
1638 // ----------------------------------------------------------------------------
1640 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1642 if ( m_pLine
!= NULL
) {
1643 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1644 Name().c_str(), m_pParent
->GetFullName().c_str());
1648 Group()->SetLastEntry(this);
1651 // second parameter is FALSE if we read the value from file and prevents the
1652 // entry from being marked as 'dirty'
1653 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1655 if ( bUser
&& IsImmutable() ) {
1656 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
1661 // do nothing if it's the same value: but don't test for it if m_bHasValue
1662 // hadn't been set yet or we'd never write empty values to the file
1663 if ( m_bHasValue
&& strValue
== m_strValue
)
1667 m_strValue
= strValue
;
1670 wxString strVal
= FilterOutValue(strValue
);
1672 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strVal
;
1674 if ( m_pLine
!= NULL
) {
1675 // entry was read from the local config file, just modify the line
1676 m_pLine
->SetText(strLine
);
1679 // add a new line to the file
1680 wxASSERT( m_nLine
== wxNOT_FOUND
); // consistency check
1682 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1683 Group()->GetLastEntryLine());
1684 Group()->SetLastEntry(this);
1691 void wxFileConfigEntry::SetDirty()
1694 Group()->SetDirty();
1697 // ============================================================================
1699 // ============================================================================
1701 // ----------------------------------------------------------------------------
1702 // compare functions for array sorting
1703 // ----------------------------------------------------------------------------
1705 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1707 #if wxCONFIG_CASE_SENSITIVE
1708 return wxStrcmp(p1
->Name(), p2
->Name());
1710 return wxStricmp(p1
->Name(), p2
->Name());
1714 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1716 #if wxCONFIG_CASE_SENSITIVE
1717 return wxStrcmp(p1
->Name(), p2
->Name());
1719 return wxStricmp(p1
->Name(), p2
->Name());
1723 // ----------------------------------------------------------------------------
1725 // ----------------------------------------------------------------------------
1727 // undo FilterOutValue
1728 static wxString
FilterInValue(const wxString
& str
)
1731 strResult
.Alloc(str
.Len());
1733 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1735 for ( size_t n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1736 if ( str
[n
] == wxT('\\') ) {
1737 switch ( str
[++n
] ) {
1739 strResult
+= wxT('\n');
1743 strResult
+= wxT('\r');
1747 strResult
+= wxT('\t');
1751 strResult
+= wxT('\\');
1755 strResult
+= wxT('"');
1760 if ( str
[n
] != wxT('"') || !bQuoted
)
1761 strResult
+= str
[n
];
1762 else if ( n
!= str
.Len() - 1 ) {
1763 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1766 //else: it's the last quote of a quoted string, ok
1773 // quote the string before writing it to file
1774 static wxString
FilterOutValue(const wxString
& str
)
1780 strResult
.Alloc(str
.Len());
1782 // quoting is necessary to preserve spaces in the beginning of the string
1783 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
1786 strResult
+= wxT('"');
1789 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
1812 //else: fall through
1815 strResult
+= str
[n
];
1816 continue; // nothing special to do
1819 // we get here only for special characters
1820 strResult
<< wxT('\\') << c
;
1824 strResult
+= wxT('"');
1829 // undo FilterOutEntryName
1830 static wxString
FilterInEntryName(const wxString
& str
)
1833 strResult
.Alloc(str
.Len());
1835 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
1836 if ( *pc
== wxT('\\') )
1845 // sanitize entry or group name: insert '\\' before any special characters
1846 static wxString
FilterOutEntryName(const wxString
& str
)
1849 strResult
.Alloc(str
.Len());
1851 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
1854 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1855 // which will probably never have special meaning
1856 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1857 // should *not* be quoted
1858 if ( !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%"), c
) && ((c
& 0x80) == 0) )
1859 strResult
+= wxT('\\');
1867 // we can't put ?: in the ctor initializer list because it confuses some
1868 // broken compilers (Borland C++)
1869 static wxString
GetAppName(const wxString
& appName
)
1871 if ( !appName
&& wxTheApp
)
1872 return wxTheApp
->GetAppName();
1877 #endif // wxUSE_CONFIG