1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
6 // Created: 07.04.98 (adapted from appconf.cpp)
7 // Copyright: (c) 1997 Karsten Ballueder & Vadim Zeitlin
8 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
12 // ----------------------------------------------------------------------------
14 // ----------------------------------------------------------------------------
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
23 #if wxUSE_CONFIG && wxUSE_FILECONFIG
26 #include "wx/dynarray.h"
27 #include "wx/string.h"
31 #include "wx/utils.h" // for wxGetHomeDir
33 #include "wx/stream.h"
34 #endif // wxUSE_STREAMS
38 #include "wx/textfile.h"
39 #include "wx/memtext.h"
40 #include "wx/config.h"
41 #include "wx/fileconf.h"
42 #include "wx/filefn.h"
44 #include "wx/base64.h"
46 #include "wx/stdpaths.h"
48 #if defined(__WINDOWS__)
49 #include "wx/msw/private.h"
59 // ----------------------------------------------------------------------------
61 // ----------------------------------------------------------------------------
67 #define FILECONF_TRACE_MASK wxT("fileconf")
69 // ----------------------------------------------------------------------------
70 // global functions declarations
71 // ----------------------------------------------------------------------------
73 // compare functions for sorting the arrays
74 static int LINKAGEMODE
CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
);
75 static int LINKAGEMODE
CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
);
78 static wxString
FilterInValue(const wxString
& str
);
79 static wxString
FilterOutValue(const wxString
& str
);
81 static wxString
FilterInEntryName(const wxString
& str
);
82 static wxString
FilterOutEntryName(const wxString
& str
);
84 // get the name to use in wxFileConfig ctor
85 static wxString
GetAppName(const wxString
& appname
);
87 // ============================================================================
89 // ============================================================================
91 // ----------------------------------------------------------------------------
92 // "template" array types
93 // ----------------------------------------------------------------------------
95 #ifdef WXMAKINGDLL_BASE
96 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
,
98 WX_DEFINE_SORTED_USER_EXPORTED_ARRAY(wxFileConfigGroup
*, ArrayGroups
,
101 WX_DEFINE_SORTED_ARRAY(wxFileConfigEntry
*, ArrayEntries
);
102 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
134 wxDECLARE_NO_COPY_CLASS(wxFileConfigLineList
);
137 // ----------------------------------------------------------------------------
138 // wxFileConfigEntry: a name/value pair
139 // ----------------------------------------------------------------------------
141 class wxFileConfigEntry
144 wxFileConfigGroup
*m_pParent
; // group that contains us
146 wxString m_strName
, // entry name
148 bool m_bImmutable
:1, // can be overridden locally?
149 m_bHasValue
:1; // set after first call to SetValue()
151 int m_nLine
; // used if m_pLine == NULL only
153 // pointer to our line in the linked list or NULL if it was found in global
154 // file (which we don't modify)
155 wxFileConfigLineList
*m_pLine
;
158 wxFileConfigEntry(wxFileConfigGroup
*pParent
,
159 const wxString
& strName
, int nLine
);
162 const wxString
& Name() const { return m_strName
; }
163 const wxString
& Value() const { return m_strValue
; }
164 wxFileConfigGroup
*Group() const { return m_pParent
; }
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);
173 void SetLine(wxFileConfigLineList
*pLine
);
175 wxDECLARE_NO_COPY_CLASS(wxFileConfigEntry
);
178 // ----------------------------------------------------------------------------
179 // wxFileConfigGroup: container of entries and other groups
180 // ----------------------------------------------------------------------------
182 class wxFileConfigGroup
185 wxFileConfig
*m_pConfig
; // config object we belong to
186 wxFileConfigGroup
*m_pParent
; // parent group (NULL for root group)
187 ArrayEntries m_aEntries
; // entries in this group
188 ArrayGroups m_aSubgroups
; // subgroups
189 wxString m_strName
; // group's name
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
);
198 void UpdateGroupAndSubgroupsLines();
202 wxFileConfigGroup(wxFileConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
204 // dtor deletes all entries and subgroups also
205 ~wxFileConfigGroup();
208 const wxString
& Name() const { return m_strName
; }
209 wxFileConfigGroup
*Parent() const { return m_pParent
; }
210 wxFileConfig
*Config() const { return m_pConfig
; }
212 const ArrayEntries
& Entries() const { return m_aEntries
; }
213 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
214 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
216 // find entry/subgroup (NULL if not found)
217 wxFileConfigGroup
*FindSubgroup(const wxString
& name
) const;
218 wxFileConfigEntry
*FindEntry (const wxString
& name
) const;
220 // delete entry/subgroup, return false if doesn't exist
221 bool DeleteSubgroupByName(const wxString
& name
);
222 bool DeleteEntry(const wxString
& name
);
224 // create new entry/subgroup returning pointer to newly created element
225 wxFileConfigGroup
*AddSubgroup(const wxString
& strName
);
226 wxFileConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= wxNOT_FOUND
);
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 // may be NULL for "/" only
239 wxFileConfigLineList
*GetLastEntryLine(); // after which our subgroups start
240 wxFileConfigLineList
*GetLastGroupLine(); // after which the next group starts
242 // called by entries/subgroups when they're created/deleted
243 void SetLastEntry(wxFileConfigEntry
*pEntry
);
244 void SetLastGroup(wxFileConfigGroup
*pGroup
)
245 { m_pLastGroup
= pGroup
; }
247 wxDECLARE_NO_COPY_CLASS(wxFileConfigGroup
);
250 // ============================================================================
252 // ============================================================================
254 // ----------------------------------------------------------------------------
256 // ----------------------------------------------------------------------------
258 // this function modifies in place the given wxFileName object if it doesn't
259 // already have an extension
261 // note that it's slightly misnamed under Mac as there it doesn't add an
262 // extension but modifies the file name instead, so you shouldn't suppose that
263 // fn.HasExt() is true after it returns
264 static void AddConfFileExtIfNeeded(wxFileName
& fn
)
268 #if defined( __WXMAC__ )
269 fn
.SetName(fn
.GetName() + wxT(" Preferences"));
270 #elif defined( __UNIX__ )
271 fn
.SetExt(wxT("conf"));
273 fn
.SetExt(wxT("ini"));
278 wxString
wxFileConfig::GetGlobalDir()
280 return wxStandardPaths::Get().GetConfigDir();
283 wxString
wxFileConfig::GetLocalDir(int style
)
287 wxStandardPathsBase
& stdp
= wxStandardPaths::Get();
289 // it so happens that user data directory is a subdirectory of user config
290 // directory on all supported platforms, which explains why we use it here
291 return style
& wxCONFIG_USE_SUBDIR
? stdp
.GetUserDataDir()
292 : stdp
.GetUserConfigDir();
295 wxFileName
wxFileConfig::GetGlobalFile(const wxString
& szFile
)
297 wxFileName
fn(GetGlobalDir(), szFile
);
299 AddConfFileExtIfNeeded(fn
);
304 wxFileName
wxFileConfig::GetLocalFile(const wxString
& szFile
, int style
)
306 wxFileName
fn(GetLocalDir(style
), szFile
);
308 #if defined( __UNIX__ ) && !defined( __WXMAC__ )
309 if ( !(style
& wxCONFIG_USE_SUBDIR
) )
311 // dot-files under Unix start with, well, a dot (but OTOH they usually
312 // don't have any specific extension)
313 fn
.SetName(wxT('.') + fn
.GetName());
315 else // we do append ".conf" extension to config files in subdirectories
316 #endif // defined( __UNIX__ ) && !defined( __WXMAC__ )
318 AddConfFileExtIfNeeded(fn
);
324 // ----------------------------------------------------------------------------
326 // ----------------------------------------------------------------------------
327 IMPLEMENT_ABSTRACT_CLASS(wxFileConfig
, wxConfigBase
)
329 void wxFileConfig::Init()
332 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
337 // It's not an error if (one of the) file(s) doesn't exist.
339 // parse the global file
340 if ( m_fnGlobalFile
.IsOk() && m_fnGlobalFile
.FileExists() )
342 wxTextFile
fileGlobal(m_fnGlobalFile
.GetFullPath());
344 if ( fileGlobal
.Open(*m_conv
/*ignored in ANSI build*/) )
346 Parse(fileGlobal
, false /* global */);
351 wxLogWarning(_("can't open global configuration file '%s'."), m_fnGlobalFile
.GetFullPath().c_str());
355 // parse the local file
356 if ( m_fnLocalFile
.IsOk() && m_fnLocalFile
.FileExists() )
358 wxTextFile
fileLocal(m_fnLocalFile
.GetFullPath());
359 if ( fileLocal
.Open(*m_conv
/*ignored in ANSI build*/) )
361 Parse(fileLocal
, true /* local */);
366 const wxString path
= m_fnLocalFile
.GetFullPath();
367 wxLogWarning(_("can't open user configuration file '%s'."),
370 if ( m_fnLocalFile
.FileExists() )
372 wxLogWarning(_("Changes won't be saved to avoid overwriting the existing file \"%s\""),
374 m_fnLocalFile
.Clear();
382 // constructor supports creation of wxFileConfig objects of any type
383 wxFileConfig::wxFileConfig(const wxString
& appName
, const wxString
& vendorName
,
384 const wxString
& strLocal
, const wxString
& strGlobal
,
386 const wxMBConv
& conv
)
387 : wxConfigBase(::GetAppName(appName
), vendorName
,
390 m_fnLocalFile(strLocal
),
391 m_fnGlobalFile(strGlobal
),
394 // Make up names for files if empty
395 if ( !m_fnLocalFile
.IsOk() && (style
& wxCONFIG_USE_LOCAL_FILE
) )
396 m_fnLocalFile
= GetLocalFile(GetAppName(), style
);
398 if ( !m_fnGlobalFile
.IsOk() && (style
& wxCONFIG_USE_GLOBAL_FILE
) )
399 m_fnGlobalFile
= GetGlobalFile(GetAppName());
401 // Check if styles are not supplied, but filenames are, in which case
402 // add the correct styles.
403 if ( m_fnLocalFile
.IsOk() )
404 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
406 if ( m_fnGlobalFile
.IsOk() )
407 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE
);
409 // if the path is not absolute, prepend the standard directory to it
410 // unless explicitly asked not to
411 if ( !(style
& wxCONFIG_USE_RELATIVE_PATH
) )
413 if ( m_fnLocalFile
.IsOk() )
414 m_fnLocalFile
.MakeAbsolute(GetLocalDir(style
));
416 if ( m_fnGlobalFile
.IsOk() )
417 m_fnGlobalFile
.MakeAbsolute(GetGlobalDir());
427 wxFileConfig::wxFileConfig(wxInputStream
&inStream
, const wxMBConv
& conv
)
428 : m_conv(conv
.Clone())
430 // always local_file when this constructor is called (?)
431 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE
);
434 m_pRootGroup
= new wxFileConfigGroup(NULL
, wxEmptyString
, this);
439 // read the entire stream contents in memory
441 static const size_t chunkLen
= 1024;
443 wxMemoryBuffer
buf(chunkLen
);
446 inStream
.Read(buf
.GetAppendBuf(chunkLen
), chunkLen
);
447 buf
.UngetAppendBuf(inStream
.LastRead());
449 const wxStreamError err
= inStream
.GetLastError();
451 if ( err
!= wxSTREAM_NO_ERROR
&& err
!= wxSTREAM_EOF
)
453 wxLogError(_("Error reading config options."));
457 while ( !inStream
.Eof() );
461 cbuf
= conv
.cMB2WC((char *)buf
.GetData(), buf
.GetDataLen() + 1, &len
);
462 if ( !len
&& buf
.GetDataLen() )
464 wxLogError(_("Failed to read config options."));
466 #else // !wxUSE_UNICODE
467 // no need for conversion
468 cbuf
= wxCharBuffer::CreateNonOwned((char *)buf
.GetData(), buf
.GetDataLen());
469 #endif // wxUSE_UNICODE/!wxUSE_UNICODE
471 // parse the input contents if there is anything to parse
474 // now break it into lines
475 wxMemoryText memText
;
476 for ( const wxChar
*s
= cbuf
; ; ++s
)
479 while ( *e
!= '\0' && *e
!= '\n' && *e
!= '\r' )
482 // notice that we throw away the original EOL kind here, maybe we
483 // should preserve it?
485 memText
.AddLine(wxString(s
, e
));
490 // skip the second EOL byte if it's a DOS one
491 if ( *e
== '\r' && e
[1] == '\n' )
497 // Finally we can parse it all.
498 Parse(memText
, true /* local */);
505 #endif // wxUSE_STREAMS
507 void wxFileConfig::CleanUp()
511 wxFileConfigLineList
*pCur
= m_linesHead
;
512 while ( pCur
!= NULL
) {
513 wxFileConfigLineList
*pNext
= pCur
->Next();
519 wxFileConfig::~wxFileConfig()
528 // ----------------------------------------------------------------------------
529 // parse a config file
530 // ----------------------------------------------------------------------------
532 void wxFileConfig::Parse(const wxTextBuffer
& buffer
, bool bLocal
)
535 size_t nLineCount
= buffer
.GetLineCount();
537 for ( size_t n
= 0; n
< nLineCount
; n
++ )
539 wxString strLine
= buffer
[n
];
540 // FIXME-UTF8: rewrite using iterators, without this buffer
541 wxWxCharBuffer
buf(strLine
.c_str());
542 const wxChar
*pStart
;
545 // add the line to linked list
547 LineListAppend(strLine
);
550 // skip leading spaces
551 for ( pStart
= buf
; wxIsspace(*pStart
); pStart
++ )
554 // skip blank/comment lines
555 if ( *pStart
== wxT('\0')|| *pStart
== wxT(';') || *pStart
== wxT('#') )
558 if ( *pStart
== wxT('[') ) { // a new group
561 while ( *++pEnd
!= wxT(']') ) {
562 if ( *pEnd
== wxT('\\') ) {
563 // the next char is escaped, so skip it even if it is ']'
567 if ( *pEnd
== wxT('\n') || *pEnd
== wxT('\0') ) {
568 // we reached the end of line, break out of the loop
573 if ( *pEnd
!= wxT(']') ) {
574 wxLogError(_("file '%s': unexpected character %c at line %d."),
575 buffer
.GetName(), *pEnd
, n
+ 1);
576 continue; // skip this line
579 // group name here is always considered as abs path
582 strGroup
<< wxCONFIG_PATH_SEPARATOR
583 << FilterInEntryName(wxString(pStart
, pEnd
- pStart
));
585 // will create it if doesn't yet exist
590 if ( m_pCurrentGroup
->Parent() )
591 m_pCurrentGroup
->Parent()->SetLastGroup(m_pCurrentGroup
);
592 m_pCurrentGroup
->SetLine(m_linesTail
);
595 // check that there is nothing except comments left on this line
597 while ( *++pEnd
!= wxT('\0') && bCont
) {
606 // ignore whitespace ('\n' impossible here)
610 wxLogWarning(_("file '%s', line %d: '%s' ignored after group header."),
611 buffer
.GetName(), n
+ 1, pEnd
);
618 while ( *pEnd
&& *pEnd
!= wxT('=') /* && !wxIsspace(*pEnd)*/ ) {
619 if ( *pEnd
== wxT('\\') ) {
620 // next character may be space or not - still take it because it's
621 // quoted (unless there is nothing)
624 // the error message will be given below anyhow
632 wxString
strKey(FilterInEntryName(wxString(pStart
, pEnd
).Trim()));
635 while ( wxIsspace(*pEnd
) )
638 if ( *pEnd
++ != wxT('=') ) {
639 wxLogError(_("file '%s', line %d: '=' expected."),
640 buffer
.GetName(), n
+ 1);
643 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
645 if ( pEntry
== NULL
) {
647 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
650 if ( bLocal
&& pEntry
->IsImmutable() ) {
651 // immutable keys can't be changed by user
652 wxLogWarning(_("file '%s', line %d: value for immutable key '%s' ignored."),
653 buffer
.GetName(), n
+ 1, strKey
.c_str());
656 // the condition below catches the cases (a) and (b) but not (c):
657 // (a) global key found second time in global file
658 // (b) key found second (or more) time in local file
659 // (c) key from global file now found in local one
660 // which is exactly what we want.
661 else if ( !bLocal
|| pEntry
->IsLocal() ) {
662 wxLogWarning(_("file '%s', line %d: key '%s' was first found at line %d."),
663 buffer
.GetName(), (int)n
+ 1, strKey
.c_str(), pEntry
->Line());
669 pEntry
->SetLine(m_linesTail
);
672 while ( wxIsspace(*pEnd
) )
675 wxString value
= pEnd
;
676 if ( !(GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
) )
677 value
= FilterInValue(value
);
679 pEntry
->SetValue(value
, false);
685 // ----------------------------------------------------------------------------
687 // ----------------------------------------------------------------------------
689 void wxFileConfig::SetRootPath()
692 m_pCurrentGroup
= m_pRootGroup
;
696 wxFileConfig::DoSetPath(const wxString
& strPath
, bool createMissingComponents
)
698 wxArrayString aParts
;
700 if ( strPath
.empty() ) {
705 if ( strPath
[0] == wxCONFIG_PATH_SEPARATOR
) {
707 wxSplitPath(aParts
, strPath
);
710 // relative path, combine with current one
711 wxString strFullPath
= m_strPath
;
712 strFullPath
<< wxCONFIG_PATH_SEPARATOR
<< strPath
;
713 wxSplitPath(aParts
, strFullPath
);
716 // change current group
718 m_pCurrentGroup
= m_pRootGroup
;
719 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
720 wxFileConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
721 if ( pNextGroup
== NULL
)
723 if ( !createMissingComponents
)
726 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
729 m_pCurrentGroup
= pNextGroup
;
732 // recombine path parts in one variable
734 for ( n
= 0; n
< aParts
.GetCount(); n
++ ) {
735 m_strPath
<< wxCONFIG_PATH_SEPARATOR
<< aParts
[n
];
741 void wxFileConfig::SetPath(const wxString
& strPath
)
743 DoSetPath(strPath
, true /* create missing path components */);
746 const wxString
& wxFileConfig::GetPath() const
751 // ----------------------------------------------------------------------------
753 // ----------------------------------------------------------------------------
755 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
) const
758 return GetNextGroup(str
, lIndex
);
761 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
) const
763 if ( size_t(lIndex
) < m_pCurrentGroup
->Groups().GetCount() ) {
764 str
= m_pCurrentGroup
->Groups()[(size_t)lIndex
++]->Name();
771 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
) const
774 return GetNextEntry(str
, lIndex
);
777 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
) const
779 if ( size_t(lIndex
) < m_pCurrentGroup
->Entries().GetCount() ) {
780 str
= m_pCurrentGroup
->Entries()[(size_t)lIndex
++]->Name();
787 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive
) const
789 size_t n
= m_pCurrentGroup
->Entries().GetCount();
791 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
793 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
794 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
795 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
796 self
->m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
797 n
+= GetNumberOfEntries(true);
798 self
->m_pCurrentGroup
= pOldCurrentGroup
;
805 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive
) const
807 size_t n
= m_pCurrentGroup
->Groups().GetCount();
809 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
811 wxFileConfigGroup
*pOldCurrentGroup
= m_pCurrentGroup
;
812 size_t nSubgroups
= m_pCurrentGroup
->Groups().GetCount();
813 for ( size_t nGroup
= 0; nGroup
< nSubgroups
; nGroup
++ ) {
814 self
->m_pCurrentGroup
= m_pCurrentGroup
->Groups()[nGroup
];
815 n
+= GetNumberOfGroups(true);
816 self
->m_pCurrentGroup
= pOldCurrentGroup
;
823 // ----------------------------------------------------------------------------
824 // tests for existence
825 // ----------------------------------------------------------------------------
827 bool wxFileConfig::HasGroup(const wxString
& strName
) const
829 // special case: DoSetPath("") does work as it's equivalent to DoSetPath("/")
830 // but there is no group with empty name so treat this separately
831 if ( strName
.empty() )
834 const wxString pathOld
= GetPath();
836 wxFileConfig
*self
= const_cast<wxFileConfig
*>(this);
838 rc
= self
->DoSetPath(strName
, false /* don't create missing components */);
840 self
->SetPath(pathOld
);
845 bool wxFileConfig::HasEntry(const wxString
& entry
) const
847 // path is the part before the last "/"
848 wxString path
= entry
.BeforeLast(wxCONFIG_PATH_SEPARATOR
);
850 // except in the special case of "/keyname" when there is nothing before "/"
851 if ( path
.empty() && *entry
.c_str() == wxCONFIG_PATH_SEPARATOR
)
853 path
= wxCONFIG_PATH_SEPARATOR
;
856 // change to the path of the entry if necessary and remember the old path
857 // to restore it later
859 wxFileConfig
* const self
= const_cast<wxFileConfig
*>(this);
863 if ( pathOld
.empty() )
864 pathOld
= wxCONFIG_PATH_SEPARATOR
;
866 if ( !self
->DoSetPath(path
, false /* don't create if doesn't exist */) )
872 // check if the entry exists in this group
873 const bool exists
= m_pCurrentGroup
->FindEntry(
874 entry
.AfterLast(wxCONFIG_PATH_SEPARATOR
)) != NULL
;
876 // restore the old path if we changed it above
877 if ( !pathOld
.empty() )
879 self
->SetPath(pathOld
);
885 // ----------------------------------------------------------------------------
887 // ----------------------------------------------------------------------------
889 bool wxFileConfig::DoReadString(const wxString
& key
, wxString
* pStr
) const
891 wxConfigPathChanger
path(this, key
);
893 wxFileConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
894 if (pEntry
== NULL
) {
898 *pStr
= pEntry
->Value();
903 bool wxFileConfig::DoReadLong(const wxString
& key
, long *pl
) const
906 if ( !Read(key
, &str
) )
909 // extra spaces shouldn't prevent us from reading numeric values
912 return str
.ToLong(pl
);
917 bool wxFileConfig::DoReadBinary(const wxString
& key
, wxMemoryBuffer
* buf
) const
919 wxCHECK_MSG( buf
, false, wxT("NULL buffer") );
922 if ( !Read(key
, &str
) )
925 *buf
= wxBase64Decode(str
);
929 #endif // wxUSE_BASE64
931 bool wxFileConfig::DoWriteString(const wxString
& key
, const wxString
& szValue
)
933 wxConfigPathChanger
path(this, key
);
934 wxString strName
= path
.Name();
936 wxLogTrace( FILECONF_TRACE_MASK
,
937 wxT(" Writing String '%s' = '%s' to Group '%s'"),
942 if ( strName
.empty() )
944 // setting the value of a group is an error
946 wxASSERT_MSG( szValue
.empty(), wxT("can't set value of a group!") );
948 // ... except if it's empty in which case it's a way to force it's creation
950 wxLogTrace( FILECONF_TRACE_MASK
,
951 wxT(" Creating group %s"),
952 m_pCurrentGroup
->Name().c_str() );
956 // this will add a line for this group if it didn't have it before (or
957 // do nothing for the root but it's ok as it always exists anyhow)
958 (void)m_pCurrentGroup
->GetGroupLine();
962 // writing an entry check that the name is reasonable
963 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
);
974 wxLogTrace( FILECONF_TRACE_MASK
,
975 wxT(" Adding Entry %s"),
977 pEntry
= m_pCurrentGroup
->AddEntry(strName
);
980 wxLogTrace( FILECONF_TRACE_MASK
,
981 wxT(" Setting value %s"),
983 pEntry
->SetValue(szValue
);
991 bool wxFileConfig::DoWriteLong(const wxString
& key
, long lValue
)
993 return Write(key
, wxString::Format(wxT("%ld"), lValue
));
998 bool wxFileConfig::DoWriteBinary(const wxString
& key
, const wxMemoryBuffer
& buf
)
1000 return Write(key
, wxBase64Encode(buf
));
1003 #endif // wxUSE_BASE64
1005 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
1007 if ( !IsDirty() || !m_fnLocalFile
.GetFullPath() )
1010 // set the umask if needed
1011 wxCHANGE_UMASK(m_umask
);
1013 wxTempFile
file(m_fnLocalFile
.GetFullPath());
1015 if ( !file
.IsOpened() )
1017 wxLogError(_("can't open user configuration file."));
1021 // write all strings to file
1023 filetext
.reserve(4096);
1024 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1026 filetext
<< p
->Text() << wxTextFile::GetEOL();
1029 if ( !file
.Write(filetext
, *m_conv
) )
1031 wxLogError(_("can't write user configuration file."));
1035 if ( !file
.Commit() )
1037 wxLogError(_("Failed to update user configuration file."));
1044 #if defined( __WXOSX_MAC__ ) && wxOSX_USE_CARBON
1045 m_fnLocalFile
.MacSetTypeAndCreator('TEXT', 'ttxt');
1053 bool wxFileConfig::Save(wxOutputStream
& os
, const wxMBConv
& conv
)
1055 // save unconditionally, even if not dirty
1056 for ( wxFileConfigLineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() )
1058 wxString line
= p
->Text();
1059 line
+= wxTextFile::GetEOL();
1061 wxCharBuffer
buf(line
.mb_str(conv
));
1062 if ( !os
.Write(buf
, strlen(buf
)) )
1064 wxLogError(_("Error saving user configuration data."));
1075 #endif // wxUSE_STREAMS
1077 // ----------------------------------------------------------------------------
1078 // renaming groups/entries
1079 // ----------------------------------------------------------------------------
1081 bool wxFileConfig::RenameEntry(const wxString
& oldName
,
1082 const wxString
& newName
)
1084 wxASSERT_MSG( oldName
.find(wxCONFIG_PATH_SEPARATOR
) == wxString::npos
,
1085 wxT("RenameEntry(): paths are not supported") );
1087 // check that the entry exists
1088 wxFileConfigEntry
*oldEntry
= m_pCurrentGroup
->FindEntry(oldName
);
1092 // check that the new entry doesn't already exist
1093 if ( m_pCurrentGroup
->FindEntry(newName
) )
1096 // delete the old entry, create the new one
1097 wxString value
= oldEntry
->Value();
1098 if ( !m_pCurrentGroup
->DeleteEntry(oldName
) )
1103 wxFileConfigEntry
*newEntry
= m_pCurrentGroup
->AddEntry(newName
);
1104 newEntry
->SetValue(value
);
1109 bool wxFileConfig::RenameGroup(const wxString
& oldName
,
1110 const wxString
& newName
)
1112 // check that the group exists
1113 wxFileConfigGroup
*group
= m_pCurrentGroup
->FindSubgroup(oldName
);
1117 // check that the new group doesn't already exist
1118 if ( m_pCurrentGroup
->FindSubgroup(newName
) )
1121 group
->Rename(newName
);
1128 // ----------------------------------------------------------------------------
1129 // delete groups/entries
1130 // ----------------------------------------------------------------------------
1132 bool wxFileConfig::DeleteEntry(const wxString
& key
, bool bGroupIfEmptyAlso
)
1134 wxConfigPathChanger
path(this, key
);
1136 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
1141 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
1142 if ( m_pCurrentGroup
!= m_pRootGroup
) {
1143 wxFileConfigGroup
*pGroup
= m_pCurrentGroup
;
1144 SetPath(wxT("..")); // changes m_pCurrentGroup!
1145 m_pCurrentGroup
->DeleteSubgroupByName(pGroup
->Name());
1147 //else: never delete the root group
1153 bool wxFileConfig::DeleteGroup(const wxString
& key
)
1155 wxConfigPathChanger
path(this, RemoveTrailingSeparator(key
));
1157 if ( !m_pCurrentGroup
->DeleteSubgroupByName(path
.Name()) )
1160 path
.UpdateIfDeleted();
1167 bool wxFileConfig::DeleteAll()
1171 if ( m_fnLocalFile
.IsOk() )
1173 if ( m_fnLocalFile
.FileExists() &&
1174 !wxRemoveFile(m_fnLocalFile
.GetFullPath()) )
1176 wxLogSysError(_("can't delete user configuration file '%s'"),
1177 m_fnLocalFile
.GetFullPath().c_str());
1187 // ----------------------------------------------------------------------------
1188 // linked list functions
1189 // ----------------------------------------------------------------------------
1191 // append a new line to the end of the list
1193 wxFileConfigLineList
*wxFileConfig::LineListAppend(const wxString
& str
)
1195 wxLogTrace( FILECONF_TRACE_MASK
,
1196 wxT(" ** Adding Line '%s'"),
1198 wxLogTrace( FILECONF_TRACE_MASK
,
1200 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1202 wxLogTrace( FILECONF_TRACE_MASK
,
1204 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1207 wxFileConfigLineList
*pLine
= new wxFileConfigLineList(str
);
1209 if ( m_linesTail
== NULL
)
1212 m_linesHead
= pLine
;
1217 m_linesTail
->SetNext(pLine
);
1218 pLine
->SetPrev(m_linesTail
);
1221 m_linesTail
= pLine
;
1223 wxLogTrace( FILECONF_TRACE_MASK
,
1225 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1227 wxLogTrace( FILECONF_TRACE_MASK
,
1229 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1235 // insert a new line after the given one or in the very beginning if !pLine
1236 wxFileConfigLineList
*wxFileConfig::LineListInsert(const wxString
& str
,
1237 wxFileConfigLineList
*pLine
)
1239 wxLogTrace( FILECONF_TRACE_MASK
,
1240 wxT(" ** Inserting Line '%s' after '%s'"),
1242 ((pLine
) ? (const wxChar
*)pLine
->Text().c_str()
1244 wxLogTrace( FILECONF_TRACE_MASK
,
1246 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1248 wxLogTrace( FILECONF_TRACE_MASK
,
1250 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1253 if ( pLine
== m_linesTail
)
1254 return LineListAppend(str
);
1256 wxFileConfigLineList
*pNewLine
= new wxFileConfigLineList(str
);
1257 if ( pLine
== NULL
)
1259 // prepend to the list
1260 pNewLine
->SetNext(m_linesHead
);
1261 m_linesHead
->SetPrev(pNewLine
);
1262 m_linesHead
= pNewLine
;
1266 // insert before pLine
1267 wxFileConfigLineList
*pNext
= pLine
->Next();
1268 pNewLine
->SetNext(pNext
);
1269 pNewLine
->SetPrev(pLine
);
1270 pNext
->SetPrev(pNewLine
);
1271 pLine
->SetNext(pNewLine
);
1274 wxLogTrace( FILECONF_TRACE_MASK
,
1276 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1278 wxLogTrace( FILECONF_TRACE_MASK
,
1280 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1286 void wxFileConfig::LineListRemove(wxFileConfigLineList
*pLine
)
1288 wxLogTrace( FILECONF_TRACE_MASK
,
1289 wxT(" ** Removing Line '%s'"),
1290 pLine
->Text().c_str() );
1291 wxLogTrace( FILECONF_TRACE_MASK
,
1293 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1295 wxLogTrace( FILECONF_TRACE_MASK
,
1297 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1300 wxFileConfigLineList
*pPrev
= pLine
->Prev(),
1301 *pNext
= pLine
->Next();
1305 if ( pPrev
== NULL
)
1306 m_linesHead
= pNext
;
1308 pPrev
->SetNext(pNext
);
1312 if ( pNext
== NULL
)
1313 m_linesTail
= pPrev
;
1315 pNext
->SetPrev(pPrev
);
1317 wxLogTrace( FILECONF_TRACE_MASK
,
1319 ((m_linesHead
) ? (const wxChar
*)m_linesHead
->Text().c_str()
1321 wxLogTrace( FILECONF_TRACE_MASK
,
1323 ((m_linesTail
) ? (const wxChar
*)m_linesTail
->Text().c_str()
1329 bool wxFileConfig::LineListIsEmpty()
1331 return m_linesHead
== NULL
;
1334 // ============================================================================
1335 // wxFileConfig::wxFileConfigGroup
1336 // ============================================================================
1338 // ----------------------------------------------------------------------------
1340 // ----------------------------------------------------------------------------
1343 wxFileConfigGroup::wxFileConfigGroup(wxFileConfigGroup
*pParent
,
1344 const wxString
& strName
,
1345 wxFileConfig
*pConfig
)
1346 : m_aEntries(CompareEntries
),
1347 m_aSubgroups(CompareGroups
),
1350 m_pConfig
= pConfig
;
1351 m_pParent
= pParent
;
1354 m_pLastEntry
= NULL
;
1355 m_pLastGroup
= NULL
;
1358 // dtor deletes all children
1359 wxFileConfigGroup::~wxFileConfigGroup()
1362 size_t n
, nCount
= m_aEntries
.GetCount();
1363 for ( n
= 0; n
< nCount
; n
++ )
1364 delete m_aEntries
[n
];
1367 nCount
= m_aSubgroups
.GetCount();
1368 for ( n
= 0; n
< nCount
; n
++ )
1369 delete m_aSubgroups
[n
];
1372 // ----------------------------------------------------------------------------
1374 // ----------------------------------------------------------------------------
1376 void wxFileConfigGroup::SetLine(wxFileConfigLineList
*pLine
)
1378 // for a normal (i.e. not root) group this method shouldn't be called twice
1379 // unless we are resetting the line
1380 wxASSERT_MSG( !m_pParent
|| !m_pLine
|| !pLine
,
1381 wxT("changing line for a non-root group?") );
1387 This is a bit complicated, so let me explain it in details. All lines that
1388 were read from the local file (the only one we will ever modify) are stored
1389 in a (doubly) linked list. Our problem is to know at which position in this
1390 list should we insert the new entries/subgroups. To solve it we keep three
1391 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
1393 m_pLine points to the line containing "[group_name]"
1394 m_pLastEntry points to the last entry of this group in the local file.
1395 m_pLastGroup subgroup
1397 Initially, they're NULL all three. When the group (an entry/subgroup) is read
1398 from the local file, the corresponding variable is set. However, if the group
1399 was read from the global file and then modified or created by the application
1400 these variables are still NULL and we need to create the corresponding lines.
1401 See the following functions (and comments preceding them) for the details of
1404 Also, when our last entry/group are deleted we need to find the new last
1405 element - the code in DeleteEntry/Subgroup does this by backtracking the list
1406 of lines until it either founds an entry/subgroup (and this is the new last
1407 element) or the m_pLine of the group, in which case there are no more entries
1408 (or subgroups) left and m_pLast<element> becomes NULL.
1410 NB: This last problem could be avoided for entries if we added new entries
1411 immediately after m_pLine, but in this case the entries would appear
1412 backwards in the config file (OTOH, it's not that important) and as we
1413 would still need to do it for the subgroups the code wouldn't have been
1414 significantly less complicated.
1417 // Return the line which contains "[our name]". If we're still not in the list,
1418 // add our line to it immediately after the last line of our parent group if we
1419 // have it or in the very beginning if we're the root group.
1420 wxFileConfigLineList
*wxFileConfigGroup::GetGroupLine()
1422 wxLogTrace( FILECONF_TRACE_MASK
,
1423 wxT(" GetGroupLine() for Group '%s'"),
1428 wxLogTrace( FILECONF_TRACE_MASK
,
1429 wxT(" Getting Line item pointer") );
1431 wxFileConfigGroup
*pParent
= Parent();
1433 // this group wasn't present in local config file, add it now
1436 wxLogTrace( FILECONF_TRACE_MASK
,
1437 wxT(" checking parent '%s'"),
1438 pParent
->Name().c_str() );
1440 wxString strFullName
;
1442 // add 1 to the name because we don't want to start with '/'
1443 strFullName
<< wxT("[")
1444 << FilterOutEntryName(GetFullName().c_str() + 1)
1446 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
1447 pParent
->GetLastGroupLine());
1448 pParent
->SetLastGroup(this); // we're surely after all the others
1450 //else: this is the root group and so we return NULL because we don't
1451 // have any group line
1457 // Return the last line belonging to the subgroups of this group (after which
1458 // we can add a new subgroup), if we don't have any subgroups or entries our
1459 // last line is the group line (m_pLine) itself.
1460 wxFileConfigLineList
*wxFileConfigGroup::GetLastGroupLine()
1462 // if we have any subgroups, our last line is the last line of the last
1466 wxFileConfigLineList
*pLine
= m_pLastGroup
->GetLastGroupLine();
1468 wxASSERT_MSG( pLine
, wxT("last group must have !NULL associated line") );
1473 // no subgroups, so the last line is the line of thelast entry (if any)
1474 return GetLastEntryLine();
1477 // return the last line belonging to the entries of this group (after which
1478 // we can add a new entry), if we don't have any entries we will add the new
1479 // one immediately after the group line itself.
1480 wxFileConfigLineList
*wxFileConfigGroup::GetLastEntryLine()
1482 wxLogTrace( FILECONF_TRACE_MASK
,
1483 wxT(" GetLastEntryLine() for Group '%s'"),
1488 wxFileConfigLineList
*pLine
= m_pLastEntry
->GetLine();
1490 wxASSERT_MSG( pLine
, wxT("last entry must have !NULL associated line") );
1495 // no entries: insert after the group header, if any
1496 return GetGroupLine();
1499 void wxFileConfigGroup::SetLastEntry(wxFileConfigEntry
*pEntry
)
1501 m_pLastEntry
= pEntry
;
1505 // the only situation in which a group without its own line can have
1506 // an entry is when the first entry is added to the initially empty
1507 // root pseudo-group
1508 wxASSERT_MSG( !m_pParent
, wxT("unexpected for non root group") );
1510 // let the group know that it does have a line in the file now
1511 m_pLine
= pEntry
->GetLine();
1515 // ----------------------------------------------------------------------------
1517 // ----------------------------------------------------------------------------
1519 void wxFileConfigGroup::UpdateGroupAndSubgroupsLines()
1521 // update the line of this group
1522 wxFileConfigLineList
*line
= GetGroupLine();
1523 wxCHECK_RET( line
, wxT("a non root group must have a corresponding line!") );
1525 // +1: skip the leading '/'
1526 line
->SetText(wxString::Format(wxT("[%s]"), GetFullName().c_str() + 1));
1529 // also update all subgroups as they have this groups name in their lines
1530 const size_t nCount
= m_aSubgroups
.GetCount();
1531 for ( size_t n
= 0; n
< nCount
; n
++ )
1533 m_aSubgroups
[n
]->UpdateGroupAndSubgroupsLines();
1537 void wxFileConfigGroup::Rename(const wxString
& newName
)
1539 wxCHECK_RET( m_pParent
, wxT("the root group can't be renamed") );
1541 if ( newName
== m_strName
)
1544 // we need to remove the group from the parent and it back under the new
1545 // name to keep the parents array of subgroups alphabetically sorted
1546 m_pParent
->m_aSubgroups
.Remove(this);
1548 m_strName
= newName
;
1550 m_pParent
->m_aSubgroups
.Add(this);
1552 // update the group lines recursively
1553 UpdateGroupAndSubgroupsLines();
1556 wxString
wxFileConfigGroup::GetFullName() const
1560 fullname
= Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR
+ Name();
1565 // ----------------------------------------------------------------------------
1567 // ----------------------------------------------------------------------------
1569 // use binary search because the array is sorted
1571 wxFileConfigGroup::FindEntry(const wxString
& name
) const
1575 hi
= m_aEntries
.GetCount();
1577 wxFileConfigEntry
*pEntry
;
1581 pEntry
= m_aEntries
[i
];
1583 #if wxCONFIG_CASE_SENSITIVE
1584 res
= pEntry
->Name().compare(name
);
1586 res
= pEntry
->Name().CmpNoCase(name
);
1601 wxFileConfigGroup::FindSubgroup(const wxString
& name
) const
1605 hi
= m_aSubgroups
.GetCount();
1607 wxFileConfigGroup
*pGroup
;
1611 pGroup
= m_aSubgroups
[i
];
1613 #if wxCONFIG_CASE_SENSITIVE
1614 res
= pGroup
->Name().compare(name
);
1616 res
= pGroup
->Name().CmpNoCase(name
);
1630 // ----------------------------------------------------------------------------
1631 // create a new item
1632 // ----------------------------------------------------------------------------
1634 // create a new entry and add it to the current group
1635 wxFileConfigEntry
*wxFileConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
1637 wxASSERT( FindEntry(strName
) == 0 );
1639 wxFileConfigEntry
*pEntry
= new wxFileConfigEntry(this, strName
, nLine
);
1641 m_aEntries
.Add(pEntry
);
1645 // create a new group and add it to the current group
1646 wxFileConfigGroup
*wxFileConfigGroup::AddSubgroup(const wxString
& strName
)
1648 wxASSERT( FindSubgroup(strName
) == 0 );
1650 wxFileConfigGroup
*pGroup
= new wxFileConfigGroup(this, strName
, m_pConfig
);
1652 m_aSubgroups
.Add(pGroup
);
1656 // ----------------------------------------------------------------------------
1658 // ----------------------------------------------------------------------------
1661 The delete operations are _very_ slow if we delete the last item of this
1662 group (see comments before GetXXXLineXXX functions for more details),
1663 so it's much better to start with the first entry/group if we want to
1664 delete several of them.
1667 bool wxFileConfigGroup::DeleteSubgroupByName(const wxString
& name
)
1669 wxFileConfigGroup
* const pGroup
= FindSubgroup(name
);
1671 return pGroup
? DeleteSubgroup(pGroup
) : false;
1674 // Delete the subgroup and remove all references to it from
1675 // other data structures.
1676 bool wxFileConfigGroup::DeleteSubgroup(wxFileConfigGroup
*pGroup
)
1678 wxCHECK_MSG( pGroup
, false, wxT("deleting non existing group?") );
1680 wxLogTrace( FILECONF_TRACE_MASK
,
1681 wxT("Deleting group '%s' from '%s'"),
1682 pGroup
->Name().c_str(),
1685 wxLogTrace( FILECONF_TRACE_MASK
,
1686 wxT(" (m_pLine) = prev: %p, this %p, next %p"),
1687 m_pLine
? static_cast<void*>(m_pLine
->Prev()) : 0,
1688 static_cast<void*>(m_pLine
),
1689 m_pLine
? static_cast<void*>(m_pLine
->Next()) : 0 );
1690 wxLogTrace( FILECONF_TRACE_MASK
,
1692 m_pLine
? (const wxChar
*)m_pLine
->Text().c_str()
1695 // delete all entries...
1696 size_t nCount
= pGroup
->m_aEntries
.GetCount();
1698 wxLogTrace(FILECONF_TRACE_MASK
,
1699 wxT("Removing %lu entries"), (unsigned long)nCount
);
1701 for ( size_t nEntry
= 0; nEntry
< nCount
; nEntry
++ )
1703 wxFileConfigLineList
*pLine
= pGroup
->m_aEntries
[nEntry
]->GetLine();
1707 wxLogTrace( FILECONF_TRACE_MASK
,
1709 pLine
->Text().c_str() );
1710 m_pConfig
->LineListRemove(pLine
);
1714 // ...and subgroups of this subgroup
1715 nCount
= pGroup
->m_aSubgroups
.GetCount();
1717 wxLogTrace( FILECONF_TRACE_MASK
,
1718 wxT("Removing %lu subgroups"), (unsigned long)nCount
);
1720 for ( size_t nGroup
= 0; nGroup
< nCount
; nGroup
++ )
1722 pGroup
->DeleteSubgroup(pGroup
->m_aSubgroups
[0]);
1725 // and then finally the group itself
1726 wxFileConfigLineList
*pLine
= pGroup
->m_pLine
;
1729 wxLogTrace( FILECONF_TRACE_MASK
,
1730 wxT(" Removing line for group '%s' : '%s'"),
1731 pGroup
->Name().c_str(),
1732 pLine
->Text().c_str() );
1733 wxLogTrace( FILECONF_TRACE_MASK
,
1734 wxT(" Removing from group '%s' : '%s'"),
1736 ((m_pLine
) ? (const wxChar
*)m_pLine
->Text().c_str()
1739 // notice that we may do this test inside the previous "if"
1740 // because the last entry's line is surely !NULL
1741 if ( pGroup
== m_pLastGroup
)
1743 wxLogTrace( FILECONF_TRACE_MASK
,
1744 wxT(" Removing last group") );
1746 // our last entry is being deleted, so find the last one which
1747 // stays by going back until we find a subgroup or reach the
1749 const size_t nSubgroups
= m_aSubgroups
.GetCount();
1751 m_pLastGroup
= NULL
;
1752 for ( wxFileConfigLineList
*pl
= pLine
->Prev();
1753 pl
&& !m_pLastGroup
;
1756 // does this line belong to our subgroup?
1757 for ( size_t n
= 0; n
< nSubgroups
; n
++ )
1759 // do _not_ call GetGroupLine! we don't want to add it to
1760 // the local file if it's not already there
1761 if ( m_aSubgroups
[n
]->m_pLine
== pl
)
1763 m_pLastGroup
= m_aSubgroups
[n
];
1768 if ( pl
== m_pLine
)
1773 m_pConfig
->LineListRemove(pLine
);
1777 wxLogTrace( FILECONF_TRACE_MASK
,
1778 wxT(" No line entry for Group '%s'?"),
1779 pGroup
->Name().c_str() );
1782 m_aSubgroups
.Remove(pGroup
);
1788 bool wxFileConfigGroup::DeleteEntry(const wxString
& name
)
1790 wxFileConfigEntry
*pEntry
= FindEntry(name
);
1793 // entry doesn't exist, nothing to do
1797 wxFileConfigLineList
*pLine
= pEntry
->GetLine();
1798 if ( pLine
!= NULL
) {
1799 // notice that we may do this test inside the previous "if" because the
1800 // last entry's line is surely !NULL
1801 if ( pEntry
== m_pLastEntry
) {
1802 // our last entry is being deleted - find the last one which stays
1803 wxASSERT( m_pLine
!= NULL
); // if we have an entry with !NULL pLine...
1805 // find the previous entry (if any)
1806 wxFileConfigEntry
*pNewLast
= NULL
;
1807 const wxFileConfigLineList
* const
1808 pNewLastLine
= m_pLastEntry
->GetLine()->Prev();
1809 const size_t nEntries
= m_aEntries
.GetCount();
1810 for ( size_t n
= 0; n
< nEntries
; n
++ ) {
1811 if ( m_aEntries
[n
]->GetLine() == pNewLastLine
) {
1812 pNewLast
= m_aEntries
[n
];
1817 // pNewLast can be NULL here -- it's ok and can happen if we have no
1819 m_pLastEntry
= pNewLast
;
1821 // For the root group only, we could be removing the first group line
1822 // here, so update m_pLine to avoid keeping a dangling pointer.
1823 if ( pLine
== m_pLine
)
1827 m_pConfig
->LineListRemove(pLine
);
1830 m_aEntries
.Remove(pEntry
);
1836 // ============================================================================
1837 // wxFileConfig::wxFileConfigEntry
1838 // ============================================================================
1840 // ----------------------------------------------------------------------------
1842 // ----------------------------------------------------------------------------
1843 wxFileConfigEntry::wxFileConfigEntry(wxFileConfigGroup
*pParent
,
1844 const wxString
& strName
,
1846 : m_strName(strName
)
1848 wxASSERT( !strName
.empty() );
1850 m_pParent
= pParent
;
1854 m_bHasValue
= false;
1856 m_bImmutable
= strName
[0] == wxCONFIG_IMMUTABLE_PREFIX
;
1858 m_strName
.erase(0, 1); // remove first character
1861 // ----------------------------------------------------------------------------
1863 // ----------------------------------------------------------------------------
1865 void wxFileConfigEntry::SetLine(wxFileConfigLineList
*pLine
)
1867 if ( m_pLine
!= NULL
) {
1868 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1869 Name().c_str(), m_pParent
->GetFullName().c_str());
1873 Group()->SetLastEntry(this);
1876 // second parameter is false if we read the value from file and prevents the
1877 // entry from being marked as 'dirty'
1878 void wxFileConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
1880 if ( bUser
&& IsImmutable() )
1882 wxLogWarning( _("attempt to change immutable key '%s' ignored."),
1887 // do nothing if it's the same value: but don't test for it if m_bHasValue
1888 // hadn't been set yet or we'd never write empty values to the file
1889 if ( m_bHasValue
&& strValue
== m_strValue
)
1893 m_strValue
= strValue
;
1897 wxString strValFiltered
;
1899 if ( Group()->Config()->GetStyle() & wxCONFIG_USE_NO_ESCAPE_CHARACTERS
)
1901 strValFiltered
= strValue
;
1904 strValFiltered
= FilterOutValue(strValue
);
1908 strLine
<< FilterOutEntryName(m_strName
) << wxT('=') << strValFiltered
;
1912 // entry was read from the local config file, just modify the line
1913 m_pLine
->SetText(strLine
);
1915 else // this entry didn't exist in the local file
1917 // add a new line to the file: note that line returned by
1918 // GetLastEntryLine() may be NULL if we're in the root group and it
1919 // doesn't have any entries yet, but this is ok as passing NULL
1920 // line to LineListInsert() means to prepend new line to the list
1921 wxFileConfigLineList
*line
= Group()->GetLastEntryLine();
1922 m_pLine
= Group()->Config()->LineListInsert(strLine
, line
);
1924 Group()->SetLastEntry(this);
1929 // ============================================================================
1931 // ============================================================================
1933 // ----------------------------------------------------------------------------
1934 // compare functions for array sorting
1935 // ----------------------------------------------------------------------------
1937 int CompareEntries(wxFileConfigEntry
*p1
, wxFileConfigEntry
*p2
)
1939 #if wxCONFIG_CASE_SENSITIVE
1940 return p1
->Name().compare(p2
->Name());
1942 return p1
->Name().CmpNoCase(p2
->Name());
1946 int CompareGroups(wxFileConfigGroup
*p1
, wxFileConfigGroup
*p2
)
1948 #if wxCONFIG_CASE_SENSITIVE
1949 return p1
->Name().compare(p2
->Name());
1951 return p1
->Name().CmpNoCase(p2
->Name());
1955 // ----------------------------------------------------------------------------
1957 // ----------------------------------------------------------------------------
1959 // undo FilterOutValue
1960 static wxString
FilterInValue(const wxString
& str
)
1966 strResult
.reserve(str
.length());
1968 wxString::const_iterator i
= str
.begin();
1969 const bool bQuoted
= *i
== '"';
1973 for ( const wxString::const_iterator end
= str
.end(); i
!= end
; ++i
)
1975 if ( *i
== wxT('\\') )
1979 wxLogWarning(_("trailing backslash ignored in '%s'"), str
.c_str());
1983 switch ( (*i
).GetValue() )
1986 strResult
+= wxT('\n');
1990 strResult
+= wxT('\r');
1994 strResult
+= wxT('\t');
1998 strResult
+= wxT('\\');
2002 strResult
+= wxT('"');
2006 else // not a backslash
2008 if ( *i
!= wxT('"') || !bQuoted
)
2012 else if ( i
!= end
- 1 )
2014 wxLogWarning(_("unexpected \" at position %d in '%s'."),
2015 i
- str
.begin(), str
.c_str());
2017 //else: it's the last quote of a quoted string, ok
2024 // quote the string before writing it to file
2025 static wxString
FilterOutValue(const wxString
& str
)
2031 strResult
.Alloc(str
.Len());
2033 // quoting is necessary to preserve spaces in the beginning of the string
2034 bool bQuote
= wxIsspace(str
[0]) || str
[0] == wxT('"');
2037 strResult
+= wxT('"');
2040 for ( size_t n
= 0; n
< str
.Len(); n
++ ) {
2041 switch ( str
[n
].GetValue() ) {
2063 //else: fall through
2066 strResult
+= str
[n
];
2067 continue; // nothing special to do
2070 // we get here only for special characters
2071 strResult
<< wxT('\\') << c
;
2075 strResult
+= wxT('"');
2080 // undo FilterOutEntryName
2081 static wxString
FilterInEntryName(const wxString
& str
)
2084 strResult
.Alloc(str
.Len());
2086 for ( const wxChar
*pc
= str
.c_str(); *pc
!= '\0'; pc
++ ) {
2087 if ( *pc
== wxT('\\') ) {
2088 // we need to test it here or we'd skip past the NUL in the loop line
2089 if ( *++pc
== wxT('\0') )
2099 // sanitize entry or group name: insert '\\' before any special characters
2100 static wxString
FilterOutEntryName(const wxString
& str
)
2103 strResult
.Alloc(str
.Len());
2105 for ( const wxChar
*pc
= str
.c_str(); *pc
!= wxT('\0'); pc
++ ) {
2106 const wxChar c
= *pc
;
2108 // we explicitly allow some of "safe" chars and 8bit ASCII characters
2109 // which will probably never have special meaning and with which we can't
2110 // use isalnum() anyhow (in ASCII built, in Unicode it's just fine)
2112 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
2113 // should *not* be quoted
2116 ((unsigned char)c
< 127) &&
2118 !wxIsalnum(c
) && !wxStrchr(wxT("@_/-!.*%()"), c
) )
2120 strResult
+= wxT('\\');
2129 // we can't put ?: in the ctor initializer list because it confuses some
2130 // broken compilers (Borland C++)
2131 static wxString
GetAppName(const wxString
& appName
)
2133 if ( !appName
&& wxTheApp
)
2134 return wxTheApp
->GetAppName();
2139 #endif // wxUSE_CONFIG