1///////////////////////////////////////////////////////////////////////////////
2// Name:        src/msw/iniconf.cpp
3// Purpose:     implementation of wxIniConfig class
4// Author:      Vadim Zeitlin
5// Modified by:
6// Created:     27.07.98
7// RCS-ID:      $Id: iniconf.cpp 41054 2006-09-07 19:01:45Z ABX $
8// Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence:     wxWindows licence
10///////////////////////////////////////////////////////////////////////////////
11
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
14
15#ifdef __BORLANDC__
16    #pragma hdrstop
17#endif
18
19// Doesn't yet compile in Unicode mode
20
21#if wxUSE_CONFIG && !wxUSE_UNICODE
22
23#ifndef   WX_PRECOMP
24    #include "wx/msw/wrapwin.h"
25    #include "wx/dynarray.h"
26    #include "wx/string.h"
27    #include "wx/intl.h"
28    #include "wx/event.h"
29    #include "wx/app.h"
30    #include "wx/utils.h"
31    #include "wx/log.h"
32#endif  //WX_PRECOMP
33
34#include  "wx/config.h"
35#include  "wx/file.h"
36
37#include  "wx/msw/iniconf.h"
38
39// ----------------------------------------------------------------------------
40// constants
41// ----------------------------------------------------------------------------
42
43// we replace all path separators with this character
44#define PATH_SEP_REPLACE  '_'
45
46// ============================================================================
47// implementation
48// ============================================================================
49
50// ----------------------------------------------------------------------------
51// ctor & dtor
52// ----------------------------------------------------------------------------
53
54wxIniConfig::wxIniConfig(const wxString& strAppName,
55                         const wxString& strVendor,
56                         const wxString& localFilename,
57                         const wxString& globalFilename,
58                         long style)
59           : wxConfigBase(strAppName, strVendor, localFilename, globalFilename, style)
60
61#if 0 // This is too complex for some compilers, e.g. BC++ 5.01
62           : wxConfigBase((strAppName.empty() && wxTheApp) ? wxTheApp->GetAppName()
63                                               : strAppName,
64                          strVendor.empty() ? (wxTheApp ? wxTheApp->GetVendorName()
65                                                  : strAppName)
66                                      : strVendor,
67                          localFilename, globalFilename, style)
68#endif
69{
70    if (strAppName.empty() && wxTheApp)
71        SetAppName(wxTheApp->GetAppName());
72    if (strVendor.empty() && wxTheApp)
73        SetVendorName(wxTheApp->GetVendorName());
74
75    m_strLocalFilename = localFilename;
76    if (m_strLocalFilename.empty())
77    {
78        m_strLocalFilename = GetAppName() + wxT(".ini");
79    }
80
81    // append the extension if none given and it's not an absolute file name
82    // (otherwise we assume that they know what they're doing)
83    if ( !wxIsPathSeparator(m_strLocalFilename[0u]) &&
84        m_strLocalFilename.Find(wxT('.')) == wxNOT_FOUND )
85    {
86        m_strLocalFilename << wxT(".ini");
87    }
88
89    // set root path
90    SetPath(wxEmptyString);
91}
92
93wxIniConfig::~wxIniConfig()
94{
95}
96
97// ----------------------------------------------------------------------------
98// path management
99// ----------------------------------------------------------------------------
100
101void wxIniConfig::SetPath(const wxString& strPath)
102{
103  wxArrayString aParts;
104
105  if ( strPath.empty() ) {
106    // nothing
107  }
108  else if ( strPath[0u] == wxCONFIG_PATH_SEPARATOR ) {
109    // absolute path
110    wxSplitPath(aParts, strPath);
111  }
112  else {
113    // relative path, combine with current one
114    wxString strFullPath = GetPath();
115    strFullPath << wxCONFIG_PATH_SEPARATOR << strPath;
116    wxSplitPath(aParts, strFullPath);
117  }
118
119  size_t nPartsCount = aParts.Count();
120  m_strPath.Empty();
121  if ( nPartsCount == 0 ) {
122    // go to the root
123    m_strGroup = PATH_SEP_REPLACE;
124  }
125  else {
126    // translate
127    m_strGroup = aParts[0u];
128    for ( size_t nPart = 1; nPart < nPartsCount; nPart++ ) {
129      if ( nPart > 1 )
130        m_strPath << PATH_SEP_REPLACE;
131      m_strPath << aParts[nPart];
132    }
133  }
134
135  // other functions assume that all this is true, i.e. there are no trailing
136  // underscores at the end except if the group is the root one
137  wxASSERT( (m_strPath.empty() || m_strPath.Last() != PATH_SEP_REPLACE) &&
138            (m_strGroup == wxString(PATH_SEP_REPLACE) ||
139             m_strGroup.Last() != PATH_SEP_REPLACE) );
140}
141
142const wxString& wxIniConfig::GetPath() const
143{
144  static wxString s_str;
145
146  // always return abs path
147  s_str = wxCONFIG_PATH_SEPARATOR;
148
149  if ( m_strGroup == wxString(PATH_SEP_REPLACE) ) {
150    // we're at the root level, nothing to do
151  }
152  else {
153    s_str << m_strGroup;
154    if ( !m_strPath.empty() )
155      s_str << wxCONFIG_PATH_SEPARATOR;
156    for ( const char *p = m_strPath; *p != '\0'; p++ ) {
157      s_str << (*p == PATH_SEP_REPLACE ? wxCONFIG_PATH_SEPARATOR : *p);
158    }
159  }
160
161  return s_str;
162}
163
164wxString wxIniConfig::GetPrivateKeyName(const wxString& szKey) const
165{
166  wxString strKey;
167
168  if ( !m_strPath.empty() )
169    strKey << m_strPath << PATH_SEP_REPLACE;
170
171  strKey << szKey;
172
173  return strKey;
174}
175
176wxString wxIniConfig::GetKeyName(const wxString& szKey) const
177{
178  wxString strKey;
179
180  if ( m_strGroup != wxString(PATH_SEP_REPLACE) )
181    strKey << m_strGroup << PATH_SEP_REPLACE;
182  if ( !m_strPath.empty() )
183    strKey << m_strPath << PATH_SEP_REPLACE;
184
185  strKey << szKey;
186
187  return strKey;
188}
189
190// ----------------------------------------------------------------------------
191// enumeration
192// ----------------------------------------------------------------------------
193
194// not implemented
195bool wxIniConfig::GetFirstGroup(wxString& WXUNUSED(str), long& WXUNUSED(lIndex)) const
196{
197    wxFAIL_MSG("not implemented");
198
199    return false;
200}
201
202bool wxIniConfig::GetNextGroup (wxString& WXUNUSED(str), long& WXUNUSED(lIndex)) const
203{
204    wxFAIL_MSG("not implemented");
205
206    return false;
207}
208
209bool wxIniConfig::GetFirstEntry(wxString& WXUNUSED(str), long& WXUNUSED(lIndex)) const
210{
211    wxFAIL_MSG("not implemented");
212
213    return false;
214}
215
216bool wxIniConfig::GetNextEntry (wxString& WXUNUSED(str), long& WXUNUSED(lIndex)) const
217{
218    wxFAIL_MSG("not implemented");
219
220    return false;
221}
222
223// ----------------------------------------------------------------------------
224// misc info
225// ----------------------------------------------------------------------------
226
227// not implemented
228size_t wxIniConfig::GetNumberOfEntries(bool WXUNUSED(bRecursive)) const
229{
230    wxFAIL_MSG("not implemented");
231
232    return (size_t)-1;
233}
234
235size_t wxIniConfig::GetNumberOfGroups(bool WXUNUSED(bRecursive)) const
236{
237    wxFAIL_MSG("not implemented");
238
239    return (size_t)-1;
240}
241
242bool wxIniConfig::HasGroup(const wxString& WXUNUSED(strName)) const
243{
244    wxFAIL_MSG("not implemented");
245
246    return false;
247}
248
249bool wxIniConfig::HasEntry(const wxString& WXUNUSED(strName)) const
250{
251    wxFAIL_MSG("not implemented");
252
253    return false;
254}
255
256// is current group empty?
257bool wxIniConfig::IsEmpty() const
258{
259    char szBuf[1024];
260
261    GetPrivateProfileString(m_strGroup, NULL, "",
262                            szBuf, WXSIZEOF(szBuf), m_strLocalFilename);
263    if ( !::IsEmpty(szBuf) )
264        return false;
265
266    GetProfileString(m_strGroup, NULL, "", szBuf, WXSIZEOF(szBuf));
267    if ( !::IsEmpty(szBuf) )
268        return false;
269
270    return true;
271}
272
273// ----------------------------------------------------------------------------
274// read/write
275// ----------------------------------------------------------------------------
276
277bool wxIniConfig::DoReadString(const wxString& szKey, wxString *pstr) const
278{
279  wxConfigPathChanger path(this, szKey);
280  wxString strKey = GetPrivateKeyName(path.Name());
281
282  char szBuf[1024]; // @@ should dynamically allocate memory...
283
284  // first look in the private INI file
285
286  // NB: the lpDefault param to GetPrivateProfileString can't be NULL
287  GetPrivateProfileString(m_strGroup, strKey, "",
288                          szBuf, WXSIZEOF(szBuf), m_strLocalFilename);
289  if ( ::IsEmpty(szBuf) ) {
290    // now look in win.ini
291    wxString strKey = GetKeyName(path.Name());
292    GetProfileString(m_strGroup, strKey, "", szBuf, WXSIZEOF(szBuf));
293  }
294
295  if ( ::IsEmpty(szBuf) )
296    return false;
297
298  *pstr = szBuf;
299  return true;
300}
301
302bool wxIniConfig::DoReadLong(const wxString& szKey, long *pl) const
303{
304  wxConfigPathChanger path(this, szKey);
305  wxString strKey = GetPrivateKeyName(path.Name());
306
307  // hack: we have no mean to know if it really found the default value or
308  // didn't find anything, so we call it twice
309
310  static const int nMagic  = 17; // 17 is some "rare" number
311  static const int nMagic2 = 28; // arbitrary number != nMagic
312  long lVal = GetPrivateProfileInt(m_strGroup, strKey, nMagic, m_strLocalFilename);
313  if ( lVal != nMagic ) {
314    // the value was read from the file
315    *pl = lVal;
316    return true;
317  }
318
319  // is it really nMagic?
320  lVal = GetPrivateProfileInt(m_strGroup, strKey, nMagic2, m_strLocalFilename);
321  if ( lVal != nMagic2 ) {
322    // the nMagic it returned was indeed read from the file
323    *pl = lVal;
324    return true;
325  }
326
327  // CS : I have no idea why they should look up in win.ini
328  // and if at all they have to do the same procedure using the two magic numbers
329  // otherwise it always returns true, even if the key was not there at all
330#if 0
331  // no, it was just returning the default value, so now look in win.ini
332 *pl = GetProfileInt(GetVendorName(), GetKeyName(szKey), *pl);
333
334  return true;
335#endif
336  return false ;
337}
338
339bool wxIniConfig::DoWriteString(const wxString& szKey, const wxString& szValue)
340{
341  wxConfigPathChanger path(this, szKey);
342  wxString strKey = GetPrivateKeyName(path.Name());
343
344  bool bOk = WritePrivateProfileString(m_strGroup, strKey,
345                                       szValue, m_strLocalFilename) != 0;
346
347  if ( !bOk )
348    wxLogLastError(wxT("WritePrivateProfileString"));
349
350  return bOk;
351}
352
353bool wxIniConfig::DoWriteLong(const wxString& szKey, long lValue)
354{
355  // ltoa() is not ANSI :-(
356  char szBuf[40];   // should be good for sizeof(long) <= 16 (128 bits)
357  sprintf(szBuf, "%ld", lValue);
358
359  return Write(szKey, szBuf);
360}
361
362bool wxIniConfig::Flush(bool /* bCurrentOnly */)
363{
364  // this is just the way it works
365  return WritePrivateProfileString(NULL, NULL, NULL, m_strLocalFilename) != 0;
366}
367
368// ----------------------------------------------------------------------------
369// delete
370// ----------------------------------------------------------------------------
371
372bool wxIniConfig::DeleteEntry(const wxString& szKey, bool bGroupIfEmptyAlso)
373{
374  // passing NULL as value to WritePrivateProfileString deletes the key
375  wxConfigPathChanger path(this, szKey);
376  wxString strKey = GetPrivateKeyName(path.Name());
377
378  if (WritePrivateProfileString(m_strGroup, strKey,
379                                         (const char*) NULL, m_strLocalFilename) == 0)
380    return false;
381
382  if ( !bGroupIfEmptyAlso || !IsEmpty() )
383    return true;
384
385  // delete the current group too
386  bool bOk = WritePrivateProfileString(m_strGroup, NULL,
387                                       NULL, m_strLocalFilename) != 0;
388
389  if ( !bOk )
390    wxLogLastError(wxT("WritePrivateProfileString"));
391
392  return bOk;
393}
394
395bool wxIniConfig::DeleteGroup(const wxString& szKey)
396{
397  wxConfigPathChanger path(this, szKey);
398
399  // passing NULL as section name to WritePrivateProfileString deletes the
400  // whole section according to the docs
401  bool bOk = WritePrivateProfileString(path.Name(), NULL,
402                                       NULL, m_strLocalFilename) != 0;
403
404  if ( !bOk )
405    wxLogLastError(wxT("WritePrivateProfileString"));
406
407  return bOk;
408}
409
410#ifndef MAX_PATH
411#define MAX_PATH 256
412#endif
413
414bool wxIniConfig::DeleteAll()
415{
416  // first delete our group in win.ini
417  WriteProfileString(GetVendorName(), NULL, NULL);
418
419  // then delete our own ini file
420  char szBuf[MAX_PATH];
421  size_t nRc = GetWindowsDirectory(szBuf, WXSIZEOF(szBuf));
422  if ( nRc == 0 )
423  {
424    wxLogLastError(wxT("GetWindowsDirectory"));
425  }
426  else if ( nRc > WXSIZEOF(szBuf) )
427  {
428    wxFAIL_MSG(wxT("buffer is too small for Windows directory."));
429  }
430
431  wxString strFile = szBuf;
432  strFile << '\\' << m_strLocalFilename;
433
434  if ( wxFile::Exists(strFile) && !wxRemoveFile(strFile) ) {
435    wxLogSysError(_("Can't delete the INI file '%s'"), strFile.c_str());
436    return false;
437  }
438
439  return true;
440}
441
442bool wxIniConfig::RenameEntry(const wxString& WXUNUSED(oldName),
443                              const wxString& WXUNUSED(newName))
444{
445    // Not implemented
446    return false;
447}
448
449bool wxIniConfig::RenameGroup(const wxString& WXUNUSED(oldName),
450                              const wxString& WXUNUSED(newName))
451{
452    // Not implemented
453    return false;
454}
455
456#endif
457    // wxUSE_CONFIG && wxUSE_UNICODE
458