diff options
Diffstat (limited to 'linden/indra/newview/llcommandlineparser.cpp')
-rw-r--r-- | linden/indra/newview/llcommandlineparser.cpp | 543 |
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 | |||
59 | namespace 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. | ||
69 | namespace | ||
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 | |||
87 | class LLCLPError : public std::logic_error { | ||
88 | public: | ||
89 | LLCLPError(const std::string& what) : std::logic_error(what) {} | ||
90 | }; | ||
91 | |||
92 | class LLCLPLastOption : public std::logic_error { | ||
93 | public: | ||
94 | LLCLPLastOption(const std::string& what) : std::logic_error(what) {} | ||
95 | }; | ||
96 | |||
97 | class 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 | |||
106 | public: | ||
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 | |||
180 | protected: | ||
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 | //---------------------------------------------------------------------------- | ||
221 | void 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 | |||
264 | bool 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 | |||
312 | bool LLCommandLineParser::parseCommandLine(int argc, char **argv) | ||
313 | { | ||
314 | po::command_line_parser clp(argc, argv); | ||
315 | return parseAndStoreResults(clp); | ||
316 | } | ||
317 | |||
318 | bool 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 | |||
340 | bool 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 | |||
348 | void LLCommandLineParser::notify() | ||
349 | { | ||
350 | po::notify(gVariableMap); | ||
351 | } | ||
352 | |||
353 | void 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 | |||
369 | std::ostream& LLCommandLineParser::printOptionsDesc(std::ostream& os) const | ||
370 | { | ||
371 | return os << gOptionsDesc; | ||
372 | } | ||
373 | |||
374 | bool LLCommandLineParser::hasOption(const std::string& name) const | ||
375 | { | ||
376 | return gVariableMap.count(name) > 0; | ||
377 | } | ||
378 | |||
379 | const 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 | //---------------------------------------------------------------------------- | ||
392 | void 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 | |||
469 | void 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 | } | ||