xref: /openbsd/usr.bin/less/option.c (revision a6445c1d)
1 /*
2  * Copyright (C) 1984-2012  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, see the README file.
8  */
9 
10 
11 /*
12  * Process command line options.
13  *
14  * Each option is a single letter which controls a program variable.
15  * The options have defaults which may be changed via
16  * the command line option, toggled via the "-" command,
17  * or queried via the "_" command.
18  */
19 
20 #include "less.h"
21 #include "option.h"
22 
23 static struct loption *pendopt;
24 public int plusoption = FALSE;
25 
26 static char *optstring();
27 static int flip_triple();
28 
29 extern int screen_trashed;
30 extern int less_is_more;
31 extern int quit_at_eof;
32 extern char *every_first_cmd;
33 extern int opt_use_backslash;
34 
35 /*
36  * Return a printable description of an option.
37  */
38 	static char *
39 opt_desc(o)
40 	struct loption *o;
41 {
42 	static char buf[OPTNAME_MAX + 10];
43 	if (o->oletter == OLETTER_NONE)
44 		SNPRINTF1(buf, sizeof(buf), "--%s", o->onames->oname);
45 	else
46 		SNPRINTF2(buf, sizeof(buf), "-%c (--%s)", o->oletter, o->onames->oname);
47 	return (buf);
48 }
49 
50 /*
51  * Return a string suitable for printing as the "name" of an option.
52  * For example, if the option letter is 'x', just return "-x".
53  */
54 	public char *
55 propt(c)
56 	int c;
57 {
58 	static char buf[8];
59 
60 	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 	public void
69 scan_option(s)
70 	char *s;
71 {
72 	register struct loption *o;
73 	register int optc;
74 	char *optname;
75 	char *printopt;
76 	char *str;
77 	int set_default;
78 	int lc;
79 	int err;
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 	{
94 		switch (pendopt->otype & OTYPE)
95 		{
96 		case STRING:
97 			(*pendopt->ofunc)(INIT, s);
98 			break;
99 		case NUMBER:
100 			printopt = opt_desc(pendopt);
101 			*(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
102 			break;
103 		}
104 		pendopt = NULL;
105 		return;
106 	}
107 
108 	set_default = FALSE;
109 	optname = NULL;
110 
111 	while (*s != '\0')
112 	{
113 		/*
114 		 * Check some special cases first.
115 		 */
116 		switch (optc = *s++)
117 		{
118 		case ' ':
119 		case '\t':
120 		case END_OPTION_STRING:
121 			continue;
122 		case '-':
123 #if GNU_OPTIONS
124 			/*
125 			 * "--" indicates an option name instead of a letter.
126 			 */
127 			if (*s == '-')
128 			{
129 				optname = ++s;
130 				break;
131 			}
132 #endif
133 			/*
134 			 * "-+" means set these options back to their defaults.
135 			 * (They may have been set otherwise by previous
136 			 * options.)
137 			 */
138 			set_default = (*s == '+');
139 			if (set_default)
140 				s++;
141 			continue;
142 		case '+':
143 			/*
144 			 * An option prefixed by a "+" is ungotten, so
145 			 * that it is interpreted as less commands
146 			 * processed at the start of the first input file.
147 			 * "++" means process the commands at the start of
148 			 * EVERY input file.
149 			 */
150 			plusoption = TRUE;
151 			s = optstring(s, &str, propt('+'), NULL);
152 			if (s == NULL)
153 				return;
154 			if (*str == '+')
155 				every_first_cmd = save(str+1);
156 			else
157 				ungetsc(str);
158 			free(str);
159 			continue;
160 		case '0':  case '1':  case '2':  case '3':  case '4':
161 		case '5':  case '6':  case '7':  case '8':  case '9':
162 			/*
163 			 * Special "more" compatibility form "-<number>"
164 			 * instead of -z<number> to set the scrolling
165 			 * window size.
166 			 */
167 			s--;
168 			optc = 'z';
169 			break;
170 		case 'n':
171 			if (less_is_more)
172 				optc = 'z';
173 			break;
174 		}
175 
176 		/*
177 		 * Not a special case.
178 		 * Look up the option letter in the option table.
179 		 */
180 		err = 0;
181 		if (optname == NULL)
182 		{
183 			printopt = propt(optc);
184 			lc = ASCII_IS_LOWER(optc);
185 			o = findopt(optc);
186 		}
187 #if GNU_OPTIONS
188 		else
189 		{
190 			printopt = optname;
191 			lc = ASCII_IS_LOWER(optname[0]);
192 			o = findopt_name(&optname, NULL, &err);
193 			s = optname;
194 			optname = NULL;
195 			if (*s == '\0' || *s == ' ')
196 			{
197 				/*
198 				 * The option name matches exactly.
199 				 */
200 				;
201 			} else if (*s == '=')
202 			{
203 				/*
204 				 * The option name is followed by "=value".
205 				 */
206 				if (o != NULL &&
207 				    (o->otype & OTYPE) != STRING &&
208 				    (o->otype & OTYPE) != NUMBER)
209 				{
210 					parg.p_string = printopt;
211 					error("The %s option should not be followed by =",
212 						&parg);
213 					return;
214 				}
215 				s++;
216 			} else
217 			{
218 				/*
219 				 * The specified name is longer than the
220 				 * real option name.
221 				 */
222 				o = NULL;
223 			}
224 		}
225 #endif
226 		if (o == NULL)
227 		{
228 			parg.p_string = printopt;
229 			if (err == OPT_AMBIG)
230 				error("%s is an ambiguous abbreviation (\"less --help\" for help)",
231 					&parg);
232 			else
233 				error("There is no %s option (\"less --help\" for help)",
234 					&parg);
235 			return;
236 		}
237 
238 		str = NULL;
239 		switch (o->otype & OTYPE)
240 		{
241 		case BOOL:
242 			if (set_default)
243 				*(o->ovar) = o->odefault;
244 			else
245 				*(o->ovar) = ! o->odefault;
246 			break;
247 		case TRIPLE:
248 			if (set_default)
249 				*(o->ovar) = o->odefault;
250 			else
251 				*(o->ovar) = flip_triple(o->odefault, lc);
252 			break;
253 		case STRING:
254 			if (*s == '\0')
255 			{
256 				/*
257 				 * Set pendopt and return.
258 				 * We will get the string next time
259 				 * scan_option is called.
260 				 */
261 				pendopt = o;
262 				return;
263 			}
264 			/*
265 			 * Don't do anything here.
266 			 * All processing of STRING options is done by
267 			 * the handling function.
268 			 */
269 			while (*s == ' ')
270 				s++;
271 			s = optstring(s, &str, printopt, o->odesc[1]);
272 			if (s == NULL)
273 				return;
274 			break;
275 		case NUMBER:
276 			if (*s == '\0')
277 			{
278 				pendopt = o;
279 				return;
280 			}
281 			*(o->ovar) = getnum(&s, printopt, (int*)NULL);
282 			break;
283 		}
284 		/*
285 		 * If the option has a handling function, call it.
286 		 */
287 		if (o->ofunc != NULL)
288 			(*o->ofunc)(INIT, str);
289 		if (str != NULL)
290 			free(str);
291 	}
292 }
293 
294 /*
295  * Toggle command line flags from within the program.
296  * Used by the "-" and "_" commands.
297  * how_toggle may be:
298  *	OPT_NO_TOGGLE	just report the current setting, without changing it.
299  *	OPT_TOGGLE	invert the current setting
300  *	OPT_UNSET	set to the default value
301  *	OPT_SET		set to the inverse of the default value
302  */
303 	public void
304 toggle_option(o, lower, s, how_toggle)
305 	struct loption *o;
306 	int lower;
307 	char *s;
308 	int how_toggle;
309 {
310 	register int num;
311 	int no_prompt;
312 	int err;
313 	PARG parg;
314 
315 	no_prompt = (how_toggle & OPT_NO_PROMPT);
316 	how_toggle &= ~OPT_NO_PROMPT;
317 
318 	if (o == NULL)
319 	{
320 		error("No such option", NULL_PARG);
321 		return;
322 	}
323 
324 	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
325 	{
326 		parg.p_string = opt_desc(o);
327 		error("Cannot change the %s option", &parg);
328 		return;
329 	}
330 
331 	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
332 	{
333 		parg.p_string = opt_desc(o);
334 		error("Cannot query the %s option", &parg);
335 		return;
336 	}
337 
338 	/*
339 	 * Check for something which appears to be a do_toggle
340 	 * (because the "-" command was used), but really is not.
341 	 * This could be a string option with no string, or
342 	 * a number option with no number.
343 	 */
344 	switch (o->otype & OTYPE)
345 	{
346 	case STRING:
347 	case NUMBER:
348 		if (how_toggle == OPT_TOGGLE && *s == '\0')
349 			how_toggle = OPT_NO_TOGGLE;
350 		break;
351 	}
352 
353 #if HILITE_SEARCH
354 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
355 		repaint_hilite(0);
356 #endif
357 
358 	/*
359 	 * Now actually toggle (change) the variable.
360 	 */
361 	if (how_toggle != OPT_NO_TOGGLE)
362 	{
363 		switch (o->otype & OTYPE)
364 		{
365 		case BOOL:
366 			/*
367 			 * Boolean.
368 			 */
369 			switch (how_toggle)
370 			{
371 			case OPT_TOGGLE:
372 				*(o->ovar) = ! *(o->ovar);
373 				break;
374 			case OPT_UNSET:
375 				*(o->ovar) = o->odefault;
376 				break;
377 			case OPT_SET:
378 				*(o->ovar) = ! o->odefault;
379 				break;
380 			}
381 			break;
382 		case TRIPLE:
383 			/*
384 			 * Triple:
385 			 *	If user gave the lower case letter, then switch
386 			 *	to 1 unless already 1, in which case make it 0.
387 			 *	If user gave the upper case letter, then switch
388 			 *	to 2 unless already 2, in which case make it 0.
389 			 */
390 			switch (how_toggle)
391 			{
392 			case OPT_TOGGLE:
393 				*(o->ovar) = flip_triple(*(o->ovar), lower);
394 				break;
395 			case OPT_UNSET:
396 				*(o->ovar) = o->odefault;
397 				break;
398 			case OPT_SET:
399 				*(o->ovar) = flip_triple(o->odefault, lower);
400 				break;
401 			}
402 			break;
403 		case STRING:
404 			/*
405 			 * String: don't do anything here.
406 			 *	The handling function will do everything.
407 			 */
408 			switch (how_toggle)
409 			{
410 			case OPT_SET:
411 			case OPT_UNSET:
412 				error("Cannot use \"-+\" or \"--\" for a string option",
413 					NULL_PARG);
414 				return;
415 			}
416 			break;
417 		case NUMBER:
418 			/*
419 			 * Number: set the variable to the given number.
420 			 */
421 			switch (how_toggle)
422 			{
423 			case OPT_TOGGLE:
424 				num = getnum(&s, NULL, &err);
425 				if (!err)
426 					*(o->ovar) = num;
427 				break;
428 			case OPT_UNSET:
429 				*(o->ovar) = o->odefault;
430 				break;
431 			case OPT_SET:
432 				error("Can't use \"-!\" for a numeric option",
433 					NULL_PARG);
434 				return;
435 			}
436 			break;
437 		}
438 	}
439 
440 	/*
441 	 * Call the handling function for any special action
442 	 * specific to this option.
443 	 */
444 	if (o->ofunc != NULL)
445 		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
446 
447 #if HILITE_SEARCH
448 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
449 		chg_hilite();
450 #endif
451 
452 	if (!no_prompt)
453 	{
454 		/*
455 		 * Print a message describing the new setting.
456 		 */
457 		switch (o->otype & OTYPE)
458 		{
459 		case BOOL:
460 		case TRIPLE:
461 			/*
462 			 * Print the odesc message.
463 			 */
464 			error(o->odesc[*(o->ovar)], NULL_PARG);
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
490 flip_triple(val, lc)
491 	int val;
492 	int lc;
493 {
494 	if (lc)
495 		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
496 	else
497 		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
498 }
499 
500 /*
501  * Determine if an option takes a parameter.
502  */
503 	public int
504 opt_has_param(o)
505 	struct loption *o;
506 {
507 	if (o == NULL)
508 		return (0);
509 	if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
510 		return (0);
511 	return (1);
512 }
513 
514 /*
515  * Return the prompt to be used for a given option letter.
516  * Only string and number valued options have prompts.
517  */
518 	public char *
519 opt_prompt(o)
520 	struct loption *o;
521 {
522 	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
523 		return ("?");
524 	return (o->odesc[0]);
525 }
526 
527 /*
528  * Return whether or not there is a string option pending;
529  * that is, if the previous option was a string-valued option letter
530  * (like -P) without a following string.
531  * In that case, the current option is taken to be the string for
532  * the previous option.
533  */
534 	public int
535 isoptpending()
536 {
537 	return (pendopt != NULL);
538 }
539 
540 /*
541  * Print error message about missing string.
542  */
543 	static void
544 nostring(printopt)
545 	char *printopt;
546 {
547 	PARG parg;
548 	parg.p_string = printopt;
549 	error("Value is required after %s", &parg);
550 }
551 
552 /*
553  * Print error message if a STRING type option is not followed by a string.
554  */
555 	public void
556 nopendopt()
557 {
558 	nostring(opt_desc(pendopt));
559 }
560 
561 /*
562  * Scan to end of string or to an END_OPTION_STRING character.
563  * In the latter case, replace the char with a null char.
564  * Return a pointer to the remainder of the string, if any.
565  */
566 	static char *
567 optstring(s, p_str, printopt, validchars)
568 	char *s;
569 	char **p_str;
570 	char *printopt;
571 	char *validchars;
572 {
573 	register char *p;
574 	register char *out;
575 
576 	if (*s == '\0')
577 	{
578 		nostring(printopt);
579 		return (NULL);
580 	}
581 	/* Alloc could be more than needed, but not worth trimming. */
582 	*p_str = (char *) ecalloc(strlen(s)+1, sizeof(char));
583 	out = *p_str;
584 
585 	for (p = s;  *p != '\0';  p++)
586 	{
587 		if (opt_use_backslash && *p == '\\' && p[1] != '\0')
588 		{
589 			/* Take next char literally. */
590 			++p;
591 		} else
592 		{
593 			if (*p == END_OPTION_STRING ||
594 			    (validchars != NULL && strchr(validchars, *p) == NULL))
595 				/* End of option string. */
596 				break;
597 		}
598 		*out++ = *p;
599 	}
600 	*out = '\0';
601 	return (p);
602 }
603 
604 /*
605  */
606 	static int
607 num_error(printopt, errp)
608 	char *printopt;
609 	int *errp;
610 {
611 	PARG parg;
612 
613 	if (errp != NULL)
614 	{
615 		*errp = TRUE;
616 		return (-1);
617 	}
618 	if (printopt != NULL)
619 	{
620 		parg.p_string = printopt;
621 		error("Number is required after %s", &parg);
622 	}
623 	return (-1);
624 }
625 
626 /*
627  * Translate a string into a number.
628  * Like atoi(), but takes a pointer to a char *, and updates
629  * the char * to point after the translated number.
630  */
631 	public int
632 getnum(sp, printopt, errp)
633 	char **sp;
634 	char *printopt;
635 	int *errp;
636 {
637 	register char *s;
638 	register int n;
639 	register int neg;
640 
641 	s = skipsp(*sp);
642 	neg = FALSE;
643 	if (*s == '-')
644 	{
645 		neg = TRUE;
646 		s++;
647 	}
648 	if (*s < '0' || *s > '9')
649 		return (num_error(printopt, errp));
650 
651 	n = 0;
652 	while (*s >= '0' && *s <= '9')
653 		n = 10 * n + *s++ - '0';
654 	*sp = s;
655 	if (errp != NULL)
656 		*errp = FALSE;
657 	if (neg)
658 		n = -n;
659 	return (n);
660 }
661 
662 /*
663  * Translate a string into a fraction, represented by the part of a
664  * number which would follow a decimal point.
665  * The value of the fraction is returned as parts per NUM_FRAC_DENOM.
666  * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM.
667  */
668 	public long
669 getfraction(sp, printopt, errp)
670 	char **sp;
671 	char *printopt;
672 	int *errp;
673 {
674 	register char *s;
675 	long frac = 0;
676 	int fraclen = 0;
677 
678 	s = skipsp(*sp);
679 	if (*s < '0' || *s > '9')
680 		return (num_error(printopt, errp));
681 
682 	for ( ;  *s >= '0' && *s <= '9';  s++)
683 	{
684 		frac = (frac * 10) + (*s - '0');
685 		fraclen++;
686 	}
687 	if (fraclen > NUM_LOG_FRAC_DENOM)
688 		while (fraclen-- > NUM_LOG_FRAC_DENOM)
689 			frac /= 10;
690 	else
691 		while (fraclen++ < NUM_LOG_FRAC_DENOM)
692 			frac *= 10;
693 	*sp = s;
694 	if (errp != NULL)
695 		*errp = FALSE;
696 	return (frac);
697 }
698 
699 
700 /*
701  * Get the value of the -e flag.
702  */
703 	public int
704 get_quit_at_eof()
705 {
706 	if (!less_is_more)
707 		return quit_at_eof;
708 	/* When less_is_more is set, the -e flag semantics are different. */
709 	return quit_at_eof ? OPT_ONPLUS : OPT_ON;
710 }
711