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>
5 *
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.
8 *
9 * For more information, see the README file.
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 int plusoption = FALSE;
26
27 static char *optstring(char *, char **, char *, char *, int);
28 static int flip_triple(int, int);
29
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;
35
36 /*
37 * Return a printable description of an option.
38 */
39 static char *
opt_desc(struct loption * o)40 opt_desc(struct loption *o)
41 {
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);
49 }
50
51 /*
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".
54 */
55 char *
propt(int c)56 propt(int c)
57 {
58 static char buf[33];
59
60 (void) snprintf(buf, sizeof (buf), "-%s", prchar(c));
61 return (buf);
62 }
63
64 /*
65 * Scan an argument (either from the command line or from the
66 * LESS environment variable) and process it.
67 */
68 void
scan_option(char * s,int env)69 scan_option(char *s, int env)
70 {
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;
81
82 if (s == NULL)
83 return;
84
85 /*
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.
91 */
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;
101 }
102 pendopt = NULL;
103 return;
104 }
105
106 set_default = FALSE;
107 optname = NULL;
108 moreopt = 0;
109 o = NULL;
110
111 while (*s != '\0') {
112 /*
113 * Check some special cases first.
114 */
115 switch (optc = *s++) {
116 case ' ':
117 case '\t':
118 case END_OPTION_STRING:
119 continue;
120 case '-':
121 /*
122 * "--" indicates an option name instead of a letter.
123 */
124 if (*s == '-') {
125 if (!less_is_more) {
126 optname = ++s;
127 }
128 break;
129 }
130 /*
131 * "-+" means set these options back to their defaults.
132 * (They may have been set otherwise by previous
133 * options.)
134 */
135 if (!less_is_more) {
136 set_default = (*s == '+');
137 if (set_default)
138 s++;
139 }
140 continue;
141 case '+':
142 /*
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.
148 */
149 plusoption = TRUE;
150 s = optstring(s, &str, propt('+'), NULL, 0);
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':
161 /*
162 * Special "more" compatibility form "-<number>"
163 * instead of -z<number> to set the scrolling
164 * window size.
165 */
166 s--;
167 optc = 'z';
168 moreopt = 1;
169 break;
170 case 'n':
171 if (less_is_more) {
172 moreopt = 1;
173 optc = 'z';
174 }
175 break;
176 case 'i':
177 if (less_is_more) {
178 moreopt = 1;
179 optc = 'I';
180 }
181 break;
182 case 'u':
183 if (less_is_more) {
184 moreopt = 1;
185 optc = 'U';
186 }
187 break;
188 case 'e':
189 if (less_is_more) {
190 moreopt = 1;
191 optc = 'E';
192 }
193 break;
194 case 'h':
195 if (less_is_more) {
196 moreopt = 1;
197 optc = '?';
198 }
199 break;
200 case 'd':
201 if (less_is_more) {
202 moreopt = 1;
203 optc = 'M';
204 }
205 break;
206 }
207
208 /*
209 * Not a special case.
210 * Look up the option letter in the option table.
211 */
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;
220 }
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;
231
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;
240 }
241 s++;
242 break;
243 default: /* name longer than option, bad */
244 o = NULL;
245 }
246 }
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);
258 }
259 return;
260 }
261
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') {
278 /*
279 * Set pendopt and return.
280 * We will get the string next time
281 * scan_option is called.
282 */
283 pendopt = o;
284 return;
285 }
286 /*
287 * Don't do anything here.
288 * All processing of STRING options is done by
289 * the handling function.
290 */
291 while (*s == ' ')
292 s++;
293 s = optstring(s, &str, printopt, o->odesc[1], env);
294 if (s == NULL)
295 return;
296 break;
297 case NUMBER:
298 if (*s == '\0') {
299 pendopt = o;
300 return;
301 }
302 *(o->ovar) = getnum(&s, printopt, NULL);
303 break;
304 }
305 /*
306 * If the option has a handling function, call it.
307 */
308 if (o->ofunc != NULL)
309 (*o->ofunc)(INIT, str);
310 free(str);
311 }
312 }
313
314 /*
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
322 */
323 void
toggle_option(struct loption * o,int lower,char * s,int how_toggle)324 toggle_option(struct loption *o, int lower, char *s, int how_toggle)
325 {
326 int num;
327 int no_prompt;
328 int err;
329 PARG parg;
330
331 no_prompt = (how_toggle & OPT_NO_PROMPT);
332 how_toggle &= ~OPT_NO_PROMPT;
333
334 if (o == NULL) {
335 error("No such option", NULL);
336 return;
337 }
338
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;
343 }
344
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;
349 }
350
351 /*
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.
356 */
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;
363 }
364
365 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
366 repaint_hilite(0);
367
368 /*
369 * Now actually toggle (change) the variable.
370 */
371 if (how_toggle != OPT_NO_TOGGLE) {
372 switch (o->otype & OTYPE) {
373 case BOOL:
374 /*
375 * Boolean.
376 */
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;
387 }
388 break;
389 case TRIPLE:
390 /*
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.
396 */
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;
407 }
408 break;
409 case STRING:
410 /*
411 * String: don't do anything here.
412 * The handling function will do everything.
413 */
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;
420 }
421 break;
422 case NUMBER:
423 /*
424 * Number: set the variable to the given number.
425 */
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;
439 }
440 break;
441 }
442 }
443
444 /*
445 * Call the handling function for any special action
446 * specific to this option.
447 */
448 if (o->ofunc != NULL)
449 (*o->ofunc)((how_toggle == OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
450
451 if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
452 chg_hilite();
453
454 if (!no_prompt) {
455 /*
456 * Print a message describing the new setting.
457 */
458 switch (o->otype & OTYPE) {
459 case BOOL:
460 case TRIPLE:
461 /*
462 * Print the odesc message.
463 */
464 error(o->odesc[*(o->ovar)], NULL);
465 break;
466 case NUMBER:
467 /*
468 * The message is in odesc[1] and has a %d for
469 * the value of the variable.
470 */
471 parg.p_int = *(o->ovar);
472 error(o->odesc[1], &parg);
473 break;
474 case STRING:
475 /*
476 * Message was already printed by the handling function.
477 */
478 break;
479 }
480 }
481
482 if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
483 screen_trashed = TRUE;
484 }
485
486 /*
487 * "Toggle" a triple-valued option.
488 */
489 static int
flip_triple(int val,int lc)490 flip_triple(int val, int lc)
491 {
492 if (lc)
493 return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
494 else
495 return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
496 }
497
498 /*
499 * Determine if an option takes a parameter.
500 */
501 int
opt_has_param(struct loption * o)502 opt_has_param(struct loption *o)
503 {
504 if (o == NULL)
505 return (0);
506 if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
507 return (0);
508 return (1);
509 }
510
511 /*
512 * Return the prompt to be used for a given option letter.
513 * Only string and number valued options have prompts.
514 */
515 char *
opt_prompt(struct loption * o)516 opt_prompt(struct loption *o)
517 {
518 if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
519 return ("?");
520 return (o->odesc[0]);
521 }
522
523 /*
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.
529 */
530 int
isoptpending(void)531 isoptpending(void)
532 {
533 return (pendopt != NULL);
534 }
535
536 /*
537 * Print error message about missing string.
538 */
539 static void
nostring(char * printopt)540 nostring(char *printopt)
541 {
542 PARG parg;
543 parg.p_string = printopt;
544 error("Value is required after %s", &parg);
545 }
546
547 /*
548 * Print error message if a STRING type option is not followed by a string.
549 */
550 void
nopendopt(void)551 nopendopt(void)
552 {
553 nostring(opt_desc(pendopt));
554 }
555
556 /*
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.
560 */
561 static char *
optstring(char * s,char ** p_str,char * printopt,char * validchars,int env)562 optstring(char *s, char **p_str, char *printopt, char *validchars, int env)
563 {
564 char *p;
565 char *out;
566
567 if (*s == '\0') {
568 nostring(printopt);
569 return (NULL);
570 }
571 /* Alloc could be more than needed, but not worth trimming. */
572 *p_str = ecalloc(strlen(s)+1, sizeof (char));
573 out = *p_str;
574
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 && env == 1) ||
581 (validchars != NULL &&
582 strchr(validchars, *p) == NULL))
583 /* End of option string. */
584 break;
585 }
586 *out++ = *p;
587 }
588 *out = '\0';
589 return (p);
590 }
591
592 /*
593 */
594 static int
num_error(char * printopt,int * errp)595 num_error(char *printopt, int *errp)
596 {
597 PARG parg;
598
599 if (errp != NULL) {
600 *errp = TRUE;
601 return (-1);
602 }
603 if (printopt != NULL) {
604 parg.p_string = printopt;
605 error("Number is required after %s", &parg);
606 }
607 return (-1);
608 }
609
610 /*
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.
614 */
615 int
getnum(char ** sp,char * printopt,int * errp)616 getnum(char **sp, char *printopt, int *errp)
617 {
618 char *s;
619 int n;
620 int neg;
621
622 s = skipsp(*sp);
623 neg = FALSE;
624 if (*s == '-') {
625 neg = TRUE;
626 s++;
627 }
628 if (*s < '0' || *s > '9')
629 return (num_error(printopt, errp));
630
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);
640 }
641
642 /*
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.
647 */
648 long
getfraction(char ** sp,char * printopt,int * errp)649 getfraction(char **sp, char *printopt, int *errp)
650 {
651 char *s;
652 long frac = 0;
653 int fraclen = 0;
654
655 s = skipsp(*sp);
656 if (*s < '0' || *s > '9')
657 return (num_error(printopt, errp));
658
659 for (; *s >= '0' && *s <= '9'; s++) {
660 frac = (frac * 10) + (*s - '0');
661 fraclen++;
662 }
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);
673 }
674
675
676 /*
677 * Get the value of the -e flag.
678 */
679 int
get_quit_at_eof(void)680 get_quit_at_eof(void)
681 {
682 return (quit_at_eof);
683 }
684