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