bincimap

Log | Files | Refs | LICENSE

argparser.cc (8400B)


      1 /* -*- Mode: c++; -*- */
      2 /*  --------------------------------------------------------------------
      3  *  Filename:
      4  *    src/argparser.cc
      5  *  
      6  *  Description:
      7  *    <--->
      8  *
      9  *  Authors:
     10  *    Andreas Aardal Hanssen <bincimap@andreas.hanssen.name>
     11  *
     12  *  Bugs:
     13  *
     14  *  ChangeLog:
     15  *
     16  *  --------------------------------------------------------------------
     17  *  Copyright 2002-2005 Andreas Aardal Hanssen
     18  *
     19  *  This program is free software; you can redistribute it and/or modify
     20  *  it under the terms of the GNU General Public License as published by
     21  *  the Free Software Foundation; either version 2 of the License, or
     22  *  (at your option) any later version.
     23  *
     24  *  This program is distributed in the hope that it will be useful,
     25  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     26  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     27  *  GNU General Public License for more details.
     28  *
     29  *  You should have received a copy of the GNU General Public License
     30  *  along with this program; if not, write to the Free Software
     31  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
     32  *  --------------------------------------------------------------------
     33  */
     34 #include "argparser.h"
     35 #include "convert.h"
     36 
     37 #include <string>
     38 #include <map>
     39 
     40 using namespace ::std;
     41 using namespace Binc;
     42 
     43 //----------------------------------------------------------------------
     44 CommandLineArgs::CommandLineArgs()
     45 {
     46   errString = "Unknown error";
     47   ac = 0;
     48 }
     49 
     50 //----------------------------------------------------------------------
     51 bool CommandLineArgs::parse(int argc, char *argv[])
     52 {
     53   ac = -1;
     54   head = argv[0];
     55   head += " <options> --\n";
     56 
     57   if (argc > 1) {
     58     string lastKey;
     59     bool lastIsBoolean = false;
     60 
     61     for (int i = 1; i < argc; ++i) {
     62       string s = argv[i];
     63       if (s.length() < 2) {
     64 	unqualified.push_back(s);
     65 	continue;
     66       }
     67 
     68       if (s[0] != '-') {
     69 	// read value of last argument
     70 	if (lastKey == "") {
     71 	  unqualified.push_back(s);
     72 	  continue;
     73 	}
     74 
     75 	if (lastIsBoolean && (s != "yes" && s != "no")) {
     76 	  errString = "syntax error: " + s;
     77 	  errString += " (expected yes or no)";
     78 	  return false;
     79 	}
     80 
     81 	args[lastKey] = s;
     82 	passedArgs[lastKey] = true;
     83 	lastKey = "";
     84 	lastIsBoolean = false;
     85       } else if (s[1] == '-') {
     86 	if (lastKey != "") {
     87 	  if (lastIsBoolean) {
     88 	    args[lastKey] = "yes";
     89 	    passedArgs[lastKey] = true;
     90 	    lastKey = "";
     91 	    lastIsBoolean = false;
     92 	  } else {
     93 	    errString = "expected value of ";
     94 	    errString += lastKey;
     95 	    return false;
     96 	  }
     97 	}
     98 
     99 	// break if '--' is detected
    100 	if (s.length() == 2) {
    101 	  ac = i + 1;
    102 	  break;
    103 	}
    104 
    105 	// parse --argument
    106 	string arg = s.substr(2);
    107 	string val;
    108 	string::size_type epos = arg.find('=');
    109 	if (epos != string::npos) {
    110 	  val = arg.substr(epos + 1);
    111 	  arg = arg.substr(0, epos);
    112 	}
    113 
    114 	if (reg.find(arg) == reg.end()) {
    115 	  errString = "unrecognized argument: --" + arg;
    116 	  return false;
    117 	}
    118 
    119 	if (reg.find(arg)->second.b) {
    120 	  if (val != "" && val != "yes" && val != "no") {
    121 	    errString = "syntax error: " + val;
    122 	    errString += " (expected yes or no)";
    123 	    return false;
    124 	  } else if (val == "") {
    125 	    val = "yes";
    126 	  }
    127 	}
    128 
    129 	if (val == "") {
    130 	  errString = "syntax error: " + arg;
    131 	  errString += " (expected --" + arg + "=<str>)";
    132 	  return false;
    133 	}
    134 
    135 	args[arg] = val;
    136 	passedArgs[arg] = true;
    137 
    138 	lastKey = "";
    139 	lastIsBoolean = false;
    140       } else {
    141 	if (lastKey != "") {
    142 	  if (lastIsBoolean) {
    143 	    args[lastKey] = "yes";
    144 	    passedArgs[lastKey] = true;
    145 	    lastKey = "";
    146 	    lastIsBoolean = false;
    147 	  } else {
    148 	    errString = "expected value of ";
    149 	    errString += lastKey;
    150 	    return false;
    151 	  }
    152 	}
    153 
    154 	// parse -argument
    155 	string arg = s.substr(1);
    156 	if (arg.length() == 1) {
    157 	  map<string, ArgOpts>::const_iterator it = reg.begin();
    158 	  bool match = false;
    159 	  for (; it != reg.end(); ++it) {
    160 	    if (it->second.c.find(arg[0]) != string::npos) {
    161 	      lastKey = it->first;
    162 	      if (it->second.b)
    163 		lastIsBoolean = true;
    164 	      match = true;
    165 	      break;
    166 	    }
    167 	  }
    168 
    169 	  if (!match) {
    170 	    errString = "unrecognized argument: -";
    171 	    errString += arg[0];
    172 	    return false;
    173 	  }
    174 
    175 	} else {
    176 	  string::const_iterator its = arg.begin();
    177 	  for (; its != arg.end(); ++its) {
    178 	    map<string, ArgOpts>::const_iterator it = reg.begin();
    179 	    bool match = false;
    180 	    for (; it != reg.end(); ++it) {
    181 	      if (it->second.c.find(*its) != string::npos) {
    182 		if (!it->second.b) {
    183 		  errString = "argument is not a boolean: ";
    184 		  errString += "--" + it->first;
    185 		  errString += " / -";
    186 		  errString += it->second.c;
    187 		  return false;
    188 		}
    189 
    190 		match = true;
    191 		args[it->first] = "yes";
    192 		passedArgs[it->first] = true;
    193 
    194 		lastKey = "";
    195 		lastIsBoolean = false;
    196 		break;
    197 	      }
    198 	    }
    199 
    200 	    if (!match) {
    201 	      errString = "unrecognized argument: ";
    202 	      errString += s;
    203 	      return false;
    204 	    }
    205 	  }
    206 	}
    207       }
    208     }
    209 
    210     if (lastKey != "") {
    211       if (lastIsBoolean) {
    212 	args[lastKey] = "yes";
    213 	passedArgs[lastKey] = true;
    214       } else {
    215 	errString = "expected value of ";
    216 	errString += lastKey;
    217 	return false;
    218       }
    219     }
    220   }
    221 
    222   // assign default "no" values for arguments that were not passed.
    223   map<string, ArgOpts>::const_iterator it = reg.begin();
    224   for (; it != reg.end(); ++it) {
    225     if (args.find(it->first) == args.end()) {
    226       if (!it->second.o) {
    227 	errString = "missing argument: ";
    228 	errString += it->first;
    229 	return false;
    230       }
    231       
    232       if (it->second.b)
    233 	args[it->first] = "no";
    234     }
    235   }
    236 
    237   if (ac == -1)
    238     ac = argc;
    239 
    240   return true;
    241 }
    242 
    243 //----------------------------------------------------------------------
    244 string CommandLineArgs::errorString(void) const
    245 {
    246   return errString;
    247 }
    248 
    249 //----------------------------------------------------------------------
    250 const string CommandLineArgs::operator [](const string &arg) const
    251 {
    252   if (args.find(arg) == args.end())
    253     return "";
    254 
    255   return args.find(arg)->second;
    256 }
    257 
    258 //----------------------------------------------------------------------
    259 void CommandLineArgs::addOptional(const string &arg, const string &desc,
    260 				  bool boolean)
    261 {
    262   registerArg(arg, desc, boolean, true);
    263 }
    264 
    265 //----------------------------------------------------------------------
    266 void CommandLineArgs::addRequired(const string &arg,
    267 				  const string &desc, bool boolean)
    268 {
    269   registerArg(arg, desc, boolean, false);
    270 }
    271 
    272 //----------------------------------------------------------------------
    273 void CommandLineArgs::registerArg(const string &arg, const string &desc,
    274 				  bool boolean, bool optional)
    275 {
    276   string name = arg;
    277 
    278   string shorts;
    279   while (name.size() > 1 && name[1] == '|') {
    280     shorts += name[0];
    281     name = name.substr(2);
    282   }
    283 
    284   reg.insert(make_pair(name, ArgOpts(shorts, boolean, optional, desc)));
    285 }
    286 
    287 //----------------------------------------------------------------------
    288 bool CommandLineArgs::hasArg(const std::string &arg) const
    289 {
    290   string tmp = arg; lowercase(tmp);
    291   return passedArgs.find(tmp) != passedArgs.end();
    292 }
    293 
    294 //----------------------------------------------------------------------
    295 string CommandLineArgs::usageString(void) const
    296 {
    297   string tmp = head;
    298   tmp += '\n';
    299 
    300   map<string, ArgOpts>::const_iterator it = reg.begin();
    301   for (; it != reg.end(); ++it) {
    302     if (it->second.c != "") {
    303       string::const_iterator sit = it->second.c.begin();
    304       for (; sit != it->second.c.end(); ++sit) {
    305 	if (sit != it->second.c.begin())
    306 	  tmp += '\n';
    307 	tmp += "  -";
    308 	tmp += *sit;
    309       }
    310       tmp += ", ";
    311     } else {
    312       tmp += "      ";
    313     }
    314 
    315     tmp += "--";
    316     tmp += it->first;
    317     if (!it->second.b) {
    318       tmp += "=<str>";
    319     }
    320 
    321     if (!it->second.o)
    322       tmp += " (required)";
    323 
    324     string::size_type lineStart = tmp.rfind('\n');
    325     if (lineStart == string::npos)
    326       lineStart = 0;
    327 
    328     int pad = 21 - (tmp.length() - lineStart);
    329     if (pad < 0) {
    330       tmp += '\n';
    331       pad = 20;
    332     }
    333 
    334     tmp += string(pad, ' ');
    335     tmp += it->second.desc;
    336     tmp += '\n';
    337   }
    338   
    339   tmp += '\n';
    340   tmp += tail;
    341   tmp += '\n';
    342 
    343   return tmp;
    344 }
    345 
    346 //----------------------------------------------------------------------
    347 int CommandLineArgs::argc(void) const
    348 {
    349   return ac;
    350 }
    351 
    352 //----------------------------------------------------------------------
    353 void CommandLineArgs::setTail(const string &str)
    354 {
    355   tail = str;
    356 }
    357 
    358 //----------------------------------------------------------------------
    359 const vector<string> &CommandLineArgs::getUnqualifiedArgs() const
    360 {
    361   return unqualified;
    362 }