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