llcommandlineparser.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. /**
  2. * @file llcommandlineparser.cpp
  3. * @brief The LLCommandLineParser class definitions
  4. *
  5. * $LicenseInfo:firstyear=2007&license=viewergpl$
  6. *
  7. * Copyright (c) 2007-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include <iostream>
  34. #include <sstream>
  35. #include "boost/tokenizer.hpp"
  36. #include "llcommandlineparser.h"
  37. namespace po = boost::program_options;
  38. // Note: currently the boost object reside in file scope. This has a couple of
  39. // negative impacts: they are always around and there can be only one instance
  40. // of each. The plus is that the boost-ly-ness of this implementation is hidden
  41. // from the rest of the world. It is important to realize that multiple
  42. // LLCommandLineParser objects will all have this single repository of option
  43. // descriptions and parsed options. This could be good or bad, and probably
  44. // would not matter for most use cases.
  45. namespace
  46. {
  47. po::options_description gOptionsDesc;
  48. po::positional_options_description gPositionalOptions;
  49. po::variables_map gVariableMap;
  50. const LLCommandLineParser::token_vector_t gEmptyValue;
  51. void read_file_into_string(std::string& str,
  52. const std::basic_istream<char>& file)
  53. {
  54. std::ostringstream oss;
  55. oss << file.rdbuf();
  56. str = oss.str();
  57. }
  58. bool gPastLastOption = false;
  59. }
  60. class LLCLPError : public std::logic_error
  61. {
  62. public:
  63. LLCLPError(const std::string& what)
  64. : std::logic_error(what)
  65. {
  66. }
  67. };
  68. class LLCLPLastOption : public std::logic_error
  69. {
  70. public:
  71. LLCLPLastOption(const std::string& what)
  72. : std::logic_error(what)
  73. {
  74. }
  75. };
  76. class LLCLPValue : public po::value_semantic_codecvt_helper<char>
  77. {
  78. public:
  79. LLCLPValue()
  80. : mMinTokens(0),
  81. mMaxTokens(0),
  82. mIsComposing(false),
  83. mLastOption(false)
  84. {
  85. }
  86. virtual ~LLCLPValue()
  87. {
  88. }
  89. LL_INLINE void setMinTokens(unsigned c)
  90. {
  91. mMinTokens = c;
  92. }
  93. LL_INLINE void setMaxTokens(unsigned c)
  94. {
  95. mMaxTokens = c;
  96. }
  97. LL_INLINE void setComposing(bool c)
  98. {
  99. mIsComposing = c;
  100. }
  101. LL_INLINE void setLastOption(bool c)
  102. {
  103. mLastOption = c;
  104. }
  105. typedef boost::function1<void,
  106. const LLCommandLineParser::token_vector_t&> notify_callback_t;
  107. LL_INLINE void setNotifyCallback(notify_callback_t f)
  108. {
  109. mNotifyCallback = f;
  110. }
  111. // Overrides to support the value_semantic interface.
  112. virtual std::string name() const
  113. {
  114. const std::string arg("arg");
  115. const std::string args("args");
  116. return max_tokens() > 1 ? args : arg;
  117. }
  118. virtual unsigned min_tokens() const
  119. {
  120. return mMinTokens;
  121. }
  122. virtual unsigned max_tokens() const
  123. {
  124. return mMaxTokens;
  125. }
  126. virtual bool is_composing() const
  127. {
  128. return mIsComposing;
  129. }
  130. // Needed for boost >= 1.42
  131. virtual bool is_required() const
  132. {
  133. return false; // All our command line options are optional.
  134. }
  135. virtual bool apply_default(boost::any& value_store) const
  136. {
  137. return false; // No defaults.
  138. }
  139. // Needed for boost >= 1.59
  140. virtual bool adjacent_tokens_only() const
  141. {
  142. return false;
  143. }
  144. virtual void notify(const boost::any& value_store) const
  145. {
  146. const LLCommandLineParser::token_vector_t* value =
  147. boost::any_cast<const LLCommandLineParser::token_vector_t>(&value_store);
  148. if (mNotifyCallback)
  149. {
  150. mNotifyCallback(*value);
  151. }
  152. }
  153. protected:
  154. void xparse(boost::any& value_store,
  155. const std::vector<std::string>& new_tokens) const
  156. {
  157. if (gPastLastOption)
  158. {
  159. throw(LLCLPLastOption("Don't parse no more!"));
  160. }
  161. // Error checks. Needed?
  162. if (!value_store.empty() && !is_composing())
  163. {
  164. throw(LLCLPError("Non composing value with multiple occurences."));
  165. }
  166. if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens())
  167. {
  168. throw(LLCLPError("Illegal number of tokens specified."));
  169. }
  170. if (value_store.empty())
  171. {
  172. value_store = boost::any(LLCommandLineParser::token_vector_t());
  173. }
  174. LLCommandLineParser::token_vector_t* tv =
  175. boost::any_cast<LLCommandLineParser::token_vector_t>(&value_store);
  176. for (unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i)
  177. {
  178. tv->push_back(new_tokens[i]);
  179. }
  180. if (mLastOption)
  181. {
  182. gPastLastOption = true;
  183. }
  184. }
  185. protected:
  186. unsigned mMinTokens;
  187. unsigned mMaxTokens;
  188. notify_callback_t mNotifyCallback;
  189. bool mIsComposing;
  190. bool mLastOption;
  191. };
  192. //----------------------------------------------------------------------------
  193. // LLCommandLineParser defintions
  194. //----------------------------------------------------------------------------
  195. void LLCommandLineParser::addOptionDesc(const std::string& option_name,
  196. boost::function1<void, const token_vector_t&> notify_callback,
  197. unsigned int token_count,
  198. const std::string& description,
  199. const std::string& short_name,
  200. bool composing,
  201. bool positional,
  202. bool last_option)
  203. {
  204. // Compose the name for boost::po.
  205. // It takes the format "long_name, short name"
  206. const std::string comma(",");
  207. std::string boost_option_name = option_name;
  208. if (short_name != LLStringUtil::null)
  209. {
  210. boost_option_name += comma;
  211. boost_option_name += short_name;
  212. }
  213. LLCLPValue* value_desc = new LLCLPValue();
  214. value_desc->setMinTokens(token_count);
  215. value_desc->setMaxTokens(token_count);
  216. value_desc->setComposing(composing);
  217. value_desc->setLastOption(last_option);
  218. std::string desc = " : " + description;
  219. // Note: we cannot replace boost::shared_ptr with std::shared_ptr here,
  220. // because boost::program_options lacks the templates for it... HB
  221. boost::shared_ptr<po::option_description> d(new po::option_description(boost_option_name.c_str(),
  222. value_desc, desc.c_str()));
  223. if (!notify_callback.empty())
  224. {
  225. value_desc->setNotifyCallback(notify_callback);
  226. }
  227. gOptionsDesc.add(d);
  228. if (positional)
  229. {
  230. gPositionalOptions.add(boost_option_name.c_str(), token_count);
  231. }
  232. }
  233. bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp)
  234. {
  235. try
  236. {
  237. clp.options(gOptionsDesc);
  238. clp.positional(gPositionalOptions);
  239. // SNOW-626: Boost 1.42 erroneously added allow_guessing to the default
  240. // style (see:
  241. // http://groups.google.com/group/boost-list/browse_thread/thread/545d7bf98ff9bb16?fwc=2&pli=1)
  242. // Remove allow_guessing from the default style, because that is not
  243. // allowed when we have options that are a prefix of other options
  244. // (aka, --help and --helperuri).
  245. clp.style((po::command_line_style::default_style &
  246. ~po::command_line_style::allow_guessing) |
  247. po::command_line_style::allow_long_disguise);
  248. if (mExtraParser)
  249. {
  250. clp.extra_parser(mExtraParser);
  251. }
  252. po::basic_parsed_options<char> opts = clp.run();
  253. po::store(opts, gVariableMap);
  254. }
  255. catch (po::error& e)
  256. {
  257. llwarns << "Caught Error:" << e.what() << llendl;
  258. mErrorMsg = e.what();
  259. return false;
  260. }
  261. catch (LLCLPError& e)
  262. {
  263. llwarns << "Caught Error:" << e.what() << llendl;
  264. mErrorMsg = e.what();
  265. return false;
  266. }
  267. catch (LLCLPLastOption&)
  268. {
  269. // This exception means a token was read after an option that must be
  270. // the last option was reached (see url and slurl options)
  271. // boost::po will have stored a malformed option. All such options will
  272. // be removed below. The last option read, the last_option option, and
  273. // its value are put into the error message.
  274. std::string last_option;
  275. std::string last_value;
  276. for (po::variables_map::iterator i = gVariableMap.begin();
  277. i != gVariableMap.end(); )
  278. {
  279. po::variables_map::iterator tempI = i++;
  280. if (tempI->second.empty())
  281. {
  282. gVariableMap.erase(tempI);
  283. }
  284. else
  285. {
  286. last_option = tempI->first;
  287. LLCommandLineParser::token_vector_t* tv;
  288. tv = boost::any_cast<LLCommandLineParser::token_vector_t>(&(tempI->second.value()));
  289. if (tv && !tv->empty())
  290. {
  291. last_value = (*tv)[tv->size() - 1];
  292. }
  293. }
  294. }
  295. // Continue without parsing.
  296. std::ostringstream msg;
  297. msg << "Caught Error: Found options after last option: " << last_option
  298. << " " << last_value;
  299. llwarns << msg.str() << llendl;
  300. mErrorMsg = msg.str();
  301. return false;
  302. }
  303. return true;
  304. }
  305. bool LLCommandLineParser::parseCommandLine(int argc, char** argv)
  306. {
  307. po::command_line_parser clp(argc, argv);
  308. return parseAndStoreResults(clp);
  309. }
  310. bool LLCommandLineParser::parseCommandLineString(const std::string& str)
  311. {
  312. std::string cmd_line;
  313. if (!str.empty())
  314. {
  315. bool add_last_c = true;
  316. U32 last_c_pos = str.size() - 1;
  317. for (U32 pos = 0; pos < last_c_pos; ++pos)
  318. {
  319. cmd_line.append(&str[pos], 1);
  320. if (str[pos] == '\\')
  321. {
  322. cmd_line += "\\";
  323. if (str[pos + 1] == '\\')
  324. {
  325. add_last_c = pos++ != last_c_pos;
  326. }
  327. }
  328. }
  329. if (add_last_c)
  330. {
  331. cmd_line.append(&str[last_c_pos], 1);
  332. if (str[last_c_pos] == '\\')
  333. {
  334. cmd_line += "\\";
  335. }
  336. }
  337. }
  338. // Split the string content into tokens
  339. boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'");
  340. boost::tokenizer< boost::escaped_list_separator<char> > tok(cmd_line, sep);
  341. std::vector<std::string> tokens;
  342. // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
  343. for (boost::tokenizer<boost::escaped_list_separator<char> >::iterator
  344. i = tok.begin();
  345. i != tok.end(); ++i)
  346. {
  347. if (i->size())
  348. {
  349. tokens.push_back(*i);
  350. }
  351. }
  352. po::command_line_parser clp(tokens);
  353. return parseAndStoreResults(clp);
  354. }
  355. bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream<char>& file)
  356. {
  357. std::string args;
  358. read_file_into_string(args, file);
  359. return parseCommandLineString(args);
  360. }
  361. void LLCommandLineParser::notify()
  362. {
  363. po::notify(gVariableMap);
  364. }
  365. void LLCommandLineParser::printOptions() const
  366. {
  367. for (po::variables_map::iterator i = gVariableMap.begin();
  368. i != gVariableMap.end(); ++i)
  369. {
  370. std::string name = i->first;
  371. token_vector_t values = i->second.as<token_vector_t>();
  372. std::ostringstream oss;
  373. oss << name << ": ";
  374. for (token_vector_t::iterator t_itr = values.begin();
  375. t_itr != values.end(); ++t_itr)
  376. {
  377. oss << t_itr->c_str() << " ";
  378. }
  379. llinfos << oss.str() << llendl;
  380. }
  381. }
  382. std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const
  383. {
  384. return os << gOptionsDesc;
  385. }
  386. bool LLCommandLineParser::hasOption(const std::string& name) const
  387. {
  388. return gVariableMap.count(name) > 0;
  389. }
  390. const LLCommandLineParser::token_vector_t& LLCommandLineParser::getOption(const std::string& name) const
  391. {
  392. if (hasOption(name))
  393. {
  394. return gVariableMap[name].as<token_vector_t>();
  395. }
  396. return gEmptyValue;
  397. }