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