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