1 /*-------------------------------------------------------------------------
2 * drawElements C++ Base Library
3 * -----------------------------
5 * Copyright 2014 The Android Open Source Project
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
21 * \brief Command line parser.
22 *//*--------------------------------------------------------------------*/
24 #include "deCommandLine.hpp"
39 struct Help { typedef bool ValueType; };
45 inline const char* getNamedValueName (const void* namedValue)
47 return static_cast<const NamedValue<deUint8>*>(namedValue)->name;
52 TypedFieldMap::TypedFieldMap (void)
56 TypedFieldMap::~TypedFieldMap (void)
61 void TypedFieldMap::clear (void)
63 for (Map::const_iterator iter = m_fields.begin(); iter != m_fields.end(); ++iter)
65 if (iter->second.value)
66 iter->second.destructor(iter->second.value);
71 bool TypedFieldMap::contains (const std::type_info* key) const
73 return m_fields.find(key) != m_fields.end();
76 const TypedFieldMap::Entry& TypedFieldMap::get (const std::type_info* key) const
78 Map::const_iterator pos = m_fields.find(key);
79 if (pos != m_fields.end())
82 throw std::out_of_range("Value not set");
85 void TypedFieldMap::set (const std::type_info* key, const Entry& value)
87 Map::iterator pos = m_fields.find(key);
89 if (pos != m_fields.end())
91 pos->second.destructor(pos->second.value);
92 pos->second.value = DE_NULL;
97 m_fields.insert(std::make_pair(key, value));
100 Parser::Parser (void)
102 addOption(Option<Help>("h", "help", "Show this help"));
105 Parser::~Parser (void)
109 void Parser::addOption (const OptInfo& option)
111 m_options.push_back(option);
114 bool Parser::parse (int numArgs, const char* const* args, CommandLine* dst, std::ostream& err) const
116 typedef map<string, const OptInfo*> OptMap;
117 typedef set<const OptInfo*> OptSet;
124 DE_ASSERT(dst->m_args.empty() && dst->m_options.empty());
126 for (vector<OptInfo>::const_iterator optIter = m_options.begin(); optIter != m_options.end(); optIter++)
128 const OptInfo& opt = *optIter;
130 DE_ASSERT(opt.shortName || opt.longName);
134 DE_ASSERT(shortOptMap.find(opt.shortName) == shortOptMap.end());
135 shortOptMap[opt.shortName] = &opt;
140 DE_ASSERT(longOptMap.find(opt.longName) == longOptMap.end());
141 longOptMap[opt.longName] = &opt;
144 // Set default values.
145 if (opt.defaultValue)
146 opt.dispatchParse(&opt, opt.defaultValue, &dst->m_options);
147 else if (opt.setDefault)
148 opt.setDefault(&dst->m_options);
151 DE_ASSERT(!dst->m_options.get<Help>());
153 for (int argNdx = 0; argNdx < numArgs; argNdx++)
155 const char* arg = args[argNdx];
156 int argLen = (int)strlen(arg);
158 if (arg[0] == '-' && arg[1] == '-' && arg[2] == 0)
160 // End of option list (--)
161 for (int optNdx = argNdx+1; optNdx < numArgs; optNdx++)
162 dst->m_args.push_back(args[optNdx]);
165 else if (arg[0] == '-')
167 const bool isLongName = arg[1] == '-';
168 const char* nameStart = arg + (isLongName ? 2 : 1);
169 const char* nameEnd = std::find(nameStart, arg+argLen, '=');
170 const bool hasImmValue = nameEnd != (arg+argLen);
171 const OptMap& optMap = isLongName ? longOptMap : shortOptMap;
172 OptMap::const_iterator optPos = optMap.find(string(nameStart, nameEnd));
173 const OptInfo* opt = optPos != optMap.end() ? optPos->second : DE_NULL;
177 err << "Unrecognized command line option '" << arg << "'\n";
182 if (seenOpts.find(opt) != seenOpts.end())
184 err << "Command line option '--" << opt->longName << "' specified multiple times\n";
189 seenOpts.insert(opt);
195 opt->dispatchParse(opt, DE_NULL, &dst->m_options);
199 err << "No value expected for command line option '--" << opt->longName << "'\n";
205 const bool hasValue = hasImmValue || (argNdx+1 < numArgs);
209 const char* value = hasValue ? (hasImmValue ? nameEnd+1 : args[argNdx+1]) : DE_NULL;
212 argNdx += 1; // Skip value
216 opt->dispatchParse(opt, value, &dst->m_options);
218 catch (const std::exception& e)
220 err << "Got error parsing command line option '--" << opt->longName << "': " << e.what() << "\n";
226 err << "Expected value for command line option '--" << opt->longName << "'\n";
234 dst->m_args.push_back(arg);
239 if (dst->m_options.get<Help>())
245 void Parser::help (std::ostream& str) const
247 for (vector<OptInfo>::const_iterator optIter = m_options.begin(); optIter != m_options.end(); ++optIter)
249 const OptInfo& opt = *optIter;
253 str << "-" << opt.shortName;
255 if (opt.shortName && opt.longName)
259 str << "--" << opt.longName;
265 for (const void* curValue = opt.namedValues; curValue != opt.namedValuesEnd; curValue = (const void*)((deUintptr)curValue + opt.namedValueStride))
267 if (curValue != opt.namedValues)
269 str << getNamedValueName(curValue);
274 else if (!opt.isFlag)
280 str << " " << opt.description << "\n";
282 if (opt.defaultValue)
283 str << " default: '" << opt.defaultValue << "'\n";
289 void CommandLine::clear (void)
295 const void* findNamedValueMatch (const char* src, const void* namedValues, const void* namedValuesEnd, size_t stride)
297 std::string srcStr(src);
299 for (const void* curValue = namedValues; curValue != namedValuesEnd; curValue = (const void*)((deUintptr)curValue + stride))
301 if (srcStr == getNamedValueName(curValue))
305 throw std::invalid_argument("unrecognized value '" + srcStr + "'");
310 // Default / parsing functions
313 void getTypeDefault (bool* dst)
319 void parseType<bool> (const char*, bool* dst)
325 void parseType<std::string> (const char* src, std::string* dst)
331 void parseType<int> (const char* src, int* dst)
333 std::istringstream str(src);
335 if (str.bad() || !str.eof())
336 throw std::invalid_argument("invalid integer literal");
341 DE_DECLARE_COMMAND_LINE_OPT(TestStringOpt, std::string);
342 DE_DECLARE_COMMAND_LINE_OPT(TestStringDefOpt, std::string);
343 DE_DECLARE_COMMAND_LINE_OPT(TestIntOpt, int);
344 DE_DECLARE_COMMAND_LINE_OPT(TestBoolOpt, bool);
345 DE_DECLARE_COMMAND_LINE_OPT(TestNamedOpt, deUint64);
349 // Parsing with no options.
354 std::ostringstream err;
356 const bool parseOk = parser.parse(0, DE_NULL, &cmdLine, err);
358 DE_TEST_ASSERT(parseOk && err.str().empty());
362 const char* args[] = { "-h" };
363 std::ostringstream err;
365 const bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
367 DE_TEST_ASSERT(!parseOk);
368 DE_TEST_ASSERT(err.str().empty()); // No message about -h
372 const char* args[] = { "--help" };
373 std::ostringstream err;
375 const bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
377 DE_TEST_ASSERT(!parseOk);
378 DE_TEST_ASSERT(err.str().empty()); // No message about -h
382 const char* args[] = { "foo", "bar", "baz baz" };
383 std::ostringstream err;
385 const bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
387 DE_TEST_ASSERT(parseOk && err.str().empty());
388 DE_TEST_ASSERT(cmdLine.getArgs().size() == DE_LENGTH_OF_ARRAY(args));
390 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(args); ndx++)
391 DE_TEST_ASSERT(cmdLine.getArgs()[ndx] == args[ndx]);
395 // Parsing with options.
399 static const NamedValue<deUint64> s_namedValues[] =
406 parser << Option<TestStringOpt> ("s", "string", "String option")
407 << Option<TestStringDefOpt> ("x", "xyz", "String option w/ default value", "foo")
408 << Option<TestIntOpt> ("i", "int", "Int option")
409 << Option<TestBoolOpt> ("b", "bool", "Test boolean flag")
410 << Option<TestNamedOpt> ("n", "named", "Test named opt", DE_ARRAY_BEGIN(s_namedValues), DE_ARRAY_END(s_namedValues), "one");
413 std::ostringstream err;
414 DE_TEST_ASSERT(err.str().empty());
416 DE_TEST_ASSERT(!err.str().empty());
422 std::ostringstream err;
423 bool parseOk = parser.parse(0, DE_NULL, &cmdLine, err);
425 DE_TEST_ASSERT(parseOk);
426 DE_TEST_ASSERT(err.str().empty());
428 DE_TEST_ASSERT(!cmdLine.hasOption<TestStringOpt>());
429 DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
430 DE_TEST_ASSERT(cmdLine.getOption<TestNamedOpt>() == 1);
431 DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>() == false);
432 DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "foo");
437 const char* args[] = { "-s", "test value", "-b", "-i=9", "--named=huge" };
439 std::ostringstream err;
440 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
442 DE_TEST_ASSERT(parseOk);
443 DE_TEST_ASSERT(err.str().empty());
445 DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "test value");
446 DE_TEST_ASSERT(cmdLine.getOption<TestIntOpt>() == 9);
447 DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
448 DE_TEST_ASSERT(cmdLine.getOption<TestNamedOpt>() == ~0ull);
449 DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "foo");
452 // End of argument list (--)
454 const char* args[] = { "--string=foo", "-b", "--", "--int=2", "-b" };
456 std::ostringstream err;
457 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
459 DE_TEST_ASSERT(parseOk);
460 DE_TEST_ASSERT(err.str().empty());
462 DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "foo");
463 DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
464 DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
466 DE_TEST_ASSERT(cmdLine.getArgs().size() == 2);
467 DE_TEST_ASSERT(cmdLine.getArgs()[0] == "--int=2");
468 DE_TEST_ASSERT(cmdLine.getArgs()[1] == "-b");
473 const char* args[] = { "--string", "--", "-b", "foo" };
475 std::ostringstream err;
476 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
478 DE_TEST_ASSERT(parseOk);
479 DE_TEST_ASSERT(err.str().empty());
481 DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "--");
482 DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());
483 DE_TEST_ASSERT(!cmdLine.hasOption<TestIntOpt>());
485 DE_TEST_ASSERT(cmdLine.getArgs().size() == 1);
486 DE_TEST_ASSERT(cmdLine.getArgs()[0] == "foo");
489 // Invalid flag usage
491 const char* args[] = { "-b=true" };
493 std::ostringstream err;
494 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
496 DE_TEST_ASSERT(!parseOk);
497 DE_TEST_ASSERT(!err.str().empty());
500 // Invalid named option
502 const char* args[] = { "-n=two" };
504 std::ostringstream err;
505 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
507 DE_TEST_ASSERT(!parseOk);
508 DE_TEST_ASSERT(!err.str().empty());
511 // Unrecognized option (-x)
513 const char* args[] = { "-x" };
515 std::ostringstream err;
516 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
518 DE_TEST_ASSERT(!parseOk);
519 DE_TEST_ASSERT(!err.str().empty());
522 // Unrecognized option (--xxx)
524 const char* args[] = { "--xxx" };
526 std::ostringstream err;
527 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
529 DE_TEST_ASSERT(!parseOk);
530 DE_TEST_ASSERT(!err.str().empty());
535 const char* args[] = { "--int", "1x" };
537 std::ostringstream err;
538 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
540 DE_TEST_ASSERT(!parseOk);
541 DE_TEST_ASSERT(!err.str().empty());
544 // Arg specified multiple times
546 const char* args[] = { "-s=2", "-s=3" };
548 std::ostringstream err;
549 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
551 DE_TEST_ASSERT(!parseOk);
552 DE_TEST_ASSERT(!err.str().empty());
557 const char* args[] = { "--int" };
559 std::ostringstream err;
560 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
562 DE_TEST_ASSERT(!parseOk);
563 DE_TEST_ASSERT(!err.str().empty());
566 // Empty value --arg=
568 const char* args[] = { "--string=", "-b", "-x", "" };
570 std::ostringstream err;
571 bool parseOk = parser.parse(DE_LENGTH_OF_ARRAY(args), &args[0], &cmdLine, err);
573 DE_TEST_ASSERT(parseOk);
574 DE_TEST_ASSERT(err.str().empty());
575 DE_TEST_ASSERT(cmdLine.getOption<TestStringOpt>() == "");
576 DE_TEST_ASSERT(cmdLine.getOption<TestStringDefOpt>() == "");
577 DE_TEST_ASSERT(cmdLine.getOption<TestBoolOpt>());