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