1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11901 $
6  * $Id: configmanager.cpp 11901 2019-11-04 19:35:26Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/configmanager.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13     #include "configmanager.h"
14     #include "globals.h"
15     #include "personalitymanager.h"
16     #include "cbexception.h"
17     #include "logmanager.h"
18     #include <wx/file.h>
19     #include <wx/dir.h>
20     #include <wx/log.h> // for wxSafeShowMessage()
21 #endif
22 
23 #include "crc32.h"
24 
25 #include <wx/url.h>
26 #include <wx/stream.h>
27 #include <wx/stdpaths.h>
28 #include <wx/filename.h>
29 
30 #ifdef __WXMSW__
31 #include <shlobj.h>
32 #endif
33 
34 #include "annoyingdialog.h"
35 
36 #if defined(__APPLE__) && defined(__MACH__)
37 #include <sys/param.h>
38 #include <mach-o/dyld.h>
39 #endif
40 
41 #ifdef __WXMAC__
42 #if wxCHECK_VERSION(3, 0, 0)
43 #include "wx/osx/core/cfstring.h"
44 #else
45 #include "wx/mac/corefoundation/cfstring.h"
46 #endif
47 #include "wx/intl.h"
48 
49 #include <CoreFoundation/CFBundle.h>
50 #include <CoreFoundation/CFURL.h>
51 #endif
52 
53 #include "tinywxuni.h"
54 #include <stdlib.h>
55 
56 #ifdef __linux__
57 #include <glib.h>
58 #endif // __linux__
59 
60 template<> CfgMgrBldr* Mgr<CfgMgrBldr>::instance = nullptr;
61 template<> bool  Mgr<CfgMgrBldr>::isShutdown = false;
62 
63 wxString ConfigManager::alternate_user_data_path;
64 bool ConfigManager::has_alternate_user_data_path=false;
65 
66 wxString ConfigManager::config_folder;
67 wxString ConfigManager::home_folder;
68 wxString ConfigManager::data_path_user;
69 wxString ConfigManager::data_path_global;
70 #ifdef CB_AUTOCONF
71 wxString ConfigManager::plugin_path_global;
72 #endif
73 wxString ConfigManager::app_path;
74 wxString ConfigManager::temp_folder;
75 
76 
77 namespace CfgMgrConsts
78 {
79     const wxString app_path(_T("app_path"));
80     const wxString data_path(_T("data_path"));
81     const wxString dotDot(_T(".."));
82     const int version = 1;
83 }
84 
85 
86 namespace
87 {
DetermineExecutablePath()88     wxString DetermineExecutablePath()
89     {
90         #ifdef __WXMSW__
91             wxChar name[MAX_PATH];
92             GetModuleFileName(0L, name, MAX_PATH);
93             wxFileName fname(name);
94             return fname.GetPath(wxPATH_GET_VOLUME);
95         #else
96         #ifdef __linux__
97             char c[PATH_MAX+1];
98             char *p = realpath("/proc/self/exe", &c[0]);
99             if (p == nullptr)
100                 return _T(".");
101             wxFileName fname(cbC2U(p));
102             return fname.GetPath(wxPATH_GET_VOLUME);
103         #elif defined(sun) || defined(__sun)
104             wxFileName fname(cbC2U(getexecname()));
105             return fname.GetPath(wxPATH_GET_VOLUME);
106         #elif defined(__APPLE__) && defined(__MACH__)
107             char path[MAXPATHLEN+1];
108             uint32_t path_len = MAXPATHLEN;
109             // SPI first appeared in Mac OS X 10.2
110             _NSGetExecutablePath(path, &path_len);
111             wxFileName fname(wxString(path, wxConvUTF8));
112             return fname.GetPath(wxPATH_GET_VOLUME);
113         #else
114             return _T(".");
115         #endif
116         #endif
117     }
118 
DetermineResourcesPath()119     wxString DetermineResourcesPath()
120     {
121         #if defined(__WXMAC__)
122             CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
123             CFURLRef absoluteURL  = CFURLCopyAbsoluteURL(resourcesURL); // relative -> absolute
124             CFRelease(resourcesURL);
125             CFStringRef cfStrPath = CFURLCopyFileSystemPath(absoluteURL,kCFURLPOSIXPathStyle);
126             CFRelease(absoluteURL);
127             #if wxCHECK_VERSION(3, 0, 0)
128               wxString str = wxCFStringRef(cfStrPath).AsString(wxLocale::GetSystemEncoding());
129             #else
130               wxString str = wxMacCFStringHolder(cfStrPath).AsString(wxLocale::GetSystemEncoding());
131             #endif
132             if (!str.Contains(wxString(_T("/Resources"))))
133                return ::DetermineExecutablePath() + _T("/.."); // not a bundle, use relative path
134             return str;
135         #else
136             return _T(".");
137         #endif
138     }
139 }
140 
141 
Collapse(wxString & str) const142 inline void ConfigManager::Collapse(wxString& str) const
143 {
144     const wxChar *src = str.c_str();
145     wxChar *dst = (wxChar*) src;
146     wxChar c;
147     size_t len = 0;
148 
149     while ((c = *src))
150     {
151         ++src;
152 
153         *dst = c;
154         ++dst;
155         ++len;
156 
157         if (c == _T('/'))
158         while (*src == _T('/'))
159             ++src;
160     }
161     str.Truncate(len);
162 }
163 
ISerializable()164 ISerializable::ISerializable()
165 {}
166 
~ISerializable()167 ISerializable::~ISerializable()
168 {}
169 
170 
171 
172 
173 /* ------------------------------------------------------------------------------------------------------------------
174 *  "Builder pattern" class for ConfigManager
175 *  Do not use this class  -  Manager::Get()->GetConfigManager() is a lot friendlier
176 */
177 
CfgMgrBldr()178 CfgMgrBldr::CfgMgrBldr() : doc(nullptr), volatile_doc(nullptr), r(false)
179 {
180     ConfigManager::MigrateFolders();
181 
182     TiXmlBase::SetCondenseWhiteSpace(false);
183     wxString personality(Manager::Get()->GetPersonalityManager()->GetPersonality());
184 
185     if (personality.StartsWith(_T("http://")))
186     {
187         SwitchToR(personality);
188         return;
189     }
190 
191     cfg = FindConfigFile(personality + _T(".conf"));
192 
193     if (cfg.IsEmpty())
194     {
195         cfg = ConfigManager::GetConfigFolder() + wxFILE_SEP_PATH + personality + _T(".conf");
196         doc = new TiXmlDocument();
197         doc->InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
198         doc->InsertEndChild(TiXmlElement("CodeBlocksConfig"));
199         doc->FirstChildElement("CodeBlocksConfig")->SetAttribute("version", CfgMgrConsts::version);
200         return;
201     }
202     SwitchTo(cfg);
203 }
204 
205 
FindConfigFile(const wxString & filename)206 wxString CfgMgrBldr::FindConfigFile(const wxString& filename)
207 {
208 
209     wxString u(ConfigManager::GetUserDataFolder() + wxFILE_SEP_PATH + filename);
210     wxString exePath(::DetermineExecutablePath());
211     wxString e(exePath + wxFILE_SEP_PATH + filename);
212 
213     if (!ConfigManager::has_alternate_user_data_path && ::wxFileExists(e))
214     {
215         ConfigManager::SetUserDataFolder(exePath);
216         return e;
217     }
218     if (::wxFileExists(u))
219     {
220         return u;
221     }
222     return wxEmptyString;
223 }
224 
225 /// Print error message an allow the user to either discard the old config or close the application.
226 /// Call this function when you've detected an error while reading the config.
handleConfigError(TiXmlDocument & doc,const wxString & fileName,const wxString & additionalMessage)227 static void handleConfigError(TiXmlDocument &doc, const wxString &fileName, const wxString &additionalMessage)
228 {
229     wxString message;
230     if (doc.ErrorId())
231     {
232         message = wxString::Format(_("TinyXML error: %s\nIn file: %s\nAt row %d, column: %d.\n\n"),
233                                    cbC2U(doc.ErrorDesc()).c_str(), fileName.wx_str(),
234                                    doc.ErrorRow(), doc.ErrorCol());
235     }
236     message += additionalMessage;
237 
238     // Show a message box and ask the user to either abort or discard the old config.
239     wxMessageDialog dlg(Manager::Get()->GetAppWindow(),
240                         message + _("\n\nDiscard old config file?"), _("Config file read error"),
241                         wxSTAY_ON_TOP|wxCENTRE|wxYES|wxNO|wxNO_DEFAULT|wxICON_ERROR);
242 #if wxCHECK_VERSION(3, 0, 0)
243     dlg.SetYesNoLabels(_("&Discard"), _("&Close"));
244 #endif
245     if (dlg.ShowModal() != wxID_YES)
246         cbThrow(message);
247 
248     doc.ClearError();
249     doc.InsertEndChild(TiXmlDeclaration("1.0", "UTF-8", "yes"));
250     doc.InsertEndChild(TiXmlElement("CodeBlocksConfig"));
251     doc.FirstChildElement("CodeBlocksConfig")->SetAttribute("version", CfgMgrConsts::version);
252 }
253 
SwitchTo(const wxString & fileName)254 void CfgMgrBldr::SwitchTo(const wxString& fileName)
255 {
256     doc = new TiXmlDocument();
257 
258     if (!TinyXML::LoadDocument(fileName, doc))
259     {
260         const wxString message = wxString::Format(_("Error reading config file: %s"),
261                                                   fileName.wx_str());
262         handleConfigError(*doc, fileName, message);
263     }
264 
265     TiXmlElement* docroot = doc->FirstChildElement("CodeBlocksConfig");
266     if (!docroot)
267     {
268         const wxString message = wxString::Format(wxT("Cannot find docroot in config file '%s'"),
269                                                   fileName.wx_str());
270         handleConfigError(*doc, fileName, message);
271         docroot = doc->FirstChildElement("CodeBlocksConfig");
272 
273         if (!docroot)
274             cbThrow(wxT("Something really bad happened while reading the config file. Aborting!"));
275     }
276 
277     const char *vers = docroot->Attribute("version");
278     if (!vers || atoi(vers) != 1)
279         cbMessageBox(_("ConfigManager encountered an unknown config file version. Continuing happily."), _("Warning"), wxICON_WARNING);
280 
281     doc->ClearError();
282 
283     wxString info;
284     info.Printf(_T(" application info:\n"
285                     "\t svn_revision:\t%u\n"
286                     "\t build_date:\t%s, %s\n"), ConfigManager::GetRevisionNumber(), wxT(__DATE__), wxT(__TIME__));
287 #if defined(__clang__)
288     info += wxString::Format(wxT("\t compiler_version:\tclang %d.%d.%d\n"), __clang_major__,
289                              __clang_minor__, __clang_patchlevel__);
290 #elif defined(__GNUC__)
291     info += wxString::Format(wxT("\t compiler_version:\tgcc %d.%d.%d\n"), __GNUC__, __GNUC_MINOR__,
292                              __GNUC_PATCHLEVEL__);
293 #endif
294 
295     if (platform::windows)
296         info.append(_T("\t Windows "));
297     else if (platform::Linux)
298         info.append(_T("\t Linux "));
299     else if (platform::macosx)
300         info.append(_T("\t Mac OS X "));
301     else if (platform::Unix)
302         info.append(_T("\t Unix "));
303 
304     info.append(platform::unicode ? _T("Unicode") : _T("ANSI"));
305     info.append(wxT("\n"));
306     TiXmlComment c;
307     c.SetValue((const char*) info.mb_str());
308 
309     TiXmlNode *firstchild = docroot->FirstChild();
310     if (firstchild && firstchild->ToComment())
311     {
312         docroot->RemoveChild(firstchild);
313         firstchild = docroot->FirstChild();
314     }
315 
316     if (firstchild)
317         docroot->InsertBeforeChild(firstchild, c);
318     else
319         docroot->InsertEndChild(c);
320 }
321 
SwitchToR(const wxString & absFileName)322 void CfgMgrBldr::SwitchToR(const wxString& absFileName)
323 {
324     if (doc)
325         delete doc;
326     doc = new TiXmlDocument();
327     doc->ClearError();
328 
329     cfg = absFileName;
330 
331     wxURL url(absFileName);
332     url.SetProxy(ConfigManager::GetProxy());
333     if (url.GetError() == wxURL_NOERR)
334     {
335         wxInputStream *is = url.GetInputStream();
336         if (is && is->IsOk())
337         {
338             size_t size = is->GetSize();
339             wxString str;
340             #if wxCHECK_VERSION(3, 0, 0)
341             wxChar* c = wxStringBuffer(str, size);
342             #else
343             wxChar* c = str.GetWriteBuf(size);
344             #endif
345             is->Read(c, size);
346             #if !wxCHECK_VERSION(3, 0, 0)
347             str.UngetWriteBuf(size);
348             #endif
349 
350             doc = new TiXmlDocument();
351 
352             if (doc->Parse(cbU2C(str)))
353             {
354                 doc->ClearError();
355                 delete is;
356                 return;
357             }
358             if (Manager::Get()->GetLogManager())
359             {
360                 Manager::Get()->GetLogManager()->DebugLog(_T("##### Error loading or parsing remote config file"));
361                 Manager::Get()->GetLogManager()->DebugLog(cbC2U(doc->ErrorDesc()));
362                 doc->ClearError();
363             }
364         }
365         delete is;
366     }
367     cfg.Empty();
368     SwitchTo(wxEmptyString); // fall back
369 }
370 
~CfgMgrBldr()371 CfgMgrBldr::~CfgMgrBldr()
372 {
373     NamespaceMap::iterator it;
374     for ( it = namespaces.begin(); it != namespaces.end(); ++it )
375         delete it->second;
376 
377     namespaces.clear();
378     Close();
379     delete volatile_doc;
380 }
381 
Flush()382 void CfgMgrBldr::Flush()
383 {
384     if (doc)
385     {
386         if (!cfg.StartsWith(_T("http://")))
387         {
388             bool done = false;
389             do
390             {
391                 if (TinyXML::SaveDocument(cfg, doc))
392                     done = true;
393                 else
394                 {
395                     AnnoyingDialog dlg(_("Error"),
396                                        F(_T("Could not save config file '%s'!"), cfg.wx_str()),
397                                        wxART_ERROR, AnnoyingDialog::TWO_BUTTONS,
398                                        AnnoyingDialog::rtTWO, _("&Retry"), _("&Close"));
399                     PlaceWindow(&dlg);
400                     switch (dlg.ShowModal())
401                     {
402                         case AnnoyingDialog::rtONE:
403                             done = false;
404                             break;
405                         case AnnoyingDialog::rtTWO:
406                         default:
407                             done = true;
408                     }
409                 }
410             } while (!done);
411         }
412         else
413         {
414             // implement WebDAV another time
415         }
416     }
417 }
418 
Close()419 void CfgMgrBldr::Close()
420 {
421     Flush();
422 
423     if (doc)
424         delete doc;
425 
426     doc = nullptr;
427 }
428 
429 
GetConfigManager(const wxString & name_space)430 ConfigManager* CfgMgrBldr::GetConfigManager(const wxString& name_space)
431 {
432     return Get()->Build(name_space);
433 }
434 
435 
Build(const wxString & name_space)436 ConfigManager* CfgMgrBldr::Build(const wxString& name_space)
437 {
438     if (name_space.IsEmpty())
439         cbThrow(_T("You attempted to get a ConfigManager instance without providing a namespace."));
440 
441     wxCriticalSectionLocker locker(cs);
442     NamespaceMap::iterator it = namespaces.find(name_space);
443     if (it != namespaces.end())
444         return it->second;
445 
446     TiXmlElement* docroot;
447 
448     if (name_space.StartsWith(_T("volatile:")))
449     {
450         if (!volatile_doc)
451         {
452             volatile_doc = new TiXmlDocument();
453             volatile_doc->InsertEndChild(TiXmlElement("CodeBlocksConfig"));
454             volatile_doc->SetCondenseWhiteSpace(false);
455         }
456         docroot = volatile_doc->FirstChildElement("CodeBlocksConfig");
457     }
458     else
459     {
460         docroot = doc->FirstChildElement("CodeBlocksConfig");
461         if (!docroot)
462         {
463             wxString err(_("Fatal error parsing supplied configuration file.\nParser error message:\n"));
464             err << wxString::Format(_T("%s\nAt row %d, column: %d."), cbC2U(doc->ErrorDesc()).c_str(), doc->ErrorRow(), doc->ErrorCol());
465             cbThrow(err);
466         }
467     }
468 
469     TiXmlElement* root = docroot->FirstChildElement(cbU2C(name_space));
470 
471     if (!root) // namespace does not exist
472     {
473         docroot->InsertEndChild(TiXmlElement(cbU2C(name_space)));
474         root = docroot->FirstChildElement(cbU2C(name_space));
475     }
476 
477     if (!root) // now what!
478         cbThrow(_T("Unable to create namespace in document tree (actually not possible..?)"));
479 
480     ConfigManager *c = new ConfigManager(root);
481     namespaces[name_space] = c;
482 
483     return c;
484 }
485 
GetConfigFile() const486 wxString CfgMgrBldr::GetConfigFile() const
487 {
488     return cfg;
489 }
490 
491 /*
492 *  Hack to enable Turkish language. wxString::Upper will convert lowercase 'i' to \u0130 instead of \u0069 in Turkish locale,
493 *  which will break the config file when used in a tag
494 */
to_upper(wxString & s)495 inline void to_upper(wxString& s)
496 {
497     #if wxCHECK_VERSION(3, 0, 0)
498     wxStringCharType *p = const_cast<wxStringCharType*>(s.wx_str());
499     wxStringCharType q;
500     #else
501     wxChar *p = (wxChar*) s.c_str();
502     wxChar q;
503     #endif
504     size_t len = s.length()+1;
505     for (;--len;++p)
506     {
507         q = *p;
508         if (q >= 'a' && q <= 'z')
509             *p = q - 32;
510     }
511 }
512 
to_lower(wxString & s)513 inline void to_lower(wxString& s)
514 {
515     #if wxCHECK_VERSION(3, 0, 0)
516     wxStringCharType *p = const_cast<wxStringCharType*>(s.wx_str());
517     wxStringCharType q;
518     #else
519     wxChar *p = (wxChar*) s.c_str();
520     wxChar q;
521     #endif
522     size_t len = s.length()+1;
523     for (;--len;++p)
524     {
525         q = *p;
526         if (q >= 'A' && q <= 'Z')
527             *p = q + 32;
528     }
529 }
530 
531 
532 /* ------------------------------------------------------------------------------------------------------------------
533 *  Functions to retrieve system paths and locate data files in a defined, consistent way.
534 *  Please note that the application determines app_path and data_path at runtime and passes the results
535 *  to ConfigManager. GetExecutableFolder() and GetDataFolder() are therefore under normal conditions
536 *  simply more efficient shortcuts for Read("app_path") and Read("data_path").
537 */
538 
GetProxy()539 wxString ConfigManager::GetProxy()
540 {
541     return Manager::Get()->GetConfigManager(_T("app"))->Read(_T("network_proxy"));
542 }
543 
544 
GetFolder(SearchDirs dir)545 wxString ConfigManager::GetFolder(SearchDirs dir)
546 {
547     static bool once = 1;
548 
549     if (once)
550     {
551         InitPaths();
552         once = false;
553     }
554 
555     switch (dir)
556     {
557         case sdHome:
558             return ConfigManager::home_folder;
559 
560         case sdBase:
561             return ConfigManager::app_path;
562 
563         case sdTemp:
564             return ConfigManager::temp_folder;
565 
566         case sdConfig:
567             return ConfigManager::config_folder;
568 
569         case sdCurrent:
570             return ::wxGetCwd();
571 
572         case sdPluginsGlobal:
573 #ifndef CB_AUTOCONF
574             return ConfigManager::data_path_global + wxFILE_SEP_PATH + _T("plugins");
575 #else
576             return ConfigManager::plugin_path_global;
577 #endif
578 
579         case sdPluginsUser:
580             return ConfigManager::data_path_user   + wxFILE_SEP_PATH + _T("plugins");
581 
582         case sdScriptsGlobal:
583             return ConfigManager::data_path_global + wxFILE_SEP_PATH + _T("scripts");
584 
585         case sdScriptsUser:
586             return ConfigManager::data_path_user   + wxFILE_SEP_PATH + _T("scripts");
587 
588         case sdDataGlobal:
589             return ConfigManager::data_path_global;
590 
591         case sdDataUser:
592             return ConfigManager::data_path_user;
593 
594         case sdPath:
595         case sdAllUser:
596         case sdAllGlobal:
597         case sdAllKnown:
598         default:
599             return wxEmptyString;
600     }
601 }
602 
GetUserDataFolder()603 inline wxString ConfigManager::GetUserDataFolder()
604 {
605     if (has_alternate_user_data_path)
606         return alternate_user_data_path;
607 #ifdef __WINDOWS__
608     TCHAR buffer[MAX_PATH];
609     if (!ConfigManager::has_alternate_user_data_path && ::GetEnvironmentVariable(_T("APPDATA"), buffer, MAX_PATH))
610         return wxString::Format(_T("%s\\CodeBlocks"), buffer);
611     else
612         return wxStandardPathsBase::Get().GetUserDataDir();
613 #else
614 #ifdef __linux__
615     return wxString::FromUTF8(g_build_filename (g_get_user_config_dir(), "codeblocks", NULL));
616 #else
617     return wxStandardPathsBase::Get().GetUserDataDir();
618 #endif // __linux__
619 #endif // __WINDOWS__
620 }
621 
622 
SetUserDataFolder(const wxString & user_data_path)623 bool ConfigManager::SetUserDataFolder(const wxString &user_data_path)
624 {
625     wxString udp = wxFileName::DirName(user_data_path).GetFullPath();
626     if (!CreateDirRecursively(udp))
627     {
628         cbMessageBox(wxString::Format(_("The --user-data-dir directory %s does not exist and could not be created. Please check the path and try again"),
629                                             user_data_path.c_str()), _("Command Line Error"));
630         return false;
631     }
632     has_alternate_user_data_path = true;
633     ConfigManager::alternate_user_data_path = udp;
634     return true;
635 }
636 
LocateDataFile(const wxString & filename,int search_dirs)637 wxString ConfigManager::LocateDataFile(const wxString& filename, int search_dirs)
638 {
639     wxPathList searchPaths;
640 
641     // user dirs have precedence
642     if (search_dirs & sdPluginsUser)
643         searchPaths.Add(GetPluginsFolder(false));
644     if (search_dirs & sdScriptsUser)
645         searchPaths.Add(GetScriptsFolder(false));
646     if (search_dirs & sdDataUser)
647         searchPaths.Add(GetDataFolder(false));
648 
649     // then we have global dirs
650     if (search_dirs & sdPluginsGlobal)
651         searchPaths.Add(GetPluginsFolder(true));
652     if (search_dirs & sdScriptsGlobal)
653         searchPaths.Add(GetScriptsFolder(true));
654     if (search_dirs & sdDataGlobal)
655         searchPaths.Add(GetDataFolder(true));
656 
657     // rest of the dirs
658     if (search_dirs & sdCurrent)
659         searchPaths.Add(::wxGetCwd());
660     if (search_dirs & sdConfig)
661         searchPaths.Add(GetConfigFolder());
662     if (search_dirs & sdHome)
663         searchPaths.Add(GetHomeFolder());
664     if (search_dirs & sdBase)
665         searchPaths.Add(GetExecutableFolder());
666     if (search_dirs & sdTemp)
667         searchPaths.Add(GetTempFolder());
668 
669     // PATH env. var
670     if (search_dirs & sdPath)
671         searchPaths.AddEnvList(_T("PATH"));
672 
673     return searchPaths.FindValidPath(filename);
674 }
675 
676 
677 
678 /* ------------------------------------------------------------------------------------------------------------------
679 *  ConfigManager
680 */
681 
ConfigManager(TiXmlElement * r)682 ConfigManager::ConfigManager(TiXmlElement* r) : doc(r->GetDocument()), root(r), pathNode(r)
683 {
684 }
685 
686 
687 
688 
689 /* ------------------------------------------------------------------------------------------------------------------
690 *  Configuration path handling
691 *  Note that due to namespaces, you do no longer need to care about saving/restoring the current path in the normal case.
692 *  Mostly, there will be only one module working with one namespace, and every namespace keeps track of its own path.
693 *  Also, please note that GetPath() is more expensive than it seems (not a mere accessor to a member variable!), while
694 *  SetPath() not only sets the current path, but also creates the respective nodes in the XML document if these don't exist.
695 */
696 
GetPath() const697 wxString ConfigManager::GetPath() const
698 {
699     TiXmlElement *e = pathNode;
700     wxString ret;
701     ret.Alloc(64);
702 
703     ret = cbC2U(e->Value());
704     while ((e = e->Parent()->ToElement()) && e != root)
705     {
706         ret.Prepend(_T('/'));
707         ret.Prepend(cbC2U(e->Value()));
708     }
709     ret.Prepend(_T('/'));
710     return ret;
711 }
712 
SetPath(const wxString & path)713 void ConfigManager::SetPath(const wxString& path)
714 {
715     wxString p(path + _T('/'));
716     pathNode = AssertPath(p);
717 }
718 
InvalidNameMessage(const wxString & what,const wxString & sub,TiXmlElement * localPath) const719 wxString ConfigManager::InvalidNameMessage(const wxString& what, const wxString& sub, TiXmlElement *localPath) const
720 {
721     wxString s;
722     s.Printf(_T("The %s %s (child of node \"%s\" in namespace \"%s\") does not meet the standard for path naming (must start with a letter)."),
723     what.c_str(),
724     sub.c_str(),
725     cbC2U(localPath->Value()).c_str(),
726     cbC2U(root->Value()).c_str());
727 
728     return s;
729 }
730 
731 
AssertPath(wxString & path)732 TiXmlElement* ConfigManager::AssertPath(wxString& path)
733 {
734     Collapse(path);
735 
736     wxString illegal(_T(" -:.\"\'$&()[]<>+#"));
737     size_t i = 0;
738     while ((i = path.find_first_of(illegal, i)) != wxString::npos)
739         path[i] = _T('_');
740 
741     TiXmlElement *localPath = pathNode ? pathNode : root;
742 
743     if (path.GetChar(0) == '/')  // absolute path
744     {
745         localPath = root;
746         path = path.Mid(1);
747     }
748 
749     if (path.find(_T('/')) != wxString::npos) // need for path walking
750         to_lower(path);
751 
752     wxString sub;
753 
754     while (path.find(_T('/')) != wxString::npos)
755     {
756         sub = path.BeforeFirst(_T('/'));
757         path = path.AfterFirst(_T('/'));
758 
759         if (localPath != root && sub.IsSameAs(CfgMgrConsts::dotDot))
760             localPath = localPath->Parent()->ToElement();
761         else if (sub.GetChar(0) < _T('a') || sub.GetChar(0) > _T('z'))
762         {
763             cbThrow(InvalidNameMessage(_T("subpath"), sub, localPath));
764         }
765         else
766         {
767             TiXmlElement* n = localPath->FirstChildElement(cbU2C(sub));
768             if (n)
769                 localPath = n;
770             else
771                 localPath = (TiXmlElement*) localPath->InsertEndChild(TiXmlElement(cbU2C(sub)));
772         }
773     }
774 
775     to_upper(path);
776 
777     if (!path.IsEmpty() && (path.GetChar(0) < _T('A') || path.GetChar(0) > _T('Z')))
778         cbThrow(InvalidNameMessage(_T("key"), path, localPath));
779 
780     return localPath;
781 }
782 
783 
784 /* -----------------------------------------------------------------------------------------------------
785 *  Clear all nodes from your namespace or delete the namespace alltogether (removing it from the config file).
786 *  After Delete() returns, the pointer to your instance is invalid.
787 */
788 
Clear()789 void ConfigManager::Clear()
790 {
791     root->Clear();
792 }
793 
Delete()794 void ConfigManager::Delete()
795 {
796     CfgMgrBldr * bld = CfgMgrBldr::Get();
797     const wxString ns(cbC2U(root->Value()));
798 
799     root->Clear();
800     doc->RootElement()->RemoveChild(root);
801 
802     wxCriticalSectionLocker(bld->cs);
803     NamespaceMap::iterator it = bld->namespaces.find(ns);
804     if (it != bld->namespaces.end())
805         bld->namespaces.erase(it);
806 
807     delete this;
808 }
809 
DeleteAll()810 void ConfigManager::DeleteAll()
811 {
812     CfgMgrBldr * bld = CfgMgrBldr::Get();
813     const wxString ns(cbC2U(root->Value()));
814 
815     if (!ns.IsSameAs(_T("app")))
816         cbThrow(_T("Illegal attempt to invoke DeleteAll()."));
817 
818     wxCriticalSectionLocker(bld->cs);
819     doc->RootElement()->Clear();
820     for (NamespaceMap::iterator it = bld->namespaces.begin(); it != bld->namespaces.end(); ++it)
821         delete it->second;
822 
823     bld->namespaces.clear();
824 }
825 
Flush()826 void ConfigManager::Flush()
827 {
828     CfgMgrBldr * bld = CfgMgrBldr::Get();
829     wxCriticalSectionLocker(bld->cs);
830     bld->Flush();
831 }
832 
833 /* ------------------------------------------------------------------------------------------------------------------
834 *  Utility functions for writing nodes
835 */
836 
GetUniqElement(TiXmlElement * p,const wxString & q)837 TiXmlElement* ConfigManager::GetUniqElement(TiXmlElement* p, const wxString& q)
838 {
839     TiXmlElement* r;
840     if ((r = p->FirstChildElement(cbU2C(q))))
841         return r;
842 
843     return (TiXmlElement*)(p->InsertEndChild(TiXmlElement(cbU2C(q))));
844 }
845 
SetNodeText(TiXmlElement * n,const TiXmlText & t)846 void ConfigManager::SetNodeText(TiXmlElement* n, const TiXmlText& t)
847 {
848     TiXmlNode *c = n->FirstChild();
849     if (c)
850         n->ReplaceChild(c, t);
851     else
852         n->InsertEndChild(t);
853 }
854 
855 
856 
857 /* ------------------------------------------------------------------------------------------------------------------
858 *  Write and read values
859 *  Regardless of namespaces, the string keys app_path and data_path always refer to the location of the application's executable
860 *  and the data path, respectively. These values are never saved to the configuration, but kept in static variables.
861 *  The application makes use of this by "writing" to the configuration file after determining these values at runtime.
862 */
Write(const wxString & name,const wxString & value,bool ignoreEmpty)863 void ConfigManager::Write(const wxString& name,  const wxString& value, bool ignoreEmpty)
864 {
865     if (name.IsSameAs(CfgMgrConsts::app_path))
866     {
867         return;
868     }
869     else if (name.IsSameAs(CfgMgrConsts::data_path))
870     {
871         data_path_global = value;
872         return;
873     }
874     if (ignoreEmpty && value.IsEmpty())
875     {
876         UnSet(name);
877         return;
878     }
879 
880     wxString key(name);
881     TiXmlElement* e = AssertPath(key);
882 
883     TiXmlElement *str = GetUniqElement(e, key);
884 
885     TiXmlElement *s = GetUniqElement(str, _T("str"));
886 
887     TiXmlText t(value.mb_str(wxConvUTF8));
888     t.SetCDATA(true);
889     SetNodeText(s, t);
890 }
891 
Write(const wxString & key,const char * str)892 void ConfigManager::Write(const wxString& key, const char* str)
893 {
894     /* NOTE (mandrav#1#): Do *not* remove 'false' from the call because in ANSI builds,
895     it matches this very function and overflows the stack... */
896     Write(key, cbC2U(str), false);
897 }
898 
Read(const wxString & name,const wxString & defaultVal)899 wxString ConfigManager::Read(const wxString& name, const wxString& defaultVal)
900 {
901     if (name.IsSameAs(CfgMgrConsts::app_path))
902         return app_path;
903     else if (name.IsSameAs(CfgMgrConsts::data_path))
904         return data_path_global;
905 
906     wxString ret;
907 
908     if (Read(name, &ret))
909         return ret;
910     else
911         return defaultVal;
912 }
913 
Read(const wxString & name,wxString * str)914 bool ConfigManager::Read(const wxString& name, wxString* str)
915 {
916     if (name.IsSameAs(CfgMgrConsts::app_path))
917     {
918         str->assign(app_path);
919         return true;
920     }
921     else if (name.IsSameAs(CfgMgrConsts::data_path))
922     {
923         str->assign(data_path_global);
924         return true;
925     }
926 
927     wxString key(name);
928     TiXmlElement* e = AssertPath(key);
929 
930     TiXmlHandle parentHandle(e);
931     TiXmlText *t = (TiXmlText *) parentHandle.FirstChild(cbU2C(key)).FirstChild("str").FirstChild().Node();
932 
933     if (t)
934     {
935         str->assign(cbC2U(t->Value()));
936         return true;
937     }
938     return false;
939 }
940 
Write(const wxString & name,const wxColour & c)941 void ConfigManager::Write(const wxString& name,  const wxColour& c)
942 {
943     wxString key(name);
944     TiXmlElement* e = AssertPath(key);
945 
946     TiXmlElement *leaf = GetUniqElement(e, key);
947 
948     TiXmlElement *s = GetUniqElement(leaf, _T("colour"));
949     if (c == wxNullColour)
950     {
951         s->SetAttribute("null", "true");
952         s->SetAttribute("r", 0);
953         s->SetAttribute("g", 0);
954         s->SetAttribute("b", 0);
955     }
956     else
957     {
958         s->SetAttribute("r", c.Red());
959         s->SetAttribute("g", c.Green());
960         s->SetAttribute("b", c.Blue());
961     }
962 }
963 
ReadColour(const wxString & name,const wxColour & defaultVal)964 wxColour ConfigManager::ReadColour(const wxString& name, const wxColour& defaultVal)
965 {
966     wxColour ret;
967 
968     if (Read(name, &ret))
969         return ret;
970     else
971         return defaultVal;
972 }
973 
Read(const wxString & name,wxColour * ret)974 bool ConfigManager::Read(const wxString& name, wxColour* ret)
975 {
976     wxString key(name);
977     TiXmlElement* e = AssertPath(key);
978 
979     TiXmlHandle parentHandle(e);
980     TiXmlElement *c = (TiXmlElement *) parentHandle.FirstChild(cbU2C(key)).FirstChild("colour").Element();
981 
982     if (c)
983     {
984         const char *isNull = c->Attribute("null");
985         if (isNull && strcmp(isNull, "true") == 0)
986         {
987             *ret = wxNullColour;
988             return true;
989         }
990         else
991         {
992             int r, g, b;
993             if (c->QueryIntAttribute("r", &r) == TIXML_SUCCESS
994                     && c->QueryIntAttribute("g", &g) == TIXML_SUCCESS
995                     && c->QueryIntAttribute("b", &b) == TIXML_SUCCESS)
996             {
997                 ret->Set(r, g, b);
998                 return true;
999             }
1000         }
1001     }
1002     *ret = wxNullColour;
1003     return false;
1004 }
1005 
Write(const wxString & name,int value)1006 void ConfigManager::Write(const wxString& name,  int value)
1007 {
1008     wxString key(name);
1009     TiXmlElement* e = AssertPath(key);
1010     TiXmlElement *leaf = GetUniqElement(e, key);
1011 
1012     leaf->SetAttribute("int", value);
1013 }
1014 
ReadInt(const wxString & name,int defaultVal)1015 int  ConfigManager::ReadInt(const wxString& name,  int defaultVal)
1016 {
1017     int ret;
1018 
1019     if (Read(name, &ret))
1020         return ret;
1021     else
1022         return defaultVal;
1023 }
1024 
Read(const wxString & name,int * value)1025 bool ConfigManager::Read(const wxString& name,  int* value)
1026 {
1027     wxString key(name);
1028     TiXmlElement* e = AssertPath(key);
1029 
1030     TiXmlHandle parentHandle(e);
1031     TiXmlElement *leaf = parentHandle.FirstChild(cbU2C(key)).Element();
1032 
1033     if (leaf)
1034         return leaf->QueryIntAttribute("int", value) == TIXML_SUCCESS;
1035     return false;
1036 }
1037 
1038 
Write(const wxString & name,bool value)1039 void ConfigManager::Write(const wxString& name,  bool value)
1040 {
1041     wxString key(name);
1042     TiXmlElement* e = AssertPath(key);
1043     TiXmlElement *leaf = GetUniqElement(e, key);
1044 
1045     leaf->SetAttribute("bool", value ? "1" : "0");
1046 }
1047 
ReadBool(const wxString & name,bool defaultVal)1048 bool  ConfigManager::ReadBool(const wxString& name,  bool defaultVal)
1049 {
1050     bool ret;
1051 
1052     if (Read(name, &ret))
1053         return ret;
1054     else
1055         return defaultVal;
1056 }
1057 
Read(const wxString & name,bool * value)1058 bool ConfigManager::Read(const wxString& name,  bool* value)
1059 {
1060     wxString key(name);
1061     TiXmlElement* e = AssertPath(key);
1062 
1063     TiXmlHandle parentHandle(e);
1064     TiXmlElement *leaf = parentHandle.FirstChild(cbU2C(key)).Element();
1065 
1066     if (leaf && leaf->Attribute("bool"))
1067     {
1068         *value = leaf->Attribute("bool")[0] == '1';
1069         return true;
1070     }
1071     return false;
1072 }
1073 
1074 
Write(const wxString & name,double value)1075 void ConfigManager::Write(const wxString& name,  double value)
1076 {
1077     wxString key(name);
1078     TiXmlElement* e = AssertPath(key);
1079     TiXmlElement *leaf = GetUniqElement(e, key);
1080 
1081     leaf->SetDoubleAttribute("double", value);
1082 }
1083 
ReadDouble(const wxString & name,double defaultVal)1084 double  ConfigManager::ReadDouble(const wxString& name,  double defaultVal)
1085 {
1086     double ret;
1087 
1088     if (Read(name, &ret))
1089         return ret;
1090     else
1091         return defaultVal;
1092 }
1093 
Read(const wxString & name,double * value)1094 bool ConfigManager::Read(const wxString& name,  double* value)
1095 {
1096     wxString key(name);
1097     TiXmlElement* e = AssertPath(key);
1098 
1099     TiXmlHandle parentHandle(e);
1100     TiXmlElement *leaf = parentHandle.FirstChild(cbU2C(key)).Element();
1101 
1102     if (leaf)
1103         return leaf->QueryDoubleAttribute("double", value) == TIXML_SUCCESS;
1104     return false;
1105 }
1106 
1107 
Set(const wxString & name)1108 void ConfigManager::Set(const wxString& name)
1109 {
1110     wxString key(name);
1111     TiXmlElement* e = AssertPath(key);
1112     GetUniqElement(e, key);
1113 }
1114 
UnSet(const wxString & name)1115 void ConfigManager::UnSet(const wxString& name)
1116 {
1117     wxString key(name);
1118     TiXmlElement* e = AssertPath(key);
1119 
1120     TiXmlNode *leaf = GetUniqElement(e, key);
1121     e->RemoveChild(leaf);
1122 }
1123 
Exists(const wxString & name)1124 bool ConfigManager::Exists(const wxString& name)
1125 {
1126     wxString key(name);
1127     TiXmlElement* e = AssertPath(key);
1128 
1129     TiXmlHandle parentHandle(e);
1130     TiXmlElement *leaf = parentHandle.FirstChild(cbU2C(key)).Element();
1131 
1132     return leaf;
1133 }
1134 
1135 
1136 
Write(const wxString & name,const wxArrayString & arrayString)1137 void ConfigManager::Write(const wxString& name,  const wxArrayString& arrayString)
1138 {
1139     wxString key(name);
1140     TiXmlElement* e = AssertPath(key);
1141 
1142     TiXmlElement *leaf = GetUniqElement(e, key);
1143 
1144     TiXmlElement *as;
1145     as = GetUniqElement(leaf, _T("astr"));
1146     leaf->RemoveChild(as);
1147     as = GetUniqElement(leaf, _T("astr"));
1148 
1149     for (unsigned int i = 0; i < arrayString.GetCount(); ++i)
1150     {
1151         TiXmlElement s("s");
1152 
1153         TiXmlText t(arrayString[i].mb_str(wxConvUTF8));
1154         t.SetCDATA(true);
1155 
1156         s.InsertEndChild(t);
1157         as->InsertEndChild(s);
1158     }
1159 }
1160 
Read(const wxString & name,wxArrayString * arrayString)1161 void ConfigManager::Read(const wxString& name, wxArrayString *arrayString)
1162 {
1163     wxString key(name);
1164     TiXmlElement* e = AssertPath(key);
1165 
1166     TiXmlHandle parentHandle(e);
1167     TiXmlNode *asNode = parentHandle.FirstChild(cbU2C(key)).FirstChild("astr").Node();
1168 
1169     TiXmlNode *curr = nullptr;
1170     if (asNode)
1171     {
1172         while ((curr = asNode->IterateChildren("s", curr)))
1173             arrayString->Add(cbC2U(curr->FirstChild()->ToText()->Value()));
1174     }
1175 }
1176 
ReadArrayString(const wxString & name)1177 wxArrayString ConfigManager::ReadArrayString(const wxString& name)
1178 {
1179     wxArrayString as;
1180     Read(name, &as);
1181     return as;
1182 }
1183 
WriteBinary(const wxString & name,const wxString & source)1184 void ConfigManager::WriteBinary(const wxString& name,  const wxString& source)
1185 {
1186     wxString key(name);
1187     TiXmlElement* e = AssertPath(key);
1188 
1189     TiXmlElement *str = GetUniqElement(e, key);
1190 
1191     TiXmlElement *s = GetUniqElement(str, _T("bin"));
1192     s->SetAttribute("crc", wxCrc32::FromString(source));
1193     SetNodeText(s, TiXmlText(wxBase64::Encode(source).mb_str(wxConvUTF8)));
1194 }
1195 
WriteBinary(const wxString & name,void * ptr,size_t len)1196 void ConfigManager::WriteBinary(const wxString& name,  void* ptr, size_t len)
1197 {
1198     wxString s((wxChar*)ptr, len);
1199     WriteBinary(name,  s);
1200 }
1201 
ReadBinary(const wxString & name)1202 wxString ConfigManager::ReadBinary(const wxString& name)
1203 {
1204     wxString str;
1205     wxString key(name);
1206     TiXmlElement* e = AssertPath(key);
1207     unsigned int crc = 0;
1208 
1209     TiXmlHandle parentHandle(e);
1210     TiXmlElement* bin = parentHandle.FirstChild(cbU2C(key)).FirstChild("bin").Element();
1211 
1212     if (!bin)
1213         return wxEmptyString;
1214 
1215     if (bin->QueryIntAttribute("crc", (int*)&crc) != TIXML_SUCCESS)
1216         return wxEmptyString;
1217 
1218     if (const TiXmlText* t = bin->FirstChild()->ToText())
1219     {
1220         str.assign(cbC2U(t->Value()));
1221         str = wxBase64::Decode(str);
1222         if (crc ==  wxCrc32::FromString(str))
1223             return str;
1224     }
1225     return wxEmptyString;
1226 }
1227 
1228 
EnumerateSubPaths(const wxString & path)1229 wxArrayString ConfigManager::EnumerateSubPaths(const wxString& path)
1230 {
1231     wxString key(path + _T('/')); // the trailing slash hack is required because AssertPath expects a key name
1232     TiXmlNode* e = AssertPath(key);
1233     wxArrayString ret;
1234 
1235     TiXmlElement *curr = nullptr;
1236     if (e)
1237     {
1238         while (e->IterateChildren(curr) && (curr = e->IterateChildren(curr)->ToElement()))
1239         {
1240             #if wxCHECK_VERSION(3, 0, 0)
1241             wxUniChar c = cbC2U(curr->Value())[0];
1242             #else
1243             wxChar c = *(cbC2U(curr->Value()));
1244             #endif
1245             if (c < _T('A') || c > _T('Z')) // first char must be a letter, uppercase letters are key names
1246                 ret.Add(cbC2U(curr->Value()));
1247         }
1248     }
1249     return ret;
1250 }
1251 
DeleteSubPath(const wxString & thePath)1252 void ConfigManager::DeleteSubPath(const wxString& thePath)
1253 {
1254     if (doc->ErrorId())
1255     {
1256         cbMessageBox(wxString(_T("### TinyXML error:\n")) << cbC2U(doc->ErrorDesc()));
1257         doc->ClearError();
1258     }
1259 
1260     wxString path(thePath);
1261     to_lower(path);
1262 
1263     Collapse(path);
1264 
1265     wxString illegal(_T(" :.,;!\"\'$%&()[]<>{}?*+-|#"));
1266     size_t i;
1267     while ((i = path.find_first_of(illegal)) != wxString::npos)
1268         path[i] = _T('_');
1269 
1270     if (path.Last() == _T('/'))
1271         path.RemoveLast();
1272 
1273     if (path.IsSameAs(_T("/"))) // this function will refuse to remove root!
1274         return;
1275 
1276     TiXmlElement* parent = pathNode ? pathNode : root;
1277 
1278     if (path.find(_T('/')) != wxString::npos)
1279     {
1280         wxString sub;
1281         do
1282         {
1283             sub = path.BeforeFirst(_T('/'));
1284             path = path.AfterFirst(_T('/'));
1285 
1286             if (sub.IsEmpty())
1287                 parent = root;
1288             else if (sub.IsSameAs(_T(".")))
1289                 ;
1290             else if (parent != root && sub.IsSameAs(_T("..")))
1291                 parent = parent->Parent()->ToElement();
1292             else
1293             {
1294                 TiXmlElement* n = parent->FirstChildElement(cbU2C(sub));
1295                 if (n)
1296                     parent = n;
1297                 else
1298                     return;
1299             }
1300         }
1301         while (path.find(_T('/')) != wxString::npos);
1302     }
1303 
1304     if (!path.IsEmpty())
1305     {
1306         if (TiXmlNode *toRemove = parent->FirstChild(cbU2C(path)))
1307         {
1308             toRemove->Clear();
1309             parent->RemoveChild(toRemove);
1310         }
1311     }
1312 }
1313 
1314 
EnumerateKeys(const wxString & path)1315 wxArrayString ConfigManager::EnumerateKeys(const wxString& path)
1316 {
1317     wxString key(path + _T('/')); // the trailing slash hack is required because AssertPath expects a key name
1318     TiXmlNode* e = AssertPath(key);
1319     wxArrayString ret;
1320 
1321     TiXmlElement *curr = nullptr;
1322     if (e)
1323     {
1324         while (e->IterateChildren(curr) && (curr = e->IterateChildren(curr)->ToElement()))
1325         {
1326             #if wxCHECK_VERSION(3, 0, 0)
1327             wxUniChar c = cbC2U(curr->Value())[0];
1328             #else
1329             wxChar c = *(cbC2U(curr->Value()));
1330             #endif
1331             if (c >= _T('A') && c <= _T('Z')) // opposite of the above
1332                 ret.Add(cbC2U(curr->Value()));
1333         }
1334     }
1335     return ret;
1336 }
1337 
Write(const wxString & name,const ISerializable & object)1338 void ConfigManager::Write(const wxString& name, const ISerializable& object)
1339 {
1340     wxString key(name);
1341     TiXmlElement* e = AssertPath(key);
1342 
1343     TiXmlElement *obj = GetUniqElement(e, key);
1344 
1345     TiXmlElement *s = GetUniqElement(obj, _T("obj"));
1346     SetNodeText(s, TiXmlText(cbU2C(wxBase64::Encode(object.SerializeOut()))));
1347 }
1348 
Read(const wxString & name,ISerializable * object)1349 bool ConfigManager::Read(const wxString& name, ISerializable* object)
1350 {
1351     wxString str;
1352     wxString key(name);
1353     TiXmlElement* e = AssertPath(key);
1354 
1355     TiXmlHandle parentHandle(e);
1356     TiXmlText *t = (TiXmlText *) parentHandle.FirstChild(cbU2C(key)).FirstChild("obj").FirstChild().Node();
1357 
1358     if (t)
1359     {
1360         str.assign(cbC2U(t->Value()));
1361         object->SerializeIn(wxBase64::Decode(str));
1362     }
1363     return wxEmptyString;
1364 }
1365 
Write(const wxString & name,const ConfigManagerContainer::StringToStringMap & map)1366 void ConfigManager::Write(const wxString& name, const ConfigManagerContainer::StringToStringMap& map)
1367 {
1368     wxString key(name);
1369     TiXmlElement* e = AssertPath(key);
1370 
1371     TiXmlElement *leaf = GetUniqElement(e, key);
1372 
1373     TiXmlElement *mNode;
1374     mNode = GetUniqElement(leaf, _T("ssmap"));
1375     leaf->RemoveChild(mNode);
1376     mNode = GetUniqElement(leaf, _T("ssmap"));
1377 
1378     for (ConfigManagerContainer::StringToStringMap::const_iterator it = map.begin(); it != map.end(); ++it)
1379     {
1380         TiXmlElement s(cbU2C(it->first));
1381 
1382         TiXmlText t(cbU2C(it->second));
1383         t.SetCDATA(true);
1384 
1385         s.InsertEndChild(t);
1386         mNode->InsertEndChild(s);
1387     }
1388 }
1389 
Read(const wxString & name,ConfigManagerContainer::StringToStringMap * map)1390 void ConfigManager::Read(const wxString& name, ConfigManagerContainer::StringToStringMap* map)
1391 {
1392     wxString key(name);
1393     TiXmlElement* e = AssertPath(key);
1394 
1395     TiXmlHandle parentHandle(e);
1396     TiXmlNode *mNode = parentHandle.FirstChild(cbU2C(key)).FirstChild("ssmap").Node();
1397 
1398     TiXmlNode *curr = nullptr;
1399     if (mNode)
1400     {
1401         while ((curr = mNode->IterateChildren(curr)))
1402             (*map)[cbC2U(curr->Value())] = cbC2U(curr->FirstChild()->ToText()->Value());
1403     }
1404 }
1405 
ReadSSMap(const wxString & name)1406 ConfigManagerContainer::StringToStringMap ConfigManager::ReadSSMap(const wxString& name)
1407 {
1408     ConfigManagerContainer::StringToStringMap ret;
1409     Read(name, &ret);
1410     return ret;
1411 }
1412 
Write(const wxString & name,const ConfigManagerContainer::IntToStringMap & map)1413 void ConfigManager::Write(const wxString& name, const ConfigManagerContainer::IntToStringMap& map)
1414 {
1415     wxString key(name);
1416     TiXmlElement* e = AssertPath(key);
1417 
1418     TiXmlElement *leaf = GetUniqElement(e, key);
1419 
1420     TiXmlElement *mNode;
1421     mNode = GetUniqElement(leaf, _T("ismap"));
1422     leaf->RemoveChild(mNode);
1423     mNode = GetUniqElement(leaf, _T("ismap"));
1424 
1425     wxString tmp;
1426     for (ConfigManagerContainer::IntToStringMap::const_iterator it = map.begin(); it != map.end(); ++it)
1427     {
1428         tmp.Printf(_T("x%d"), (int) it->first);
1429         TiXmlElement s(tmp.mb_str());
1430 
1431         TiXmlText t(cbU2C(it->second));
1432         t.SetCDATA(true);
1433 
1434         s.InsertEndChild(t);
1435         mNode->InsertEndChild(s);
1436     }
1437 }
1438 
Read(const wxString & name,ConfigManagerContainer::IntToStringMap * map)1439 void ConfigManager::Read(const wxString& name, ConfigManagerContainer::IntToStringMap* map)
1440 {
1441     wxString key(name);
1442     TiXmlElement* e = AssertPath(key);
1443 
1444     TiXmlHandle parentHandle(e);
1445     TiXmlNode *mNode = parentHandle.FirstChild(cbU2C(key)).FirstChild("ismap").Node();
1446 
1447     TiXmlNode *curr = nullptr;
1448     long tmp;
1449     if (mNode)
1450     {
1451         while ((curr = mNode->IterateChildren(curr)))
1452         {
1453             cbC2U(curr->Value()).Mid(1).ToLong(&tmp);
1454             (*map)[tmp] = cbC2U(curr->FirstChild()->ToText()->Value());
1455         }
1456     }
1457 }
1458 
ReadISMap(const wxString & name)1459 ConfigManagerContainer::IntToStringMap ConfigManager::ReadISMap(const wxString& name)
1460 {
1461     ConfigManagerContainer::IntToStringMap ret;
1462     Read(name, &ret);
1463     return ret;
1464 }
1465 
1466 
1467 
1468 
1469 
1470 
Write(const wxString & name,const ConfigManagerContainer::StringSet & set)1471 void ConfigManager::Write(const wxString& name, const ConfigManagerContainer::StringSet& set)
1472 {
1473     wxString key(name);
1474     TiXmlElement* e = AssertPath(key);
1475 
1476     TiXmlElement *leaf = GetUniqElement(e, key);
1477 
1478     TiXmlElement *mNode;
1479     mNode = GetUniqElement(leaf, _T("sset"));
1480     leaf->RemoveChild(mNode);
1481     mNode = GetUniqElement(leaf, _T("sset"));
1482 
1483     for (ConfigManagerContainer::StringSet::const_iterator it = set.begin(); it != set.end(); ++it)
1484     {
1485         TiXmlElement s("s");
1486 
1487         TiXmlText t(cbU2C(*it));
1488         t.SetCDATA(true);
1489 
1490         s.InsertEndChild(t);
1491         mNode->InsertEndChild(s);
1492     }
1493 }
1494 
1495 
Read(const wxString & name,ConfigManagerContainer::StringSet * set)1496 void ConfigManager::Read(const wxString& name, ConfigManagerContainer::StringSet* set)
1497 {
1498     wxString key(name);
1499     TiXmlElement* e = AssertPath(key);
1500 
1501     TiXmlHandle parentHandle(e);
1502     TiXmlNode *mNode = parentHandle.FirstChild(cbU2C(key)).FirstChild("sset").Node();
1503 
1504     TiXmlNode *curr = nullptr;
1505     if (mNode)
1506     {
1507         while ((curr = mNode->IterateChildren(curr)))
1508             set->insert(cbC2U(curr->FirstChild()->ToText()->Value()));
1509     }
1510 }
1511 
ReadSSet(const wxString & name)1512 ConfigManagerContainer::StringSet ConfigManager::ReadSSet(const wxString& name)
1513 {
1514     ConfigManagerContainer::StringSet ret;
1515     Read(name, &ret);
1516     return ret;
1517 }
1518 
1519 
Write(const wxString & name,const ConfigManagerContainer::SerializableObjectMap * map)1520 void ConfigManager::Write(const wxString& name, const ConfigManagerContainer::SerializableObjectMap* map)
1521 {
1522     wxString key(name);
1523     TiXmlElement* e = AssertPath(key);
1524 
1525     TiXmlElement *leaf = GetUniqElement(e, key);
1526 
1527     TiXmlElement *mNode;
1528     mNode = GetUniqElement(leaf, _T("objmap"));
1529     leaf->RemoveChild(mNode);
1530     mNode = GetUniqElement(leaf, _T("objmap"));
1531 
1532     for (ConfigManagerContainer::SerializableObjectMap::const_iterator it = map->begin(); it != map->end(); ++it)
1533     {
1534         TiXmlElement s(cbU2C(it->first));
1535         s.InsertEndChild(TiXmlText(cbU2C(wxBase64::Encode(it->second->SerializeOut()))));
1536         mNode->InsertEndChild(s);
1537     }
1538 }
1539 
1540 
InitPaths()1541 void ConfigManager::InitPaths()
1542 {
1543     ConfigManager::config_folder = ConfigManager::GetUserDataFolder();
1544     ConfigManager::home_folder = wxStandardPathsBase::Get().GetUserConfigDir();
1545     ConfigManager::app_path = ::DetermineExecutablePath();
1546     wxString res_path = ::DetermineResourcesPath();
1547 
1548     // if non-empty, the app has overriden it (e.g. "--prefix" was passed in the command line)
1549     if (data_path_global.IsEmpty())
1550     {
1551         if (platform::windows)
1552             ConfigManager::data_path_global = app_path + _T("\\share\\codeblocks");
1553         else if (platform::macosx)
1554             ConfigManager::data_path_global = res_path + _T("/share/codeblocks");
1555         else
1556             ConfigManager::data_path_global = wxStandardPathsBase::Get().GetDataDir();
1557     }
1558     else
1559         ConfigManager::data_path_global = UnixFilename(data_path_global);
1560 
1561 #ifdef CB_AUTOCONF
1562     if (plugin_path_global.IsEmpty())
1563     {
1564         if (platform::windows)
1565             ConfigManager::plugin_path_global = data_path_global;
1566         else if (platform::macosx)
1567             ConfigManager::plugin_path_global = data_path_global + _T("/plugins");
1568         else
1569         {
1570 #ifdef __WXGTK__
1571             // It seems we can not longer rely on wxStandardPathsBase::Get().GetPluginsDir(),
1572             // because its behaviour has changed on some systems (at least Fedora 14 64-bit).
1573             // So we create the pathname manually
1574             ConfigManager::plugin_path_global = ((const wxStandardPaths&)wxStandardPaths::Get()).GetInstallPrefix() + _T("/lib/codeblocks/plugins");
1575             // first assume, we use standard-paths
1576             if (!wxDirExists(ConfigManager::plugin_path_global) && wxIsPlatform64Bit())
1577             {
1578                 // if standard-path does not exist and we are on 64-bit system, use lib64 instead
1579                 ConfigManager::plugin_path_global = ((const wxStandardPaths&)wxStandardPaths::Get()).GetInstallPrefix() + _T("/lib64/codeblocks/plugins");
1580             }
1581 #endif // __WXGTK__
1582         }
1583     }
1584 #endif
1585 
1586     wxString dataPathUser = ConfigManager::config_folder + wxFILE_SEP_PATH + _T("share");
1587 #ifdef __linux__
1588     if (!has_alternate_user_data_path)
1589       dataPathUser = wxString::FromUTF8(g_build_filename (g_get_user_data_dir(), NULL));
1590 #endif // __linux__
1591 
1592     ConfigManager::data_path_user = dataPathUser + wxFILE_SEP_PATH + _T("codeblocks");
1593 
1594     // if user- and global-datapath are the same (can happen in portable mode) we run in conflicts
1595     // so we extend the user-datapath with the users name
1596     if (wxFileName(ConfigManager::data_path_user) == wxFileName(ConfigManager::data_path_global))
1597         ConfigManager::data_path_user.append(_(".")+wxGetUserId());
1598 
1599     CreateDirRecursively(ConfigManager::config_folder);
1600     CreateDirRecursively(ConfigManager::data_path_user   + _T("/plugins/"));
1601     CreateDir(ConfigManager::data_path_user   + _T("/scripts/"));
1602 
1603     ConfigManager::temp_folder = wxStandardPathsBase::Get().GetTempDir();
1604 }
1605 
MigrateFolders()1606 void ConfigManager::MigrateFolders()
1607 {
1608 #ifdef __linux__
1609     // if the old config-folder (~/.codeblocks) does not exist, we have nothing to do.
1610     if (!wxDirExists(wxStandardPaths::Get().GetUserDataDir()))
1611         return;
1612 
1613     // ConfigManager::config_folder might be the portable-path but we want to migrate the standard-conform folder,
1614     // but only if it not already exists
1615     wxString newConfigFolder = wxString::FromUTF8(g_build_filename (g_get_user_config_dir(), "codeblocks", NULL));
1616     // if the new config folder already exist, we step out immediately
1617     if (wxDirExists(newConfigFolder))
1618         return;
1619 
1620     wxString oldConfigFolder = wxStandardPaths::Get().GetUserDataDir();
1621     wxString oldDataFolder = oldConfigFolder + wxFILE_SEP_PATH + _T("share") + wxFILE_SEP_PATH + _T("codeblocks");
1622     wxString newDataFolder = wxString::FromUTF8(g_build_filename (g_get_user_data_dir(), NULL)) + wxFILE_SEP_PATH + _T("codeblocks");
1623     wxString msg;
1624     msg = F(_("The places where the configuration files and user-data files are stored\n"
1625               "have been changed to be more standard-conform.\n"
1626               "\n"
1627               "Now moving \"%s\"\n"
1628               "to \"%s\"\n"
1629               "and \"%s\"\n"
1630               "to \"%s\".\n"),
1631             oldDataFolder.wx_str(),
1632             newDataFolder.wx_str(),
1633             oldConfigFolder.wx_str(),
1634             newConfigFolder.wx_str());
1635     cbMessageBox(msg, _("Try to migrate config-folder ..."), wxICON_INFORMATION);
1636 
1637     bool success = true;
1638     if (wxDirExists(oldDataFolder))
1639     {
1640         // make sure the target-folder exists
1641         CreateDirRecursively(newDataFolder);
1642         success = wxRenameFile(oldDataFolder, newDataFolder);
1643         wxRmdir(oldConfigFolder + wxFILE_SEP_PATH + _T("share"));
1644     }
1645     if (success)
1646     {
1647         // make sure the target-folder exists
1648         CreateDirRecursively(newConfigFolder);
1649         success = wxRenameFile(oldConfigFolder, newConfigFolder);
1650     }
1651     if (!success)
1652     {
1653         msg = F(_("Error moving \"%s\"\n"
1654                   "to \"%s\"\n"
1655                   "or \"%s\"\n"
1656                   "to \"%s\".\n\n"
1657                   "Please check the folders manually (access rights?) !\n"
1658                   "A new configuration will be created from scratch!"),
1659                 oldDataFolder.wx_str(),
1660                 newDataFolder.wx_str(),
1661                 oldConfigFolder.wx_str(),
1662                 newConfigFolder.wx_str());
1663         cbMessageBox(msg, _("Error migrating config-folder ..."), wxICON_ERROR);
1664     }
1665 #endif // __linux__
1666 }
1667 
Write(const wxString & name,const wxString & value,bool ignoreEmpty)1668 void ConfigManagerWrapper::Write(const wxString& name, const wxString& value, bool ignoreEmpty)
1669 {
1670     if (m_namespace.empty())
1671         return;
1672     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1673     c->Write(m_basepath + name, value, ignoreEmpty);
1674 }
1675 
Read(const wxString & key,const wxString & defaultVal)1676 wxString ConfigManagerWrapper::Read(const wxString& key, const wxString& defaultVal)
1677 {
1678     if (m_namespace.empty())
1679         return defaultVal;
1680     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1681     return c->Read(m_basepath + key, defaultVal);
1682 }
1683 
Read(const wxString & key,wxString * str)1684 bool ConfigManagerWrapper::Read(const wxString& key, wxString* str)
1685 {
1686     if (m_namespace.empty())
1687         return false;
1688     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1689     return c->Read(key, str);
1690 }
Write(const wxString & key,const char * str)1691 void ConfigManagerWrapper::Write(const wxString& key, const char* str)
1692 {
1693     if (m_namespace.empty())
1694         return;
1695     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1696     c->Write(key, str);
1697 }
1698 
Write(const wxString & name,int value)1699 void ConfigManagerWrapper::Write(const wxString& name, int value)
1700 {
1701     if (m_namespace.empty())
1702         return;
1703     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1704     c->Write(m_basepath + name, value);
1705 }
Read(const wxString & name,int * value)1706 bool ConfigManagerWrapper::Read(const wxString& name, int* value)
1707 {
1708     if (m_namespace.empty())
1709         return false;
1710     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1711     return c->Read(m_basepath + name, value);
1712 }
1713 
ReadInt(const wxString & name,int defaultVal)1714 int ConfigManagerWrapper::ReadInt(const wxString& name, int defaultVal)
1715 {
1716     if (m_namespace.empty())
1717         return defaultVal;
1718     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1719     return c->ReadInt(m_basepath + name, defaultVal);
1720 }
1721 
Write(const wxString & name,bool value)1722 void ConfigManagerWrapper::Write(const wxString& name, bool value)
1723 {
1724     if (m_namespace.empty())
1725         return;
1726     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1727     c->Write(m_basepath + name, value);
1728 }
Read(const wxString & name,bool * value)1729 bool ConfigManagerWrapper::Read(const wxString& name, bool* value)
1730 {
1731     if (m_namespace.empty())
1732         return false;
1733     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1734     return c->Read(m_basepath + name, value);
1735 }
ReadBool(const wxString & name,bool defaultVal)1736 bool ConfigManagerWrapper::ReadBool(const wxString& name, bool defaultVal)
1737 {
1738     if (m_namespace.empty())
1739         return defaultVal;
1740     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1741     return c->ReadBool(m_basepath + name, defaultVal);
1742 }
1743 
Write(const wxString & name,double value)1744 void ConfigManagerWrapper::Write(const wxString& name, double value)
1745 {
1746     if (m_namespace.empty())
1747         return;
1748     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1749     c->Write(m_basepath + name, value);
1750 }
Read(const wxString & name,double * value)1751 bool ConfigManagerWrapper::Read(const wxString& name, double* value)
1752 {
1753     if (m_namespace.empty())
1754         return false;
1755     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1756     return c->Read(m_basepath + name, value);
1757 }
ReadDouble(const wxString & name,double defaultVal)1758 double ConfigManagerWrapper::ReadDouble(const wxString& name, double defaultVal)
1759 {
1760     if (m_namespace.empty())
1761         return defaultVal;
1762     ConfigManager *c = Manager::Get()->GetConfigManager(m_namespace);
1763     return c->ReadDouble(m_basepath + name, defaultVal);
1764 }
1765 
getCompilerPluginFilename()1766 static wxString getCompilerPluginFilename()
1767 {
1768     if (platform::windows)
1769         return wxT("compiler.dll");
1770     else if (platform::darwin || platform::macosx)
1771         return wxT("libcompiler.dylib");
1772     else
1773         return wxT("libcompiler.so");
1774 }
1775 
cbReadBatchBuildPlugins()1776 wxArrayString cbReadBatchBuildPlugins()
1777 {
1778     ConfigManager *bbcfg = Manager::Get()->GetConfigManager(_T("plugins"));
1779     wxArrayString bbplugins = bbcfg->ReadArrayString(_T("/batch_build_plugins"));
1780 
1781     if (!bbplugins.GetCount())
1782         bbplugins.Add(getCompilerPluginFilename());
1783 
1784     return bbplugins;
1785 }
1786 
cbWriteBatchBuildPlugins(wxArrayString bbplugins,wxWindow * messageBoxParent)1787 void cbWriteBatchBuildPlugins(wxArrayString bbplugins, wxWindow *messageBoxParent)
1788 {
1789     const wxString &compiler = getCompilerPluginFilename();
1790 
1791     if (bbplugins.Index(compiler) == wxNOT_FOUND)
1792     {
1793         bbplugins.Add(compiler);
1794         cbMessageBox(_("The compiler plugin must always be loaded for batch builds!\n"
1795                     "Automatically re-enabled."),
1796                     _("Warning"), wxICON_WARNING, messageBoxParent);
1797     }
1798     ConfigManager *bbcfg = Manager::Get()->GetConfigManager(_T("plugins"));
1799     bbcfg->Write(_T("/batch_build_plugins"), bbplugins);
1800 }
1801