From 70f8dc7a4f4be217fea5439e474fc75e567c23c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Sat, 10 Feb 2024 22:37:52 +0100 Subject: miscellaneous: BOOL (int) to real bool --- indra/newview/llcommandlineparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llcommandlineparser.cpp') diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 06d959ba3c..794bb78b9c 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -572,7 +572,7 @@ void setControlValueCB(const LLCommandLineParser::token_vector_t& value, // There's a token. check the string for true/false/1/0 etc. BOOL result = false; - BOOL gotSet = LLStringUtil::convertToBOOL(token, result); + bool gotSet = LLStringUtil::convertToBOOL(token, result); if (gotSet) { ctrl->setValue(LLSD(result), false); -- cgit v1.2.3 From 9e854b697a06abed2a0917fb6120445f176764f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20N=C3=A6sbye=20Christensen?= Date: Fri, 16 Feb 2024 19:29:51 +0100 Subject: misc: BOOL to bool --- indra/newview/llcommandlineparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/newview/llcommandlineparser.cpp') diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 794bb78b9c..bc61fac00b 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -571,7 +571,7 @@ void setControlValueCB(const LLCommandLineParser::token_vector_t& value, std::string token(onevalue(option, value)); // There's a token. check the string for true/false/1/0 etc. - BOOL result = false; + bool result = false; bool gotSet = LLStringUtil::convertToBOOL(token, result); if (gotSet) { -- cgit v1.2.3 From a865d423974ea06dffa47798c81e98e7570b02ec Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Tue, 5 Mar 2024 17:03:11 +0100 Subject: viewer#819 Avoid reading the same XML file multiple times --- indra/newview/llcommandlineparser.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'indra/newview/llcommandlineparser.cpp') diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index bc61fac00b..030d24bed8 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -657,12 +657,11 @@ void LLControlGroupCLP::configure(const std::string& config_filename, LLControlG // This method reads the llsd based config file, and uses it to set // members of a control group. LLSD clpConfigLLSD; - - llifstream input_stream; - input_stream.open(config_filename.c_str(), std::ios::in | std::ios::binary); - if(input_stream.is_open()) + std::string xml = gDirUtilp->getFileContents(config_filename); + if (!xml.empty()) { + std::stringstream input_stream(xml); LLSDSerialize::fromXML(clpConfigLLSD, input_stream); for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap(); option_itr != clpConfigLLSD.endMap(); -- cgit v1.2.3 From 2008f87f10d51a2f9372aa4a4d72e86ac94e1e81 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Mon, 13 May 2024 18:26:53 +0300 Subject: Revert "viewer#819 Avoid reading the same XML file multiple times" This reverts commit a865d423974ea06dffa47798c81e98e7570b02ec. Reason for revert: viewer#1420, reverting to not hold maint-A (is deepCopy not full?) --- indra/newview/llcommandlineparser.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'indra/newview/llcommandlineparser.cpp') diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 030d24bed8..bc61fac00b 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -657,11 +657,12 @@ void LLControlGroupCLP::configure(const std::string& config_filename, LLControlG // This method reads the llsd based config file, and uses it to set // members of a control group. LLSD clpConfigLLSD; + + llifstream input_stream; + input_stream.open(config_filename.c_str(), std::ios::in | std::ios::binary); - std::string xml = gDirUtilp->getFileContents(config_filename); - if (!xml.empty()) + if(input_stream.is_open()) { - std::stringstream input_stream(xml); LLSDSerialize::fromXML(clpConfigLLSD, input_stream); for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap(); option_itr != clpConfigLLSD.endMap(); -- cgit v1.2.3 From e2e37cced861b98de8c1a7c9c0d3a50d2d90e433 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Wed, 22 May 2024 21:25:21 +0200 Subject: Fix line endlings --- indra/newview/llcommandlineparser.cpp | 1510 ++++++++++++++++----------------- 1 file changed, 755 insertions(+), 755 deletions(-) (limited to 'indra/newview/llcommandlineparser.cpp') diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 62eec9fd5f..5b8d8b5480 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -1,755 +1,755 @@ -/** - * @file llcommandlineparser.cpp - * @brief The LLCommandLineParser class definitions - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" -#include "llcommandlineparser.h" -#include "llexception.h" - -// *NOTE: The boost::lexical_cast generates -// the warning C4701(local used with out assignment) in VC7.1. -// Disable the warning for the boost includes. -#if _MSC_VER -# pragma warning(push) -# pragma warning( disable : 4701 ) -#else -// NOTE: For the other platforms? -#endif - -#include -#include -#include -#include -#include - -#if _MSC_VER -# pragma warning(pop) -#endif - -#include "llsdserialize.h" -#include "llerror.h" -#include "stringize.h" -#include "llexception.h" -#include -#include -#include -#include -#include - -#include "llcontrol.h" - -namespace po = boost::program_options; - -// *NOTE:MEP - Currently the boost object reside in file scope. -// This has a couple of negatives, they are always around and -// there can be only one instance of each. -// The plus is that the boost-ly-ness of this implementation is -// hidden from the rest of the world. -// Its importatnt to realize that multiple LLCommandLineParser objects -// will all have this single repository of option escs and parsed options. -// This could be good or bad, and probably won't matter for most use cases. -namespace -{ - // List of command-line switches that can't map-to settings variables. - // Going forward, we want every new command-line switch to map-to some - // settings variable. This list is used to validate that. - const std::set unmapped_options = boost::assign::list_of - ("help") - ("set") - ("setdefault") - ("settings") - ("sessionsettings") - ("usersessionsettings") - ; - - po::options_description gOptionsDesc; - po::positional_options_description gPositionalOptions; - po::variables_map gVariableMap; - - const LLCommandLineParser::token_vector_t gEmptyValue; - - void read_file_into_string(std::string& str, const std::basic_istream < char >& file) - { - std::ostringstream oss; - oss << file.rdbuf(); - str = oss.str(); - } - - bool gPastLastOption = false; -} - -class LLCLPError : public LLException { -public: - LLCLPError(const std::string& what) : LLException(what) {} -}; - -class LLCLPLastOption : public LLException { -public: - LLCLPLastOption(const std::string& what) : LLException(what) {} -}; - -class LLCLPValue : public po::value_semantic_codecvt_helper -{ - unsigned mMinTokens; - unsigned mMaxTokens; - bool mIsComposing; - typedef boost::function1 notify_callback_t; - notify_callback_t mNotifyCallback; - bool mLastOption; - -public: - LLCLPValue() : - mMinTokens(0), - mMaxTokens(0), - mIsComposing(false), - mLastOption(false) - {} - - virtual ~LLCLPValue() {}; - - void setMinTokens(unsigned c) - { - mMinTokens = c; - } - - void setMaxTokens(unsigned c) - { - mMaxTokens = c; - } - - void setComposing(bool c) - { - mIsComposing = c; - } - - void setLastOption(bool c) - { - mLastOption = c; - } - - void setNotifyCallback(notify_callback_t f) - { - mNotifyCallback = f; - } - - // Overrides to support the value_semantic interface. - virtual std::string name() const - { - const std::string arg("arg"); - const std::string args("args"); - return (max_tokens() > 1) ? args : arg; - } - - virtual unsigned min_tokens() const - { - return mMinTokens; - } - - virtual unsigned max_tokens() const - { - return mMaxTokens; - } - - virtual bool is_composing() const - { - return mIsComposing; - } - - // Needed for boost 1.42 - virtual bool is_required() const - { - return false; // All our command line options are optional. - } - - virtual bool apply_default(boost::any& value_store) const - { - return false; // No defaults. - } - - virtual void notify(const boost::any& value_store) const - { - const LLCommandLineParser::token_vector_t* value = - boost::any_cast(&value_store); - if(mNotifyCallback) - { - mNotifyCallback(*value); - } - } - -protected: - void xparse(boost::any& value_store, - const std::vector& new_tokens) const - { - if(gPastLastOption) - { - LLTHROW(LLCLPLastOption("Don't parse no more!")); - } - - // Error checks. Needed? - if (!value_store.empty() && !is_composing()) - { - LLTHROW(LLCLPError("Non composing value with multiple occurences.")); - } - if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens()) - { - LLTHROW(LLCLPError("Illegal number of tokens specified.")); - } - - if(value_store.empty()) - { - value_store = boost::any(LLCommandLineParser::token_vector_t()); - } - LLCommandLineParser::token_vector_t* tv = - boost::any_cast(&value_store); - - for(unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i) - { - tv->push_back(new_tokens[i]); - } - - if(mLastOption) - { - gPastLastOption = true; - } - } -}; - -//---------------------------------------------------------------------------- -// LLCommandLineParser defintions -//---------------------------------------------------------------------------- -void LLCommandLineParser::addOptionDesc(const std::string& option_name, - boost::function1 notify_callback, - unsigned int token_count, - const std::string& description, - const std::string& short_name, - bool composing, - bool positional, - bool last_option) -{ - // Compose the name for boost::po. - // It takes the format "long_name, short name" - const std::string comma(","); - std::string boost_option_name = option_name; - if(short_name != LLStringUtil::null) - { - boost_option_name += comma; - boost_option_name += short_name; - } - - LLCLPValue* value_desc = new LLCLPValue(); - value_desc->setMinTokens(token_count); - value_desc->setMaxTokens(token_count); - value_desc->setComposing(composing); - value_desc->setLastOption(last_option); - - boost::shared_ptr d( - new po::option_description(boost_option_name.c_str(), - value_desc, - description.c_str())); - - if(!notify_callback.empty()) - { - value_desc->setNotifyCallback(notify_callback); - } - - gOptionsDesc.add(d); - - if(positional) - { - gPositionalOptions.add(boost_option_name.c_str(), token_count); - } -} - -bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp) -{ - try - { - clp.options(gOptionsDesc); - clp.positional(gPositionalOptions); - // SNOW-626: Boost 1.42 erroneously added allow_guessing to the default style - // (see http://groups.google.com/group/boost-list/browse_thread/thread/545d7bf98ff9bb16?fwc=2&pli=1) - // Remove allow_guessing from the default style, because that is not allowed - // when we have options that are a prefix of other options (aka, --help and --helperuri). - clp.style((po::command_line_style::default_style & ~po::command_line_style::allow_guessing) - | po::command_line_style::allow_long_disguise); - if(mExtraParser) - { - clp.extra_parser(mExtraParser); - } - - po::basic_parsed_options opts = clp.run(); - po::store(opts, gVariableMap); - } - catch(po::error& e) - { - LL_WARNS() << "Caught Error:" << e.what() << LL_ENDL; - mErrorMsg = e.what(); - return false; - } - catch(LLCLPError& e) - { - LL_WARNS() << "Caught Error:" << e.what() << LL_ENDL; - mErrorMsg = e.what(); - return false; - } - catch(LLCLPLastOption&) - { - // This exception means a token was read after an option - // that must be the last option was reached (see url and slurl options) - - // boost::po will have stored a malformed option. - // All such options will be removed below. - // The last option read, the last_option option, and its value - // are put into the error message. - std::string last_option; - std::string last_value; - for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();) - { - po::variables_map::iterator tempI = i++; - if(tempI->second.empty()) - { - gVariableMap.erase(tempI); - } - else - { - last_option = tempI->first; - LLCommandLineParser::token_vector_t* tv = - boost::any_cast(&(tempI->second.value())); - if(!tv->empty()) - { - last_value = (*tv)[tv->size()-1]; - } - } - } - - // Continue without parsing. - std::ostringstream msg; - msg << "Caught Error: Found options after last option: " - << last_option << " " - << last_value; - - LL_WARNS() << msg.str() << LL_ENDL; - mErrorMsg = msg.str(); - return false; - } - return true; -} - -bool LLCommandLineParser::parseCommandLine(int argc, char **argv) -{ - po::command_line_parser clp(argc, argv); - return parseAndStoreResults(clp); -} - -// TODO: -// - Break out this funky parsing logic into separate method -// - Unit-test it with tests like LLStringUtil::getTokens() (the command-line -// overload that supports quoted tokens) -// - Unless this logic offers significant semantic benefits, replace it with -// LLStringUtil::getTokens(). This would fix a known bug: you cannot --set a -// string-valued variable to the empty string, because empty strings are -// eliminated below. - -bool LLCommandLineParser::parseCommandLineString(const std::string& str) -{ - std::string cmd_line_string(""); - if (!str.empty()) - { - bool add_last_c = true; - S32 last_c_pos = str.size() - 1; //don't get out of bounds on pos+1, last char will be processed separately - for (S32 pos = 0; pos < last_c_pos; ++pos) - { - cmd_line_string.append(&str[pos], 1); - if (str[pos] == '\\') - { - cmd_line_string.append("\\", 1); - if (str[pos + 1] == '\\') - { - ++pos; - add_last_c = (pos != last_c_pos); - } - } - } - if (add_last_c) - { - cmd_line_string.append(&str[last_c_pos], 1); - if (str[last_c_pos] == '\\') - { - cmd_line_string.append("\\", 1); - } - } - } - - std::vector tokens; - try - { - // Split the string content into tokens - const char* escape_chars = "\\"; - const char* separator_chars = "\r\n "; - const char* quote_chars = "\"'"; - boost::escaped_list_separator sep(escape_chars, separator_chars, quote_chars); - boost::tokenizer< boost::escaped_list_separator > tok(cmd_line_string, sep); - // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens)); - for (boost::tokenizer< boost::escaped_list_separator >::iterator i = tok.begin(); - i != tok.end(); - ++i) - { - if (0 != i->size()) - { - tokens.push_back(*i); - } - } - } - catch (...) - { - CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("Unexpected crash while parsing: " << str)); - } - - po::command_line_parser clp(tokens); - return parseAndStoreResults(clp); - -} - -bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream < char >& file) -{ - std::string args; - read_file_into_string(args, file); - - return parseCommandLineString(args); -} - -bool LLCommandLineParser::notify() -{ - try - { - po::notify(gVariableMap); - return true; - } - catch (const LLCLPError& e) - { - LL_WARNS() << "Caught Error: " << e.what() << LL_ENDL; - mErrorMsg = e.what(); - return false; - } -} - -void LLCommandLineParser::printOptions() const -{ - for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end(); ++i) - { - std::string name = i->first; - token_vector_t values = i->second.as(); - std::ostringstream oss; - oss << name << ": "; - for(token_vector_t::iterator t_itr = values.begin(); t_itr != values.end(); ++t_itr) - { - oss << t_itr->c_str() << " "; - } - LL_INFOS() << oss.str() << LL_ENDL; - } -} - -std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const -{ - return os << gOptionsDesc; -} - -bool LLCommandLineParser::hasOption(const std::string& name) const -{ - return gVariableMap.count(name) > 0; -} - -const LLCommandLineParser::token_vector_t& LLCommandLineParser::getOption(const std::string& name) const -{ - if(hasOption(name)) - { - return gVariableMap[name].as(); - } - - return gEmptyValue; -} - -//---------------------------------------------------------------------------- -// LLControlGroupCLP defintions -//---------------------------------------------------------------------------- -namespace { -LLCommandLineParser::token_vector_t::value_type -onevalue(const std::string& option, - const LLCommandLineParser::token_vector_t& value) -{ - if (value.empty()) - { - // What does it mean when the user specifies a command-line switch - // that requires a value, but omits the value? Complain. - LLTHROW(LLCLPError(STRINGIZE("No value specified for --" << option << "!"))); - } - else if (value.size() > 1) - { - LL_WARNS() << "Ignoring extra tokens specified for --" - << option << "." << LL_ENDL; - } - return value[0]; -} - -void badvalue(const std::string& option, - const std::string& varname, - const std::string& type, - const std::string& value) -{ - // If the user passes an unusable value for a command-line switch, it - // seems like a really bad idea to just ignore it, even with a log - // warning. - LLTHROW(LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option - << "' for variable '" << varname << "' of type " << type - << ": '" << value << "'"))); -} - -template -T convertTo(const std::string& option, - const std::string& varname, - const LLCommandLineParser::token_vector_t::value_type& value) -{ - try - { - return boost::lexical_cast(value); - } - catch (const boost::bad_lexical_cast&) - { - badvalue(option, varname, typeid(T).name(), value); - // bogus return; compiler unaware that badvalue() won't return - return T(); - } -} - -void setControlValueCB(const LLCommandLineParser::token_vector_t& value, - const std::string& option, - LLControlVariable* ctrl) -{ - // *FIX: Do semantic conversion here. - // LLSD (ImplString) Is no good for doing string to type conversion for... - // booleans - // compound types - // ?... - - if(NULL != ctrl) - { - switch(ctrl->type()) - { - case TYPE_BOOLEAN: - if (value.empty()) - { - // Boolean-valued command-line switches are unusual. If you - // simply specify the switch without an explicit value, we can - // infer you mean 'true'. - ctrl->setValue(LLSD(true), false); - } - else - { - // Only call onevalue() AFTER handling value.empty() case! - std::string token(onevalue(option, value)); - - // There's a token. check the string for true/false/1/0 etc. - bool result = false; - bool gotSet = LLStringUtil::convertToBOOL(token, result); - if (gotSet) - { - ctrl->setValue(LLSD(result), false); - } - else - { - badvalue(option, ctrl->getName(), "bool", token); - } - } - break; - - case TYPE_U32: - { - std::string token(onevalue(option, value)); - // To my surprise, for an unsigned target, lexical_cast() doesn't - // complain about an input string such as "-17". In that case, you - // get a very large positive result. So for U32, make sure there's - // no minus sign! - if (token.find('-') == std::string::npos) - { - ctrl->setValue(LLSD::Integer(convertTo(option, ctrl->getName(), token)), - false); - } - else - { - badvalue(option, ctrl->getName(), "unsigned", token); - } - break; - } - - case TYPE_S32: - ctrl->setValue(convertTo(option, ctrl->getName(), - onevalue(option, value)), false); - break; - - case TYPE_F32: - ctrl->setValue(convertTo(option, ctrl->getName(), - onevalue(option, value)), false); - break; - - // It appears that no one has yet tried to define a command-line - // switch mapped to a settings variable of TYPE_VEC3, TYPE_VEC3D, - // TYPE_RECT, TYPE_COL4, TYPE_COL3. Such types would certainly seem to - // call for a bit of special handling here... - default: - { - // For the default types, let llsd do the conversion. - if(value.size() > 1 && ctrl->isType(TYPE_LLSD)) - { - // Assume its an array... - LLSD llsdArray; - for(unsigned int i = 0; i < value.size(); ++i) - { - LLSD llsdValue; - llsdValue.assign(LLSD::String(value[i])); - llsdArray.set(i, llsdValue); - } - - ctrl->setValue(llsdArray, false); - } - else - { - ctrl->setValue(onevalue(option, value), false); - } - } - break; - } - } - else - { - // This isn't anything a user can affect -- it's a misconfiguration on - // the part of the coder. Rub the coder's nose in the problem right - // away so even preliminary testing will surface it. - LL_ERRS() << "Command Line option --" << option - << " maps to unknown setting!" << LL_ENDL; - } -} -} // anonymous namespace - -void LLControlGroupCLP::configure(const std::string& config_filename, LLControlGroup* controlGroup) -{ - // This method reads the llsd based config file, and uses it to set - // members of a control group. - LLSD clpConfigLLSD; - - llifstream input_stream; - input_stream.open(config_filename.c_str(), std::ios::in | std::ios::binary); - - if(input_stream.is_open()) - { - LLSDSerialize::fromXML(clpConfigLLSD, input_stream); - for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap(); - option_itr != clpConfigLLSD.endMap(); - ++option_itr) - { - LLSD::String long_name = option_itr->first; - LLSD option_params = option_itr->second; - - std::string desc("n/a"); - if(option_params.has("desc")) - { - desc = option_params["desc"].asString(); - } - - std::string short_name = LLStringUtil::null; - if(option_params.has("short")) - { - short_name = option_params["short"].asString(); - } - - unsigned int token_count = 0; - if(option_params.has("count")) - { - token_count = option_params["count"].asInteger(); - } - - bool composing = false; - if(option_params.has("compose")) - { - composing = option_params["compose"].asBoolean(); - } - - bool positional = false; - if(option_params.has("positional")) - { - positional = option_params["positional"].asBoolean(); - } - - bool last_option = false; - if(option_params.has("last_option")) - { - last_option = option_params["last_option"].asBoolean(); - } - - boost::function1 callback; - if (! option_params.has("map-to")) - { - // If this option isn't mapped to a settings variable, is it - // one of the ones for which that's unreasonable, or did - // someone carelessly add a new option? (Make all these - // configuration errors fatal so a maintainer will catch them - // right away.) - std::set::const_iterator found = unmapped_options.find(long_name); - if (found == unmapped_options.end()) - { - LL_ERRS() << "New command-line option " << long_name - << " should map-to a variable in settings.xml" << LL_ENDL; - } - } - else // option specifies map-to - { - std::string controlName = option_params["map-to"].asString(); - if (! controlGroup) - { - LL_ERRS() << "Must pass gSavedSettings to LLControlGroupCLP::configure() for " - << long_name << " (map-to " << controlName << ")" << LL_ENDL; - } - - LLControlVariable* ctrl = controlGroup->getControl(controlName); - if (! ctrl) - { - LL_ERRS() << "Option " << long_name << " specifies map-to " << controlName - << " which does not exist" << LL_ENDL; - } - - callback = boost::bind(setControlValueCB, _1, long_name, ctrl); - } - - this->addOptionDesc( - long_name, - callback, - token_count, - desc, - short_name, - composing, - positional, - last_option); - } - } -} +/** + * @file llcommandlineparser.cpp + * @brief The LLCommandLineParser class definitions + * + * $LicenseInfo:firstyear=2007&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llcommandlineparser.h" +#include "llexception.h" + +// *NOTE: The boost::lexical_cast generates +// the warning C4701(local used with out assignment) in VC7.1. +// Disable the warning for the boost includes. +#if _MSC_VER +# pragma warning(push) +# pragma warning( disable : 4701 ) +#else +// NOTE: For the other platforms? +#endif + +#include +#include +#include +#include +#include + +#if _MSC_VER +# pragma warning(pop) +#endif + +#include "llsdserialize.h" +#include "llerror.h" +#include "stringize.h" +#include "llexception.h" +#include +#include +#include +#include +#include + +#include "llcontrol.h" + +namespace po = boost::program_options; + +// *NOTE:MEP - Currently the boost object reside in file scope. +// This has a couple of negatives, they are always around and +// there can be only one instance of each. +// The plus is that the boost-ly-ness of this implementation is +// hidden from the rest of the world. +// Its importatnt to realize that multiple LLCommandLineParser objects +// will all have this single repository of option escs and parsed options. +// This could be good or bad, and probably won't matter for most use cases. +namespace +{ + // List of command-line switches that can't map-to settings variables. + // Going forward, we want every new command-line switch to map-to some + // settings variable. This list is used to validate that. + const std::set unmapped_options = boost::assign::list_of + ("help") + ("set") + ("setdefault") + ("settings") + ("sessionsettings") + ("usersessionsettings") + ; + + po::options_description gOptionsDesc; + po::positional_options_description gPositionalOptions; + po::variables_map gVariableMap; + + const LLCommandLineParser::token_vector_t gEmptyValue; + + void read_file_into_string(std::string& str, const std::basic_istream < char >& file) + { + std::ostringstream oss; + oss << file.rdbuf(); + str = oss.str(); + } + + bool gPastLastOption = false; +} + +class LLCLPError : public LLException { +public: + LLCLPError(const std::string& what) : LLException(what) {} +}; + +class LLCLPLastOption : public LLException { +public: + LLCLPLastOption(const std::string& what) : LLException(what) {} +}; + +class LLCLPValue : public po::value_semantic_codecvt_helper +{ + unsigned mMinTokens; + unsigned mMaxTokens; + bool mIsComposing; + typedef boost::function1 notify_callback_t; + notify_callback_t mNotifyCallback; + bool mLastOption; + +public: + LLCLPValue() : + mMinTokens(0), + mMaxTokens(0), + mIsComposing(false), + mLastOption(false) + {} + + virtual ~LLCLPValue() {}; + + void setMinTokens(unsigned c) + { + mMinTokens = c; + } + + void setMaxTokens(unsigned c) + { + mMaxTokens = c; + } + + void setComposing(bool c) + { + mIsComposing = c; + } + + void setLastOption(bool c) + { + mLastOption = c; + } + + void setNotifyCallback(notify_callback_t f) + { + mNotifyCallback = f; + } + + // Overrides to support the value_semantic interface. + virtual std::string name() const + { + const std::string arg("arg"); + const std::string args("args"); + return (max_tokens() > 1) ? args : arg; + } + + virtual unsigned min_tokens() const + { + return mMinTokens; + } + + virtual unsigned max_tokens() const + { + return mMaxTokens; + } + + virtual bool is_composing() const + { + return mIsComposing; + } + + // Needed for boost 1.42 + virtual bool is_required() const + { + return false; // All our command line options are optional. + } + + virtual bool apply_default(boost::any& value_store) const + { + return false; // No defaults. + } + + virtual void notify(const boost::any& value_store) const + { + const LLCommandLineParser::token_vector_t* value = + boost::any_cast(&value_store); + if(mNotifyCallback) + { + mNotifyCallback(*value); + } + } + +protected: + void xparse(boost::any& value_store, + const std::vector& new_tokens) const + { + if(gPastLastOption) + { + LLTHROW(LLCLPLastOption("Don't parse no more!")); + } + + // Error checks. Needed? + if (!value_store.empty() && !is_composing()) + { + LLTHROW(LLCLPError("Non composing value with multiple occurences.")); + } + if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens()) + { + LLTHROW(LLCLPError("Illegal number of tokens specified.")); + } + + if(value_store.empty()) + { + value_store = boost::any(LLCommandLineParser::token_vector_t()); + } + LLCommandLineParser::token_vector_t* tv = + boost::any_cast(&value_store); + + for(unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i) + { + tv->push_back(new_tokens[i]); + } + + if(mLastOption) + { + gPastLastOption = true; + } + } +}; + +//---------------------------------------------------------------------------- +// LLCommandLineParser defintions +//---------------------------------------------------------------------------- +void LLCommandLineParser::addOptionDesc(const std::string& option_name, + boost::function1 notify_callback, + unsigned int token_count, + const std::string& description, + const std::string& short_name, + bool composing, + bool positional, + bool last_option) +{ + // Compose the name for boost::po. + // It takes the format "long_name, short name" + const std::string comma(","); + std::string boost_option_name = option_name; + if(short_name != LLStringUtil::null) + { + boost_option_name += comma; + boost_option_name += short_name; + } + + LLCLPValue* value_desc = new LLCLPValue(); + value_desc->setMinTokens(token_count); + value_desc->setMaxTokens(token_count); + value_desc->setComposing(composing); + value_desc->setLastOption(last_option); + + boost::shared_ptr d( + new po::option_description(boost_option_name.c_str(), + value_desc, + description.c_str())); + + if(!notify_callback.empty()) + { + value_desc->setNotifyCallback(notify_callback); + } + + gOptionsDesc.add(d); + + if(positional) + { + gPositionalOptions.add(boost_option_name.c_str(), token_count); + } +} + +bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp) +{ + try + { + clp.options(gOptionsDesc); + clp.positional(gPositionalOptions); + // SNOW-626: Boost 1.42 erroneously added allow_guessing to the default style + // (see http://groups.google.com/group/boost-list/browse_thread/thread/545d7bf98ff9bb16?fwc=2&pli=1) + // Remove allow_guessing from the default style, because that is not allowed + // when we have options that are a prefix of other options (aka, --help and --helperuri). + clp.style((po::command_line_style::default_style & ~po::command_line_style::allow_guessing) + | po::command_line_style::allow_long_disguise); + if(mExtraParser) + { + clp.extra_parser(mExtraParser); + } + + po::basic_parsed_options opts = clp.run(); + po::store(opts, gVariableMap); + } + catch(po::error& e) + { + LL_WARNS() << "Caught Error:" << e.what() << LL_ENDL; + mErrorMsg = e.what(); + return false; + } + catch(LLCLPError& e) + { + LL_WARNS() << "Caught Error:" << e.what() << LL_ENDL; + mErrorMsg = e.what(); + return false; + } + catch(LLCLPLastOption&) + { + // This exception means a token was read after an option + // that must be the last option was reached (see url and slurl options) + + // boost::po will have stored a malformed option. + // All such options will be removed below. + // The last option read, the last_option option, and its value + // are put into the error message. + std::string last_option; + std::string last_value; + for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();) + { + po::variables_map::iterator tempI = i++; + if(tempI->second.empty()) + { + gVariableMap.erase(tempI); + } + else + { + last_option = tempI->first; + LLCommandLineParser::token_vector_t* tv = + boost::any_cast(&(tempI->second.value())); + if(!tv->empty()) + { + last_value = (*tv)[tv->size()-1]; + } + } + } + + // Continue without parsing. + std::ostringstream msg; + msg << "Caught Error: Found options after last option: " + << last_option << " " + << last_value; + + LL_WARNS() << msg.str() << LL_ENDL; + mErrorMsg = msg.str(); + return false; + } + return true; +} + +bool LLCommandLineParser::parseCommandLine(int argc, char **argv) +{ + po::command_line_parser clp(argc, argv); + return parseAndStoreResults(clp); +} + +// TODO: +// - Break out this funky parsing logic into separate method +// - Unit-test it with tests like LLStringUtil::getTokens() (the command-line +// overload that supports quoted tokens) +// - Unless this logic offers significant semantic benefits, replace it with +// LLStringUtil::getTokens(). This would fix a known bug: you cannot --set a +// string-valued variable to the empty string, because empty strings are +// eliminated below. + +bool LLCommandLineParser::parseCommandLineString(const std::string& str) +{ + std::string cmd_line_string(""); + if (!str.empty()) + { + bool add_last_c = true; + S32 last_c_pos = str.size() - 1; //don't get out of bounds on pos+1, last char will be processed separately + for (S32 pos = 0; pos < last_c_pos; ++pos) + { + cmd_line_string.append(&str[pos], 1); + if (str[pos] == '\\') + { + cmd_line_string.append("\\", 1); + if (str[pos + 1] == '\\') + { + ++pos; + add_last_c = (pos != last_c_pos); + } + } + } + if (add_last_c) + { + cmd_line_string.append(&str[last_c_pos], 1); + if (str[last_c_pos] == '\\') + { + cmd_line_string.append("\\", 1); + } + } + } + + std::vector tokens; + try + { + // Split the string content into tokens + const char* escape_chars = "\\"; + const char* separator_chars = "\r\n "; + const char* quote_chars = "\"'"; + boost::escaped_list_separator sep(escape_chars, separator_chars, quote_chars); + boost::tokenizer< boost::escaped_list_separator > tok(cmd_line_string, sep); + // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens)); + for (boost::tokenizer< boost::escaped_list_separator >::iterator i = tok.begin(); + i != tok.end(); + ++i) + { + if (0 != i->size()) + { + tokens.push_back(*i); + } + } + } + catch (...) + { + CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("Unexpected crash while parsing: " << str)); + } + + po::command_line_parser clp(tokens); + return parseAndStoreResults(clp); + +} + +bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream < char >& file) +{ + std::string args; + read_file_into_string(args, file); + + return parseCommandLineString(args); +} + +bool LLCommandLineParser::notify() +{ + try + { + po::notify(gVariableMap); + return true; + } + catch (const LLCLPError& e) + { + LL_WARNS() << "Caught Error: " << e.what() << LL_ENDL; + mErrorMsg = e.what(); + return false; + } +} + +void LLCommandLineParser::printOptions() const +{ + for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end(); ++i) + { + std::string name = i->first; + token_vector_t values = i->second.as(); + std::ostringstream oss; + oss << name << ": "; + for(token_vector_t::iterator t_itr = values.begin(); t_itr != values.end(); ++t_itr) + { + oss << t_itr->c_str() << " "; + } + LL_INFOS() << oss.str() << LL_ENDL; + } +} + +std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const +{ + return os << gOptionsDesc; +} + +bool LLCommandLineParser::hasOption(const std::string& name) const +{ + return gVariableMap.count(name) > 0; +} + +const LLCommandLineParser::token_vector_t& LLCommandLineParser::getOption(const std::string& name) const +{ + if(hasOption(name)) + { + return gVariableMap[name].as(); + } + + return gEmptyValue; +} + +//---------------------------------------------------------------------------- +// LLControlGroupCLP defintions +//---------------------------------------------------------------------------- +namespace { +LLCommandLineParser::token_vector_t::value_type +onevalue(const std::string& option, + const LLCommandLineParser::token_vector_t& value) +{ + if (value.empty()) + { + // What does it mean when the user specifies a command-line switch + // that requires a value, but omits the value? Complain. + LLTHROW(LLCLPError(STRINGIZE("No value specified for --" << option << "!"))); + } + else if (value.size() > 1) + { + LL_WARNS() << "Ignoring extra tokens specified for --" + << option << "." << LL_ENDL; + } + return value[0]; +} + +void badvalue(const std::string& option, + const std::string& varname, + const std::string& type, + const std::string& value) +{ + // If the user passes an unusable value for a command-line switch, it + // seems like a really bad idea to just ignore it, even with a log + // warning. + LLTHROW(LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option + << "' for variable '" << varname << "' of type " << type + << ": '" << value << "'"))); +} + +template +T convertTo(const std::string& option, + const std::string& varname, + const LLCommandLineParser::token_vector_t::value_type& value) +{ + try + { + return boost::lexical_cast(value); + } + catch (const boost::bad_lexical_cast&) + { + badvalue(option, varname, typeid(T).name(), value); + // bogus return; compiler unaware that badvalue() won't return + return T(); + } +} + +void setControlValueCB(const LLCommandLineParser::token_vector_t& value, + const std::string& option, + LLControlVariable* ctrl) +{ + // *FIX: Do semantic conversion here. + // LLSD (ImplString) Is no good for doing string to type conversion for... + // booleans + // compound types + // ?... + + if(NULL != ctrl) + { + switch(ctrl->type()) + { + case TYPE_BOOLEAN: + if (value.empty()) + { + // Boolean-valued command-line switches are unusual. If you + // simply specify the switch without an explicit value, we can + // infer you mean 'true'. + ctrl->setValue(LLSD(true), false); + } + else + { + // Only call onevalue() AFTER handling value.empty() case! + std::string token(onevalue(option, value)); + + // There's a token. check the string for true/false/1/0 etc. + bool result = false; + bool gotSet = LLStringUtil::convertToBOOL(token, result); + if (gotSet) + { + ctrl->setValue(LLSD(result), false); + } + else + { + badvalue(option, ctrl->getName(), "bool", token); + } + } + break; + + case TYPE_U32: + { + std::string token(onevalue(option, value)); + // To my surprise, for an unsigned target, lexical_cast() doesn't + // complain about an input string such as "-17". In that case, you + // get a very large positive result. So for U32, make sure there's + // no minus sign! + if (token.find('-') == std::string::npos) + { + ctrl->setValue(LLSD::Integer(convertTo(option, ctrl->getName(), token)), + false); + } + else + { + badvalue(option, ctrl->getName(), "unsigned", token); + } + break; + } + + case TYPE_S32: + ctrl->setValue(convertTo(option, ctrl->getName(), + onevalue(option, value)), false); + break; + + case TYPE_F32: + ctrl->setValue(convertTo(option, ctrl->getName(), + onevalue(option, value)), false); + break; + + // It appears that no one has yet tried to define a command-line + // switch mapped to a settings variable of TYPE_VEC3, TYPE_VEC3D, + // TYPE_RECT, TYPE_COL4, TYPE_COL3. Such types would certainly seem to + // call for a bit of special handling here... + default: + { + // For the default types, let llsd do the conversion. + if(value.size() > 1 && ctrl->isType(TYPE_LLSD)) + { + // Assume its an array... + LLSD llsdArray; + for(unsigned int i = 0; i < value.size(); ++i) + { + LLSD llsdValue; + llsdValue.assign(LLSD::String(value[i])); + llsdArray.set(i, llsdValue); + } + + ctrl->setValue(llsdArray, false); + } + else + { + ctrl->setValue(onevalue(option, value), false); + } + } + break; + } + } + else + { + // This isn't anything a user can affect -- it's a misconfiguration on + // the part of the coder. Rub the coder's nose in the problem right + // away so even preliminary testing will surface it. + LL_ERRS() << "Command Line option --" << option + << " maps to unknown setting!" << LL_ENDL; + } +} +} // anonymous namespace + +void LLControlGroupCLP::configure(const std::string& config_filename, LLControlGroup* controlGroup) +{ + // This method reads the llsd based config file, and uses it to set + // members of a control group. + LLSD clpConfigLLSD; + + llifstream input_stream; + input_stream.open(config_filename.c_str(), std::ios::in | std::ios::binary); + + if(input_stream.is_open()) + { + LLSDSerialize::fromXML(clpConfigLLSD, input_stream); + for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap(); + option_itr != clpConfigLLSD.endMap(); + ++option_itr) + { + LLSD::String long_name = option_itr->first; + LLSD option_params = option_itr->second; + + std::string desc("n/a"); + if(option_params.has("desc")) + { + desc = option_params["desc"].asString(); + } + + std::string short_name = LLStringUtil::null; + if(option_params.has("short")) + { + short_name = option_params["short"].asString(); + } + + unsigned int token_count = 0; + if(option_params.has("count")) + { + token_count = option_params["count"].asInteger(); + } + + bool composing = false; + if(option_params.has("compose")) + { + composing = option_params["compose"].asBoolean(); + } + + bool positional = false; + if(option_params.has("positional")) + { + positional = option_params["positional"].asBoolean(); + } + + bool last_option = false; + if(option_params.has("last_option")) + { + last_option = option_params["last_option"].asBoolean(); + } + + boost::function1 callback; + if (! option_params.has("map-to")) + { + // If this option isn't mapped to a settings variable, is it + // one of the ones for which that's unreasonable, or did + // someone carelessly add a new option? (Make all these + // configuration errors fatal so a maintainer will catch them + // right away.) + std::set::const_iterator found = unmapped_options.find(long_name); + if (found == unmapped_options.end()) + { + LL_ERRS() << "New command-line option " << long_name + << " should map-to a variable in settings.xml" << LL_ENDL; + } + } + else // option specifies map-to + { + std::string controlName = option_params["map-to"].asString(); + if (! controlGroup) + { + LL_ERRS() << "Must pass gSavedSettings to LLControlGroupCLP::configure() for " + << long_name << " (map-to " << controlName << ")" << LL_ENDL; + } + + LLControlVariable* ctrl = controlGroup->getControl(controlName); + if (! ctrl) + { + LL_ERRS() << "Option " << long_name << " specifies map-to " << controlName + << " which does not exist" << LL_ENDL; + } + + callback = boost::bind(setControlValueCB, _1, long_name, ctrl); + } + + this->addOptionDesc( + long_name, + callback, + token_count, + desc, + short_name, + composing, + positional, + last_option); + } + } +} -- cgit v1.2.3 From b42f9d836b4c0f7fbd4bdae1734021e2a09fdbe8 Mon Sep 17 00:00:00 2001 From: Ansariel Date: Sat, 1 Jun 2024 15:49:26 +0200 Subject: Re-enable a lot of compiler warnings for MSVC and address the C4267 "possible loss of precision" warnings --- indra/newview/llcommandlineparser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/newview/llcommandlineparser.cpp') diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp index 5b8d8b5480..f301af9511 100644 --- a/indra/newview/llcommandlineparser.cpp +++ b/indra/newview/llcommandlineparser.cpp @@ -378,8 +378,8 @@ bool LLCommandLineParser::parseCommandLineString(const std::string& str) if (!str.empty()) { bool add_last_c = true; - S32 last_c_pos = str.size() - 1; //don't get out of bounds on pos+1, last char will be processed separately - for (S32 pos = 0; pos < last_c_pos; ++pos) + auto last_c_pos = str.size() - 1; //don't get out of bounds on pos+1, last char will be processed separately + for (size_t pos = 0; pos < last_c_pos; ++pos) { cmd_line_string.append(&str[pos], 1); if (str[pos] == '\\') -- cgit v1.2.3