PageRenderTime 30ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/newview/llcommandlineparser.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 573 lines | 470 code | 46 blank | 57 comment | 19 complexity | 7b571d9a7c98b6d3413123b090f0151c MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llcommandlineparser.cpp
  3. * @brief The LLCommandLineParser class definitions
  4. *
  5. * $LicenseInfo:firstyear=2007&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "llviewerprecompiledheaders.h"
  27. #include "llcommandlineparser.h"
  28. // *NOTE: The boost::lexical_cast generates
  29. // the warning C4701(local used with out assignment) in VC7.1.
  30. // Disable the warning for the boost includes.
  31. #if _MSC_VER
  32. # pragma warning(push)
  33. # pragma warning( disable : 4701 )
  34. #else
  35. // NOTE: For the other platforms?
  36. #endif
  37. #include <boost/program_options.hpp>
  38. #include <boost/bind.hpp>
  39. #include<boost/tokenizer.hpp>
  40. #if _MSC_VER
  41. # pragma warning(pop)
  42. #endif
  43. #include "llsdserialize.h"
  44. #include <iostream>
  45. #include <sstream>
  46. #include "llcontrol.h"
  47. namespace po = boost::program_options;
  48. // *NOTE:MEP - Currently the boost object reside in file scope.
  49. // This has a couple of negatives, they are always around and
  50. // there can be only one instance of each.
  51. // The plus is that the boost-ly-ness of this implementation is
  52. // hidden from the rest of the world.
  53. // Its importatnt to realize that multiple LLCommandLineParser objects
  54. // will all have this single repository of option escs and parsed options.
  55. // This could be good or bad, and probably won't matter for most use cases.
  56. namespace
  57. {
  58. po::options_description gOptionsDesc;
  59. po::positional_options_description gPositionalOptions;
  60. po::variables_map gVariableMap;
  61. const LLCommandLineParser::token_vector_t gEmptyValue;
  62. void read_file_into_string(std::string& str, const std::basic_istream < char >& file)
  63. {
  64. std::ostringstream oss;
  65. oss << file.rdbuf();
  66. str = oss.str();
  67. }
  68. bool gPastLastOption = false;
  69. }
  70. class LLCLPError : public std::logic_error {
  71. public:
  72. LLCLPError(const std::string& what) : std::logic_error(what) {}
  73. };
  74. class LLCLPLastOption : public std::logic_error {
  75. public:
  76. LLCLPLastOption(const std::string& what) : std::logic_error(what) {}
  77. };
  78. class LLCLPValue : public po::value_semantic_codecvt_helper<char>
  79. {
  80. unsigned mMinTokens;
  81. unsigned mMaxTokens;
  82. bool mIsComposing;
  83. typedef boost::function1<void, const LLCommandLineParser::token_vector_t&> notify_callback_t;
  84. notify_callback_t mNotifyCallback;
  85. bool mLastOption;
  86. public:
  87. LLCLPValue() :
  88. mMinTokens(0),
  89. mMaxTokens(0),
  90. mIsComposing(false),
  91. mLastOption(false)
  92. {}
  93. virtual ~LLCLPValue() {};
  94. void setMinTokens(unsigned c)
  95. {
  96. mMinTokens = c;
  97. }
  98. void setMaxTokens(unsigned c)
  99. {
  100. mMaxTokens = c;
  101. }
  102. void setComposing(bool c)
  103. {
  104. mIsComposing = c;
  105. }
  106. void setLastOption(bool c)
  107. {
  108. mLastOption = c;
  109. }
  110. void setNotifyCallback(notify_callback_t f)
  111. {
  112. mNotifyCallback = f;
  113. }
  114. // Overrides to support the value_semantic interface.
  115. virtual std::string name() const
  116. {
  117. const std::string arg("arg");
  118. const std::string args("args");
  119. return (max_tokens() > 1) ? args : arg;
  120. }
  121. virtual unsigned min_tokens() const
  122. {
  123. return mMinTokens;
  124. }
  125. virtual unsigned max_tokens() const
  126. {
  127. return mMaxTokens;
  128. }
  129. virtual bool is_composing() const
  130. {
  131. return mIsComposing;
  132. }
  133. // Needed for boost 1.42
  134. virtual bool is_required() const
  135. {
  136. return false; // All our command line options are optional.
  137. }
  138. virtual bool apply_default(boost::any& value_store) const
  139. {
  140. return false; // No defaults.
  141. }
  142. virtual void notify(const boost::any& value_store) const
  143. {
  144. const LLCommandLineParser::token_vector_t* value =
  145. boost::any_cast<const LLCommandLineParser::token_vector_t>(&value_store);
  146. if(mNotifyCallback)
  147. {
  148. mNotifyCallback(*value);
  149. }
  150. }
  151. protected:
  152. void xparse(boost::any& value_store,
  153. const std::vector<std::string>& new_tokens) const
  154. {
  155. if(gPastLastOption)
  156. {
  157. throw(LLCLPLastOption("Don't parse no more!"));
  158. }
  159. // Error checks. Needed?
  160. if (!value_store.empty() && !is_composing())
  161. {
  162. throw(LLCLPError("Non composing value with multiple occurences."));
  163. }
  164. if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens())
  165. {
  166. throw(LLCLPError("Illegal number of tokens specified."));
  167. }
  168. if(value_store.empty())
  169. {
  170. value_store = boost::any(LLCommandLineParser::token_vector_t());
  171. }
  172. LLCommandLineParser::token_vector_t* tv =
  173. boost::any_cast<LLCommandLineParser::token_vector_t>(&value_store);
  174. for(unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i)
  175. {
  176. tv->push_back(new_tokens[i]);
  177. }
  178. if(mLastOption)
  179. {
  180. gPastLastOption = true;
  181. }
  182. }
  183. };
  184. //----------------------------------------------------------------------------
  185. // LLCommandLineParser defintions
  186. //----------------------------------------------------------------------------
  187. void LLCommandLineParser::addOptionDesc(const std::string& option_name,
  188. boost::function1<void, const token_vector_t&> notify_callback,
  189. unsigned int token_count,
  190. const std::string& description,
  191. const std::string& short_name,
  192. bool composing,
  193. bool positional,
  194. bool last_option)
  195. {
  196. // Compose the name for boost::po.
  197. // It takes the format "long_name, short name"
  198. const std::string comma(",");
  199. std::string boost_option_name = option_name;
  200. if(short_name != LLStringUtil::null)
  201. {
  202. boost_option_name += comma;
  203. boost_option_name += short_name;
  204. }
  205. LLCLPValue* value_desc = new LLCLPValue();
  206. value_desc->setMinTokens(token_count);
  207. value_desc->setMaxTokens(token_count);
  208. value_desc->setComposing(composing);
  209. value_desc->setLastOption(last_option);
  210. boost::shared_ptr<po::option_description> d(
  211. new po::option_description(boost_option_name.c_str(),
  212. value_desc,
  213. description.c_str()));
  214. if(!notify_callback.empty())
  215. {
  216. value_desc->setNotifyCallback(notify_callback);
  217. }
  218. gOptionsDesc.add(d);
  219. if(positional)
  220. {
  221. gPositionalOptions.add(boost_option_name.c_str(), token_count);
  222. }
  223. }
  224. bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp)
  225. {
  226. try
  227. {
  228. clp.options(gOptionsDesc);
  229. clp.positional(gPositionalOptions);
  230. // SNOW-626: Boost 1.42 erroneously added allow_guessing to the default style
  231. // (see http://groups.google.com/group/boost-list/browse_thread/thread/545d7bf98ff9bb16?fwc=2&pli=1)
  232. // Remove allow_guessing from the default style, because that is not allowed
  233. // when we have options that are a prefix of other options (aka, --help and --helperuri).
  234. clp.style((po::command_line_style::default_style & ~po::command_line_style::allow_guessing)
  235. | po::command_line_style::allow_long_disguise);
  236. if(mExtraParser)
  237. {
  238. clp.extra_parser(mExtraParser);
  239. }
  240. po::basic_parsed_options<char> opts = clp.run();
  241. po::store(opts, gVariableMap);
  242. }
  243. catch(po::error& e)
  244. {
  245. llwarns << "Caught Error:" << e.what() << llendl;
  246. mErrorMsg = e.what();
  247. return false;
  248. }
  249. catch(LLCLPError& e)
  250. {
  251. llwarns << "Caught Error:" << e.what() << llendl;
  252. mErrorMsg = e.what();
  253. return false;
  254. }
  255. catch(LLCLPLastOption&)
  256. {
  257. // This exception means a token was read after an option
  258. // that must be the last option was reached (see url and slurl options)
  259. // boost::po will have stored a malformed option.
  260. // All such options will be removed below.
  261. // The last option read, the last_option option, and its value
  262. // are put into the error message.
  263. std::string last_option;
  264. std::string last_value;
  265. for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();)
  266. {
  267. po::variables_map::iterator tempI = i++;
  268. if(tempI->second.empty())
  269. {
  270. gVariableMap.erase(tempI);
  271. }
  272. else
  273. {
  274. last_option = tempI->first;
  275. LLCommandLineParser::token_vector_t* tv =
  276. boost::any_cast<LLCommandLineParser::token_vector_t>(&(tempI->second.value()));
  277. if(!tv->empty())
  278. {
  279. last_value = (*tv)[tv->size()-1];
  280. }
  281. }
  282. }
  283. // Continue without parsing.
  284. std::ostringstream msg;
  285. msg << "Caught Error: Found options after last option: "
  286. << last_option << " "
  287. << last_value;
  288. llwarns << msg.str() << llendl;
  289. mErrorMsg = msg.str();
  290. return false;
  291. }
  292. return true;
  293. }
  294. bool LLCommandLineParser::parseCommandLine(int argc, char **argv)
  295. {
  296. po::command_line_parser clp(argc, argv);
  297. return parseAndStoreResults(clp);
  298. }
  299. bool LLCommandLineParser::parseCommandLineString(const std::string& str)
  300. {
  301. // Split the string content into tokens
  302. const char* escape_chars = "\\";
  303. const char* separator_chars = "\r\n ";
  304. const char* quote_chars = "\"'";
  305. boost::escaped_list_separator<char> sep(escape_chars, separator_chars, quote_chars);
  306. boost::tokenizer< boost::escaped_list_separator<char> > tok(str, sep);
  307. std::vector<std::string> tokens;
  308. // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
  309. for(boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin();
  310. i != tok.end();
  311. ++i)
  312. {
  313. if(0 != i->size())
  314. {
  315. tokens.push_back(*i);
  316. }
  317. }
  318. po::command_line_parser clp(tokens);
  319. return parseAndStoreResults(clp);
  320. }
  321. bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream < char >& file)
  322. {
  323. std::string args;
  324. read_file_into_string(args, file);
  325. return parseCommandLineString(args);
  326. }
  327. void LLCommandLineParser::notify()
  328. {
  329. po::notify(gVariableMap);
  330. }
  331. void LLCommandLineParser::printOptions() const
  332. {
  333. for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end(); ++i)
  334. {
  335. std::string name = i->first;
  336. token_vector_t values = i->second.as<token_vector_t>();
  337. std::ostringstream oss;
  338. oss << name << ": ";
  339. for(token_vector_t::iterator t_itr = values.begin(); t_itr != values.end(); ++t_itr)
  340. {
  341. oss << t_itr->c_str() << " ";
  342. }
  343. llinfos << oss.str() << llendl;
  344. }
  345. }
  346. std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const
  347. {
  348. return os << gOptionsDesc;
  349. }
  350. bool LLCommandLineParser::hasOption(const std::string& name) const
  351. {
  352. return gVariableMap.count(name) > 0;
  353. }
  354. const LLCommandLineParser::token_vector_t& LLCommandLineParser::getOption(const std::string& name) const
  355. {
  356. if(hasOption(name))
  357. {
  358. return gVariableMap[name].as<token_vector_t>();
  359. }
  360. return gEmptyValue;
  361. }
  362. //----------------------------------------------------------------------------
  363. // LLControlGroupCLP defintions
  364. //----------------------------------------------------------------------------
  365. void setControlValueCB(const LLCommandLineParser::token_vector_t& value,
  366. const std::string& opt_name,
  367. LLControlGroup* ctrlGroup)
  368. {
  369. // *FIX: Do sematic conversion here.
  370. // LLSD (ImplString) Is no good for doing string to type conversion for...
  371. // booleans
  372. // compound types
  373. // ?...
  374. LLControlVariable* ctrl = ctrlGroup->getControl(opt_name);
  375. if(NULL != ctrl)
  376. {
  377. switch(ctrl->type())
  378. {
  379. case TYPE_BOOLEAN:
  380. if(value.size() > 1)
  381. {
  382. llwarns << "Ignoring extra tokens." << llendl;
  383. }
  384. if(value.size() > 0)
  385. {
  386. // There's a token. check the string for true/false/1/0 etc.
  387. BOOL result = false;
  388. BOOL gotSet = LLStringUtil::convertToBOOL(value[0], result);
  389. if(gotSet)
  390. {
  391. ctrl->setValue(LLSD(result), false);
  392. }
  393. }
  394. else
  395. {
  396. ctrl->setValue(LLSD(true), false);
  397. }
  398. break;
  399. default:
  400. {
  401. // For the default types, let llsd do the conversion.
  402. if(value.size() > 1 && ctrl->isType(TYPE_LLSD))
  403. {
  404. // Assume its an array...
  405. LLSD llsdArray;
  406. for(unsigned int i = 0; i < value.size(); ++i)
  407. {
  408. LLSD llsdValue;
  409. llsdValue.assign(LLSD::String(value[i]));
  410. llsdArray.set(i, llsdValue);
  411. }
  412. ctrl->setValue(llsdArray, false);
  413. }
  414. else if(value.size() > 0)
  415. {
  416. if(value.size() > 1)
  417. {
  418. llwarns << "Ignoring extra tokens mapped to the setting: " << opt_name << "." << llendl;
  419. }
  420. LLSD llsdValue;
  421. llsdValue.assign(LLSD::String(value[0]));
  422. ctrl->setValue(llsdValue, false);
  423. }
  424. }
  425. break;
  426. }
  427. }
  428. else
  429. {
  430. llwarns << "Command Line option mapping '"
  431. << opt_name
  432. << "' not found! Ignoring."
  433. << llendl;
  434. }
  435. }
  436. void LLControlGroupCLP::configure(const std::string& config_filename, LLControlGroup* controlGroup)
  437. {
  438. // This method reads the llsd based config file, and uses it to set
  439. // members of a control group.
  440. LLSD clpConfigLLSD;
  441. llifstream input_stream;
  442. input_stream.open(config_filename, std::ios::in | std::ios::binary);
  443. if(input_stream.is_open())
  444. {
  445. LLSDSerialize::fromXML(clpConfigLLSD, input_stream);
  446. for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap();
  447. option_itr != clpConfigLLSD.endMap();
  448. ++option_itr)
  449. {
  450. LLSD::String long_name = option_itr->first;
  451. LLSD option_params = option_itr->second;
  452. std::string desc("n/a");
  453. if(option_params.has("desc"))
  454. {
  455. desc = option_params["desc"].asString();
  456. }
  457. std::string short_name = LLStringUtil::null;
  458. if(option_params.has("short"))
  459. {
  460. short_name = option_params["short"].asString();
  461. }
  462. unsigned int token_count = 0;
  463. if(option_params.has("count"))
  464. {
  465. token_count = option_params["count"].asInteger();
  466. }
  467. bool composing = false;
  468. if(option_params.has("compose"))
  469. {
  470. composing = option_params["compose"].asBoolean();
  471. }
  472. bool positional = false;
  473. if(option_params.has("positional"))
  474. {
  475. positional = option_params["positional"].asBoolean();
  476. }
  477. bool last_option = false;
  478. if(option_params.has("last_option"))
  479. {
  480. last_option = option_params["last_option"].asBoolean();
  481. }
  482. boost::function1<void, const token_vector_t&> callback;
  483. if(option_params.has("map-to") && (NULL != controlGroup))
  484. {
  485. std::string controlName = option_params["map-to"].asString();
  486. callback = boost::bind(setControlValueCB, _1,
  487. controlName, controlGroup);
  488. }
  489. this->addOptionDesc(
  490. long_name,
  491. callback,
  492. token_count,
  493. desc,
  494. short_name,
  495. composing,
  496. positional,
  497. last_option);
  498. }
  499. }
  500. }