GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/less/less/../option.c Lines: 2 235 0.9 %
Date: 2017-11-13 Branches: 0 218 0.0 %

Line Branch Exec Source
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
24
static struct loption *pendopt;
25
int plusoption = FALSE;
26
27
static char *optstring(char *, char **, char *, char *);
28
static int flip_triple(int, int);
29
30
extern int screen_trashed;
31
extern int less_is_more;
32
extern int quit_at_eof;
33
extern char *every_first_cmd;
34
extern int opt_use_backslash;
35
36
/*
37
 * Return a printable description of an option.
38
 */
39
static char *
40
opt_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
 */
55
char *
56
propt(int c)
57
{
58
	static char buf[8];
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
 */
68
void
69
scan_option(char *s)
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);
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]);
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
 */
323
void
324
toggle_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
 */
489
static int
490
flip_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
 */
501
int
502
opt_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
 */
515
char *
516
opt_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
 */
530
int
531
isoptpending(void)
532
{
533
32
	return (pendopt != NULL);
534
}
535
536
/*
537
 * Print error message about missing string.
538
 */
539
static void
540
nostring(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
 */
550
void
551
nopendopt(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
 */
561
static char *
562
optstring(char *s, char **p_str, char *printopt, char *validchars)
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 ||
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
 */
594
static int
595
num_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
 */
615
int
616
getnum(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
 */
648
long
649
getfraction(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
 */
679
int
680
get_quit_at_eof(void)
681
{
682
126
	return (quit_at_eof);
683
}