1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #include <util/system.h>
7 
8 #include <chainparamsbase.h>
9 #include <util/strencodings.h>
10 #include <util/string.h>
11 #include <util/translation.h>
12 #include <regex>
13 #include <iomanip>
14 
15 
16 #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__))
17 #include <pthread.h>
18 #include <pthread_np.h>
19 #endif
20 
21 #ifndef WIN32
22 // for posix_fallocate
23 #ifdef __linux__
24 
25 #ifdef _POSIX_C_SOURCE
26 #undef _POSIX_C_SOURCE
27 #endif
28 
29 #define _POSIX_C_SOURCE 200112L
30 
31 #endif // __linux__
32 
33 #include <algorithm>
34 #include <fcntl.h>
35 #include <sched.h>
36 #include <sys/resource.h>
37 #include <sys/stat.h>
38 
39 #else
40 
41 #ifdef _MSC_VER
42 #pragma warning(disable:4786)
43 #pragma warning(disable:4804)
44 #pragma warning(disable:4805)
45 #pragma warning(disable:4717)
46 #endif
47 
48 #ifdef _WIN32_IE
49 #undef _WIN32_IE
50 #endif
51 #define _WIN32_IE 0x0501
52 
53 #define WIN32_LEAN_AND_MEAN 1
54 #ifndef NOMINMAX
55 #define NOMINMAX
56 #endif
57 #include <codecvt>
58 
59 #include <io.h> /* for _commit */
60 #include <shellapi.h>
61 #include <shlobj.h>
62 #endif
63 
64 #ifdef HAVE_MALLOPT_ARENA_MAX
65 #include <malloc.h>
66 #endif
67 
68 #include <boost/algorithm/string/replace.hpp>
69 #include <thread>
70 #include <typeinfo>
71 #include <univalue.h>
72 
73 // Application startup time (used for uptime calculation)
74 const int64_t nStartupTime = GetTime();
75 
76 const char * const BITCOIN_CONF_FILENAME = "qtum.conf";
77 
78 ArgsManager gArgs;
79 
80 /** A map that contains all the currently held directory locks. After
81  * successful locking, these will be held here until the global destructor
82  * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
83  * is called.
84  */
85 static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
86 /** Mutex to protect dir_locks. */
87 static std::mutex cs_dir_locks;
88 
LockDirectory(const fs::path & directory,const std::string lockfile_name,bool probe_only,bool try_lock)89 bool LockDirectory(const fs::path& directory, const std::string lockfile_name, bool probe_only, bool try_lock)
90 {
91     std::lock_guard<std::mutex> ulock(cs_dir_locks);
92     fs::path pathLockFile = directory / lockfile_name;
93 
94     // If a lock for this directory already exists in the map, don't try to re-lock it
95     if (dir_locks.count(pathLockFile.string())) {
96         return true;
97     }
98 
99     // Create empty lock file if it doesn't exist.
100     FILE* file = fsbridge::fopen(pathLockFile, "a");
101     if (file) fclose(file);
102     auto lock = MakeUnique<fsbridge::FileLock>(pathLockFile);
103     if (try_lock && !lock->TryLock()) {
104         return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason());
105     }
106     if (!probe_only) {
107         // Lock successful and we're not just probing, put it into the map
108         dir_locks.emplace(pathLockFile.string(), std::move(lock));
109     }
110     return true;
111 }
112 
UnlockDirectory(const fs::path & directory,const std::string & lockfile_name)113 void UnlockDirectory(const fs::path& directory, const std::string& lockfile_name)
114 {
115     std::lock_guard<std::mutex> lock(cs_dir_locks);
116     dir_locks.erase((directory / lockfile_name).string());
117 }
118 
ReleaseDirectoryLocks()119 void ReleaseDirectoryLocks()
120 {
121     std::lock_guard<std::mutex> ulock(cs_dir_locks);
122     dir_locks.clear();
123 }
124 
DirIsWritable(const fs::path & directory)125 bool DirIsWritable(const fs::path& directory)
126 {
127     fs::path tmpFile = directory / fs::unique_path();
128 
129     FILE* file = fsbridge::fopen(tmpFile, "a");
130     if (!file) return false;
131 
132     fclose(file);
133     remove(tmpFile);
134 
135     return true;
136 }
137 
CheckDiskSpace(const fs::path & dir,uint64_t additional_bytes)138 bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
139 {
140     constexpr uint64_t min_disk_space = 52428800; // 50 MiB
141 
142     uint64_t free_bytes_available = fs::space(dir).available;
143     return free_bytes_available >= min_disk_space + additional_bytes;
144 }
145 
146 /**
147  * Interpret a string argument as a boolean.
148  *
149  * The definition of atoi() requires that non-numeric string values like "foo",
150  * return 0. This means that if a user unintentionally supplies a non-integer
151  * argument here, the return value is always false. This means that -foo=false
152  * does what the user probably expects, but -foo=true is well defined but does
153  * not do what they probably expected.
154  *
155  * The return value of atoi() is undefined when given input not representable as
156  * an int. On most systems this means string value between "-2147483648" and
157  * "2147483647" are well defined (this method will return true). Setting
158  * -txindex=2147483648 on most systems, however, is probably undefined.
159  *
160  * For a more extensive discussion of this topic (and a wide range of opinions
161  * on the Right Way to change this code), see PR12713.
162  */
InterpretBool(const std::string & strValue)163 static bool InterpretBool(const std::string& strValue)
164 {
165     if (strValue.empty())
166         return true;
167     return (atoi(strValue) != 0);
168 }
169 
SettingName(const std::string & arg)170 static std::string SettingName(const std::string& arg)
171 {
172     return arg.size() > 0 && arg[0] == '-' ? arg.substr(1) : arg;
173 }
174 
175 /**
176  * Interpret -nofoo as if the user supplied -foo=0.
177  *
178  * This method also tracks when the -no form was supplied, and if so,
179  * checks whether there was a double-negative (-nofoo=0 -> -foo=1).
180  *
181  * If there was not a double negative, it removes the "no" from the key
182  * and returns false.
183  *
184  * If there was a double negative, it removes "no" from the key, and
185  * returns true.
186  *
187  * If there was no "no", it returns the string value untouched.
188  *
189  * Where an option was negated can be later checked using the
190  * IsArgNegated() method. One use case for this is to have a way to disable
191  * options that are not normally boolean (e.g. using -nodebuglogfile to request
192  * that debug log output is not sent to any file at all).
193  */
194 
InterpretOption(std::string & section,std::string & key,const std::string & value)195 static util::SettingsValue InterpretOption(std::string& section, std::string& key, const std::string& value)
196 {
197     // Split section name from key name for keys like "testnet.foo" or "regtest.bar"
198     size_t option_index = key.find('.');
199     if (option_index != std::string::npos) {
200         section = key.substr(0, option_index);
201         key.erase(0, option_index + 1);
202     }
203     if (key.substr(0, 2) == "no") {
204         key.erase(0, 2);
205         // Double negatives like -nofoo=0 are supported (but discouraged)
206         if (!InterpretBool(value)) {
207             LogPrintf("Warning: parsed potentially confusing double-negative -%s=%s\n", key, value);
208             return true;
209         }
210         return false;
211     }
212     return value;
213 }
214 
215 /**
216  * Check settings value validity according to flags.
217  *
218  * TODO: Add more meaningful error checks here in the future
219  * See "here's how the flags are meant to behave" in
220  * https://github.com/bitcoin/bitcoin/pull/16097#issuecomment-514627823
221  */
CheckValid(const std::string & key,const util::SettingsValue & val,unsigned int flags,std::string & error)222 static bool CheckValid(const std::string& key, const util::SettingsValue& val, unsigned int flags, std::string& error)
223 {
224     if (val.isBool() && !(flags & ArgsManager::ALLOW_BOOL)) {
225         error = strprintf("Negating of -%s is meaningless and therefore forbidden", key);
226         return false;
227     }
228     return true;
229 }
230 
ArgsManager()231 ArgsManager::ArgsManager()
232 {
233     // nothing to do
234 }
235 
GetUnsuitableSectionOnlyArgs() const236 const std::set<std::string> ArgsManager::GetUnsuitableSectionOnlyArgs() const
237 {
238     std::set<std::string> unsuitables;
239 
240     LOCK(cs_args);
241 
242     // if there's no section selected, don't worry
243     if (m_network.empty()) return std::set<std::string> {};
244 
245     // if it's okay to use the default section for this network, don't worry
246     if (m_network == CBaseChainParams::MAIN) return std::set<std::string> {};
247 
248     for (const auto& arg : m_network_only_args) {
249         if (OnlyHasDefaultSectionSetting(m_settings, m_network, SettingName(arg))) {
250             unsuitables.insert(arg);
251         }
252     }
253     return unsuitables;
254 }
255 
GetUnrecognizedSections() const256 const std::list<SectionInfo> ArgsManager::GetUnrecognizedSections() const
257 {
258     // Section names to be recognized in the config file.
259     static const std::set<std::string> available_sections{
260         CBaseChainParams::REGTEST,
261         CBaseChainParams::TESTNET,
262         CBaseChainParams::MAIN
263     };
264 
265     LOCK(cs_args);
266     std::list<SectionInfo> unrecognized = m_config_sections;
267     unrecognized.remove_if([](const SectionInfo& appeared){ return available_sections.find(appeared.m_name) != available_sections.end(); });
268     return unrecognized;
269 }
270 
SelectConfigNetwork(const std::string & network)271 void ArgsManager::SelectConfigNetwork(const std::string& network)
272 {
273     LOCK(cs_args);
274     m_network = network;
275 }
276 
ParseParameters(int argc,const char * const argv[],std::string & error)277 bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error)
278 {
279     LOCK(cs_args);
280     m_settings.command_line_options.clear();
281 
282     for (int i = 1; i < argc; i++) {
283         std::string key(argv[i]);
284 
285 #ifdef MAC_OSX
286         // At the first time when a user gets the "App downloaded from the
287         // internet" warning, and clicks the Open button, macOS passes
288         // a unique process serial number (PSN) as -psn_... command-line
289         // argument, which we filter out.
290         if (key.substr(0, 5) == "-psn_") continue;
291 #endif
292 
293         if (key == "-") break; //bitcoin-tx using stdin
294         std::string val;
295         size_t is_index = key.find('=');
296         if (is_index != std::string::npos) {
297             val = key.substr(is_index + 1);
298             key.erase(is_index);
299         }
300 #ifdef WIN32
301         key = ToLower(key);
302         if (key[0] == '/')
303             key[0] = '-';
304 #endif
305 
306         if (key[0] != '-')
307             break;
308 
309         // Transform --foo to -foo
310         if (key.length() > 1 && key[1] == '-')
311             key.erase(0, 1);
312 
313         // Transform -foo to foo
314         key.erase(0, 1);
315         std::string section;
316         util::SettingsValue value = InterpretOption(section, key, val);
317         Optional<unsigned int> flags = GetArgFlags('-' + key);
318 
319         // Unknown command line options and command line options with dot
320         // characters (which are returned from InterpretOption with nonempty
321         // section strings) are not valid.
322         if (!flags || !section.empty()) {
323             error = strprintf("Invalid parameter %s", argv[i]);
324             return false;
325         }
326 
327         if (!CheckValid(key, value, *flags, error)) return false;
328 
329         m_settings.command_line_options[key].push_back(value);
330     }
331 
332     // we do not allow -includeconf from command line
333     bool success = true;
334     if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
335         for (const auto& include : util::SettingsSpan(*includes)) {
336             error += "-includeconf cannot be used from commandline; -includeconf=" + include.get_str() + "\n";
337             success = false;
338         }
339     }
340     return success;
341 }
342 
GetArgFlags(const std::string & name) const343 Optional<unsigned int> ArgsManager::GetArgFlags(const std::string& name) const
344 {
345     LOCK(cs_args);
346     for (const auto& arg_map : m_available_args) {
347         const auto search = arg_map.second.find(name);
348         if (search != arg_map.second.end()) {
349             return search->second.m_flags;
350         }
351     }
352     return nullopt;
353 }
354 
GetArgs(const std::string & strArg) const355 std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
356 {
357     std::vector<std::string> result;
358     for (const util::SettingsValue& value : GetSettingsList(strArg)) {
359         result.push_back(value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str());
360     }
361     return result;
362 }
363 
IsArgSet(const std::string & strArg) const364 bool ArgsManager::IsArgSet(const std::string& strArg) const
365 {
366     return !GetSetting(strArg).isNull();
367 }
368 
IsArgNegated(const std::string & strArg) const369 bool ArgsManager::IsArgNegated(const std::string& strArg) const
370 {
371     return GetSetting(strArg).isFalse();
372 }
373 
GetArg(const std::string & strArg,const std::string & strDefault) const374 std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
375 {
376     const util::SettingsValue value = GetSetting(strArg);
377     return value.isNull() ? strDefault : value.isFalse() ? "0" : value.isTrue() ? "1" : value.get_str();
378 }
379 
GetArg(const std::string & strArg,int64_t nDefault) const380 int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
381 {
382     const util::SettingsValue value = GetSetting(strArg);
383     return value.isNull() ? nDefault : value.isFalse() ? 0 : value.isTrue() ? 1 : value.isNum() ? value.get_int64() : atoi64(value.get_str());
384 }
385 
GetBoolArg(const std::string & strArg,bool fDefault) const386 bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
387 {
388     const util::SettingsValue value = GetSetting(strArg);
389     return value.isNull() ? fDefault : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
390 }
391 
SoftSetArg(const std::string & strArg,const std::string & strValue)392 bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
393 {
394     LOCK(cs_args);
395     if (IsArgSet(strArg)) return false;
396     ForceSetArg(strArg, strValue);
397     return true;
398 }
399 
SoftSetBoolArg(const std::string & strArg,bool fValue)400 bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
401 {
402     if (fValue)
403         return SoftSetArg(strArg, std::string("1"));
404     else
405         return SoftSetArg(strArg, std::string("0"));
406 }
407 
ForceSetArg(const std::string & strArg,const std::string & strValue)408 void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
409 {
410     LOCK(cs_args);
411     m_settings.forced_settings[SettingName(strArg)] = strValue;
412 }
413 
AddArg(const std::string & name,const std::string & help,unsigned int flags,const OptionsCategory & cat)414 void ArgsManager::AddArg(const std::string& name, const std::string& help, unsigned int flags, const OptionsCategory& cat)
415 {
416     // Split arg name from its help param
417     size_t eq_index = name.find('=');
418     if (eq_index == std::string::npos) {
419         eq_index = name.size();
420     }
421     std::string arg_name = name.substr(0, eq_index);
422 
423     LOCK(cs_args);
424     std::map<std::string, Arg>& arg_map = m_available_args[cat];
425     auto ret = arg_map.emplace(arg_name, Arg{name.substr(eq_index, name.size() - eq_index), help, flags});
426     assert(ret.second); // Make sure an insertion actually happened
427 
428     if (flags & ArgsManager::NETWORK_ONLY) {
429         m_network_only_args.emplace(arg_name);
430     }
431 }
432 
AddHiddenArgs(const std::vector<std::string> & names)433 void ArgsManager::AddHiddenArgs(const std::vector<std::string>& names)
434 {
435     for (const std::string& name : names) {
436         AddArg(name, "", ArgsManager::ALLOW_ANY, OptionsCategory::HIDDEN);
437     }
438 }
439 
GetHelpMessage() const440 std::string ArgsManager::GetHelpMessage() const
441 {
442     const bool show_debug = gArgs.GetBoolArg("-help-debug", false);
443 
444     std::string usage = "";
445     LOCK(cs_args);
446     for (const auto& arg_map : m_available_args) {
447         switch(arg_map.first) {
448             case OptionsCategory::OPTIONS:
449                 usage += HelpMessageGroup("Options:");
450                 break;
451             case OptionsCategory::CONNECTION:
452                 usage += HelpMessageGroup("Connection options:");
453                 break;
454             case OptionsCategory::ZMQ:
455                 usage += HelpMessageGroup("ZeroMQ notification options:");
456                 break;
457             case OptionsCategory::DEBUG_TEST:
458                 usage += HelpMessageGroup("Debugging/Testing options:");
459                 break;
460             case OptionsCategory::NODE_RELAY:
461                 usage += HelpMessageGroup("Node relay options:");
462                 break;
463             case OptionsCategory::BLOCK_CREATION:
464                 usage += HelpMessageGroup("Block creation options:");
465                 break;
466             case OptionsCategory::RPC:
467                 usage += HelpMessageGroup("RPC server options:");
468                 break;
469             case OptionsCategory::WALLET:
470                 usage += HelpMessageGroup("Wallet options:");
471                 break;
472             case OptionsCategory::WALLET_DEBUG_TEST:
473                 if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:");
474                 break;
475             case OptionsCategory::CHAINPARAMS:
476                 usage += HelpMessageGroup("Chain selection options:");
477                 break;
478             case OptionsCategory::GUI:
479                 usage += HelpMessageGroup("UI Options:");
480                 break;
481             case OptionsCategory::COMMANDS:
482                 usage += HelpMessageGroup("Commands:");
483                 break;
484             case OptionsCategory::REGISTER_COMMANDS:
485                 usage += HelpMessageGroup("Register Commands:");
486                 break;
487             default:
488                 break;
489         }
490 
491         // When we get to the hidden options, stop
492         if (arg_map.first == OptionsCategory::HIDDEN) break;
493 
494         for (const auto& arg : arg_map.second) {
495             if (show_debug || !(arg.second.m_flags & ArgsManager::DEBUG_ONLY)) {
496                 std::string name;
497                 if (arg.second.m_help_param.empty()) {
498                     name = arg.first;
499                 } else {
500                     name = arg.first + arg.second.m_help_param;
501                 }
502                 usage += HelpMessageOpt(name, arg.second.m_help_text);
503             }
504         }
505     }
506     return usage;
507 }
508 
HelpRequested(const ArgsManager & args)509 bool HelpRequested(const ArgsManager& args)
510 {
511     return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help") || args.IsArgSet("-help-debug");
512 }
513 
SetupHelpOptions(ArgsManager & args)514 void SetupHelpOptions(ArgsManager& args)
515 {
516     args.AddArg("-?", "Print this help message and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
517     args.AddHiddenArgs({"-h", "-help"});
518 }
519 
520 static const int screenWidth = 79;
521 static const int optIndent = 2;
522 static const int msgIndent = 7;
523 
HelpMessageGroup(const std::string & message)524 std::string HelpMessageGroup(const std::string &message) {
525     return std::string(message) + std::string("\n\n");
526 }
527 
HelpMessageOpt(const std::string & option,const std::string & message)528 std::string HelpMessageOpt(const std::string &option, const std::string &message) {
529     return std::string(optIndent,' ') + std::string(option) +
530            std::string("\n") + std::string(msgIndent,' ') +
531            FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
532            std::string("\n\n");
533 }
534 
FormatException(const std::exception * pex,const char * pszThread)535 static std::string FormatException(const std::exception* pex, const char* pszThread)
536 {
537 #ifdef WIN32
538     char pszModule[MAX_PATH] = "";
539     GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
540 #else
541     const char* pszModule = "bitcoin";
542 #endif
543     if (pex)
544         return strprintf(
545             "EXCEPTION: %s       \n%s       \n%s in %s       \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
546     else
547         return strprintf(
548             "UNKNOWN EXCEPTION       \n%s in %s       \n", pszModule, pszThread);
549 }
550 
PrintExceptionContinue(const std::exception * pex,const char * pszThread)551 void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
552 {
553     std::string message = FormatException(pex, pszThread);
554     LogPrintf("\n\n************************\n%s\n", message);
555     tfm::format(std::cerr, "\n\n************************\n%s\n", message);
556 }
557 
GetDefaultDataDir()558 fs::path GetDefaultDataDir()
559 {
560     // Windows < Vista: C:\Documents and Settings\Username\Application Data\Qtum
561     // Windows >= Vista: C:\Users\Username\AppData\Roaming\Qtum
562     // Mac: ~/Library/Application Support/Qtum
563     // Unix: ~/.qtum
564 #ifdef WIN32
565     // Windows
566     return GetSpecialFolderPath(CSIDL_APPDATA) / "Qtum";
567 #else
568     fs::path pathRet;
569     char* pszHome = getenv("HOME");
570     if (pszHome == nullptr || strlen(pszHome) == 0)
571         pathRet = fs::path("/");
572     else
573         pathRet = fs::path(pszHome);
574 #ifdef MAC_OSX
575     // Mac
576     return pathRet / "Library/Application Support/Qtum";
577 #else
578     // Unix
579     return pathRet / ".qtum";
580 #endif
581 #endif
582 }
583 
584 static fs::path g_blocks_path_cache_net_specific;
585 static fs::path pathCached;
586 static fs::path pathCachedNetSpecific;
587 static RecursiveMutex csPathCached;
588 
GetBlocksDir()589 const fs::path &GetBlocksDir()
590 {
591     LOCK(csPathCached);
592     fs::path &path = g_blocks_path_cache_net_specific;
593 
594     // Cache the path to avoid calling fs::create_directories on every call of
595     // this function
596     if (!path.empty()) return path;
597 
598     if (gArgs.IsArgSet("-blocksdir")) {
599         path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
600         if (!fs::is_directory(path)) {
601             path = "";
602             return path;
603         }
604     } else {
605         path = GetDataDir(false);
606     }
607 
608     path /= BaseParams().DataDir();
609     path /= "blocks";
610     fs::create_directories(path);
611     return path;
612 }
613 
GetDataDir(bool fNetSpecific)614 const fs::path &GetDataDir(bool fNetSpecific)
615 {
616     LOCK(csPathCached);
617     fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached;
618 
619     // Cache the path to avoid calling fs::create_directories on every call of
620     // this function
621     if (!path.empty()) return path;
622 
623     std::string datadir = gArgs.GetArg("-datadir", "");
624     if (!datadir.empty()) {
625         path = fs::system_complete(datadir);
626         if (!fs::is_directory(path)) {
627             path = "";
628             return path;
629         }
630     } else {
631         path = GetDefaultDataDir();
632     }
633     if (fNetSpecific)
634         path /= BaseParams().DataDir();
635 
636     if (fs::create_directories(path)) {
637         // This is the first run, create wallets subdirectory too
638         fs::create_directories(path / "wallets");
639     }
640 
641     return path;
642 }
643 
CheckDataDirOption()644 bool CheckDataDirOption()
645 {
646     std::string datadir = gArgs.GetArg("-datadir", "");
647     return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
648 }
649 
ClearDatadirCache()650 void ClearDatadirCache()
651 {
652     LOCK(csPathCached);
653 
654     pathCached = fs::path();
655     pathCachedNetSpecific = fs::path();
656     g_blocks_path_cache_net_specific = fs::path();
657 }
658 
GetConfigFile(const std::string & confPath)659 fs::path GetConfigFile(const std::string& confPath)
660 {
661     return AbsPathForConfigVal(fs::path(confPath), false);
662 }
663 
GetConfigOptions(std::istream & stream,const std::string & filepath,std::string & error,std::vector<std::pair<std::string,std::string>> & options,std::list<SectionInfo> & sections)664 static bool GetConfigOptions(std::istream& stream, const std::string& filepath, std::string& error, std::vector<std::pair<std::string, std::string>>& options, std::list<SectionInfo>& sections)
665 {
666     std::string str, prefix;
667     std::string::size_type pos;
668     int linenr = 1;
669     while (std::getline(stream, str)) {
670         bool used_hash = false;
671         if ((pos = str.find('#')) != std::string::npos) {
672             str = str.substr(0, pos);
673             used_hash = true;
674         }
675         const static std::string pattern = " \t\r\n";
676         str = TrimString(str, pattern);
677         if (!str.empty()) {
678             if (*str.begin() == '[' && *str.rbegin() == ']') {
679                 const std::string section = str.substr(1, str.size() - 2);
680                 sections.emplace_back(SectionInfo{section, filepath, linenr});
681                 prefix = section + '.';
682             } else if (*str.begin() == '-') {
683                 error = strprintf("parse error on line %i: %s, options in configuration file must be specified without leading -", linenr, str);
684                 return false;
685             } else if ((pos = str.find('=')) != std::string::npos) {
686                 std::string name = prefix + TrimString(str.substr(0, pos), pattern);
687                 std::string value = TrimString(str.substr(pos + 1), pattern);
688                 if (used_hash && name.find("rpcpassword") != std::string::npos) {
689                     error = strprintf("parse error on line %i, using # in rpcpassword can be ambiguous and should be avoided", linenr);
690                     return false;
691                 }
692                 options.emplace_back(name, value);
693                 if ((pos = name.rfind('.')) != std::string::npos && prefix.length() <= pos) {
694                     sections.emplace_back(SectionInfo{name.substr(0, pos), filepath, linenr});
695                 }
696             } else {
697                 error = strprintf("parse error on line %i: %s", linenr, str);
698                 if (str.size() >= 2 && str.substr(0, 2) == "no") {
699                     error += strprintf(", if you intended to specify a negated option, use %s=1 instead", str);
700                 }
701                 return false;
702             }
703         }
704         ++linenr;
705     }
706     return true;
707 }
708 
ReadConfigStream(std::istream & stream,const std::string & filepath,std::string & error,bool ignore_invalid_keys)709 bool ArgsManager::ReadConfigStream(std::istream& stream, const std::string& filepath, std::string& error, bool ignore_invalid_keys)
710 {
711     LOCK(cs_args);
712     std::vector<std::pair<std::string, std::string>> options;
713     if (!GetConfigOptions(stream, filepath, error, options, m_config_sections)) {
714         return false;
715     }
716     for (const std::pair<std::string, std::string>& option : options) {
717         std::string section;
718         std::string key = option.first;
719         util::SettingsValue value = InterpretOption(section, key, option.second);
720         Optional<unsigned int> flags = GetArgFlags('-' + key);
721         if (flags) {
722             if (!CheckValid(key, value, *flags, error)) {
723                 return false;
724             }
725             m_settings.ro_config[section][key].push_back(value);
726         } else {
727             if (ignore_invalid_keys) {
728                 LogPrintf("Ignoring unknown configuration value %s\n", option.first);
729             } else {
730                 error = strprintf("Invalid configuration value %s", option.first);
731                 return false;
732             }
733         }
734     }
735     return true;
736 }
737 
ReadConfigFiles(std::string & error,bool ignore_invalid_keys)738 bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys)
739 {
740     {
741         LOCK(cs_args);
742         m_settings.ro_config.clear();
743         m_config_sections.clear();
744     }
745 
746     const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME);
747     fsbridge::ifstream stream(GetConfigFile(confPath));
748 
749     // ok to not have a config file
750     if (stream.good()) {
751         if (!ReadConfigStream(stream, confPath, error, ignore_invalid_keys)) {
752             return false;
753         }
754         // `-includeconf` cannot be included in the command line arguments except
755         // as `-noincludeconf` (which indicates that no included conf file should be used).
756         bool use_conf_file{true};
757         {
758             LOCK(cs_args);
759             if (auto* includes = util::FindKey(m_settings.command_line_options, "includeconf")) {
760                 // ParseParameters() fails if a non-negated -includeconf is passed on the command-line
761                 assert(util::SettingsSpan(*includes).last_negated());
762                 use_conf_file = false;
763             }
764         }
765         if (use_conf_file) {
766             std::string chain_id = GetChainName();
767             std::vector<std::string> conf_file_names;
768 
769             auto add_includes = [&](const std::string& network, size_t skip = 0) {
770                 size_t num_values = 0;
771                 LOCK(cs_args);
772                 if (auto* section = util::FindKey(m_settings.ro_config, network)) {
773                     if (auto* values = util::FindKey(*section, "includeconf")) {
774                         for (size_t i = std::max(skip, util::SettingsSpan(*values).negated()); i < values->size(); ++i) {
775                             conf_file_names.push_back((*values)[i].get_str());
776                         }
777                         num_values = values->size();
778                     }
779                 }
780                 return num_values;
781             };
782 
783             // We haven't set m_network yet (that happens in SelectParams()), so manually check
784             // for network.includeconf args.
785             const size_t chain_includes = add_includes(chain_id);
786             const size_t default_includes = add_includes({});
787 
788             for (const std::string& conf_file_name : conf_file_names) {
789                 fsbridge::ifstream conf_file_stream(GetConfigFile(conf_file_name));
790                 if (conf_file_stream.good()) {
791                     if (!ReadConfigStream(conf_file_stream, conf_file_name, error, ignore_invalid_keys)) {
792                         return false;
793                     }
794                     LogPrintf("Included configuration file %s\n", conf_file_name);
795                 } else {
796                     error = "Failed to include configuration file " + conf_file_name;
797                     return false;
798                 }
799             }
800 
801             // Warn about recursive -includeconf
802             conf_file_names.clear();
803             add_includes(chain_id, /* skip= */ chain_includes);
804             add_includes({}, /* skip= */ default_includes);
805             std::string chain_id_final = GetChainName();
806             if (chain_id_final != chain_id) {
807                 // Also warn about recursive includeconf for the chain that was specified in one of the includeconfs
808                 add_includes(chain_id_final);
809             }
810             for (const std::string& conf_file_name : conf_file_names) {
811                 tfm::format(std::cerr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", conf_file_name);
812             }
813         }
814     }
815 
816     // If datadir is changed in .conf file:
817     ClearDatadirCache();
818     if (!CheckDataDirOption()) {
819         error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", ""));
820         return false;
821     }
822     return true;
823 }
824 
GetChainName() const825 std::string ArgsManager::GetChainName() const
826 {
827     auto get_net = [&](const std::string& arg) {
828         LOCK(cs_args);
829         util::SettingsValue value = util::GetSetting(m_settings, /* section= */ "", SettingName(arg),
830             /* ignore_default_section_config= */ false,
831             /* get_chain_name= */ true);
832         return value.isNull() ? false : value.isBool() ? value.get_bool() : InterpretBool(value.get_str());
833     };
834 
835     const bool fRegTest = get_net("-regtest");
836     const bool fTestNet = get_net("-testnet");
837     const bool is_chain_arg_set = IsArgSet("-chain");
838 
839     if ((int)is_chain_arg_set + (int)fRegTest + (int)fTestNet > 1) {
840         throw std::runtime_error("Invalid combination of -regtest, -testnet and -chain. Can use at most one.");
841     }
842     if (fRegTest)
843         return CBaseChainParams::REGTEST;
844     if (fTestNet)
845         return CBaseChainParams::TESTNET;
846     return GetArg("-chain", CBaseChainParams::MAIN);
847 }
848 
UseDefaultSection(const std::string & arg) const849 bool ArgsManager::UseDefaultSection(const std::string& arg) const
850 {
851     return m_network == CBaseChainParams::MAIN || m_network_only_args.count(arg) == 0;
852 }
853 
GetSetting(const std::string & arg) const854 util::SettingsValue ArgsManager::GetSetting(const std::string& arg) const
855 {
856     LOCK(cs_args);
857     return util::GetSetting(
858         m_settings, m_network, SettingName(arg), !UseDefaultSection(arg), /* get_chain_name= */ false);
859 }
860 
GetSettingsList(const std::string & arg) const861 std::vector<util::SettingsValue> ArgsManager::GetSettingsList(const std::string& arg) const
862 {
863     LOCK(cs_args);
864     return util::GetSettingsList(m_settings, m_network, SettingName(arg), !UseDefaultSection(arg));
865 }
866 
logArgsPrefix(const std::string & prefix,const std::string & section,const std::map<std::string,std::vector<util::SettingsValue>> & args) const867 void ArgsManager::logArgsPrefix(
868     const std::string& prefix,
869     const std::string& section,
870     const std::map<std::string, std::vector<util::SettingsValue>>& args) const
871 {
872     std::string section_str = section.empty() ? "" : "[" + section + "] ";
873     for (const auto& arg : args) {
874         for (const auto& value : arg.second) {
875             Optional<unsigned int> flags = GetArgFlags('-' + arg.first);
876             if (flags) {
877                 std::string value_str = (*flags & SENSITIVE) ? "****" : value.write();
878                 LogPrintf("%s %s%s=%s\n", prefix, section_str, arg.first, value_str);
879             }
880         }
881     }
882 }
883 
LogArgs() const884 void ArgsManager::LogArgs() const
885 {
886     LOCK(cs_args);
887     for (const auto& section : m_settings.ro_config) {
888         logArgsPrefix("Config file arg:", section.first, section.second);
889     }
890     logArgsPrefix("Command-line arg:", "", m_settings.command_line_options);
891 }
892 
getArgsList(const std::vector<std::string> & paramListType) const893 std::map<std::string, std::vector<std::string>> ArgsManager::getArgsList(const std::vector<std::string>& paramListType) const
894 {
895     // Get argument list
896     std::map<std::string, bool> args;
897     for (const auto& arg : m_settings.forced_settings) {
898         args[arg.first] = true;
899     }
900     for (const auto& arg : m_settings.command_line_options) {
901         args[arg.first] = true;
902     }
903     for (const auto& arg : m_settings.ro_config) {
904         for(const auto& confArg : arg.second)
905             args[confArg.first] = true;
906     }
907 
908     // Fill argument list with values
909     std::map<std::string, std::vector<std::string>> ret;
910     for (const auto& arg : args) {
911         std::string paramName = '-' + arg.first;
912         std::vector<std::string> paramValue;
913         bool isList = std::find(std::begin(paramListType), std::end(paramListType), paramName) != std::end(paramListType);
914         if(isList) {
915             paramValue = GetArgs(paramName);
916         }
917         else {
918             paramValue.push_back(GetArg(paramName, ""));
919         }
920         ret[arg.first] = paramValue;
921     }
922 
923     return ret;
924 }
925 
RenameOver(fs::path src,fs::path dest)926 bool RenameOver(fs::path src, fs::path dest)
927 {
928 #ifdef WIN32
929     return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
930                        MOVEFILE_REPLACE_EXISTING) != 0;
931 #else
932     int rc = std::rename(src.string().c_str(), dest.string().c_str());
933     return (rc == 0);
934 #endif /* WIN32 */
935 }
936 
937 /**
938  * Ignores exceptions thrown by Boost's create_directories if the requested directory exists.
939  * Specifically handles case where path p exists, but it wasn't possible for the user to
940  * write to the parent directory.
941  */
TryCreateDirectories(const fs::path & p)942 bool TryCreateDirectories(const fs::path& p)
943 {
944     try
945     {
946         return fs::create_directories(p);
947     } catch (const fs::filesystem_error&) {
948         if (!fs::exists(p) || !fs::is_directory(p))
949             throw;
950     }
951 
952     // create_directories didn't create the directory, it had to have existed already
953     return false;
954 }
955 
FileCommit(FILE * file)956 bool FileCommit(FILE *file)
957 {
958     if (fflush(file) != 0) { // harmless if redundantly called
959         LogPrintf("%s: fflush failed: %d\n", __func__, errno);
960         return false;
961     }
962 #ifdef WIN32
963     HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
964     if (FlushFileBuffers(hFile) == 0) {
965         LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
966         return false;
967     }
968 #else
969     #if defined(__linux__) || defined(__NetBSD__)
970     if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
971         LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
972         return false;
973     }
974     #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
975     if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
976         LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
977         return false;
978     }
979     #else
980     if (fsync(fileno(file)) != 0 && errno != EINVAL) {
981         LogPrintf("%s: fsync failed: %d\n", __func__, errno);
982         return false;
983     }
984     #endif
985 #endif
986     return true;
987 }
988 
TruncateFile(FILE * file,unsigned int length)989 bool TruncateFile(FILE *file, unsigned int length) {
990 #if defined(WIN32)
991     return _chsize(_fileno(file), length) == 0;
992 #else
993     return ftruncate(fileno(file), length) == 0;
994 #endif
995 }
996 
997 /**
998  * this function tries to raise the file descriptor limit to the requested number.
999  * It returns the actual file descriptor limit (which may be more or less than nMinFD)
1000  */
RaiseFileDescriptorLimit(int nMinFD)1001 int RaiseFileDescriptorLimit(int nMinFD) {
1002 #if defined(WIN32)
1003     return 2048;
1004 #else
1005     struct rlimit limitFD;
1006     if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
1007         if (limitFD.rlim_cur < (rlim_t)nMinFD) {
1008             limitFD.rlim_cur = nMinFD;
1009             if (limitFD.rlim_cur > limitFD.rlim_max)
1010                 limitFD.rlim_cur = limitFD.rlim_max;
1011             setrlimit(RLIMIT_NOFILE, &limitFD);
1012             getrlimit(RLIMIT_NOFILE, &limitFD);
1013         }
1014         return limitFD.rlim_cur;
1015     }
1016     return nMinFD; // getrlimit failed, assume it's fine
1017 #endif
1018 }
1019 
1020 /**
1021  * this function tries to make a particular range of a file allocated (corresponding to disk space)
1022  * it is advisory, and the range specified in the arguments will never contain live data
1023  */
AllocateFileRange(FILE * file,unsigned int offset,unsigned int length)1024 void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) {
1025 #if defined(WIN32)
1026     // Windows-specific version
1027     HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
1028     LARGE_INTEGER nFileSize;
1029     int64_t nEndPos = (int64_t)offset + length;
1030     nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
1031     nFileSize.u.HighPart = nEndPos >> 32;
1032     SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
1033     SetEndOfFile(hFile);
1034 #elif defined(MAC_OSX)
1035     // OSX specific version
1036     // NOTE: Contrary to other OS versions, the OSX version assumes that
1037     // NOTE: offset is the size of the file.
1038     fstore_t fst;
1039     fst.fst_flags = F_ALLOCATECONTIG;
1040     fst.fst_posmode = F_PEOFPOSMODE;
1041     fst.fst_offset = 0;
1042     fst.fst_length = length; // mac os fst_length takes the # of free bytes to allocate, not desired file size
1043     fst.fst_bytesalloc = 0;
1044     if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
1045         fst.fst_flags = F_ALLOCATEALL;
1046         fcntl(fileno(file), F_PREALLOCATE, &fst);
1047     }
1048     ftruncate(fileno(file), static_cast<off_t>(offset) + length);
1049 #else
1050     #if defined(__linux__)
1051     // Version using posix_fallocate
1052     off_t nEndPos = (off_t)offset + length;
1053     if (0 == posix_fallocate(fileno(file), 0, nEndPos)) return;
1054     #endif
1055     // Fallback version
1056     // TODO: just write one byte per block
1057     static const char buf[65536] = {};
1058     if (fseek(file, offset, SEEK_SET)) {
1059         return;
1060     }
1061     while (length > 0) {
1062         unsigned int now = 65536;
1063         if (length < now)
1064             now = length;
1065         fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
1066         length -= now;
1067     }
1068 #endif
1069 }
1070 
1071 #ifdef WIN32
GetSpecialFolderPath(int nFolder,bool fCreate)1072 fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
1073 {
1074     WCHAR pszPath[MAX_PATH] = L"";
1075 
1076     if(SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate))
1077     {
1078         return fs::path(pszPath);
1079     }
1080 
1081     LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
1082     return fs::path("");
1083 }
1084 #endif
1085 
1086 #ifndef WIN32
ShellEscape(const std::string & arg)1087 std::string ShellEscape(const std::string& arg)
1088 {
1089     std::string escaped = arg;
1090     boost::replace_all(escaped, "'", "'\"'\"'");
1091     return "'" + escaped + "'";
1092 }
1093 #endif
1094 
1095 #if HAVE_SYSTEM
runCommand(const std::string & strCommand)1096 void runCommand(const std::string& strCommand)
1097 {
1098     if (strCommand.empty()) return;
1099 #ifndef WIN32
1100     int nErr = ::system(strCommand.c_str());
1101 #else
1102     int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str());
1103 #endif
1104     if (nErr)
1105         LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
1106 }
1107 #endif
1108 
SetupEnvironment()1109 void SetupEnvironment()
1110 {
1111 #ifdef HAVE_MALLOPT_ARENA_MAX
1112     // glibc-specific: On 32-bit systems set the number of arenas to 1.
1113     // By default, since glibc 2.10, the C library will create up to two heap
1114     // arenas per core. This is known to cause excessive virtual address space
1115     // usage in our usage. Work around it by setting the maximum number of
1116     // arenas to 1.
1117     if (sizeof(void*) == 4) {
1118         mallopt(M_ARENA_MAX, 1);
1119     }
1120 #endif
1121     // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
1122     // may be invalid, in which case the "C.UTF-8" locale is used as fallback.
1123 #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
1124     try {
1125         std::locale(""); // Raises a runtime error if current locale is invalid
1126     } catch (const std::runtime_error&) {
1127         setenv("LC_ALL", "C.UTF-8", 1);
1128     }
1129 #elif defined(WIN32)
1130     // Set the default input/output charset is utf-8
1131     SetConsoleCP(CP_UTF8);
1132     SetConsoleOutputCP(CP_UTF8);
1133 #endif
1134     // The path locale is lazy initialized and to avoid deinitialization errors
1135     // in multithreading environments, it is set explicitly by the main thread.
1136     // A dummy locale is used to extract the internal default locale, used by
1137     // fs::path, which is then used to explicitly imbue the path.
1138     std::locale loc = fs::path::imbue(std::locale::classic());
1139 #ifndef WIN32
1140     fs::path::imbue(loc);
1141 #else
1142     fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
1143 #endif
1144 }
1145 
SetupNetworking()1146 bool SetupNetworking()
1147 {
1148 #ifdef WIN32
1149     // Initialize Windows Sockets
1150     WSADATA wsadata;
1151     int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
1152     if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
1153         return false;
1154 #endif
1155     return true;
1156 }
1157 
GetNumCores()1158 int GetNumCores()
1159 {
1160     return std::thread::hardware_concurrency();
1161 }
1162 
CopyrightHolders(const std::string & strPrefix)1163 std::string CopyrightHolders(const std::string& strPrefix)
1164 {
1165     const auto copyright_devs = strprintf(_(COPYRIGHT_HOLDERS).translated, COPYRIGHT_HOLDERS_SUBSTITUTION);
1166     std::string strCopyrightHolders = strPrefix + copyright_devs;
1167 
1168     // Make sure Bitcoin Core copyright is not removed by accident
1169     if (copyright_devs.find("Qtum Core") == std::string::npos) {
1170         strCopyrightHolders += "\n" + strPrefix + "The Qtum Core Developers";
1171     }
1172     return strCopyrightHolders;
1173 }
1174 
CheckHex(const std::string & str)1175 bool CheckHex(const std::string& str) {
1176     size_t data=0;
1177     if(str.size() > 2 && (str.compare(0, 2, "0x") == 0 || str.compare(0, 2, "0X") == 0)){
1178         data=2;
1179     }
1180     return str.size() > data && str.find_first_not_of("0123456789abcdefABCDEF", data) == std::string::npos;
1181 }
1182 
1183 // Obtain the application startup time (used for uptime calculation)
GetStartupTime()1184 int64_t GetStartupTime()
1185 {
1186     return nStartupTime;
1187 }
1188 
AbsPathForConfigVal(const fs::path & path,bool net_specific)1189 fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
1190 {
1191     if (path.is_absolute()) {
1192         return path;
1193     }
1194     return fs::absolute(path, GetDataDir(net_specific));
1195 }
1196 
ScheduleBatchPriority()1197 void ScheduleBatchPriority()
1198 {
1199 #ifdef SCHED_BATCH
1200     const static sched_param param{};
1201     const int rc = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param);
1202     if (rc != 0) {
1203         LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(rc));
1204     }
1205 #endif
1206 }
1207 
toHexString(int64_t intValue)1208 std::string toHexString(int64_t intValue) {
1209     //Store big endian representation in a vector
1210     uint64_t num = (uint64_t)intValue;
1211     std::vector<unsigned char> bigEndian;
1212     for(int i=sizeof(num) -1; i>=0; i--){
1213        bigEndian.push_back( (num>>(8*i)) & 0xff );
1214     }
1215 
1216     //Convert the vector into hex string
1217     return "0x" + HexStr(bigEndian.begin(), bigEndian.end());
1218 }
1219 
ReplaceInt(const int64_t & number,const std::string & key,std::string & str)1220 void ReplaceInt(const int64_t& number, const std::string& key, std::string& str)
1221 {
1222     // Convert the number into hex string
1223     std::string num_hex = toHexString(number);
1224 
1225     // Search for key in str and replace it with the hex string
1226     std::string str_replaced = std::regex_replace(str, std::regex(key), num_hex);
1227     str = str_replaced;
1228 }
1229 
1230 namespace util {
1231 #ifdef WIN32
WinCmdLineArgs()1232 WinCmdLineArgs::WinCmdLineArgs()
1233 {
1234     wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
1235     std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
1236     argv = new char*[argc];
1237     args.resize(argc);
1238     for (int i = 0; i < argc; i++) {
1239         args[i] = utf8_cvt.to_bytes(wargv[i]);
1240         argv[i] = &*args[i].begin();
1241     }
1242     LocalFree(wargv);
1243 }
1244 
~WinCmdLineArgs()1245 WinCmdLineArgs::~WinCmdLineArgs()
1246 {
1247     delete[] argv;
1248 }
1249 
get()1250 std::pair<int, char**> WinCmdLineArgs::get()
1251 {
1252     return std::make_pair(argc, argv);
1253 }
1254 #endif
1255 } // namespace util
1256