option.c revision 60786
160786Sps/*
260786Sps * Copyright (C) 1984-2000  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
2460786Spsstatic struct option *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{
4260786Sps	register struct option *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;
11960786Sps			if (*s == '+')
12060786Sps				every_first_cmd = save(++s);
12160786Sps			else
12260786Sps				ungetsc(s);
12360786Sps			s = optstring(s, propt('+'));
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			 */
22760786Sps			str = s;
22860786Sps			s = optstring(s, printopt);
22960786Sps			break;
23060786Sps		case NUMBER:
23160786Sps			if (*s == '\0')
23260786Sps			{
23360786Sps				pendopt = o;
23460786Sps				return;
23560786Sps			}
23660786Sps			*(o->ovar) = getnum(&s, printopt, (int*)NULL);
23760786Sps			break;
23860786Sps		}
23960786Sps		/*
24060786Sps		 * If the option has a handling function, call it.
24160786Sps		 */
24260786Sps		if (o->ofunc != NULL)
24360786Sps			(*o->ofunc)(INIT, str);
24460786Sps	}
24560786Sps}
24660786Sps
24760786Sps/*
24860786Sps * Toggle command line flags from within the program.
24960786Sps * Used by the "-" and "_" commands.
25060786Sps * how_toggle may be:
25160786Sps *	OPT_NO_TOGGLE	just report the current setting, without changing it.
25260786Sps *	OPT_TOGGLE	invert the current setting
25360786Sps *	OPT_UNSET	set to the default value
25460786Sps *	OPT_SET		set to the inverse of the default value
25560786Sps */
25660786Sps	public void
25760786Spstoggle_option(c, s, how_toggle)
25860786Sps	int c;
25960786Sps	char *s;
26060786Sps	int how_toggle;
26160786Sps{
26260786Sps	register struct option *o;
26360786Sps	register int num;
26460786Sps	int no_prompt;
26560786Sps	int err;
26660786Sps	PARG parg;
26760786Sps
26860786Sps	no_prompt = (how_toggle & OPT_NO_PROMPT);
26960786Sps	how_toggle &= ~OPT_NO_PROMPT;
27060786Sps
27160786Sps	/*
27260786Sps	 * Look up the option letter in the option table.
27360786Sps	 */
27460786Sps	o = findopt(c);
27560786Sps	if (o == NULL)
27660786Sps	{
27760786Sps		parg.p_string = propt(c);
27860786Sps		error("There is no %s option", &parg);
27960786Sps		return;
28060786Sps	}
28160786Sps
28260786Sps	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
28360786Sps	{
28460786Sps		parg.p_string = propt(c);
28560786Sps		error("Cannot change the %s option", &parg);
28660786Sps		return;
28760786Sps	}
28860786Sps
28960786Sps	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
29060786Sps	{
29160786Sps		parg.p_string = propt(c);
29260786Sps		error("Cannot query the %s option", &parg);
29360786Sps		return;
29460786Sps	}
29560786Sps
29660786Sps	/*
29760786Sps	 * Check for something which appears to be a do_toggle
29860786Sps	 * (because the "-" command was used), but really is not.
29960786Sps	 * This could be a string option with no string, or
30060786Sps	 * a number option with no number.
30160786Sps	 */
30260786Sps	switch (o->otype & OTYPE)
30360786Sps	{
30460786Sps	case STRING:
30560786Sps	case NUMBER:
30660786Sps		if (how_toggle == OPT_TOGGLE && *s == '\0')
30760786Sps			how_toggle = OPT_NO_TOGGLE;
30860786Sps		break;
30960786Sps	}
31060786Sps
31160786Sps#if HILITE_SEARCH
31260786Sps	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
31360786Sps		repaint_hilite(0);
31460786Sps#endif
31560786Sps
31660786Sps	/*
31760786Sps	 * Now actually toggle (change) the variable.
31860786Sps	 */
31960786Sps	if (how_toggle != OPT_NO_TOGGLE)
32060786Sps	{
32160786Sps		switch (o->otype & OTYPE)
32260786Sps		{
32360786Sps		case BOOL:
32460786Sps			/*
32560786Sps			 * Boolean.
32660786Sps			 */
32760786Sps			switch (how_toggle)
32860786Sps			{
32960786Sps			case OPT_TOGGLE:
33060786Sps				*(o->ovar) = ! *(o->ovar);
33160786Sps				break;
33260786Sps			case OPT_UNSET:
33360786Sps				*(o->ovar) = o->odefault;
33460786Sps				break;
33560786Sps			case OPT_SET:
33660786Sps				*(o->ovar) = ! o->odefault;
33760786Sps				break;
33860786Sps			}
33960786Sps			break;
34060786Sps		case TRIPLE:
34160786Sps			/*
34260786Sps			 * Triple:
34360786Sps			 *	If user gave the lower case letter, then switch
34460786Sps			 *	to 1 unless already 1, in which case make it 0.
34560786Sps			 *	If user gave the upper case letter, then switch
34660786Sps			 *	to 2 unless already 2, in which case make it 0.
34760786Sps			 */
34860786Sps			switch (how_toggle)
34960786Sps			{
35060786Sps			case OPT_TOGGLE:
35160786Sps				*(o->ovar) = flip_triple(*(o->ovar),
35260786Sps						islower(c));
35360786Sps				break;
35460786Sps			case OPT_UNSET:
35560786Sps				*(o->ovar) = o->odefault;
35660786Sps				break;
35760786Sps			case OPT_SET:
35860786Sps				*(o->ovar) = flip_triple(o->odefault,
35960786Sps						islower(c));
36060786Sps				break;
36160786Sps			}
36260786Sps			break;
36360786Sps		case STRING:
36460786Sps			/*
36560786Sps			 * String: don't do anything here.
36660786Sps			 *	The handling function will do everything.
36760786Sps			 */
36860786Sps			switch (how_toggle)
36960786Sps			{
37060786Sps			case OPT_SET:
37160786Sps			case OPT_UNSET:
37260786Sps				error("Cannot use \"-+\" or \"--\" for a string option",
37360786Sps					NULL_PARG);
37460786Sps				return;
37560786Sps			}
37660786Sps			break;
37760786Sps		case NUMBER:
37860786Sps			/*
37960786Sps			 * Number: set the variable to the given number.
38060786Sps			 */
38160786Sps			switch (how_toggle)
38260786Sps			{
38360786Sps			case OPT_TOGGLE:
38460786Sps				num = getnum(&s, '\0', &err);
38560786Sps				if (!err)
38660786Sps					*(o->ovar) = num;
38760786Sps				break;
38860786Sps			case OPT_UNSET:
38960786Sps				*(o->ovar) = o->odefault;
39060786Sps				break;
39160786Sps			case OPT_SET:
39260786Sps				error("Can't use \"-!\" for a numeric option",
39360786Sps					NULL_PARG);
39460786Sps				return;
39560786Sps			}
39660786Sps			break;
39760786Sps		}
39860786Sps	}
39960786Sps
40060786Sps	/*
40160786Sps	 * Call the handling function for any special action
40260786Sps	 * specific to this option.
40360786Sps	 */
40460786Sps	if (o->ofunc != NULL)
40560786Sps		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
40660786Sps
40760786Sps#if HILITE_SEARCH
40860786Sps	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
40960786Sps		chg_hilite();
41060786Sps#endif
41160786Sps
41260786Sps	if (!no_prompt)
41360786Sps	{
41460786Sps		/*
41560786Sps		 * Print a message describing the new setting.
41660786Sps		 */
41760786Sps		switch (o->otype & OTYPE)
41860786Sps		{
41960786Sps		case BOOL:
42060786Sps		case TRIPLE:
42160786Sps			/*
42260786Sps			 * Print the odesc message.
42360786Sps			 */
42460786Sps			error(o->odesc[*(o->ovar)], NULL_PARG);
42560786Sps			break;
42660786Sps		case NUMBER:
42760786Sps			/*
42860786Sps			 * The message is in odesc[1] and has a %d for
42960786Sps			 * the value of the variable.
43060786Sps			 */
43160786Sps			parg.p_int = *(o->ovar);
43260786Sps			error(o->odesc[1], &parg);
43360786Sps			break;
43460786Sps		case STRING:
43560786Sps			/*
43660786Sps			 * Message was already printed by the handling function.
43760786Sps			 */
43860786Sps			break;
43960786Sps		}
44060786Sps	}
44160786Sps
44260786Sps	if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
44360786Sps		screen_trashed = TRUE;
44460786Sps}
44560786Sps
44660786Sps/*
44760786Sps * "Toggle" a triple-valued option.
44860786Sps */
44960786Sps	static int
45060786Spsflip_triple(val, lc)
45160786Sps	int val;
45260786Sps	int lc;
45360786Sps{
45460786Sps	if (lc)
45560786Sps		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
45660786Sps	else
45760786Sps		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
45860786Sps}
45960786Sps
46060786Sps/*
46160786Sps * Return a string suitable for printing as the "name" of an option.
46260786Sps * For example, if the option letter is 'x', just return "-x".
46360786Sps */
46460786Sps	static char *
46560786Spspropt(c)
46660786Sps	int c;
46760786Sps{
46860786Sps	static char buf[8];
46960786Sps
47060786Sps	sprintf(buf, "-%s", prchar(c));
47160786Sps	return (buf);
47260786Sps}
47360786Sps
47460786Sps/*
47560786Sps * Determine if an option is a single character option (BOOL or TRIPLE),
47660786Sps * or if it a multi-character option (NUMBER).
47760786Sps */
47860786Sps	public int
47960786Spssingle_char_option(c)
48060786Sps	int c;
48160786Sps{
48260786Sps	register struct option *o;
48360786Sps
48460786Sps	o = findopt(c);
48560786Sps	if (o == NULL)
48660786Sps		return (TRUE);
48760786Sps	return ((o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE)) != 0);
48860786Sps}
48960786Sps
49060786Sps/*
49160786Sps * Return the prompt to be used for a given option letter.
49260786Sps * Only string and number valued options have prompts.
49360786Sps */
49460786Sps	public char *
49560786Spsopt_prompt(c)
49660786Sps	int c;
49760786Sps{
49860786Sps	register struct option *o;
49960786Sps
50060786Sps	o = findopt(c);
50160786Sps	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
50260786Sps		return (NULL);
50360786Sps	return (o->odesc[0]);
50460786Sps}
50560786Sps
50660786Sps/*
50760786Sps * Return whether or not there is a string option pending;
50860786Sps * that is, if the previous option was a string-valued option letter
50960786Sps * (like -P) without a following string.
51060786Sps * In that case, the current option is taken to be the string for
51160786Sps * the previous option.
51260786Sps */
51360786Sps	public int
51460786Spsisoptpending()
51560786Sps{
51660786Sps	return (pendopt != NULL);
51760786Sps}
51860786Sps
51960786Sps/*
52060786Sps * Print error message about missing string.
52160786Sps */
52260786Sps	static void
52360786Spsnostring(printopt)
52460786Sps	char *printopt;
52560786Sps{
52660786Sps	PARG parg;
52760786Sps	parg.p_string = printopt;
52860786Sps	error("Value is required after %s", &parg);
52960786Sps}
53060786Sps
53160786Sps/*
53260786Sps * Print error message if a STRING type option is not followed by a string.
53360786Sps */
53460786Sps	public void
53560786Spsnopendopt()
53660786Sps{
53760786Sps	nostring(propt(pendopt->oletter));
53860786Sps}
53960786Sps
54060786Sps/*
54160786Sps * Scan to end of string or to an END_OPTION_STRING character.
54260786Sps * In the latter case, replace the char with a null char.
54360786Sps * Return a pointer to the remainder of the string, if any.
54460786Sps */
54560786Sps	static char *
54660786Spsoptstring(s, printopt)
54760786Sps	char *s;
54860786Sps	char *printopt;
54960786Sps{
55060786Sps	register char *p;
55160786Sps
55260786Sps	if (*s == '\0')
55360786Sps	{
55460786Sps		nostring(printopt);
55560786Sps		quit(QUIT_ERROR);
55660786Sps	}
55760786Sps	for (p = s;  *p != '\0';  p++)
55860786Sps		if (*p == END_OPTION_STRING)
55960786Sps		{
56060786Sps			*p = '\0';
56160786Sps			return (p+1);
56260786Sps		}
56360786Sps	return (p);
56460786Sps}
56560786Sps
56660786Sps/*
56760786Sps * Translate a string into a number.
56860786Sps * Like atoi(), but takes a pointer to a char *, and updates
56960786Sps * the char * to point after the translated number.
57060786Sps */
57160786Sps	public int
57260786Spsgetnum(sp, printopt, errp)
57360786Sps	char **sp;
57460786Sps	char *printopt;
57560786Sps	int *errp;
57660786Sps{
57760786Sps	register char *s;
57860786Sps	register int n;
57960786Sps	register int neg;
58060786Sps	PARG parg;
58160786Sps
58260786Sps	s = skipsp(*sp);
58360786Sps	neg = FALSE;
58460786Sps	if (*s == '-')
58560786Sps	{
58660786Sps		neg = TRUE;
58760786Sps		s++;
58860786Sps	}
58960786Sps	if (*s < '0' || *s > '9')
59060786Sps	{
59160786Sps		if (errp != NULL)
59260786Sps		{
59360786Sps			*errp = TRUE;
59460786Sps			return (-1);
59560786Sps		}
59660786Sps		parg.p_string = printopt;
59760786Sps		error("Number is required after %s", &parg);
59860786Sps		quit(QUIT_ERROR);
59960786Sps	}
60060786Sps
60160786Sps	n = 0;
60260786Sps	while (*s >= '0' && *s <= '9')
60360786Sps		n = 10 * n + *s++ - '0';
60460786Sps	*sp = s;
60560786Sps	if (errp != NULL)
60660786Sps		*errp = FALSE;
60760786Sps	if (neg)
60860786Sps		n = -n;
60960786Sps	return (n);
61060786Sps}
611