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