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