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