| 1 | /* $Id: CoinParamUtils.cpp 1469 2011-09-03 18:49:33Z stefan $ */ |
| 2 | // Copyright (C) 2007, International Business Machines |
| 3 | // Corporation and others. All Rights Reserved. |
| 4 | // This code is licensed under the terms of the Eclipse Public License (EPL). |
| 5 | |
| 6 | #include <cassert> |
| 7 | #include <cerrno> |
| 8 | #include <iostream> |
| 9 | |
| 10 | #include "CoinUtilsConfig.h" |
| 11 | #include "CoinParam.hpp" |
| 12 | #include <cstdlib> |
| 13 | #include <cstring> |
| 14 | #include <cstdio> |
| 15 | |
| 16 | #ifdef COIN_HAS_READLINE |
| 17 | #include <readline/readline.h> |
| 18 | #include <readline/history.h> |
| 19 | #endif |
| 20 | |
| 21 | /* Unnamed local namespace */ |
| 22 | namespace |
| 23 | { |
| 24 | |
| 25 | /* |
| 26 | cmdField: The index of the current command line field. Forced to -1 when |
| 27 | accepting commands from stdin (interactive) or a command file. |
| 28 | readSrc: Current input source. |
| 29 | |
| 30 | pendingVal: When the form param=value is encountered, both keyword and value |
| 31 | form one command line field. We need to return `param' as the |
| 32 | field and somehow keep the value around for the upcoming call |
| 33 | that'll request it. That's the purpose of pendingVal. |
| 34 | */ |
| 35 | |
| 36 | int cmdField = 1 ; |
| 37 | FILE *readSrc = stdin ; |
| 38 | std::string pendingVal = "" ; |
| 39 | |
| 40 | |
| 41 | /* |
| 42 | Get next command or field in command. When in interactive mode, prompt the |
| 43 | user and read the resulting line of input. |
| 44 | */ |
| 45 | std::string nextField (const char *prompt) |
| 46 | { |
| 47 | static char line[1000] ; |
| 48 | static char *where = NULL ; |
| 49 | std::string field ; |
| 50 | const char *dflt_prompt = "Eh? " ; |
| 51 | |
| 52 | if (prompt == 0) |
| 53 | { prompt = dflt_prompt ; } |
| 54 | /* |
| 55 | Do we have a line at the moment? If not, acquire one. When we're done, |
| 56 | line holds the input line and where points to the start of the line. If we're |
| 57 | using the readline library, add non-empty lines to the history list. |
| 58 | */ |
| 59 | if (!where) { |
| 60 | #ifdef COIN_HAS_READLINE |
| 61 | if (readSrc == stdin) |
| 62 | { where = readline(prompt) ; |
| 63 | if (where) |
| 64 | { if (*where) |
| 65 | add_history (where) ; |
| 66 | strcpy(line,where) ; |
| 67 | free(where) ; |
| 68 | where = line ; } } |
| 69 | else |
| 70 | { where = fgets(line,1000,readSrc) ; } |
| 71 | #else |
| 72 | if (readSrc == stdin) |
| 73 | { fprintf(stdout,"%s" ,prompt) ; |
| 74 | fflush(stdout) ; } |
| 75 | where = fgets(line,1000,readSrc) ; |
| 76 | #endif |
| 77 | /* |
| 78 | If where is NULL, we have EOF. Return a null string. |
| 79 | */ |
| 80 | if (!where) |
| 81 | return field ; |
| 82 | /* |
| 83 | Clean the image. Trailing junk first. The line will be cut off at the last |
| 84 | non-whitespace character, but we need to scan until we find the end of the |
| 85 | string or some other non-printing character to make sure we don't miss a |
| 86 | printing character after whitespace. |
| 87 | */ |
| 88 | char *lastNonBlank = line-1 ; |
| 89 | for (where = line ; *where != '\0' ; where++) |
| 90 | { if (*where != '\t' && *where < ' ') |
| 91 | { break ; } |
| 92 | if (*where != '\t' && *where != ' ') |
| 93 | { lastNonBlank = where ; } } |
| 94 | *(lastNonBlank+1) = '\0' ; |
| 95 | where = line ; } |
| 96 | /* |
| 97 | Munch through leading white space. |
| 98 | */ |
| 99 | while (*where == ' ' || *where == '\t') |
| 100 | where++ ; |
| 101 | /* |
| 102 | See if we can separate a field; if so, copy it over into field for return. |
| 103 | If we're out of line, return the string "EOL". |
| 104 | */ |
| 105 | char *saveWhere = where ; |
| 106 | while (*where != ' ' && *where != '\t' && *where!='\0') |
| 107 | where++ ; |
| 108 | if (where != saveWhere) |
| 109 | { char save = *where ; |
| 110 | *where = '\0' ; |
| 111 | field = saveWhere ; |
| 112 | *where = save ; } |
| 113 | else |
| 114 | { where = NULL ; |
| 115 | field = "EOL" ; } |
| 116 | |
| 117 | return (field) ; } |
| 118 | |
| 119 | } |
| 120 | |
| 121 | |
| 122 | /* Visible functions */ |
| 123 | |
| 124 | namespace CoinParamUtils |
| 125 | { |
| 126 | |
| 127 | /* |
| 128 | As mentioned above, cmdField set to -1 is the indication that we're reading |
| 129 | from stdin or a file. |
| 130 | */ |
| 131 | void setInputSrc (FILE *src) |
| 132 | |
| 133 | { if (src != 0) |
| 134 | { cmdField = -1 ; |
| 135 | readSrc = src ; } } |
| 136 | |
| 137 | /* |
| 138 | A utility to allow clients to determine if we're processing parameters from |
| 139 | the comand line or otherwise. |
| 140 | */ |
| 141 | bool isCommandLine () |
| 142 | |
| 143 | { assert(cmdField != 0) ; |
| 144 | |
| 145 | if (cmdField > 0) |
| 146 | { return (true) ; } |
| 147 | else |
| 148 | { return (false) ; } } |
| 149 | |
| 150 | /* |
| 151 | A utility to allow clients to determine if we're accepting parameters |
| 152 | interactively. |
| 153 | */ |
| 154 | bool isInteractive () |
| 155 | |
| 156 | { assert(cmdField != 0) ; |
| 157 | |
| 158 | if (cmdField < 0 && readSrc == stdin) |
| 159 | { return (true) ; } |
| 160 | else |
| 161 | { return (false) ; } } |
| 162 | |
| 163 | /* |
| 164 | Utility functions for acquiring input. |
| 165 | */ |
| 166 | |
| 167 | |
| 168 | /* |
| 169 | Return the next field (word) from the current command line. Generally, this |
| 170 | is expected to be of the form `-param' or `--param', with special cases as |
| 171 | set out below. |
| 172 | |
| 173 | If we're in interactive mode (cmdField == -1), nextField does all the work |
| 174 | to prompt the user and return the next field from the resulting input. It is |
| 175 | assumed that the user knows not to use `-' or `--' prefixes in interactive |
| 176 | mode. |
| 177 | |
| 178 | If we're in command line mode (cmdField > 0), cmdField indicates the |
| 179 | current command line word. The order of processing goes like this: |
| 180 | * A stand-alone `-' is converted to `stdin' |
| 181 | * A stand-alone '--' is returned as a word; interpretation is up to the |
| 182 | client. |
| 183 | * A prefix of '-' or '--' is stripped from the field. |
| 184 | If the result is `stdin', it's assumed we're switching to interactive mode |
| 185 | and the user is prompted for another command. |
| 186 | |
| 187 | Whatever results from the above sequence is returned to the client as the |
| 188 | next field. An empty string indicates end of input. |
| 189 | |
| 190 | Prompt will be used by nextField if it's necessary to prompt the user for |
| 191 | a command (only when reading from stdin). |
| 192 | |
| 193 | If provided, pfx is set to the prefix ("-", "--", or "") stripped from the |
| 194 | field. Lack of prefix is not necessarily an error because of the following |
| 195 | scenario: To read a file, the verbose command might be "foo -import |
| 196 | myfile". But we might want to allow a short form, "foo myfile". And we'd |
| 197 | like "foo import" to be interpreted as "foo -import import" (i.e., import the |
| 198 | file named `import'). |
| 199 | */ |
| 200 | |
| 201 | std::string getCommand (int argc, const char *argv[], |
| 202 | const std::string prompt, std::string *pfx) |
| 203 | |
| 204 | { std::string field = "EOL" ; |
| 205 | pendingVal = "" ; |
| 206 | int pfxlen ; |
| 207 | |
| 208 | if (pfx != 0) |
| 209 | { (*pfx) = "" ; } |
| 210 | /* |
| 211 | Acquire the next field, and convert as outlined above if we're processing |
| 212 | command line parameters. |
| 213 | */ |
| 214 | while (field == "EOL" ) |
| 215 | { pfxlen = 0 ; |
| 216 | if (cmdField > 0) |
| 217 | { if (cmdField < argc) |
| 218 | { field = argv[cmdField++] ; |
| 219 | if (field == "-" ) |
| 220 | { field = "stdin" ; } |
| 221 | else |
| 222 | if (field == "--" ) |
| 223 | { /* Prevent `--' from being eaten by next case. */ } |
| 224 | else |
| 225 | { if (field[0] == '-') |
| 226 | { pfxlen = 1 ; |
| 227 | if (field[1] == '-') |
| 228 | pfxlen = 2 ; |
| 229 | if (pfx != 0) |
| 230 | (*pfx) = field.substr(0,pfxlen) ; |
| 231 | field = field.substr(pfxlen) ; } } } |
| 232 | else |
| 233 | { field = "" ; } } |
| 234 | else |
| 235 | { field = nextField(prompt.c_str()) ; } |
| 236 | if (field == "stdin" ) |
| 237 | { std::cout << "Switching to line mode" << std::endl ; |
| 238 | cmdField = -1 ; |
| 239 | field = nextField(prompt.c_str()) ; } } |
| 240 | /* |
| 241 | Are we left with something of the form param=value? If so, separate the |
| 242 | pieces, returning `param' and saving `value' for later use as per comments |
| 243 | at the head of the file. |
| 244 | */ |
| 245 | std::string::size_type found = field.find('='); |
| 246 | if (found != std::string::npos) |
| 247 | { pendingVal = field.substr(found+1) ; |
| 248 | field = field.substr(0,found) ; } |
| 249 | |
| 250 | return (field) ; } |
| 251 | |
| 252 | |
| 253 | /* |
| 254 | Function to look up a parameter keyword (name) in the parameter vector and |
| 255 | deal with the result. The keyword may end in one or more `?' characters; |
| 256 | this is a query for information about matching parameters. |
| 257 | |
| 258 | If we have a single match satisfying the minimal match requirements, and |
| 259 | there's no query, we simply return the index of the matching parameter in |
| 260 | the parameter vector. If there are no matches, and no query, the return |
| 261 | value will be -3. No matches on a query returns -1. |
| 262 | |
| 263 | A single short match, or a single match of any length with a query, will |
| 264 | result in a short help message |
| 265 | |
| 266 | If present, these values are set as follows: |
| 267 | * matchCntp is set to the number of parameters that matched. |
| 268 | * shortCntp is set to the number of matches that failed to meet the minimum |
| 269 | match requirement. |
| 270 | * queryCntp is set to the number of trailing `?' characters at the end |
| 271 | of name. |
| 272 | |
| 273 | Return values: |
| 274 | >0: index of the single unique match for the name |
| 275 | -1: query present |
| 276 | -2: no query, one or more short matches |
| 277 | -3: no query, no match |
| 278 | -4: multiple full matches (indicates configuration error) |
| 279 | |
| 280 | The final three parameters (matchCnt, shortCnt, queryCnt) are optional and |
| 281 | default to null. Use them if you want more detail on the match. |
| 282 | */ |
| 283 | |
| 284 | int lookupParam (std::string name, CoinParamVec ¶mVec, |
| 285 | int *matchCntp, int *shortCntp, int *queryCntp) |
| 286 | |
| 287 | { |
| 288 | int retval = -3 ; |
| 289 | |
| 290 | if (matchCntp != 0) |
| 291 | { *matchCntp = 0 ; } |
| 292 | if (shortCntp != 0) |
| 293 | { *shortCntp = 0 ; } |
| 294 | if (queryCntp != 0) |
| 295 | { *queryCntp = 0 ; } |
| 296 | /* |
| 297 | Is there anything here at all? |
| 298 | */ |
| 299 | if (name.length() == 0) |
| 300 | { return (retval) ; } |
| 301 | /* |
| 302 | Scan the parameter name to see if it ends in one or more `?' characters. If |
| 303 | so, take it as a request to return a list of parameters that match name up |
| 304 | to the first `?'. The strings '?' and '???' are considered to be valid |
| 305 | parameter names (short and long help, respectively) and are handled as |
| 306 | special cases: If the whole string is `?'s, one and three are commands as |
| 307 | is, while 2 and 4 or more are queries about `?' or `???'. |
| 308 | */ |
| 309 | int numQuery = 0 ; |
| 310 | { int length = static_cast<int>(name.length()) ; |
| 311 | int i ; |
| 312 | for (i = length-1 ; i >= 0 && name[i] == '?' ; i--) |
| 313 | { numQuery++ ; } |
| 314 | if (numQuery == length) |
| 315 | { switch (length) |
| 316 | { case 1: |
| 317 | case 3: |
| 318 | { numQuery = 0 ; |
| 319 | break ; } |
| 320 | case 2: |
| 321 | { numQuery -= 1 ; |
| 322 | break ; } |
| 323 | default: |
| 324 | { numQuery -= 3 ; |
| 325 | break ; } } } |
| 326 | name = name.substr(0,length-numQuery) ; |
| 327 | if (queryCntp != 0) |
| 328 | { *queryCntp = numQuery ; } } |
| 329 | /* |
| 330 | See if we can match the parameter name. On return, matchNdx is set to the |
| 331 | last match satisfying the minimal match criteria, or -1 if there's no |
| 332 | match. matchCnt is the number of matches satisfying the minimum match |
| 333 | length, and shortCnt is possible matches that were short of the minimum |
| 334 | match length, |
| 335 | */ |
| 336 | int matchNdx = -1 ; |
| 337 | int shortCnt = 0 ; |
| 338 | int matchCnt = CoinParamUtils::matchParam(paramVec,name,matchNdx,shortCnt) ; |
| 339 | /* |
| 340 | Set up return values before we get into further processing. |
| 341 | */ |
| 342 | if (matchCntp != 0) |
| 343 | { *matchCntp = matchCnt ; } |
| 344 | if (shortCntp != 0) |
| 345 | { *shortCntp = shortCnt ; } |
| 346 | if (numQuery > 0) |
| 347 | { retval = -1 ; } |
| 348 | else |
| 349 | { if (matchCnt+shortCnt == 0) |
| 350 | { retval = -3 ; } |
| 351 | else |
| 352 | if (matchCnt > 1) |
| 353 | { retval = -4 ; } |
| 354 | else |
| 355 | { retval = -2 ; } } |
| 356 | /* |
| 357 | No matches? Nothing more to be done here. |
| 358 | */ |
| 359 | if (matchCnt+shortCnt == 0) |
| 360 | { return (retval) ; } |
| 361 | /* |
| 362 | A unique match and no `?' in the name says we have our parameter. Return |
| 363 | the result. |
| 364 | */ |
| 365 | if (matchCnt == 1 && shortCnt == 0 && numQuery == 0) |
| 366 | { assert (matchNdx >= 0 && matchNdx < static_cast<int>(paramVec.size())) ; |
| 367 | return (matchNdx) ; } |
| 368 | /* |
| 369 | A single match? There are two possibilities: |
| 370 | * The string specified is shorter than the match length requested by the |
| 371 | parameter. (Useful for avoiding inadvertent execution of commands that |
| 372 | the client might regret.) |
| 373 | * The string specified contained a `?', in which case we print the help. |
| 374 | The match may or may not be short. |
| 375 | */ |
| 376 | if (matchCnt+shortCnt == 1) |
| 377 | { CoinParamUtils::shortOrHelpOne(paramVec,matchNdx,name,numQuery) ; |
| 378 | return (retval) ; } |
| 379 | /* |
| 380 | The final case: multiple matches. Most commonly this will be multiple short |
| 381 | matches. If we have multiple matches satisfying the minimal length |
| 382 | criteria, we have a configuration problem. The other question is whether |
| 383 | the user wanted help information. Two question marks gets short help. |
| 384 | */ |
| 385 | if (matchCnt > 1) |
| 386 | { std::cout |
| 387 | << "Configuration error! `" << name |
| 388 | <<"' was fully matched " << matchCnt << " times!" |
| 389 | << std::endl ; } |
| 390 | std::cout |
| 391 | << "Multiple matches for `" << name << "'; possible completions:" |
| 392 | << std::endl ; |
| 393 | CoinParamUtils::shortOrHelpMany(paramVec,name,numQuery) ; |
| 394 | |
| 395 | return (retval) ; } |
| 396 | |
| 397 | |
| 398 | /* |
| 399 | Utility functions to acquire parameter values from the command line. For |
| 400 | all of these, a pendingVal is consumed if it exists. |
| 401 | */ |
| 402 | |
| 403 | |
| 404 | /* |
| 405 | Read a string and return a pointer to the string. Set valid to indicate the |
| 406 | result of parsing: 0: okay, 1: <unused>, 2: not present. |
| 407 | */ |
| 408 | |
| 409 | std::string getStringField (int argc, const char *argv[], int *valid) |
| 410 | |
| 411 | { std::string field ; |
| 412 | |
| 413 | if (pendingVal != "" ) |
| 414 | { field = pendingVal ; |
| 415 | pendingVal = "" ; } |
| 416 | else |
| 417 | { field = "EOL" ; |
| 418 | if (cmdField > 0) |
| 419 | { if (cmdField < argc) |
| 420 | { field = argv[cmdField++] ; } } |
| 421 | else |
| 422 | { field = nextField(0) ; } } |
| 423 | |
| 424 | if (valid != 0) |
| 425 | { if (field != "EOL" ) |
| 426 | { *valid = 0 ; } |
| 427 | else |
| 428 | { *valid = 2 ; } } |
| 429 | |
| 430 | return (field) ; } |
| 431 | |
| 432 | /* |
| 433 | Read an int and return the value. Set valid to indicate the result of |
| 434 | parsing: 0: okay, 1: parse error, 2: not present. |
| 435 | */ |
| 436 | |
| 437 | int getIntField (int argc, const char *argv[], int *valid) |
| 438 | |
| 439 | { std::string field ; |
| 440 | |
| 441 | if (pendingVal != "" ) |
| 442 | { field = pendingVal ; |
| 443 | pendingVal = "" ; } |
| 444 | else |
| 445 | { field = "EOL" ; |
| 446 | if (cmdField > 0) |
| 447 | { if (cmdField < argc) |
| 448 | { field = argv[cmdField++] ; } } |
| 449 | else |
| 450 | { field = nextField(0) ; } } |
| 451 | /* |
| 452 | The only way to check for parse error here is to set the system variable |
| 453 | errno to 0 and then see if it's nonzero after we try to convert the string |
| 454 | to integer. |
| 455 | */ |
| 456 | int value = 0 ; |
| 457 | errno = 0 ; |
| 458 | if (field != "EOL" ) |
| 459 | { value = atoi(field.c_str()) ; } |
| 460 | |
| 461 | if (valid != 0) |
| 462 | { if (field != "EOL" ) |
| 463 | { if (errno == 0) |
| 464 | { *valid = 0 ; } |
| 465 | else |
| 466 | { *valid = 1 ; } } |
| 467 | else |
| 468 | { *valid = 2 ; } } |
| 469 | |
| 470 | return (value) ; } |
| 471 | |
| 472 | |
| 473 | /* |
| 474 | Read a double and return the value. Set valid to indicate the result of |
| 475 | parsing: 0: okay, 1: bad parse, 2: not present. But we'll never return |
| 476 | valid == 1 because atof gives us no way to tell.) |
| 477 | */ |
| 478 | |
| 479 | double getDoubleField (int argc, const char *argv[], int *valid) |
| 480 | |
| 481 | { std::string field ; |
| 482 | |
| 483 | if (pendingVal != "" ) |
| 484 | { field = pendingVal ; |
| 485 | pendingVal = "" ; } |
| 486 | else |
| 487 | { field = "EOL" ; |
| 488 | if (cmdField > 0) |
| 489 | { if (cmdField < argc) |
| 490 | { field = argv[cmdField++] ; } } |
| 491 | else |
| 492 | { field = nextField(0) ; } } |
| 493 | /* |
| 494 | The only way to check for parse error here is to set the system variable |
| 495 | errno to 0 and then see if it's nonzero after we try to convert the string |
| 496 | to integer. |
| 497 | */ |
| 498 | double value = 0.0 ; |
| 499 | errno = 0 ; |
| 500 | if (field != "EOL" ) |
| 501 | { value = atof(field.c_str()) ; } |
| 502 | |
| 503 | if (valid != 0) |
| 504 | { if (field != "EOL" ) |
| 505 | { if (errno == 0) |
| 506 | { *valid = 0 ; } |
| 507 | else |
| 508 | { *valid = 1 ; } } |
| 509 | else |
| 510 | { *valid = 2 ; } } |
| 511 | |
| 512 | return (value) ; } |
| 513 | |
| 514 | |
| 515 | /* |
| 516 | Utility function to scan a parameter vector for matches. Sets matchNdx to |
| 517 | the index of the last parameter that meets the minimal match criteria (but |
| 518 | note there should be at most one such parameter if the parameter vector is |
| 519 | properly configured). Sets shortCnt to the number of short matches (should |
| 520 | be zero in a properly configured vector if a minimal match is found). |
| 521 | Returns the number of matches satisfying the minimal match requirement |
| 522 | (should be 0 or 1 in a properly configured vector). |
| 523 | |
| 524 | The routine allows for the possibility of null entries in the parameter |
| 525 | vector. |
| 526 | |
| 527 | In order to handle `?' and `???', there's nothing to it but to force a |
| 528 | unique match if we match `?' exactly. (This is another quirk of clp/cbc |
| 529 | parameter parsing, which we need to match for historical reasons.) |
| 530 | */ |
| 531 | |
| 532 | int matchParam (const CoinParamVec ¶mVec, std::string name, |
| 533 | int &matchNdx, int &shortCnt) |
| 534 | |
| 535 | { |
| 536 | int vecLen = static_cast<int>(paramVec.size()) ; |
| 537 | int matchCnt = 0 ; |
| 538 | |
| 539 | matchNdx = -1 ; |
| 540 | shortCnt = 0 ; |
| 541 | |
| 542 | for (int i = 0 ; i < vecLen ; i++) |
| 543 | { CoinParam *param = paramVec[i] ; |
| 544 | if (param == 0) continue ; |
| 545 | int match = paramVec[i]->matches(name) ; |
| 546 | if (match == 1) |
| 547 | { matchNdx = i ; |
| 548 | matchCnt++ ; |
| 549 | if (name == "?" ) |
| 550 | { matchCnt = 1 ; |
| 551 | break ; } } |
| 552 | else |
| 553 | { shortCnt += match>>1 ; } } |
| 554 | |
| 555 | return (matchCnt) ; |
| 556 | } |
| 557 | |
| 558 | /* |
| 559 | Now a bunch of routines that are useful in the context of generating help |
| 560 | messages. |
| 561 | */ |
| 562 | |
| 563 | /* |
| 564 | Simple formatting routine for long messages. Used to print long help for |
| 565 | parameters. Lines are broken at the first white space after 65 characters, |
| 566 | or when an explicit return (`\n') character is scanned. Leading spaces are |
| 567 | suppressed. |
| 568 | */ |
| 569 | |
| 570 | void printIt (const char *msg) |
| 571 | |
| 572 | { int length = static_cast<int>(strlen(msg)) ; |
| 573 | char temp[101] ; |
| 574 | int i ; |
| 575 | int n = 0 ; |
| 576 | for (i = 0 ; i < length ; i++) |
| 577 | { if (msg[i] == '\n' || |
| 578 | (n >= 65 && (msg[i] == ' ' || msg[i] == '\t'))) |
| 579 | { temp[n] = '\0' ; |
| 580 | std::cout << temp << std::endl ; |
| 581 | n = 0 ; } |
| 582 | else |
| 583 | if (n || msg[i] != ' ') |
| 584 | { temp[n++] = msg[i] ; } } |
| 585 | if (n > 0) |
| 586 | { temp[n] = '\0' ; |
| 587 | std::cout << temp << std::endl ; } |
| 588 | |
| 589 | return ; } |
| 590 | |
| 591 | |
| 592 | /* |
| 593 | Utility function for the case where a name matches a single parameter, but |
| 594 | either it's short, or the user wanted help, or both. |
| 595 | |
| 596 | The routine allows for the possibility that there are null entries in the |
| 597 | parameter vector, but matchNdx should point to a valid entry if it's >= 0. |
| 598 | */ |
| 599 | |
| 600 | void shortOrHelpOne (CoinParamVec ¶mVec, |
| 601 | int matchNdx, std::string name, int numQuery) |
| 602 | |
| 603 | { int i ; |
| 604 | int numParams = static_cast<int>(paramVec.size()) ; |
| 605 | int lclNdx = -1 ; |
| 606 | /* |
| 607 | For a short match, we need to look up the parameter again. This should find |
| 608 | a short match, given the conditions where this routine is called. But be |
| 609 | prepared to find a full match. |
| 610 | |
| 611 | If matchNdx >= 0, just use the index we're handed. |
| 612 | */ |
| 613 | if (matchNdx < 0) |
| 614 | { int match = 0 ; |
| 615 | for (i = 0 ; i < numParams ; i++) |
| 616 | { CoinParam *param = paramVec[i] ; |
| 617 | if (param == 0) continue ; |
| 618 | int match = param->matches(name) ; |
| 619 | if (match != 0) |
| 620 | { lclNdx = i ; |
| 621 | break ; } } |
| 622 | |
| 623 | assert (lclNdx >= 0) ; |
| 624 | |
| 625 | if (match == 1) |
| 626 | { std::cout |
| 627 | << "Match for '" << name << "': " |
| 628 | << paramVec[matchNdx]->matchName() << "." ; } |
| 629 | else |
| 630 | { std::cout |
| 631 | << "Short match for '" << name << "'; possible completion: " |
| 632 | << paramVec[lclNdx]->matchName() << "." ; } } |
| 633 | else |
| 634 | { assert(matchNdx >= 0 && matchNdx < static_cast<int>(paramVec.size())) ; |
| 635 | std::cout << "Match for `" << name << "': " |
| 636 | << paramVec[matchNdx]->matchName() ; |
| 637 | lclNdx = matchNdx ; } |
| 638 | /* |
| 639 | Print some help, if there was a `?' in the name. `??' gets the long help. |
| 640 | */ |
| 641 | if (numQuery > 0) |
| 642 | { std::cout << std::endl ; |
| 643 | if (numQuery == 1) |
| 644 | { std::cout << paramVec[lclNdx]->shortHelp() ; } |
| 645 | else |
| 646 | { paramVec[lclNdx]->printLongHelp() ; } } |
| 647 | std::cout << std::endl ; |
| 648 | |
| 649 | return ; } |
| 650 | |
| 651 | /* |
| 652 | Utility function for the case where a name matches multiple parameters. |
| 653 | Zero or one `?' gets just the matching names, while `??' gets short help |
| 654 | with each match. |
| 655 | |
| 656 | The routine allows for the possibility that there are null entries in the |
| 657 | parameter vector. |
| 658 | */ |
| 659 | |
| 660 | void shortOrHelpMany (CoinParamVec ¶mVec, std::string name, int numQuery) |
| 661 | |
| 662 | { int numParams = static_cast<int>(paramVec.size()) ; |
| 663 | /* |
| 664 | Scan the parameter list. For each match, print just the name, or the name |
| 665 | and short help. |
| 666 | */ |
| 667 | int lineLen = 0 ; |
| 668 | bool printed = false ; |
| 669 | for (int i = 0 ; i < numParams ; i++) |
| 670 | { CoinParam *param = paramVec[i] ; |
| 671 | if (param == 0) continue ; |
| 672 | int match = param->matches(name) ; |
| 673 | if (match > 0) |
| 674 | { std::string nme = param->matchName() ; |
| 675 | int len = static_cast<int>(nme.length()) ; |
| 676 | if (numQuery >= 2) |
| 677 | { std::cout << nme << " : " << param->shortHelp() ; |
| 678 | std::cout << std::endl ; } |
| 679 | else |
| 680 | { lineLen += 2+len ; |
| 681 | if (lineLen > 80) |
| 682 | { std::cout << std::endl ; |
| 683 | lineLen = 2+len ; } |
| 684 | std::cout << " " << nme ; |
| 685 | printed = true ; } } } |
| 686 | |
| 687 | if (printed) |
| 688 | { std::cout << std::endl ; } |
| 689 | |
| 690 | return ; } |
| 691 | |
| 692 | |
| 693 | /* |
| 694 | A generic help message that explains the basic operation of parameter |
| 695 | parsing. |
| 696 | */ |
| 697 | |
| 698 | void printGenericHelp () |
| 699 | |
| 700 | { std::cout << std::endl ; |
| 701 | std::cout |
| 702 | << "For command line arguments, keywords have a leading `-' or '--'; " |
| 703 | << std::endl ; |
| 704 | std::cout |
| 705 | << "-stdin or just - switches to stdin with a prompt." |
| 706 | << std::endl ; |
| 707 | std::cout |
| 708 | << "When prompted, one command per line, without the leading `-'." |
| 709 | << std::endl ; |
| 710 | std::cout |
| 711 | << "abcd value sets abcd to value." |
| 712 | << std::endl ; |
| 713 | std::cout |
| 714 | << "abcd without a value (where one is expected) gives the current value." |
| 715 | << std::endl ; |
| 716 | std::cout |
| 717 | << "abcd? gives a list of possible matches; if there's only one, a short" |
| 718 | << std::endl ; |
| 719 | std::cout |
| 720 | << "help message is printed." |
| 721 | << std::endl ; |
| 722 | std::cout |
| 723 | << "abcd?? prints the short help for all matches; if there's only one" |
| 724 | << std::endl ; |
| 725 | std::cout |
| 726 | << "match, a longer help message and current value are printed." |
| 727 | << std::endl ; |
| 728 | |
| 729 | return ; } |
| 730 | |
| 731 | |
| 732 | /* |
| 733 | Utility function for various levels of `help' command. The entries between |
| 734 | paramVec[firstParam] and paramVec[lastParam], inclusive, will be printed. |
| 735 | If shortHelp is true, the short help message will be printed for each |
| 736 | parameter. If longHelp is true, the long help message will be printed for |
| 737 | each parameter. If hidden is true, even parameters with display = false |
| 738 | will be printed. Each line is prefaced with the specified prefix. |
| 739 | |
| 740 | The routine allows for the possibility that there are null entries in the |
| 741 | parameter vector. |
| 742 | */ |
| 743 | |
| 744 | void printHelp (CoinParamVec ¶mVec, int firstParam, int lastParam, |
| 745 | std::string prefix, |
| 746 | bool shortHelp, bool longHelp, bool hidden) |
| 747 | |
| 748 | { bool noHelp = !(shortHelp || longHelp) ; |
| 749 | int i ; |
| 750 | int pfxLen = static_cast<int>(prefix.length()) ; |
| 751 | bool printed = false ; |
| 752 | |
| 753 | if (noHelp) |
| 754 | { int lineLen = 0 ; |
| 755 | for (i = firstParam ; i <= lastParam ; i++) |
| 756 | { CoinParam *param = paramVec[i] ; |
| 757 | if (param == 0) continue ; |
| 758 | if (param->display() || hidden) |
| 759 | { std::string nme = param->matchName() ; |
| 760 | int len = static_cast<int>(nme.length()) ; |
| 761 | if (!printed) |
| 762 | { std::cout << std::endl << prefix ; |
| 763 | lineLen += pfxLen ; |
| 764 | printed = true ; } |
| 765 | lineLen += 2+len ; |
| 766 | if (lineLen > 80) |
| 767 | { std::cout << std::endl << prefix ; |
| 768 | lineLen = pfxLen+2+len ; } |
| 769 | std::cout << " " << nme ; } } |
| 770 | if (printed) |
| 771 | { std::cout << std::endl ; } } |
| 772 | else |
| 773 | if (shortHelp) |
| 774 | { for (i = firstParam ; i <= lastParam ; i++) |
| 775 | { CoinParam *param = paramVec[i] ; |
| 776 | if (param == 0) continue ; |
| 777 | if (param->display() || hidden) |
| 778 | { std::cout << std::endl << prefix ; |
| 779 | std::cout << param->matchName() ; |
| 780 | std::cout << ": " ; |
| 781 | std::cout << param->shortHelp() ; } } |
| 782 | std::cout << std::endl ; } |
| 783 | else |
| 784 | if (longHelp) |
| 785 | { for (i = firstParam ; i <= lastParam ; i++) |
| 786 | { CoinParam *param = paramVec[i] ; |
| 787 | if (param == 0) continue ; |
| 788 | if (param->display() || hidden) |
| 789 | { std::cout << std::endl << prefix ; |
| 790 | std::cout << "Command: " << param->matchName() ; |
| 791 | std::cout << std::endl << prefix ; |
| 792 | std::cout << "---- description" << std::endl ; |
| 793 | printIt(param->longHelp().c_str()) ; |
| 794 | std::cout << prefix << "----" << std::endl ; } } } |
| 795 | |
| 796 | std::cout << std::endl ; |
| 797 | |
| 798 | return ; } |
| 799 | |
| 800 | } // end namespace CoinParamUtils |
| 801 | |