70
70
#include < malloc.h>
71
71
#endif
72
72
73
- #include < boost/algorithm/string/case_conv.hpp> // for to_lower()
74
- #include < boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
75
73
#include < boost/interprocess/sync/file_lock.hpp>
76
74
#include < boost/program_options/detail/config_file.hpp>
77
75
#include < boost/thread.hpp>
@@ -432,21 +430,54 @@ bool DirIsWritable(const fs::path& directory)
432
430
return true ;
433
431
}
434
432
435
- /* * Interpret string as boolean, for argument parsing */
433
+ /* *
434
+ * Interpret a string argument as a boolean.
435
+ *
436
+ * The definition of atoi() requires that non-numeric string values like "foo",
437
+ * return 0. This means that if a user unintentionally supplies a non-integer
438
+ * argument here, the return value is always false. This means that -foo=false
439
+ * does what the user probably expects, but -foo=true is well defined but does
440
+ * not do what they probably expected.
441
+ *
442
+ * The return value of atoi() is undefined when given input not representable as
443
+ * an int. On most systems this means string value between "-2147483648" and
444
+ * "2147483647" are well defined (this method will return true). Setting
445
+ * -txindex=2147483648 on most systems, however, is probably undefined.
446
+ *
447
+ * For a more extensive discussion of this topic (and a wide range of opinions
448
+ * on the Right Way to change this code), see PR12713.
449
+ */
436
450
static bool InterpretBool (const std::string& strValue)
437
451
{
438
452
if (strValue.empty ())
439
453
return true ;
440
454
return (atoi (strValue) != 0 );
441
455
}
442
456
443
- /* * Turn -noX into -X=0 */
444
- static void InterpretNegativeSetting (std::string& strKey, std::string& strValue)
457
+ /* *
458
+ * Interpret -nofoo as if the user supplied -foo=0.
459
+ *
460
+ * This method also tracks when the -no form was supplied, and treats "-foo" as
461
+ * a negated option when this happens. This can be later checked using the
462
+ * IsArgNegated() method. One use case for this is to have a way to disable
463
+ * options that are not normally boolean (e.g. using -nodebuglogfile to request
464
+ * that debug log output is not sent to any file at all).
465
+ */
466
+ void ArgsManager::InterpretNegatedOption (std::string& key, std::string& val)
445
467
{
446
- if (strKey.length ()>3 && strKey[0 ]==' -' && strKey[1 ]==' n' && strKey[2 ]==' o' )
447
- {
448
- strKey = " -" + strKey.substr (3 );
449
- strValue = InterpretBool (strValue) ? " 0" : " 1" ;
468
+ if (key.substr (0 , 3 ) == " -no" ) {
469
+ bool bool_val = InterpretBool (val);
470
+ if (!bool_val ) {
471
+ // Double negatives like -nofoo=0 are supported (but discouraged)
472
+ LogPrintf (" Warning: parsed potentially confusing double-negative %s=%s\n " , key, val);
473
+ }
474
+ key.erase (1 , 2 );
475
+ m_negated_args.insert (key);
476
+ val = bool_val ? " 0" : " 1" ;
477
+ } else {
478
+ // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo
479
+ // as negated when we see the second option.
480
+ m_negated_args.erase (key);
450
481
}
451
482
}
452
483
@@ -455,34 +486,34 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[])
455
486
LOCK (cs_args);
456
487
mapArgs.clear ();
457
488
mapMultiArgs.clear ();
458
-
459
- for (int i = 1 ; i < argc; i++)
460
- {
461
- std::string str (argv[i]);
462
- std::string strValue;
463
- size_t is_index = str.find (' =' );
464
- if (is_index != std::string::npos)
465
- {
466
- strValue = str.substr (is_index+1 );
467
- str = str.substr (0 , is_index);
489
+ m_negated_args.clear ();
490
+
491
+ for (int i = 1 ; i < argc; i++) {
492
+ std::string key (argv[i]);
493
+ std::string val;
494
+ size_t is_index = key.find (' =' );
495
+ if (is_index != std::string::npos) {
496
+ val = key.substr (is_index + 1 );
497
+ key.erase (is_index);
468
498
}
469
499
#ifdef WIN32
470
- boost::to_lower (str );
471
- if (boost::algorithm::starts_with (str, " / " ) )
472
- str = " - " + str. substr ( 1 ) ;
500
+ std::transform (key. begin (), key. end (), key. begin (), ::tolower );
501
+ if (key[ 0 ] == ' / ' )
502
+ key[ 0 ] = ' - ' ;
473
503
#endif
474
504
475
- if (str [0 ] != ' -' )
505
+ if (key [0 ] != ' -' )
476
506
break ;
477
507
478
- // Interpret --foo as -foo.
479
- // If both --foo and -foo are set, the last takes effect.
480
- if (str.length () > 1 && str[1 ] == ' -' )
481
- str = str.substr (1 );
482
- InterpretNegativeSetting (str, strValue);
508
+ // Transform --foo to -foo
509
+ if (key.length () > 1 && key[1 ] == ' -' )
510
+ key.erase (0 , 1 );
511
+
512
+ // Transform -nofoo to -foo=0
513
+ InterpretNegatedOption (key, val);
483
514
484
- mapArgs[str ] = strValue ;
485
- mapMultiArgs[str ].push_back (strValue );
515
+ mapArgs[key ] = val ;
516
+ mapMultiArgs[key ].push_back (val );
486
517
}
487
518
}
488
519
@@ -500,6 +531,12 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const
500
531
return mapArgs.count (strArg);
501
532
}
502
533
534
+ bool ArgsManager::IsArgNegated (const std::string& strArg) const
535
+ {
536
+ LOCK (cs_args);
537
+ return m_negated_args.find (strArg) != m_negated_args.end ();
538
+ }
539
+
503
540
std::string ArgsManager::GetArg (const std::string& strArg, const std::string& strDefault) const
504
541
{
505
542
LOCK (cs_args);
@@ -711,7 +748,7 @@ void ArgsManager::ReadConfigFile(const std::string& confPath)
711
748
// Don't overwrite existing settings so command line settings override bitcoin.conf
712
749
std::string strKey = std::string (" -" ) + it->string_key ;
713
750
std::string strValue = it->value [0 ];
714
- InterpretNegativeSetting (strKey, strValue);
751
+ InterpretNegatedOption (strKey, strValue);
715
752
if (mapArgs.count (strKey) == 0 )
716
753
mapArgs[strKey] = strValue;
717
754
mapMultiArgs[strKey].push_back (strValue);
0 commit comments