Annotation of src/usr.bin/less/option.c, Revision 1.12
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:
27: static char *optstring();
28: static int flip_triple();
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.8 shadchin 310: if (str != NULL)
311: free(str);
1.1 etheisen 312: }
313: }
314:
315: /*
316: * Toggle command line flags from within the program.
317: * Used by the "-" and "_" commands.
318: * how_toggle may be:
319: * OPT_NO_TOGGLE just report the current setting, without changing it.
320: * OPT_TOGGLE invert the current setting
321: * OPT_UNSET set to the default value
322: * OPT_SET set to the inverse of the default value
323: */
1.10 nicm 324: void
325: toggle_option(struct loption *o, int lower, char *s, int how_toggle)
1.1 etheisen 326: {
1.10 nicm 327: int num;
1.5 millert 328: int no_prompt;
1.1 etheisen 329: int err;
330: PARG parg;
331:
1.5 millert 332: no_prompt = (how_toggle & OPT_NO_PROMPT);
333: how_toggle &= ~OPT_NO_PROMPT;
334:
1.10 nicm 335: if (o == NULL) {
1.7 shadchin 336: error("No such option", NULL_PARG);
1.1 etheisen 337: return;
338: }
339:
1.10 nicm 340: if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) {
1.7 shadchin 341: parg.p_string = opt_desc(o);
1.5 millert 342: error("Cannot change the %s option", &parg);
1.1 etheisen 343: return;
1.7 shadchin 344: }
1.1 etheisen 345:
1.10 nicm 346: if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) {
1.7 shadchin 347: parg.p_string = opt_desc(o);
1.5 millert 348: error("Cannot query the %s option", &parg);
1.1 etheisen 349: return;
1.10 nicm 350: }
1.1 etheisen 351:
352: /*
353: * Check for something which appears to be a do_toggle
354: * (because the "-" command was used), but really is not.
355: * This could be a string option with no string, or
356: * a number option with no number.
357: */
1.10 nicm 358: switch (o->otype & OTYPE) {
1.1 etheisen 359: case STRING:
360: case NUMBER:
361: if (how_toggle == OPT_TOGGLE && *s == '\0')
362: how_toggle = OPT_NO_TOGGLE;
363: break;
364: }
365:
366: if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
367: repaint_hilite(0);
368:
369: /*
370: * Now actually toggle (change) the variable.
371: */
1.10 nicm 372: if (how_toggle != OPT_NO_TOGGLE) {
373: switch (o->otype & OTYPE) {
1.1 etheisen 374: case BOOL:
375: /*
376: * Boolean.
377: */
1.10 nicm 378: switch (how_toggle) {
1.1 etheisen 379: case OPT_TOGGLE:
380: *(o->ovar) = ! *(o->ovar);
381: break;
382: case OPT_UNSET:
383: *(o->ovar) = o->odefault;
384: break;
385: case OPT_SET:
386: *(o->ovar) = ! o->odefault;
387: break;
388: }
389: break;
390: case TRIPLE:
391: /*
392: * Triple:
1.10 nicm 393: * If user gave the lower case letter, then switch
1.1 etheisen 394: * to 1 unless already 1, in which case make it 0.
395: * If user gave the upper case letter, then switch
396: * to 2 unless already 2, in which case make it 0.
397: */
1.10 nicm 398: switch (how_toggle) {
1.1 etheisen 399: case OPT_TOGGLE:
1.7 shadchin 400: *(o->ovar) = flip_triple(*(o->ovar), lower);
1.1 etheisen 401: break;
402: case OPT_UNSET:
403: *(o->ovar) = o->odefault;
404: break;
405: case OPT_SET:
1.7 shadchin 406: *(o->ovar) = flip_triple(o->odefault, lower);
1.1 etheisen 407: break;
408: }
409: break;
410: case STRING:
411: /*
412: * String: don't do anything here.
413: * The handling function will do everything.
414: */
1.10 nicm 415: switch (how_toggle) {
1.1 etheisen 416: case OPT_SET:
417: case OPT_UNSET:
1.10 nicm 418: error("Cannot use \"-+\" or \"--\" "
419: "for a string option", NULL_PARG);
1.1 etheisen 420: return;
421: }
422: break;
423: case NUMBER:
424: /*
425: * Number: set the variable to the given number.
426: */
1.10 nicm 427: switch (how_toggle) {
1.1 etheisen 428: case OPT_TOGGLE:
1.5 millert 429: num = getnum(&s, NULL, &err);
1.1 etheisen 430: if (!err)
431: *(o->ovar) = num;
432: break;
433: case OPT_UNSET:
434: *(o->ovar) = o->odefault;
435: break;
436: case OPT_SET:
1.5 millert 437: error("Can't use \"-!\" for a numeric option",
1.10 nicm 438: NULL_PARG);
1.1 etheisen 439: return;
440: }
441: break;
442: }
443: }
444:
445: /*
1.10 nicm 446: * Call the handling function for any special action
1.1 etheisen 447: * specific to this option.
448: */
449: if (o->ofunc != NULL)
1.10 nicm 450: (*o->ofunc)((how_toggle == OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
1.1 etheisen 451:
452: if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
453: chg_hilite();
454:
1.10 nicm 455: if (!no_prompt) {
1.1 etheisen 456: /*
1.5 millert 457: * Print a message describing the new setting.
1.1 etheisen 458: */
1.10 nicm 459: switch (o->otype & OTYPE) {
1.5 millert 460: case BOOL:
461: case TRIPLE:
462: /*
463: * Print the odesc message.
464: */
465: error(o->odesc[*(o->ovar)], NULL_PARG);
466: break;
467: case NUMBER:
468: /*
1.10 nicm 469: * The message is in odesc[1] and has a %d for
1.5 millert 470: * the value of the variable.
471: */
472: parg.p_int = *(o->ovar);
473: error(o->odesc[1], &parg);
474: break;
475: case STRING:
476: /*
477: * Message was already printed by the handling function.
478: */
479: break;
480: }
1.1 etheisen 481: }
482:
483: if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
484: screen_trashed = TRUE;
485: }
486:
487: /*
488: * "Toggle" a triple-valued option.
489: */
1.10 nicm 490: static int
491: flip_triple(int val, int lc)
1.1 etheisen 492: {
493: if (lc)
494: return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
495: else
496: return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
497: }
498:
499: /*
1.7 shadchin 500: * Determine if an option takes a parameter.
1.1 etheisen 501: */
1.10 nicm 502: int
503: opt_has_param(struct loption *o)
1.1 etheisen 504: {
505: if (o == NULL)
1.7 shadchin 506: return (0);
507: if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
508: return (0);
509: return (1);
1.1 etheisen 510: }
511:
512: /*
513: * Return the prompt to be used for a given option letter.
514: * Only string and number valued options have prompts.
515: */
1.10 nicm 516: char *
517: opt_prompt(struct loption *o)
1.1 etheisen 518: {
519: if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
1.7 shadchin 520: return ("?");
1.1 etheisen 521: return (o->odesc[0]);
522: }
523:
524: /*
525: * Return whether or not there is a string option pending;
1.10 nicm 526: * that is, if the previous option was a string-valued option letter
1.1 etheisen 527: * (like -P) without a following string.
528: * In that case, the current option is taken to be the string for
529: * the previous option.
530: */
1.10 nicm 531: int
532: isoptpending(void)
1.1 etheisen 533: {
534: return (pendopt != NULL);
535: }
536:
537: /*
538: * Print error message about missing string.
539: */
1.10 nicm 540: static void
541: nostring(char *printopt)
1.1 etheisen 542: {
543: PARG parg;
1.5 millert 544: parg.p_string = printopt;
545: error("Value is required after %s", &parg);
1.1 etheisen 546: }
547:
548: /*
549: * Print error message if a STRING type option is not followed by a string.
550: */
1.10 nicm 551: void
552: nopendopt(void)
1.1 etheisen 553: {
1.7 shadchin 554: nostring(opt_desc(pendopt));
1.1 etheisen 555: }
556:
557: /*
558: * Scan to end of string or to an END_OPTION_STRING character.
559: * In the latter case, replace the char with a null char.
560: * Return a pointer to the remainder of the string, if any.
561: */
1.10 nicm 562: static char *
563: optstring(char *s, char **p_str, char *printopt, char *validchars)
1.1 etheisen 564: {
1.10 nicm 565: char *p;
566: char *out;
1.1 etheisen 567:
1.10 nicm 568: if (*s == '\0') {
1.5 millert 569: nostring(printopt);
1.8 shadchin 570: return (NULL);
1.1 etheisen 571: }
1.8 shadchin 572: /* Alloc could be more than needed, but not worth trimming. */
1.10 nicm 573: *p_str = ecalloc(strlen(s)+1, sizeof (char));
1.8 shadchin 574: out = *p_str;
575:
1.10 nicm 576: for (p = s; *p != '\0'; p++) {
577: if (opt_use_backslash && *p == '\\' && p[1] != '\0') {
1.8 shadchin 578: /* Take next char literally. */
579: ++p;
1.10 nicm 580: } else {
581: if (*p == END_OPTION_STRING ||
582: (validchars != NULL &&
583: strchr(validchars, *p) == NULL))
1.8 shadchin 584: /* End of option string. */
1.5 millert 585: break;
1.1 etheisen 586: }
1.8 shadchin 587: *out++ = *p;
1.5 millert 588: }
1.8 shadchin 589: *out = '\0';
1.1 etheisen 590: return (p);
591: }
592:
593: /*
1.7 shadchin 594: */
1.10 nicm 595: static int
596: num_error(char *printopt, int *errp)
1.7 shadchin 597: {
598: PARG parg;
599:
1.10 nicm 600: if (errp != NULL) {
1.7 shadchin 601: *errp = TRUE;
602: return (-1);
603: }
1.10 nicm 604: if (printopt != NULL) {
1.7 shadchin 605: parg.p_string = printopt;
606: error("Number is required after %s", &parg);
607: }
608: return (-1);
609: }
610:
611: /*
1.1 etheisen 612: * Translate a string into a number.
613: * Like atoi(), but takes a pointer to a char *, and updates
614: * the char * to point after the translated number.
615: */
1.10 nicm 616: int
617: getnum(char **sp, char *printopt, int *errp)
1.1 etheisen 618: {
1.10 nicm 619: char *s;
620: int n;
621: int neg;
1.1 etheisen 622:
623: s = skipsp(*sp);
624: neg = FALSE;
1.10 nicm 625: if (*s == '-') {
1.1 etheisen 626: neg = TRUE;
627: s++;
628: }
629: if (*s < '0' || *s > '9')
1.7 shadchin 630: return (num_error(printopt, errp));
1.1 etheisen 631:
632: n = 0;
633: while (*s >= '0' && *s <= '9')
634: n = 10 * n + *s++ - '0';
635: *sp = s;
636: if (errp != NULL)
637: *errp = FALSE;
638: if (neg)
639: n = -n;
640: return (n);
1.7 shadchin 641: }
642:
643: /*
644: * Translate a string into a fraction, represented by the part of a
645: * number which would follow a decimal point.
646: * The value of the fraction is returned as parts per NUM_FRAC_DENOM.
647: * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM.
648: */
1.10 nicm 649: long
650: getfraction(char **sp, char *printopt, int *errp)
1.7 shadchin 651: {
1.10 nicm 652: char *s;
1.7 shadchin 653: long frac = 0;
654: int fraclen = 0;
655:
656: s = skipsp(*sp);
657: if (*s < '0' || *s > '9')
658: return (num_error(printopt, errp));
659:
1.10 nicm 660: for (; *s >= '0' && *s <= '9'; s++) {
1.7 shadchin 661: frac = (frac * 10) + (*s - '0');
662: fraclen++;
663: }
664: if (fraclen > NUM_LOG_FRAC_DENOM)
665: while (fraclen-- > NUM_LOG_FRAC_DENOM)
666: frac /= 10;
667: else
668: while (fraclen++ < NUM_LOG_FRAC_DENOM)
669: frac *= 10;
670: *sp = s;
671: if (errp != NULL)
672: *errp = FALSE;
673: return (frac);
674: }
675:
676:
677: /*
678: * Get the value of the -e flag.
679: */
1.10 nicm 680: int
681: get_quit_at_eof(void)
1.7 shadchin 682: {
1.10 nicm 683: return (quit_at_eof);
1.1 etheisen 684: }