00001
00032 #include "llviewerprecompiledheaders.h"
00033 #include "llcommandlineparser.h"
00034
00035
00036
00037
00038 #if _MSC_VER
00039 # pragma warning(push)
00040 # pragma warning( disable : 4701 )
00041 #else
00042
00043 #endif
00044
00045 #include <boost/program_options.hpp>
00046 #include <boost/bind.hpp>
00047 #include<boost/tokenizer.hpp>
00048
00049 #if _MSC_VER
00050 # pragma warning(pop)
00051 #endif
00052
00053 #include "llsdserialize.h"
00054 #include <iostream>
00055 #include <sstream>
00056
00057 #include "llcontrol.h"
00058
00059 namespace po = boost::program_options;
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069 namespace
00070 {
00071 po::options_description gOptionsDesc;
00072 po::positional_options_description gPositionalOptions;
00073 po::variables_map gVariableMap;
00074
00075 const LLCommandLineParser::token_vector_t gEmptyValue;
00076
00077 void read_file_into_string(std::string& str, const std::basic_istream < char >& file)
00078 {
00079 std::ostringstream oss;
00080 oss << file.rdbuf();
00081 str = oss.str();
00082 }
00083
00084 bool gPastLastOption = false;
00085 }
00086
00087 class LLCLPError : public std::logic_error {
00088 public:
00089 LLCLPError(const std::string& what) : std::logic_error(what) {}
00090 };
00091
00092 class LLCLPLastOption : public std::logic_error {
00093 public:
00094 LLCLPLastOption(const std::string& what) : std::logic_error(what) {}
00095 };
00096
00097 class LLCLPValue : public po::value_semantic_codecvt_helper<char>
00098 {
00099 unsigned mMinTokens;
00100 unsigned mMaxTokens;
00101 bool mIsComposing;
00102 typedef boost::function1<void, const LLCommandLineParser::token_vector_t&> notify_callback_t;
00103 notify_callback_t mNotifyCallback;
00104 bool mLastOption;
00105
00106 public:
00107 LLCLPValue() :
00108 mMinTokens(0),
00109 mMaxTokens(0),
00110 mIsComposing(false),
00111 mLastOption(false)
00112 {}
00113
00114 virtual ~LLCLPValue() {};
00115
00116 void setMinTokens(unsigned c)
00117 {
00118 mMinTokens = c;
00119 }
00120
00121 void setMaxTokens(unsigned c)
00122 {
00123 mMaxTokens = c;
00124 }
00125
00126 void setComposing(bool c)
00127 {
00128 mIsComposing = c;
00129 }
00130
00131 void setLastOption(bool c)
00132 {
00133 mLastOption = c;
00134 }
00135
00136 void setNotifyCallback(notify_callback_t f)
00137 {
00138 mNotifyCallback = f;
00139 }
00140
00141
00142 virtual std::string name() const
00143 {
00144 const std::string arg("arg");
00145 const std::string args("args");
00146 return (max_tokens() > 1) ? args : arg;
00147 }
00148
00149 virtual unsigned min_tokens() const
00150 {
00151 return mMinTokens;
00152 }
00153
00154 virtual unsigned max_tokens() const
00155 {
00156 return mMaxTokens;
00157 }
00158
00159 virtual bool is_composing() const
00160 {
00161 return mIsComposing;
00162 }
00163
00164 virtual bool apply_default(boost::any& value_store) const
00165 {
00166 return false;
00167 }
00168
00169 virtual void notify(const boost::any& value_store) const
00170 {
00171 const LLCommandLineParser::token_vector_t* value =
00172 boost::any_cast<const LLCommandLineParser::token_vector_t>(&value_store);
00173 if(mNotifyCallback)
00174 {
00175 mNotifyCallback(*value);
00176 }
00177
00178 }
00179
00180 protected:
00181 void xparse(boost::any& value_store,
00182 const std::vector<std::string>& new_tokens) const
00183 {
00184 if(gPastLastOption)
00185 {
00186 throw(LLCLPLastOption("Don't parse no more!"));
00187 }
00188
00189
00190 if (!value_store.empty() && !is_composing())
00191 {
00192 throw(LLCLPError("Non composing value with multiple occurences."));
00193 }
00194 if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens())
00195 {
00196 throw(LLCLPError("Illegal number of tokens specified."));
00197 }
00198
00199 if(value_store.empty())
00200 {
00201 value_store = boost::any(LLCommandLineParser::token_vector_t());
00202 }
00203 LLCommandLineParser::token_vector_t* tv =
00204 boost::any_cast<LLCommandLineParser::token_vector_t>(&value_store);
00205
00206 for(unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i)
00207 {
00208 tv->push_back(new_tokens[i]);
00209 }
00210
00211 if(mLastOption)
00212 {
00213 gPastLastOption = true;
00214 }
00215 }
00216 };
00217
00218
00219
00220
00221 void LLCommandLineParser::addOptionDesc(const LLString& option_name,
00222 boost::function1<void, const token_vector_t&> notify_callback,
00223 unsigned int token_count,
00224 const LLString& description,
00225 const LLString& short_name,
00226 bool composing,
00227 bool positional,
00228 bool last_option)
00229 {
00230
00231
00232 const LLString comma(",");
00233 LLString boost_option_name = option_name;
00234 if(short_name != LLString::null)
00235 {
00236 boost_option_name += comma;
00237 boost_option_name += short_name;
00238 }
00239
00240 LLCLPValue* value_desc = new LLCLPValue();
00241 value_desc->setMinTokens(token_count);
00242 value_desc->setMaxTokens(token_count);
00243 value_desc->setComposing(composing);
00244 value_desc->setLastOption(last_option);
00245
00246 boost::shared_ptr<po::option_description> d(
00247 new po::option_description(boost_option_name.c_str(),
00248 value_desc,
00249 description.c_str()));
00250
00251 if(!notify_callback.empty())
00252 {
00253 value_desc->setNotifyCallback(notify_callback);
00254 }
00255
00256 gOptionsDesc.add(d);
00257
00258 if(positional)
00259 {
00260 gPositionalOptions.add(boost_option_name.c_str(), token_count);
00261 }
00262 }
00263
00264 bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp)
00265 {
00266 try
00267 {
00268 clp.options(gOptionsDesc);
00269 clp.positional(gPositionalOptions);
00270 clp.style(po::command_line_style::default_style
00271 | po::command_line_style::allow_long_disguise);
00272 if(mExtraParser)
00273 {
00274 clp.extra_parser(mExtraParser);
00275 }
00276
00277 po::basic_parsed_options<char> opts = clp.run();
00278 po::store(opts, gVariableMap);
00279 }
00280 catch(po::error& e)
00281 {
00282 llwarns << "Caught Error:" << e.what() << llendl;
00283 mErrorMsg = e.what();
00284 return false;
00285 }
00286 catch(LLCLPError& e)
00287 {
00288 llwarns << "Caught Error:" << e.what() << llendl;
00289 mErrorMsg = e.what();
00290 return false;
00291 }
00292 catch(LLCLPLastOption&)
00293 {
00294
00295 std::string msg = "Found tokens past last option. Ignoring.";
00296 llwarns << msg << llendl;
00297 mErrorMsg = msg;
00298
00299
00300 for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();)
00301 {
00302 po::variables_map::iterator tempI = i++;
00303 if(tempI->second.empty())
00304 {
00305 gVariableMap.erase(tempI);
00306 }
00307 }
00308 }
00309 return true;
00310 }
00311
00312 bool LLCommandLineParser::parseCommandLine(int argc, char **argv)
00313 {
00314 po::command_line_parser clp(argc, argv);
00315 return parseAndStoreResults(clp);
00316 }
00317
00318 bool LLCommandLineParser::parseCommandLineString(const std::string& str)
00319 {
00320
00321 boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'");
00322 boost::tokenizer< boost::escaped_list_separator<char> > tok(str, sep);
00323 std::vector<std::string> tokens;
00324
00325 for(boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin();
00326 i != tok.end();
00327 ++i)
00328 {
00329 if(0 != i->size())
00330 {
00331 tokens.push_back(*i);
00332 }
00333 }
00334
00335 po::command_line_parser clp(tokens);
00336 return parseAndStoreResults(clp);
00337
00338 }
00339
00340 bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream < char >& file)
00341 {
00342 std::string args;
00343 read_file_into_string(args, file);
00344
00345 return parseCommandLineString(args);
00346 }
00347
00348 void LLCommandLineParser::notify()
00349 {
00350 po::notify(gVariableMap);
00351 }
00352
00353 void LLCommandLineParser::printOptions() const
00354 {
00355 for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end(); ++i)
00356 {
00357 std::string name = i->first;
00358 token_vector_t values = i->second.as<token_vector_t>();
00359 std::ostringstream oss;
00360 oss << name << ": ";
00361 for(token_vector_t::iterator t_itr = values.begin(); t_itr != values.end(); ++t_itr)
00362 {
00363 oss << t_itr->c_str() << " ";
00364 }
00365 llinfos << oss.str() << llendl;
00366 }
00367 }
00368
00369 std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const
00370 {
00371 return os << gOptionsDesc;
00372 }
00373
00374 bool LLCommandLineParser::hasOption(const std::string& name) const
00375 {
00376 return gVariableMap.count(name) > 0;
00377 }
00378
00379 const LLCommandLineParser::token_vector_t& LLCommandLineParser::getOption(const std::string& name) const
00380 {
00381 if(hasOption(name))
00382 {
00383 return gVariableMap[name].as<token_vector_t>();
00384 }
00385
00386 return gEmptyValue;
00387 }
00388
00389
00390
00391
00392 void setControlValueCB(const LLCommandLineParser::token_vector_t& value,
00393 const LLString& opt_name,
00394 LLControlGroup* ctrlGroup)
00395 {
00396 if(value.size() > 1)
00397 {
00398 llwarns << "Ignoring extra tokens mapped to the setting: " << opt_name << "." << llendl;
00399 }
00400
00401
00402
00403
00404
00405
00406
00407 LLControlVariable* ctrl = ctrlGroup->getControl(opt_name);
00408 if(NULL != ctrl)
00409 {
00410 switch(ctrl->type())
00411 {
00412 case TYPE_BOOLEAN:
00413 if(value.size() > 1)
00414 {
00415 llwarns << "Ignoring extra tokens." << llendl;
00416 }
00417
00418 if(value.size() > 0)
00419 {
00420
00421 BOOL result = false;
00422 BOOL gotSet = LLString::convertToBOOL(value[0], result);
00423 if(gotSet)
00424 {
00425 ctrl->setValue(LLSD(result), false);
00426 }
00427 }
00428 else
00429 {
00430 ctrl->setValue(LLSD(true), false);
00431 }
00432 break;
00433
00434 default:
00435 {
00436
00437 if(value.size() > 1)
00438 {
00439
00440 LLSD llsdArray;
00441 for(unsigned int i = 0; i < value.size(); ++i)
00442 {
00443 LLSD llsdValue;
00444 llsdValue.assign(LLSD::String(value[i]));
00445 llsdArray.set(i, llsdValue);
00446 }
00447
00448 ctrl->setValue(llsdArray, false);
00449 }
00450 else if(value.size() > 0)
00451 {
00452 LLSD llsdValue;
00453 llsdValue.assign(LLSD::String(value[0]));
00454 ctrl->setValue(llsdValue, false);
00455 }
00456 }
00457 break;
00458 }
00459 }
00460 else
00461 {
00462 llwarns << "Command Line option mapping '"
00463 << opt_name
00464 << "' not found! Ignoring."
00465 << llendl;
00466 }
00467 }
00468
00469 void LLControlGroupCLP::configure(const LLString& config_filename, LLControlGroup* controlGroup)
00470 {
00471
00472
00473 LLSD clpConfigLLSD;
00474
00475 llifstream input_stream;
00476 input_stream.open(config_filename.c_str(), std::ios::in | std::ios::binary);
00477
00478 if(input_stream.is_open())
00479 {
00480 LLSDSerialize::fromXML(clpConfigLLSD, input_stream);
00481 for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap();
00482 option_itr != clpConfigLLSD.endMap();
00483 ++option_itr)
00484 {
00485 LLSD::String long_name = option_itr->first;
00486 LLSD option_params = option_itr->second;
00487
00488 LLString desc("n/a");
00489 if(option_params.has("desc"))
00490 {
00491 desc = option_params["desc"].asString();
00492 }
00493
00494 LLString short_name = LLString::null;
00495 if(option_params.has("short"))
00496 {
00497 short_name = option_params["short"].asString();
00498 }
00499
00500 unsigned int token_count = 0;
00501 if(option_params.has("count"))
00502 {
00503 token_count = option_params["count"].asInteger();
00504 }
00505
00506 bool composing = false;
00507 if(option_params.has("compose"))
00508 {
00509 composing = option_params["compose"].asBoolean();
00510 }
00511
00512 bool positional = false;
00513 if(option_params.has("positional"))
00514 {
00515 positional = option_params["positional"].asBoolean();
00516 }
00517
00518 bool last_option = false;
00519 if(option_params.has("last_option"))
00520 {
00521 last_option = option_params["last_option"].asBoolean();
00522 }
00523
00524 boost::function1<void, const token_vector_t&> callback;
00525 if(option_params.has("map-to") && (NULL != controlGroup))
00526 {
00527 LLString controlName = option_params["map-to"].asString();
00528 callback = boost::bind(setControlValueCB, _1,
00529 controlName, controlGroup);
00530 }
00531
00532 this->addOptionDesc(
00533 long_name,
00534 callback,
00535 token_count,
00536 desc,
00537 short_name,
00538 composing,
00539 positional,
00540 last_option);
00541 }
00542 }
00543 }