xref: /netbsd-src/external/bsd/less/dist/option.c (revision 1b9578b8c2c1f848eeb16dabbfd7d1f0d9fdefbd)
1 /*	$NetBSD: option.c,v 1.3 2011/07/03 20:14:13 tron Exp $	*/
2 
3 /*
4  * Copyright (C) 1984-2011  Mark Nudelman
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information about less, or for information on how to
10  * contact the author, see the README file.
11  */
12 
13 
14 /*
15  * Process command line options.
16  *
17  * Each option is a single letter which controls a program variable.
18  * The options have defaults which may be changed via
19  * the command line option, toggled via the "-" command,
20  * or queried via the "_" command.
21  */
22 
23 #include "less.h"
24 #include "option.h"
25 
26 static struct loption *pendopt;
27 public int plusoption = FALSE;
28 
29 static char *optstring __P((char *, char **, char *, char *));
30 static int flip_triple __P((int, int));
31 static void nostring __P((char *));
32 
33 extern int screen_trashed;
34 extern int less_is_more;
35 extern int quit_at_eof;
36 extern char *every_first_cmd;
37 
38 /*
39  * Return a printable description of an option.
40  */
41 	static char *
42 opt_desc(o)
43 	struct loption *o;
44 {
45 	static char buf[OPTNAME_MAX + 10];
46 	if (o->oletter == OLETTER_NONE)
47 		SNPRINTF1(buf, sizeof(buf), "--%s", o->onames->oname);
48 	else
49 		SNPRINTF2(buf, sizeof(buf), "-%c (--%s)", o->oletter, o->onames->oname);
50 	return (buf);
51 }
52 
53 /*
54  * Return a string suitable for printing as the "name" of an option.
55  * For example, if the option letter is 'x', just return "-x".
56  */
57 	public char *
58 propt(c)
59 	int c;
60 {
61 	static char buf[8];
62 
63 	sprintf(buf, "-%s", prchar(c));
64 	return (buf);
65 }
66 
67 /*
68  * Scan an argument (either from the command line or from the
69  * LESS environment variable) and process it.
70  */
71 	public void
72 scan_option(s)
73 	char *s;
74 {
75 	register struct loption *o;
76 	register int optc;
77 	char *optname;
78 	char *printopt;
79 	char *str;
80 	int set_default;
81 	int lc;
82 	int err;
83 	PARG parg;
84 
85 	if (s == NULL)
86 		return;
87 
88 	/*
89 	 * If we have a pending option which requires an argument,
90 	 * handle it now.
91 	 * This happens if the previous option was, for example, "-P"
92 	 * without a following string.  In that case, the current
93 	 * option is simply the argument for the previous option.
94 	 */
95 	if (pendopt != NULL)
96 	{
97 		switch (pendopt->otype & OTYPE)
98 		{
99 		case STRING:
100 			(*pendopt->ofunc)(INIT, s);
101 			break;
102 		case NUMBER:
103 			printopt = opt_desc(pendopt);
104 			*(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
105 			break;
106 		}
107 		pendopt = NULL;
108 		return;
109 	}
110 
111 	set_default = FALSE;
112 	optname = NULL;
113 
114 	while (*s != '\0')
115 	{
116 		/*
117 		 * Check some special cases first.
118 		 */
119 		switch (optc = *s++)
120 		{
121 		case ' ':
122 		case '\t':
123 		case END_OPTION_STRING:
124 			continue;
125 		case '-':
126 			/*
127 			 * "--" indicates an option name instead of a letter.
128 			 */
129 			if (*s == '-')
130 			{
131 				optname = ++s;
132 				break;
133 			}
134 			/*
135 			 * "-+" means set these options back to their defaults.
136 			 * (They may have been set otherwise by previous
137 			 * options.)
138 			 */
139 			set_default = (*s == '+');
140 			if (set_default)
141 				s++;
142 			continue;
143 		case '+':
144 			/*
145 			 * An option prefixed by a "+" is ungotten, so
146 			 * that it is interpreted as less commands
147 			 * processed at the start of the first input file.
148 			 * "++" means process the commands at the start of
149 			 * EVERY input file.
150 			 */
151 			plusoption = TRUE;
152 			s = optstring(s, &str, propt('+'), NULL);
153 			if (*str == '+')
154 				every_first_cmd = save(++str);
155 			else
156 				ungetsc(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 					quit(QUIT_ERROR);
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 			quit(QUIT_ERROR);
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 			break;
268 		case NUMBER:
269 			if (*s == '\0')
270 			{
271 				pendopt = o;
272 				return;
273 			}
274 			*(o->ovar) = getnum(&s, printopt, (int*)NULL);
275 			break;
276 		}
277 		/*
278 		 * If the option has a handling function, call it.
279 		 */
280 		if (o->ofunc != NULL)
281 			(*o->ofunc)(INIT, str);
282 	}
283 }
284 
285 /*
286  * Toggle command line flags from within the program.
287  * Used by the "-" and "_" commands.
288  * how_toggle may be:
289  *	OPT_NO_TOGGLE	just report the current setting, without changing it.
290  *	OPT_TOGGLE	invert the current setting
291  *	OPT_UNSET	set to the default value
292  *	OPT_SET		set to the inverse of the default value
293  */
294 	public void
295 toggle_option(o, lower, s, how_toggle)
296 	struct loption *o;
297 	int lower;
298 	char *s;
299 	int how_toggle;
300 {
301 	register int num;
302 	int no_prompt;
303 	int err;
304 	PARG parg;
305 
306 	no_prompt = (how_toggle & OPT_NO_PROMPT);
307 	how_toggle &= ~OPT_NO_PROMPT;
308 
309 	if (o == NULL)
310 	{
311 		error("No such option", NULL_PARG);
312 		return;
313 	}
314 
315 	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
316 	{
317 		parg.p_string = opt_desc(o);
318 		error("Cannot change the %s option", &parg);
319 		return;
320 	}
321 
322 	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
323 	{
324 		parg.p_string = opt_desc(o);
325 		error("Cannot query the %s option", &parg);
326 		return;
327 	}
328 
329 	/*
330 	 * Check for something which appears to be a do_toggle
331 	 * (because the "-" command was used), but really is not.
332 	 * This could be a string option with no string, or
333 	 * a number option with no number.
334 	 */
335 	switch (o->otype & OTYPE)
336 	{
337 	case STRING:
338 	case NUMBER:
339 		if (how_toggle == OPT_TOGGLE && *s == '\0')
340 			how_toggle = OPT_NO_TOGGLE;
341 		break;
342 	}
343 
344 #if HILITE_SEARCH
345 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
346 		repaint_hilite(0);
347 #endif
348 
349 	/*
350 	 * Now actually toggle (change) the variable.
351 	 */
352 	if (how_toggle != OPT_NO_TOGGLE)
353 	{
354 		switch (o->otype & OTYPE)
355 		{
356 		case BOOL:
357 			/*
358 			 * Boolean.
359 			 */
360 			switch (how_toggle)
361 			{
362 			case OPT_TOGGLE:
363 				*(o->ovar) = ! *(o->ovar);
364 				break;
365 			case OPT_UNSET:
366 				*(o->ovar) = o->odefault;
367 				break;
368 			case OPT_SET:
369 				*(o->ovar) = ! o->odefault;
370 				break;
371 			}
372 			break;
373 		case TRIPLE:
374 			/*
375 			 * Triple:
376 			 *	If user gave the lower case letter, then switch
377 			 *	to 1 unless already 1, in which case make it 0.
378 			 *	If user gave the upper case letter, then switch
379 			 *	to 2 unless already 2, in which case make it 0.
380 			 */
381 			switch (how_toggle)
382 			{
383 			case OPT_TOGGLE:
384 				*(o->ovar) = flip_triple(*(o->ovar), lower);
385 				break;
386 			case OPT_UNSET:
387 				*(o->ovar) = o->odefault;
388 				break;
389 			case OPT_SET:
390 				*(o->ovar) = flip_triple(o->odefault, lower);
391 				break;
392 			}
393 			break;
394 		case STRING:
395 			/*
396 			 * String: don't do anything here.
397 			 *	The handling function will do everything.
398 			 */
399 			switch (how_toggle)
400 			{
401 			case OPT_SET:
402 			case OPT_UNSET:
403 				error("Cannot use \"-+\" or \"--\" for a string option",
404 					NULL_PARG);
405 				return;
406 			}
407 			break;
408 		case NUMBER:
409 			/*
410 			 * Number: set the variable to the given number.
411 			 */
412 			switch (how_toggle)
413 			{
414 			case OPT_TOGGLE:
415 				num = getnum(&s, NULL, &err);
416 				if (!err)
417 					*(o->ovar) = num;
418 				break;
419 			case OPT_UNSET:
420 				*(o->ovar) = o->odefault;
421 				break;
422 			case OPT_SET:
423 				error("Can't use \"-!\" for a numeric option",
424 					NULL_PARG);
425 				return;
426 			}
427 			break;
428 		}
429 	}
430 
431 	/*
432 	 * Call the handling function for any special action
433 	 * specific to this option.
434 	 */
435 	if (o->ofunc != NULL)
436 		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
437 
438 #if HILITE_SEARCH
439 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
440 		chg_hilite();
441 #endif
442 
443 	if (!no_prompt)
444 	{
445 		/*
446 		 * Print a message describing the new setting.
447 		 */
448 		switch (o->otype & OTYPE)
449 		{
450 		case BOOL:
451 		case TRIPLE:
452 			if (*(o->ovar) < 0)
453 				error("Negative option is invalid", NULL_PARG);
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