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