option.c revision 191930
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
24static struct loption *pendopt;
25public int plusoption = FALSE;
26
27static char *propt();
28static char *optstring();
29static int flip_triple();
30
31extern int screen_trashed;
32extern int less_is_more;
33extern int quit_at_eof;
34extern 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
41scan_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
264toggle_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
457flip_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 *
472propt(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
486single_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 *
502opt_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
521isoptpending()
522{
523	return (pendopt != NULL);
524}
525
526/*
527 * Print error message about missing string.
528 */
529	static void
530nostring(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
542nopendopt()
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 *
553optstring(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
595num_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
622getnum(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
659getfraction(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
694get_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