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