summaryrefslogtreecommitdiff
path: root/indra/newview/llcommandlineparser.cpp
diff options
context:
space:
mode:
authorMark Palange <palange@lindenlab.com>2008-03-14 23:21:38 +0000
committerMark Palange <palange@lindenlab.com>2008-03-14 23:21:38 +0000
commit04611efae8a3291ceba8a29dd920bdae0d404830 (patch)
tree43966566a1eeb42cf546a638310348f0585fc395 /indra/newview/llcommandlineparser.cpp
parentc0c5bdbbb90e0bcdab558ec22ea352c9d08dc078 (diff)
[NOTE: This was an erroneous commit, and was reverted in the next revision]
QAR-369 - viewer-cleanup2-7 81916 merged into release.
Diffstat (limited to 'indra/newview/llcommandlineparser.cpp')
-rw-r--r--indra/newview/llcommandlineparser.cpp535
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);
+ }
+ }
+}