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