1 /* 2 * Copyright (C) 1984-2002 Mark Nudelman 3 * 4 * You may distribute under the terms of either the GNU General Public 5 * License or the Less License, as specified in the README file. 6 * 7 * For more information about less, or for information on how to 8 * contact the author, see the README file. 9 */ 10 11 12 /* 13 * Process command line options. 14 * 15 * Each option is a single letter which controls a program variable. 16 * The options have defaults which may be changed via 17 * the command line option, toggled via the "-" command, 18 * or queried via the "_" command. 19 */ 20 21 #include "less.h" 22 #include "option.h" 23 24 static struct loption *pendopt; 25 public int plusoption = FALSE; 26 27 static char *propt(); 28 static char *optstring(); 29 static int flip_triple(); 30 31 extern int screen_trashed; 32 extern char *every_first_cmd; 33 34 /* 35 * Scan an argument (either from the command line or from the 36 * LESS environment variable) and process it. 37 */ 38 public void 39 scan_option(s) 40 char *s; 41 { 42 register struct loption *o; 43 register int optc; 44 char *optname; 45 char *printopt; 46 char *str; 47 int set_default; 48 int lc; 49 int err; 50 PARG parg; 51 52 if (s == NULL) 53 return; 54 55 /* 56 * If we have a pending option which requires an argument, 57 * handle it now. 58 * This happens if the previous option was, for example, "-P" 59 * without a following string. In that case, the current 60 * option is simply the argument for the previous option. 61 */ 62 if (pendopt != NULL) 63 { 64 switch (pendopt->otype & OTYPE) 65 { 66 case STRING: 67 (*pendopt->ofunc)(INIT, s); 68 break; 69 case NUMBER: 70 printopt = propt(pendopt->oletter); 71 *(pendopt->ovar) = getnum(&s, printopt, (int*)NULL); 72 break; 73 } 74 pendopt = NULL; 75 return; 76 } 77 78 set_default = FALSE; 79 optname = NULL; 80 81 while (*s != '\0') 82 { 83 /* 84 * Check some special cases first. 85 */ 86 switch (optc = *s++) 87 { 88 case ' ': 89 case '\t': 90 case END_OPTION_STRING: 91 continue; 92 case '-': 93 #if GNU_OPTIONS 94 /* 95 * "--" indicates an option name instead of a letter. 96 */ 97 if (*s == '-') 98 { 99 optname = ++s; 100 break; 101 } 102 #endif 103 /* 104 * "-+" means set these options back to their defaults. 105 * (They may have been set otherwise by previous 106 * options.) 107 */ 108 set_default = (*s == '+'); 109 if (set_default) 110 s++; 111 continue; 112 case '+': 113 /* 114 * An option prefixed by a "+" is ungotten, so 115 * that it is interpreted as less commands 116 * processed at the start of the first input file. 117 * "++" means process the commands at the start of 118 * EVERY input file. 119 */ 120 plusoption = TRUE; 121 s = optstring(s, &str, propt('+'), NULL); 122 if (*str == '+') 123 every_first_cmd = save(++str); 124 else 125 ungetsc(str); 126 continue; 127 case '0': case '1': case '2': case '3': case '4': 128 case '5': case '6': case '7': case '8': case '9': 129 /* 130 * Special "more" compatibility form "-<number>" 131 * instead of -z<number> to set the scrolling 132 * window size. 133 */ 134 s--; 135 optc = 'z'; 136 break; 137 } 138 139 /* 140 * Not a special case. 141 * Look up the option letter in the option table. 142 */ 143 err = 0; 144 if (optname == NULL) 145 { 146 printopt = propt(optc); 147 lc = SIMPLE_IS_LOWER(optc); 148 o = findopt(optc); 149 } 150 #if GNU_OPTIONS 151 else 152 { 153 printopt = optname; 154 lc = SIMPLE_IS_LOWER(optname[0]); 155 o = findopt_name(&optname, NULL, &err); 156 s = optname; 157 optname = NULL; 158 if (*s == '\0' || *s == ' ') 159 { 160 /* 161 * The option name matches exactly. 162 */ 163 ; 164 } else if (*s == '=') 165 { 166 /* 167 * The option name is followed by "=value". 168 */ 169 if (o != NULL && 170 (o->otype & OTYPE) != STRING && 171 (o->otype & OTYPE) != NUMBER) 172 { 173 parg.p_string = printopt; 174 error("The %s option should not be followed by =", 175 &parg); 176 quit(QUIT_ERROR); 177 } 178 s++; 179 } else 180 { 181 /* 182 * The specified name is longer than the 183 * real option name. 184 */ 185 o = NULL; 186 } 187 } 188 #endif 189 if (o == NULL) 190 { 191 parg.p_string = printopt; 192 if (err == OPT_AMBIG) 193 error("%s is an ambiguous abbreviation (\"less --help\" for help)", 194 &parg); 195 else 196 error("There is no %s option (\"less --help\" for help)", 197 &parg); 198 quit(QUIT_ERROR); 199 } 200 201 str = NULL; 202 switch (o->otype & OTYPE) 203 { 204 case BOOL: 205 if (set_default) 206 *(o->ovar) = o->odefault; 207 else 208 *(o->ovar) = ! o->odefault; 209 break; 210 case TRIPLE: 211 if (set_default) 212 *(o->ovar) = o->odefault; 213 else 214 *(o->ovar) = flip_triple(o->odefault, lc); 215 break; 216 case STRING: 217 if (*s == '\0') 218 { 219 /* 220 * Set pendopt and return. 221 * We will get the string next time 222 * scan_option is called. 223 */ 224 pendopt = o; 225 return; 226 } 227 /* 228 * Don't do anything here. 229 * All processing of STRING options is done by 230 * the handling function. 231 */ 232 while (*s == ' ') 233 s++; 234 s = optstring(s, &str, printopt, o->odesc[1]); 235 break; 236 case NUMBER: 237 if (*s == '\0') 238 { 239 pendopt = o; 240 return; 241 } 242 *(o->ovar) = getnum(&s, printopt, (int*)NULL); 243 break; 244 } 245 /* 246 * If the option has a handling function, call it. 247 */ 248 if (o->ofunc != NULL) 249 (*o->ofunc)(INIT, str); 250 } 251 } 252 253 /* 254 * Toggle command line flags from within the program. 255 * Used by the "-" and "_" commands. 256 * how_toggle may be: 257 * OPT_NO_TOGGLE just report the current setting, without changing it. 258 * OPT_TOGGLE invert the current setting 259 * OPT_UNSET set to the default value 260 * OPT_SET set to the inverse of the default value 261 */ 262 public void 263 toggle_option(c, s, how_toggle) 264 int c; 265 char *s; 266 int how_toggle; 267 { 268 register struct loption *o; 269 register int num; 270 int no_prompt; 271 int err; 272 PARG parg; 273 274 no_prompt = (how_toggle & OPT_NO_PROMPT); 275 how_toggle &= ~OPT_NO_PROMPT; 276 277 /* 278 * Look up the option letter in the option table. 279 */ 280 o = findopt(c); 281 if (o == NULL) 282 { 283 parg.p_string = propt(c); 284 error("There is no %s option", &parg); 285 return; 286 } 287 288 if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) 289 { 290 parg.p_string = propt(c); 291 error("Cannot change the %s option", &parg); 292 return; 293 } 294 295 if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) 296 { 297 parg.p_string = propt(c); 298 error("Cannot query the %s option", &parg); 299 return; 300 } 301 302 /* 303 * Check for something which appears to be a do_toggle 304 * (because the "-" command was used), but really is not. 305 * This could be a string option with no string, or 306 * a number option with no number. 307 */ 308 switch (o->otype & OTYPE) 309 { 310 case STRING: 311 case NUMBER: 312 if (how_toggle == OPT_TOGGLE && *s == '\0') 313 how_toggle = OPT_NO_TOGGLE; 314 break; 315 } 316 317 #if HILITE_SEARCH 318 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) 319 repaint_hilite(0); 320 #endif 321 322 /* 323 * Now actually toggle (change) the variable. 324 */ 325 if (how_toggle != OPT_NO_TOGGLE) 326 { 327 switch (o->otype & OTYPE) 328 { 329 case BOOL: 330 /* 331 * Boolean. 332 */ 333 switch (how_toggle) 334 { 335 case OPT_TOGGLE: 336 *(o->ovar) = ! *(o->ovar); 337 break; 338 case OPT_UNSET: 339 *(o->ovar) = o->odefault; 340 break; 341 case OPT_SET: 342 *(o->ovar) = ! o->odefault; 343 break; 344 } 345 break; 346 case TRIPLE: 347 /* 348 * Triple: 349 * If user gave the lower case letter, then switch 350 * to 1 unless already 1, in which case make it 0. 351 * If user gave the upper case letter, then switch 352 * to 2 unless already 2, in which case make it 0. 353 */ 354 switch (how_toggle) 355 { 356 case OPT_TOGGLE: 357 *(o->ovar) = flip_triple(*(o->ovar), 358 islower(c)); 359 break; 360 case OPT_UNSET: 361 *(o->ovar) = o->odefault; 362 break; 363 case OPT_SET: 364 *(o->ovar) = flip_triple(o->odefault, 365 islower(c)); 366 break; 367 } 368 break; 369 case STRING: 370 /* 371 * String: don't do anything here. 372 * The handling function will do everything. 373 */ 374 switch (how_toggle) 375 { 376 case OPT_SET: 377 case OPT_UNSET: 378 error("Cannot use \"-+\" or \"--\" for a string option", 379 NULL_PARG); 380 return; 381 } 382 break; 383 case NUMBER: 384 /* 385 * Number: set the variable to the given number. 386 */ 387 switch (how_toggle) 388 { 389 case OPT_TOGGLE: 390 num = getnum(&s, NULL, &err); 391 if (!err) 392 *(o->ovar) = num; 393 break; 394 case OPT_UNSET: 395 *(o->ovar) = o->odefault; 396 break; 397 case OPT_SET: 398 error("Can't use \"-!\" for a numeric option", 399 NULL_PARG); 400 return; 401 } 402 break; 403 } 404 } 405 406 /* 407 * Call the handling function for any special action 408 * specific to this option. 409 */ 410 if (o->ofunc != NULL) 411 (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s); 412 413 #if HILITE_SEARCH 414 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT)) 415 chg_hilite(); 416 #endif 417 418 if (!no_prompt) 419 { 420 /* 421 * Print a message describing the new setting. 422 */ 423 switch (o->otype & OTYPE) 424 { 425 case BOOL: 426 case TRIPLE: 427 /* 428 * Print the odesc message. 429 */ 430 error(o->odesc[*(o->ovar)], NULL_PARG); 431 break; 432 case NUMBER: 433 /* 434 * The message is in odesc[1] and has a %d for 435 * the value of the variable. 436 */ 437 parg.p_int = *(o->ovar); 438 error(o->odesc[1], &parg); 439 break; 440 case STRING: 441 /* 442 * Message was already printed by the handling function. 443 */ 444 break; 445 } 446 } 447 448 if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT)) 449 screen_trashed = TRUE; 450 } 451 452 /* 453 * "Toggle" a triple-valued option. 454 */ 455 static int 456 flip_triple(val, lc) 457 int val; 458 int lc; 459 { 460 if (lc) 461 return ((val == OPT_ON) ? OPT_OFF : OPT_ON); 462 else 463 return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS); 464 } 465 466 /* 467 * Return a string suitable for printing as the "name" of an option. 468 * For example, if the option letter is 'x', just return "-x". 469 */ 470 static char * 471 propt(c) 472 int c; 473 { 474 static char buf[8]; 475 476 snprintf(buf, sizeof(buf), "-%s", prchar(c)); 477 return (buf); 478 } 479 480 /* 481 * Determine if an option is a single character option (BOOL or TRIPLE), 482 * or if it a multi-character option (NUMBER). 483 */ 484 public int 485 single_char_option(c) 486 int c; 487 { 488 register struct loption *o; 489 490 o = findopt(c); 491 if (o == NULL) 492 return (TRUE); 493 return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0); 494 } 495 496 /* 497 * Return the prompt to be used for a given option letter. 498 * Only string and number valued options have prompts. 499 */ 500 public char * 501 opt_prompt(c) 502 int c; 503 { 504 register struct loption *o; 505 506 o = findopt(c); 507 if (o == NULL || (o->otype & (STRING|NUMBER)) == 0) 508 return (NULL); 509 return (o->odesc[0]); 510 } 511 512 /* 513 * Return whether or not there is a string option pending; 514 * that is, if the previous option was a string-valued option letter 515 * (like -P) without a following string. 516 * In that case, the current option is taken to be the string for 517 * the previous option. 518 */ 519 public int 520 isoptpending() 521 { 522 return (pendopt != NULL); 523 } 524 525 /* 526 * Print error message about missing string. 527 */ 528 static void 529 nostring(printopt) 530 char *printopt; 531 { 532 PARG parg; 533 parg.p_string = printopt; 534 error("Value is required after %s", &parg); 535 } 536 537 /* 538 * Print error message if a STRING type option is not followed by a string. 539 */ 540 public void 541 nopendopt() 542 { 543 nostring(propt(pendopt->oletter)); 544 } 545 546 /* 547 * Scan to end of string or to an END_OPTION_STRING character. 548 * In the latter case, replace the char with a null char. 549 * Return a pointer to the remainder of the string, if any. 550 */ 551 static char * 552 optstring(s, p_str, printopt, validchars) 553 char *s; 554 char **p_str; 555 char *printopt; 556 char *validchars; 557 { 558 register char *p; 559 560 if (*s == '\0') 561 { 562 nostring(printopt); 563 quit(QUIT_ERROR); 564 } 565 *p_str = s; 566 for (p = s; *p != '\0'; p++) 567 { 568 if (*p == END_OPTION_STRING || 569 (validchars != NULL && strchr(validchars, *p) == NULL)) 570 { 571 switch (*p) 572 { 573 case END_OPTION_STRING: 574 case ' ': case '\t': case '-': 575 /* Replace the char with a null to terminate string. */ 576 *p++ = '\0'; 577 break; 578 default: 579 /* Cannot replace char; make a copy of the string. */ 580 *p_str = (char *) ecalloc(p-s+1, sizeof(char)); 581 strncpy(*p_str, s, p-s); 582 (*p_str)[p-s] = '\0'; 583 break; 584 } 585 break; 586 } 587 } 588 return (p); 589 } 590 591 /* 592 * Translate a string into a number. 593 * Like atoi(), but takes a pointer to a char *, and updates 594 * the char * to point after the translated number. 595 */ 596 public int 597 getnum(sp, printopt, errp) 598 char **sp; 599 char *printopt; 600 int *errp; 601 { 602 register char *s; 603 register int n; 604 register int neg; 605 PARG parg; 606 607 s = skipsp(*sp); 608 neg = FALSE; 609 if (*s == '-') 610 { 611 neg = TRUE; 612 s++; 613 } 614 if (*s < '0' || *s > '9') 615 { 616 if (errp != NULL) 617 { 618 *errp = TRUE; 619 return (-1); 620 } 621 if (printopt != NULL) 622 { 623 parg.p_string = printopt; 624 error("Number is required after %s", &parg); 625 } 626 quit(QUIT_ERROR); 627 } 628 629 n = 0; 630 while (*s >= '0' && *s <= '9') 631 n = 10 * n + *s++ - '0'; 632 *sp = s; 633 if (errp != NULL) 634 *errp = FALSE; 635 if (neg) 636 n = -n; 637 return (n); 638 } 639