]> git.saurik.com Git - wxWidgets.git/blob - src/common/fileconf.cpp
1. corrected (but the fix is ugly) the multiple def button problem
[wxWidgets.git] / src / common / fileconf.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: fileconf.cpp
3 // Purpose: implementation of wxFileConfig derivation of wxConfig
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 07.04.98 (adapted from appconf.cpp)
7 // RCS-ID: $Id$
8 // Copyright: (c) 1997 Karsten Ballüder & Vadim Zeitlin
9 // Ballueder@usa.net <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence: wxWindows license
11 ///////////////////////////////////////////////////////////////////////////////
12
13 #ifdef __GNUG__
14 #pragma implementation "fileconf.h"
15 #endif
16
17 // ----------------------------------------------------------------------------
18 // headers
19 // ----------------------------------------------------------------------------
20
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif //__BORLANDC__
26
27 #if wxUSE_CONFIG
28
29 #ifndef WX_PRECOMP
30 #include "wx/string.h"
31 #include "wx/intl.h"
32 #endif //WX_PRECOMP
33
34 #include "wx/app.h"
35 #include "wx/dynarray.h"
36 #include "wx/file.h"
37 #include "wx/log.h"
38 #include "wx/textfile.h"
39 #include "wx/config.h"
40 #include "wx/fileconf.h"
41
42 #include "wx/utils.h" // for wxGetHomeDir
43
44 // _WINDOWS_ is defined when windows.h is included,
45 // __WXMSW__ is defined for MS Windows compilation
46 #if defined(__WXMSW__) && !defined(_WINDOWS_)
47 #include <windows.h>
48 #endif //windows.h
49 #if defined(__WXPM__)
50 #define INCL_DOS
51 #include <os2.h>
52 #define LINKAGEMODE _Optlink
53 #else
54 #define LINKAGEMODE
55 #endif
56
57 #include <stdlib.h>
58 #include <ctype.h>
59
60 // ----------------------------------------------------------------------------
61 // macros
62 // ----------------------------------------------------------------------------
63 #define CONST_CAST ((wxFileConfig *)this)->
64
65 // ----------------------------------------------------------------------------
66 // constants
67 // ----------------------------------------------------------------------------
68 #ifndef MAX_PATH
69 #define MAX_PATH 512
70 #endif
71
72 // ----------------------------------------------------------------------------
73 // global functions declarations
74 // ----------------------------------------------------------------------------
75
76 // compare functions for sorting the arrays
77 static int LINKAGEMODE CompareEntries(ConfigEntry *p1, ConfigEntry *p2);
78 static int LINKAGEMODE CompareGroups(ConfigGroup *p1, ConfigGroup *p2);
79
80 // filter strings
81 static wxString FilterInValue(const wxString& str);
82 static wxString FilterOutValue(const wxString& str);
83
84 static wxString FilterInEntryName(const wxString& str);
85 static wxString FilterOutEntryName(const wxString& str);
86
87 // get the name to use in wxFileConfig ctor
88 static wxString GetAppName(const wxString& appname);
89
90 // ============================================================================
91 // implementation
92 // ============================================================================
93
94 // ----------------------------------------------------------------------------
95 // static functions
96 // ----------------------------------------------------------------------------
97 wxString wxFileConfig::GetGlobalDir()
98 {
99 wxString strDir;
100
101 #ifdef __UNIX__
102 strDir = wxT("/etc/");
103 #elif defined(__WXPM__)
104 ULONG aulSysInfo[QSV_MAX] = {0};
105 UINT drive;
106 APIRET rc;
107
108 rc = DosQuerySysInfo( 1L, QSV_MAX, (PVOID)aulSysInfo, sizeof(ULONG)*QSV_MAX);
109 if (rc == 0)
110 {
111 drive = aulSysInfo[QSV_BOOT_DRIVE - 1];
112 switch(drive)
113 {
114 case 1:
115 strDir = "A:\\OS2\\";
116 break;
117 case 2:
118 strDir = "B:\\OS2\\";
119 break;
120 case 3:
121 strDir = "C:\\OS2\\";
122 break;
123 case 4:
124 strDir = "D:\\OS2\\";
125 break;
126 case 5:
127 strDir = "E:\\OS2\\";
128 break;
129 case 6:
130 strDir = "F:\\OS2\\";
131 break;
132 case 7:
133 strDir = "G:\\OS2\\";
134 break;
135 case 8:
136 strDir = "H:\\OS2\\";
137 break;
138 case 9:
139 strDir = "I:\\OS2\\";
140 break;
141 case 10:
142 strDir = "J:\\OS2\\";
143 break;
144 case 11:
145 strDir = "K:\\OS2\\";
146 break;
147 case 12:
148 strDir = "L:\\OS2\\";
149 break;
150 case 13:
151 strDir = "M:\\OS2\\";
152 break;
153 case 14:
154 strDir = "N:\\OS2\\";
155 break;
156 case 15:
157 strDir = "O:\\OS2\\";
158 break;
159 case 16:
160 strDir = "P:\\OS2\\";
161 break;
162 case 17:
163 strDir = "Q:\\OS2\\";
164 break;
165 case 18:
166 strDir = "R:\\OS2\\";
167 break;
168 case 19:
169 strDir = "S:\\OS2\\";
170 break;
171 case 20:
172 strDir = "T:\\OS2\\";
173 break;
174 case 21:
175 strDir = "U:\\OS2\\";
176 break;
177 case 22:
178 strDir = "V:\\OS2\\";
179 break;
180 case 23:
181 strDir = "W:\\OS2\\";
182 break;
183 case 24:
184 strDir = "X:\\OS2\\";
185 break;
186 case 25:
187 strDir = "Y:\\OS2\\";
188 break;
189 case 26:
190 strDir = "Z:\\OS2\\";
191 break;
192 }
193 }
194 #elif defined(__WXSTUBS__)
195 wxASSERT_MSG( FALSE, wxT("TODO") ) ;
196 #elif defined(__WXMAC__)
197 wxASSERT_MSG( FALSE, wxT("TODO") ) ;
198 #else // Windows
199 wxChar szWinDir[MAX_PATH];
200 ::GetWindowsDirectory(szWinDir, MAX_PATH);
201
202 strDir = szWinDir;
203 strDir << wxT('\\');
204 #endif // Unix/Windows
205
206 return strDir;
207 }
208
209 wxString wxFileConfig::GetLocalDir()
210 {
211 wxString strDir;
212
213 wxGetHomeDir(&strDir);
214
215 #ifdef __UNIX__
216 if (strDir.Last() != wxT('/')) strDir << wxT('/');
217 #else
218 if (strDir.Last() != wxT('\\')) strDir << wxT('\\');
219 #endif
220
221 return strDir;
222 }
223
224 wxString wxFileConfig::GetGlobalFileName(const wxChar *szFile)
225 {
226 wxString str = GetGlobalDir();
227 str << szFile;
228
229 if ( wxStrchr(szFile, wxT('.')) == NULL )
230 #ifdef __UNIX__
231 str << wxT(".conf");
232 #else // Windows
233 str << wxT(".ini");
234 #endif // UNIX/Win
235
236 return str;
237 }
238
239 wxString wxFileConfig::GetLocalFileName(const wxChar *szFile)
240 {
241 wxString str = GetLocalDir();
242
243 #ifdef __UNIX__
244 str << wxT('.');
245 #endif
246
247 str << szFile;
248
249 #ifdef __WXMSW__
250 if ( wxStrchr(szFile, wxT('.')) == NULL )
251 str << wxT(".ini");
252 #endif
253
254 return str;
255 }
256
257 // ----------------------------------------------------------------------------
258 // ctor
259 // ----------------------------------------------------------------------------
260
261 void wxFileConfig::Init()
262 {
263 m_pCurrentGroup =
264 m_pRootGroup = new ConfigGroup(NULL, "", this);
265
266 m_linesHead =
267 m_linesTail = NULL;
268
269 // it's not an error if (one of the) file(s) doesn't exist
270
271 // parse the global file
272 if ( !m_strGlobalFile.IsEmpty() && wxFile::Exists(m_strGlobalFile) ) {
273 wxTextFile fileGlobal(m_strGlobalFile);
274
275 if ( fileGlobal.Open() ) {
276 Parse(fileGlobal, FALSE /* global */);
277 SetRootPath();
278 }
279 else
280 wxLogWarning(_("can't open global configuration file '%s'."),
281 m_strGlobalFile.c_str());
282 }
283
284 // parse the local file
285 if ( !m_strLocalFile.IsEmpty() && wxFile::Exists(m_strLocalFile) ) {
286 wxTextFile fileLocal(m_strLocalFile);
287 if ( fileLocal.Open() ) {
288 Parse(fileLocal, TRUE /* local */);
289 SetRootPath();
290 }
291 else
292 wxLogWarning(_("can't open user configuration file '%s'."),
293 m_strLocalFile.c_str());
294 }
295 }
296
297 // constructor supports creation of wxFileConfig objects of any type
298 wxFileConfig::wxFileConfig(const wxString& appName, const wxString& vendorName,
299 const wxString& strLocal, const wxString& strGlobal,
300 long style)
301 : wxConfigBase(::GetAppName(appName), vendorName,
302 strLocal, strGlobal,
303 style),
304 m_strLocalFile(strLocal), m_strGlobalFile(strGlobal)
305 {
306 // Make up names for files if empty
307 if ( m_strLocalFile.IsEmpty() && (style & wxCONFIG_USE_LOCAL_FILE) )
308 {
309 m_strLocalFile = GetLocalFileName(GetAppName());
310 }
311
312 if ( m_strGlobalFile.IsEmpty() && (style & wxCONFIG_USE_GLOBAL_FILE) )
313 {
314 m_strGlobalFile = GetGlobalFileName(GetAppName());
315 }
316
317 // Check if styles are not supplied, but filenames are, in which case
318 // add the correct styles.
319 if ( !m_strLocalFile.IsEmpty() )
320 SetStyle(GetStyle() | wxCONFIG_USE_LOCAL_FILE);
321
322 if ( !m_strGlobalFile.IsEmpty() )
323 SetStyle(GetStyle() | wxCONFIG_USE_GLOBAL_FILE);
324
325 // if the path is not absolute, prepend the standard directory to it
326 // UNLESS wxCONFIG_USE_RELATIVE_PATH style is set
327 if ( !(style & wxCONFIG_USE_RELATIVE_PATH) ){
328 if ( !m_strLocalFile.IsEmpty() && !wxIsAbsolutePath(m_strLocalFile) )
329 {
330 wxString strLocal = m_strLocalFile;
331 m_strLocalFile = GetLocalDir();
332 m_strLocalFile << strLocal;
333 }
334
335 if ( !m_strGlobalFile.IsEmpty() && !wxIsAbsolutePath(m_strGlobalFile) )
336 {
337 wxString strGlobal = m_strGlobalFile;
338 m_strGlobalFile = GetGlobalDir();
339 m_strGlobalFile << strGlobal;
340 }
341 }
342
343 Init();
344 }
345
346 void wxFileConfig::CleanUp()
347 {
348 delete m_pRootGroup;
349
350 LineList *pCur = m_linesHead;
351 while ( pCur != NULL ) {
352 LineList *pNext = pCur->Next();
353 delete pCur;
354 pCur = pNext;
355 }
356 }
357
358 wxFileConfig::~wxFileConfig()
359 {
360 Flush();
361
362 CleanUp();
363 }
364
365 // ----------------------------------------------------------------------------
366 // parse a config file
367 // ----------------------------------------------------------------------------
368
369 void wxFileConfig::Parse(wxTextFile& file, bool bLocal)
370 {
371 const wxChar *pStart;
372 const wxChar *pEnd;
373 wxString strLine;
374
375 size_t nLineCount = file.GetLineCount();
376 for ( size_t n = 0; n < nLineCount; n++ ) {
377 strLine = file[n];
378
379 // add the line to linked list
380 if ( bLocal )
381 LineListAppend(strLine);
382
383 // skip leading spaces
384 for ( pStart = strLine; wxIsspace(*pStart); pStart++ )
385 ;
386
387 // skip blank/comment lines
388 if ( *pStart == wxT('\0')|| *pStart == wxT(';') || *pStart == wxT('#') )
389 continue;
390
391 if ( *pStart == wxT('[') ) { // a new group
392 pEnd = pStart;
393
394 while ( *++pEnd != wxT(']') ) {
395 if ( *pEnd == wxT('\n') || *pEnd == wxT('\0') )
396 break;
397 }
398
399 if ( *pEnd != wxT(']') ) {
400 wxLogError(_("file '%s': unexpected character %c at line %d."),
401 file.GetName(), *pEnd, n + 1);
402 continue; // skip this line
403 }
404
405 // group name here is always considered as abs path
406 wxString strGroup;
407 pStart++;
408 strGroup << wxCONFIG_PATH_SEPARATOR
409 << FilterInEntryName(wxString(pStart, pEnd - pStart));
410
411 // will create it if doesn't yet exist
412 SetPath(strGroup);
413
414 if ( bLocal )
415 m_pCurrentGroup->SetLine(m_linesTail);
416
417 // check that there is nothing except comments left on this line
418 bool bCont = TRUE;
419 while ( *++pEnd != wxT('\0') && bCont ) {
420 switch ( *pEnd ) {
421 case wxT('#'):
422 case wxT(';'):
423 bCont = FALSE;
424 break;
425
426 case wxT(' '):
427 case wxT('\t'):
428 // ignore whitespace ('\n' impossible here)
429 break;
430
431 default:
432 wxLogWarning(_("file '%s', line %d: '%s' "
433 "ignored after group header."),
434 file.GetName(), n + 1, pEnd);
435 bCont = FALSE;
436 }
437 }
438 }
439 else { // a key
440 const wxChar *pEnd = pStart;
441 while ( *pEnd != wxT('=') && !wxIsspace(*pEnd) ) {
442 if ( *pEnd == wxT('\\') ) {
443 // next character may be space or not - still take it because it's
444 // quoted
445 pEnd++;
446 }
447
448 pEnd++;
449 }
450
451 wxString strKey(FilterInEntryName(wxString(pStart, pEnd)));
452
453 // skip whitespace
454 while ( isspace(*pEnd) )
455 pEnd++;
456
457 if ( *pEnd++ != wxT('=') ) {
458 wxLogError(_("file '%s', line %d: '=' expected."),
459 file.GetName(), n + 1);
460 }
461 else {
462 ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strKey);
463
464 if ( pEntry == NULL ) {
465 // new entry
466 pEntry = m_pCurrentGroup->AddEntry(strKey, n);
467
468 if ( bLocal )
469 pEntry->SetLine(m_linesTail);
470 }
471 else {
472 if ( bLocal && pEntry->IsImmutable() ) {
473 // immutable keys can't be changed by user
474 wxLogWarning(_("file '%s', line %d: value for "
475 "immutable key '%s' ignored."),
476 file.GetName(), n + 1, strKey.c_str());
477 continue;
478 }
479 // the condition below catches the cases (a) and (b) but not (c):
480 // (a) global key found second time in global file
481 // (b) key found second (or more) time in local file
482 // (c) key from global file now found in local one
483 // which is exactly what we want.
484 else if ( !bLocal || pEntry->IsLocal() ) {
485 wxLogWarning(_("file '%s', line %d: key '%s' was first "
486 "found at line %d."),
487 file.GetName(), n + 1, strKey.c_str(), pEntry->Line());
488
489 if ( bLocal )
490 pEntry->SetLine(m_linesTail);
491 }
492 }
493
494 // skip whitespace
495 while ( wxIsspace(*pEnd) )
496 pEnd++;
497
498 pEntry->SetValue(FilterInValue(pEnd), FALSE /* read from file */);
499 }
500 }
501 }
502 }
503
504 // ----------------------------------------------------------------------------
505 // set/retrieve path
506 // ----------------------------------------------------------------------------
507
508 void wxFileConfig::SetRootPath()
509 {
510 m_strPath.Empty();
511 m_pCurrentGroup = m_pRootGroup;
512 }
513
514 void wxFileConfig::SetPath(const wxString& strPath)
515 {
516 wxArrayString aParts;
517
518 if ( strPath.IsEmpty() ) {
519 SetRootPath();
520 return;
521 }
522
523 if ( strPath[0] == wxCONFIG_PATH_SEPARATOR ) {
524 // absolute path
525 wxSplitPath(aParts, strPath);
526 }
527 else {
528 // relative path, combine with current one
529 wxString strFullPath = m_strPath;
530 strFullPath << wxCONFIG_PATH_SEPARATOR << strPath;
531 wxSplitPath(aParts, strFullPath);
532 }
533
534 // change current group
535 size_t n;
536 m_pCurrentGroup = m_pRootGroup;
537 for ( n = 0; n < aParts.Count(); n++ ) {
538 ConfigGroup *pNextGroup = m_pCurrentGroup->FindSubgroup(aParts[n]);
539 if ( pNextGroup == NULL )
540 pNextGroup = m_pCurrentGroup->AddSubgroup(aParts[n]);
541 m_pCurrentGroup = pNextGroup;
542 }
543
544 // recombine path parts in one variable
545 m_strPath.Empty();
546 for ( n = 0; n < aParts.Count(); n++ ) {
547 m_strPath << wxCONFIG_PATH_SEPARATOR << aParts[n];
548 }
549 }
550
551 // ----------------------------------------------------------------------------
552 // enumeration
553 // ----------------------------------------------------------------------------
554
555 bool wxFileConfig::GetFirstGroup(wxString& str, long& lIndex) const
556 {
557 lIndex = 0;
558 return GetNextGroup(str, lIndex);
559 }
560
561 bool wxFileConfig::GetNextGroup (wxString& str, long& lIndex) const
562 {
563 if ( size_t(lIndex) < m_pCurrentGroup->Groups().Count() ) {
564 str = m_pCurrentGroup->Groups()[lIndex++]->Name();
565 return TRUE;
566 }
567 else
568 return FALSE;
569 }
570
571 bool wxFileConfig::GetFirstEntry(wxString& str, long& lIndex) const
572 {
573 lIndex = 0;
574 return GetNextEntry(str, lIndex);
575 }
576
577 bool wxFileConfig::GetNextEntry (wxString& str, long& lIndex) const
578 {
579 if ( size_t(lIndex) < m_pCurrentGroup->Entries().Count() ) {
580 str = m_pCurrentGroup->Entries()[lIndex++]->Name();
581 return TRUE;
582 }
583 else
584 return FALSE;
585 }
586
587 size_t wxFileConfig::GetNumberOfEntries(bool bRecursive) const
588 {
589 size_t n = m_pCurrentGroup->Entries().Count();
590 if ( bRecursive ) {
591 ConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
592 size_t nSubgroups = m_pCurrentGroup->Groups().Count();
593 for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
594 CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
595 n += GetNumberOfEntries(TRUE);
596 CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
597 }
598 }
599
600 return n;
601 }
602
603 size_t wxFileConfig::GetNumberOfGroups(bool bRecursive) const
604 {
605 size_t n = m_pCurrentGroup->Groups().Count();
606 if ( bRecursive ) {
607 ConfigGroup *pOldCurrentGroup = m_pCurrentGroup;
608 size_t nSubgroups = m_pCurrentGroup->Groups().Count();
609 for ( size_t nGroup = 0; nGroup < nSubgroups; nGroup++ ) {
610 CONST_CAST m_pCurrentGroup = m_pCurrentGroup->Groups()[nGroup];
611 n += GetNumberOfGroups(TRUE);
612 CONST_CAST m_pCurrentGroup = pOldCurrentGroup;
613 }
614 }
615
616 return n;
617 }
618
619 // ----------------------------------------------------------------------------
620 // tests for existence
621 // ----------------------------------------------------------------------------
622
623 bool wxFileConfig::HasGroup(const wxString& strName) const
624 {
625 wxConfigPathChanger path(this, strName);
626
627 ConfigGroup *pGroup = m_pCurrentGroup->FindSubgroup(path.Name());
628 return pGroup != NULL;
629 }
630
631 bool wxFileConfig::HasEntry(const wxString& strName) const
632 {
633 wxConfigPathChanger path(this, strName);
634
635 ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
636 return pEntry != NULL;
637 }
638
639 // ----------------------------------------------------------------------------
640 // read/write values
641 // ----------------------------------------------------------------------------
642
643 bool wxFileConfig::Read(const wxString& key,
644 wxString* pStr) const
645 {
646 wxConfigPathChanger path(this, key);
647
648 ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
649 if (pEntry == NULL) {
650 return FALSE;
651 }
652 else {
653 *pStr = ExpandEnvVars(pEntry->Value());
654 return TRUE;
655 }
656 }
657
658 bool wxFileConfig::Read(const wxString& key,
659 wxString* pStr, const wxString& defVal) const
660 {
661 wxConfigPathChanger path(this, key);
662
663 ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(path.Name());
664 if (pEntry == NULL) {
665 if( IsRecordingDefaults() )
666 ((wxFileConfig *)this)->Write(key,defVal);
667 *pStr = ExpandEnvVars(defVal);
668 return FALSE;
669 }
670 else {
671 *pStr = ExpandEnvVars(pEntry->Value());
672 return TRUE;
673 }
674 }
675
676 bool wxFileConfig::Read(const wxString& key, long *pl) const
677 {
678 wxString str;
679 if ( Read(key, & str) ) {
680 *pl = wxAtol(str);
681 return TRUE;
682 }
683 else {
684 return FALSE;
685 }
686 }
687
688 bool wxFileConfig::Write(const wxString& key, const wxString& szValue)
689 {
690 wxConfigPathChanger path(this, key);
691
692 wxString strName = path.Name();
693 if ( strName.IsEmpty() ) {
694 // setting the value of a group is an error
695 wxASSERT_MSG( wxIsEmpty(szValue), wxT("can't set value of a group!") );
696
697 // ... except if it's empty in which case it's a way to force it's creation
698 m_pCurrentGroup->SetDirty();
699
700 // this will add a line for this group if it didn't have it before
701 (void)m_pCurrentGroup->GetGroupLine();
702 }
703 else {
704 // writing an entry
705
706 // check that the name is reasonable
707 if ( strName[0u] == wxCONFIG_IMMUTABLE_PREFIX ) {
708 wxLogError(_("Config entry name cannot start with '%c'."),
709 wxCONFIG_IMMUTABLE_PREFIX);
710 return FALSE;
711 }
712
713 ConfigEntry *pEntry = m_pCurrentGroup->FindEntry(strName);
714 if ( pEntry == NULL )
715 pEntry = m_pCurrentGroup->AddEntry(strName);
716
717 pEntry->SetValue(szValue);
718 }
719
720 return TRUE;
721 }
722
723 bool wxFileConfig::Write(const wxString& key, long lValue)
724 {
725 // ltoa() is not ANSI :-(
726 wxString buf;
727 buf.Printf(wxT("%ld"), lValue);
728 return Write(key, buf);
729 }
730
731 bool wxFileConfig::Flush(bool /* bCurrentOnly */)
732 {
733 if ( LineListIsEmpty() || !m_pRootGroup->IsDirty() )
734 return TRUE;
735
736 wxTempFile file(m_strLocalFile);
737
738 if ( !file.IsOpened() ) {
739 wxLogError(_("can't open user configuration file."));
740 return FALSE;
741 }
742
743 // write all strings to file
744 for ( LineList *p = m_linesHead; p != NULL; p = p->Next() ) {
745 if ( !file.Write(p->Text() + wxTextFile::GetEOL()) ) {
746 wxLogError(_("can't write user configuration file."));
747 return FALSE;
748 }
749 }
750
751 return file.Commit();
752 }
753
754 // ----------------------------------------------------------------------------
755 // renaming groups/entries
756 // ----------------------------------------------------------------------------
757
758 bool wxFileConfig::RenameEntry(const wxString& oldName,
759 const wxString& newName)
760 {
761 // check that the entry exists
762 ConfigEntry *oldEntry = m_pCurrentGroup->FindEntry(oldName);
763 if ( !oldEntry )
764 return FALSE;
765
766 // check that the new entry doesn't already exist
767 if ( m_pCurrentGroup->FindEntry(newName) )
768 return FALSE;
769
770 // delete the old entry, create the new one
771 wxString value = oldEntry->Value();
772 if ( !m_pCurrentGroup->DeleteEntry(oldName) )
773 return FALSE;
774
775 ConfigEntry *newEntry = m_pCurrentGroup->AddEntry(newName);
776 newEntry->SetValue(value);
777
778 return TRUE;
779 }
780
781 bool wxFileConfig::RenameGroup(const wxString& oldName,
782 const wxString& newName)
783 {
784 // check that the group exists
785 ConfigGroup *group = m_pCurrentGroup->FindSubgroup(oldName);
786 if ( !group )
787 return FALSE;
788
789 // check that the new group doesn't already exist
790 if ( m_pCurrentGroup->FindSubgroup(newName) )
791 return FALSE;
792
793 group->Rename(newName);
794
795 return TRUE;
796 }
797
798 // ----------------------------------------------------------------------------
799 // delete groups/entries
800 // ----------------------------------------------------------------------------
801
802 bool wxFileConfig::DeleteEntry(const wxString& key, bool bGroupIfEmptyAlso)
803 {
804 wxConfigPathChanger path(this, key);
805
806 if ( !m_pCurrentGroup->DeleteEntry(path.Name()) )
807 return FALSE;
808
809 if ( bGroupIfEmptyAlso && m_pCurrentGroup->IsEmpty() ) {
810 if ( m_pCurrentGroup != m_pRootGroup ) {
811 ConfigGroup *pGroup = m_pCurrentGroup;
812 SetPath(wxT("..")); // changes m_pCurrentGroup!
813 m_pCurrentGroup->DeleteSubgroupByName(pGroup->Name());
814 }
815 //else: never delete the root group
816 }
817
818 return TRUE;
819 }
820
821 bool wxFileConfig::DeleteGroup(const wxString& key)
822 {
823 wxConfigPathChanger path(this, key);
824
825 return m_pCurrentGroup->DeleteSubgroupByName(path.Name());
826 }
827
828 bool wxFileConfig::DeleteAll()
829 {
830 CleanUp();
831
832 if ( remove(m_strLocalFile.fn_str()) == -1 )
833 wxLogSysError(_("can't delete user configuration file '%s'"), m_strLocalFile.c_str());
834
835 m_strLocalFile = m_strGlobalFile = wxT("");
836 Init();
837
838 return TRUE;
839 }
840
841 // ----------------------------------------------------------------------------
842 // linked list functions
843 // ----------------------------------------------------------------------------
844
845 // append a new line to the end of the list
846 LineList *wxFileConfig::LineListAppend(const wxString& str)
847 {
848 LineList *pLine = new LineList(str);
849
850 if ( m_linesTail == NULL ) {
851 // list is empty
852 m_linesHead = pLine;
853 }
854 else {
855 // adjust pointers
856 m_linesTail->SetNext(pLine);
857 pLine->SetPrev(m_linesTail);
858 }
859
860 m_linesTail = pLine;
861 return m_linesTail;
862 }
863
864 // insert a new line after the given one or in the very beginning if !pLine
865 LineList *wxFileConfig::LineListInsert(const wxString& str,
866 LineList *pLine)
867 {
868 if ( pLine == m_linesTail )
869 return LineListAppend(str);
870
871 LineList *pNewLine = new LineList(str);
872 if ( pLine == NULL ) {
873 // prepend to the list
874 pNewLine->SetNext(m_linesHead);
875 m_linesHead->SetPrev(pNewLine);
876 m_linesHead = pNewLine;
877 }
878 else {
879 // insert before pLine
880 LineList *pNext = pLine->Next();
881 pNewLine->SetNext(pNext);
882 pNewLine->SetPrev(pLine);
883 pNext->SetPrev(pNewLine);
884 pLine->SetNext(pNewLine);
885 }
886
887 return pNewLine;
888 }
889
890 void wxFileConfig::LineListRemove(LineList *pLine)
891 {
892 LineList *pPrev = pLine->Prev(),
893 *pNext = pLine->Next();
894
895 // first entry?
896 if ( pPrev == NULL )
897 m_linesHead = pNext;
898 else
899 pPrev->SetNext(pNext);
900
901 // last entry?
902 if ( pNext == NULL )
903 m_linesTail = pPrev;
904 else
905 pNext->SetPrev(pPrev);
906
907 delete pLine;
908 }
909
910 bool wxFileConfig::LineListIsEmpty()
911 {
912 return m_linesHead == NULL;
913 }
914
915 // ============================================================================
916 // wxFileConfig::ConfigGroup
917 // ============================================================================
918
919 // ----------------------------------------------------------------------------
920 // ctor/dtor
921 // ----------------------------------------------------------------------------
922
923 // ctor
924 ConfigGroup::ConfigGroup(ConfigGroup *pParent,
925 const wxString& strName,
926 wxFileConfig *pConfig)
927 : m_aEntries(CompareEntries),
928 m_aSubgroups(CompareGroups),
929 m_strName(strName)
930 {
931 m_pConfig = pConfig;
932 m_pParent = pParent;
933 m_bDirty = FALSE;
934 m_pLine = NULL;
935
936 m_pLastEntry = NULL;
937 m_pLastGroup = NULL;
938 }
939
940 // dtor deletes all children
941 ConfigGroup::~ConfigGroup()
942 {
943 // entries
944 size_t n, nCount = m_aEntries.Count();
945 for ( n = 0; n < nCount; n++ )
946 delete m_aEntries[n];
947
948 // subgroups
949 nCount = m_aSubgroups.Count();
950 for ( n = 0; n < nCount; n++ )
951 delete m_aSubgroups[n];
952 }
953
954 // ----------------------------------------------------------------------------
955 // line
956 // ----------------------------------------------------------------------------
957
958 void ConfigGroup::SetLine(LineList *pLine)
959 {
960 wxASSERT( m_pLine == NULL ); // shouldn't be called twice
961
962 m_pLine = pLine;
963 }
964
965 /*
966 This is a bit complicated, so let me explain it in details. All lines that
967 were read from the local file (the only one we will ever modify) are stored
968 in a (doubly) linked list. Our problem is to know at which position in this
969 list should we insert the new entries/subgroups. To solve it we keep three
970 variables for each group: m_pLine, m_pLastEntry and m_pLastGroup.
971
972 m_pLine points to the line containing "[group_name]"
973 m_pLastEntry points to the last entry of this group in the local file.
974 m_pLastGroup subgroup
975
976 Initially, they're NULL all three. When the group (an entry/subgroup) is read
977 from the local file, the corresponding variable is set. However, if the group
978 was read from the global file and then modified or created by the application
979 these variables are still NULL and we need to create the corresponding lines.
980 See the following functions (and comments preceding them) for the details of
981 how we do it.
982
983 Also, when our last entry/group are deleted we need to find the new last
984 element - the code in DeleteEntry/Subgroup does this by backtracking the list
985 of lines until it either founds an entry/subgroup (and this is the new last
986 element) or the m_pLine of the group, in which case there are no more entries
987 (or subgroups) left and m_pLast<element> becomes NULL.
988
989 NB: This last problem could be avoided for entries if we added new entries
990 immediately after m_pLine, but in this case the entries would appear
991 backwards in the config file (OTOH, it's not that important) and as we
992 would still need to do it for the subgroups the code wouldn't have been
993 significantly less complicated.
994 */
995
996 // Return the line which contains "[our name]". If we're still not in the list,
997 // add our line to it immediately after the last line of our parent group if we
998 // have it or in the very beginning if we're the root group.
999 LineList *ConfigGroup::GetGroupLine()
1000 {
1001 if ( m_pLine == NULL ) {
1002 ConfigGroup *pParent = Parent();
1003
1004 // this group wasn't present in local config file, add it now
1005 if ( pParent != NULL ) {
1006 wxString strFullName;
1007 strFullName << wxT("[")
1008 // +1: no '/'
1009 << FilterOutEntryName(GetFullName().c_str() + 1)
1010 << wxT("]");
1011 m_pLine = m_pConfig->LineListInsert(strFullName,
1012 pParent->GetLastGroupLine());
1013 pParent->SetLastGroup(this); // we're surely after all the others
1014 }
1015 else {
1016 // we return NULL, so that LineListInsert() will insert us in the
1017 // very beginning
1018 }
1019 }
1020
1021 return m_pLine;
1022 }
1023
1024 // Return the last line belonging to the subgroups of this group (after which
1025 // we can add a new subgroup), if we don't have any subgroups or entries our
1026 // last line is the group line (m_pLine) itself.
1027 LineList *ConfigGroup::GetLastGroupLine()
1028 {
1029 // if we have any subgroups, our last line is the last line of the last
1030 // subgroup
1031 if ( m_pLastGroup != NULL ) {
1032 LineList *pLine = m_pLastGroup->GetLastGroupLine();
1033
1034 wxASSERT( pLine != NULL ); // last group must have !NULL associated line
1035 return pLine;
1036 }
1037
1038 // no subgroups, so the last line is the line of thelast entry (if any)
1039 return GetLastEntryLine();
1040 }
1041
1042 // return the last line belonging to the entries of this group (after which
1043 // we can add a new entry), if we don't have any entries we will add the new
1044 // one immediately after the group line itself.
1045 LineList *ConfigGroup::GetLastEntryLine()
1046 {
1047 if ( m_pLastEntry != NULL ) {
1048 LineList *pLine = m_pLastEntry->GetLine();
1049
1050 wxASSERT( pLine != NULL ); // last entry must have !NULL associated line
1051 return pLine;
1052 }
1053
1054 // no entries: insert after the group header
1055 return GetGroupLine();
1056 }
1057
1058 // ----------------------------------------------------------------------------
1059 // group name
1060 // ----------------------------------------------------------------------------
1061
1062 void ConfigGroup::Rename(const wxString& newName)
1063 {
1064 m_strName = newName;
1065
1066 LineList *line = GetGroupLine();
1067 wxString strFullName;
1068 strFullName << wxT("[") << (GetFullName().c_str() + 1) << wxT("]"); // +1: no '/'
1069 line->SetText(strFullName);
1070
1071 SetDirty();
1072 }
1073
1074 wxString ConfigGroup::GetFullName() const
1075 {
1076 if ( Parent() )
1077 return Parent()->GetFullName() + wxCONFIG_PATH_SEPARATOR + Name();
1078 else
1079 return wxT("");
1080 }
1081
1082 // ----------------------------------------------------------------------------
1083 // find an item
1084 // ----------------------------------------------------------------------------
1085
1086 // use binary search because the array is sorted
1087 ConfigEntry *
1088 ConfigGroup::FindEntry(const wxChar *szName) const
1089 {
1090 size_t i,
1091 lo = 0,
1092 hi = m_aEntries.Count();
1093 int res;
1094 ConfigEntry *pEntry;
1095
1096 while ( lo < hi ) {
1097 i = (lo + hi)/2;
1098 pEntry = m_aEntries[i];
1099
1100 #if wxCONFIG_CASE_SENSITIVE
1101 res = wxStrcmp(pEntry->Name(), szName);
1102 #else
1103 res = wxStricmp(pEntry->Name(), szName);
1104 #endif
1105
1106 if ( res > 0 )
1107 hi = i;
1108 else if ( res < 0 )
1109 lo = i + 1;
1110 else
1111 return pEntry;
1112 }
1113
1114 return NULL;
1115 }
1116
1117 ConfigGroup *
1118 ConfigGroup::FindSubgroup(const wxChar *szName) const
1119 {
1120 size_t i,
1121 lo = 0,
1122 hi = m_aSubgroups.Count();
1123 int res;
1124 ConfigGroup *pGroup;
1125
1126 while ( lo < hi ) {
1127 i = (lo + hi)/2;
1128 pGroup = m_aSubgroups[i];
1129
1130 #if wxCONFIG_CASE_SENSITIVE
1131 res = wxStrcmp(pGroup->Name(), szName);
1132 #else
1133 res = wxStricmp(pGroup->Name(), szName);
1134 #endif
1135
1136 if ( res > 0 )
1137 hi = i;
1138 else if ( res < 0 )
1139 lo = i + 1;
1140 else
1141 return pGroup;
1142 }
1143
1144 return NULL;
1145 }
1146
1147 // ----------------------------------------------------------------------------
1148 // create a new item
1149 // ----------------------------------------------------------------------------
1150
1151 // create a new entry and add it to the current group
1152 ConfigEntry *
1153 ConfigGroup::AddEntry(const wxString& strName, int nLine)
1154 {
1155 wxASSERT( FindEntry(strName) == NULL );
1156
1157 ConfigEntry *pEntry = new ConfigEntry(this, strName, nLine);
1158 m_aEntries.Add(pEntry);
1159
1160 return pEntry;
1161 }
1162
1163 // create a new group and add it to the current group
1164 ConfigGroup *
1165 ConfigGroup::AddSubgroup(const wxString& strName)
1166 {
1167 wxASSERT( FindSubgroup(strName) == NULL );
1168
1169 ConfigGroup *pGroup = new ConfigGroup(this, strName, m_pConfig);
1170 m_aSubgroups.Add(pGroup);
1171
1172 return pGroup;
1173 }
1174
1175 // ----------------------------------------------------------------------------
1176 // delete an item
1177 // ----------------------------------------------------------------------------
1178
1179 /*
1180 The delete operations are _very_ slow if we delete the last item of this
1181 group (see comments before GetXXXLineXXX functions for more details),
1182 so it's much better to start with the first entry/group if we want to
1183 delete several of them.
1184 */
1185
1186 bool ConfigGroup::DeleteSubgroupByName(const wxChar *szName)
1187 {
1188 return DeleteSubgroup(FindSubgroup(szName));
1189 }
1190
1191 // doesn't delete the subgroup itself, but does remove references to it from
1192 // all other data structures (and normally the returned pointer should be
1193 // deleted a.s.a.p. because there is nothing much to be done with it anyhow)
1194 bool ConfigGroup::DeleteSubgroup(ConfigGroup *pGroup)
1195 {
1196 wxCHECK( pGroup != NULL, FALSE ); // deleting non existing group?
1197
1198 // delete all entries
1199 size_t nCount = pGroup->m_aEntries.Count();
1200 for ( size_t nEntry = 0; nEntry < nCount; nEntry++ ) {
1201 LineList *pLine = pGroup->m_aEntries[nEntry]->GetLine();
1202 if ( pLine != NULL )
1203 m_pConfig->LineListRemove(pLine);
1204 }
1205
1206 // and subgroups of this sungroup
1207 nCount = pGroup->m_aSubgroups.Count();
1208 for ( size_t nGroup = 0; nGroup < nCount; nGroup++ ) {
1209 pGroup->DeleteSubgroup(pGroup->m_aSubgroups[0]);
1210 }
1211
1212 LineList *pLine = pGroup->m_pLine;
1213 if ( pLine != NULL ) {
1214 // notice that we may do this test inside the previous "if" because the
1215 // last entry's line is surely !NULL
1216 if ( pGroup == m_pLastGroup ) {
1217 // our last entry is being deleted - find the last one which stays
1218 wxASSERT( m_pLine != NULL ); // we have a subgroup with !NULL pLine...
1219
1220 // go back until we find a subgroup or reach the group's line
1221 ConfigGroup *pNewLast = NULL;
1222 size_t n, nSubgroups = m_aSubgroups.Count();
1223 LineList *pl;
1224 for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() ) {
1225 // is it our subgroup?
1226 for ( n = 0; (pNewLast == NULL) && (n < nSubgroups); n++ ) {
1227 // do _not_ call GetGroupLine! we don't want to add it to the local
1228 // file if it's not already there
1229 if ( m_aSubgroups[n]->m_pLine == m_pLine )
1230 pNewLast = m_aSubgroups[n];
1231 }
1232
1233 if ( pNewLast != NULL ) // found?
1234 break;
1235 }
1236
1237 if ( pl == m_pLine ) {
1238 wxASSERT( !pNewLast ); // how comes it has the same line as we?
1239
1240 // we've reached the group line without finding any subgroups
1241 m_pLastGroup = NULL;
1242 }
1243 else
1244 m_pLastGroup = pNewLast;
1245 }
1246
1247 m_pConfig->LineListRemove(pLine);
1248 }
1249
1250 SetDirty();
1251
1252 m_aSubgroups.Remove(pGroup);
1253 delete pGroup;
1254
1255 return TRUE;
1256 }
1257
1258 bool ConfigGroup::DeleteEntry(const wxChar *szName)
1259 {
1260 ConfigEntry *pEntry = FindEntry(szName);
1261 wxCHECK( pEntry != NULL, FALSE ); // deleting non existing item?
1262
1263 LineList *pLine = pEntry->GetLine();
1264 if ( pLine != NULL ) {
1265 // notice that we may do this test inside the previous "if" because the
1266 // last entry's line is surely !NULL
1267 if ( pEntry == m_pLastEntry ) {
1268 // our last entry is being deleted - find the last one which stays
1269 wxASSERT( m_pLine != NULL ); // if we have an entry with !NULL pLine...
1270
1271 // go back until we find another entry or reach the group's line
1272 ConfigEntry *pNewLast = NULL;
1273 size_t n, nEntries = m_aEntries.Count();
1274 LineList *pl;
1275 for ( pl = pLine->Prev(); pl != m_pLine; pl = pl->Prev() ) {
1276 // is it our subgroup?
1277 for ( n = 0; (pNewLast == NULL) && (n < nEntries); n++ ) {
1278 if ( m_aEntries[n]->GetLine() == m_pLine )
1279 pNewLast = m_aEntries[n];
1280 }
1281
1282 if ( pNewLast != NULL ) // found?
1283 break;
1284 }
1285
1286 if ( pl == m_pLine ) {
1287 wxASSERT( !pNewLast ); // how comes it has the same line as we?
1288
1289 // we've reached the group line without finding any subgroups
1290 m_pLastEntry = NULL;
1291 }
1292 else
1293 m_pLastEntry = pNewLast;
1294 }
1295
1296 m_pConfig->LineListRemove(pLine);
1297 }
1298
1299 // we must be written back for the changes to be saved
1300 SetDirty();
1301
1302 m_aEntries.Remove(pEntry);
1303 delete pEntry;
1304
1305 return TRUE;
1306 }
1307
1308 // ----------------------------------------------------------------------------
1309 //
1310 // ----------------------------------------------------------------------------
1311 void ConfigGroup::SetDirty()
1312 {
1313 m_bDirty = TRUE;
1314 if ( Parent() != NULL ) // propagate upwards
1315 Parent()->SetDirty();
1316 }
1317
1318 // ============================================================================
1319 // wxFileConfig::ConfigEntry
1320 // ============================================================================
1321
1322 // ----------------------------------------------------------------------------
1323 // ctor
1324 // ----------------------------------------------------------------------------
1325 ConfigEntry::ConfigEntry(ConfigGroup *pParent,
1326 const wxString& strName,
1327 int nLine)
1328 : m_strName(strName)
1329 {
1330 wxASSERT( !strName.IsEmpty() );
1331
1332 m_pParent = pParent;
1333 m_nLine = nLine;
1334 m_pLine = NULL;
1335
1336 m_bDirty = FALSE;
1337
1338 m_bImmutable = strName[0] == wxCONFIG_IMMUTABLE_PREFIX;
1339 if ( m_bImmutable )
1340 m_strName.erase(0, 1); // remove first character
1341 }
1342
1343 // ----------------------------------------------------------------------------
1344 // set value
1345 // ----------------------------------------------------------------------------
1346
1347 void ConfigEntry::SetLine(LineList *pLine)
1348 {
1349 if ( m_pLine != NULL ) {
1350 wxLogWarning(_("entry '%s' appears more than once in group '%s'"),
1351 Name().c_str(), m_pParent->GetFullName().c_str());
1352 }
1353
1354 m_pLine = pLine;
1355 Group()->SetLastEntry(this);
1356 }
1357
1358 // second parameter is FALSE if we read the value from file and prevents the
1359 // entry from being marked as 'dirty'
1360 void ConfigEntry::SetValue(const wxString& strValue, bool bUser)
1361 {
1362 if ( bUser && IsImmutable() ) {
1363 wxLogWarning(_("attempt to change immutable key '%s' ignored."),
1364 Name().c_str());
1365 return;
1366 }
1367
1368 // do nothing if it's the same value
1369 if ( strValue == m_strValue )
1370 return;
1371
1372 m_strValue = strValue;
1373
1374 if ( bUser ) {
1375 wxString strVal = FilterOutValue(strValue);
1376 wxString strLine;
1377 strLine << FilterOutEntryName(m_strName) << wxT('=') << strVal;
1378
1379 if ( m_pLine != NULL ) {
1380 // entry was read from the local config file, just modify the line
1381 m_pLine->SetText(strLine);
1382 }
1383 else {
1384 // add a new line to the file
1385 wxASSERT( m_nLine == wxNOT_FOUND ); // consistency check
1386
1387 m_pLine = Group()->Config()->LineListInsert(strLine,
1388 Group()->GetLastEntryLine());
1389 Group()->SetLastEntry(this);
1390 }
1391
1392 SetDirty();
1393 }
1394 }
1395
1396 void ConfigEntry::SetDirty()
1397 {
1398 m_bDirty = TRUE;
1399 Group()->SetDirty();
1400 }
1401
1402 // ============================================================================
1403 // global functions
1404 // ============================================================================
1405
1406 // ----------------------------------------------------------------------------
1407 // compare functions for array sorting
1408 // ----------------------------------------------------------------------------
1409
1410 int CompareEntries(ConfigEntry *p1,
1411 ConfigEntry *p2)
1412 {
1413 #if wxCONFIG_CASE_SENSITIVE
1414 return wxStrcmp(p1->Name(), p2->Name());
1415 #else
1416 return wxStricmp(p1->Name(), p2->Name());
1417 #endif
1418 }
1419
1420 int CompareGroups(ConfigGroup *p1,
1421 ConfigGroup *p2)
1422 {
1423 #if wxCONFIG_CASE_SENSITIVE
1424 return wxStrcmp(p1->Name(), p2->Name());
1425 #else
1426 return wxStricmp(p1->Name(), p2->Name());
1427 #endif
1428 }
1429
1430 // ----------------------------------------------------------------------------
1431 // filter functions
1432 // ----------------------------------------------------------------------------
1433
1434 // undo FilterOutValue
1435 static wxString FilterInValue(const wxString& str)
1436 {
1437 wxString strResult;
1438 strResult.Alloc(str.Len());
1439
1440 bool bQuoted = !str.IsEmpty() && str[0] == '"';
1441
1442 for ( size_t n = bQuoted ? 1 : 0; n < str.Len(); n++ ) {
1443 if ( str[n] == wxT('\\') ) {
1444 switch ( str[++n] ) {
1445 case wxT('n'):
1446 strResult += wxT('\n');
1447 break;
1448
1449 case wxT('r'):
1450 strResult += wxT('\r');
1451 break;
1452
1453 case wxT('t'):
1454 strResult += wxT('\t');
1455 break;
1456
1457 case wxT('\\'):
1458 strResult += wxT('\\');
1459 break;
1460
1461 case wxT('"'):
1462 strResult += wxT('"');
1463 break;
1464 }
1465 }
1466 else {
1467 if ( str[n] != wxT('"') || !bQuoted )
1468 strResult += str[n];
1469 else if ( n != str.Len() - 1 ) {
1470 wxLogWarning(_("unexpected \" at position %d in '%s'."),
1471 n, str.c_str());
1472 }
1473 //else: it's the last quote of a quoted string, ok
1474 }
1475 }
1476
1477 return strResult;
1478 }
1479
1480 // quote the string before writing it to file
1481 static wxString FilterOutValue(const wxString& str)
1482 {
1483 if ( !str )
1484 return str;
1485
1486 wxString strResult;
1487 strResult.Alloc(str.Len());
1488
1489 // quoting is necessary to preserve spaces in the beginning of the string
1490 bool bQuote = wxIsspace(str[0]) || str[0] == wxT('"');
1491
1492 if ( bQuote )
1493 strResult += wxT('"');
1494
1495 wxChar c;
1496 for ( size_t n = 0; n < str.Len(); n++ ) {
1497 switch ( str[n] ) {
1498 case wxT('\n'):
1499 c = wxT('n');
1500 break;
1501
1502 case wxT('\r'):
1503 c = wxT('r');
1504 break;
1505
1506 case wxT('\t'):
1507 c = wxT('t');
1508 break;
1509
1510 case wxT('\\'):
1511 c = wxT('\\');
1512 break;
1513
1514 case wxT('"'):
1515 if ( bQuote ) {
1516 c = wxT('"');
1517 break;
1518 }
1519 //else: fall through
1520
1521 default:
1522 strResult += str[n];
1523 continue; // nothing special to do
1524 }
1525
1526 // we get here only for special characters
1527 strResult << wxT('\\') << c;
1528 }
1529
1530 if ( bQuote )
1531 strResult += wxT('"');
1532
1533 return strResult;
1534 }
1535
1536 // undo FilterOutEntryName
1537 static wxString FilterInEntryName(const wxString& str)
1538 {
1539 wxString strResult;
1540 strResult.Alloc(str.Len());
1541
1542 for ( const wxChar *pc = str.c_str(); *pc != '\0'; pc++ ) {
1543 if ( *pc == wxT('\\') )
1544 pc++;
1545
1546 strResult += *pc;
1547 }
1548
1549 return strResult;
1550 }
1551
1552 // sanitize entry or group name: insert '\\' before any special characters
1553 static wxString FilterOutEntryName(const wxString& str)
1554 {
1555 wxString strResult;
1556 strResult.Alloc(str.Len());
1557
1558 for ( const wxChar *pc = str.c_str(); *pc != wxT('\0'); pc++ ) {
1559 wxChar c = *pc;
1560
1561 // we explicitly allow some of "safe" chars and 8bit ASCII characters
1562 // which will probably never have special meaning
1563 // NB: note that wxCONFIG_IMMUTABLE_PREFIX and wxCONFIG_PATH_SEPARATOR
1564 // should *not* be quoted
1565 if ( !wxIsalnum(c) && !wxStrchr(wxT("@_/-!.*%"), c) && ((c & 0x80) == 0) )
1566 strResult += wxT('\\');
1567
1568 strResult += c;
1569 }
1570
1571 return strResult;
1572 }
1573
1574 // we can't put ?: in the ctor initializer list because it confuses some
1575 // broken compilers (Borland C++)
1576 static wxString GetAppName(const wxString& appName)
1577 {
1578 if ( !appName && wxTheApp )
1579 return wxTheApp->GetAppName();
1580 else
1581 return appName;
1582 }
1583
1584 #endif // wxUSE_CONFIG
1585