llcommandlineparser.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 #include "llcommandlineparser.h"
00034 
00035 // *NOTE: The boost::lexical_cast generates 
00036 // the warning C4701(local used with out assignment) in VC7.1.
00037 // Disable the warning for the boost includes.
00038 #if _MSC_VER
00039 #   pragma warning(push)
00040 #   pragma warning( disable : 4701 )
00041 #else
00042 // NOTE: For the other platforms?
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 // *NTOE:MEP - Currently the boost object reside in file scope. 
00062 // This has a couple of negatives, they are always around and 
00063 // there can be only one instance of each. 
00064 // The plus is that the boost-ly-ness of this implementation is 
00065 // hidden from the rest of the world. 
00066 // Its importatnt to realize that multiple LLCommandLineParser objects 
00067 // will all have this single repository of option escs and parsed options.
00068 // This could be good or bad, and probably won't matter for most use cases.
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     // Overrides to support the value_semantic interface.
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; // No defaults.
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         // Error checks. Needed?
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 // LLCommandLineParser defintions
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     // Compose the name for boost::po. 
00231     // It takes the format "long_name, short name"
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         // Continue without parsing.
00295                 std::string msg = "Found tokens past last option. Ignoring.";
00296         llwarns << msg << llendl;
00297                 mErrorMsg = msg;
00298         // boost::po will have stored a mal-formed option. 
00299         // All such options will be removed below.
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     // Split the string content into tokens
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     // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
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 // LLControlGroupCLP defintions
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     // *FIX: Do sematic conversion here.
00402     // LLSD (ImplString) Is no good for doing string to type conversion for...
00403     // booleans
00404     // compound types
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                 // There's a token. check the string for true/false/1/0 etc.
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                 // For the default types, let llsd do the conversion.
00437                 if(value.size() > 1)
00438                 {
00439                     // Assume its an array...
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     // This method reads the llsd based config file, and uses it to set 
00472     // members of a control group.
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 }

Generated on Fri May 16 08:33:16 2008 for SecondLife by  doxygen 1.5.5