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