diff options
Diffstat (limited to 'indra/newview/llcommandlineparser.cpp')
-rw-r--r-- | indra/newview/llcommandlineparser.cpp | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp new file mode 100644 index 0000000000..2f99ca1247 --- /dev/null +++ b/indra/newview/llcommandlineparser.cpp @@ -0,0 +1,535 @@ +/** + * @file llcommandlineparser.cpp + * @brief The LLCommandLineParser class definitions + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" +#include "llcommandlineparser.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 <boost/program_options.hpp> +#include <boost/bind.hpp> +#include<boost/tokenizer.hpp> + +#if _MSC_VER +# pragma warning(pop) +#endif + +#include "llsdserialize.h" +#include <iostream> +#include <sstream> + +#include "llcontrol.h" + +namespace po = boost::program_options; + +// *NTOE: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 +{ + 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 std::logic_error { +public: + LLCLPError(const std::string& what) : std::logic_error(what) {} +}; + +class LLCLPLastOption : public std::logic_error { +public: + LLCLPLastOption(const std::string& what) : std::logic_error(what) {} +}; + +class LLCLPValue : public po::value_semantic_codecvt_helper<char> +{ + unsigned mMinTokens; + unsigned mMaxTokens; + bool mIsComposing; + typedef boost::function1<void, const LLCommandLineParser::token_vector_t&> 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; + } + + 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<const LLCommandLineParser::token_vector_t>(&value_store); + if(mNotifyCallback) + { + mNotifyCallback(*value); + } + + } + +protected: + void xparse(boost::any& value_store, + const std::vector<std::string>& new_tokens) const + { + if(gPastLastOption) + { + throw(LLCLPLastOption("Don't parse no more!")); + } + + // Error checks. Needed? + if (!value_store.empty() && !is_composing()) + { + throw(LLCLPError("Non composing value with multiple occurences.")); + } + if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens()) + { + throw(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<LLCommandLineParser::token_vector_t>(&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 LLString& option_name, + boost::function1<void, const token_vector_t&> notify_callback, + unsigned int token_count, + const LLString& description, + const LLString& short_name, + bool composing, + bool positional, + bool last_option) +{ + // Compose the name for boost::po. + // It takes the format "long_name, short name" + const LLString comma(","); + LLString boost_option_name = option_name; + if(short_name != LLString::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<po::option_description> 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 parseAndStoreResults(po::command_line_parser& clp) +{ + try + { + clp.options(gOptionsDesc); + clp.positional(gPositionalOptions); + clp.style(po::command_line_style::default_style + | po::command_line_style::allow_long_disguise); + po::basic_parsed_options<char> opts = clp.run(); + po::store(opts, gVariableMap); + } + catch(po::error& e) + { + llwarns << "Caught Error:" << e.what() << llendl; + return false; + } + catch(LLCLPError& e) + { + llwarns << "Caught Error:" << e.what() << llendl; + return false; + } + catch(LLCLPLastOption&) + { + // Continue without parsing. + llwarns << "Found tokens past last option. Ignoring." << llendl; + + // boost::po will have stored a mal-formed option. + // All such options will be removed below. + for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();) + { + po::variables_map::iterator tempI = i++; + if(tempI->second.empty()) + { + gVariableMap.erase(tempI); + } + } + } + return true; +} + +bool LLCommandLineParser::parseCommandLine(int argc, char **argv) +{ + po::command_line_parser clp(argc, argv); + return parseAndStoreResults(clp); +} + +bool LLCommandLineParser::parseCommandLineString(const std::string& str) +{ + // Split the string content into tokens + boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'"); + boost::tokenizer< boost::escaped_list_separator<char> > tok(str, sep); + std::vector<std::string> tokens; + // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens)); + for(boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin(); + i != tok.end(); + ++i) + { + if(0 != i->size()) + { + tokens.push_back(*i); + } + } + + 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); +} + +void LLCommandLineParser::notify() +{ + po::notify(gVariableMap); +} + +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<token_vector_t>(); + 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() << " "; + } + llinfos << oss.str() << llendl; + } +} + +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<token_vector_t>(); + } + + return gEmptyValue; +} + +//---------------------------------------------------------------------------- +// LLControlGroupCLP defintions +//---------------------------------------------------------------------------- +void setControlValueCB(const LLCommandLineParser::token_vector_t& value, + const LLString& opt_name, + LLControlGroup* ctrlGroup) +{ + if(value.size() > 1) + { + llwarns << "Ignoring extra tokens mapped to the setting: " << opt_name << "." << llendl; + } + + // *FIX: Do sematic conversion here. + // LLSD (ImplString) Is no good for doing string to type conversion for... + // booleans + // compound types + // ?... + + LLControlVariable* ctrl = ctrlGroup->getControl(opt_name); + if(NULL != ctrl) + { + switch(ctrl->type()) + { + case TYPE_BOOLEAN: + if(value.size() > 1) + { + llwarns << "Ignoring extra tokens." << llendl; + } + + if(value.size() > 0) + { + // There's a token. check the string for true/false/1/0 etc. + BOOL result = false; + BOOL gotSet = LLString::convertToBOOL(value[0], result); + if(gotSet) + { + ctrl->setValue(LLSD(result), false); + } + } + else + { + ctrl->setValue(LLSD(true), false); + } + break; + + default: + { + // For the default types, let llsd do the conversion. + if(value.size() > 1) + { + // 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 if(value.size() > 0) + { + LLSD llsdValue; + llsdValue.assign(LLSD::String(value[0])); + ctrl->setValue(llsdValue, false); + } + } + break; + } + } + else + { + llwarns << "Command Line option mapping '" + << opt_name + << "' not found! Ignoring." + << llendl; + } +} + +void LLControlGroupCLP::configure(const LLString& 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; + + LLString desc("n/a"); + if(option_params.has("desc")) + { + desc = option_params["desc"].asString(); + } + + LLString short_name = LLString::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<void, const token_vector_t&> callback; + if(option_params.has("map-to") && (NULL != controlGroup)) + { + LLString controlName = option_params["map-to"].asString(); + callback = boost::bind(setControlValueCB, _1, + controlName, controlGroup); + } + + this->addOptionDesc( + long_name, + callback, + token_count, + desc, + short_name, + composing, + positional, + last_option); + } + } +} |