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 // ----------------------------------------------------------------------------
23 // ----------------------------------------------------------------------------
24 #include "wx/wxprec.h"
31 #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 // _WINDOWS_ is defined when windows.h is included,
43 // __WXMSW__ is defined for MS Windows compilation
44 #if defined(__WXMSW__) && !defined(_WINDOWS_)
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
54 #define CONST_CAST ((wxFileConfig *)this)->
56 // ----------------------------------------------------------------------------
57 // global functions declarations
58 // ----------------------------------------------------------------------------
60 // is 'c' a valid character in group name?
61 // NB: APPCONF_IMMUTABLE_PREFIX and APPCONF_PATH_SEPARATOR must be valid chars,
62 // but _not_ ']' (group name delimiter)
63 inline bool IsValid(char c
) { return isalnum(c
) || strchr("@_/-!.*%", c
); }
65 // compare functions for sorting the arrays
66 static int CompareEntries(wxFileConfig::ConfigEntry
*p1
,
67 wxFileConfig::ConfigEntry
*p2
);
68 static int CompareGroups(wxFileConfig::ConfigGroup
*p1
,
69 wxFileConfig::ConfigGroup
*p2
);
72 static wxString
FilterIn(const wxString
& str
);
73 static wxString
FilterOut(const wxString
& str
);
75 // ============================================================================
77 // ============================================================================
79 // ----------------------------------------------------------------------------
81 // ----------------------------------------------------------------------------
82 wxString
wxFileConfig::GetGlobalFileName(const char *szFile
)
86 bool bNoExt
= strchr(szFile
, '.') == NULL
;
89 str
<< "/etc/" << szFile
;
97 char szWinDir
[_MAX_PATH
];
98 ::GetWindowsDirectory(szWinDir
, _MAX_PATH
);
99 str
<< szWinDir
<< "\\" << szFile
;
107 wxString
wxFileConfig::GetLocalFileName(const char *szFile
)
112 const char *szHome
= getenv("HOME");
113 if ( szHome
== NULL
) {
115 wxLogWarning(_("can't find user's HOME, using current directory."));
118 str
<< szHome
<< "/." << szFile
;
121 const char *szHome
= getenv("HOMEDRIVE");
122 if ( szHome
!= NULL
)
124 szHome
= getenv("HOMEPATH");
125 if ( szHome
!= NULL
)
128 if ( strchr(szFile
, '.') == NULL
)
131 // Win16 has no idea about home, so use the current directory instead
132 str
<< ".\\" << szFile
;
139 // ----------------------------------------------------------------------------
141 // ----------------------------------------------------------------------------
143 void wxFileConfig::Init()
146 m_pRootGroup
= new ConfigGroup(NULL
, "", this);
154 wxFileConfig::wxFileConfig(const wxString
& strLocal
, const wxString
& strGlobal
)
155 : m_strLocalFile(strLocal
), m_strGlobalFile(strGlobal
)
159 // it's not an error if (one of the) file(s) doesn't exist
161 // parse the global file
162 if ( !strGlobal
.IsEmpty() ) {
163 if ( wxFile::Exists(strGlobal
) ) {
164 wxTextFile
fileGlobal(strGlobal
);
166 if ( fileGlobal
.Open() ) {
167 Parse(fileGlobal
, FALSE
/* global */);
171 wxLogWarning(_("can't open global configuration file '%s'."),
176 // parse the local file
177 if ( wxFile::Exists(strLocal
) ) {
178 wxTextFile
fileLocal(strLocal
);
179 if ( fileLocal
.Open() ) {
180 Parse(fileLocal
, TRUE
/* local */);
184 wxLogWarning(_("can't open user configuration file '%s'."),
189 wxFileConfig::~wxFileConfig()
194 LineList
*pCur
= m_linesHead
;
195 while ( pCur
!= NULL
) {
196 LineList
*pNext
= pCur
->Next();
202 // ----------------------------------------------------------------------------
203 // parse a config file
204 // ----------------------------------------------------------------------------
206 void wxFileConfig::Parse(wxTextFile
& file
, bool bLocal
)
212 uint nLineCount
= file
.GetLineCount();
213 for ( uint n
= 0; n
< nLineCount
; n
++ ) {
216 // add the line to linked list
218 LineListAppend(strLine
);
220 // skip leading spaces
221 for ( pStart
= strLine
; isspace(*pStart
); pStart
++ )
224 // skip blank/comment lines
225 if ( *pStart
== '\0'|| *pStart
== ';' || *pStart
== '#' )
228 if ( *pStart
== '[' ) { // a new group
231 while ( *++pEnd
!= ']' ) {
232 if ( !IsValid(*pEnd
) && *pEnd
!= ' ' ) // allow spaces in group names
236 if ( *pEnd
!= ']' ) {
237 wxLogError(_("file '%s': unexpected character %c at line %d."),
238 file
.GetName(), *pEnd
, n
+ 1);
239 continue; // skip this line
242 // group name here is always considered as abs path
245 strGroup
<< APPCONF_PATH_SEPARATOR
<< wxString(pStart
, pEnd
- pStart
);
247 // will create it if doesn't yet exist
251 m_pCurrentGroup
->SetLine(m_linesTail
);
253 // check that there is nothing except comments left on this line
255 while ( *++pEnd
!= '\0' && bCont
) {
264 // ignore whitespace ('\n' impossible here)
268 wxLogWarning(_("file '%s', line %d: '%s' "
269 "ignored after group header."),
270 file
.GetName(), n
+ 1, pEnd
);
276 const char *pEnd
= pStart
;
277 while ( IsValid(*pEnd
) )
280 wxString
strKey(pStart
, pEnd
);
283 while ( isspace(*pEnd
) )
286 if ( *pEnd
++ != '=' ) {
287 wxLogError(_("file '%s', line %d: '=' expected."),
288 file
.GetName(), n
+ 1);
291 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
293 if ( pEntry
== NULL
) {
295 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
298 pEntry
->SetLine(m_linesTail
);
301 if ( bLocal
&& pEntry
->IsImmutable() ) {
302 // immutable keys can't be changed by user
303 wxLogWarning(_("file '%s', line %d: value for "
304 "immutable key '%s' ignored."),
305 file
.GetName(), n
+ 1, strKey
.c_str());
308 // the condition below catches the cases (a) and (b) but not (c):
309 // (a) global key found second time in global file
310 // (b) key found second (or more) time in local file
311 // (c) key from global file now found in local one
312 // which is exactly what we want.
313 else if ( !bLocal
|| pEntry
->IsLocal() ) {
314 wxLogWarning(_("file '%s', line %d: key '%s' was first "
315 "found at line %d."),
316 file
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
319 pEntry
->SetLine(m_linesTail
);
324 while ( isspace(*pEnd
) )
327 pEntry
->SetValue(FilterIn(pEnd
), FALSE
/* read from file */);
333 // ----------------------------------------------------------------------------
335 // ----------------------------------------------------------------------------
337 void wxFileConfig::SetRootPath()
340 m_pCurrentGroup
= m_pRootGroup
;
343 void wxFileConfig::SetPath(const wxString
& strPath
)
345 wxArrayString aParts
;
347 if ( strPath
.IsEmpty() ) {
352 if ( strPath
[0] == APPCONF_PATH_SEPARATOR
) {
354 wxSplitPath(aParts
, strPath
);
357 // relative path, combine with current one
358 wxString strFullPath
= m_strPath
;
359 strFullPath
<< APPCONF_PATH_SEPARATOR
<< strPath
;
360 wxSplitPath(aParts
, strFullPath
);
363 // change current group
365 m_pCurrentGroup
= m_pRootGroup
;
366 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
367 ConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
368 if ( pNextGroup
== NULL
)
369 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
370 m_pCurrentGroup
= pNextGroup
;
373 // recombine path parts in one variable
375 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
376 m_strPath
<< APPCONF_PATH_SEPARATOR
<< aParts
[n
];
380 // ----------------------------------------------------------------------------
382 // ----------------------------------------------------------------------------
384 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
)
387 return GetNextGroup(str
, lIndex
);
390 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
)
392 if ( uint(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
393 str
= m_pCurrentGroup
->Groups()[lIndex
++]->Name();
400 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
)
403 return GetNextEntry(str
, lIndex
);
406 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
)
408 if ( uint(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
409 str
= m_pCurrentGroup
->Entries()[lIndex
++]->Name();
416 uint
wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
418 uint n
= m_pCurrentGroup
->Entries().Count();
420 ConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
421 uint nSubgroups
= m_pCurrentGroup
->Groups().Count();
422 for ( uint nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
423 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
424 n
+= GetNumberOfEntries(TRUE
);
425 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
432 uint
wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
434 uint n
= m_pCurrentGroup
->Groups().Count();
436 ConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
437 uint nSubgroups
= m_pCurrentGroup
->Groups().Count();
438 for ( uint nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
439 CONST_CAST m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
440 n
+= GetNumberOfGroups(TRUE
);
441 CONST_CAST m_pCurrentGroup
= pOldCurrentGroup
;
448 // ----------------------------------------------------------------------------
449 // tests for existence
450 // ----------------------------------------------------------------------------
452 bool wxFileConfig::HasGroup(const wxString
& strName
) const
454 PathChanger
path(this, strName
);
456 ConfigGroup
*pGroup
= m_pCurrentGroup
->FindSubgroup(path
.Name());
457 return pGroup
!= NULL
;
460 bool wxFileConfig::HasEntry(const wxString
& strName
) const
462 PathChanger
path(this, strName
);
464 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
465 return pEntry
!= NULL
;
468 // ----------------------------------------------------------------------------
470 // ----------------------------------------------------------------------------
472 bool wxFileConfig::Read(wxString
*pstr
,
474 const char *szDefault
) const
476 PathChanger
path(this, szKey
);
478 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
479 if (pEntry
== NULL
) {
480 *pstr
= ExpandEnvVars(szDefault
);
484 *pstr
= ExpandEnvVars(pEntry
->Value());
489 const char *wxFileConfig::Read(const char *szKey
,
490 const char *szDefault
) const
492 static wxString s_str
;
493 Read(&s_str
, szKey
, szDefault
);
495 return s_str
.c_str();
498 bool wxFileConfig::Read(long *pl
, const char *szKey
, long lDefault
) const
501 if ( Read(&str
, szKey
) ) {
511 bool wxFileConfig::Write(const char *szKey
, const char *szValue
)
513 PathChanger
path(this, szKey
);
515 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
516 if ( pEntry
== NULL
)
517 pEntry
= m_pCurrentGroup
->AddEntry(path
.Name());
518 pEntry
->SetValue(szValue
);
523 bool wxFileConfig::Write(const char *szKey
, long lValue
)
525 // ltoa() is not ANSI :-(
526 char szBuf
[40]; // should be good for sizeof(long) <= 16 (128 bits)
527 sprintf(szBuf
, "%ld", lValue
);
528 return Write(szKey
, szBuf
);
531 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
533 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() )
536 wxTempFile
file(m_strLocalFile
);
538 if ( !file
.IsOpened() ) {
539 wxLogError(_("can't open user configuration file."));
543 // write all strings to file
544 for ( LineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
545 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
546 wxLogError(_("can't write user configuration file."));
551 return file
.Commit();
554 // ----------------------------------------------------------------------------
555 // delete groups/entries
556 // ----------------------------------------------------------------------------
558 bool wxFileConfig::DeleteEntry(const char *szKey
, bool bGroupIfEmptyAlso
)
560 PathChanger
path(this, szKey
);
562 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
565 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
566 if ( m_pCurrentGroup
!= m_pRootGroup
) {
567 ConfigGroup
*pGroup
= m_pCurrentGroup
;
568 SetPath(".."); // changes m_pCurrentGroup!
569 m_pCurrentGroup
->DeleteSubgroup(pGroup
->Name());
571 //else: never delete the root group
577 bool wxFileConfig::DeleteGroup(const char *szKey
)
579 PathChanger
path(this, szKey
);
581 return m_pCurrentGroup
->DeleteSubgroup(path
.Name());
584 bool wxFileConfig::DeleteAll()
586 const char *szFile
= m_strLocalFile
;
590 if ( remove(szFile
) == -1 )
591 wxLogSysError(_("can't delete user configuration file '%s'"), szFile
);
593 szFile
= m_strGlobalFile
;
594 if ( remove(szFile
) )
595 wxLogSysError(_("can't delete system configuration file '%s'"), szFile
);
600 // ----------------------------------------------------------------------------
601 // linked list functions
602 // ----------------------------------------------------------------------------
604 // append a new line to the end of the list
605 wxFileConfig::LineList
*wxFileConfig::LineListAppend(const wxString
& str
)
607 LineList
*pLine
= new LineList(str
);
609 if ( m_linesTail
== NULL
) {
615 m_linesTail
->SetNext(pLine
);
616 pLine
->SetPrev(m_linesTail
);
623 // insert a new line after the given one or in the very beginning if !pLine
624 wxFileConfig::LineList
*wxFileConfig::LineListInsert(const wxString
& str
,
627 if ( pLine
== m_linesTail
)
628 return LineListAppend(str
);
630 LineList
*pNewLine
= new LineList(str
);
631 if ( pLine
== NULL
) {
632 // prepend to the list
633 pNewLine
->SetNext(m_linesHead
);
634 m_linesHead
->SetPrev(pNewLine
);
635 m_linesHead
= pNewLine
;
638 // insert before pLine
639 LineList
*pNext
= pLine
->Next();
640 pNewLine
->SetNext(pNext
);
641 pNewLine
->SetPrev(pLine
);
642 pNext
->SetPrev(pNewLine
);
643 pLine
->SetNext(pNewLine
);
649 void wxFileConfig::LineListRemove(LineList
*pLine
)
651 LineList
*pPrev
= pLine
->Prev(),
652 *pNext
= pLine
->Next();
653 if ( pPrev
== NULL
) {
654 // deleting the first entry
658 // not the first entry
659 pPrev
->SetNext(pNext
);
662 pNext
->SetPrev(pPrev
);
667 bool wxFileConfig::LineListIsEmpty()
669 return m_linesHead
== NULL
;
672 // ============================================================================
673 // wxFileConfig::ConfigGroup
674 // ============================================================================
676 // ----------------------------------------------------------------------------
678 // ----------------------------------------------------------------------------
681 wxFileConfig::ConfigGroup::ConfigGroup(wxFileConfig::ConfigGroup
*pParent
,
682 const wxString
& strName
,
683 wxFileConfig
*pConfig
)
684 : m_aEntries(CompareEntries
),
685 m_aSubgroups(CompareGroups
),
696 // dtor deletes all children
697 wxFileConfig::ConfigGroup::~ConfigGroup()
700 uint n
, nCount
= m_aEntries
.Count();
701 for ( n
= 0; n
< nCount
; n
++ )
702 delete m_aEntries
[n
];
705 nCount
= m_aSubgroups
.Count();
706 for ( n
= 0; n
< nCount
; n
++ )
707 delete m_aSubgroups
[n
];
710 // ----------------------------------------------------------------------------
712 // ----------------------------------------------------------------------------
714 void wxFileConfig::ConfigGroup::SetLine(LineList
*pLine
)
716 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
721 // return the line which contains "[our name]"
722 wxFileConfig::LineList
*wxFileConfig::ConfigGroup::GetGroupLine()
724 if ( m_pLine
== NULL
) {
725 // this group wasn't present in local config file, add it now
726 if ( Parent() != NULL
) {
727 wxString strFullName
;
728 strFullName
<< "[" << GetFullName().c_str() + 1 << "]"; // +1: no '/'
729 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
730 Parent()->GetLastGroupLine());
731 Parent()->SetLastGroup(this);
734 // we return NULL, so that LineListInsert() will insert us in the
742 // return the last line belonging to the subgroups of this group
743 // (after which we can add a new subgroup)
744 wxFileConfig::LineList
*wxFileConfig::ConfigGroup::GetLastGroupLine()
746 // if we have any subgroups, our last line is the last line of the last
748 if ( m_pLastGroup
!= NULL
)
749 return m_pLastGroup
->GetLastGroupLine();
751 // if we have any entries, our last line is the last entry
752 if ( m_pLastEntry
!= NULL
)
753 return m_pLastEntry
->GetLine();
755 // nothing at all: last line is the first one
756 return GetGroupLine();
759 // return the last line belonging to the entries of this group
760 // (after which we can add a new entry)
761 wxFileConfig::LineList
*wxFileConfig::ConfigGroup::GetLastEntryLine()
763 if ( m_pLastEntry
!= NULL
) {
764 wxFileConfig::LineList
*pLine
= m_pLastEntry
->GetLine();
766 wxASSERT( pLine
!= NULL
); // last entry must have !NULL associated line
770 // no entrues: insert after the group header
771 return GetGroupLine();
774 // ----------------------------------------------------------------------------
776 // ----------------------------------------------------------------------------
778 wxString
wxFileConfig::ConfigGroup::GetFullName() const
781 return Parent()->GetFullName() + APPCONF_PATH_SEPARATOR
+ Name();
786 // ----------------------------------------------------------------------------
788 // ----------------------------------------------------------------------------
790 // use binary search because the array is sorted
791 wxFileConfig::ConfigEntry
*
792 wxFileConfig::ConfigGroup::FindEntry(const char *szName
) const
796 hi
= m_aEntries
.Count();
798 wxFileConfig::ConfigEntry
*pEntry
;
802 pEntry
= m_aEntries
[i
];
804 #if APPCONF_CASE_SENSITIVE
805 res
= strcmp(pEntry
->Name(), szName
);
807 res
= Stricmp(pEntry
->Name(), szName
);
821 wxFileConfig::ConfigGroup
*
822 wxFileConfig::ConfigGroup::FindSubgroup(const char *szName
) const
826 hi
= m_aSubgroups
.Count();
828 wxFileConfig::ConfigGroup
*pGroup
;
832 pGroup
= m_aSubgroups
[i
];
834 #if APPCONF_CASE_SENSITIVE
835 res
= strcmp(pGroup
->Name(), szName
);
837 res
= Stricmp(pGroup
->Name(), szName
);
851 // ----------------------------------------------------------------------------
853 // ----------------------------------------------------------------------------
855 // create a new entry and add it to the current group
856 wxFileConfig::ConfigEntry
*
857 wxFileConfig::ConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
859 wxASSERT( FindEntry(strName
) == NULL
);
861 ConfigEntry
*pEntry
= new ConfigEntry(this, strName
, nLine
);
862 m_aEntries
.Add(pEntry
);
867 // create a new group and add it to the current group
868 wxFileConfig::ConfigGroup
*
869 wxFileConfig::ConfigGroup::AddSubgroup(const wxString
& strName
)
871 wxASSERT( FindSubgroup(strName
) == NULL
);
873 ConfigGroup
*pGroup
= new ConfigGroup(this, strName
, m_pConfig
);
874 m_aSubgroups
.Add(pGroup
);
879 // ----------------------------------------------------------------------------
881 // ----------------------------------------------------------------------------
883 bool wxFileConfig::ConfigGroup::DeleteSubgroup(const char *szName
)
885 ConfigGroup
*pGroup
= FindSubgroup(szName
);
886 wxCHECK( pGroup
!= NULL
, FALSE
); // deleting non existing group?
888 // delete all entries
889 uint nCount
= pGroup
->m_aEntries
.Count();
890 for ( uint nEntry
= 0; nEntry
< nCount
; nEntry
++ ) {
891 LineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
893 m_pConfig
->LineListRemove(pLine
);
896 LineList
*pLine
= pGroup
->m_pLine
;
898 m_pConfig
->LineListRemove(pLine
);
902 m_aSubgroups
.Remove(pGroup
);
908 bool wxFileConfig::ConfigGroup::DeleteEntry(const char *szName
)
910 ConfigEntry
*pEntry
= FindEntry(szName
);
911 if ( pEntry
== NULL
)
914 LineList
*pLine
= pEntry
->GetLine();
916 m_pConfig
->LineListRemove(pLine
);
920 m_aEntries
.Remove(pEntry
);
926 // ----------------------------------------------------------------------------
928 // ----------------------------------------------------------------------------
929 void wxFileConfig::ConfigGroup::SetDirty()
932 if ( Parent() != NULL
) // propagate upwards
933 Parent()->SetDirty();
936 // ============================================================================
937 // wxFileConfig::ConfigEntry
938 // ============================================================================
940 // ----------------------------------------------------------------------------
942 // ----------------------------------------------------------------------------
943 wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup
*pParent
,
944 const wxString
& strName
,
954 m_bImmutable
= strName
[0] == APPCONF_IMMUTABLE_PREFIX
;
956 m_strName
.erase(0, 1); // remove first character
959 // ----------------------------------------------------------------------------
961 // ----------------------------------------------------------------------------
963 void wxFileConfig::ConfigEntry::SetLine(LineList
*pLine
)
965 if ( m_pLine
!= NULL
) {
966 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
967 Name().c_str(), m_pParent
->GetFullName().c_str());
971 Group()->SetLastEntry(this);
974 // second parameter is FALSE if we read the value from file and prevents the
975 // entry from being marked as 'dirty'
976 void wxFileConfig::ConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
978 if ( bUser
&& IsImmutable() ) {
979 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
984 // do nothing if it's the same value
985 if ( strValue
== m_strValue
)
988 m_strValue
= strValue
;
991 wxString strVal
= FilterOut(strValue
);
993 strLine
<< m_strName
<< " = " << strVal
;
995 if ( m_pLine
!= NULL
) {
996 // entry was read from the local config file, just modify the line
997 m_pLine
->SetText(strLine
);
1000 // add a new line to the file
1001 wxASSERT( m_nLine
== NOT_FOUND
); // consistency check
1003 m_pLine
= Group()->Config()->LineListInsert(strLine
,
1004 Group()->GetLastEntryLine());
1005 Group()->SetLastEntry(this);
1012 void wxFileConfig::ConfigEntry::SetDirty()
1015 Group()->SetDirty();
1018 // ============================================================================
1020 // ============================================================================
1022 // ----------------------------------------------------------------------------
1023 // compare functions for array sorting
1024 // ----------------------------------------------------------------------------
1026 int CompareEntries(wxFileConfig::ConfigEntry
*p1
,
1027 wxFileConfig::ConfigEntry
*p2
)
1029 #if APPCONF_CASE_SENSITIVE
1030 return strcmp(p1
->Name(), p2
->Name());
1032 return Stricmp(p1
->Name(), p2
->Name());
1036 int CompareGroups(wxFileConfig::ConfigGroup
*p1
,
1037 wxFileConfig::ConfigGroup
*p2
)
1039 #if APPCONF_CASE_SENSITIVE
1040 return strcmp(p1
->Name(), p2
->Name());
1042 return Stricmp(p1
->Name(), p2
->Name());
1046 // ----------------------------------------------------------------------------
1048 // ----------------------------------------------------------------------------
1051 wxString
FilterIn(const wxString
& str
)
1054 strResult
.Alloc(str
.Len());
1056 bool bQuoted
= !str
.IsEmpty() && str
[0] == '"';
1058 for ( uint n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1059 if ( str
[n
] == '\\' ) {
1060 switch ( str
[++n
] ) {
1083 if ( str
[n
] != '"' || !bQuoted
)
1084 strResult
+= str
[n
];
1085 else if ( n
!= str
.Len() - 1 ) {
1086 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1089 //else: it's the last quote of a quoted string, ok
1096 // quote the string before writing it to file
1097 wxString
FilterOut(const wxString
& str
)
1100 strResult
.Alloc(str
.Len());
1102 // quoting is necessary to preserve spaces in the beginning of the string
1103 bool bQuote
= isspace(str
[0]) || str
[0] == '"';
1109 for ( uint n
= 0; n
< str
.Len(); n
++ ) {
1130 //else: fall through
1133 strResult
+= str
[n
];
1134 continue; // nothing special to do
1137 // we get here only for special characters
1138 strResult
<< '\\' << c
;