Ticket #1343: 0001-program_options-report-unknown-options-as-error.patch

File 0001-program_options-report-unknown-options-as-error.patch, 14.6 KB (added by davidf, 9 years ago)
  • source/App/TAppDecoder/TAppDecCfg.cpp

    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[] ) 
    9393  ;
    9494
    9595  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);
    9798
    9899  for (list<const Char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++)
    99100  {
    Bool TAppDecCfg::parseCfg( Int argc, Char* argv[] ) 
    106107    return false;
    107108  }
    108109
     110  if (err.is_errored)
     111  {
     112    /* errors have already been reported to stderr */
     113    return false;
     114  }
     115
    109116  m_outputColourSpaceConvert = stringToInputColourSpaceConvert(outputColourSpaceConvert, false);
    110117  if (m_outputColourSpaceConvert>=NUMBER_INPUT_COLOUR_SPACE_CONVERSIONS)
    111118  {
  • source/App/TAppEncoder/TAppEncCfg.cpp

    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[] ) 
    10471047    opts.addOptions()(cOSS.str(), m_GOPList[i-1], GOPEntry());
    10481048  }
    10491049  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);
    10511052
    10521053  for (list<const Char*>::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++)
    10531054  {
    Bool TAppEncCfg::parseCfg( Int argc, Char* argv[] ) 
    10611062    return false;
    10621063  }
    10631064
     1065  if (err.is_errored)
     1066  {
     1067    /* error report has already been printed on stderr */
     1068    return false;
     1069  }
     1070
    10641071  /*
    10651072   * Set any derived parameters
    10661073   */
  • source/Lib/TAppCommon/program_options_lite.cpp

    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 
    4949{
    5050  namespace program_options_lite
    5151  {
     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    }
    5266
    5367    Options::~Options()
    5468    {
    namespace df 
    96110      return OptionSpecific(*this);
    97111    }
    98112
    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)
    100114    {
    101115      /* multiple options may be registered for the same name:
    102116       *   allow each to parse value */
    103117      for (Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it)
    104118      {
    105         (*it)->opt->parse(value);
     119        (*it)->opt->parse(value, error_reporter);
    106120      }
    107121    }
    108122
    namespace df 
    235249      }
    236250    }
    237251
    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)
    239272    {
    240273      bool found = false;
    241274      Options::NamesMap::iterator opt_it;
    namespace df 
    260293
    261294      if (!found)
    262295      {
    263         /* not found */
    264         cerr << "Unknown option: `" << name << "' (value:`" << value << "')" << endl;
     296        error_reporter.error(where())
     297          << "Unknown option `" << name << "' (value:`" << value << "')\n";
    265298        return false;
    266299      }
    267300
    268       setOptions((*opt_it).second, value);
     301      setOptions((*opt_it).second, value, error_reporter);
    269302      return true;
    270303    }
    271304
    272     bool storePair(Options& opts, const string& name, const string& value)
     305    struct ArgvParser : public OptionWriter
    273306    {
    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    };
    276316
    277317    /**
    278318     * returns number of extra arguments consumed
    279319     */
    280     unsigned parseGNU(Options& opts, unsigned argc, const char* argv[])
     320    unsigned ArgvParser::parseGNU(unsigned argc, const char* argv[])
    281321    {
    282322      /* gnu style long options can take the forms:
    283323       *  --option=arg
    namespace df 
    303343        }
    304344        extra_argc_consumed = 1;
    305345#endif
    306         if(!storePair(opts, true, false, option, "1"))
     346        if(!storePair(true, false, option, "1"))
    307347        {
    308348          return 0;
    309349        }
    namespace df 
    312352      {
    313353        /* argument occurs after option_sep */
    314354        string val = arg.substr(arg_opt_sep + 1);
    315         storePair(opts, true, false, option, val);
     355        storePair(true, false, option, val);
    316356      }
    317357
    318358      return extra_argc_consumed;
    319359    }
    320360
    321     unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[])
     361    unsigned ArgvParser::parseSHORT(unsigned argc, const char* argv[])
    322362    {
    323363      /* short options can take the forms:
    324364       *  --option arg
    namespace df 
    333373      /* xxx, need to handle case where option isn't required */
    334374      if (argc == 1)
    335375      {
    336         cerr << "Not processing option without argument `" << option << "'" << endl;
     376        error_reporter.error(where())
     377          << "Not processing option `" << option << "' without argument\n";
    337378        return 0; /* run out of argv for argument */
    338379      }
    339       storePair(opts, false, true, option, string(argv[1]));
     380      storePair(false, true, option, string(argv[1]));
    340381
    341382      return 1;
    342383    }
    343384
    344385    list<const char*>
    345     scanArgv(Options& opts, unsigned argc, const char* argv[])
     386    scanArgv(Options& opts, unsigned argc, const char* argv[], ErrorReporter& error_reporter)
    346387    {
     388      ArgvParser avp(opts, error_reporter);
     389
    347390      /* a list for anything that didn't get handled as an option */
    348391      list<const char*> non_option_arguments;
    349392
    namespace df 
    365408        if (argv[i][1] != '-')
    366409        {
    367410          /* 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]);
    373412          continue;
    374413        }
    375414
    namespace df 
    384423        }
    385424
    386425        /* handle long (double dash) options */
    387         i += parseGNU(opts, argc - i, &argv[i]);
     426        i += avp.parseGNU(argc - i, &argv[i]);
    388427      }
    389428
    390429      return non_option_arguments;
    391430    }
    392431
    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)
    394454    {
    395455      /* strip any leading whitespace */
    396456      size_t start = line.find_first_not_of(" \t\n\r");
    namespace df 
    413473      if (start == string::npos)
    414474      {
    415475        /* error: badly formatted line */
     476        error_reporter.warn(where()) << "line formatting error\n";
    416477        return;
    417478      }
    418479      if (line[start] != ':')
    419480      {
    420481        /* error: badly formatted line */
     482        error_reporter.warn(where()) << "line formatting error\n";
    421483        return;
    422484      }
    423485
    namespace df 
    426488      if (start == string::npos)
    427489      {
    428490        /* error: badly formatted line */
     491        error_reporter.warn(where()) << "line formatting error\n";
    429492        return;
    430493      }
    431494
    namespace df 
    456519      else
    457520      {
    458521        /* error: no value */
     522        error_reporter.warn(where()) << "no value found\n";
    459523        return;
    460524      }
    461525
    462526      /* store the value in option */
    463       storePair(opts, true, false, option, value);
     527      storePair(true, false, option, value);
    464528    }
    465529
    466     void scanFile(Options& opts, istream& in)
     530    void CfgStreamParser::scanStream(istream& in)
    467531    {
    468532      do
    469533      {
     534        linenum++;
    470535        string line;
    471536        getline(in, line);
    472         scanLine(opts, line);
     537        scanLine(line);
    473538      } while(!!in);
    474539    }
    475540
    namespace df 
    483548      }
    484549    }
    485550
    486     void parseConfigFile(Options& opts, const string& filename)
     551    void parseConfigFile(Options& opts, const string& filename, ErrorReporter& error_reporter)
    487552    {
    488553      ifstream cfgstream(filename.c_str(), ifstream::in);
    489554      if (!cfgstream)
    490555      {
    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;
    493558      }
    494       scanFile(opts, cfgstream);
     559      CfgStreamParser csp(filename, opts, error_reporter);
     560      csp.scanStream(cfgstream);
    495561    }
    496562
    497   }
     563}
    498564}
    499565
    500566//! \}
  • source/Lib/TAppCommon/program_options_lite.h

    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 
    6363      const char* what() const throw() { return "Option Parse Failure"; }
    6464    };
    6565
     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
    6677    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);
    7279    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);
    7581
    7682    /** OptionBase: Virtual base class for storing information relating to a
    7783     * specific option This base class describes common elements.  Type specific
    namespace df 
    8591      virtual ~OptionBase() {}
    8692
    8793      /* 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;
    8995      /* set the argument to the default value */
    9096      virtual void setDefault() = 0;
    9197
    namespace df 
    101107      : OptionBase(name, desc), opt_storage(storage), opt_default_val(default_val)
    102108      {}
    103109
    104       void parse(const std::string& arg);
     110      void parse(const std::string& arg, ErrorReporter&);
    105111
    106112      void setDefault()
    107113      {
    namespace df 
    115121    /* Generic parsing */
    116122    template<typename T>
    117123    inline void
    118     Option<T>::parse(const std::string& arg)
     124    Option<T>::parse(const std::string& arg, ErrorReporter&)
    119125    {
    120126      std::istringstream arg_ss (arg,std::istringstream::in);
    121127      arg_ss.exceptions(std::ios::failbit);
    namespace df 
    133139     * first word */
    134140    template<>
    135141    inline void
    136     Option<std::string>::parse(const std::string& arg)
     142    Option<std::string>::parse(const std::string& arg, ErrorReporter&)
    137143    {
    138144      opt_storage = arg;
    139145    }
    namespace df 
    141147    /** Option class for argument handling using a user provided function */
    142148    struct OptionFunc : public OptionBase
    143149    {
    144       typedef void (Func)(Options&, const std::string&);
     150      typedef void (Func)(Options&, const std::string&, ErrorReporter&);
    145151
    146152      OptionFunc(const std::string& name, Options& parent_, Func *func_, const std::string& desc)
    147153      : OptionBase(name, desc), parent(parent_), func(func_)
    148154      {}
    149155
    150       void parse(const std::string& arg)
     156      void parse(const std::string& arg, ErrorReporter& error_reporter)
    151157      {
    152         func(parent, arg);
     158        func(parent, arg, error_reporter);
    153159      }
    154160
    155161      void setDefault()
    namespace df 
    159165
    160166    private:
    161167      Options& parent;
    162       void (*func)(Options&, const std::string&);
     168      Func* func;
    163169    };
    164170
    165171    class OptionSpecific;