option.c revision 128345
160786Sps/*
2128345Stjr * Copyright (C) 1984-2002  Mark Nudelman
360786Sps *
460786Sps * You may distribute under the terms of either the GNU General Public
560786Sps * License or the Less License, as specified in the README file.
660786Sps *
760786Sps * For more information about less, or for information on how to
860786Sps * contact the author, see the README file.
960786Sps */
1060786Sps
1160786Sps
1260786Sps/*
1360786Sps * Process command line options.
1460786Sps *
1560786Sps * Each option is a single letter which controls a program variable.
1660786Sps * The options have defaults which may be changed via
1760786Sps * the command line option, toggled via the "-" command,
1860786Sps * or queried via the "_" command.
1960786Sps */
2060786Sps
2160786Sps#include "less.h"
2260786Sps#include "option.h"
2360786Sps
24128345Stjrstatic struct loption *pendopt;
2560786Spspublic int plusoption = FALSE;
2660786Sps
2760786Spsstatic char *propt();
2860786Spsstatic char *optstring();
2960786Spsstatic int flip_triple();
3060786Sps
3160786Spsextern int screen_trashed;
3260786Spsextern char *every_first_cmd;
3360786Sps
3460786Sps/*
3560786Sps * Scan an argument (either from the command line or from the
3660786Sps * LESS environment variable) and process it.
3760786Sps */
3860786Sps	public void
3960786Spsscan_option(s)
4060786Sps	char *s;
4160786Sps{
42128345Stjr	register struct loption *o;
4360786Sps	register int optc;
4460786Sps	char *optname;
4560786Sps	char *printopt;
4660786Sps	char *str;
4760786Sps	int set_default;
4860786Sps	int lc;
4960786Sps	int err;
5060786Sps	PARG parg;
5160786Sps
5260786Sps	if (s == NULL)
5360786Sps		return;
5460786Sps
5560786Sps	/*
5660786Sps	 * If we have a pending option which requires an argument,
5760786Sps	 * handle it now.
5860786Sps	 * This happens if the previous option was, for example, "-P"
5960786Sps	 * without a following string.  In that case, the current
6060786Sps	 * option is simply the argument for the previous option.
6160786Sps	 */
6260786Sps	if (pendopt != NULL)
6360786Sps	{
6460786Sps		switch (pendopt->otype & OTYPE)
6560786Sps		{
6660786Sps		case STRING:
6760786Sps			(*pendopt->ofunc)(INIT, s);
6860786Sps			break;
6960786Sps		case NUMBER:
7060786Sps			printopt = propt(pendopt->oletter);
7160786Sps			*(pendopt->ovar) = getnum(&s, printopt, (int*)NULL);
7260786Sps			break;
7360786Sps		}
7460786Sps		pendopt = NULL;
7560786Sps		return;
7660786Sps	}
7760786Sps
7860786Sps	set_default = FALSE;
7960786Sps	optname = NULL;
8060786Sps
8160786Sps	while (*s != '\0')
8260786Sps	{
8360786Sps		/*
8460786Sps		 * Check some special cases first.
8560786Sps		 */
8660786Sps		switch (optc = *s++)
8760786Sps		{
8860786Sps		case ' ':
8960786Sps		case '\t':
9060786Sps		case END_OPTION_STRING:
9160786Sps			continue;
9260786Sps		case '-':
9360786Sps			/*
9460786Sps			 * "--" indicates an option name instead of a letter.
9560786Sps			 */
9660786Sps			if (*s == '-')
9760786Sps			{
9860786Sps				optname = ++s;
9960786Sps				break;
10060786Sps			}
10160786Sps			/*
10260786Sps			 * "-+" means set these options back to their defaults.
10360786Sps			 * (They may have been set otherwise by previous
10460786Sps			 * options.)
10560786Sps			 */
10660786Sps			set_default = (*s == '+');
10760786Sps			if (set_default)
10860786Sps				s++;
10960786Sps			continue;
11060786Sps		case '+':
11160786Sps			/*
11260786Sps			 * An option prefixed by a "+" is ungotten, so
11360786Sps			 * that it is interpreted as less commands
11460786Sps			 * processed at the start of the first input file.
11560786Sps			 * "++" means process the commands at the start of
11660786Sps			 * EVERY input file.
11760786Sps			 */
11860786Sps			plusoption = TRUE;
119128345Stjr			s = optstring(s, &str, propt('+'), NULL);
12089019Sps			if (*str == '+')
12189019Sps				every_first_cmd = save(++str);
12260786Sps			else
12389019Sps				ungetsc(str);
12460786Sps			continue;
12560786Sps		case '0':  case '1':  case '2':  case '3':  case '4':
12660786Sps		case '5':  case '6':  case '7':  case '8':  case '9':
12760786Sps			/*
12860786Sps			 * Special "more" compatibility form "-<number>"
12960786Sps			 * instead of -z<number> to set the scrolling
13060786Sps			 * window size.
13160786Sps			 */
13260786Sps			s--;
13360786Sps			optc = 'z';
13460786Sps			break;
13560786Sps		}
13660786Sps
13760786Sps		/*
13860786Sps		 * Not a special case.
13960786Sps		 * Look up the option letter in the option table.
14060786Sps		 */
14160786Sps		err = 0;
14260786Sps		if (optname == NULL)
14360786Sps		{
14460786Sps			printopt = propt(optc);
14560786Sps			lc = SIMPLE_IS_LOWER(optc);
14660786Sps			o = findopt(optc);
14760786Sps		} else
14860786Sps		{
14960786Sps			printopt = optname;
15060786Sps			lc = SIMPLE_IS_LOWER(optname[0]);
15160786Sps			o = findopt_name(&optname, NULL, &err);
15260786Sps			s = optname;
15360786Sps			optname = NULL;
15460786Sps			if (*s == '\0' || *s == ' ')
15560786Sps			{
15660786Sps				/*
15760786Sps				 * The option name matches exactly.
15860786Sps				 */
15960786Sps				;
16060786Sps			} else if (*s == '=')
16160786Sps			{
16260786Sps				/*
16360786Sps				 * The option name is followed by "=value".
16460786Sps				 */
16560786Sps				if (o != NULL &&
16660786Sps				    (o->otype & OTYPE) != STRING &&
16760786Sps				    (o->otype & OTYPE) != NUMBER)
16860786Sps				{
16960786Sps					parg.p_string = printopt;
17060786Sps					error("The %s option should not be followed by =",
17160786Sps						&parg);
17260786Sps					quit(QUIT_ERROR);
17360786Sps				}
17460786Sps				s++;
17560786Sps			} else
17660786Sps			{
17760786Sps				/*
17860786Sps				 * The specified name is longer than the
17960786Sps				 * real option name.
18060786Sps				 */
18160786Sps				o = NULL;
18260786Sps			}
18360786Sps		}
18460786Sps		if (o == NULL)
18560786Sps		{
18660786Sps			parg.p_string = printopt;
18760786Sps			if (err == OPT_AMBIG)
18860786Sps				error("%s is an ambiguous abbreviation (\"less --help\" for help)",
18960786Sps					&parg);
19060786Sps			else
19160786Sps				error("There is no %s option (\"less --help\" for help)",
19260786Sps					&parg);
19360786Sps			quit(QUIT_ERROR);
19460786Sps		}
19560786Sps
19660786Sps		str = NULL;
19760786Sps		switch (o->otype & OTYPE)
19860786Sps		{
19960786Sps		case BOOL:
20060786Sps			if (set_default)
20160786Sps				*(o->ovar) = o->odefault;
20260786Sps			else
20360786Sps				*(o->ovar) = ! o->odefault;
20460786Sps			break;
20560786Sps		case TRIPLE:
20660786Sps			if (set_default)
20760786Sps				*(o->ovar) = o->odefault;
20860786Sps			else
20960786Sps				*(o->ovar) = flip_triple(o->odefault, lc);
21060786Sps			break;
21160786Sps		case STRING:
21260786Sps			if (*s == '\0')
21360786Sps			{
21460786Sps				/*
21560786Sps				 * Set pendopt and return.
21660786Sps				 * We will get the string next time
21760786Sps				 * scan_option is called.
21860786Sps				 */
21960786Sps				pendopt = o;
22060786Sps				return;
22160786Sps			}
22260786Sps			/*
22360786Sps			 * Don't do anything here.
22460786Sps			 * All processing of STRING options is done by
22560786Sps			 * the handling function.
22660786Sps			 */
227128345Stjr			while (*s == ' ')
228128345Stjr				s++;
229128345Stjr			s = optstring(s, &str, printopt, o->odesc[1]);
23060786Sps			break;
23160786Sps		case NUMBER:
23260786Sps			if (*s == '\0')
23360786Sps			{
23460786Sps				pendopt = o;
23560786Sps				return;
23660786Sps			}
23760786Sps			*(o->ovar) = getnum(&s, printopt, (int*)NULL);
23860786Sps			break;
23960786Sps		}
24060786Sps		/*
24160786Sps		 * If the option has a handling function, call it.
24260786Sps		 */
24360786Sps		if (o->ofunc != NULL)
24460786Sps			(*o->ofunc)(INIT, str);
24560786Sps	}
24660786Sps}
24760786Sps
24860786Sps/*
24960786Sps * Toggle command line flags from within the program.
25060786Sps * Used by the "-" and "_" commands.
25160786Sps * how_toggle may be:
25260786Sps *	OPT_NO_TOGGLE	just report the current setting, without changing it.
25360786Sps *	OPT_TOGGLE	invert the current setting
25460786Sps *	OPT_UNSET	set to the default value
25560786Sps *	OPT_SET		set to the inverse of the default value
25660786Sps */
25760786Sps	public void
25860786Spstoggle_option(c, s, how_toggle)
25960786Sps	int c;
26060786Sps	char *s;
26160786Sps	int how_toggle;
26260786Sps{
263128345Stjr	register struct loption *o;
26460786Sps	register int num;
26560786Sps	int no_prompt;
26660786Sps	int err;
26760786Sps	PARG parg;
26860786Sps
26960786Sps	no_prompt = (how_toggle & OPT_NO_PROMPT);
27060786Sps	how_toggle &= ~OPT_NO_PROMPT;
27160786Sps
27260786Sps	/*
27360786Sps	 * Look up the option letter in the option table.
27460786Sps	 */
27560786Sps	o = findopt(c);
27660786Sps	if (o == NULL)
27760786Sps	{
27860786Sps		parg.p_string = propt(c);
27960786Sps		error("There is no %s option", &parg);
28060786Sps		return;
28160786Sps	}
28260786Sps
28360786Sps	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
28460786Sps	{
28560786Sps		parg.p_string = propt(c);
28660786Sps		error("Cannot change the %s option", &parg);
28760786Sps		return;
28860786Sps	}
28960786Sps
29060786Sps	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
29160786Sps	{
29260786Sps		parg.p_string = propt(c);
29360786Sps		error("Cannot query the %s option", &parg);
29460786Sps		return;
29560786Sps	}
29660786Sps
29760786Sps	/*
29860786Sps	 * Check for something which appears to be a do_toggle
29960786Sps	 * (because the "-" command was used), but really is not.
30060786Sps	 * This could be a string option with no string, or
30160786Sps	 * a number option with no number.
30260786Sps	 */
30360786Sps	switch (o->otype & OTYPE)
30460786Sps	{
30560786Sps	case STRING:
30660786Sps	case NUMBER:
30760786Sps		if (how_toggle == OPT_TOGGLE && *s == '\0')
30860786Sps			how_toggle = OPT_NO_TOGGLE;
30960786Sps		break;
31060786Sps	}
31160786Sps
31260786Sps#if HILITE_SEARCH
31360786Sps	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
31460786Sps		repaint_hilite(0);
31560786Sps#endif
31660786Sps
31760786Sps	/*
31860786Sps	 * Now actually toggle (change) the variable.
31960786Sps	 */
32060786Sps	if (how_toggle != OPT_NO_TOGGLE)
32160786Sps	{
32260786Sps		switch (o->otype & OTYPE)
32360786Sps		{
32460786Sps		case BOOL:
32560786Sps			/*
32660786Sps			 * Boolean.
32760786Sps			 */
32860786Sps			switch (how_toggle)
32960786Sps			{
33060786Sps			case OPT_TOGGLE:
33160786Sps				*(o->ovar) = ! *(o->ovar);
33260786Sps				break;
33360786Sps			case OPT_UNSET:
33460786Sps				*(o->ovar) = o->odefault;
33560786Sps				break;
33660786Sps			case OPT_SET:
33760786Sps				*(o->ovar) = ! o->odefault;
33860786Sps				break;
33960786Sps			}
34060786Sps			break;
34160786Sps		case TRIPLE:
34260786Sps			/*
34360786Sps			 * Triple:
34460786Sps			 *	If user gave the lower case letter, then switch
34560786Sps			 *	to 1 unless already 1, in which case make it 0.
34660786Sps			 *	If user gave the upper case letter, then switch
34760786Sps			 *	to 2 unless already 2, in which case make it 0.
34860786Sps			 */
34960786Sps			switch (how_toggle)
35060786Sps			{
35160786Sps			case OPT_TOGGLE:
35260786Sps				*(o->ovar) = flip_triple(*(o->ovar),
35360786Sps						islower(c));
35460786Sps				break;
35560786Sps			case OPT_UNSET:
35660786Sps				*(o->ovar) = o->odefault;
35760786Sps				break;
35860786Sps			case OPT_SET:
35960786Sps				*(o->ovar) = flip_triple(o->odefault,
36060786Sps						islower(c));
36160786Sps				break;
36260786Sps			}
36360786Sps			break;
36460786Sps		case STRING:
36560786Sps			/*
36660786Sps			 * String: don't do anything here.
36760786Sps			 *	The handling function will do everything.
36860786Sps			 */
36960786Sps			switch (how_toggle)
37060786Sps			{
37160786Sps			case OPT_SET:
37260786Sps			case OPT_UNSET:
37360786Sps				error("Cannot use \"-+\" or \"--\" for a string option",
37460786Sps					NULL_PARG);
37560786Sps				return;
37660786Sps			}
37760786Sps			break;
37860786Sps		case NUMBER:
37960786Sps			/*
38060786Sps			 * Number: set the variable to the given number.
38160786Sps			 */
38260786Sps			switch (how_toggle)
38360786Sps			{
38460786Sps			case OPT_TOGGLE:
385128345Stjr				num = getnum(&s, NULL, &err);
38660786Sps				if (!err)
38760786Sps					*(o->ovar) = num;
38860786Sps				break;
38960786Sps			case OPT_UNSET:
39060786Sps				*(o->ovar) = o->odefault;
39160786Sps				break;
39260786Sps			case OPT_SET:
39360786Sps				error("Can't use \"-!\" for a numeric option",
39460786Sps					NULL_PARG);
39560786Sps				return;
39660786Sps			}
39760786Sps			break;
39860786Sps		}
39960786Sps	}
40060786Sps
40160786Sps	/*
40260786Sps	 * Call the handling function for any special action
40360786Sps	 * specific to this option.
40460786Sps	 */
40560786Sps	if (o->ofunc != NULL)
40660786Sps		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
40760786Sps
40860786Sps#if HILITE_SEARCH
40960786Sps	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
41060786Sps		chg_hilite();
41160786Sps#endif
41260786Sps
41360786Sps	if (!no_prompt)
41460786Sps	{
41560786Sps		/*
41660786Sps		 * Print a message describing the new setting.
41760786Sps		 */
41860786Sps		switch (o->otype & OTYPE)
41960786Sps		{
42060786Sps		case BOOL:
42160786Sps		case TRIPLE:
42260786Sps			/*
42360786Sps			 * Print the odesc message.
42460786Sps			 */
42560786Sps			error(o->odesc[*(o->ovar)], NULL_PARG);
42660786Sps			break;
42760786Sps		case NUMBER:
42860786Sps			/*
42960786Sps			 * The message is in odesc[1] and has a %d for
43060786Sps			 * the value of the variable.
43160786Sps			 */
43260786Sps			parg.p_int = *(o->ovar);
43360786Sps			error(o->odesc[1], &parg);
43460786Sps			break;
43560786Sps		case STRING:
43660786Sps			/*
43760786Sps			 * Message was already printed by the handling function.
43860786Sps			 */
43960786Sps			break;
44060786Sps		}
44160786Sps	}
44260786Sps
44360786Sps	if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
44460786Sps		screen_trashed = TRUE;
44560786Sps}
44660786Sps
44760786Sps/*
44860786Sps * "Toggle" a triple-valued option.
44960786Sps */
45060786Sps	static int
45160786Spsflip_triple(val, lc)
45260786Sps	int val;
45360786Sps	int lc;
45460786Sps{
45560786Sps	if (lc)
45660786Sps		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
45760786Sps	else
45860786Sps		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
45960786Sps}
46060786Sps
46160786Sps/*
46260786Sps * Return a string suitable for printing as the "name" of an option.
46360786Sps * For example, if the option letter is 'x', just return "-x".
46460786Sps */
46560786Sps	static char *
46660786Spspropt(c)
46760786Sps	int c;
46860786Sps{
46960786Sps	static char buf[8];
47060786Sps
47160786Sps	sprintf(buf, "-%s", prchar(c));
47260786Sps	return (buf);
47360786Sps}
47460786Sps
47560786Sps/*
47660786Sps * Determine if an option is a single character option (BOOL or TRIPLE),
47760786Sps * or if it a multi-character option (NUMBER).
47860786Sps */
47960786Sps	public int
48060786Spssingle_char_option(c)
48160786Sps	int c;
48260786Sps{
483128345Stjr	register struct loption *o;
48460786Sps
48560786Sps	o = findopt(c);
48660786Sps	if (o == NULL)
48760786Sps		return (TRUE);
48860786Sps	return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
48960786Sps}
49060786Sps
49160786Sps/*
49260786Sps * Return the prompt to be used for a given option letter.
49360786Sps * Only string and number valued options have prompts.
49460786Sps */
49560786Sps	public char *
49660786Spsopt_prompt(c)
49760786Sps	int c;
49860786Sps{
499128345Stjr	register struct loption *o;
50060786Sps
50160786Sps	o = findopt(c);
50260786Sps	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
50360786Sps		return (NULL);
50460786Sps	return (o->odesc[0]);
50560786Sps}
50660786Sps
50760786Sps/*
50860786Sps * Return whether or not there is a string option pending;
50960786Sps * that is, if the previous option was a string-valued option letter
51060786Sps * (like -P) without a following string.
51160786Sps * In that case, the current option is taken to be the string for
51260786Sps * the previous option.
51360786Sps */
51460786Sps	public int
51560786Spsisoptpending()
51660786Sps{
51760786Sps	return (pendopt != NULL);
51860786Sps}
51960786Sps
52060786Sps/*
52160786Sps * Print error message about missing string.
52260786Sps */
52360786Sps	static void
52460786Spsnostring(printopt)
52560786Sps	char *printopt;
52660786Sps{
52760786Sps	PARG parg;
52860786Sps	parg.p_string = printopt;
52960786Sps	error("Value is required after %s", &parg);
53060786Sps}
53160786Sps
53260786Sps/*
53360786Sps * Print error message if a STRING type option is not followed by a string.
53460786Sps */
53560786Sps	public void
53660786Spsnopendopt()
53760786Sps{
53860786Sps	nostring(propt(pendopt->oletter));
53960786Sps}
54060786Sps
54160786Sps/*
54260786Sps * Scan to end of string or to an END_OPTION_STRING character.
54360786Sps * In the latter case, replace the char with a null char.
54460786Sps * Return a pointer to the remainder of the string, if any.
54560786Sps */
54660786Sps	static char *
547128345Stjroptstring(s, p_str, printopt, validchars)
54860786Sps	char *s;
549128345Stjr	char **p_str;
55060786Sps	char *printopt;
55189019Sps	char *validchars;
55260786Sps{
55360786Sps	register char *p;
55460786Sps
55560786Sps	if (*s == '\0')
55660786Sps	{
55760786Sps		nostring(printopt);
55860786Sps		quit(QUIT_ERROR);
55960786Sps	}
560128345Stjr	*p_str = s;
56160786Sps	for (p = s;  *p != '\0';  p++)
562128345Stjr	{
56389019Sps		if (*p == END_OPTION_STRING ||
56489019Sps		    (validchars != NULL && strchr(validchars, *p) == NULL))
56560786Sps		{
56689019Sps			switch (*p)
56789019Sps			{
56889019Sps			case END_OPTION_STRING:
56989019Sps			case ' ':  case '\t':  case '-':
570128345Stjr				/* Replace the char with a null to terminate string. */
571128345Stjr				*p++ = '\0';
57289019Sps				break;
57389019Sps			default:
574128345Stjr				/* Cannot replace char; make a copy of the string. */
575128345Stjr				*p_str = (char *) ecalloc(p-s+1, sizeof(char));
576128345Stjr				strncpy(*p_str, s, p-s);
577128345Stjr				(*p_str)[p-s] = '\0';
57889019Sps				break;
57989019Sps			}
580128345Stjr			break;
58160786Sps		}
582128345Stjr	}
58360786Sps	return (p);
58460786Sps}
58560786Sps
58660786Sps/*
58760786Sps * Translate a string into a number.
58860786Sps * Like atoi(), but takes a pointer to a char *, and updates
58960786Sps * the char * to point after the translated number.
59060786Sps */
59160786Sps	public int
59260786Spsgetnum(sp, printopt, errp)
59360786Sps	char **sp;
59460786Sps	char *printopt;
59560786Sps	int *errp;
59660786Sps{
59760786Sps	register char *s;
59860786Sps	register int n;
59960786Sps	register int neg;
60060786Sps	PARG parg;
60160786Sps
60260786Sps	s = skipsp(*sp);
60360786Sps	neg = FALSE;
60460786Sps	if (*s == '-')
60560786Sps	{
60660786Sps		neg = TRUE;
60760786Sps		s++;
60860786Sps	}
60960786Sps	if (*s < '0' || *s > '9')
61060786Sps	{
61160786Sps		if (errp != NULL)
61260786Sps		{
61360786Sps			*errp = TRUE;
61460786Sps			return (-1);
61560786Sps		}
616128345Stjr		if (printopt != NULL)
617128345Stjr		{
618128345Stjr			parg.p_string = printopt;
619128345Stjr			error("Number is required after %s", &parg);
620128345Stjr		}
62160786Sps		quit(QUIT_ERROR);
62260786Sps	}
62360786Sps
62460786Sps	n = 0;
62560786Sps	while (*s >= '0' && *s <= '9')
62660786Sps		n = 10 * n + *s++ - '0';
62760786Sps	*sp = s;
62860786Sps	if (errp != NULL)
62960786Sps		*errp = FALSE;
63060786Sps	if (neg)
63160786Sps		n = -n;
63260786Sps	return (n);
63360786Sps}
634