7474#include < sys/prctl.h>
7575#endif
7676
77- #include < boost/algorithm/string/case_conv.hpp> // for to_lower()
7877#include < boost/algorithm/string/join.hpp>
79- #include < boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
8078#include < boost/program_options/detail/config_file.hpp>
8179#include < boost/program_options/parsers.hpp>
8280#include < boost/thread.hpp>
@@ -160,20 +158,54 @@ class CInit
160158} instance_of_cinit;
161159
162160
163- /* * Interpret string as boolean, for argument parsing */
161+ /* *
162+ * Interpret a string argument as a boolean.
163+ *
164+ * The definition of atoi() requires that non-numeric string values like "foo",
165+ * return 0. This means that if a user unintentionally supplies a non-integer
166+ * argument here, the return value is always false. This means that -foo=false
167+ * does what the user probably expects, but -foo=true is well defined but does
168+ * not do what they probably expected.
169+ *
170+ * The return value of atoi() is undefined when given input not representable as
171+ * an int. On most systems this means string value between "-2147483648" and
172+ * "2147483647" are well defined (this method will return true). Setting
173+ * -txindex=2147483648 on most systems, however, is probably undefined.
174+ *
175+ * For a more extensive discussion of this topic (and a wide range of opinions
176+ * on the Right Way to change this code), see upstream PR12713.
177+ */
164178static bool InterpretBool (const std::string& strValue)
165179{
166180 if (strValue.empty ())
167181 return true ;
168182 return (atoi (strValue) != 0 );
169183}
170184
171- /* * Turn -noX into -X=0 */
172- static void InterpretNegativeSetting (std::string& strKey, std::string& strValue)
185+ /* *
186+ * Interpret -nofoo as if the user supplied -foo=0.
187+ *
188+ * This method also tracks when the -no form was supplied, and treats "-foo" as
189+ * a negated option when this happens. This 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+ void ArgsManager::InterpretNegatedOption (std::string& key, std::string& val)
173195{
174- if (strKey.length ()>3 && strKey[0 ]==' -' && strKey[1 ]==' n' && strKey[2 ]==' o' ) {
175- strKey = " -" + strKey.substr (3 );
176- strValue = InterpretBool (strValue) ? " 0" : " 1" ;
196+ if (key.substr (0 , 3 ) == " -no" ) {
197+ bool bool_val = InterpretBool (val);
198+ if (!bool_val ) {
199+ // Double negatives like -nofoo=0 are supported (but discouraged)
200+ LogPrintf (" Warning: parsed potentially confusing double-negative %s=%s\n " , key, val);
201+ }
202+ key.erase (1 , 2 );
203+ m_negated_args.insert (key);
204+ val = bool_val ? " 0" : " 1" ;
205+ } else {
206+ // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo
207+ // as negated when we see the second option.
208+ m_negated_args.erase (key);
177209 }
178210}
179211
@@ -182,32 +214,34 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[])
182214 LOCK (cs_args);
183215 mapArgs.clear ();
184216 mapMultiArgs.clear ();
217+ m_negated_args.clear ();
185218
186219 for (int i = 1 ; i < argc; i++) {
187- std::string str (argv[i]);
188- std::string strValue ;
189- size_t is_index = str .find (' =' );
220+ std::string key (argv[i]);
221+ std::string val ;
222+ size_t is_index = key .find (' =' );
190223 if (is_index != std::string::npos) {
191- strValue = str .substr (is_index + 1 );
192- str = str. substr ( 0 , is_index);
224+ val = key .substr (is_index + 1 );
225+ key. erase ( is_index);
193226 }
194227#ifdef WIN32
195- boost::to_lower (str );
196- if (boost::algorithm::starts_with (str, " / " ) )
197- str = " - " + str. substr ( 1 ) ;
228+ std::transform (key. begin (), key. end (), key. begin (), ::tolower );
229+ if (key[ 0 ] == ' / ' )
230+ key[ 0 ] = ' - ' ;
198231#endif
199232
200- if (str [0 ] != ' -' )
233+ if (key [0 ] != ' -' )
201234 break ;
202235
203- // Interpret --foo as -foo.
204- // If both --foo and -foo are set, the last takes effect.
205- if (str.length () > 1 && str[1 ] == ' -' )
206- str = str.substr (1 );
207- InterpretNegativeSetting (str, strValue);
236+ // Transform --foo to -foo
237+ if (key.length () > 1 && key[1 ] == ' -' )
238+ key.erase (0 , 1 );
208239
209- mapArgs[str] = strValue;
210- mapMultiArgs[str].push_back (strValue);
240+ // Transform -nofoo to -foo=0
241+ InterpretNegatedOption (key, val);
242+
243+ mapArgs[key] = val;
244+ mapMultiArgs[key].push_back (val);
211245 }
212246}
213247
@@ -225,6 +259,12 @@ bool ArgsManager::IsArgSet(const std::string& strArg) const
225259 return mapArgs.count (strArg);
226260}
227261
262+ bool ArgsManager::IsArgNegated (const std::string& strArg) const
263+ {
264+ LOCK (cs_args);
265+ return m_negated_args.find (strArg) != m_negated_args.end ();
266+ }
267+
228268std::string ArgsManager::GetArg (const std::string& strArg, const std::string& strDefault) const
229269{
230270 LOCK (cs_args);
@@ -507,7 +547,7 @@ void ArgsManager::ReadConfigFile()
507547 // Don't overwrite existing settings so command line settings override pivx.conf
508548 std::string strKey = std::string (" -" ) + it->string_key ;
509549 std::string strValue = it->value [0 ];
510- InterpretNegativeSetting (strKey, strValue);
550+ InterpretNegatedOption (strKey, strValue);
511551 if (mapArgs.count (strKey) == 0 )
512552 mapArgs[strKey] = strValue;
513553 mapMultiArgs[strKey].push_back (strValue);
0 commit comments