llcommandlineparser.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:16k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file llcommandlineparser.cpp
  3.  * @brief The LLCommandLineParser class definitions
  4.  *
  5.  * $LicenseInfo:firstyear=2007&license=viewergpl$
  6.  * 
  7.  * Copyright (c) 2007-2010, 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 "llviewerprecompiledheaders.h"
  33. #include "llcommandlineparser.h"
  34. // *NOTE: The boost::lexical_cast generates 
  35. // the warning C4701(local used with out assignment) in VC7.1.
  36. // Disable the warning for the boost includes.
  37. #if _MSC_VER
  38. #   pragma warning(push)
  39. #   pragma warning( disable : 4701 )
  40. #else
  41. // NOTE: For the other platforms?
  42. #endif
  43. #include <boost/program_options.hpp>
  44. #include <boost/bind.hpp>
  45. #include<boost/tokenizer.hpp>
  46. #if _MSC_VER
  47. #   pragma warning(pop)
  48. #endif
  49. #include "llsdserialize.h"
  50. #include <iostream>
  51. #include <sstream>
  52. #include "llcontrol.h"
  53. namespace po = boost::program_options;
  54. // *NTOE:MEP - Currently the boost object reside in file scope. 
  55. // This has a couple of negatives, they are always around and 
  56. // there can be only one instance of each. 
  57. // The plus is that the boost-ly-ness of this implementation is 
  58. // hidden from the rest of the world. 
  59. // Its importatnt to realize that multiple LLCommandLineParser objects 
  60. // will all have this single repository of option escs and parsed options.
  61. // This could be good or bad, and probably won't matter for most use cases.
  62. namespace 
  63. {
  64.     po::options_description gOptionsDesc;
  65.     po::positional_options_description gPositionalOptions;
  66. po::variables_map gVariableMap;
  67.     
  68.     const LLCommandLineParser::token_vector_t gEmptyValue;
  69.     void read_file_into_string(std::string& str, const std::basic_istream < char >& file)
  70.     {
  71.     std::ostringstream oss;
  72.     oss << file.rdbuf();
  73.     str = oss.str();
  74.     }
  75.     bool gPastLastOption = false;
  76. }
  77. class LLCLPError : public std::logic_error {
  78. public:
  79.     LLCLPError(const std::string& what) : std::logic_error(what) {}
  80. };
  81. class LLCLPLastOption : public std::logic_error {
  82. public:
  83.     LLCLPLastOption(const std::string& what) : std::logic_error(what) {}
  84. };
  85. class LLCLPValue : public po::value_semantic_codecvt_helper<char> 
  86. {
  87.     unsigned mMinTokens;
  88.     unsigned mMaxTokens;
  89.     bool mIsComposing;
  90.     typedef boost::function1<void, const LLCommandLineParser::token_vector_t&> notify_callback_t;
  91.     notify_callback_t mNotifyCallback;
  92.     bool mLastOption;
  93. public:
  94.     LLCLPValue() :
  95.         mMinTokens(0),
  96.         mMaxTokens(0),
  97.         mIsComposing(false),
  98.         mLastOption(false)
  99.         {}
  100.       
  101.     virtual ~LLCLPValue() {};
  102.     void setMinTokens(unsigned c) 
  103.     {
  104.         mMinTokens = c;
  105.     }
  106.     void setMaxTokens(unsigned c) 
  107.     {
  108.         mMaxTokens = c;
  109.     }
  110.     void setComposing(bool c)
  111.     {
  112.         mIsComposing = c;
  113.     }
  114.     void setLastOption(bool c)
  115.     {
  116.         mLastOption = c;
  117.     }
  118.     void setNotifyCallback(notify_callback_t f)
  119.     {
  120.         mNotifyCallback = f;
  121.     }
  122.     // Overrides to support the value_semantic interface.
  123.     virtual std::string name() const 
  124.     { 
  125.         const std::string arg("arg");
  126.         const std::string args("args");
  127.         return (max_tokens() > 1) ? args : arg; 
  128.     }
  129.     virtual unsigned min_tokens() const
  130.     {
  131.         return mMinTokens;
  132.     }
  133.     virtual unsigned max_tokens() const 
  134.     {
  135.         return mMaxTokens;
  136.     }
  137.     virtual bool is_composing() const 
  138.     {
  139.         return mIsComposing;
  140.     }
  141.     virtual bool apply_default(boost::any& value_store) const
  142.     {
  143.         return false; // No defaults.
  144.     }
  145.     virtual void notify(const boost::any& value_store) const
  146.     {
  147.         const LLCommandLineParser::token_vector_t* value =
  148.             boost::any_cast<const LLCommandLineParser::token_vector_t>(&value_store);
  149.         if(mNotifyCallback) 
  150.         {
  151.            mNotifyCallback(*value);
  152.         }
  153.     }
  154. protected:
  155.     void xparse(boost::any& value_store,
  156.          const std::vector<std::string>& new_tokens) const
  157.     {
  158.         if(gPastLastOption)
  159.         {
  160.             throw(LLCLPLastOption("Don't parse no more!"));
  161.         }
  162.         // Error checks. Needed?
  163.         if (!value_store.empty() && !is_composing()) 
  164.         {
  165.             throw(LLCLPError("Non composing value with multiple occurences."));
  166.         }
  167.         if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens())
  168.         {
  169.             throw(LLCLPError("Illegal number of tokens specified."));
  170.         }
  171.         
  172.         if(value_store.empty())
  173.         {
  174.             value_store = boost::any(LLCommandLineParser::token_vector_t());
  175.         }
  176.         LLCommandLineParser::token_vector_t* tv = 
  177.             boost::any_cast<LLCommandLineParser::token_vector_t>(&value_store); 
  178.        
  179.         for(unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i)
  180.         {
  181.             tv->push_back(new_tokens[i]);
  182.         }
  183.         if(mLastOption)
  184.         {
  185.             gPastLastOption = true;
  186.         }
  187.     }
  188. };
  189. //----------------------------------------------------------------------------
  190. // LLCommandLineParser defintions
  191. //----------------------------------------------------------------------------
  192. void LLCommandLineParser::addOptionDesc(const std::string& option_name, 
  193.                                         boost::function1<void, const token_vector_t&> notify_callback,
  194.                                         unsigned int token_count,
  195.                                         const std::string& description,
  196.                                         const std::string& short_name,
  197.                                         bool composing,
  198.                                         bool positional,
  199.                                         bool last_option)
  200. {
  201.     // Compose the name for boost::po. 
  202.     // It takes the format "long_name, short name"
  203.     const std::string comma(",");
  204.     std::string boost_option_name = option_name;
  205.     if(short_name != LLStringUtil::null)
  206.     {
  207.         boost_option_name += comma;
  208.         boost_option_name += short_name;
  209.     }
  210.    
  211.     LLCLPValue* value_desc = new LLCLPValue();
  212.     value_desc->setMinTokens(token_count);
  213.     value_desc->setMaxTokens(token_count);
  214.     value_desc->setComposing(composing);
  215.     value_desc->setLastOption(last_option);
  216.     boost::shared_ptr<po::option_description> d(
  217.             new po::option_description(boost_option_name.c_str(), 
  218.                                     value_desc, 
  219.                                     description.c_str()));
  220.     if(!notify_callback.empty())
  221.     {
  222.         value_desc->setNotifyCallback(notify_callback);
  223.     }
  224.     gOptionsDesc.add(d);
  225.     if(positional)
  226.     {
  227.         gPositionalOptions.add(boost_option_name.c_str(), token_count);
  228.     }
  229. }
  230. bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp)
  231. {
  232.     try
  233.     {
  234.         clp.options(gOptionsDesc);
  235.         clp.positional(gPositionalOptions);
  236.         clp.style(po::command_line_style::default_style 
  237.                   | po::command_line_style::allow_long_disguise);
  238. if(mExtraParser)
  239. {
  240. clp.extra_parser(mExtraParser);
  241. }
  242.         po::basic_parsed_options<char> opts = clp.run();
  243.         po::store(opts, gVariableMap);
  244.     }
  245.     catch(po::error& e)
  246.     {
  247.         llwarns << "Caught Error:" << e.what() << llendl;
  248. mErrorMsg = e.what();
  249.         return false;
  250.     }
  251.     catch(LLCLPError& e)
  252.     {
  253.         llwarns << "Caught Error:" << e.what() << llendl;
  254. mErrorMsg = e.what();
  255.         return false;
  256.     }
  257.     catch(LLCLPLastOption&) 
  258.     {
  259. // This exception means a token was read after an option 
  260. // that must be the last option was reached (see url and slurl options)
  261.         // boost::po will have stored a malformed option. 
  262.         // All such options will be removed below.
  263. // The last option read, the last_option option, and its value
  264. // are put into the error message.
  265. std::string last_option;
  266. std::string last_value;
  267.         for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();)
  268.         {
  269.             po::variables_map::iterator tempI = i++;
  270.             if(tempI->second.empty())
  271.             {
  272.                 gVariableMap.erase(tempI);
  273.             }
  274. else
  275. {
  276. last_option = tempI->first;
  277.         LLCommandLineParser::token_vector_t* tv = 
  278.     boost::any_cast<LLCommandLineParser::token_vector_t>(&(tempI->second.value())); 
  279. if(!tv->empty())
  280. {
  281. last_value = (*tv)[tv->size()-1];
  282. }
  283. }
  284.         }
  285. // Continue without parsing.
  286. std::ostringstream msg;
  287. msg << "Caught Error: Found options after last option: " 
  288. << last_option << " "
  289. << last_value;
  290.         llwarns << msg.str() << llendl;
  291. mErrorMsg = msg.str();
  292.         return false;
  293.     } 
  294.     return true;
  295. }
  296. bool LLCommandLineParser::parseCommandLine(int argc, char **argv)
  297. {
  298.     po::command_line_parser clp(argc, argv);
  299.     return parseAndStoreResults(clp);
  300. }
  301. bool LLCommandLineParser::parseCommandLineString(const std::string& str)
  302. {
  303.     // Split the string content into tokens
  304.     boost::escaped_list_separator<char> sep("\", "rn ", ""'");
  305.     boost::tokenizer< boost::escaped_list_separator<char> > tok(str, sep);
  306.     std::vector<std::string> tokens;
  307.     // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
  308.     for(boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin();
  309.         i != tok.end();
  310.         ++i)
  311.     {
  312.         if(0 != i->size())
  313.         {
  314.             tokens.push_back(*i);
  315.         }
  316.     }
  317.     po::command_line_parser clp(tokens);
  318.     return parseAndStoreResults(clp);
  319.         
  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.               
  385.             if(value.size() > 0)
  386.             {
  387.                 // There's a token. check the string for true/false/1/0 etc.
  388.                 BOOL result = false;
  389.                 BOOL gotSet = LLStringUtil::convertToBOOL(value[0], result);
  390.                 if(gotSet)
  391.                 {
  392.                     ctrl->setValue(LLSD(result), false);
  393.                 }
  394.             }
  395.             else
  396.             {
  397.                 ctrl->setValue(LLSD(true), false);
  398.             }
  399.             break;
  400.         default:
  401.             {
  402.                 // For the default types, let llsd do the conversion.
  403.                 if(value.size() > 1 && ctrl->isType(TYPE_LLSD))
  404.                 {
  405.                     // Assume its an array...
  406.                     LLSD llsdArray;
  407.                     for(unsigned int i = 0; i < value.size(); ++i)
  408.                     {
  409.                         LLSD llsdValue;
  410.                         llsdValue.assign(LLSD::String(value[i]));
  411.                         llsdArray.set(i, llsdValue);
  412.                     }
  413.                     ctrl->setValue(llsdArray, false);
  414.                 }
  415.                 else if(value.size() > 0)
  416.                 {
  417. if(value.size() > 1)
  418. {
  419. llwarns << "Ignoring extra tokens mapped to the setting: " << opt_name << "." << llendl; 
  420. }
  421.                     LLSD llsdValue;
  422.                     llsdValue.assign(LLSD::String(value[0]));
  423.                     ctrl->setValue(llsdValue, false);
  424.                 }
  425.             }
  426.             break;
  427.         }
  428.     }
  429.     else
  430.     {
  431.         llwarns << "Command Line option mapping '" 
  432.             << opt_name 
  433.             << "' not found! Ignoring." 
  434.             << llendl;
  435.     }
  436. }
  437. void LLControlGroupCLP::configure(const std::string& config_filename, LLControlGroup* controlGroup)
  438. {
  439.     // This method reads the llsd based config file, and uses it to set 
  440.     // members of a control group.
  441.     LLSD clpConfigLLSD;
  442.     
  443.     llifstream input_stream;
  444.     input_stream.open(config_filename, std::ios::in | std::ios::binary);
  445.     if(input_stream.is_open())
  446.     {
  447.         LLSDSerialize::fromXML(clpConfigLLSD, input_stream);
  448.         for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap(); 
  449.             option_itr != clpConfigLLSD.endMap(); 
  450.             ++option_itr)
  451.         {
  452.             LLSD::String long_name = option_itr->first;
  453.             LLSD option_params = option_itr->second;
  454.             
  455.             std::string desc("n/a");
  456.             if(option_params.has("desc"))
  457.             {
  458.                 desc = option_params["desc"].asString();
  459.             }
  460.             
  461.             std::string short_name = LLStringUtil::null;
  462.             if(option_params.has("short"))
  463.             {
  464.                 short_name = option_params["short"].asString();
  465.             }
  466.             unsigned int token_count = 0;
  467.             if(option_params.has("count"))
  468.             {
  469.                 token_count = option_params["count"].asInteger();
  470.             }
  471.             bool composing = false;
  472.             if(option_params.has("compose"))
  473.             {
  474.                 composing = option_params["compose"].asBoolean();
  475.             }
  476.             bool positional = false;
  477.             if(option_params.has("positional"))
  478.             {
  479.                 positional = option_params["positional"].asBoolean();
  480.             }
  481.             bool last_option = false;
  482.             if(option_params.has("last_option"))
  483.             {
  484.                 last_option = option_params["last_option"].asBoolean();
  485.             }
  486.             boost::function1<void, const token_vector_t&> callback;
  487.             if(option_params.has("map-to") && (NULL != controlGroup))
  488.             {
  489.                 std::string controlName = option_params["map-to"].asString();
  490.                 callback = boost::bind(setControlValueCB, _1, 
  491.                                        controlName, controlGroup);
  492.             }
  493.             this->addOptionDesc(
  494.                 long_name, 
  495.                 callback,
  496.                 token_count, 
  497.                 desc, 
  498.                 short_name, 
  499.                 composing,
  500.                 positional,
  501.                 last_option);
  502.         }
  503.     }
  504. }