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