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