Merge commit 'db1c88f6dab43484b6c33636600ac4596ff4c354'
[unleashed.git] / bin / less / option.c
blob83615f377d645d52fe4f66cfbee7ed85fc404bb5
1 /*
2 * Copyright (C) 1984-2012 Mark Nudelman
3 * Modified for use with illumos by Garrett D'Amore.
4 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
6 * You may distribute under the terms of either the GNU General Public
7 * License or the Less License, as specified in the README file.
9 * For more information, see the README file.
13 * Process command line options.
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.
21 #include "less.h"
22 #include "option.h"
24 static struct loption *pendopt;
25 int plusoption = FALSE;
27 static char *optstring(char *, char **, char *, char *);
28 static int flip_triple(int, int);
30 extern int screen_trashed;
31 extern int less_is_more;
32 extern int quit_at_eof;
33 extern char *every_first_cmd;
34 extern int opt_use_backslash;
37 * Return a printable description of an option.
39 static char *
40 opt_desc(struct loption *o)
42 static char buf[OPTNAME_MAX + 10];
43 if (o->oletter == OLETTER_NONE)
44 (void) snprintf(buf, sizeof (buf), "--%s", o->onames->oname);
45 else
46 (void) snprintf(buf, sizeof (buf), "-%c (--%s)",
47 o->oletter, o->onames->oname);
48 return (buf);
52 * Return a string suitable for printing as the "name" of an option.
53 * For example, if the option letter is 'x', just return "-x".
55 char *
56 propt(int c)
58 static char buf[8];
60 (void) snprintf(buf, sizeof (buf), "-%s", prchar(c));
61 return (buf);
65 * Scan an argument (either from the command line or from the
66 * LESS environment variable) and process it.
68 void
69 scan_option(char *s)
71 struct loption *o;
72 int optc;
73 char *optname;
74 char *printopt;
75 char *str;
76 int set_default;
77 int lc;
78 int err;
79 int moreopt;
80 PARG parg;
82 if (s == NULL)
83 return;
86 * If we have a pending option which requires an argument,
87 * handle it now.
88 * This happens if the previous option was, for example, "-P"
89 * without a following string. In that case, the current
90 * option is simply the argument for the previous option.
92 if (pendopt != NULL) {
93 switch (pendopt->otype & OTYPE) {
94 case STRING:
95 (*pendopt->ofunc)(INIT, s);
96 break;
97 case NUMBER:
98 printopt = opt_desc(pendopt);
99 *(pendopt->ovar) = getnum(&s, printopt, NULL);
100 break;
102 pendopt = NULL;
103 return;
106 set_default = FALSE;
107 optname = NULL;
108 moreopt = 0;
109 o = NULL;
111 while (*s != '\0') {
113 * Check some special cases first.
115 switch (optc = *s++) {
116 case ' ':
117 case '\t':
118 case END_OPTION_STRING:
119 continue;
120 case '-':
122 * "--" indicates an option name instead of a letter.
124 if (*s == '-') {
125 if (!less_is_more) {
126 optname = ++s;
128 break;
131 * "-+" means set these options back to their defaults.
132 * (They may have been set otherwise by previous
133 * options.)
135 if (!less_is_more) {
136 set_default = (*s == '+');
137 if (set_default)
138 s++;
140 continue;
141 case '+':
143 * An option prefixed by a "+" is ungotten, so
144 * that it is interpreted as less commands
145 * processed at the start of the first input file.
146 * "++" means process the commands at the start of
147 * EVERY input file.
149 plusoption = TRUE;
150 s = optstring(s, &str, propt('+'), NULL);
151 if (s == NULL)
152 return;
153 if (*str == '+')
154 every_first_cmd = estrdup(str+1);
155 else
156 ungetsc(str);
157 free(str);
158 continue;
159 case '0': case '1': case '2': case '3': case '4':
160 case '5': case '6': case '7': case '8': case '9':
162 * Special "more" compatibility form "-<number>"
163 * instead of -z<number> to set the scrolling
164 * window size.
166 s--;
167 optc = 'z';
168 moreopt = 1;
169 break;
170 case 'n':
171 if (less_is_more) {
172 moreopt = 1;
173 optc = 'z';
175 break;
176 case 'i':
177 if (less_is_more) {
178 moreopt = 1;
179 optc = 'I';
181 break;
182 case 'u':
183 if (less_is_more) {
184 moreopt = 1;
185 optc = 'U';
187 break;
188 case 'e':
189 if (less_is_more) {
190 moreopt = 1;
191 optc = 'E';
193 break;
194 case 'h':
195 if (less_is_more) {
196 moreopt = 1;
197 optc = '?';
199 break;
200 case 'd':
201 if (less_is_more) {
202 moreopt = 1;
203 optc = 'M';
205 break;
209 * Not a special case.
210 * Look up the option letter in the option table.
212 err = 0;
213 if (optname == NULL) {
214 printopt = propt(optc);
215 lc = islower(optc);
216 o = findopt(optc);
217 if (less_is_more && (!moreopt) && (o != NULL) &&
218 ((o->otype & MORE_OK) == 0)) {
219 o = NULL;
221 } else {
222 printopt = optname;
223 lc = islower(optname[0]);
224 o = findopt_name(&optname, NULL, &err);
225 s = optname;
226 optname = NULL;
227 switch (*s) {
228 case ' ': /* name matches exactly */
229 case '\0':
230 break;
232 case '=': /* name followed by "=value" */
233 if (o != NULL &&
234 (o->otype & OTYPE) != STRING &&
235 (o->otype & OTYPE) != NUMBER) {
236 parg.p_string = printopt;
237 error("The %s option should not be "
238 "followed by =", &parg);
239 return;
241 s++;
242 break;
243 default: /* name longer than option, bad */
244 o = NULL;
247 if (o == NULL) {
248 parg.p_string = printopt;
249 if (less_is_more) {
250 error("Illegal option %s (more -h for help)",
251 &parg);
252 } else if (err == OPT_AMBIG) {
253 error("%s is an ambiguous abbreviation "
254 "(\"less --help\" for help)", &parg);
255 } else {
256 error("There is no %s option "
257 "(\"less --help\" for help)", &parg);
259 return;
262 str = NULL;
263 switch (o->otype & OTYPE) {
264 case BOOL:
265 if (set_default)
266 *(o->ovar) = o->odefault;
267 else
268 *(o->ovar) = ! o->odefault;
269 break;
270 case TRIPLE:
271 if (set_default)
272 *(o->ovar) = o->odefault;
273 else
274 *(o->ovar) = flip_triple(o->odefault, lc);
275 break;
276 case STRING:
277 if (*s == '\0') {
279 * Set pendopt and return.
280 * We will get the string next time
281 * scan_option is called.
283 pendopt = o;
284 return;
287 * Don't do anything here.
288 * All processing of STRING options is done by
289 * the handling function.
291 while (*s == ' ')
292 s++;
293 s = optstring(s, &str, printopt, o->odesc[1]);
294 if (s == NULL)
295 return;
296 break;
297 case NUMBER:
298 if (*s == '\0') {
299 pendopt = o;
300 return;
302 *(o->ovar) = getnum(&s, printopt, NULL);
303 break;
306 * If the option has a handling function, call it.
308 if (o->ofunc != NULL)
309 (*o->ofunc)(INIT, str);
310 free(str);
315 * Toggle command line flags from within the program.
316 * Used by the "-" and "_" commands.
317 * how_toggle may be:
318 * OPT_NO_TOGGLE just report the current setting, without changing it.
319 * OPT_TOGGLE invert the current setting
320 * OPT_UNSET set to the default value
321 * OPT_SET set to the inverse of the default value
323 void
324 toggle_option(struct loption *o, int lower, char *s, int how_toggle)
326 int num;
327 int no_prompt;
328 int err;
329 PARG parg;
331 no_prompt = (how_toggle & OPT_NO_PROMPT);
332 how_toggle &= ~OPT_NO_PROMPT;
334 if (o == NULL) {
335 error("No such option", NULL);
336 return;
339 if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE)) {
340 parg.p_string = opt_desc(o);
341 error("Cannot change the %s option", &parg);
342 return;
345 if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY)) {
346 parg.p_string = opt_desc(o);
347 error("Cannot query the %s option", &parg);
348 return;
352 * Check for something which appears to be a do_toggle
353 * (because the "-" command was used), but really is not.
354 * This could be a string option with no string, or
355 * a number option with no number.
357 switch (o->otype & OTYPE) {
358 case STRING:
359 case NUMBER:
360 if (how_toggle == OPT_TOGGLE && *s == '\0')
361 how_toggle = OPT_NO_TOGGLE;
362 break;
365 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
366 repaint_hilite(0);
369 * Now actually toggle (change) the variable.
371 if (how_toggle != OPT_NO_TOGGLE) {
372 switch (o->otype & OTYPE) {
373 case BOOL:
375 * Boolean.
377 switch (how_toggle) {
378 case OPT_TOGGLE:
379 *(o->ovar) = ! *(o->ovar);
380 break;
381 case OPT_UNSET:
382 *(o->ovar) = o->odefault;
383 break;
384 case OPT_SET:
385 *(o->ovar) = ! o->odefault;
386 break;
388 break;
389 case TRIPLE:
391 * Triple:
392 * If user gave the lower case letter, then switch
393 * to 1 unless already 1, in which case make it 0.
394 * If user gave the upper case letter, then switch
395 * to 2 unless already 2, in which case make it 0.
397 switch (how_toggle) {
398 case OPT_TOGGLE:
399 *(o->ovar) = flip_triple(*(o->ovar), lower);
400 break;
401 case OPT_UNSET:
402 *(o->ovar) = o->odefault;
403 break;
404 case OPT_SET:
405 *(o->ovar) = flip_triple(o->odefault, lower);
406 break;
408 break;
409 case STRING:
411 * String: don't do anything here.
412 * The handling function will do everything.
414 switch (how_toggle) {
415 case OPT_SET:
416 case OPT_UNSET:
417 error("Cannot use \"-+\" or \"--\" "
418 "for a string option", NULL);
419 return;
421 break;
422 case NUMBER:
424 * Number: set the variable to the given number.
426 switch (how_toggle) {
427 case OPT_TOGGLE:
428 num = getnum(&s, NULL, &err);
429 if (!err)
430 *(o->ovar) = num;
431 break;
432 case OPT_UNSET:
433 *(o->ovar) = o->odefault;
434 break;
435 case OPT_SET:
436 error("Can't use \"-!\" for a numeric option",
437 NULL);
438 return;
440 break;
445 * Call the handling function for any special action
446 * specific to this option.
448 if (o->ofunc != NULL)
449 (*o->ofunc)((how_toggle == OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
451 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
452 chg_hilite();
454 if (!no_prompt) {
456 * Print a message describing the new setting.
458 switch (o->otype & OTYPE) {
459 case BOOL:
460 case TRIPLE:
462 * Print the odesc message.
464 error(o->odesc[*(o->ovar)], NULL);
465 break;
466 case NUMBER:
468 * The message is in odesc[1] and has a %d for
469 * the value of the variable.
471 parg.p_int = *(o->ovar);
472 error(o->odesc[1], &parg);
473 break;
474 case STRING:
476 * Message was already printed by the handling function.
478 break;
482 if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
483 screen_trashed = TRUE;
487 * "Toggle" a triple-valued option.
489 static int
490 flip_triple(int val, int lc)
492 if (lc)
493 return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
494 else
495 return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
499 * Determine if an option takes a parameter.
502 opt_has_param(struct loption *o)
504 if (o == NULL)
505 return (0);
506 if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
507 return (0);
508 return (1);
512 * Return the prompt to be used for a given option letter.
513 * Only string and number valued options have prompts.
515 char *
516 opt_prompt(struct loption *o)
518 if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
519 return ("?");
520 return (o->odesc[0]);
524 * Return whether or not there is a string option pending;
525 * that is, if the previous option was a string-valued option letter
526 * (like -P) without a following string.
527 * In that case, the current option is taken to be the string for
528 * the previous option.
531 isoptpending(void)
533 return (pendopt != NULL);
537 * Print error message about missing string.
539 static void
540 nostring(char *printopt)
542 PARG parg;
543 parg.p_string = printopt;
544 error("Value is required after %s", &parg);
548 * Print error message if a STRING type option is not followed by a string.
550 void
551 nopendopt(void)
553 nostring(opt_desc(pendopt));
557 * Scan to end of string or to an END_OPTION_STRING character.
558 * In the latter case, replace the char with a null char.
559 * Return a pointer to the remainder of the string, if any.
561 static char *
562 optstring(char *s, char **p_str, char *printopt, char *validchars)
564 char *p;
565 char *out;
567 if (*s == '\0') {
568 nostring(printopt);
569 return (NULL);
571 /* Alloc could be more than needed, but not worth trimming. */
572 *p_str = ecalloc(strlen(s)+1, sizeof (char));
573 out = *p_str;
575 for (p = s; *p != '\0'; p++) {
576 if (opt_use_backslash && *p == '\\' && p[1] != '\0') {
577 /* Take next char literally. */
578 ++p;
579 } else {
580 if (*p == END_OPTION_STRING ||
581 (validchars != NULL &&
582 strchr(validchars, *p) == NULL))
583 /* End of option string. */
584 break;
586 *out++ = *p;
588 *out = '\0';
589 return (p);
594 static int
595 num_error(char *printopt, int *errp)
597 PARG parg;
599 if (errp != NULL) {
600 *errp = TRUE;
601 return (-1);
603 if (printopt != NULL) {
604 parg.p_string = printopt;
605 error("Number is required after %s", &parg);
607 return (-1);
611 * Translate a string into a number.
612 * Like atoi(), but takes a pointer to a char *, and updates
613 * the char * to point after the translated number.
616 getnum(char **sp, char *printopt, int *errp)
618 char *s;
619 int n;
620 int neg;
622 s = skipsp(*sp);
623 neg = FALSE;
624 if (*s == '-') {
625 neg = TRUE;
626 s++;
628 if (*s < '0' || *s > '9')
629 return (num_error(printopt, errp));
631 n = 0;
632 while (*s >= '0' && *s <= '9')
633 n = 10 * n + *s++ - '0';
634 *sp = s;
635 if (errp != NULL)
636 *errp = FALSE;
637 if (neg)
638 n = -n;
639 return (n);
643 * Translate a string into a fraction, represented by the part of a
644 * number which would follow a decimal point.
645 * The value of the fraction is returned as parts per NUM_FRAC_DENOM.
646 * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM.
648 long
649 getfraction(char **sp, char *printopt, int *errp)
651 char *s;
652 long frac = 0;
653 int fraclen = 0;
655 s = skipsp(*sp);
656 if (*s < '0' || *s > '9')
657 return (num_error(printopt, errp));
659 for (; *s >= '0' && *s <= '9'; s++) {
660 frac = (frac * 10) + (*s - '0');
661 fraclen++;
663 if (fraclen > NUM_LOG_FRAC_DENOM)
664 while (fraclen-- > NUM_LOG_FRAC_DENOM)
665 frac /= 10;
666 else
667 while (fraclen++ < NUM_LOG_FRAC_DENOM)
668 frac *= 10;
669 *sp = s;
670 if (errp != NULL)
671 *errp = FALSE;
672 return (frac);
677 * Get the value of the -e flag.
680 get_quit_at_eof(void)
682 return (quit_at_eof);