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