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