xref: /openbsd-src/usr.bin/less/option.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: option.c,v 1.2 2001/01/29 01:58:03 niklas Exp $	*/
2 
3 /*
4  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice in the documentation and/or other materials provided with
14  *    the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
22  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 
30 /*
31  * Process command line options.
32  *
33  * Each option is a single letter which controls a program variable.
34  * The options have defaults which may be changed via
35  * the command line option, toggled via the "-" command,
36  * or queried via the "_" command.
37  */
38 
39 #include "less.h"
40 #include "option.h"
41 
42 static struct option *pendopt;
43 public int plusoption = FALSE;
44 
45 static char *propt();
46 static char *optstring();
47 static int flip_triple();
48 
49 extern int screen_trashed;
50 extern char *every_first_cmd;
51 
52 /*
53  * Scan an argument (either from the command line or from the
54  * LESS environment variable) and process it.
55  */
56 	public void
57 scan_option(s)
58 	char *s;
59 {
60 	register struct option *o;
61 	register int c;
62 	char *str;
63 	int set_default;
64 	PARG parg;
65 
66 	if (s == NULL)
67 		return;
68 
69 	/*
70 	 * If we have a pending string-valued option, handle it now.
71 	 * This happens if the previous option was, for example, "-P"
72 	 * without a following string.  In that case, the current
73 	 * option is simply the string for the previous option.
74 	 */
75 	if (pendopt != NULL)
76 	{
77 		(*pendopt->ofunc)(INIT, s);
78 		pendopt = NULL;
79 		return;
80 	}
81 
82 	set_default = FALSE;
83 
84 	while (*s != '\0')
85 	{
86 		/*
87 		 * Check some special cases first.
88 		 */
89 		switch (c = *s++)
90 		{
91 		case ' ':
92 		case '\t':
93 		case END_OPTION_STRING:
94 			continue;
95 		case '-':
96 			/*
97 			 * "-+" means set these options back to their defaults.
98 			 * (They may have been set otherwise by previous
99 			 * options.)
100 			 */
101 			if (set_default = (*s == '+'))
102 				s++;
103 			continue;
104 		case '+':
105 			/*
106 			 * An option prefixed by a "+" is ungotten, so
107 			 * that it is interpreted as less commands
108 			 * processed at the start of the first input file.
109 			 * "++" means process the commands at the start of
110 			 * EVERY input file.
111 			 */
112 			plusoption = TRUE;
113 			if (*s == '+')
114 				every_first_cmd = save(++s);
115 			else
116 				ungetsc(s);
117 			s = optstring(s, c);
118 			continue;
119 		case '0':  case '1':  case '2':  case '3':  case '4':
120 		case '5':  case '6':  case '7':  case '8':  case '9':
121 			/*
122 			 * Special "more" compatibility form "-<number>"
123 			 * instead of -z<number> to set the scrolling
124 			 * window size.
125 			 */
126 			s--;
127 			c = 'z';
128 			break;
129 		}
130 
131 		/*
132 		 * Not a special case.
133 		 * Look up the option letter in the option table.
134 		 */
135 		o = findopt(c);
136 		if (o == NULL)
137 		{
138 			parg.p_string = propt(c);
139 #if MSOFTC || OS2
140 			error("There is no %s flag (\"less -?\" for help)",
141 				&parg);
142 #else
143 			error("There is no %s flag (\"less -\\?\" for help)",
144 				&parg);
145 #endif
146 			quit(QUIT_ERROR);
147 		}
148 
149 		switch (o->otype & OTYPE)
150 		{
151 		case BOOL:
152 			if (set_default)
153 				*(o->ovar) = o->odefault;
154 			else
155 				*(o->ovar) = ! o->odefault;
156 			break;
157 		case TRIPLE:
158 			if (set_default)
159 				*(o->ovar) = o->odefault;
160 			else
161 				*(o->ovar) = flip_triple(o->odefault,
162 						(o->oletter == c));
163 			break;
164 		case STRING:
165 			if (*s == '\0')
166 			{
167 				/*
168 				 * Set pendopt and return.
169 				 * We will get the string next time
170 				 * scan_option is called.
171 				 */
172 				pendopt = o;
173 				return;
174 			}
175 			/*
176 			 * Don't do anything here.
177 			 * All processing of STRING options is done by
178 			 * the handling function.
179 			 */
180 			str = s;
181 			s = optstring(s, c);
182 			break;
183 		case NUMBER:
184 			*(o->ovar) = getnum(&s, c, (int*)NULL);
185 			break;
186 		}
187 		/*
188 		 * If the option has a handling function, call it.
189 		 */
190 		if (o->ofunc != NULL)
191 			(*o->ofunc)(INIT, str);
192 	}
193 }
194 
195 /*
196  * Toggle command line flags from within the program.
197  * Used by the "-" and "_" commands.
198  * how_toggle may be:
199  *	OPT_NO_TOGGLE	just report the current setting, without changing it.
200  *	OPT_TOGGLE	invert the current setting
201  *	OPT_UNSET	set to the default value
202  *	OPT_SET		set to the inverse of the default value
203  */
204 	public void
205 toggle_option(c, s, how_toggle)
206 	int c;
207 	char *s;
208 	int how_toggle;
209 {
210 	register struct option *o;
211 	register int num;
212 	int err;
213 	PARG parg;
214 
215 	/*
216 	 * Look up the option letter in the option table.
217 	 */
218 	o = findopt(c);
219 	if (o == NULL)
220 	{
221 		parg.p_string = propt(c);
222 		error("There is no %s flag", &parg);
223 		return;
224 	}
225 
226 	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
227 	{
228 		parg.p_string = propt(c);
229 		error("Cannot change the %s flag", &parg);
230 		return;
231 	}
232 
233 	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
234 	{
235 		parg.p_string = propt(c);
236 		error("Cannot query the %s flag", &parg);
237 		return;
238 	}
239 
240 	/*
241 	 * Check for something which appears to be a do_toggle
242 	 * (because the "-" command was used), but really is not.
243 	 * This could be a string option with no string, or
244 	 * a number option with no number.
245 	 */
246 	switch (o->otype & OTYPE)
247 	{
248 	case STRING:
249 	case NUMBER:
250 		if (how_toggle == OPT_TOGGLE && *s == '\0')
251 			how_toggle = OPT_NO_TOGGLE;
252 		break;
253 	}
254 
255 #if HILITE_SEARCH
256 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
257 		repaint_hilite(0);
258 #endif
259 
260 	/*
261 	 * Now actually toggle (change) the variable.
262 	 */
263 	if (how_toggle != OPT_NO_TOGGLE)
264 	{
265 		switch (o->otype & OTYPE)
266 		{
267 		case BOOL:
268 			/*
269 			 * Boolean.
270 			 */
271 			switch (how_toggle)
272 			{
273 			case OPT_TOGGLE:
274 				*(o->ovar) = ! *(o->ovar);
275 				break;
276 			case OPT_UNSET:
277 				*(o->ovar) = o->odefault;
278 				break;
279 			case OPT_SET:
280 				*(o->ovar) = ! o->odefault;
281 				break;
282 			}
283 			break;
284 		case TRIPLE:
285 			/*
286 			 * Triple:
287 			 *	If user gave the lower case letter, then switch
288 			 *	to 1 unless already 1, in which case make it 0.
289 			 *	If user gave the upper case letter, then switch
290 			 *	to 2 unless already 2, in which case make it 0.
291 			 */
292 			switch (how_toggle)
293 			{
294 			case OPT_TOGGLE:
295 				*(o->ovar) = flip_triple(*(o->ovar),
296 						o->oletter == c);
297 				break;
298 			case OPT_UNSET:
299 				*(o->ovar) = o->odefault;
300 				break;
301 			case OPT_SET:
302 				*(o->ovar) = flip_triple(o->odefault,
303 						o->oletter == c);
304 				break;
305 			}
306 			break;
307 		case STRING:
308 			/*
309 			 * String: don't do anything here.
310 			 *	The handling function will do everything.
311 			 */
312 			switch (how_toggle)
313 			{
314 			case OPT_SET:
315 			case OPT_UNSET:
316 				error("Can't use \"-+\" or \"--\" for a string flag",
317 					NULL_PARG);
318 				return;
319 			}
320 			break;
321 		case NUMBER:
322 			/*
323 			 * Number: set the variable to the given number.
324 			 */
325 			switch (how_toggle)
326 			{
327 			case OPT_TOGGLE:
328 				num = getnum(&s, '\0', &err);
329 				if (!err)
330 					*(o->ovar) = num;
331 				break;
332 			case OPT_UNSET:
333 				*(o->ovar) = o->odefault;
334 				break;
335 			case OPT_SET:
336 				error("Can't use \"--\" for a numeric flag",
337 					NULL_PARG);
338 				return;
339 			}
340 			break;
341 		}
342 	}
343 
344 	/*
345 	 * Call the handling function for any special action
346 	 * specific to this option.
347 	 */
348 	if (o->ofunc != NULL)
349 		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
350 
351 #if HILITE_SEARCH
352 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
353 		chg_hilite();
354 #endif
355 
356 	/*
357 	 * Print a message describing the new setting.
358 	 */
359 	switch (o->otype & OTYPE)
360 	{
361 	case BOOL:
362 	case TRIPLE:
363 		/*
364 		 * Print the odesc message.
365 		 */
366 		error(o->odesc[*(o->ovar)], NULL_PARG);
367 		break;
368 	case NUMBER:
369 		/*
370 		 * The message is in odesc[1] and has a %d for
371 		 * the value of the variable.
372 		 */
373 		parg.p_int = *(o->ovar);
374 		error(o->odesc[1], &parg);
375 		break;
376 	case STRING:
377 		/*
378 		 * Message was already printed by the handling function.
379 		 */
380 		break;
381 	}
382 
383 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
384 		screen_trashed = TRUE;
385 }
386 
387 /*
388  * "Toggle" a triple-valued option.
389  */
390 	static int
391 flip_triple(val, lc)
392 	int val;
393 	int lc;
394 {
395 	if (lc)
396 		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
397 	else
398 		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
399 }
400 
401 /*
402  * Return a string suitable for printing as the "name" of an option.
403  * For example, if the option letter is 'x', just return "-x".
404  */
405 	static char *
406 propt(c)
407 	int c;
408 {
409 	static char buf[8];
410 
411 	sprintf(buf, "-%s", prchar(c));
412 	return (buf);
413 }
414 
415 /*
416  * Determine if an option is a single character option (BOOL or TRIPLE),
417  * or if it a multi-character option (NUMBER).
418  */
419 	public int
420 single_char_option(c)
421 	int c;
422 {
423 	register struct option *o;
424 
425 	o = findopt(c);
426 	if (o == NULL)
427 		return (TRUE);
428 	return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
429 }
430 
431 /*
432  * Return the prompt to be used for a given option letter.
433  * Only string and number valued options have prompts.
434  */
435 	public char *
436 opt_prompt(c)
437 	int c;
438 {
439 	register struct option *o;
440 
441 	o = findopt(c);
442 	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
443 		return (NULL);
444 	return (o->odesc[0]);
445 }
446 
447 /*
448  * Return whether or not there is a string option pending;
449  * that is, if the previous option was a string-valued option letter
450  * (like -P) without a following string.
451  * In that case, the current option is taken to be the string for
452  * the previous option.
453  */
454 	public int
455 isoptpending()
456 {
457 	return (pendopt != NULL);
458 }
459 
460 /*
461  * Print error message about missing string.
462  */
463 	static void
464 nostring(c)
465 	int c;
466 {
467 	PARG parg;
468 	parg.p_string = propt(c);
469 	error("String is required after %s", &parg);
470 }
471 
472 /*
473  * Print error message if a STRING type option is not followed by a string.
474  */
475 	public void
476 nopendopt()
477 {
478 	nostring(pendopt->oletter);
479 }
480 
481 /*
482  * Scan to end of string or to an END_OPTION_STRING character.
483  * In the latter case, replace the char with a null char.
484  * Return a pointer to the remainder of the string, if any.
485  */
486 	static char *
487 optstring(s, c)
488 	char *s;
489 	int c;
490 {
491 	register char *p;
492 
493 	if (*s == '\0')
494 	{
495 		nostring(c);
496 		quit(QUIT_ERROR);
497 	}
498 	for (p = s;  *p != '\0';  p++)
499 		if (*p == END_OPTION_STRING)
500 		{
501 			*p = '\0';
502 			return (p+1);
503 		}
504 	return (p);
505 }
506 
507 /*
508  * Translate a string into a number.
509  * Like atoi(), but takes a pointer to a char *, and updates
510  * the char * to point after the translated number.
511  */
512 	public int
513 getnum(sp, c, errp)
514 	char **sp;
515 	int c;
516 	int *errp;
517 {
518 	register char *s;
519 	register int n;
520 	register int neg;
521 	PARG parg;
522 
523 	s = skipsp(*sp);
524 	neg = FALSE;
525 	if (*s == '-')
526 	{
527 		neg = TRUE;
528 		s++;
529 	}
530 	if (*s < '0' || *s > '9')
531 	{
532 		if (errp != NULL)
533 		{
534 			*errp = TRUE;
535 			return (-1);
536 		}
537 		parg.p_string = propt(c);
538 		error("Number is required after %s", &parg);
539 		quit(QUIT_ERROR);
540 	}
541 
542 	n = 0;
543 	while (*s >= '0' && *s <= '9')
544 		n = 10 * n + *s++ - '0';
545 	*sp = s;
546 	if (errp != NULL)
547 		*errp = FALSE;
548 	if (neg)
549 		n = -n;
550 	return (n);
551 }
552