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