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