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