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