1/*
2 * Copyright (C) 1984-2012  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();
27static int flip_triple();
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	register struct loption *o;
73	register 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				every_first_cmd = save(str+1);
154			else
155				ungetsc(str);
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 */
298	public void
299toggle_option(o, lower, s, how_toggle)
300	struct loption *o;
301	int lower;
302	char *s;
303	int how_toggle;
304{
305	register int num;
306	int no_prompt;
307	int err;
308	PARG parg;
309
310	no_prompt = (how_toggle & OPT_NO_PROMPT);
311	how_toggle &= ~OPT_NO_PROMPT;
312
313	if (o == NULL)
314	{
315		error("No such option", NULL_PARG);
316		return;
317	}
318
319	if (how_toggle == OPT_TOGGLE && (o->otype & NO_TOGGLE))
320	{
321		parg.p_string = opt_desc(o);
322		error("Cannot change the %s option", &parg);
323		return;
324	}
325
326	if (how_toggle == OPT_NO_TOGGLE && (o->otype & NO_QUERY))
327	{
328		parg.p_string = opt_desc(o);
329		error("Cannot query the %s option", &parg);
330		return;
331	}
332
333	/*
334	 * Check for something which appears to be a do_toggle
335	 * (because the "-" command was used), but really is not.
336	 * This could be a string option with no string, or
337	 * a number option with no number.
338	 */
339	switch (o->otype & OTYPE)
340	{
341	case STRING:
342	case NUMBER:
343		if (how_toggle == OPT_TOGGLE && *s == '\0')
344			how_toggle = OPT_NO_TOGGLE;
345		break;
346	}
347
348#if HILITE_SEARCH
349	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
350		repaint_hilite(0);
351#endif
352
353	/*
354	 * Now actually toggle (change) the variable.
355	 */
356	if (how_toggle != OPT_NO_TOGGLE)
357	{
358		switch (o->otype & OTYPE)
359		{
360		case BOOL:
361			/*
362			 * Boolean.
363			 */
364			switch (how_toggle)
365			{
366			case OPT_TOGGLE:
367				*(o->ovar) = ! *(o->ovar);
368				break;
369			case OPT_UNSET:
370				*(o->ovar) = o->odefault;
371				break;
372			case OPT_SET:
373				*(o->ovar) = ! o->odefault;
374				break;
375			}
376			break;
377		case TRIPLE:
378			/*
379			 * Triple:
380			 *	If user gave the lower case letter, then switch
381			 *	to 1 unless already 1, in which case make it 0.
382			 *	If user gave the upper case letter, then switch
383			 *	to 2 unless already 2, in which case make it 0.
384			 */
385			switch (how_toggle)
386			{
387			case OPT_TOGGLE:
388				*(o->ovar) = flip_triple(*(o->ovar), lower);
389				break;
390			case OPT_UNSET:
391				*(o->ovar) = o->odefault;
392				break;
393			case OPT_SET:
394				*(o->ovar) = flip_triple(o->odefault, lower);
395				break;
396			}
397			break;
398		case STRING:
399			/*
400			 * String: don't do anything here.
401			 *	The handling function will do everything.
402			 */
403			switch (how_toggle)
404			{
405			case OPT_SET:
406			case OPT_UNSET:
407				error("Cannot use \"-+\" or \"--\" for a string option",
408					NULL_PARG);
409				return;
410			}
411			break;
412		case NUMBER:
413			/*
414			 * Number: set the variable to the given number.
415			 */
416			switch (how_toggle)
417			{
418			case OPT_TOGGLE:
419				num = getnum(&s, NULL, &err);
420				if (!err)
421					*(o->ovar) = num;
422				break;
423			case OPT_UNSET:
424				*(o->ovar) = o->odefault;
425				break;
426			case OPT_SET:
427				error("Can't use \"-!\" for a numeric option",
428					NULL_PARG);
429				return;
430			}
431			break;
432		}
433	}
434
435	/*
436	 * Call the handling function for any special action
437	 * specific to this option.
438	 */
439	if (o->ofunc != NULL)
440		(*o->ofunc)((how_toggle==OPT_NO_TOGGLE) ? QUERY : TOGGLE, s);
441
442#if HILITE_SEARCH
443	if (how_toggle != OPT_NO_TOGGLE && (o->otype & HL_REPAINT))
444		chg_hilite();
445#endif
446
447	if (!no_prompt)
448	{
449		/*
450		 * Print a message describing the new setting.
451		 */
452		switch (o->otype & OTYPE)
453		{
454		case BOOL:
455		case TRIPLE:
456			/*
457			 * Print the odesc message.
458			 */
459			error(o->odesc[*(o->ovar)], NULL_PARG);
460			break;
461		case NUMBER:
462			/*
463			 * The message is in odesc[1] and has a %d for
464			 * the value of the variable.
465			 */
466			parg.p_int = *(o->ovar);
467			error(o->odesc[1], &parg);
468			break;
469		case STRING:
470			/*
471			 * Message was already printed by the handling function.
472			 */
473			break;
474		}
475	}
476
477	if (how_toggle != OPT_NO_TOGGLE && (o->otype & REPAINT))
478		screen_trashed = TRUE;
479}
480
481/*
482 * "Toggle" a triple-valued option.
483 */
484	static int
485flip_triple(val, lc)
486	int val;
487	int lc;
488{
489	if (lc)
490		return ((val == OPT_ON) ? OPT_OFF : OPT_ON);
491	else
492		return ((val == OPT_ONPLUS) ? OPT_OFF : OPT_ONPLUS);
493}
494
495/*
496 * Determine if an option takes a parameter.
497 */
498	public int
499opt_has_param(o)
500	struct loption *o;
501{
502	if (o == NULL)
503		return (0);
504	if (o->otype & (BOOL|TRIPLE|NOVAR|NO_TOGGLE))
505		return (0);
506	return (1);
507}
508
509/*
510 * Return the prompt to be used for a given option letter.
511 * Only string and number valued options have prompts.
512 */
513	public char *
514opt_prompt(o)
515	struct loption *o;
516{
517	if (o == NULL || (o->otype & (STRING|NUMBER)) == 0)
518		return ("?");
519	return (o->odesc[0]);
520}
521
522/*
523 * Return whether or not there is a string option pending;
524 * that is, if the previous option was a string-valued option letter
525 * (like -P) without a following string.
526 * In that case, the current option is taken to be the string for
527 * the previous option.
528 */
529	public int
530isoptpending()
531{
532	return (pendopt != NULL);
533}
534
535/*
536 * Print error message about missing string.
537 */
538	static void
539nostring(printopt)
540	char *printopt;
541{
542	PARG parg;
543	parg.p_string = printopt;
544	error("Value is required after %s", &parg);
545}
546
547/*
548 * Print error message if a STRING type option is not followed by a string.
549 */
550	public void
551nopendopt()
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 */
561	static char *
562optstring(s, p_str, printopt, validchars)
563	char *s;
564	char **p_str;
565	char *printopt;
566	char *validchars;
567{
568	register char *p;
569	register char *out;
570
571	if (*s == '\0')
572	{
573		nostring(printopt);
574		return (NULL);
575	}
576	/* Alloc could be more than needed, but not worth trimming. */
577	*p_str = (char *) ecalloc(strlen(s)+1, sizeof(char));
578	out = *p_str;
579
580	for (p = s;  *p != '\0';  p++)
581	{
582		if (opt_use_backslash && *p == '\\' && p[1] != '\0')
583		{
584			/* Take next char literally. */
585			++p;
586		} else
587		{
588			if (*p == END_OPTION_STRING ||
589			    (validchars != NULL && strchr(validchars, *p) == NULL))
590				/* End of option string. */
591				break;
592		}
593		*out++ = *p;
594	}
595	*out = '\0';
596	return (p);
597}
598
599/*
600 */
601	static int
602num_error(printopt, errp)
603	char *printopt;
604	int *errp;
605{
606	PARG parg;
607
608	if (errp != NULL)
609	{
610		*errp = TRUE;
611		return (-1);
612	}
613	if (printopt != NULL)
614	{
615		parg.p_string = printopt;
616		error("Number is required after %s", &parg);
617	}
618	return (-1);
619}
620
621/*
622 * Translate a string into a number.
623 * Like atoi(), but takes a pointer to a char *, and updates
624 * the char * to point after the translated number.
625 */
626	public int
627getnum(sp, printopt, errp)
628	char **sp;
629	char *printopt;
630	int *errp;
631{
632	register char *s;
633	register int n;
634	register int neg;
635
636	s = skipsp(*sp);
637	neg = FALSE;
638	if (*s == '-')
639	{
640		neg = TRUE;
641		s++;
642	}
643	if (*s < '0' || *s > '9')
644		return (num_error(printopt, errp));
645
646	n = 0;
647	while (*s >= '0' && *s <= '9')
648		n = 10 * n + *s++ - '0';
649	*sp = s;
650	if (errp != NULL)
651		*errp = FALSE;
652	if (neg)
653		n = -n;
654	return (n);
655}
656
657/*
658 * Translate a string into a fraction, represented by the part of a
659 * number which would follow a decimal point.
660 * The value of the fraction is returned as parts per NUM_FRAC_DENOM.
661 * That is, if "n" is returned, the fraction intended is n/NUM_FRAC_DENOM.
662 */
663	public long
664getfraction(sp, printopt, errp)
665	char **sp;
666	char *printopt;
667	int *errp;
668{
669	register char *s;
670	long frac = 0;
671	int fraclen = 0;
672
673	s = skipsp(*sp);
674	if (*s < '0' || *s > '9')
675		return (num_error(printopt, errp));
676
677	for ( ;  *s >= '0' && *s <= '9';  s++)
678	{
679		frac = (frac * 10) + (*s - '0');
680		fraclen++;
681	}
682	if (fraclen > NUM_LOG_FRAC_DENOM)
683		while (fraclen-- > NUM_LOG_FRAC_DENOM)
684			frac /= 10;
685	else
686		while (fraclen++ < NUM_LOG_FRAC_DENOM)
687			frac *= 10;
688	*sp = s;
689	if (errp != NULL)
690		*errp = FALSE;
691	return (frac);
692}
693
694
695/*
696 * Get the value of the -e flag.
697 */
698	public int
699get_quit_at_eof()
700{
701	if (!less_is_more)
702		return quit_at_eof;
703	/* When less_is_more is set, the -e flag semantics are different. */
704	return quit_at_eof ? OPT_ON : OPT_ONPLUS;
705}
706