1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/config.cpp
3 // Purpose:     implementation of wxConfigBase class
4 // Author:      Vadim Zeitlin
5 // Modified by:
6 // Created:     07.04.98
7 // RCS-ID:      $Id: config.cpp 50711 2007-12-15 02:57:58Z VZ $
8 // Copyright:   (c) 1997 Karsten Ballueder   Ballueder@usa.net
9 //                       Vadim Zeitlin      <zeitlin@dptmaths.ens-cachan.fr>
10 // Licence:     wxWindows licence
11 ///////////////////////////////////////////////////////////////////////////////
12 
13 // ----------------------------------------------------------------------------
14 // headers
15 // ----------------------------------------------------------------------------
16 
17 #include "wx/wxprec.h"
18 
19 #ifdef __BORLANDC__
20     #pragma hdrstop
21 #endif  //__BORLANDC__
22 
23 #ifndef wxUSE_CONFIG_NATIVE
24     #define wxUSE_CONFIG_NATIVE 1
25 #endif
26 
27 #include "wx/config.h"
28 
29 #ifndef WX_PRECOMP
30     #include "wx/intl.h"
31     #include "wx/log.h"
32     #include "wx/app.h"
33     #include "wx/utils.h"
34     #include "wx/arrstr.h"
35     #include "wx/math.h"
36 #endif //WX_PRECOMP
37 
38 #if wxUSE_CONFIG && ((wxUSE_FILE && wxUSE_TEXTFILE) || wxUSE_CONFIG_NATIVE)
39 
40 #include "wx/file.h"
41 
42 #include <stdlib.h>
43 #include <ctype.h>
44 #include <limits.h>     // for INT_MAX
45 
46 // ----------------------------------------------------------------------------
47 // global and class static variables
48 // ----------------------------------------------------------------------------
49 
50 wxConfigBase *wxConfigBase::ms_pConfig     = NULL;
51 bool          wxConfigBase::ms_bAutoCreate = true;
52 
53 // ============================================================================
54 // implementation
55 // ============================================================================
56 
57 // ----------------------------------------------------------------------------
58 // wxConfigBase
59 // ----------------------------------------------------------------------------
60 
61 // Not all args will always be used by derived classes, but including them all
62 // in each class ensures compatibility.
wxConfigBase(const wxString & appName,const wxString & vendorName,const wxString & WXUNUSED (localFilename),const wxString & WXUNUSED (globalFilename),long style)63 wxConfigBase::wxConfigBase(const wxString& appName,
64                            const wxString& vendorName,
65                            const wxString& WXUNUSED(localFilename),
66                            const wxString& WXUNUSED(globalFilename),
67                            long style)
68             : m_appName(appName), m_vendorName(vendorName), m_style(style)
69 {
70     m_bExpandEnvVars = true;
71     m_bRecordDefaults = false;
72 }
73 
~wxConfigBase()74 wxConfigBase::~wxConfigBase()
75 {
76     // required here for Darwin
77 }
78 
Set(wxConfigBase * pConfig)79 wxConfigBase *wxConfigBase::Set(wxConfigBase *pConfig)
80 {
81   wxConfigBase *pOld = ms_pConfig;
82   ms_pConfig = pConfig;
83   return pOld;
84 }
85 
Create()86 wxConfigBase *wxConfigBase::Create()
87 {
88   if ( ms_bAutoCreate && ms_pConfig == NULL ) {
89     ms_pConfig =
90     #if defined(__WXMSW__) && wxUSE_CONFIG_NATIVE
91         new wxRegConfig(wxTheApp->GetAppName(), wxTheApp->GetVendorName());
92     #elif defined(__WXPALMOS__) && wxUSE_CONFIG_NATIVE
93         new wxPrefConfig(wxTheApp->GetAppName());
94     #else // either we're under Unix or wish to use files even under Windows
95       new wxFileConfig(wxTheApp->GetAppName());
96     #endif
97   }
98 
99   return ms_pConfig;
100 }
101 
102 // ----------------------------------------------------------------------------
103 // wxConfigBase reading entries
104 // ----------------------------------------------------------------------------
105 
106 // implement both Read() overloads for the given type in terms of DoRead()
107 #define IMPLEMENT_READ_FOR_TYPE(name, type, deftype, extra)                 \
108     bool wxConfigBase::Read(const wxString& key, type *val) const           \
109     {                                                                       \
110         wxCHECK_MSG( val, false, _T("wxConfig::Read(): NULL parameter") );  \
111                                                                             \
112         if ( !DoRead##name(key, val) )                                      \
113             return false;                                                   \
114                                                                             \
115         *val = extra(*val);                                                 \
116                                                                             \
117         return true;                                                        \
118     }                                                                       \
119                                                                             \
120     bool wxConfigBase::Read(const wxString& key,                            \
121                             type *val,                                      \
122                             deftype defVal) const                           \
123     {                                                                       \
124         wxCHECK_MSG( val, false, _T("wxConfig::Read(): NULL parameter") );  \
125                                                                             \
126         bool read = DoRead##name(key, val);                                 \
127         if ( !read )                                                        \
128         {                                                                   \
129             if ( IsRecordingDefaults() )                                    \
130             {                                                               \
131                 ((wxConfigBase *)this)->DoWrite##name(key, defVal);         \
132             }                                                               \
133                                                                             \
134             *val = defVal;                                                  \
135         }                                                                   \
136                                                                             \
137         *val = extra(*val);                                                 \
138                                                                             \
139         return read;                                                        \
140     }
141 
142 
IMPLEMENT_READ_FOR_TYPE(String,wxString,const wxString &,ExpandEnvVars)143 IMPLEMENT_READ_FOR_TYPE(String, wxString, const wxString&, ExpandEnvVars)
144 IMPLEMENT_READ_FOR_TYPE(Long, long, long, long)
145 IMPLEMENT_READ_FOR_TYPE(Int, int, int, int)
146 IMPLEMENT_READ_FOR_TYPE(Double, double, double, double)
147 IMPLEMENT_READ_FOR_TYPE(Bool, bool, bool, bool)
148 
149 #undef IMPLEMENT_READ_FOR_TYPE
150 
151 // the DoReadXXX() for the other types have implementation in the base class
152 // but can be overridden in the derived ones
153 bool wxConfigBase::DoReadInt(const wxString& key, int *pi) const
154 {
155     wxCHECK_MSG( pi, false, _T("wxConfig::Read(): NULL parameter") );
156 
157     long l;
158     if ( !DoReadLong(key, &l) )
159         return false;
160 
161     wxASSERT_MSG( l < INT_MAX, _T("overflow in wxConfig::DoReadInt") );
162 
163     *pi = (int)l;
164 
165     return true;
166 }
167 
DoReadBool(const wxString & key,bool * val) const168 bool wxConfigBase::DoReadBool(const wxString& key, bool* val) const
169 {
170     wxCHECK_MSG( val, false, _T("wxConfig::Read(): NULL parameter") );
171 
172     long l;
173     if ( !DoReadLong(key, &l) )
174         return false;
175 
176     wxASSERT_MSG( l == 0 || l == 1, _T("bad bool value in wxConfig::DoReadInt") );
177 
178     *val = l != 0;
179 
180     return true;
181 }
182 
DoReadDouble(const wxString & key,double * val) const183 bool wxConfigBase::DoReadDouble(const wxString& key, double* val) const
184 {
185     wxString str;
186     if ( Read(key, &str) )
187     {
188         return str.ToDouble(val);
189     }
190 
191     return false;
192 }
193 
194 // string reading helper
ExpandEnvVars(const wxString & str) const195 wxString wxConfigBase::ExpandEnvVars(const wxString& str) const
196 {
197     wxString tmp; // Required for BC++
198     if (IsExpandingEnvVars())
199         tmp = wxExpandEnvVars(str);
200     else
201         tmp = str;
202     return tmp;
203 }
204 
205 // ----------------------------------------------------------------------------
206 // wxConfigBase writing
207 // ----------------------------------------------------------------------------
208 
DoWriteDouble(const wxString & key,double val)209 bool wxConfigBase::DoWriteDouble(const wxString& key, double val)
210 {
211     return DoWriteString(key, wxString::Format(_T("%g"), val));
212 }
213 
DoWriteInt(const wxString & key,int value)214 bool wxConfigBase::DoWriteInt(const wxString& key, int value)
215 {
216     return DoWriteLong(key, (long)value);
217 }
218 
DoWriteBool(const wxString & key,bool value)219 bool wxConfigBase::DoWriteBool(const wxString& key, bool value)
220 {
221     return DoWriteLong(key, value ? 1l : 0l);
222 }
223 
224 // ----------------------------------------------------------------------------
225 // wxConfigPathChanger
226 // ----------------------------------------------------------------------------
227 
wxConfigPathChanger(const wxConfigBase * pContainer,const wxString & strEntry)228 wxConfigPathChanger::wxConfigPathChanger(const wxConfigBase *pContainer,
229                                          const wxString& strEntry)
230 {
231   m_bChanged = false;
232   m_pContainer = (wxConfigBase *)pContainer;
233 
234   // the path is everything which precedes the last slash
235   wxString strPath = strEntry.BeforeLast(wxCONFIG_PATH_SEPARATOR);
236 
237   // except in the special case of "/keyname" when there is nothing before "/"
238   if ( strPath.empty() &&
239        ((!strEntry.empty()) && strEntry[0] == wxCONFIG_PATH_SEPARATOR) )
240   {
241     strPath = wxCONFIG_PATH_SEPARATOR;
242   }
243 
244   if ( !strPath.empty() )
245   {
246     if ( m_pContainer->GetPath() != strPath )
247     {
248         // we do change the path so restore it later
249         m_bChanged = true;
250 
251         /* JACS: work around a memory bug that causes an assert
252            when using wxRegConfig, related to reference-counting.
253            Can be reproduced by removing (const wxChar*) below and
254            adding the following code to the config sample OnInit under
255            Windows:
256 
257            pConfig->SetPath(wxT("MySettings"));
258            pConfig->SetPath(wxT(".."));
259            int value;
260            pConfig->Read(_T("MainWindowX"), & value);
261         */
262         m_strOldPath = (const wxChar*) m_pContainer->GetPath();
263         if ( *m_strOldPath.c_str() != wxCONFIG_PATH_SEPARATOR )
264           m_strOldPath += wxCONFIG_PATH_SEPARATOR;
265         m_pContainer->SetPath(strPath);
266     }
267 
268     // in any case, use the just the name, not full path
269     m_strName = strEntry.AfterLast(wxCONFIG_PATH_SEPARATOR);
270   }
271   else {
272     // it's a name only, without path - nothing to do
273     m_strName = strEntry;
274   }
275 }
276 
UpdateIfDeleted()277 void wxConfigPathChanger::UpdateIfDeleted()
278 {
279     // we don't have to do anything at all if we didn't change the path
280     if ( !m_bChanged )
281         return;
282 
283     // find the deepest still existing parent path of the original path
284     while ( !m_pContainer->HasGroup(m_strOldPath) )
285     {
286         m_strOldPath = m_strOldPath.BeforeLast(wxCONFIG_PATH_SEPARATOR);
287         if ( m_strOldPath.empty() )
288             m_strOldPath = wxCONFIG_PATH_SEPARATOR;
289     }
290 }
291 
~wxConfigPathChanger()292 wxConfigPathChanger::~wxConfigPathChanger()
293 {
294   // only restore path if it was changed
295   if ( m_bChanged ) {
296     m_pContainer->SetPath(m_strOldPath);
297   }
298 }
299 
300 // this is a wxConfig method but it's mainly used with wxConfigPathChanger
301 /* static */
RemoveTrailingSeparator(const wxString & key)302 wxString wxConfigBase::RemoveTrailingSeparator(const wxString& key)
303 {
304     wxString path(key);
305 
306     // don't remove the only separator from a root group path!
307     while ( path.length() > 1 )
308     {
309         if ( *path.rbegin() != wxCONFIG_PATH_SEPARATOR )
310             break;
311 
312         path.erase(path.end() - 1);
313     }
314 
315     return path;
316 }
317 
318 #endif // wxUSE_CONFIG
319 
320 // ----------------------------------------------------------------------------
321 // static & global functions
322 // ----------------------------------------------------------------------------
323 
324 // understands both Unix and Windows (but only under Windows) environment
325 // variables expansion: i.e. $var, $(var) and ${var} are always understood
326 // and in addition under Windows %var% is also.
327 
328 // don't change the values the enum elements: they must be equal
329 // to the matching [closing] delimiter.
330 enum Bracket
331 {
332   Bracket_None,
333   Bracket_Normal  = ')',
334   Bracket_Curly   = '}',
335 #ifdef  __WXMSW__
336   Bracket_Windows = '%',    // yeah, Windows people are a bit strange ;-)
337 #endif
338   Bracket_Max
339 };
340 
wxExpandEnvVars(const wxString & str)341 wxString wxExpandEnvVars(const wxString& str)
342 {
343   wxString strResult;
344   strResult.Alloc(str.length());
345 
346   size_t m;
347   for ( size_t n = 0; n < str.length(); n++ ) {
348     switch ( str[n] ) {
349 #ifdef  __WXMSW__
350       case wxT('%'):
351 #endif  //WINDOWS
352       case wxT('$'):
353         {
354           Bracket bracket;
355           #ifdef  __WXMSW__
356             if ( str[n] == wxT('%') )
357               bracket = Bracket_Windows;
358             else
359           #endif  //WINDOWS
360           if ( n == str.length() - 1 ) {
361             bracket = Bracket_None;
362           }
363           else {
364             switch ( str[n + 1] ) {
365               case wxT('('):
366                 bracket = Bracket_Normal;
367                 n++;                   // skip the bracket
368                 break;
369 
370               case wxT('{'):
371                 bracket = Bracket_Curly;
372                 n++;                   // skip the bracket
373                 break;
374 
375               default:
376                 bracket = Bracket_None;
377             }
378           }
379 
380           m = n + 1;
381 
382           while ( m < str.length() && (wxIsalnum(str[m]) || str[m] == wxT('_')) )
383             m++;
384 
385           wxString strVarName(str.c_str() + n + 1, m - n - 1);
386 
387 #ifdef __WXWINCE__
388           const wxChar *pszValue = NULL;
389 #else
390           // NB: use wxGetEnv instead of wxGetenv as otherwise variables
391           //     set through wxSetEnv may not be read correctly!
392           const wxChar *pszValue = NULL;
393           wxString tmp;
394           if (wxGetEnv(strVarName, &tmp))
395               pszValue = tmp;
396 #endif
397           if ( pszValue != NULL ) {
398             strResult += pszValue;
399           }
400           else {
401             // variable doesn't exist => don't change anything
402             #ifdef  __WXMSW__
403               if ( bracket != Bracket_Windows )
404             #endif
405                 if ( bracket != Bracket_None )
406                   strResult << str[n - 1];
407             strResult << str[n] << strVarName;
408           }
409 
410           // check the closing bracket
411           if ( bracket != Bracket_None ) {
412             if ( m == str.length() || str[m] != (wxChar)bracket ) {
413               // under MSW it's common to have '%' characters in the registry
414               // and it's annoying to have warnings about them each time, so
415               // ignroe them silently if they are not used for env vars
416               //
417               // under Unix, OTOH, this warning could be useful for the user to
418               // understand why isn't the variable expanded as intended
419               #ifndef __WXMSW__
420                 wxLogWarning(_("Environment variables expansion failed: missing '%c' at position %u in '%s'."),
421                              (char)bracket, (unsigned int) (m + 1), str.c_str());
422               #endif // __WXMSW__
423             }
424             else {
425               // skip closing bracket unless the variables wasn't expanded
426               if ( pszValue == NULL )
427                 strResult << (wxChar)bracket;
428               m++;
429             }
430           }
431 
432           n = m - 1;  // skip variable name
433         }
434         break;
435 
436       case '\\':
437         // backslash can be used to suppress special meaning of % and $
438         if ( n != str.length() - 1 &&
439                 (str[n + 1] == wxT('%') || str[n + 1] == wxT('$')) ) {
440           strResult += str[++n];
441 
442           break;
443         }
444         //else: fall through
445 
446       default:
447         strResult += str[n];
448     }
449   }
450 
451   return strResult;
452 }
453 
454 // this function is used to properly interpret '..' in path
wxSplitPath(wxArrayString & aParts,const wxChar * sz)455 void wxSplitPath(wxArrayString& aParts, const wxChar *sz)
456 {
457   aParts.clear();
458 
459   wxString strCurrent;
460   const wxChar *pc = sz;
461   for ( ;; ) {
462     if ( *pc == wxT('\0') || *pc == wxCONFIG_PATH_SEPARATOR ) {
463       if ( strCurrent == wxT(".") ) {
464         // ignore
465       }
466       else if ( strCurrent == wxT("..") ) {
467         // go up one level
468         if ( aParts.size() == 0 )
469           wxLogWarning(_("'%s' has extra '..', ignored."), sz);
470         else
471           aParts.erase(aParts.end() - 1);
472 
473         strCurrent.Empty();
474       }
475       else if ( !strCurrent.empty() ) {
476         aParts.push_back(strCurrent);
477         strCurrent.Empty();
478       }
479       //else:
480         // could log an error here, but we prefer to ignore extra '/'
481 
482       if ( *pc == wxT('\0') )
483         break;
484     }
485     else
486       strCurrent += *pc;
487 
488     pc++;
489   }
490 }
491