GetOpt.cc

00001 /* 
00002    EyeDB Object Database Management System
00003    Copyright (C) 1994-2008 SYSRA
00004       
00005    EyeDB is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009    
00010    EyeDB is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014    
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with this library; if not, write to the Free Software
00017    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA 
00018 */
00019 
00020 /*
00021    Author: Eric Viara <viara@sysra.com>
00022 */
00023 
00024 
00025 #include "GetOpt.h"
00026 
00027 using namespace std;
00028 
00029 Option::Option(char opt,
00030                const std::string &long_opt,
00031                const OptionType &type,
00032                unsigned int flags,
00033                const std::string &defval,
00034                const OptionDesc &optdesc)
00035 {
00036   init(opt, long_opt, type, flags, defval, optdesc);
00037 }
00038 
00039 Option::Option(char opt,
00040                const std::string &long_opt,
00041                const OptionType &type,
00042                unsigned int flags,
00043                const OptionDesc &optdesc)
00044 {
00045   if (flags & SetByDefault)
00046     defval = type.getDefaultValue();
00047 
00048   init(opt, long_opt, type, flags, defval, optdesc);
00049 }
00050 
00051 Option::Option(char opt,
00052                const OptionType &type,
00053                unsigned int flags,
00054                const OptionDesc &optdesc)
00055 {
00056   if (flags & SetByDefault)
00057     defval = type.getDefaultValue();
00058 
00059   init(opt, "", type, flags, defval, optdesc);
00060 }
00061 
00062 
00063 Option::Option(const std::string &long_opt,
00064                const OptionType &type,
00065                unsigned int flags,
00066                const OptionDesc &optdesc)
00067 {
00068   if (flags & SetByDefault)
00069     defval = type.getDefaultValue();
00070 
00071   init(0, long_opt, type, flags, defval, optdesc);
00072 }
00073 
00074 Option::Option(char opt,
00075                const OptionType &type,
00076                unsigned int flags,
00077                const std::string &defval,
00078                const OptionDesc &optdesc)
00079 {
00080   init(opt, "", type, flags, defval, optdesc);
00081 }
00082 
00083 Option::Option(const std::string &long_opt,
00084                const OptionType &type,
00085                unsigned int flags,
00086                const std::string &defval,
00087 const OptionDesc &optdesc)
00088 {
00089   init(0, long_opt, type, flags, defval, optdesc);
00090 }
00091 
00092 void Option::init(char _opt,
00093                   const std::string &_long_opt,
00094                   const OptionType &_type,
00095                   unsigned int _flags,
00096                   const std::string &_defval,
00097                   const OptionDesc &_optdesc)
00098 {
00099   opt = _opt;
00100   long_opt = _long_opt;
00101   type = _type.clone();
00102   flags = _flags;
00103   defval = _defval;
00104 
00105   optdesc = _optdesc;
00106 }
00107 
00108 
00109 GetOpt::GetOpt(const std::string &prog, const Option opts[],
00110                unsigned int opt_cnt, unsigned int flags,
00111                std::ostream &err_os) : prog(prog), flags(flags),
00112                                        err_os(err_os), _maxlen(0)
00113 {
00114   for (unsigned int n = 0; n < opt_cnt; n++)
00115     add(opts[n]);
00116 }
00117 
00118 GetOpt::GetOpt(const std::string &prog, const std::vector<Option> &opts,
00119                unsigned int flags,
00120                std::ostream &err_os) : prog(prog), flags(flags),
00121                                        err_os(err_os), _maxlen(0)
00122 {
00123   unsigned int opt_cnt = opts.size();
00124 
00125   for (unsigned int n = 0; n < opt_cnt; n++)
00126     add(opts[n]);
00127 }
00128 
00129 void GetOpt::usage(const std::string &append, const std::string &prefix,
00130                    std::ostream &os) const
00131 {
00132   std::vector<Option>::const_iterator begin = opt_v.begin();
00133   std::vector<Option>::const_iterator end = opt_v.end();
00134 
00135   os << prefix << prog;
00136   if (prog.length() > 0)
00137     os << ' ';
00138 
00139   while (begin != end) {
00140     const Option &opt = (*begin);
00141     if (begin != opt_v.begin())
00142       os << " ";
00143 
00144     if (!(opt.getFlags() & Option::Mandatory))
00145       os << '[';
00146 
00147     if (opt.getOpt()) {
00148       os << "-" << opt.getOpt();
00149       if (opt.getFlags() & Option::MandatoryValue)
00150         os << " " << opt.getOptionDesc().value_name;
00151       else if (opt.getFlags() & Option::OptionalValue)
00152         os << " [" << opt.getOptionDesc().value_name << "]";
00153     }
00154 
00155     if (opt.getLongOpt().length() > 0) {
00156       os << (opt.getOpt() ? "|" : "") << "--" << opt.getLongOpt();
00157       if (opt.getFlags() & Option::MandatoryValue)
00158         os << "=" << opt.getOptionDesc().value_name;
00159       else if (opt.getFlags() & Option::OptionalValue)
00160         os << "[=" << opt.getOptionDesc().value_name << "]";
00161     }
00162 
00163     if (!(opt.getFlags() & Option::Mandatory))
00164       os << ']';
00165 
00166     ++begin;
00167   }
00168 
00169   os << append;
00170 }
00171 
00172 void GetOpt::displayHelpOpt(const Option &opt, ostream &os) const
00173 {
00174   if (opt.getOpt()) {
00175     os << "-" << opt.getOpt();
00176     if (opt.getFlags() & Option::MandatoryValue)
00177       os << " " << opt.getOptionDesc().value_name;
00178     else if (opt.getFlags() & Option::OptionalValue)
00179       os << " [" << opt.getOptionDesc().value_name << "]";
00180   }
00181 
00182   if (opt.getLongOpt().length() > 0) {
00183     os << (opt.getOpt() ? ", " : "") << "--" << opt.getLongOpt();
00184     if (opt.getFlags() & Option::MandatoryValue)
00185       os << "=" << opt.getOptionDesc().value_name;
00186     else if (opt.getFlags() & Option::OptionalValue)
00187       os << "[=" << opt.getOptionDesc().value_name << "]";
00188   }
00189 }
00190 
00191 void GetOpt::adjustMaxLen(const std::string &opt)
00192 {
00193   adjustMaxLen(opt.length());
00194 }
00195 
00196 void GetOpt::adjustMaxLen(unsigned int maxlen)
00197 {
00198   (void)getMaxLen();
00199   if (_maxlen < maxlen) {
00200     _maxlen = maxlen;
00201   }
00202 }
00203 
00204 unsigned int GetOpt::getMaxLen() const
00205 {
00206   if (_maxlen)
00207     return _maxlen;
00208 
00209   std::vector<Option>::const_iterator begin = opt_v.begin();
00210   std::vector<Option>::const_iterator end = opt_v.end();
00211 
00212   while (begin != end) {
00213     const Option &opt = *begin;
00214 
00215     ostringstream ostr;
00216 
00217     displayHelpOpt(opt, ostr);
00218 
00219     if (ostr.str().length() > _maxlen)
00220       const_cast<GetOpt*>(this)->_maxlen = ostr.str().length();
00221 
00222     ++begin;
00223   }
00224 
00225   return _maxlen;
00226 }
00227 
00228 void GetOpt::helpLine(const std::string &option, const std::string &detail,
00229                       std::ostream &os, const std::string &indent) const
00230 {
00231   os << indent;
00232 
00233   unsigned int maxlen = getMaxLen();
00234 
00235   ostringstream ostr;
00236   ostr << option;
00237 
00238   os << ostr.str();
00239   for (unsigned int n = ostr.str().length(); n < maxlen; n++)
00240     os << ' ';
00241 
00242   os << ' ' << detail << '\n';
00243 }
00244 
00245 void GetOpt::displayOpt(const std::string &opt, const std::string &detail, std::ostream &os, const std::string &indent) const
00246 {
00247   unsigned int maxlen = getMaxLen();
00248 
00249   os << indent;
00250   ostringstream ostr;
00251   ostr << opt;
00252 
00253   os << ostr.str();
00254 
00255   for (unsigned int n = ostr.str().length(); n < maxlen; n++)
00256     os << ' ';
00257   
00258   os << ' ' << detail << endl;
00259 }
00260 
00261 void GetOpt::help(std::ostream &os, const std::string &indent) const
00262 {
00263   std::vector<Option>::const_iterator begin = opt_v.begin();
00264   std::vector<Option>::const_iterator end = opt_v.end();
00265 
00266   unsigned int maxlen = getMaxLen();
00267 
00268   while (begin != end) {
00269     const Option &opt = (*begin);
00270     os << indent;
00271     ostringstream ostr;
00272 
00273     displayHelpOpt(opt, ostr);
00274 
00275     os << ostr.str();
00276     for (unsigned int n = ostr.str().length(); n < maxlen; n++)
00277       os << ' ';
00278 
00279     os << ' ' << opt.getOptionDesc().help << endl;
00280     ++begin;
00281   }
00282 }
00283 
00284 void GetOpt::add(const Option &opt)
00285 {
00286   opt_v.push_back(opt);
00287 
00288   char c = opt.getOpt();
00289   if (c) {
00290     char s[3];
00291     s[0] = '-';
00292     s[1] = c;
00293     s[2] = 0;
00294     opt_map[s] = opt;
00295   }
00296 
00297   const std::string &l = opt.getLongOpt();
00298   if (l.length()) {
00299     std::string s = "--" + l;
00300     long_opt_map[s] = opt;
00301   }
00302 }
00303 
00304 bool OptionIntType::checkValue(const std::string &value,
00305                                const std::string &prog,
00306                                ostream &err_os) const
00307 {
00308   const char *s = value.c_str();
00309   while (*s) {
00310     if (*s < '0' || *s > '9') {
00311       err_os << prog << ": invalid integer value " << s << endl;
00312       return false;
00313     }
00314     s++;
00315   }
00316 
00317   return true;
00318 }
00319 
00320 bool OptionBoolType::checkValue(const std::string &value,
00321                                 const std::string &prog,
00322                                 ostream &err_os) const
00323 {
00324   const char *s = value.c_str();
00325 
00326   if (!strcasecmp(s, "true") ||
00327       !strcasecmp(s, "yes") ||
00328       !strcasecmp(s, "on") ||
00329       !strcasecmp(s, "false") ||
00330       !strcasecmp(s, "no") ||
00331       !strcasecmp(s, "off"))
00332     return true;
00333 
00334   err_os << prog << ": unexpected boolean value " << s << endl;
00335   return false;
00336 }
00337 
00338 bool OptionBoolType::getBoolValue(const std::string &value) const
00339 {
00340   const char *s = value.c_str();
00341 
00342   if (!strcasecmp(s, "true") ||
00343       !strcasecmp(s, "yes") ||
00344       !strcasecmp(s, "on"))
00345     return true;
00346 
00347   if (!strcasecmp(s, "false") ||
00348       !strcasecmp(s, "no") ||
00349       !strcasecmp(s, "off"))
00350     return false;
00351 
00352   return false;
00353 }
00354 
00355 
00356 int OptionIntType::getIntValue(const std::string &value) const
00357 {
00358   return atoi(value.c_str());
00359 }
00360 
00361 bool OptionChoiceType::checkValue(const std::string &value,
00362                                   const std::string &prog,
00363                                   ostream &err_os) const
00364 {
00365   std::vector<std::string>::const_iterator begin = choice.begin();
00366   std::vector<std::string>::const_iterator end = choice.end();
00367 
00368   while (begin != end) {
00369     if (*begin == value)
00370       return true;
00371     ++begin;
00372   }
00373 
00374   err_os << prog << ": invalid value " << value << endl;
00375   return false;
00376 }
00377 
00378 unsigned int GetOpt::add_map(const Option &opt, const std::string &_value)
00379 {
00380   if (!opt.getOptionType().checkValue(_value, prog, err_os))
00381     return 1;
00382 
00383   OptionValue value(opt.getOptionType(), _value);
00384 
00385   char c = opt.getOpt();
00386   if (c) {
00387     char s[2];
00388     s[0] = c;
00389     s[1] = 0;
00390     if (map.find(s) != map.end()) {
00391       if (!map[s].def) {
00392         err_os << prog << ": option -" << s;
00393         if (opt.getLongOpt().length())
00394           err_os << "/--" << opt.getLongOpt();
00395         err_os << " already set" << endl;
00396         return 1;
00397       }
00398     }
00399     map[s] = value;
00400   }
00401 
00402   const std::string &s = opt.getLongOpt();
00403   if (s.length()) {
00404     if (map.find(s) != map.end()) {
00405       if (!map[s].def) {
00406         err_os << prog << ": option ";
00407         if (opt.getOpt())
00408           err_os << " -" << opt.getOpt() << "/";
00409         err_os << "--" << opt.getLongOpt();
00410         err_os << " already set" << endl;
00411         return 1;
00412       }
00413     }
00414     map[s] = value;
00415   }
00416   return 0;
00417 }
00418 
00419 void GetOpt::init_map(std::map<std::string, Option> &_map)
00420 {
00421   std::map<std::string, Option>::iterator begin = _map.begin();
00422   std::map<std::string, Option>::iterator end = _map.end();
00423   
00424   while (begin != end) {
00425     const Option &opt = (*begin).second;
00426     unsigned int flags = opt.getFlags();
00427     if (flags & Option::SetByDefault) {
00428       add_map(opt, opt.getDefaultValue());
00429     }
00430     ++begin;
00431   }
00432 }
00433 
00434 unsigned int GetOpt::check_mandatory()
00435 {
00436   unsigned int error = 0;
00437 
00438   std::map<std::string, Option>::iterator begin = opt_map.begin();
00439   std::map<std::string, Option>::iterator end = opt_map.end();
00440 
00441   bool found = false;
00442   while (begin != end) {
00443       const Option &opt = (*begin).second;
00444       if (opt.getFlags() & Option::Mandatory) {
00445         char s[2];
00446         s[0] = opt.getOpt();
00447         s[1] = 0;
00448         if (map.find(s) == map.end()) {
00449           err_os << prog << ": mandatory option -" << opt.getOpt() << " is missing" <<
00450             endl;
00451           if (opt.getLongOpt().length() > 0)
00452             map[opt.getLongOpt()] = OptionValue();
00453           error++;
00454         }
00455       }
00456       ++begin;
00457     }
00458   
00459   begin = long_opt_map.begin();
00460   end = long_opt_map.end();
00461 
00462   while (begin != end) {
00463     const Option &opt = (*begin).second;
00464     if (opt.getFlags() & Option::Mandatory) {
00465       if (map.find(opt.getLongOpt()) == map.end()) {
00466         err_os << prog << ": mandatory option -" << opt.getLongOpt() << " is missing" <<
00467           endl;
00468         error++;
00469       }
00470     }
00471     ++begin;
00472   }
00473 
00474   return error;
00475 }
00476 
00477 bool GetOpt::parseLongOpt(const std::string &arg, const std::string &opt,
00478                           std::string *value)
00479 {
00480   if (value) {
00481     if (!strncmp((arg + "=").c_str(),
00482                  ("--" + opt + "=").c_str(),
00483                  2 + strlen(opt.c_str()) + 1)) {
00484       *value = arg.c_str() + 3 + strlen(opt.c_str());
00485       return true;
00486     }
00487 
00488     return false;
00489   }
00490 
00491   if (!strcmp(arg.c_str(), ("--" + opt).c_str()))
00492     return true;
00493 
00494   return false;
00495 }
00496 
00497 static void display(const std::string &action, const std::vector<std::string> &argv)
00498 {
00499   std::cout << action << ":\n";
00500   for (unsigned int n = 0; n < argv.size(); n++) {
00501     std::cout << "#" << n << ": " << argv[n] << '\n';
00502   }
00503 }
00504 
00505 bool GetOpt::parse(const std::string &prog, std::vector<std::string> &argv)
00506 {
00507   unsigned int argv_cnt = argv.size();
00508   char ** c_argv = new char*[argv_cnt + 2];
00509 
00510   c_argv[0] = strdup(prog.c_str());
00511 
00512   for (unsigned int n = 0; n < argv_cnt; n++) {
00513     c_argv[n+1] = strdup(argv[n].c_str());
00514   }  
00515 
00516   c_argv[argv_cnt + 1] = 0;
00517 
00518   //display("before: " , argv);
00519   int argc = argv_cnt + 1;
00520   int oargc = argc;
00521   bool r = parse(argc, c_argv);
00522 
00523   if (oargc != argc) {
00524     argv.clear();
00525     for (unsigned int n = 1; n < argc; n++) {
00526       argv.push_back(c_argv[n]);
00527     }
00528   }
00529 
00530   //display("after: " , argv);
00531 
00532 #if 1
00533   for (unsigned int n = 0; n < argc; n++) {
00534     free(c_argv[n]);
00535   }
00536 #else
00537   for (unsigned int n = 0; n < argv_cnt + 1; n++) {
00538     free(c_argv[n]);
00539   }
00540 #endif
00541 
00542   delete [] c_argv;
00543   return r;
00544 }
00545 
00546 bool GetOpt::parse(int &argc, char *argv[])
00547 {
00548   init_map(opt_map);
00549   init_map(long_opt_map);
00550 
00551   unsigned int error = 0;
00552 
00553   vector<char *> keep_v;
00554   for (int n = 1; n < argc; n++) {
00555     char *s = argv[n];
00556     if (*s != '-') {
00557       keep_v.push_back(argv[n]);
00558       continue;
00559     }
00560 
00561     if (!(flags & PurgeArgv))
00562       keep_v.push_back(argv[n]);
00563 
00564     s = strdup(s);
00565 
00566     std::map<std::string, Option> *omap;
00567     char *value = 0;
00568     bool long_opt = false;
00569 
00570     if (strlen(s) > 2 && *(s+1) == '-') {
00571       long_opt = true;
00572       omap = &long_opt_map;
00573       char *c = strchr(s, '=');
00574       if (c) {
00575         *c = 0;
00576         value = c+1;
00577       }
00578     }
00579     else {
00580       long_opt = false;
00581       omap = &opt_map;
00582       if (n < argc-1) {
00583         if (*argv[n+1] != '-')
00584           value = argv[n+1];
00585       }
00586     }
00587 
00588     std::map<std::string, Option>::iterator begin = omap->begin();
00589     std::map<std::string, Option>::iterator end = omap->end();
00590 
00591     bool found = false;
00592     while (begin != end) {
00593       if (!strcmp(s, (*begin).first.c_str())) {
00594         const Option &opt = (*begin).second;
00595         found = true;
00596         if (opt.getFlags() & Option::MandatoryValue) {
00597           if (!value) {
00598             err_os << prog << ": missing value after " <<
00599               s << endl;
00600             error++;
00601             ++begin;
00602             continue;
00603           }
00604           error += add_map(opt, value);
00605           if (!long_opt) {
00606             if (!(flags & PurgeArgv))
00607               keep_v.push_back(value);
00608             ++n;
00609           }
00610         }
00611         else if (opt.getFlags() & Option::OptionalValue) {
00612           if (!value)
00613             value = (char *)opt.getDefaultValue().c_str();
00614           error += add_map(opt, value);
00615           if (!long_opt) {
00616             if (!(flags & PurgeArgv))
00617               keep_v.push_back(value);
00618             ++n;
00619           }
00620         }
00621         // disconnected 19/10/05. But was added a few days before because
00622         // of a good reason, I don't remember which one ...
00623         // I added '&& long_opt' in the test
00624 #if 0
00625         else if (value) {
00626           error += add_map(opt, value);
00627           if (!long_opt)
00628             ++n;
00629         }
00630 #else
00631         else if (value && long_opt) {
00632           error += add_map(opt, value);
00633           if (!long_opt) {
00634             if (!(flags & PurgeArgv))
00635               keep_v.push_back(value);
00636             ++n;
00637           }
00638         }
00639 #endif
00640         else
00641           error += add_map(opt, "true");
00642       }
00643       ++begin;
00644     }
00645 
00646     if (!found) {
00647       if (!(flags & SkipUnknownOption)) {
00648         err_os << prog << ": unknown option " << s << endl;
00649         error++;
00650       }
00651       if (flags & PurgeArgv)
00652         keep_v.push_back(argv[n]);
00653     }
00654 
00655     free(s);
00656   }
00657 
00658   if (!error)
00659     error += check_mandatory();
00660 
00661   if (error)
00662     map.erase(map.begin(), map.end());
00663 
00664   if (!error) {
00665     vector<char *>::iterator b = keep_v.begin();
00666     vector<char *>::iterator e = keep_v.end();
00667     for (int n = 1; b != e; n++) {
00668       argv[n] = *b;
00669       ++b;
00670     }
00671 
00672     argc = keep_v.size() + 1;
00673     argv[argc] = 0;
00674   }
00675 
00676   return error == 0;
00677 }
00678 
00679 // unitary test
00680 #if 0
00681 int
00682 main(int argc, char *argv[])
00683 {
00684   Option opts[] = {
00685     Option('d', "database", OptionStringType(),
00686            Option::Mandatory|Option::MandatoryValue,
00687            OptionDesc("name of the database", "dbname")),
00688     Option('p', "print", OptionStringType(),
00689            Option::Mandatory|Option::OptionalValue,
00690            "/dev/tty1",
00691            OptionDesc("name of the device", "device")),
00692     Option('w', "read-write"),
00693     Option('x'),
00694     Option("zoulou")
00695   };
00696 
00697   GetOpt getopt("test", opts, sizeof(opts)/sizeof(opts[0]),
00698                 GetOpt::SkipUnknownOption|GetOpt::PurgeArgv);
00699 
00700   getopt.usage();
00701   getopt.help(cerr, "\t", 30);
00702 
00703   if (getopt.parse(argc, argv)) {
00704     cout << "argc is: " << argc << endl;
00705     for (int n = 0; n < argc; n++)
00706       cout << "argv[" << n << "] = " << argv[n] << endl;
00707     const GetOpt::Map &map = getopt.getMap();
00708     GetOpt::Map::const_iterator begin = map.begin();
00709     GetOpt::Map::const_iterator end = map.end();
00710     while (begin != end) {
00711       const OptionValue &value = (*begin).second;
00712       cout << (*begin).first << "=" << value.value << endl;
00713       ++begin;
00714     }
00715   }
00716 }
00717 #endif

Generated on Mon Dec 22 18:15:55 2008 for eyedb by  doxygen 1.5.3