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