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 }