]>
git.saurik.com Git - wxWidgets.git/blob - src/common/fileconf.cpp
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 ///////////////////////////////////////////////////////////////////////////////
13 // ============================================================================
15 // ============================================================================
17 // ----------------------------------------------------------------------------
19 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
27 #include <wx/string.h>
31 #include <wx/dynarray.h>
34 #include <wx/textfile.h>
35 #include <wx/config.h>
44 // ----------------------------------------------------------------------------
45 // global functions declarations
46 // ----------------------------------------------------------------------------
48 // is 'c' a valid character in group name?
49 // NB: APPCONF_IMMUTABLE_PREFIX and APPCONF_PATH_SEPARATOR must be valid chars,
50 // but _not_ ']' (group name delimiter)
51 inline bool IsValid(char c
) { return isalnum(c
) || strchr("_/-!.*%", c
); }
53 // get the system wide and user configuration file full path
54 static const char *GetGlobalFileName(const char *szFile
);
55 static const char *GetLocalFileName(const char *szFile
);
57 // split path into parts removing '..' in progress
58 static void SplitPath(wxArrayString
& aParts
, const char *sz
);
61 static wxString
FilterIn(const wxString
& str
);
62 static wxString
FilterOut(const wxString
& str
);
64 // ----------------------------------------------------------------------------
66 // ----------------------------------------------------------------------------
69 FileConfig derives from BaseConfig and implements file based config class,
70 i.e. it uses ASCII disk files to store the information. These files are
71 alternatively called INI, .conf or .rc in the documentation. They are
72 organized in groups or sections, which can nest (i.e. a group contains
73 subgroups, which contain their own subgroups &c). Each group has some
74 number of entries, which are "key = value" pairs. More precisely, the format
77 # comments are allowed after either ';' or '#' (Win/UNIX standard)
79 # blank lines (as above) are ignored
81 # global entries are members of special (no name) top group
82 written_for = wxWindows
85 # the start of the group 'Foo'
86 [Foo] # may put comments like this also
87 # following 3 lines are entries
89 another_key = " strings with spaces in the beginning should be quoted, \
90 otherwise the spaces are lost"
91 last_key = but you don't have to put " normally (nor quote them, like here)
93 # subgroup of the group 'Foo'
94 # (order is not important, only the name is: separator is '/', as in paths)
96 # entries prefixed with "!" are immutable, i.e. can't be changed if they are
97 # set in the system-wide config file
101 [Foo/Bar/Fubar] # depth is (theoretically :-) unlimited
102 # may have the same name as key in another section
103 bar_entry = whatever not
105 You have {read/write/delete}Entry functions (guess what they do) and also
106 setCurrentPath to select current group. enum{Subgroups/Entries} allow you
107 to get all entries in the config file (in the current group). Finally,
108 flush() writes immediately all changed entries to disk (otherwise it would
109 be done automatically in dtor)
111 FileConfig manages not less than 2 config files for each program: global
112 and local (or system and user if you prefer). Entries are read from both of
113 them and the local entries override the global ones unless the latter is
114 immutable (prefixed with '!') in which case a warning message is generated
115 and local value is ignored. Of course, the changes are always written to local
119 class wxFileConfig
: public wxConfig
123 // the config file is searched in the following locations
125 // Unix /etc/file.ext ~/.file
126 // Win %windir%\file.ext %USERPROFILE%\file.ext
128 // where file is the basename of strFile, ext is it's extension
129 // or .conf (Unix) or .ini (Win) if it has none
130 wxFileConfig(const wxString
& strFile
, bool bLocalOnly
= FALSE
);
131 // dtor will save unsaved data
132 virtual ~wxFileConfig();
134 // implement inherited pure virtual functions
135 virtual void SetPath(const wxString
& strPath
);
136 virtual const wxString
& GetPath() const { return m_strPath
; }
138 virtual bool GetFirstGroup(wxString
& str
, long& lIndex
);
139 virtual bool GetNextGroup (wxString
& str
, long& lIndex
);
140 virtual bool GetFirstEntry(wxString
& str
, long& lIndex
);
141 virtual bool GetNextEntry (wxString
& str
, long& lIndex
);
143 virtual const char *Read(const char *szKey
, const char *szDefault
= 0) const;
144 virtual long Read(const char *szKey
, long lDefault
) const;
145 virtual bool Write(const char *szKey
, const char *szValue
);
146 virtual bool Write(const char *szKey
, long Value
);
147 virtual bool Flush(bool bCurrentOnly
= FALSE
);
149 virtual bool DeleteEntry(const char *szKey
, bool bGroupIfEmptyAlso
);
150 virtual bool DeleteGroup(const char *szKey
);
151 virtual bool DeleteAll();
158 // we store all lines of the local config file as a linked list in memory
163 LineList(const wxString
& str
, LineList
*pNext
= NULL
) : m_strLine(str
)
167 LineList
*Next() const { return m_pNext
; }
168 void SetNext(LineList
*pNext
) { m_pNext
= pNext
; }
171 void SetText(const wxString
& str
) { m_strLine
= str
; }
172 const wxString
& Text() const { return m_strLine
; }
175 wxString m_strLine
; // line contents
176 LineList
*m_pNext
; // next node
179 // functions to work with this list
180 LineList
*LineListAppend(const wxString
& str
);
181 LineList
*LineListInsert(const wxString
& str
,
182 LineList
*pLine
); // NULL => Append()
183 bool LineListIsEmpty();
186 // put the object in the initial state
189 // parse the whole file
190 void Parse(wxTextFile
& file
, bool bLocal
);
192 // the same as SetPath("/")
197 LineList
*m_linesHead
, // head of the linked list
198 *m_linesTail
; // tail
200 wxString m_strFile
; // file name passed to ctor
201 wxString m_strPath
; // current path (not '/' terminated)
203 ConfigGroup
*m_pRootGroup
, // the top (unnamed) group
204 *m_pCurrentGroup
; // the current group
206 // a handy little class which changes current path to the path of given entry
207 // and restores it in dtor: so if you declare a local variable of this type,
208 // you work in the entry directory and the path is automatically restored
209 // when function returns
213 // ctor/dtor do path changing/restorin
214 PathChanger(const wxFileConfig
*pContainer
, const wxString
& strEntry
);
218 const wxString
& Name() const { return m_strName
; }
221 wxFileConfig
*m_pContainer
; // object we live in
222 wxString m_strName
, // name of entry (i.e. name only)
223 m_strOldPath
; // saved path
224 bool m_bChanged
; // was the path changed?
227 //protected: --- if FileConfig::ConfigEntry is not public, functions in
228 // ConfigGroup such as Find/AddEntry can't return "ConfigEntry *"
230 WX_DEFINE_ARRAY(ConfigEntry
*, ArrayEntries
);
231 WX_DEFINE_ARRAY(ConfigGroup
*, ArrayGroups
);
236 ConfigGroup
*m_pParent
; // group that contains us
237 wxString m_strName
, // entry name
239 bool m_bDirty
, // changed since last read?
240 m_bImmutable
; // can be overriden locally?
241 int m_nLine
; // used if m_pLine == NULL only
242 LineList
*m_pLine
; // pointer to our line in the linked list
243 // or NULL if it was found in global file
246 ConfigEntry(ConfigGroup
*pParent
, const wxString
& strName
, int nLine
);
249 const wxString
& Name() const { return m_strName
; }
250 const wxString
& Value() const { return m_strValue
; }
251 ConfigGroup
*Group() const { return m_pParent
; }
252 bool IsDirty() const { return m_bDirty
; }
253 bool IsImmutable() const { return m_bImmutable
; }
254 bool IsLocal() const { return m_pLine
!= 0; }
255 int Line() const { return m_nLine
; }
256 LineList
*GetLine() const { return m_pLine
; }
258 // modify entry attributes
259 void SetValue(const wxString
& strValue
, bool bUser
= TRUE
);
261 void SetLine(LineList
*pLine
);
268 wxFileConfig
*m_pConfig
; // config object we belong to
269 ConfigGroup
*m_pParent
; // parent group (NULL for root group)
270 ArrayEntries m_aEntries
; // entries in this group
271 ArrayGroups m_aSubgroups
; // subgroups
272 wxString m_strName
; // group's name
273 bool m_bDirty
; // if FALSE => all subgroups are not dirty
274 LineList
*m_pLine
; // pointer to our line in the linked list
275 int m_nLastEntry
, // last here means "last added"
280 ConfigGroup(ConfigGroup
*pParent
, const wxString
& strName
, wxFileConfig
*);
282 // dtor deletes all entries and subgroups also
286 const wxString
& Name() const { return m_strName
; }
287 ConfigGroup
*Parent() const { return m_pParent
; }
288 wxFileConfig
*Config() const { return m_pConfig
; }
289 bool IsDirty() const { return m_bDirty
; }
291 bool IsEmpty() const { return Entries().IsEmpty() && Groups().IsEmpty(); }
292 const ArrayEntries
& Entries() const { return m_aEntries
; }
293 const ArrayGroups
& Groups() const { return m_aSubgroups
; }
295 // find entry/subgroup (NULL if not found)
296 ConfigGroup
*FindSubgroup(const char *szName
) const;
297 ConfigEntry
*FindEntry (const char *szName
) const;
299 // delete entry/subgroup, return FALSE if doesn't exist
300 bool DeleteSubgroup(const char *szName
);
301 bool DeleteEntry(const char *szName
);
303 // create new entry/subgroup returning pointer to newly created element
304 ConfigGroup
*AddSubgroup(const wxString
& strName
);
305 ConfigEntry
*AddEntry (const wxString
& strName
, int nLine
= NOT_FOUND
);
307 // will also recursively set parent's dirty flag
309 void SetLine(LineList
*pLine
);
311 wxString
GetFullName() const;
313 // get the last line belonging to an entry/subgroup of this group
314 LineList
*GetGroupLine();
315 LineList
*GetLastEntryLine();
316 LineList
*GetLastGroupLine();
320 // ============================================================================
322 // ============================================================================
324 // ----------------------------------------------------------------------------
326 // ----------------------------------------------------------------------------
328 void wxFileConfig::Init()
331 m_pRootGroup
= new ConfigGroup(NULL
, "", this);
336 m_bExpandEnvVars
= TRUE
;
341 wxFileConfig::wxFileConfig(const wxString
& strFile
, bool bLocalOnly
)
348 // it's not an error if (one of the) file(s) doesn't exist
350 // parse the global file
352 szFile
= GetGlobalFileName(strFile
);
353 if ( wxFile::Exists(szFile
) ) {
354 wxTextFile
fileGlobal(szFile
);
356 if ( fileGlobal
.Open() ) {
357 Parse(fileGlobal
, FALSE
/* global */);
361 wxLogWarning("Can't open global configuration file.");
365 // parse the local file
366 szFile
= GetLocalFileName(strFile
);
367 if ( wxFile::Exists(szFile
) ) {
368 wxTextFile
fileLocal(szFile
);
369 if ( fileLocal
.Open() ) {
370 Parse(fileLocal
, TRUE
/* local */);
374 wxLogWarning("Can't open user configuration file.");
378 wxFileConfig::~wxFileConfig()
384 // ----------------------------------------------------------------------------
385 // parse a config file
386 // ----------------------------------------------------------------------------
388 void wxFileConfig::Parse(wxTextFile
& file
, bool bLocal
)
393 for ( uint n
= 0; n
< file
.GetLineCount(); n
++ ) {
394 // add the line to linked list
396 LineListAppend(file
[n
]);
398 // skip leading spaces
399 for ( pStart
= file
[n
]; isspace(*pStart
); pStart
++ )
402 // skip blank/comment lines
403 if ( *pStart
== '\0'|| *pStart
== ';' || *pStart
== '#' )
406 if ( *pStart
== '[' ) { // a new group
409 while ( *++pEnd
!= ']' ) {
410 if ( !IsValid(*pEnd
) && *pEnd
!= ' ' ) // allow spaces in group names
414 if ( *pEnd
!= ']' ) {
415 wxLogError("file '%s': unexpected character at line %d (missing ']'?)",
416 file
.GetName(), n
+ 1);
417 continue; // skip this line
420 // group name here is always considered as abs path
423 strGroup
<< APPCONF_PATH_SEPARATOR
<< wxString(pStart
, pEnd
- pStart
);
425 // will create it if doesn't yet exist
429 m_pCurrentGroup
->SetLine(m_linesTail
);
431 // check that there is nothing except comments left on this line
433 while ( *++pEnd
!= '\0' && bCont
) {
442 // ignore whitespace ('\n' impossible here)
446 wxLogWarning("file '%s', line %d: '%s' ignored after group header.",
447 file
.GetName(), n
+ 1, pEnd
);
453 const char *pEnd
= pStart
;
454 while ( IsValid(*pEnd
) )
457 wxString
strKey(pStart
, pEnd
);
460 while ( isspace(*pEnd
) )
463 if ( *pEnd
++ != '=' ) {
464 wxLogError("file '%s', line %d: '=' expected.", file
.GetName(), n
+ 1);
467 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(strKey
);
469 if ( pEntry
== NULL
) {
471 pEntry
= m_pCurrentGroup
->AddEntry(strKey
, n
);
474 pEntry
->SetLine(m_linesTail
);
477 if ( bLocal
&& pEntry
->IsImmutable() ) {
478 // immutable keys can't be changed by user
479 wxLogWarning("file '%s', line %d: value for immutable key '%s' ignored.",
480 file
.GetName(), n
+ 1, strKey
.c_str());
483 // the condition below catches the cases (a) and (b) but not (c):
484 // (a) global key found second time in global file
485 // (b) key found second (or more) time in local file
486 // (c) key from global file now found in local one
487 // which is exactly what we want.
488 else if ( !bLocal
|| pEntry
->IsLocal() ) {
489 wxLogWarning("file '%s', line %d: key '%s' was first found at line %d.",
490 file
.GetName(), n
+ 1, strKey
.c_str(), pEntry
->Line());
493 pEntry
->SetLine(m_linesTail
);
498 while ( isspace(*pEnd
) )
502 if (m_bExpandEnvVars
)
503 strValue
= ExpandEnvVars(FilterIn(pEnd
));
505 strValue
= FilterIn(pEnd
);
506 pEntry
->SetValue(strValue
, FALSE
);
512 // ----------------------------------------------------------------------------
514 // ----------------------------------------------------------------------------
516 void wxFileConfig::SetRootPath()
519 m_pCurrentGroup
= m_pRootGroup
;
522 void wxFileConfig::SetPath(const wxString
& strPath
)
524 wxArrayString aParts
;
526 if ( strPath
.IsEmpty() )
529 if ( strPath
[0] == APPCONF_PATH_SEPARATOR
) {
531 SplitPath(aParts
, strPath
);
534 // relative path, combine with current one
535 wxString strFullPath
= m_strPath
;
536 strFullPath
<< APPCONF_PATH_SEPARATOR
<< strPath
;
537 SplitPath(aParts
, strFullPath
);
540 // change current group
542 m_pCurrentGroup
= m_pRootGroup
;
543 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
544 ConfigGroup
*pNextGroup
= m_pCurrentGroup
->FindSubgroup(aParts
[n
]);
545 if ( pNextGroup
== NULL
)
546 pNextGroup
= m_pCurrentGroup
->AddSubgroup(aParts
[n
]);
547 m_pCurrentGroup
= pNextGroup
;
550 // recombine path parts in one variable
552 for ( n
= 0; n
< aParts
.Count(); n
++ ) {
553 m_strPath
<< APPCONF_PATH_SEPARATOR
<< aParts
[n
];
557 // ----------------------------------------------------------------------------
559 // ----------------------------------------------------------------------------
561 bool wxFileConfig::GetFirstGroup(wxString
& str
, long& lIndex
)
564 return GetNextGroup(str
, lIndex
);
567 bool wxFileConfig::GetNextGroup (wxString
& str
, long& lIndex
)
569 if ( uint(lIndex
) < m_pCurrentGroup
->Groups().Count() ) {
570 str
= m_pCurrentGroup
->Groups()[lIndex
++]->Name();
577 bool wxFileConfig::GetFirstEntry(wxString
& str
, long& lIndex
)
580 return GetNextEntry(str
, lIndex
);
583 bool wxFileConfig::GetNextEntry (wxString
& str
, long& lIndex
)
585 if ( uint(lIndex
) < m_pCurrentGroup
->Entries().Count() ) {
586 str
= m_pCurrentGroup
->Entries()[lIndex
++]->Name();
593 // ----------------------------------------------------------------------------
595 // ----------------------------------------------------------------------------
597 const char *wxFileConfig::Read(const char *szKey
, const char *szDefault
) const
599 PathChanger
path(this, szKey
);
601 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
605 return pEntry
->Value();
606 // return pEntry == NULL ? szDefault : pEntry->Value();
609 long wxFileConfig::Read(const char *szKey
, long lDefault
) const
611 const char *pc
= Read(szKey
);
612 return pc
== NULL
? lDefault
: atol(pc
);
615 bool wxFileConfig::Write(const char *szKey
, const char *szValue
)
617 PathChanger
path(this, szKey
);
619 ConfigEntry
*pEntry
= m_pCurrentGroup
->FindEntry(path
.Name());
620 if ( pEntry
== NULL
)
621 pEntry
= m_pCurrentGroup
->AddEntry(path
.Name());
622 pEntry
->SetValue(szValue
);
627 bool wxFileConfig::Write(const char *szKey
, long lValue
)
629 // ltoa() is not ANSI :-(
630 char szBuf
[40]; // should be good for sizeof(long) <= 16 (128 bits)
631 sprintf(szBuf
, "%ld", lValue
);
632 return Write(szKey
, szBuf
);
635 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
637 if ( LineListIsEmpty() || !m_pRootGroup
->IsDirty() )
640 wxTempFile
file(GetLocalFileName(m_strFile
));
642 if ( !file
.IsOpened() ) {
643 wxLogError("Can't open user configuration file.");
647 // write all strings to file
648 for ( LineList
*p
= m_linesHead
; p
!= NULL
; p
= p
->Next() ) {
649 if ( !file
.Write(p
->Text() + wxTextFile::GetEOL()) ) {
650 wxLogError("Can't write user configuration file.");
655 return file
.Commit();
658 // ----------------------------------------------------------------------------
659 // delete groups/entries
660 // ----------------------------------------------------------------------------
662 bool wxFileConfig::DeleteEntry(const char *szKey
, bool bGroupIfEmptyAlso
)
664 PathChanger
path(this, szKey
);
666 if ( !m_pCurrentGroup
->DeleteEntry(path
.Name()) )
669 if ( bGroupIfEmptyAlso
&& m_pCurrentGroup
->IsEmpty() ) {
670 if ( m_pCurrentGroup
!= m_pRootGroup
) {
671 ConfigGroup
*pGroup
= m_pCurrentGroup
;
672 SetPath(".."); // changes m_pCurrentGroup!
673 m_pCurrentGroup
->DeleteSubgroup(pGroup
->Name());
675 //else: never delete the root group
681 bool wxFileConfig::DeleteGroup(const char *szKey
)
683 PathChanger
path(this, szKey
);
685 return m_pCurrentGroup
->DeleteSubgroup(path
.Name());
688 bool wxFileConfig::DeleteAll()
690 const char *szFile
= GetLocalFileName(m_strFile
);
694 if ( remove(szFile
) == -1 )
695 wxLogSysError("Can't delete user configuration file '%s'", szFile
);
697 szFile
= GetGlobalFileName(m_strFile
);
698 if ( remove(szFile
) )
699 wxLogSysError("Can't delete system configuration file '%s'", szFile
);
704 // ----------------------------------------------------------------------------
705 // linked list functions
706 // ----------------------------------------------------------------------------
708 // append a new line to the end of the list
709 wxFileConfig::LineList
*wxFileConfig::LineListAppend(const wxString
& str
)
711 LineList
*pLine
= new LineList(str
);
713 if ( m_linesTail
== NULL
) {
719 m_linesTail
->SetNext(pLine
);
726 // insert a new line after the given one
727 wxFileConfig::LineList
*wxFileConfig::LineListInsert(const wxString
& str
,
731 return LineListAppend(str
);
733 LineList
*pNewLine
= new LineList(str
, pLine
->Next());
734 pLine
->SetNext(pNewLine
);
739 bool wxFileConfig::LineListIsEmpty()
741 return m_linesHead
== NULL
;
744 // ============================================================================
745 // wxFileConfig::ConfigGroup
746 // ============================================================================
748 // ----------------------------------------------------------------------------
750 // ----------------------------------------------------------------------------
753 wxFileConfig::ConfigGroup::ConfigGroup(wxFileConfig::ConfigGroup
*pParent
,
754 const wxString
& strName
,
755 wxFileConfig
*pConfig
)
764 m_nLastGroup
= NOT_FOUND
;
767 // dtor deletes all children
768 wxFileConfig::ConfigGroup::~ConfigGroup()
771 uint n
, nCount
= m_aEntries
.Count();
772 for ( n
= 0; n
< nCount
; n
++ )
773 delete m_aEntries
[n
];
776 nCount
= m_aSubgroups
.Count();
777 for ( n
= 0; n
< nCount
; n
++ )
778 delete m_aSubgroups
[n
];
781 // ----------------------------------------------------------------------------
783 // ----------------------------------------------------------------------------
785 void wxFileConfig::ConfigGroup::SetLine(LineList
*pLine
)
787 wxASSERT( m_pLine
== NULL
); // shouldn't be called twice
792 // return the line which contains "[our name]"
793 wxFileConfig::LineList
*wxFileConfig::ConfigGroup::GetGroupLine()
795 if ( m_pLine
== NULL
) {
796 // this group wasn't present in local config file, add it now
797 if ( Parent() != NULL
) {
798 wxString strFullName
;
799 strFullName
<< "[" << GetFullName().c_str() + 1 << "]"; // +1: no '/'
800 m_pLine
= m_pConfig
->LineListInsert(strFullName
,
801 Parent()->GetLastGroupLine());
804 // we're the root group, yet we were not in the local file => there were
805 // only comments and blank lines in config file or nothing at all
806 // we return NULL, so that LineListInsert() will do Append()
813 // return the last line belonging to the subgroups of this group
814 // (after which we can add a new subgroup)
815 wxFileConfig::LineList
*wxFileConfig::ConfigGroup::GetLastGroupLine()
817 // if we have any subgroups, our last line is the last line of the last
819 if ( m_nLastGroup
!= NOT_FOUND
) {
820 return m_aSubgroups
[m_nLastGroup
]->GetLastGroupLine();
823 // if we have any entries, our last line is the last entry
824 if ( m_nLastEntry
!= NOT_FOUND
) {
825 return m_aEntries
[m_nLastEntry
]->GetLine();
828 // nothing at all: last line is the first one
829 return GetGroupLine();
832 // return the last line belonging to the entries of this group
833 // (after which we can add a new entry)
834 wxFileConfig::LineList
*wxFileConfig::ConfigGroup::GetLastEntryLine()
836 if ( m_nLastEntry
!= NOT_FOUND
)
837 return m_aEntries
[m_nLastEntry
]->GetLine();
839 return GetGroupLine();
842 // ----------------------------------------------------------------------------
844 // ----------------------------------------------------------------------------
846 wxString
wxFileConfig::ConfigGroup::GetFullName() const
849 return Parent()->GetFullName() + APPCONF_PATH_SEPARATOR
+ Name();
854 // ----------------------------------------------------------------------------
856 // ----------------------------------------------------------------------------
858 wxFileConfig::ConfigEntry
*
859 wxFileConfig::ConfigGroup::FindEntry(const char *szName
) const
861 uint nCount
= m_aEntries
.Count();
862 for ( uint n
= 0; n
< nCount
; n
++ ) {
863 if ( m_aEntries
[n
]->Name().IsSameAs(szName
, APPCONF_CASE_SENSITIVE
) )
864 return m_aEntries
[n
];
870 wxFileConfig::ConfigGroup
*
871 wxFileConfig::ConfigGroup::FindSubgroup(const char *szName
) const
873 uint nCount
= m_aSubgroups
.Count();
874 for ( uint n
= 0; n
< nCount
; n
++ ) {
875 if ( m_aSubgroups
[n
]->Name().IsSameAs(szName
, APPCONF_CASE_SENSITIVE
) )
876 return m_aSubgroups
[n
];
882 // ----------------------------------------------------------------------------
884 // ----------------------------------------------------------------------------
886 // create a new entry and add it to the current group
887 wxFileConfig::ConfigEntry
*
888 wxFileConfig::ConfigGroup::AddEntry(const wxString
& strName
, int nLine
)
890 wxASSERT( FindEntry(strName
) == NULL
);
892 ConfigEntry
*pEntry
= new ConfigEntry(this, strName
, nLine
);
893 m_aEntries
.Add(pEntry
);
898 // create a new group and add it to the current group
899 wxFileConfig::ConfigGroup
*
900 wxFileConfig::ConfigGroup::AddSubgroup(const wxString
& strName
)
902 wxASSERT( FindSubgroup(strName
) == NULL
);
904 ConfigGroup
*pGroup
= new ConfigGroup(this, strName
, m_pConfig
);
905 m_aSubgroups
.Add(pGroup
);
910 // ----------------------------------------------------------------------------
912 // ----------------------------------------------------------------------------
914 bool wxFileConfig::ConfigGroup::DeleteSubgroup(const char *szName
)
916 uint n
, nCount
= m_aSubgroups
.Count();
917 for ( n
= 0; n
< nCount
; n
++ ) {
918 if ( m_aSubgroups
[n
]->Name().IsSameAs(szName
, APPCONF_CASE_SENSITIVE
) )
925 delete m_aSubgroups
[n
];
926 m_aSubgroups
.Remove(n
);
930 bool wxFileConfig::ConfigGroup::DeleteEntry(const char *szName
)
932 uint n
, nCount
= m_aEntries
.Count();
933 for ( n
= 0; n
< nCount
; n
++ ) {
934 if ( m_aEntries
[n
]->Name().IsSameAs(szName
, APPCONF_CASE_SENSITIVE
) )
941 delete m_aEntries
[n
];
942 m_aEntries
.Remove(n
);
946 // ----------------------------------------------------------------------------
948 // ----------------------------------------------------------------------------
949 void wxFileConfig::ConfigGroup::SetDirty()
952 if ( Parent() != NULL
) // propagate upwards
953 Parent()->SetDirty();
956 // ============================================================================
957 // wxFileConfig::ConfigEntry
958 // ============================================================================
960 // ----------------------------------------------------------------------------
962 // ----------------------------------------------------------------------------
963 wxFileConfig::ConfigEntry::ConfigEntry(wxFileConfig::ConfigGroup
*pParent
,
964 const wxString
& strName
,
974 m_bImmutable
= strName
[0] == APPCONF_IMMUTABLE_PREFIX
;
976 m_strName
.erase(0, 1); // remove first character
979 // ----------------------------------------------------------------------------
981 // ----------------------------------------------------------------------------
983 void wxFileConfig::ConfigEntry::SetLine(LineList
*pLine
)
985 wxASSERT( m_pLine
== NULL
);
990 // second parameter is FALSE if we read the value from file and prevents the
991 // entry from being marked as 'dirty'
992 void wxFileConfig::ConfigEntry::SetValue(const wxString
& strValue
, bool bUser
)
994 if ( bUser
&& IsImmutable() ) {
995 wxLogWarning("Attempt to change immutable key '%s' ignored.",
1000 // do nothing if it's the same value
1001 if ( strValue
== m_strValue
)
1004 m_strValue
= strValue
;
1007 wxString strVal
= FilterOut(strValue
);
1009 strLine
<< m_strName
<< " = " << strVal
;
1011 if ( m_pLine
!= NULL
) {
1012 // entry was read from the local config file, just modify the line
1013 m_pLine
->SetText(strLine
);
1016 // add a new line to the file
1017 wxASSERT( m_nLine
== NOT_FOUND
); // consistency check
1019 Group()->Config()->LineListInsert(strLine
, Group()->GetLastEntryLine());
1026 void wxFileConfig::ConfigEntry::SetDirty()
1029 Group()->SetDirty();
1032 // ============================================================================
1033 // wxFileConfig::PathChanger
1034 // ============================================================================
1036 wxFileConfig::PathChanger::PathChanger(const wxFileConfig
*pContainer
,
1037 const wxString
& strEntry
)
1039 m_pContainer
= (wxFileConfig
*)pContainer
;
1040 wxString strPath
= strEntry
.Before(APPCONF_PATH_SEPARATOR
);
1041 if ( !strPath
.IsEmpty() ) {
1042 // do change the path
1044 m_strName
= strEntry
.Right(APPCONF_PATH_SEPARATOR
);
1045 m_strOldPath
= m_pContainer
->GetPath();
1046 m_strOldPath
+= APPCONF_PATH_SEPARATOR
;
1047 m_pContainer
->SetPath(strPath
);
1050 // it's a name only, without path - nothing to do
1052 m_strName
= strEntry
;
1056 wxFileConfig::PathChanger::~PathChanger()
1058 // only restore path if it was changed
1060 m_pContainer
->SetPath(m_strOldPath
);
1064 // ============================================================================
1066 // ============================================================================
1068 const char *GetGlobalFileName(const char *szFile
)
1070 static wxString s_str
;
1073 bool bNoExt
= strchr(szFile
, '.') == NULL
;
1076 s_str
<< "/etc/" << szFile
;
1081 #define _MAX_PATH 512
1083 char szWinDir
[_MAX_PATH
];
1084 ::GetWindowsDirectory(szWinDir
, _MAX_PATH
);
1085 s_str
<< szWinDir
<< "\\" << szFile
;
1090 return s_str
.c_str();
1093 const char *GetLocalFileName(const char *szFile
)
1095 static wxString s_str
;
1099 const char *szHome
= getenv("HOME");
1100 if ( szHome
== NULL
) {
1101 // we're homeless...
1102 wxLogWarning("can't find user's HOME, using current directory.");
1105 s_str
<< szHome
<< "/." << szFile
;
1108 const char *szHome
= getenv("HOMEDRIVE");
1109 if ( szHome
== NULL
)
1112 szHome
= getenv("HOMEPATH");
1113 s_str
<< ( szHome
== NULL
? "." : szHome
) << szFile
;
1114 if ( strchr(szFile
, '.') == NULL
)
1117 // Win16 has no idea about home, so use the current directory instead
1118 s_str
<< ".\\" << szFile
;
1122 return s_str
.c_str();
1125 void SplitPath(wxArrayString
& aParts
, const char *sz
)
1129 wxString strCurrent
;
1130 const char *pc
= sz
;
1132 if ( *pc
== '\0' || *pc
== APPCONF_PATH_SEPARATOR
) {
1133 if ( strCurrent
== "." ) {
1136 else if ( strCurrent
== ".." ) {
1138 if ( aParts
.IsEmpty() )
1139 wxLogWarning("'%s' has extra '..', ignored.", sz
);
1141 aParts
.Remove(aParts
.Count() - 1);
1143 else if ( !strCurrent
.IsEmpty() ) {
1144 aParts
.Add(strCurrent
);
1148 // could log an error here, but we prefer to ignore extra '/'
1161 wxString
FilterIn(const wxString
& str
)
1165 bool bQuoted
= str
[0] == '"';
1167 for ( uint n
= bQuoted
? 1 : 0; n
< str
.Len(); n
++ ) {
1168 if ( str
[n
] == '\\' ) {
1169 switch ( str
[++n
] ) {
1188 if ( str
[n
] != '"' || !bQuoted
)
1189 strResult
+= str
[n
];
1190 else if ( n
!= str
.Len() - 1 )
1191 wxLogWarning("unexpected \" at position %d in '%s'.", n
, str
.c_str());
1192 //else: it's the last quote of a quoted string, ok
1199 // quote the string before writing it to file
1200 wxString
FilterOut(const wxString
& str
)
1204 // quoting is necessary to preserve spaces in the beginning of the string
1205 bool bQuote
= isspace(str
[0]) || str
[0] == '"';
1211 for ( uint n
= 0; n
< str
.Len(); n
++ ) {
1228 //else: fall through
1231 strResult
+= str
[n
];
1232 continue; // nothing special to do
1235 // we get here only for special characters
1236 strResult
<< '\\' << c
;
1245 wxConfig
*CreateFileConfig(const wxString
& strFile
, bool bLocalOnly
)
1247 return new wxFileConfig(strFile
, bLocalOnly
);