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