Annotation of src/usr.bin/less/option.c, Revision 1.3
1.3 ! mpech 1: /* $OpenBSD: option.c,v 1.2 2001/01/29 01:58:03 niklas Exp $ */
1.2 niklas 2:
1.1 etheisen 3: /*
4: * Copyright (c) 1984,1985,1989,1994,1995 Mark Nudelman
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice in the documentation and/or other materials provided with
14: * the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17: * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
20: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22: * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23: * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27: */
28:
29:
30: /*
31: * Process command line options.
32: *
33: * Each option is a single letter which controls a program variable.
34: * The options have defaults which may be changed via
35: * the command line option, toggled via the "-" command,
36: * or queried via the "_" command.
37: */
38:
39: #include "less.h"
40: #include "option.h"
41:
42: static struct option *pendopt;
43: public int plusoption = FALSE;
44:
45: static char *propt();
46: static char *optstring();
47: static int flip_triple();
48:
49: extern int screen_trashed;
50: extern char *every_first_cmd;
51:
52: /*
53: * Scan an argument (either from the command line or from the
54: * LESS environment variable) and process it.
55: */
56: public void
57: scan_option(s)
58: char *s;
59: {
1.3 ! mpech 60: struct option *o;
! 61: int c;
1.1 etheisen 62: char *str;
63: int set_default;
64: PARG parg;
65:
66: if (s == NULL)
67: return;
68:
69: /*
70: * If we have a pending string-valued option, handle it now.
71: * This happens if the previous option was, for example, "-P"
72: * without a following string. In that case, the current
73: * option is simply the string for the previous option.
74: */
75: if (pendopt != NULL)
76: {
77: (*pendopt->ofunc)(INIT, s);
78: pendopt = NULL;
79: return;
80: }
81:
82: set_default = FALSE;
83:
84: while (*s != '\0')
85: {
86: /*
87: * Check some special cases first.
88: */
89: switch (c = *s++)
90: {
91: case ' ':
92: case '\t':
93: case END_OPTION_STRING:
94: continue;
95: case '-':
96: /*
97: * "-+" means set these options back to their defaults.
98: * (They may have been set otherwise by previous
99: * options.)
100: */
101: if (set_default = (*s == '+'))
102: s++;
103: continue;
104: case '+':
105: /*
106: * An option prefixed by a "+" is ungotten, so
107: * that it is interpreted as less commands
108: * processed at the start of the first input file.
109: * "++" means process the commands at the start of
110: * EVERY input file.
111: */
112: plusoption = TRUE;
113: if (*s == '+')
114: every_first_cmd = save(++s);
115: else
116: ungetsc(s);
117: s = optstring(s, c);
118: continue;
119: case '0': case '1': case '2': case '3': case '4':
120: case '5': case '6': case '7': case '8': case '9':
121: /*
122: * Special "more" compatibility form "-<number>"
123: * instead of -z<number> to set the scrolling
124: * window size.
125: */
126: s--;
127: c = 'z';
128: break;
129: }
130:
131: /*
132: * Not a special case.
133: * Look up the option letter in the option table.
134: */
135: o = findopt(c);
136: if (o == NULL)
137: {
138: parg.p_string = propt(c);
139: #if MSOFTC || OS2
140: error("There is no %s flag (\"less -?\" for help)",
141: &parg);
142: #else
143: error("There is no %s flag (\"less -\\?\" for help)",
144: &parg);
145: #endif
146: quit(QUIT_ERROR);
147: }
148:
149: switch (o->otype & OTYPE)
150: {
151: case BOOL:
152: if (set_default)
153: *(o->ovar) = o->odefault;
154: else
155: *(o->ovar) = ! o->odefault;
156: break;
157: case TRIPLE:
158: if (set_default)
159: *(o->ovar) = o->odefault;
160: else
161: *(o->ovar) = flip_triple(o->odefault,
162: (o->oletter == c));
163: break;
164: case STRING:
165: if (*s == '\0')
166: {
167: /*
168: * Set pendopt and return.
169: * We will get the string next time
170: * scan_option is called.
171: */
172: pendopt = o;
173: return;
174: }
175: /*
176: * Don't do anything here.
177: * All processing of STRING options is done by
178: * the handling function.
179: */
180: str = s;
181: s = optstring(s, c);
182: break;
183: case NUMBER:
184: *(o->ovar) = getnum(&s, c, (int*)NULL);
185: break;
186: }
187: /*
188: * If the option has a handling function, call it.
189: */
190: if (o->ofunc != NULL)
191: (*o->ofunc)(INIT, str);
192: }
193: }
194:
195: /*
196: * Toggle command line flags from within the program.
197: * Used by the "-" and "_" commands.
198: * how_toggle may be:
199: * OPT_NO_TOGGLE just report the current setting, without changing it.
200: * OPT_TOGGLE invert the current setting
201: * OPT_UNSET set to the default value
202: * OPT_SET set to the inverse of the default value
203: */
204: public void
205: toggle_option(c, s, how_toggle)
206: int c;
207: char *s;
208: int how_toggle;
209: {
1.3 ! mpech 210: struct option *o;
! 211: int num;
1.1 etheisen 212: int err;
213: PARG parg;
214:
215: /*
216: * Look up the option letter in the option table.
217: */
218: o = findopt(c);
219: if (o == NULL)
220: {
221: parg.p_string = propt(c);
222: error("There is no %s flag", &parg);
223: return;
224: }
225:
226: if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
227: {
228: parg.p_string = propt(c);
229: error("Cannot change the %s flag", &parg);
230: return;
231: }
232:
233: if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
234: {
235: parg.p_string = propt(c);
236: error("Cannot query the %s flag", &parg);
237: return;
238: }
239:
240: /*
241: * Check for something which appears to be a do_toggle
242: * (because the "-" command was used), but really is not.
243: * This could be a string option with no string, or
244: * a number option with no number.
245: */
246: switch (o->otype & OTYPE)
247: {
248: case STRING:
249: case NUMBER:
250: if (how_toggle == OPT_TOGGLE && *s == '\0')
251: how_toggle = OPT_NO_TOGGLE;
252: break;
253: }
254:
255: #if HILITE_SEARCH
256: if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
257: repaint_hilite(0);
258: #endif
259:
260: /*
261: * Now actually toggle (change) the variable.
262: */
263: if (how_toggle != OPT_NO_TOGGLE)
264: {
265: switch (o->otype & OTYPE)
266: {
267: case BOOL:
268: /*
269: * Boolean.
270: */
271: switch (how_toggle)
272: {
273: case OPT_TOGGLE:
274: *(o->ovar) = ! *(o->ovar);
275: break;
276: case OPT_UNSET:
277: *(o->ovar) = o->odefault;
278: break;
279: case OPT_SET:
280: *(o->ovar) = ! o->odefault;
281: break;
282: }
283: break;
284: case TRIPLE:
285: /*
286: * Triple:
287: * If user gave the lower case letter, then switch
288: * to 1 unless already 1, in which case make it 0.
289: * If user gave the upper case letter, then switch
290: * to 2 unless already 2, in which case make it 0.
291: */
292: switch (how_toggle)
293: {
294: case OPT_TOGGLE:
295: *(o->ovar) = flip_triple(*(o->ovar),
296: o->oletter == c);
297: break;
298: case OPT_UNSET:
299: *(o->ovar) = o->odefault;
300: break;
301: case OPT_SET:
302: *(o->ovar) = flip_triple(o->odefault,
303: o->oletter == c);
304: break;
305: }
306: break;
307: case STRING:
308: /*
309: * String: don't do anything here.
310: * The handling function will do everything.
311: */
312: switch (how_toggle)
313: {
314: case OPT_SET:
315: case OPT_UNSET:
316: error("Can't use \"-+\" or \"--\" for a string flag",
317: NULL_PARG);
318: return;
319: }
320: break;
321: case NUMBER:
322: /*
323: * Number: set the variable to the given number.
324: */
325: switch (how_toggle)
326: {
327: case OPT_TOGGLE:
328: num = getnum(&s, '\0', &err);
329: if (!err)
330: *(o->ovar) = num;
331: break;
332: case OPT_UNSET:
333: *(o->ovar) = o->odefault;
334: break;
335: case OPT_SET:
336: error("Can't use \"--\" for a numeric flag",
337: NULL_PARG);
338: return;
339: }
340: break;
341: }
342: }
343:
344: /*
345: * Call the handling function for any special action
346: * specific to this option.
347: */
348: if (o->ofunc != NULL)
349: (*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
350:
351: #if HILITE_SEARCH
352: if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
353: chg_hilite();
354: #endif
355:
356: /*
357: * Print a message describing the new setting.
358: */
359: switch (o->otype & OTYPE)
360: {
361: case BOOL:
362: case TRIPLE:
363: /*
364: * Print the odesc message.
365: */
366: error(o->odesc[*(o->ovar)], NULL_PARG);
367: break;
368: case NUMBER:
369: /*
370: * The message is in odesc[1] and has a %d for
371: * the value of the variable.
372: */
373: parg.p_int = *(o->ovar);
374: error(o->odesc[1], &parg);
375: break;
376: case STRING:
377: /*
378: * Message was already printed by the handling function.
379: */
380: break;
381: }
382:
383: if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
384: screen_trashed = TRUE;
385: }
386:
387: /*
388: * "Toggle" a triple-valued option.
389: */
390: static int
391: flip_triple(val, lc)
392: int val;
393: int lc;
394: {
395: if (lc)
396: return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
397: else
398: return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
399: }
400:
401: /*
402: * Return a string suitable for printing as the "name" of an option.
403: * For example, if the option letter is 'x', just return "-x".
404: */
405: static char *
406: propt(c)
407: int c;
408: {
409: static char buf[8];
410:
411: sprintf(buf, "-%s", prchar(c));
412: return (buf);
413: }
414:
415: /*
416: * Determine if an option is a single character option (BOOL or TRIPLE),
417: * or if it a multi-character option (NUMBER).
418: */
419: public int
420: single_char_option(c)
421: int c;
422: {
1.3 ! mpech 423: struct option *o;
1.1 etheisen 424:
425: o = findopt(c);
426: if (o == NULL)
427: return (TRUE);
428: return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
429: }
430:
431: /*
432: * Return the prompt to be used for a given option letter.
433: * Only string and number valued options have prompts.
434: */
435: public char *
436: opt_prompt(c)
437: int c;
438: {
1.3 ! mpech 439: struct option *o;
1.1 etheisen 440:
441: o = findopt(c);
442: if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
443: return (NULL);
444: return (o->odesc[0]);
445: }
446:
447: /*
448: * Return whether or not there is a string option pending;
449: * that is, if the previous option was a string-valued option letter
450: * (like -P) without a following string.
451: * In that case, the current option is taken to be the string for
452: * the previous option.
453: */
454: public int
455: isoptpending()
456: {
457: return (pendopt != NULL);
458: }
459:
460: /*
461: * Print error message about missing string.
462: */
463: static void
464: nostring(c)
465: int c;
466: {
467: PARG parg;
468: parg.p_string = propt(c);
469: error("String is required after %s", &parg);
470: }
471:
472: /*
473: * Print error message if a STRING type option is not followed by a string.
474: */
475: public void
476: nopendopt()
477: {
478: nostring(pendopt->oletter);
479: }
480:
481: /*
482: * Scan to end of string or to an END_OPTION_STRING character.
483: * In the latter case, replace the char with a null char.
484: * Return a pointer to the remainder of the string, if any.
485: */
486: static char *
487: optstring(s, c)
488: char *s;
489: int c;
490: {
1.3 ! mpech 491: char *p;
1.1 etheisen 492:
493: if (*s == '\0')
494: {
495: nostring(c);
496: quit(QUIT_ERROR);
497: }
498: for (p = s; *p != '\0'; p++)
499: if (*p == END_OPTION_STRING)
500: {
501: *p = '\0';
502: return (p+1);
503: }
504: return (p);
505: }
506:
507: /*
508: * Translate a string into a number.
509: * Like atoi(), but takes a pointer to a char *, and updates
510: * the char * to point after the translated number.
511: */
512: public int
513: getnum(sp, c, errp)
514: char **sp;
515: int c;
516: int *errp;
517: {
1.3 ! mpech 518: char *s;
! 519: int n;
! 520: int neg;
1.1 etheisen 521: PARG parg;
522:
523: s = skipsp(*sp);
524: neg = FALSE;
525: if (*s == '-')
526: {
527: neg = TRUE;
528: s++;
529: }
530: if (*s < '0' || *s > '9')
531: {
532: if (errp != NULL)
533: {
534: *errp = TRUE;
535: return (-1);
536: }
537: parg.p_string = propt(c);
538: error("Number is required after %s", &parg);
539: quit(QUIT_ERROR);
540: }
541:
542: n = 0;
543: while (*s >= '0' && *s <= '9')
544: n = 10 * n + *s++ - '0';
545: *sp = s;
546: if (errp != NULL)
547: *errp = FALSE;
548: if (neg)
549: n = -n;
550: return (n);
551: }