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