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