From a9bdb920611cda5745e3ad82c9ede723b766f3ca Mon Sep 17 00:00:00 2001
From: David Flynn <dflynn@blackberry.com>
Date: Fri Feb 6 21:39:16 2015 -0500
Subject: [PATCH] program_options: report unknown options as error
This commit provides:
- an error reporter to program_options. This removes the hard
coded writing to cerr, and allows for reporting of line numbers
when config file errors are encountered.
- reporting of malformed config lines is now supported (previously
these were silently ignored).
- when any error is logged, after all options are processed,
TAppDecoder/TAppEncoder exit.
---
source/App/TAppDecoder/TAppDecCfg.cpp | 9 +-
source/App/TAppEncoder/TAppEncCfg.cpp | 9 +-
source/Lib/TAppCommon/program_options_lite.cpp | 128 ++++++++++++++++++------
source/Lib/TAppCommon/program_options_lite.h | 36 ++++---
4 files changed, 134 insertions(+), 48 deletions(-)
diff --git a/source/App/TAppDecoder/TAppDecCfg.cpp b/source/App/TAppDecoder/TAppDecCfg.cpp
index ce60a46..6f65e1b 100644
a
|
b
|
Bool TAppDecCfg::parseCfg( Int argc, Char* argv[] ) |
93 | 93 | ; |
94 | 94 | |
95 | 95 | po::setDefaults(opts); |
96 | | const list<const Char*>& argv_unhandled = po::scanArgv(opts, argc, (const Char**) argv); |
| 96 | po::ErrorReporter err; |
| 97 | const list<const Char*>& argv_unhandled = po::scanArgv(opts, argc, (const Char**) argv, err); |
97 | 98 | |
98 | 99 | for (list<const Char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++) |
99 | 100 | { |
… |
… |
Bool TAppDecCfg::parseCfg( Int argc, Char* argv[] ) |
106 | 107 | return false; |
107 | 108 | } |
108 | 109 | |
| 110 | if (err.is_errored) |
| 111 | { |
| 112 | /* errors have already been reported to stderr */ |
| 113 | return false; |
| 114 | } |
| 115 | |
109 | 116 | m_outputColourSpaceConvert = stringToInputColourSpaceConvert(outputColourSpaceConvert, false); |
110 | 117 | if (m_outputColourSpaceConvert>=NUMBER_INPUT_COLOUR_SPACE_CONVERSIONS) |
111 | 118 | { |
diff --git a/source/App/TAppEncoder/TAppEncCfg.cpp b/source/App/TAppEncoder/TAppEncCfg.cpp
index 361132b..3eb6225 100644
a
|
b
|
Bool TAppEncCfg::parseCfg( Int argc, Char* argv[] ) |
1047 | 1047 | opts.addOptions()(cOSS.str(), m_GOPList[i-1], GOPEntry()); |
1048 | 1048 | } |
1049 | 1049 | po::setDefaults(opts); |
1050 | | const list<const Char*>& argv_unhandled = po::scanArgv(opts, argc, (const Char**) argv); |
| 1050 | po::ErrorReporter err; |
| 1051 | const list<const Char*>& argv_unhandled = po::scanArgv(opts, argc, (const Char**) argv, err); |
1051 | 1052 | |
1052 | 1053 | for (list<const Char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++) |
1053 | 1054 | { |
… |
… |
Bool TAppEncCfg::parseCfg( Int argc, Char* argv[] ) |
1061 | 1062 | return false; |
1062 | 1063 | } |
1063 | 1064 | |
| 1065 | if (err.is_errored) |
| 1066 | { |
| 1067 | /* error report has already been printed on stderr */ |
| 1068 | return false; |
| 1069 | } |
| 1070 | |
1064 | 1071 | /* |
1065 | 1072 | * Set any derived parameters |
1066 | 1073 | */ |
diff --git a/source/Lib/TAppCommon/program_options_lite.cpp b/source/Lib/TAppCommon/program_options_lite.cpp
index 96b49a6..02912ea 100644
a
|
b
|
namespace df |
49 | 49 | { |
50 | 50 | namespace program_options_lite |
51 | 51 | { |
| 52 | ErrorReporter default_error_reporter; |
| 53 | |
| 54 | ostream& ErrorReporter::error(const string& where) |
| 55 | { |
| 56 | is_errored = 1; |
| 57 | cerr << where << " error: "; |
| 58 | return cerr; |
| 59 | } |
| 60 | |
| 61 | ostream& ErrorReporter::warn(const string& where) |
| 62 | { |
| 63 | cerr << where << " warning: "; |
| 64 | return cerr; |
| 65 | } |
52 | 66 | |
53 | 67 | Options::~Options() |
54 | 68 | { |
… |
… |
namespace df |
96 | 110 | return OptionSpecific(*this); |
97 | 111 | } |
98 | 112 | |
99 | | static void setOptions(Options::NamesPtrList& opt_list, const string& value) |
| 113 | static void setOptions(Options::NamesPtrList& opt_list, const string& value, ErrorReporter& error_reporter) |
100 | 114 | { |
101 | 115 | /* multiple options may be registered for the same name: |
102 | 116 | * allow each to parse value */ |
103 | 117 | for (Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it) |
104 | 118 | { |
105 | | (*it)->opt->parse(value); |
| 119 | (*it)->opt->parse(value, error_reporter); |
106 | 120 | } |
107 | 121 | } |
108 | 122 | |
… |
… |
namespace df |
235 | 249 | } |
236 | 250 | } |
237 | 251 | |
238 | | bool storePair(Options& opts, bool allow_long, bool allow_short, const string& name, const string& value) |
| 252 | struct OptionWriter |
| 253 | { |
| 254 | OptionWriter(Options& opts, ErrorReporter& err) |
| 255 | : opts(opts), error_reporter(err) |
| 256 | {} |
| 257 | virtual ~OptionWriter() {} |
| 258 | |
| 259 | virtual const string where() = 0; |
| 260 | |
| 261 | bool storePair(bool allow_long, bool allow_short, const string& name, const string& value); |
| 262 | bool storePair(const string& name, const string& value) |
| 263 | { |
| 264 | return storePair(true, true, name, value); |
| 265 | } |
| 266 | |
| 267 | Options& opts; |
| 268 | ErrorReporter& error_reporter; |
| 269 | }; |
| 270 | |
| 271 | bool OptionWriter::storePair(bool allow_long, bool allow_short, const string& name, const string& value) |
239 | 272 | { |
240 | 273 | bool found = false; |
241 | 274 | Options::NamesMap::iterator opt_it; |
… |
… |
namespace df |
260 | 293 | |
261 | 294 | if (!found) |
262 | 295 | { |
263 | | /* not found */ |
264 | | cerr << "Unknown option: `" << name << "' (value:`" << value << "')" << endl; |
| 296 | error_reporter.error(where()) |
| 297 | << "Unknown option `" << name << "' (value:`" << value << "')\n"; |
265 | 298 | return false; |
266 | 299 | } |
267 | 300 | |
268 | | setOptions((*opt_it).second, value); |
| 301 | setOptions((*opt_it).second, value, error_reporter); |
269 | 302 | return true; |
270 | 303 | } |
271 | 304 | |
272 | | bool storePair(Options& opts, const string& name, const string& value) |
| 305 | struct ArgvParser : public OptionWriter |
273 | 306 | { |
274 | | return storePair(opts, true, true, name, value); |
275 | | } |
| 307 | ArgvParser(Options& opts, ErrorReporter& error_reporter) |
| 308 | : OptionWriter(opts, error_reporter) |
| 309 | {} |
| 310 | |
| 311 | const string where() { return "command line"; } |
| 312 | |
| 313 | unsigned parseGNU(unsigned argc, const char* argv[]); |
| 314 | unsigned parseSHORT(unsigned argc, const char* argv[]); |
| 315 | }; |
276 | 316 | |
277 | 317 | /** |
278 | 318 | * returns number of extra arguments consumed |
279 | 319 | */ |
280 | | unsigned parseGNU(Options& opts, unsigned argc, const char* argv[]) |
| 320 | unsigned ArgvParser::parseGNU(unsigned argc, const char* argv[]) |
281 | 321 | { |
282 | 322 | /* gnu style long options can take the forms: |
283 | 323 | * --option=arg |
… |
… |
namespace df |
303 | 343 | } |
304 | 344 | extra_argc_consumed = 1; |
305 | 345 | #endif |
306 | | if(!storePair(opts, true, false, option, "1")) |
| 346 | if(!storePair(true, false, option, "1")) |
307 | 347 | { |
308 | 348 | return 0; |
309 | 349 | } |
… |
… |
namespace df |
312 | 352 | { |
313 | 353 | /* argument occurs after option_sep */ |
314 | 354 | string val = arg.substr(arg_opt_sep + 1); |
315 | | storePair(opts, true, false, option, val); |
| 355 | storePair(true, false, option, val); |
316 | 356 | } |
317 | 357 | |
318 | 358 | return extra_argc_consumed; |
319 | 359 | } |
320 | 360 | |
321 | | unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[]) |
| 361 | unsigned ArgvParser::parseSHORT(unsigned argc, const char* argv[]) |
322 | 362 | { |
323 | 363 | /* short options can take the forms: |
324 | 364 | * --option arg |
… |
… |
namespace df |
333 | 373 | /* xxx, need to handle case where option isn't required */ |
334 | 374 | if (argc == 1) |
335 | 375 | { |
336 | | cerr << "Not processing option without argument `" << option << "'" << endl; |
| 376 | error_reporter.error(where()) |
| 377 | << "Not processing option `" << option << "' without argument\n"; |
337 | 378 | return 0; /* run out of argv for argument */ |
338 | 379 | } |
339 | | storePair(opts, false, true, option, string(argv[1])); |
| 380 | storePair(false, true, option, string(argv[1])); |
340 | 381 | |
341 | 382 | return 1; |
342 | 383 | } |
343 | 384 | |
344 | 385 | list<const char*> |
345 | | scanArgv(Options& opts, unsigned argc, const char* argv[]) |
| 386 | scanArgv(Options& opts, unsigned argc, const char* argv[], ErrorReporter& error_reporter) |
346 | 387 | { |
| 388 | ArgvParser avp(opts, error_reporter); |
| 389 | |
347 | 390 | /* a list for anything that didn't get handled as an option */ |
348 | 391 | list<const char*> non_option_arguments; |
349 | 392 | |
… |
… |
namespace df |
365 | 408 | if (argv[i][1] != '-') |
366 | 409 | { |
367 | 410 | /* handle short (single dash) options */ |
368 | | #if 0 |
369 | | i += parsePOSIX(opts, argc - i, &argv[i]); |
370 | | #else |
371 | | i += parseSHORT(opts, argc - i, &argv[i]); |
372 | | #endif |
| 411 | i += avp.parseSHORT(argc - i, &argv[i]); |
373 | 412 | continue; |
374 | 413 | } |
375 | 414 | |
… |
… |
namespace df |
384 | 423 | } |
385 | 424 | |
386 | 425 | /* handle long (double dash) options */ |
387 | | i += parseGNU(opts, argc - i, &argv[i]); |
| 426 | i += avp.parseGNU(argc - i, &argv[i]); |
388 | 427 | } |
389 | 428 | |
390 | 429 | return non_option_arguments; |
391 | 430 | } |
392 | 431 | |
393 | | void scanLine(Options& opts, string& line) |
| 432 | struct CfgStreamParser : public OptionWriter |
| 433 | { |
| 434 | CfgStreamParser(const string& name, Options& opts, ErrorReporter& error_reporter) |
| 435 | : OptionWriter(opts, error_reporter) |
| 436 | , name(name) |
| 437 | , linenum(0) |
| 438 | {} |
| 439 | |
| 440 | const string name; |
| 441 | int linenum; |
| 442 | const string where() |
| 443 | { |
| 444 | ostringstream os; |
| 445 | os << name << ":" << linenum; |
| 446 | return os.str(); |
| 447 | } |
| 448 | |
| 449 | void scanLine(string& line); |
| 450 | void scanStream(istream& in); |
| 451 | }; |
| 452 | |
| 453 | void CfgStreamParser::scanLine(string& line) |
394 | 454 | { |
395 | 455 | /* strip any leading whitespace */ |
396 | 456 | size_t start = line.find_first_not_of(" \t\n\r"); |
… |
… |
namespace df |
413 | 473 | if (start == string::npos) |
414 | 474 | { |
415 | 475 | /* error: badly formatted line */ |
| 476 | error_reporter.warn(where()) << "line formatting error\n"; |
416 | 477 | return; |
417 | 478 | } |
418 | 479 | if (line[start] != ':') |
419 | 480 | { |
420 | 481 | /* error: badly formatted line */ |
| 482 | error_reporter.warn(where()) << "line formatting error\n"; |
421 | 483 | return; |
422 | 484 | } |
423 | 485 | |
… |
… |
namespace df |
426 | 488 | if (start == string::npos) |
427 | 489 | { |
428 | 490 | /* error: badly formatted line */ |
| 491 | error_reporter.warn(where()) << "line formatting error\n"; |
429 | 492 | return; |
430 | 493 | } |
431 | 494 | |
… |
… |
namespace df |
456 | 519 | else |
457 | 520 | { |
458 | 521 | /* error: no value */ |
| 522 | error_reporter.warn(where()) << "no value found\n"; |
459 | 523 | return; |
460 | 524 | } |
461 | 525 | |
462 | 526 | /* store the value in option */ |
463 | | storePair(opts, true, false, option, value); |
| 527 | storePair(true, false, option, value); |
464 | 528 | } |
465 | 529 | |
466 | | void scanFile(Options& opts, istream& in) |
| 530 | void CfgStreamParser::scanStream(istream& in) |
467 | 531 | { |
468 | 532 | do |
469 | 533 | { |
| 534 | linenum++; |
470 | 535 | string line; |
471 | 536 | getline(in, line); |
472 | | scanLine(opts, line); |
| 537 | scanLine(line); |
473 | 538 | } while(!!in); |
474 | 539 | } |
475 | 540 | |
… |
… |
namespace df |
483 | 548 | } |
484 | 549 | } |
485 | 550 | |
486 | | void parseConfigFile(Options& opts, const string& filename) |
| 551 | void parseConfigFile(Options& opts, const string& filename, ErrorReporter& error_reporter) |
487 | 552 | { |
488 | 553 | ifstream cfgstream(filename.c_str(), ifstream::in); |
489 | 554 | if (!cfgstream) |
490 | 555 | { |
491 | | cerr << "Failed to open config file: `" << filename << "'" << endl; |
492 | | exit(EXIT_FAILURE); |
| 556 | error_reporter.error(filename) << "Failed to open config file\n"; |
| 557 | return; |
493 | 558 | } |
494 | | scanFile(opts, cfgstream); |
| 559 | CfgStreamParser csp(filename, opts, error_reporter); |
| 560 | csp.scanStream(cfgstream); |
495 | 561 | } |
496 | 562 | |
497 | | } |
| 563 | } |
498 | 564 | } |
499 | 565 | |
500 | 566 | //! \} |
diff --git a/source/Lib/TAppCommon/program_options_lite.h b/source/Lib/TAppCommon/program_options_lite.h
index a3a4303..e995419 100644
a
|
b
|
namespace df |
63 | 63 | const char* what() const throw() { return "Option Parse Failure"; } |
64 | 64 | }; |
65 | 65 | |
| 66 | struct ErrorReporter |
| 67 | { |
| 68 | ErrorReporter() : is_errored(0) {} |
| 69 | virtual ~ErrorReporter() {} |
| 70 | virtual std::ostream& error(const std::string& where); |
| 71 | virtual std::ostream& warn(const std::string& where); |
| 72 | bool is_errored; |
| 73 | }; |
| 74 | |
| 75 | extern ErrorReporter default_error_reporter; |
| 76 | |
66 | 77 | void doHelp(std::ostream& out, Options& opts, unsigned columns = 80); |
67 | | unsigned parseGNU(Options& opts, unsigned argc, const char* argv[]); |
68 | | unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[]); |
69 | | std::list<const char*> scanArgv(Options& opts, unsigned argc, const char* argv[]); |
70 | | void scanLine(Options& opts, std::string& line); |
71 | | void scanFile(Options& opts, std::istream& in); |
| 78 | std::list<const char*> scanArgv(Options& opts, unsigned argc, const char* argv[], ErrorReporter& error_reporter = default_error_reporter); |
72 | 79 | void setDefaults(Options& opts); |
73 | | void parseConfigFile(Options& opts, const std::string& filename); |
74 | | bool storePair(Options& opts, const std::string& name, const std::string& value); |
| 80 | void parseConfigFile(Options& opts, const std::string& filename, ErrorReporter& error_reporter = default_error_reporter); |
75 | 81 | |
76 | 82 | /** OptionBase: Virtual base class for storing information relating to a |
77 | 83 | * specific option This base class describes common elements. Type specific |
… |
… |
namespace df |
85 | 91 | virtual ~OptionBase() {} |
86 | 92 | |
87 | 93 | /* parse argument arg, to obtain a value for the option */ |
88 | | virtual void parse(const std::string& arg) = 0; |
| 94 | virtual void parse(const std::string& arg, ErrorReporter&) = 0; |
89 | 95 | /* set the argument to the default value */ |
90 | 96 | virtual void setDefault() = 0; |
91 | 97 | |
… |
… |
namespace df |
101 | 107 | : OptionBase(name, desc), opt_storage(storage), opt_default_val(default_val) |
102 | 108 | {} |
103 | 109 | |
104 | | void parse(const std::string& arg); |
| 110 | void parse(const std::string& arg, ErrorReporter&); |
105 | 111 | |
106 | 112 | void setDefault() |
107 | 113 | { |
… |
… |
namespace df |
115 | 121 | /* Generic parsing */ |
116 | 122 | template<typename T> |
117 | 123 | inline void |
118 | | Option<T>::parse(const std::string& arg) |
| 124 | Option<T>::parse(const std::string& arg, ErrorReporter&) |
119 | 125 | { |
120 | 126 | std::istringstream arg_ss (arg,std::istringstream::in); |
121 | 127 | arg_ss.exceptions(std::ios::failbit); |
… |
… |
namespace df |
133 | 139 | * first word */ |
134 | 140 | template<> |
135 | 141 | inline void |
136 | | Option<std::string>::parse(const std::string& arg) |
| 142 | Option<std::string>::parse(const std::string& arg, ErrorReporter&) |
137 | 143 | { |
138 | 144 | opt_storage = arg; |
139 | 145 | } |
… |
… |
namespace df |
141 | 147 | /** Option class for argument handling using a user provided function */ |
142 | 148 | struct OptionFunc : public OptionBase |
143 | 149 | { |
144 | | typedef void (Func)(Options&, const std::string&); |
| 150 | typedef void (Func)(Options&, const std::string&, ErrorReporter&); |
145 | 151 | |
146 | 152 | OptionFunc(const std::string& name, Options& parent_, Func *func_, const std::string& desc) |
147 | 153 | : OptionBase(name, desc), parent(parent_), func(func_) |
148 | 154 | {} |
149 | 155 | |
150 | | void parse(const std::string& arg) |
| 156 | void parse(const std::string& arg, ErrorReporter& error_reporter) |
151 | 157 | { |
152 | | func(parent, arg); |
| 158 | func(parent, arg, error_reporter); |
153 | 159 | } |
154 | 160 | |
155 | 161 | void setDefault() |
… |
… |
namespace df |
159 | 165 | |
160 | 166 | private: |
161 | 167 | Options& parent; |
162 | | void (*func)(Options&, const std::string&); |
| 168 | Func* func; |
163 | 169 | }; |
164 | 170 | |
165 | 171 | class OptionSpecific; |