command_line.hh

Go to the documentation of this file.
00001 //
00002 // $Id: command_line.hh,v 1.7 2003/12/26 23:27:07 cholm Exp $ 
00003 //  
00004 //  optionmm::command_line
00005 //  Copyright (C) 2002 Christian Holm Christensen <cholm@nbi.dk> 
00006 //
00007 //  This library is free software; you can redistribute it and/or 
00008 //  modify it under the terms of the GNU Lesser General Public License 
00009 //  as published by the Free Software Foundation; either version 2.1 
00010 //  of the License, or (at your option) any later version. 
00011 //
00012 //  This library is distributed in the hope that it will be useful, 
00013 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
00014 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
00015 //  Lesser General Public License for more details. 
00016 // 
00017 //  You should have received a copy of the GNU Lesser General Public 
00018 //  License along with this library; if not, write to the Free 
00019 //  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
00020 //  02111-1307 USA 
00021 //
00022 #ifndef OPTIONMM_command_line
00023 #define OPTIONMM_command_line
00024 #ifndef OPTIONMM_optionmm
00025 #include <optionmm/option.hh>
00026 #endif
00027 #ifndef __VECTOR__
00028 #include <vector>
00029 #endif
00030 #ifndef __STRING__
00031 #include <string>
00032 #endif
00033 #ifndef __IOSTREAM__
00034 #include <iostream>
00035 #endif
00036 
00037 /** @file   command_line.hh
00038     @author Christian Holm
00039     @date   Sat Dec 28 19:02:59 2002
00040     @brief  Command line parser */
00041 
00042 namespace optionmm
00043 {
00044   /** @class default_error_handler command_line.hh <optionmm/command_line.hh>
00045       @brief The default error handler for the command line. 
00046 
00047       Client programs can define similar structures or classes, and
00048       pass them as a template parameter to basic_command_line.
00049   */
00050   struct default_error_handler 
00051   {
00052     /** Handle an unknown short option.
00053         @param c The short option
00054         @param progname name of the program
00055         @return true if basic_command_line<>::process should fail,
00056         false if it should go on. */
00057     bool on_unknown(char c, const std::string& progname) 
00058     {
00059       std::cerr << "Option `-" << c << "' unknown, try `" 
00060                 << progname << " --help'" << std::endl;
00061       return true;
00062     }
00063     /** Handle an unknown long option.
00064         @param str The long option
00065         @param progname name of the program
00066         @return true if basic_command_line<>::process should fail,
00067         false if it should go on. */
00068     bool on_unknown(const std::string& str, const std::string& progname)
00069     {
00070       std::cerr << "Option `" << str << "' unknown, try `" 
00071                 << progname << " --help'" << std::endl;
00072       return true;
00073     }
00074     /** Handle a bad handling of an argument to an option.  Short
00075         version. 
00076         @param o The option
00077         @param is_short Whether the short form was given or not.
00078         @param progname The program invocation name.
00079         @return true if basic_command_line<>::process should fail,
00080         false if it should go on. */
00081     template <typename Option>
00082     bool on_missing_argument(Option& o, 
00083                              bool is_short, 
00084                              const std::string& progname) 
00085     {
00086       std::cerr << "Option ";
00087       if (is_short) std::cerr << "-" << o.short_name();
00088       else          std::cerr << "--" << o.long_name();
00089       std::cerr << " need an argument, try " << progname << "  --help" 
00090                  << std::endl; 
00091       return true;
00092     }
00093     /** Handle a bad handling of an argument to an option.  Short
00094         version. 
00095         @param o The option
00096         @param is_short Whether the short form was given or not.
00097         @param progname The program invocation name.
00098         @return true if basic_command_line<>::process should fail,
00099         false if it should go on. */
00100     template <typename Option>
00101     bool on_bad_argument(Option& o, 
00102                          bool is_short, 
00103                          const std::string& progname) 
00104     {
00105       std::cerr << "Bad argument to option ";
00106       if (is_short) std::cerr << "-" << o.short_name();
00107       else          std::cerr << "--" << o.long_name();
00108       std::cerr << ", try " << progname << "  --help" << std::endl; 
00109       return true;
00110     }
00111   };
00112 
00113   //==================================================================
00114   /** @class basic_command_line command_line.hh <optionmm/command_line.hh>
00115       @brief Command line parser.
00116       @param ErrorHandler A policy class to handle bad command line
00117       options and arguments.  See also default_error_handler.
00118 
00119       The command line option manager is straight forward to use. 
00120       First, one declares on instance of the manager, parsing it 
00121       the proper arguments 
00122       
00123       @dontinclude demo.cc 
00124       @skip int
00125       @until ""
00126 
00127       Then, one creates option objects and add them to the manager. 
00128       @until if 
00129 
00130       After that, the manager should process the command line. 
00131       @until if 
00132 
00133       If the application should exit if the help option was given,
00134       then it may do so, and similar for the version option. 
00135       @until for 
00136 
00137       After processing, the option objects hold the value(s) of the
00138       command line options given. 
00139       @until return
00140    */
00141   template <typename ErrorHandler=default_error_handler>
00142   class basic_command_line : public ErrorHandler
00143   {
00144   public:
00145     /// The container of options. 
00146     typedef std::vector<option_base*> option_list;
00147   private:
00148     /// List of options 
00149     option_list _options;
00150     /// The program name (sans directory part)
00151     std::string _program_name;
00152     /// The number of arguments cached from command line 
00153     int& _argc;
00154     /// The command line arguments. 
00155     char** _argv;
00156     /// The title of the application 
00157     std::string _title;
00158     /// The version number of the application
00159     std::string _version; 
00160     /// The copyright of the application 
00161     std::string _copyright;
00162     /// The copyright of the application 
00163     std::string _usage;
00164     /// The help option. 
00165     basic_option<bool,false,false> _help_option;
00166     /// The version information option. 
00167     basic_option<bool,false,false> _version_option; 
00168     /** Handle the i'th short options 
00169         @return true on success, false oterwise. */
00170     bool handle_short(int& i);
00171     /** Handle the i'th long option
00172         @return true on success, false oterwise. */
00173     bool handle_long(int& i);
00174     /// Clean up command line 
00175     void cleanup();
00176   public:
00177     /** Constructor. 
00178         @param argc reference to the number of command line options
00179         from @c main
00180         @param argv the command line options from main 
00181         @param title the title of the application.
00182         @param version the version of the application 
00183         @param copy The copyright of the application.
00184         @param usage What to print as syntax (may be empty) */
00185     basic_command_line(const std::string title, 
00186                        const std::string& version, 
00187                        const std::string& copy, 
00188                        const std::string& usage, 
00189                        int&               argc, 
00190                        char**             argv);
00191     /** Add an option to the command line 
00192         @param option The option object to add to the manager. */
00193     template <typename T, bool a, bool m>
00194     void add(basic_option<T,a,m>& option) { _options.push_back(&option); }
00195     /** Print a help message 
00196         If the manager saw the @c --help option, then print the
00197         help message to stream @a o, and return true. 
00198         One can use the return value to jump out of the main program
00199         if the @c --help option was given, like 
00200         @code 
00201           int main(int argc, char** argv) { 
00202             using namespace optionmm; 
00203             option_manager om("foo", "1.0", "bar", "", argc, argv);
00204             ...
00205             if (!om.process()) return 1;
00206             if (om.help()) return 0;
00207             ...
00208           }
00209         @endcode 
00210         @return true if the help option was given. */
00211     bool help(std::ostream& o=std::cout);
00212     /** Print version information. 
00213         If the manager saw the @c --version option, then print the
00214         version information to stream @a o, and return true. 
00215         @param o The stream to write information to. 
00216         @return true if version option was given. */
00217     bool version(std::ostream& o=std::cout);
00218     /** Process the command line 
00219         @return true on success, false oterwise. */
00220     bool process();
00221     /** Get the application name */
00222     const std::string& program_name() const { return _program_name; }
00223     /** Get the application title */
00224     const std::string& title() const { return _title; }
00225     /** Get the application version */
00226     const std::string& version() const { return _title; }
00227     /** Get the application copyright */
00228     const std::string& copyright() const { return _copyright; }
00229   };
00230 
00231   //____________________________________________________________________
00232   template <typename ErrorHandler>
00233   inline
00234   basic_command_line<ErrorHandler>::
00235   basic_command_line(const std::string  title,
00236                      const std::string& version, 
00237                      const std::string& copy,
00238                      const std::string& usage,
00239                      int&               argc, 
00240                      char**             argv)
00241     : _program_name(argv[0]), 
00242       _argc(argc), 
00243       _argv(argv),
00244       _title(title), 
00245       _version(version), 
00246       _copyright(copy), 
00247       _usage(usage),
00248       _help_option('h',"help","Show this help",false), 
00249       _version_option('\0',"version", "Show version information",false)
00250   {
00251     std::string::size_type slash = _program_name.find_last_of('/');
00252     _program_name.erase(0, slash+1);
00253     add(_help_option);
00254     add(_version_option);
00255   }
00256 
00257   //____________________________________________________________________
00258   template <typename ErrorHandler>
00259   inline
00260   bool
00261   basic_command_line<ErrorHandler>::help(std::ostream& out) 
00262   {
00263     if (!_help_option.value()) return false;
00264 
00265     _version_option.push_arg(_help_option.position());
00266     version();
00267     out << std::endl << "Usage: " << _program_name;
00268     if (_usage.empty()) out << " [OPTIONS]";
00269     else                out << " " << _usage;
00270     out << std::endl << std::endl; 
00271 
00272     /// Figure out the longest long_name
00273     int ll = 0;
00274     for (option_list::iterator o = _options.begin(); o != _options.end(); ++o) 
00275       if ((*o)->long_name().length() >= ll) ll = (*o)->long_name().length();
00276     for (option_list::iterator p = _options.begin(); 
00277          p != _options.end(); ++p) {
00278       out << "    ";
00279       (*p)->print(ll, out);
00280       out << std::endl;
00281     }
00282     out << std::endl;
00283     return true;
00284   }
00285 
00286   //____________________________________________________________________
00287   template <typename ErrorHandler>
00288   inline
00289   bool
00290   basic_command_line<ErrorHandler>::version(std::ostream& o) 
00291   { 
00292     if (!_version_option.value()) return false;
00293     o << _title << " version " << _version << std::endl
00294       << _copyright << std::endl;
00295     return true;
00296   }
00297 
00298   //____________________________________________________________________
00299   template <typename ErrorHandler>
00300   inline
00301   bool
00302   basic_command_line<ErrorHandler>::process() 
00303   {
00304     for (int i = 1; i < _argc; i++) {
00305       if (_argv[i] && _argv[i][0] == '-') {
00306         // Got an option
00307         bool ret;
00308         if (_argv[i][1] != '-') ret = handle_short(i);
00309         else                    ret = handle_long(i);
00310         if (!ret) return false;
00311       }
00312     }
00313     cleanup();
00314     return true;
00315   } 
00316 
00317   //____________________________________________________________________
00318   template <typename ErrorHandler>
00319   inline
00320   void
00321   basic_command_line<ErrorHandler>::cleanup() 
00322   {
00323     int n = 1;
00324     for (int i = 1; i < _argc; i++) {
00325       int j = i;
00326       while (!_argv[j] && j < _argc-1) { j++; }
00327       if (i != j) {
00328         _argv[i] = _argv[j]; 
00329         _argv[j] = 0;
00330       }
00331       if (_argv[i]) n++;
00332     }
00333     _argc = n;
00334   }
00335 
00336 
00337   //____________________________________________________________________
00338   template <typename ErrorHandler>
00339   inline
00340   bool
00341   basic_command_line<ErrorHandler>::handle_short(int& i) 
00342   {
00343     int  j     = 1;
00344     int  ret   = 0;
00345     bool gotit = false;
00346     while (_argv[i] && _argv[i][j] && _argv[i][j] != '-') {
00347       option_list::iterator o;
00348       for (o = _options.begin(); o < _options.end(); o++) {
00349         char* arg = &(_argv[i][j]);
00350         if ((ret = (*o)->handle(arg, _argv[i+1], i)) 
00351             == option_base::can_handle) {
00352           int k = j;
00353           // Eat away this argument. 
00354           while (_argv[i][k] != '\0') { 
00355             // I really wanted to have `_argv[i][++k]' on the right hand
00356             // side of this assignment, but GCC 3.0 optimises that
00357             // incrementation away when -O1 or higher is specified, so
00358             // we pin it out a bit more. 
00359             _argv[i][k] = _argv[i][k+1];
00360             k++;
00361           }
00362           j--;
00363           if (!_argv[i+1]) gotit = true;
00364           break;
00365         }
00366         switch (ret) {
00367         case option_base::bad_argument: 
00368           if (ErrorHandler::on_bad_argument(**o, true, _program_name))
00369             return false;
00370           break;
00371         case option_base::missing_argument: 
00372           if (ErrorHandler::on_missing_argument(**o, true, _program_name))
00373             return false;
00374           break;
00375         }
00376       }
00377       if (gotit) break;
00378       if (o == _options.end())
00379         if (ErrorHandler::on_unknown(_argv[i][j], _program_name))
00380           return false;
00381       j++;
00382     } 
00383     if (_argv[i][0] == '-' && _argv[i][1] == '\0') _argv[i] = 0;
00384     if (gotit) i++;
00385     return true;
00386   }
00387 
00388   //____________________________________________________________________
00389   template <typename ErrorHandler>
00390   inline
00391   bool
00392   basic_command_line<ErrorHandler>::handle_long(int& i) 
00393   {
00394     int ret = 0;
00395     std::string n(_argv[i]);
00396     option_list::iterator o;
00397     for (o = _options.begin(); o < _options.end(); o++) {
00398       if ((ret = (*o)->handle(n, i)) == option_base::can_handle) {
00399         _argv[i] = 0;
00400         break;
00401       }
00402       switch (ret) {
00403       case option_base::bad_argument: 
00404         if (ErrorHandler::on_bad_argument(**o, false, _program_name))
00405           return false;
00406         break;
00407       case option_base::missing_argument: 
00408         if (ErrorHandler::on_missing_argument(**o, false, _program_name))
00409           return false;
00410         break;
00411       }
00412     } 
00413     if (o == _options.end())
00414       if (ErrorHandler::on_unknown(n, _program_name))
00415         return false;
00416     return true;
00417   }
00418 
00419 
00420   /// Explit specialisatation as a typedef 
00421   typedef basic_option<int,true,true>          int_option;
00422 
00423   /// Explit specialisatation as a typedef 
00424   typedef basic_option<float,true,true>        float_option;
00425 
00426   /// Explit specialisatation as a typedef 
00427   typedef basic_option<bool,false,false>       bool_option;
00428 
00429   /// Explit specialisatation as a typedef 
00430   typedef basic_option<std::string,true,true>  string_option;
00431 
00432   /// Explicit specialisation as a typedef 
00433   typedef basic_command_line<>                 command_line;
00434 }
00435 
00436 #endif
00437 //____________________________________________________________________
00438 //
00439 // EOF
00440 //
Top of page Last update Tue Jan 13 19:10:34 2004
Christian Holm
Created by DoxyGen 1.3.4