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