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