// Copyright (C) 2003 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_CMD_LINE_PARSER_KERNEl_1_ #define DLIB_CMD_LINE_PARSER_KERNEl_1_ #include "cmd_line_parser_kernel_abstract.h" #include "../algs.h" #include #include #include "../interfaces/enumerable.h" #include "../interfaces/cmd_line_parser_option.h" #include "../assert.h" #include "../string.h" namespace dlib { template < typename charT, typename map, typename sequence, typename sequence2 > class cmd_line_parser_kernel_1 : public enumerable > { /*! REQUIREMENTS ON map is an implementation of map/map_kernel_abstract.h is instantiated to map items of type std::basic_string to void* REQUIREMENTS ON sequence is an implementation of sequence/sequence_kernel_abstract.h and is instantiated with std::basic_string REQUIREMENTS ON sequence2 is an implementation of sequence/sequence_kernel_abstract.h and is instantiated with std::basic_string* INITIAL VALUE options.size() == 0 argv.size() == 0 have_parsed_line == false CONVENTION have_parsed_line == parsed_line() argv[index] == operator[](index) argv.size() == number_of_arguments() *((option_t*)options[name]) == option(name) options.is_in_domain(name) == option_is_defined(name) !*/ public: typedef charT char_type; typedef std::basic_string string_type; typedef cmd_line_parser_option option_type; // exception class class cmd_line_parse_error : public dlib::error { void set_info_string ( ) { std::ostringstream sout; switch (type) { case EINVALID_OPTION: sout << "Command line error: '" << narrow(item) << "' is not a valid option."; break; case ETOO_FEW_ARGS: if (num > 1) { sout << "Command line error: The '" << narrow(item) << "' option requires " << num << " arguments."; } else { sout << "Command line error: The '" << narrow(item) << "' option requires " << num << " argument."; } break; case ETOO_MANY_ARGS: sout << "Command line error: The '" << narrow(item) << "' option does not take any arguments.\n"; break; default: sout << "Command line error."; break; } const_cast(info) = wrap_string(sout.str(),0,0); } public: cmd_line_parse_error( error_type t, const std::basic_string& _item ) : dlib::error(t), item(_item), num(0) { set_info_string();} cmd_line_parse_error( error_type t, const std::basic_string& _item, unsigned long _num ) : dlib::error(t), item(_item), num(_num) { set_info_string();} cmd_line_parse_error( ) : dlib::error(), item(), num(0) { set_info_string();} ~cmd_line_parse_error() throw() {} const std::basic_string item; const unsigned long num; }; private: class option_t : public cmd_line_parser_option { /*! INITIAL VALUE options.size() == 0 CONVENTION name_ == name() description_ == description() number_of_arguments_ == number_of_arguments() options[N][arg] == argument(arg,N) num_present == count() !*/ friend class cmd_line_parser_kernel_1; public: const std::basic_string& name ( ) const { return name_; } const std::basic_string& group_name ( ) const { return group_name_; } const std::basic_string& description ( ) const { return description_; } unsigned long number_of_arguments( ) const { return number_of_arguments_; } unsigned long count ( ) const { return num_present; } const std::basic_string& argument ( unsigned long arg, unsigned long N ) const { // make sure requires clause is not broken DLIB_CASSERT( N < count() && arg < number_of_arguments(), "\tconst string_type& cmd_line_parser_option::argument(unsigned long,unsigned long)" << "\n\tInvalid arguments were given to this function." << "\n\tthis: " << this << "\n\tN: " << N << "\n\targ: " << arg << "\n\tname(): " << narrow(name()) << "\n\tcount(): " << count() << "\n\tnumber_of_arguments(): " << number_of_arguments() ); return options[N][arg]; } protected: option_t ( ) : num_present(0) {} ~option_t() { clear(); } private: void clear() /*! ensures - #count() == 0 - clears everything out of options and frees memory !*/ { for (unsigned long i = 0; i < options.size(); ++i) { delete [] options[i]; } options.clear(); num_present = 0; } // data members std::basic_string name_; std::basic_string group_name_; std::basic_string description_; sequence2 options; unsigned long number_of_arguments_; unsigned long num_present; // restricted functions option_t(option_t&); // copy constructor option_t& operator=(option_t&); // assignment operator }; // -------------------------- public: cmd_line_parser_kernel_1 ( ); virtual ~cmd_line_parser_kernel_1 ( ); void clear( ); void parse ( int argc, const charT** argv ); void parse ( int argc, charT** argv ) { parse(argc, const_cast(argv)); } bool parsed_line( ) const; bool option_is_defined ( const string_type& name ) const; void add_option ( const string_type& name, const string_type& description, unsigned long number_of_arguments = 0 ); void set_group_name ( const string_type& group_name ); string_type get_group_name ( ) const { return group_name; } const cmd_line_parser_option& option ( const string_type& name ) const; unsigned long number_of_arguments( ) const; const string_type& operator[] ( unsigned long index ) const; void swap ( cmd_line_parser_kernel_1& item ); // functions from the enumerable interface bool at_start ( ) const { return options.at_start(); } void reset ( ) const { options.reset(); } bool current_element_valid ( ) const { return options.current_element_valid(); } const cmd_line_parser_option& element ( ) const { return *static_cast*>(options.element().value()); } cmd_line_parser_option& element ( ) { return *static_cast*>(options.element().value()); } bool move_next ( ) const { return options.move_next(); } unsigned long size ( ) const { return options.size(); } private: // data members map options; sequence argv; bool have_parsed_line; string_type group_name; // restricted functions cmd_line_parser_kernel_1(cmd_line_parser_kernel_1&); // copy constructor cmd_line_parser_kernel_1& operator=(cmd_line_parser_kernel_1&); // assignment operator }; // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > inline void swap ( cmd_line_parser_kernel_1& a, cmd_line_parser_kernel_1& b ) { a.swap(b); } // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // member function definitions // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > cmd_line_parser_kernel_1:: cmd_line_parser_kernel_1 ( ) : have_parsed_line(false) { } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > cmd_line_parser_kernel_1:: ~cmd_line_parser_kernel_1 ( ) { // delete all option_t objects in options options.reset(); while (options.move_next()) { delete static_cast(options.element().value()); } } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > void cmd_line_parser_kernel_1:: clear( ) { have_parsed_line = false; argv.clear(); // delete all option_t objects in options options.reset(); while (options.move_next()) { delete static_cast(options.element().value()); } options.clear(); reset(); } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > void cmd_line_parser_kernel_1:: parse ( int argc_, const charT** argv ) { using namespace std; // make sure there aren't any arguments hanging around from the last time // parse was called this->argv.clear(); // make sure that the options have been cleared of any arguments since // the last time parse() was called if (have_parsed_line) { options.reset(); while (options.move_next()) { static_cast(options.element().value())->clear(); } options.reset(); } // this tells us if we have seen -- on the command line all by itself // or not. bool escape = false; const unsigned long argc = static_cast(argc_); try { for (unsigned long i = 1; i < argc; ++i) { if (argv[i][0] == _dT(charT,'-') && !escape) { // we are looking at the start of an option // -------------------------------------------------------------------- if (argv[i][1] == _dT(charT,'-')) { // we are looking at the start of a "long named" option string_type temp = &argv[i][2]; string_type first_argument; typename string_type::size_type pos = temp.find_first_of(_dT(charT,'=')); // This variable will be 1 if there is an argument supplied via the = sign // and 0 otherwise. unsigned long extra_argument = 0; if (pos != string_type::npos) { // there should be an extra argument extra_argument = 1; first_argument = temp.substr(pos+1); temp = temp.substr(0,pos); } // make sure this name is defined if (!options.is_in_domain(temp)) { // the long name is not a valid option if (argv[i][2] == _dT(charT,'\0')) { // there was nothing after the -- on the command line escape = true; continue; } else { // there was something after the command line but it // wasn't a valid option throw cmd_line_parse_error(EINVALID_OPTION,temp); } } option_t* o = static_cast(options[temp]); // check the number of arguments after this option and make sure // it is correct if (argc + extra_argument <= o->number_of_arguments() + i) { // there are too few arguments throw cmd_line_parse_error(ETOO_FEW_ARGS,temp,o->number_of_arguments()); } if (extra_argument && first_argument.size() == 0 ) { // if there would be exactly the right number of arguments if // the first_argument wasn't empty if (argc == o->number_of_arguments() + i) throw cmd_line_parse_error(ETOO_FEW_ARGS,temp,o->number_of_arguments()); else { // in this case we just ignore the trailing = and parse everything // the same. extra_argument = 0; } } // you can't force an option that doesn't have any arguments to take // one by using the --option=arg syntax if (extra_argument == 1 && o->number_of_arguments() == 0) { throw cmd_line_parse_error(ETOO_MANY_ARGS,temp); } // at this point we know that the option is ok and we should // populate its options object if (o->number_of_arguments() > 0) { string_type* stemp = new string_type[o->number_of_arguments()]; unsigned long j = 0; // add the argument after the = sign if one is present if (extra_argument) { stemp[0] = first_argument; ++j; } for (; j < o->number_of_arguments(); ++j) { stemp[j] = argv[i+j+1-extra_argument]; } o->options.add(o->options.size(),stemp); } o->num_present += 1; // adjust the value of i to account for the arguments to // this option i += o->number_of_arguments() - extra_argument; } // -------------------------------------------------------------------- else { // we are looking at the start of a list of a single char options // make sure there is something in this string other than - if (argv[i][1] == _dT(charT,'\0')) { throw cmd_line_parse_error(); } string_type temp = &argv[i][1]; const typename string_type::size_type num = temp.size(); for (unsigned long k = 0; k < num; ++k) { string_type name; // Doing this instead of name = temp[k] seems to avoid a bug in g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2 // which results in name[0] having the wrong value. name.resize(1); name[0] = temp[k]; // make sure this name is defined if (!options.is_in_domain(name)) { // the name is not a valid option throw cmd_line_parse_error(EINVALID_OPTION,name); } option_t* o = static_cast(options[name]); // if there are chars immediately following this option int delta = 0; if (num != k+1) { delta = 1; } // check the number of arguments after this option and make sure // it is correct if (argc + delta <= o->number_of_arguments() + i) { // there are too few arguments std::ostringstream sout; throw cmd_line_parse_error(ETOO_FEW_ARGS,name,o->number_of_arguments()); } o->num_present += 1; // at this point we know that the option is ok and we should // populate its options object if (o->number_of_arguments() > 0) { string_type* stemp = new string_type[o->number_of_arguments()]; if (delta == 1) { temp = &argv[i][2+k]; k = (unsigned long)num; // this ensures that the argument to this // option isn't going to be treated as a // list of options stemp[0] = temp; } for (unsigned long j = 0; j < o->number_of_arguments()-delta; ++j) { stemp[j+delta] = argv[i+j+1]; } o->options.add(o->options.size(),stemp); // adjust the value of i to account for the arguments to // this option i += o->number_of_arguments()-delta; } } // for (unsigned long k = 0; k < num; ++k) } // -------------------------------------------------------------------- } else { // this is just a normal argument string_type temp = argv[i]; this->argv.add(this->argv.size(),temp); } } have_parsed_line = true; } catch (...) { have_parsed_line = false; // clear all the option objects options.reset(); while (options.move_next()) { static_cast(options.element().value())->clear(); } options.reset(); throw; } } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > bool cmd_line_parser_kernel_1:: parsed_line( ) const { return have_parsed_line; } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > bool cmd_line_parser_kernel_1:: option_is_defined ( const string_type& name ) const { return options.is_in_domain(name); } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > void cmd_line_parser_kernel_1:: set_group_name ( const string_type& group_name_ ) { group_name = group_name_; } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > void cmd_line_parser_kernel_1:: add_option ( const string_type& name, const string_type& description, unsigned long number_of_arguments ) { option_t* temp = new option_t; try { temp->name_ = name; temp->group_name_ = group_name; temp->description_ = description; temp->number_of_arguments_ = number_of_arguments; void* t = temp; string_type n(name); options.add(n,t); }catch (...) { delete temp; throw;} } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > const cmd_line_parser_option& cmd_line_parser_kernel_1:: option ( const string_type& name ) const { return *static_cast*>(options[name]); } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > unsigned long cmd_line_parser_kernel_1:: number_of_arguments( ) const { return argv.size(); } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > const std::basic_string& cmd_line_parser_kernel_1:: operator[] ( unsigned long index ) const { return argv[index]; } // ---------------------------------------------------------------------------------------- template < typename charT, typename map, typename sequence, typename sequence2 > void cmd_line_parser_kernel_1:: swap ( cmd_line_parser_kernel_1& item ) { options.swap(item.options); argv.swap(item.argv); exchange(have_parsed_line,item.have_parsed_line); } // ---------------------------------------------------------------------------------------- } #endif // DLIB_CMD_LINE_PARSER_KERNEl_1_