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