xref: /freebsd/contrib/less/option.c (revision 8ab2f5ecc596131f6ca790d6ae35540c06ed7985)
1 /*
2  * Copyright (C) 1984-2002  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 *propt();
28 static char *optstring();
29 static int flip_triple();
30 
31 extern int screen_trashed;
32 extern char *every_first_cmd;
33 
34 /*
35  * Scan an argument (either from the command line or from the
36  * LESS environment variable) and process it.
37  */
38 	public void
39 scan_option(s)
40 	char *s;
41 {
42 	register struct loption *o;
43 	register int optc;
44 	char *optname;
45 	char *printopt;
46 	char *str;
47 	int set_default;
48 	int lc;
49 	int err;
50 	PARG parg;
51 
52 	if (s == NULL)
53 		return;
54 
55 	/*
56 	 * If we have a pending option which requires an argument,
57 	 * handle it now.
58 	 * This happens if the previous option was, for example, "-P"
59 	 * without a following string.  In that case, the current
60 	 * option is simply the argument for the previous option.
61 	 */
62 	if (pendopt != NULL)
63 	{
64 		switch (pendopt->otype & OTYPE)
65 		{
66 		case STRING:
67 			(*pendopt->ofunc)(INIT, s);
68 			break;
69 		case NUMBER:
70 			printopt = propt(pendopt->oletter);
71 			*(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
72 			break;
73 		}
74 		pendopt = NULL;
75 		return;
76 	}
77 
78 	set_default = FALSE;
79 	optname = NULL;
80 
81 	while (*s != '\0')
82 	{
83 		/*
84 		 * Check some special cases first.
85 		 */
86 		switch (optc = *s++)
87 		{
88 		case ' ':
89 		case '\t':
90 		case END_OPTION_STRING:
91 			continue;
92 		case '-':
93 			/*
94 			 * "--" indicates an option name instead of a letter.
95 			 */
96 			if (*s == '-')
97 			{
98 				optname = ++s;
99 				break;
100 			}
101 			/*
102 			 * "-+" means set these options back to their defaults.
103 			 * (They may have been set otherwise by previous
104 			 * options.)
105 			 */
106 			set_default = (*s == '+');
107 			if (set_default)
108 				s++;
109 			continue;
110 		case '+':
111 			/*
112 			 * An option prefixed by a "+" is ungotten, so
113 			 * that it is interpreted as less commands
114 			 * processed at the start of the first input file.
115 			 * "++" means process the commands at the start of
116 			 * EVERY input file.
117 			 */
118 			plusoption = TRUE;
119 			s = optstring(s, &str, propt('+'), NULL);
120 			if (*str == '+')
121 				every_first_cmd = save(++str);
122 			else
123 				ungetsc(str);
124 			continue;
125 		case '0':  case '1':  case '2':  case '3':  case '4':
126 		case '5':  case '6':  case '7':  case '8':  case '9':
127 			/*
128 			 * Special "more" compatibility form "-<number>"
129 			 * instead of -z<number> to set the scrolling
130 			 * window size.
131 			 */
132 			s--;
133 			optc = 'z';
134 			break;
135 		}
136 
137 		/*
138 		 * Not a special case.
139 		 * Look up the option letter in the option table.
140 		 */
141 		err = 0;
142 		if (optname == NULL)
143 		{
144 			printopt = propt(optc);
145 			lc = SIMPLE_IS_LOWER(optc);
146 			o = findopt(optc);
147 		} else
148 		{
149 			printopt = optname;
150 			lc = SIMPLE_IS_LOWER(optname[0]);
151 			o = findopt_name(&optname, NULL, &err);
152 			s = optname;
153 			optname = NULL;
154 			if (*s == '\0' || *s == ' ')
155 			{
156 				/*
157 				 * The option name matches exactly.
158 				 */
159 				;
160 			} else if (*s == '=')
161 			{
162 				/*
163 				 * The option name is followed by "=value".
164 				 */
165 				if (o != NULL &&
166 				    (o->otype & OTYPE) != STRING &&
167 				    (o->otype & OTYPE) != NUMBER)
168 				{
169 					parg.p_string = printopt;
170 					error("The %s option should not be followed by =",
171 						&parg);
172 					quit(QUIT_ERROR);
173 				}
174 				s++;
175 			} else
176 			{
177 				/*
178 				 * The specified name is longer than the
179 				 * real option name.
180 				 */
181 				o = NULL;
182 			}
183 		}
184 		if (o == NULL)
185 		{
186 			parg.p_string = printopt;
187 			if (err == OPT_AMBIG)
188 				error("%s is an ambiguous abbreviation (\"less --help\" for help)",
189 					&parg);
190 			else
191 				error("There is no %s option (\"less --help\" for help)",
192 					&parg);
193 			quit(QUIT_ERROR);
194 		}
195 
196 		str = NULL;
197 		switch (o->otype & OTYPE)
198 		{
199 		case BOOL:
200 			if (set_default)
201 				*(o->ovar) = o->odefault;
202 			else
203 				*(o->ovar) = ! o->odefault;
204 			break;
205 		case TRIPLE:
206 			if (set_default)
207 				*(o->ovar) = o->odefault;
208 			else
209 				*(o->ovar) = flip_triple(o->odefault, lc);
210 			break;
211 		case STRING:
212 			if (*s == '\0')
213 			{
214 				/*
215 				 * Set pendopt and return.
216 				 * We will get the string next time
217 				 * scan_option is called.
218 				 */
219 				pendopt = o;
220 				return;
221 			}
222 			/*
223 			 * Don't do anything here.
224 			 * All processing of STRING options is done by
225 			 * the handling function.
226 			 */
227 			while (*s == ' ')
228 				s++;
229 			s = optstring(s, &str, printopt, o->odesc[1]);
230 			break;
231 		case NUMBER:
232 			if (*s == '\0')
233 			{
234 				pendopt = o;
235 				return;
236 			}
237 			*(o->ovar) = getnum(&s, printopt, (int*)NULL);
238 			break;
239 		}
240 		/*
241 		 * If the option has a handling function, call it.
242 		 */
243 		if (o->ofunc != NULL)
244 			(*o->ofunc)(INIT, str);
245 	}
246 }
247 
248 /*
249  * Toggle command line flags from within the program.
250  * Used by the "-" and "_" commands.
251  * how_toggle may be:
252  *	OPT_NO_TOGGLE	just report the current setting, without changing it.
253  *	OPT_TOGGLE	invert the current setting
254  *	OPT_UNSET	set to the default value
255  *	OPT_SET		set to the inverse of the default value
256  */
257 	public void
258 toggle_option(c, s, how_toggle)
259 	int c;
260 	char *s;
261 	int how_toggle;
262 {
263 	register struct loption *o;
264 	register int num;
265 	int no_prompt;
266 	int err;
267 	PARG parg;
268 
269 	no_prompt = (how_toggle & OPT_NO_PROMPT);
270 	how_toggle &= ~OPT_NO_PROMPT;
271 
272 	/*
273 	 * Look up the option letter in the option table.
274 	 */
275 	o = findopt(c);
276 	if (o == NULL)
277 	{
278 		parg.p_string = propt(c);
279 		error("There is no %s option", &parg);
280 		return;
281 	}
282 
283 	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
284 	{
285 		parg.p_string = propt(c);
286 		error("Cannot change the %s option", &parg);
287 		return;
288 	}
289 
290 	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
291 	{
292 		parg.p_string = propt(c);
293 		error("Cannot query the %s option", &parg);
294 		return;
295 	}
296 
297 	/*
298 	 * Check for something which appears to be a do_toggle
299 	 * (because the "-" command was used), but really is not.
300 	 * This could be a string option with no string, or
301 	 * a number option with no number.
302 	 */
303 	switch (o->otype & OTYPE)
304 	{
305 	case STRING:
306 	case NUMBER:
307 		if (how_toggle == OPT_TOGGLE && *s == '\0')
308 			how_toggle = OPT_NO_TOGGLE;
309 		break;
310 	}
311 
312 #if HILITE_SEARCH
313 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
314 		repaint_hilite(0);
315 #endif
316 
317 	/*
318 	 * Now actually toggle (change) the variable.
319 	 */
320 	if (how_toggle != OPT_NO_TOGGLE)
321 	{
322 		switch (o->otype & OTYPE)
323 		{
324 		case BOOL:
325 			/*
326 			 * Boolean.
327 			 */
328 			switch (how_toggle)
329 			{
330 			case OPT_TOGGLE:
331 				*(o->ovar) = ! *(o->ovar);
332 				break;
333 			case OPT_UNSET:
334 				*(o->ovar) = o->odefault;
335 				break;
336 			case OPT_SET:
337 				*(o->ovar) = ! o->odefault;
338 				break;
339 			}
340 			break;
341 		case TRIPLE:
342 			/*
343 			 * Triple:
344 			 *	If user gave the lower case letter, then switch
345 			 *	to 1 unless already 1, in which case make it 0.
346 			 *	If user gave the upper case letter, then switch
347 			 *	to 2 unless already 2, in which case make it 0.
348 			 */
349 			switch (how_toggle)
350 			{
351 			case OPT_TOGGLE:
352 				*(o->ovar) = flip_triple(*(o->ovar),
353 						islower(c));
354 				break;
355 			case OPT_UNSET:
356 				*(o->ovar) = o->odefault;
357 				break;
358 			case OPT_SET:
359 				*(o->ovar) = flip_triple(o->odefault,
360 						islower(c));
361 				break;
362 			}
363 			break;
364 		case STRING:
365 			/*
366 			 * String: don't do anything here.
367 			 *	The handling function will do everything.
368 			 */
369 			switch (how_toggle)
370 			{
371 			case OPT_SET:
372 			case OPT_UNSET:
373 				error("Cannot use \"-+\" or \"--\" for a string option",
374 					NULL_PARG);
375 				return;
376 			}
377 			break;
378 		case NUMBER:
379 			/*
380 			 * Number: set the variable to the given number.
381 			 */
382 			switch (how_toggle)
383 			{
384 			case OPT_TOGGLE:
385 				num = getnum(&s, NULL, &err);
386 				if (!err)
387 					*(o->ovar) = num;
388 				break;
389 			case OPT_UNSET:
390 				*(o->ovar) = o->odefault;
391 				break;
392 			case OPT_SET:
393 				error("Can't use \"-!\" for a numeric option",
394 					NULL_PARG);
395 				return;
396 			}
397 			break;
398 		}
399 	}
400 
401 	/*
402 	 * Call the handling function for any special action
403 	 * specific to this option.
404 	 */
405 	if (o->ofunc != NULL)
406 		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
407 
408 #if HILITE_SEARCH
409 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
410 		chg_hilite();
411 #endif
412 
413 	if (!no_prompt)
414 	{
415 		/*
416 		 * Print a message describing the new setting.
417 		 */
418 		switch (o->otype & OTYPE)
419 		{
420 		case BOOL:
421 		case TRIPLE:
422 			/*
423 			 * Print the odesc message.
424 			 */
425 			error(o->odesc[*(o->ovar)], NULL_PARG);
426 			break;
427 		case NUMBER:
428 			/*
429 			 * The message is in odesc[1] and has a %d for
430 			 * the value of the variable.
431 			 */
432 			parg.p_int = *(o->ovar);
433 			error(o->odesc[1], &parg);
434 			break;
435 		case STRING:
436 			/*
437 			 * Message was already printed by the handling function.
438 			 */
439 			break;
440 		}
441 	}
442 
443 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
444 		screen_trashed = TRUE;
445 }
446 
447 /*
448  * "Toggle" a triple-valued option.
449  */
450 	static int
451 flip_triple(val, lc)
452 	int val;
453 	int lc;
454 {
455 	if (lc)
456 		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
457 	else
458 		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
459 }
460 
461 /*
462  * Return a string suitable for printing as the "name" of an option.
463  * For example, if the option letter is 'x', just return "-x".
464  */
465 	static char *
466 propt(c)
467 	int c;
468 {
469 	static char buf[8];
470 
471 	sprintf(buf, "-%s", prchar(c));
472 	return (buf);
473 }
474 
475 /*
476  * Determine if an option is a single character option (BOOL or TRIPLE),
477  * or if it a multi-character option (NUMBER).
478  */
479 	public int
480 single_char_option(c)
481 	int c;
482 {
483 	register struct loption *o;
484 
485 	o = findopt(c);
486 	if (o == NULL)
487 		return (TRUE);
488 	return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
489 }
490 
491 /*
492  * Return the prompt to be used for a given option letter.
493  * Only string and number valued options have prompts.
494  */
495 	public char *
496 opt_prompt(c)
497 	int c;
498 {
499 	register struct loption *o;
500 
501 	o = findopt(c);
502 	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
503 		return (NULL);
504 	return (o->odesc[0]);
505 }
506 
507 /*
508  * Return whether or not there is a string option pending;
509  * that is, if the previous option was a string-valued option letter
510  * (like -P) without a following string.
511  * In that case, the current option is taken to be the string for
512  * the previous option.
513  */
514 	public int
515 isoptpending()
516 {
517 	return (pendopt != NULL);
518 }
519 
520 /*
521  * Print error message about missing string.
522  */
523 	static void
524 nostring(printopt)
525 	char *printopt;
526 {
527 	PARG parg;
528 	parg.p_string = printopt;
529 	error("Value is required after %s", &parg);
530 }
531 
532 /*
533  * Print error message if a STRING type option is not followed by a string.
534  */
535 	public void
536 nopendopt()
537 {
538 	nostring(propt(pendopt->oletter));
539 }
540 
541 /*
542  * Scan to end of string or to an END_OPTION_STRING character.
543  * In the latter case, replace the char with a null char.
544  * Return a pointer to the remainder of the string, if any.
545  */
546 	static char *
547 optstring(s, p_str, printopt, validchars)
548 	char *s;
549 	char **p_str;
550 	char *printopt;
551 	char *validchars;
552 {
553 	register char *p;
554 
555 	if (*s == '\0')
556 	{
557 		nostring(printopt);
558 		quit(QUIT_ERROR);
559 	}
560 	*p_str = s;
561 	for (p = s;  *p != '\0';  p++)
562 	{
563 		if (*p == END_OPTION_STRING ||
564 		    (validchars != NULL && strchr(validchars, *p) == NULL))
565 		{
566 			switch (*p)
567 			{
568 			case END_OPTION_STRING:
569 			case ' ':  case '\t':  case '-':
570 				/* Replace the char with a null to terminate string. */
571 				*p++ = '\0';
572 				break;
573 			default:
574 				/* Cannot replace char; make a copy of the string. */
575 				*p_str = (char *) ecalloc(p-s+1, sizeof(char));
576 				strncpy(*p_str, s, p-s);
577 				(*p_str)[p-s] = '\0';
578 				break;
579 			}
580 			break;
581 		}
582 	}
583 	return (p);
584 }
585 
586 /*
587  * Translate a string into a number.
588  * Like atoi(), but takes a pointer to a char *, and updates
589  * the char * to point after the translated number.
590  */
591 	public int
592 getnum(sp, printopt, errp)
593 	char **sp;
594 	char *printopt;
595 	int *errp;
596 {
597 	register char *s;
598 	register int n;
599 	register int neg;
600 	PARG parg;
601 
602 	s = skipsp(*sp);
603 	neg = FALSE;
604 	if (*s == '-')
605 	{
606 		neg = TRUE;
607 		s++;
608 	}
609 	if (*s < '0' || *s > '9')
610 	{
611 		if (errp != NULL)
612 		{
613 			*errp = TRUE;
614 			return (-1);
615 		}
616 		if (printopt != NULL)
617 		{
618 			parg.p_string = printopt;
619 			error("Number is required after %s", &parg);
620 		}
621 		quit(QUIT_ERROR);
622 	}
623 
624 	n = 0;
625 	while (*s >= '0' && *s <= '9')
626 		n = 10 * n + *s++ - '0';
627 	*sp = s;
628 	if (errp != NULL)
629 		*errp = FALSE;
630 	if (neg)
631 		n = -n;
632 	return (n);
633 }
634