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