Annotation of src/usr.bin/less/option.c, Revision 1.15
1.1 etheisen 1: /*
1.8 shadchin 2: * Copyright (C) 1984-2012 Mark Nudelman
1.11 nicm 3: * Modified for use with illumos by Garrett D'Amore.
4: * Copyright 2014 Garrett D'Amore <garrett@damore.org>
1.1 etheisen 5: *
1.5 millert 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.
1.1 etheisen 8: *
1.8 shadchin 9: * For more information, see the README file.
1.10 nicm 10: */
1.1 etheisen 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
1.10 nicm 17: * the command line option, toggled via the "-" command,
1.1 etheisen 18: * or queried via the "_" command.
19: */
20:
21: #include "less.h"
22: #include "option.h"
23:
1.5 millert 24: static struct loption *pendopt;
1.10 nicm 25: int plusoption = FALSE;
1.1 etheisen 26:
1.15 ! nicm 27: static char *optstring(char *, char **, char *, char *);
! 28: static int flip_triple(int, int);
1.1 etheisen 29:
30: extern int screen_trashed;
1.7 shadchin 31: extern int less_is_more;
32: extern int quit_at_eof;
1.1 etheisen 33: extern char *every_first_cmd;
1.8 shadchin 34: extern int opt_use_backslash;
1.1 etheisen 35:
1.7 shadchin 36: /*
37: * Return a printable description of an option.
38: */
1.10 nicm 39: static char *
40: opt_desc(struct loption *o)
1.7 shadchin 41: {
42: static char buf[OPTNAME_MAX + 10];
43: if (o->oletter == OLETTER_NONE)
1.10 nicm 44: (void) snprintf(buf, sizeof (buf), "--%s", o->onames->oname);
1.7 shadchin 45: else
1.10 nicm 46: (void) snprintf(buf, sizeof (buf), "-%c (--%s)",
47: o->oletter, o->onames->oname);
1.7 shadchin 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: */
1.10 nicm 55: char *
56: propt(int c)
1.7 shadchin 57: {
58: static char buf[8];
59:
1.10 nicm 60: (void) snprintf(buf, sizeof (buf), "-%s", prchar(c));
1.7 shadchin 61: return (buf);
62: }
63:
1.10 nicm 64: /*
65: * Scan an argument (either from the command line or from the
1.1 etheisen 66: * LESS environment variable) and process it.
67: */
1.10 nicm 68: void
69: scan_option(char *s)
1.1 etheisen 70: {
1.10 nicm 71: struct loption *o;
72: int optc;
1.5 millert 73: char *optname;
74: char *printopt;
1.1 etheisen 75: char *str;
76: int set_default;
1.5 millert 77: int lc;
78: int err;
1.10 nicm 79: int moreopt;
1.1 etheisen 80: PARG parg;
81:
82: if (s == NULL)
83: return;
84:
85: /*
1.5 millert 86: * If we have a pending option which requires an argument,
87: * handle it now.
1.1 etheisen 88: * This happens if the previous option was, for example, "-P"
89: * without a following string. In that case, the current
1.5 millert 90: * option is simply the argument for the previous option.
1.1 etheisen 91: */
1.10 nicm 92: if (pendopt != NULL) {
93: switch (pendopt->otype & OTYPE) {
1.5 millert 94: case STRING:
95: (*pendopt->ofunc)(INIT, s);
96: break;
97: case NUMBER:
1.7 shadchin 98: printopt = opt_desc(pendopt);
1.10 nicm 99: *(pendopt->ovar) = getnum(&s, printopt, NULL);
1.5 millert 100: break;
101: }
1.1 etheisen 102: pendopt = NULL;
103: return;
104: }
105:
106: set_default = FALSE;
1.5 millert 107: optname = NULL;
1.10 nicm 108: moreopt = 0;
109: o = NULL;
1.1 etheisen 110:
1.10 nicm 111: while (*s != '\0') {
1.1 etheisen 112: /*
113: * Check some special cases first.
114: */
1.10 nicm 115: switch (optc = *s++) {
1.1 etheisen 116: case ' ':
117: case '\t':
118: case END_OPTION_STRING:
119: continue;
120: case '-':
121: /*
1.5 millert 122: * "--" indicates an option name instead of a letter.
123: */
1.10 nicm 124: if (*s == '-') {
125: if (!less_is_more) {
126: optname = ++s;
127: }
1.5 millert 128: break;
129: }
130: /*
1.1 etheisen 131: * "-+" means set these options back to their defaults.
1.10 nicm 132: * (They may have been set otherwise by previous
1.1 etheisen 133: * options.)
134: */
1.10 nicm 135: if (!less_is_more) {
136: set_default = (*s == '+');
137: if (set_default)
138: s++;
139: }
1.1 etheisen 140: continue;
141: case '+':
142: /*
1.10 nicm 143: * An option prefixed by a "+" is ungotten, so
144: * that it is interpreted as less commands
1.1 etheisen 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;
1.5 millert 150: s = optstring(s, &str, propt('+'), NULL);
1.8 shadchin 151: if (s == NULL)
152: return;
1.5 millert 153: if (*str == '+')
1.12 tedu 154: every_first_cmd = estrdup(str+1);
1.1 etheisen 155: else
1.5 millert 156: ungetsc(str);
1.8 shadchin 157: free(str);
1.1 etheisen 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>"
1.10 nicm 163: * instead of -z<number> to set the scrolling
1.1 etheisen 164: * window size.
165: */
166: s--;
1.5 millert 167: optc = 'z';
1.10 nicm 168: moreopt = 1;
1.1 etheisen 169: break;
1.7 shadchin 170: case 'n':
1.10 nicm 171: if (less_is_more) {
172: moreopt = 1;
1.7 shadchin 173: optc = 'z';
1.10 nicm 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: }
1.7 shadchin 205: break;
1.1 etheisen 206: }
207:
208: /*
209: * Not a special case.
210: * Look up the option letter in the option table.
211: */
1.5 millert 212: err = 0;
1.10 nicm 213: if (optname == NULL) {
1.5 millert 214: printopt = propt(optc);
1.10 nicm 215: lc = islower(optc);
1.5 millert 216: o = findopt(optc);
1.10 nicm 217: if (less_is_more && (!moreopt) && (o != NULL) &&
218: ((o->otype & MORE_OK) == 0)) {
219: o = NULL;
220: }
221: } else {
1.5 millert 222: printopt = optname;
1.10 nicm 223: lc = islower(optname[0]);
1.5 millert 224: o = findopt_name(&optname, NULL, &err);
225: s = optname;
226: optname = NULL;
1.10 nicm 227: switch (*s) {
228: case ' ': /* name matches exactly */
229: case '\0':
230: break;
231:
232: case '=': /* name followed by "=value" */
1.5 millert 233: if (o != NULL &&
234: (o->otype & OTYPE) != STRING &&
1.10 nicm 235: (o->otype & OTYPE) != NUMBER) {
1.5 millert 236: parg.p_string = printopt;
1.10 nicm 237: error("The %s option should not be "
238: "followed by =", &parg);
1.8 shadchin 239: return;
1.5 millert 240: }
241: s++;
1.10 nicm 242: break;
243: default: /* name longer than option, bad */
1.5 millert 244: o = NULL;
245: }
246: }
1.10 nicm 247: if (o == NULL) {
1.5 millert 248: parg.p_string = printopt;
1.10 nicm 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: }
1.8 shadchin 259: return;
1.1 etheisen 260: }
261:
1.5 millert 262: str = NULL;
1.10 nicm 263: switch (o->otype & OTYPE) {
1.1 etheisen 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
1.5 millert 274: *(o->ovar) = flip_triple(o->odefault, lc);
1.1 etheisen 275: break;
276: case STRING:
1.10 nicm 277: if (*s == '\0') {
1.1 etheisen 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.
1.10 nicm 288: * All processing of STRING options is done by
1.1 etheisen 289: * the handling function.
290: */
1.5 millert 291: while (*s == ' ')
292: s++;
293: s = optstring(s, &str, printopt, o->odesc[1]);
1.8 shadchin 294: if (s == NULL)
295: return;
1.1 etheisen 296: break;
297: case NUMBER:
1.10 nicm 298: if (*s == '\0') {
1.5 millert 299: pendopt = o;
300: return;
301: }
1.10 nicm 302: *(o->ovar) = getnum(&s, printopt, NULL);
1.1 etheisen 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);
1.13 mmcc 310: free(str);
1.1 etheisen 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: */
1.10 nicm 323: void
324: toggle_option(struct loption *o, int lower, char *s, int how_toggle)
1.1 etheisen 325: {
1.10 nicm 326: int num;
1.5 millert 327: int no_prompt;
1.1 etheisen 328: int err;
329: PARG parg;
330:
1.5 millert 331: no_prompt = (how_toggle & OPT_NO_PROMPT);
332: how_toggle &= ~OPT_NO_PROMPT;
333:
1.10 nicm 334: if (o == NULL) {
1.14 deraadt 335: error("No such option", NULL);
1.1 etheisen 336: return;
337: }
338:
1.10 nicm 339: if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) {
1.7 shadchin 340: parg.p_string = opt_desc(o);
1.5 millert 341: error("Cannot change the %s option", &parg);
1.1 etheisen 342: return;
1.7 shadchin 343: }
1.1 etheisen 344:
1.10 nicm 345: if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) {
1.7 shadchin 346: parg.p_string = opt_desc(o);
1.5 millert 347: error("Cannot query the %s option", &parg);
1.1 etheisen 348: return;
1.10 nicm 349: }
1.1 etheisen 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: */
1.10 nicm 357: switch (o->otype & OTYPE) {
1.1 etheisen 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: */
1.10 nicm 371: if (how_toggle != OPT_NO_TOGGLE) {
372: switch (o->otype & OTYPE) {
1.1 etheisen 373: case BOOL:
374: /*
375: * Boolean.
376: */
1.10 nicm 377: switch (how_toggle) {
1.1 etheisen 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:
1.10 nicm 392: * If user gave the lower case letter, then switch
1.1 etheisen 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: */
1.10 nicm 397: switch (how_toggle) {
1.1 etheisen 398: case OPT_TOGGLE:
1.7 shadchin 399: *(o->ovar) = flip_triple(*(o->ovar), lower);
1.1 etheisen 400: break;
401: case OPT_UNSET:
402: *(o->ovar) = o->odefault;
403: break;
404: case OPT_SET:
1.7 shadchin 405: *(o->ovar) = flip_triple(o->odefault, lower);
1.1 etheisen 406: break;
407: }
408: break;
409: case STRING:
410: /*
411: * String: don't do anything here.
412: * The handling function will do everything.
413: */
1.10 nicm 414: switch (how_toggle) {
1.1 etheisen 415: case OPT_SET:
416: case OPT_UNSET:
1.10 nicm 417: error("Cannot use \"-+\" or \"--\" "
1.14 deraadt 418: "for a string option", NULL);
1.1 etheisen 419: return;
420: }
421: break;
422: case NUMBER:
423: /*
424: * Number: set the variable to the given number.
425: */
1.10 nicm 426: switch (how_toggle) {
1.1 etheisen 427: case OPT_TOGGLE:
1.5 millert 428: num = getnum(&s, NULL, &err);
1.1 etheisen 429: if (!err)
430: *(o->ovar) = num;
431: break;
432: case OPT_UNSET:
433: *(o->ovar) = o->odefault;
434: break;
435: case OPT_SET:
1.5 millert 436: error("Can't use \"-!\" for a numeric option",
1.14 deraadt 437: NULL);
1.1 etheisen 438: return;
439: }
440: break;
441: }
442: }
443:
444: /*
1.10 nicm 445: * Call the handling function for any special action
1.1 etheisen 446: * specific to this option.
447: */
448: if (o->ofunc != NULL)
1.10 nicm 449: (*o->ofunc)((how_toggle == OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
1.1 etheisen 450:
451: if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
452: chg_hilite();
453:
1.10 nicm 454: if (!no_prompt) {
1.1 etheisen 455: /*
1.5 millert 456: * Print a message describing the new setting.
1.1 etheisen 457: */
1.10 nicm 458: switch (o->otype & OTYPE) {
1.5 millert 459: case BOOL:
460: case TRIPLE:
461: /*
462: * Print the odesc message.
463: */
1.14 deraadt 464: error(o->odesc[*(o->ovar)], NULL);
1.5 millert 465: break;
466: case NUMBER:
467: /*
1.10 nicm 468: * The message is in odesc[1] and has a %d for
1.5 millert 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: }
1.1 etheisen 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: */
1.10 nicm 489: static int
490: flip_triple(int val, int lc)
1.1 etheisen 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: /*
1.7 shadchin 499: * Determine if an option takes a parameter.
1.1 etheisen 500: */
1.10 nicm 501: int
502: opt_has_param(struct loption *o)
1.1 etheisen 503: {
504: if (o == NULL)
1.7 shadchin 505: return (0);
506: if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
507: return (0);
508: return (1);
1.1 etheisen 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: */
1.10 nicm 515: char *
516: opt_prompt(struct loption *o)
1.1 etheisen 517: {
518: if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
1.7 shadchin 519: return ("?");
1.1 etheisen 520: return (o->odesc[0]);
521: }
522:
523: /*
524: * Return whether or not there is a string option pending;
1.10 nicm 525: * that is, if the previous option was a string-valued option letter
1.1 etheisen 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: */
1.10 nicm 530: int
531: isoptpending(void)
1.1 etheisen 532: {
533: return (pendopt != NULL);
534: }
535:
536: /*
537: * Print error message about missing string.
538: */
1.10 nicm 539: static void
540: nostring(char *printopt)
1.1 etheisen 541: {
542: PARG parg;
1.5 millert 543: parg.p_string = printopt;
544: error("Value is required after %s", &parg);
1.1 etheisen 545: }
546:
547: /*
548: * Print error message if a STRING type option is not followed by a string.
549: */
1.10 nicm 550: void
551: nopendopt(void)
1.1 etheisen 552: {
1.7 shadchin 553: nostring(opt_desc(pendopt));
1.1 etheisen 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: */
1.10 nicm 561: static char *
562: optstring(char *s, char **p_str, char *printopt, char *validchars)
1.1 etheisen 563: {
1.10 nicm 564: char *p;
565: char *out;
1.1 etheisen 566:
1.10 nicm 567: if (*s == '\0') {
1.5 millert 568: nostring(printopt);
1.8 shadchin 569: return (NULL);
1.1 etheisen 570: }
1.8 shadchin 571: /* Alloc could be more than needed, but not worth trimming. */
1.10 nicm 572: *p_str = ecalloc(strlen(s)+1, sizeof (char));
1.8 shadchin 573: out = *p_str;
574:
1.10 nicm 575: for (p = s; *p != '\0'; p++) {
576: if (opt_use_backslash && *p == '\\' && p[1] != '\0') {
1.8 shadchin 577: /* Take next char literally. */
578: ++p;
1.10 nicm 579: } else {
580: if (*p == END_OPTION_STRING ||
581: (validchars != NULL &&
582: strchr(validchars, *p) == NULL))
1.8 shadchin 583: /* End of option string. */
1.5 millert 584: break;
1.1 etheisen 585: }
1.8 shadchin 586: *out++ = *p;
1.5 millert 587: }
1.8 shadchin 588: *out = '\0';
1.1 etheisen 589: return (p);
590: }
591:
592: /*
1.7 shadchin 593: */
1.10 nicm 594: static int
595: num_error(char *printopt, int *errp)
1.7 shadchin 596: {
597: PARG parg;
598:
1.10 nicm 599: if (errp != NULL) {
1.7 shadchin 600: *errp = TRUE;
601: return (-1);
602: }
1.10 nicm 603: if (printopt != NULL) {
1.7 shadchin 604: parg.p_string = printopt;
605: error("Number is required after %s", &parg);
606: }
607: return (-1);
608: }
609:
610: /*
1.1 etheisen 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: */
1.10 nicm 615: int
616: getnum(char **sp, char *printopt, int *errp)
1.1 etheisen 617: {
1.10 nicm 618: char *s;
619: int n;
620: int neg;
1.1 etheisen 621:
622: s = skipsp(*sp);
623: neg = FALSE;
1.10 nicm 624: if (*s == '-') {
1.1 etheisen 625: neg = TRUE;
626: s++;
627: }
628: if (*s < '0' || *s > '9')
1.7 shadchin 629: return (num_error(printopt, errp));
1.1 etheisen 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);
1.7 shadchin 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: */
1.10 nicm 648: long
649: getfraction(char **sp, char *printopt, int *errp)
1.7 shadchin 650: {
1.10 nicm 651: char *s;
1.7 shadchin 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:
1.10 nicm 659: for (; *s >= '0' && *s <= '9'; s++) {
1.7 shadchin 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: */
1.10 nicm 679: int
680: get_quit_at_eof(void)
1.7 shadchin 681: {
1.10 nicm 682: return (quit_at_eof);
1.1 etheisen 683: }