aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llcommandlineparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linden/indra/newview/llcommandlineparser.cpp')
-rw-r--r--linden/indra/newview/llcommandlineparser.cpp543
1 files changed, 543 insertions, 0 deletions
diff --git a/linden/indra/newview/llcommandlineparser.cpp b/linden/indra/newview/llcommandlineparser.cpp
new file mode 100644
index 0000000..8123632
--- /dev/null
+++ b/linden/indra/newview/llcommandlineparser.cpp
@@ -0,0 +1,543 @@
1/**
2 * @file llcommandlineparser.cpp
3 * @brief The LLCommandLineParser class definitions
4 *
5 * $LicenseInfo:firstyear=2007&license=viewergpl$
6 *
7 * Copyright (c) 2007-2008, 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 http://secondlifegrid.net/programs/open_source/licensing/flossexception
21 *
22 * By copying, modifying or distributing this software, you acknowledge
23 * that you have read and understood your obligations described above,
24 * and agree to abide by those obligations.
25 *
26 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
27 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
28 * COMPLETENESS OR PERFORMANCE.
29 * $/LicenseInfo$
30 */
31
32#include "llviewerprecompiledheaders.h"
33#include "llcommandlineparser.h"
34
35// *NOTE: The boost::lexical_cast generates
36// the warning C4701(local used with out assignment) in VC7.1.
37// Disable the warning for the boost includes.
38#if _MSC_VER
39# pragma warning(push)
40# pragma warning( disable : 4701 )
41#else
42// NOTE: For the other platforms?
43#endif
44
45#include <boost/program_options.hpp>
46#include <boost/bind.hpp>
47#include<boost/tokenizer.hpp>
48
49#if _MSC_VER
50# pragma warning(pop)
51#endif
52
53#include "llsdserialize.h"
54#include <iostream>
55#include <sstream>
56
57#include "llcontrol.h"
58
59namespace po = boost::program_options;
60
61// *NTOE:MEP - Currently the boost object reside in file scope.
62// This has a couple of negatives, they are always around and
63// there can be only one instance of each.
64// The plus is that the boost-ly-ness of this implementation is
65// hidden from the rest of the world.
66// Its importatnt to realize that multiple LLCommandLineParser objects
67// will all have this single repository of option escs and parsed options.
68// This could be good or bad, and probably won't matter for most use cases.
69namespace
70{
71 po::options_description gOptionsDesc;
72 po::positional_options_description gPositionalOptions;
73 po::variables_map gVariableMap;
74
75 const LLCommandLineParser::token_vector_t gEmptyValue;
76
77 void read_file_into_string(std::string& str, const std::basic_istream < char >& file)
78 {
79 std::ostringstream oss;
80 oss << file.rdbuf();
81 str = oss.str();
82 }
83
84 bool gPastLastOption = false;
85}
86
87class LLCLPError : public std::logic_error {
88public:
89 LLCLPError(const std::string& what) : std::logic_error(what) {}
90};
91
92class LLCLPLastOption : public std::logic_error {
93public:
94 LLCLPLastOption(const std::string& what) : std::logic_error(what) {}
95};
96
97class LLCLPValue : public po::value_semantic_codecvt_helper<char>
98{
99 unsigned mMinTokens;
100 unsigned mMaxTokens;
101 bool mIsComposing;
102 typedef boost::function1<void, const LLCommandLineParser::token_vector_t&> notify_callback_t;
103 notify_callback_t mNotifyCallback;
104 bool mLastOption;
105
106public:
107 LLCLPValue() :
108 mMinTokens(0),
109 mMaxTokens(0),
110 mIsComposing(false),
111 mLastOption(false)
112 {}
113
114 virtual ~LLCLPValue() {};
115
116 void setMinTokens(unsigned c)
117 {
118 mMinTokens = c;
119 }
120
121 void setMaxTokens(unsigned c)
122 {
123 mMaxTokens = c;
124 }
125
126 void setComposing(bool c)
127 {
128 mIsComposing = c;
129 }
130
131 void setLastOption(bool c)
132 {
133 mLastOption = c;
134 }
135
136 void setNotifyCallback(notify_callback_t f)
137 {
138 mNotifyCallback = f;
139 }
140
141 // Overrides to support the value_semantic interface.
142 virtual std::string name() const
143 {
144 const std::string arg("arg");
145 const std::string args("args");
146 return (max_tokens() > 1) ? args : arg;
147 }
148
149 virtual unsigned min_tokens() const
150 {
151 return mMinTokens;
152 }
153
154 virtual unsigned max_tokens() const
155 {
156 return mMaxTokens;
157 }
158
159 virtual bool is_composing() const
160 {
161 return mIsComposing;
162 }
163
164 virtual bool apply_default(boost::any& value_store) const
165 {
166 return false; // No defaults.
167 }
168
169 virtual void notify(const boost::any& value_store) const
170 {
171 const LLCommandLineParser::token_vector_t* value =
172 boost::any_cast<const LLCommandLineParser::token_vector_t>(&value_store);
173 if(mNotifyCallback)
174 {
175 mNotifyCallback(*value);
176 }
177
178 }
179
180protected:
181 void xparse(boost::any& value_store,
182 const std::vector<std::string>& new_tokens) const
183 {
184 if(gPastLastOption)
185 {
186 throw(LLCLPLastOption("Don't parse no more!"));
187 }
188
189 // Error checks. Needed?
190 if (!value_store.empty() && !is_composing())
191 {
192 throw(LLCLPError("Non composing value with multiple occurences."));
193 }
194 if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens())
195 {
196 throw(LLCLPError("Illegal number of tokens specified."));
197 }
198
199 if(value_store.empty())
200 {
201 value_store = boost::any(LLCommandLineParser::token_vector_t());
202 }
203 LLCommandLineParser::token_vector_t* tv =
204 boost::any_cast<LLCommandLineParser::token_vector_t>(&value_store);
205
206 for(unsigned i = 0; i < new_tokens.size() && i < mMaxTokens; ++i)
207 {
208 tv->push_back(new_tokens[i]);
209 }
210
211 if(mLastOption)
212 {
213 gPastLastOption = true;
214 }
215 }
216};
217
218//----------------------------------------------------------------------------
219// LLCommandLineParser defintions
220//----------------------------------------------------------------------------
221void LLCommandLineParser::addOptionDesc(const LLString& option_name,
222 boost::function1<void, const token_vector_t&> notify_callback,
223 unsigned int token_count,
224 const LLString& description,
225 const LLString& short_name,
226 bool composing,
227 bool positional,
228 bool last_option)
229{
230 // Compose the name for boost::po.
231 // It takes the format "long_name, short name"
232 const LLString comma(",");
233 LLString boost_option_name = option_name;
234 if(short_name != LLString::null)
235 {
236 boost_option_name += comma;
237 boost_option_name += short_name;
238 }
239
240 LLCLPValue* value_desc = new LLCLPValue();
241 value_desc->setMinTokens(token_count);
242 value_desc->setMaxTokens(token_count);
243 value_desc->setComposing(composing);
244 value_desc->setLastOption(last_option);
245
246 boost::shared_ptr<po::option_description> d(
247 new po::option_description(boost_option_name.c_str(),
248 value_desc,
249 description.c_str()));
250
251 if(!notify_callback.empty())
252 {
253 value_desc->setNotifyCallback(notify_callback);
254 }
255
256 gOptionsDesc.add(d);
257
258 if(positional)
259 {
260 gPositionalOptions.add(boost_option_name.c_str(), token_count);
261 }
262}
263
264bool LLCommandLineParser::parseAndStoreResults(po::command_line_parser& clp)
265{
266 try
267 {
268 clp.options(gOptionsDesc);
269 clp.positional(gPositionalOptions);
270 clp.style(po::command_line_style::default_style
271 | po::command_line_style::allow_long_disguise);
272 if(mExtraParser)
273 {
274 clp.extra_parser(mExtraParser);
275 }
276
277 po::basic_parsed_options<char> opts = clp.run();
278 po::store(opts, gVariableMap);
279 }
280 catch(po::error& e)
281 {
282 llwarns << "Caught Error:" << e.what() << llendl;
283 mErrorMsg = e.what();
284 return false;
285 }
286 catch(LLCLPError& e)
287 {
288 llwarns << "Caught Error:" << e.what() << llendl;
289 mErrorMsg = e.what();
290 return false;
291 }
292 catch(LLCLPLastOption&)
293 {
294 // Continue without parsing.
295 std::string msg = "Found tokens past last option. Ignoring.";
296 llwarns << msg << llendl;
297 mErrorMsg = msg;
298 // boost::po will have stored a mal-formed option.
299 // All such options will be removed below.
300 for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end();)
301 {
302 po::variables_map::iterator tempI = i++;
303 if(tempI->second.empty())
304 {
305 gVariableMap.erase(tempI);
306 }
307 }
308 }
309 return true;
310}
311
312bool LLCommandLineParser::parseCommandLine(int argc, char **argv)
313{
314 po::command_line_parser clp(argc, argv);
315 return parseAndStoreResults(clp);
316}
317
318bool LLCommandLineParser::parseCommandLineString(const std::string& str)
319{
320 // Split the string content into tokens
321 boost::escaped_list_separator<char> sep("\\", "\r\n ", "\"'");
322 boost::tokenizer< boost::escaped_list_separator<char> > tok(str, sep);
323 std::vector<std::string> tokens;
324 // std::copy(tok.begin(), tok.end(), std::back_inserter(tokens));
325 for(boost::tokenizer< boost::escaped_list_separator<char> >::iterator i = tok.begin();
326 i != tok.end();
327 ++i)
328 {
329 if(0 != i->size())
330 {
331 tokens.push_back(*i);
332 }
333 }
334
335 po::command_line_parser clp(tokens);
336 return parseAndStoreResults(clp);
337
338}
339
340bool LLCommandLineParser::parseCommandLineFile(const std::basic_istream < char >& file)
341{
342 std::string args;
343 read_file_into_string(args, file);
344
345 return parseCommandLineString(args);
346}
347
348void LLCommandLineParser::notify()
349{
350 po::notify(gVariableMap);
351}
352
353void LLCommandLineParser::printOptions() const
354{
355 for(po::variables_map::iterator i = gVariableMap.begin(); i != gVariableMap.end(); ++i)
356 {
357 std::string name = i->first;
358 token_vector_t values = i->second.as<token_vector_t>();
359 std::ostringstream oss;
360 oss << name << ": ";
361 for(token_vector_t::iterator t_itr = values.begin(); t_itr != values.end(); ++t_itr)
362 {
363 oss << t_itr->c_str() << " ";
364 }
365 llinfos << oss.str() << llendl;
366 }
367}
368
369std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const
370{
371 return os << gOptionsDesc;
372}
373
374bool LLCommandLineParser::hasOption(const std::string& name) const
375{
376 return gVariableMap.count(name) > 0;
377}
378
379const LLCommandLineParser::token_vector_t& LLCommandLineParser::getOption(const std::string& name) const
380{
381 if(hasOption(name))
382 {
383 return gVariableMap[name].as<token_vector_t>();
384 }
385
386 return gEmptyValue;
387}
388
389//----------------------------------------------------------------------------
390// LLControlGroupCLP defintions
391//----------------------------------------------------------------------------
392void setControlValueCB(const LLCommandLineParser::token_vector_t& value,
393 const LLString& opt_name,
394 LLControlGroup* ctrlGroup)
395{
396 if(value.size() > 1)
397 {
398 llwarns << "Ignoring extra tokens mapped to the setting: " << opt_name << "." << llendl;
399 }
400
401 // *FIX: Do sematic conversion here.
402 // LLSD (ImplString) Is no good for doing string to type conversion for...
403 // booleans
404 // compound types
405 // ?...
406
407 LLControlVariable* ctrl = ctrlGroup->getControl(opt_name);
408 if(NULL != ctrl)
409 {
410 switch(ctrl->type())
411 {
412 case TYPE_BOOLEAN:
413 if(value.size() > 1)
414 {
415 llwarns << "Ignoring extra tokens." << llendl;
416 }
417
418 if(value.size() > 0)
419 {
420 // There's a token. check the string for true/false/1/0 etc.
421 BOOL result = false;
422 BOOL gotSet = LLString::convertToBOOL(value[0], result);
423 if(gotSet)
424 {
425 ctrl->setValue(LLSD(result), false);
426 }
427 }
428 else
429 {
430 ctrl->setValue(LLSD(true), false);
431 }
432 break;
433
434 default:
435 {
436 // For the default types, let llsd do the conversion.
437 if(value.size() > 1)
438 {
439 // Assume its an array...
440 LLSD llsdArray;
441 for(unsigned int i = 0; i < value.size(); ++i)
442 {
443 LLSD llsdValue;
444 llsdValue.assign(LLSD::String(value[i]));
445 llsdArray.set(i, llsdValue);
446 }
447
448 ctrl->setValue(llsdArray, false);
449 }
450 else if(value.size() > 0)
451 {
452 LLSD llsdValue;
453 llsdValue.assign(LLSD::String(value[0]));
454 ctrl->setValue(llsdValue, false);
455 }
456 }
457 break;
458 }
459 }
460 else
461 {
462 llwarns << "Command Line option mapping '"
463 << opt_name
464 << "' not found! Ignoring."
465 << llendl;
466 }
467}
468
469void LLControlGroupCLP::configure(const LLString& config_filename, LLControlGroup* controlGroup)
470{
471 // This method reads the llsd based config file, and uses it to set
472 // members of a control group.
473 LLSD clpConfigLLSD;
474
475 llifstream input_stream;
476 input_stream.open(config_filename.c_str(), std::ios::in | std::ios::binary);
477
478 if(input_stream.is_open())
479 {
480 LLSDSerialize::fromXML(clpConfigLLSD, input_stream);
481 for(LLSD::map_iterator option_itr = clpConfigLLSD.beginMap();
482 option_itr != clpConfigLLSD.endMap();
483 ++option_itr)
484 {
485 LLSD::String long_name = option_itr->first;
486 LLSD option_params = option_itr->second;
487
488 LLString desc("n/a");
489 if(option_params.has("desc"))
490 {
491 desc = option_params["desc"].asString();
492 }
493
494 LLString short_name = LLString::null;
495 if(option_params.has("short"))
496 {
497 short_name = option_params["short"].asString();
498 }
499
500 unsigned int token_count = 0;
501 if(option_params.has("count"))
502 {
503 token_count = option_params["count"].asInteger();
504 }
505
506 bool composing = false;
507 if(option_params.has("compose"))
508 {
509 composing = option_params["compose"].asBoolean();
510 }
511
512 bool positional = false;
513 if(option_params.has("positional"))
514 {
515 positional = option_params["positional"].asBoolean();
516 }
517
518 bool last_option = false;
519 if(option_params.has("last_option"))
520 {
521 last_option = option_params["last_option"].asBoolean();
522 }
523
524 boost::function1<void, const token_vector_t&> callback;
525 if(option_params.has("map-to") && (NULL != controlGroup))
526 {
527 LLString controlName = option_params["map-to"].asString();
528 callback = boost::bind(setControlValueCB, _1,
529 controlName, controlGroup);
530 }
531
532 this->addOptionDesc(
533 long_name,
534 callback,
535 token_count,
536 desc,
537 short_name,
538 composing,
539 positional,
540 last_option);
541 }
542 }
543}