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