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