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