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