xref: /freebsd/contrib/less/option.c (revision ce4946daa5ce852d28008dac492029500ab2ee95)
1 /*
2  * Copyright (C) 1984-2000  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 option *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 option *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 			if (*s == '+')
120 				every_first_cmd = save(++s);
121 			else
122 				ungetsc(s);
123 			s = optstring(s, propt('+'));
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 			str = s;
228 			s = optstring(s, printopt);
229 			break;
230 		case NUMBER:
231 			if (*s == '\0')
232 			{
233 				pendopt = o;
234 				return;
235 			}
236 			*(o->ovar) = getnum(&s, printopt, (int*)NULL);
237 			break;
238 		}
239 		/*
240 		 * If the option has a handling function, call it.
241 		 */
242 		if (o->ofunc != NULL)
243 			(*o->ofunc)(INIT, str);
244 	}
245 }
246 
247 /*
248  * Toggle command line flags from within the program.
249  * Used by the "-" and "_" commands.
250  * how_toggle may be:
251  *	OPT_NO_TOGGLE	just report the current setting, without changing it.
252  *	OPT_TOGGLE	invert the current setting
253  *	OPT_UNSET	set to the default value
254  *	OPT_SET		set to the inverse of the default value
255  */
256 	public void
257 toggle_option(c, s, how_toggle)
258 	int c;
259 	char *s;
260 	int how_toggle;
261 {
262 	register struct option *o;
263 	register int num;
264 	int no_prompt;
265 	int err;
266 	PARG parg;
267 
268 	no_prompt = (how_toggle & OPT_NO_PROMPT);
269 	how_toggle &= ~OPT_NO_PROMPT;
270 
271 	/*
272 	 * Look up the option letter in the option table.
273 	 */
274 	o = findopt(c);
275 	if (o == NULL)
276 	{
277 		parg.p_string = propt(c);
278 		error("There is no %s option", &parg);
279 		return;
280 	}
281 
282 	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
283 	{
284 		parg.p_string = propt(c);
285 		error("Cannot change the %s option", &parg);
286 		return;
287 	}
288 
289 	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
290 	{
291 		parg.p_string = propt(c);
292 		error("Cannot query the %s option", &parg);
293 		return;
294 	}
295 
296 	/*
297 	 * Check for something which appears to be a do_toggle
298 	 * (because the "-" command was used), but really is not.
299 	 * This could be a string option with no string, or
300 	 * a number option with no number.
301 	 */
302 	switch (o->otype & OTYPE)
303 	{
304 	case STRING:
305 	case NUMBER:
306 		if (how_toggle == OPT_TOGGLE && *s == '\0')
307 			how_toggle = OPT_NO_TOGGLE;
308 		break;
309 	}
310 
311 #if HILITE_SEARCH
312 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
313 		repaint_hilite(0);
314 #endif
315 
316 	/*
317 	 * Now actually toggle (change) the variable.
318 	 */
319 	if (how_toggle != OPT_NO_TOGGLE)
320 	{
321 		switch (o->otype & OTYPE)
322 		{
323 		case BOOL:
324 			/*
325 			 * Boolean.
326 			 */
327 			switch (how_toggle)
328 			{
329 			case OPT_TOGGLE:
330 				*(o->ovar) = ! *(o->ovar);
331 				break;
332 			case OPT_UNSET:
333 				*(o->ovar) = o->odefault;
334 				break;
335 			case OPT_SET:
336 				*(o->ovar) = ! o->odefault;
337 				break;
338 			}
339 			break;
340 		case TRIPLE:
341 			/*
342 			 * Triple:
343 			 *	If user gave the lower case letter, then switch
344 			 *	to 1 unless already 1, in which case make it 0.
345 			 *	If user gave the upper case letter, then switch
346 			 *	to 2 unless already 2, in which case make it 0.
347 			 */
348 			switch (how_toggle)
349 			{
350 			case OPT_TOGGLE:
351 				*(o->ovar) = flip_triple(*(o->ovar),
352 						islower(c));
353 				break;
354 			case OPT_UNSET:
355 				*(o->ovar) = o->odefault;
356 				break;
357 			case OPT_SET:
358 				*(o->ovar) = flip_triple(o->odefault,
359 						islower(c));
360 				break;
361 			}
362 			break;
363 		case STRING:
364 			/*
365 			 * String: don't do anything here.
366 			 *	The handling function will do everything.
367 			 */
368 			switch (how_toggle)
369 			{
370 			case OPT_SET:
371 			case OPT_UNSET:
372 				error("Cannot use \"-+\" or \"--\" for a string option",
373 					NULL_PARG);
374 				return;
375 			}
376 			break;
377 		case NUMBER:
378 			/*
379 			 * Number: set the variable to the given number.
380 			 */
381 			switch (how_toggle)
382 			{
383 			case OPT_TOGGLE:
384 				num = getnum(&s, '\0', &err);
385 				if (!err)
386 					*(o->ovar) = num;
387 				break;
388 			case OPT_UNSET:
389 				*(o->ovar) = o->odefault;
390 				break;
391 			case OPT_SET:
392 				error("Can't use \"-!\" for a numeric option",
393 					NULL_PARG);
394 				return;
395 			}
396 			break;
397 		}
398 	}
399 
400 	/*
401 	 * Call the handling function for any special action
402 	 * specific to this option.
403 	 */
404 	if (o->ofunc != NULL)
405 		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
406 
407 #if HILITE_SEARCH
408 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
409 		chg_hilite();
410 #endif
411 
412 	if (!no_prompt)
413 	{
414 		/*
415 		 * Print a message describing the new setting.
416 		 */
417 		switch (o->otype & OTYPE)
418 		{
419 		case BOOL:
420 		case TRIPLE:
421 			/*
422 			 * Print the odesc message.
423 			 */
424 			error(o->odesc[*(o->ovar)], NULL_PARG);
425 			break;
426 		case NUMBER:
427 			/*
428 			 * The message is in odesc[1] and has a %d for
429 			 * the value of the variable.
430 			 */
431 			parg.p_int = *(o->ovar);
432 			error(o->odesc[1], &parg);
433 			break;
434 		case STRING:
435 			/*
436 			 * Message was already printed by the handling function.
437 			 */
438 			break;
439 		}
440 	}
441 
442 	if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
443 		screen_trashed = TRUE;
444 }
445 
446 /*
447  * "Toggle" a triple-valued option.
448  */
449 	static int
450 flip_triple(val, lc)
451 	int val;
452 	int lc;
453 {
454 	if (lc)
455 		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
456 	else
457 		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
458 }
459 
460 /*
461  * Return a string suitable for printing as the "name" of an option.
462  * For example, if the option letter is 'x', just return "-x".
463  */
464 	static char *
465 propt(c)
466 	int c;
467 {
468 	static char buf[8];
469 
470 	sprintf(buf, "-%s", prchar(c));
471 	return (buf);
472 }
473 
474 /*
475  * Determine if an option is a single character option (BOOL or TRIPLE),
476  * or if it a multi-character option (NUMBER).
477  */
478 	public int
479 single_char_option(c)
480 	int c;
481 {
482 	register struct option *o;
483 
484 	o = findopt(c);
485 	if (o == NULL)
486 		return (TRUE);
487 	return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
488 }
489 
490 /*
491  * Return the prompt to be used for a given option letter.
492  * Only string and number valued options have prompts.
493  */
494 	public char *
495 opt_prompt(c)
496 	int c;
497 {
498 	register struct option *o;
499 
500 	o = findopt(c);
501 	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
502 		return (NULL);
503 	return (o->odesc[0]);
504 }
505 
506 /*
507  * Return whether or not there is a string option pending;
508  * that is, if the previous option was a string-valued option letter
509  * (like -P) without a following string.
510  * In that case, the current option is taken to be the string for
511  * the previous option.
512  */
513 	public int
514 isoptpending()
515 {
516 	return (pendopt != NULL);
517 }
518 
519 /*
520  * Print error message about missing string.
521  */
522 	static void
523 nostring(printopt)
524 	char *printopt;
525 {
526 	PARG parg;
527 	parg.p_string = printopt;
528 	error("Value is required after %s", &parg);
529 }
530 
531 /*
532  * Print error message if a STRING type option is not followed by a string.
533  */
534 	public void
535 nopendopt()
536 {
537 	nostring(propt(pendopt->oletter));
538 }
539 
540 /*
541  * Scan to end of string or to an END_OPTION_STRING character.
542  * In the latter case, replace the char with a null char.
543  * Return a pointer to the remainder of the string, if any.
544  */
545 	static char *
546 optstring(s, printopt)
547 	char *s;
548 	char *printopt;
549 {
550 	register char *p;
551 
552 	if (*s == '\0')
553 	{
554 		nostring(printopt);
555 		quit(QUIT_ERROR);
556 	}
557 	for (p = s;  *p != '\0';  p++)
558 		if (*p == END_OPTION_STRING)
559 		{
560 			*p = '\0';
561 			return (p+1);
562 		}
563 	return (p);
564 }
565 
566 /*
567  * Translate a string into a number.
568  * Like atoi(), but takes a pointer to a char *, and updates
569  * the char * to point after the translated number.
570  */
571 	public int
572 getnum(sp, printopt, errp)
573 	char **sp;
574 	char *printopt;
575 	int *errp;
576 {
577 	register char *s;
578 	register int n;
579 	register int neg;
580 	PARG parg;
581 
582 	s = skipsp(*sp);
583 	neg = FALSE;
584 	if (*s == '-')
585 	{
586 		neg = TRUE;
587 		s++;
588 	}
589 	if (*s < '0' || *s > '9')
590 	{
591 		if (errp != NULL)
592 		{
593 			*errp = TRUE;
594 			return (-1);
595 		}
596 		parg.p_string = printopt;
597 		error("Number is required after %s", &parg);
598 		quit(QUIT_ERROR);
599 	}
600 
601 	n = 0;
602 	while (*s >= '0' && *s <= '9')
603 		n = 10 * n + *s++ - '0';
604 	*sp = s;
605 	if (errp != NULL)
606 		*errp = FALSE;
607 	if (neg)
608 		n = -n;
609 	return (n);
610 }
611