xref: /openbsd/usr.bin/less/option.c (revision b14c4897)
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