Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/bin/sh/options.c
39476 views
1
/*-
2
* SPDX-License-Identifier: BSD-3-Clause
3
*
4
* Copyright (c) 1991, 1993
5
* The Regents of the University of California. All rights reserved.
6
*
7
* This code is derived from software contributed to Berkeley by
8
* Kenneth Almquist.
9
*
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
12
* are met:
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. Neither the name of the University nor the names of its contributors
19
* may be used to endorse or promote products derived from this software
20
* without specific prior written permission.
21
*
22
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32
* SUCH DAMAGE.
33
*/
34
35
#include <signal.h>
36
#include <unistd.h>
37
#include <stdlib.h>
38
39
#include "shell.h"
40
#define DEFINE_OPTIONS
41
#include "options.h"
42
#undef DEFINE_OPTIONS
43
#include "nodes.h" /* for other header files */
44
#include "eval.h"
45
#include "jobs.h"
46
#include "input.h"
47
#include "output.h"
48
#include "trap.h"
49
#include "var.h"
50
#include "memalloc.h"
51
#include "error.h"
52
#include "mystring.h"
53
#include "builtins.h"
54
#ifndef NO_HISTORY
55
#include "myhistedit.h"
56
#endif
57
58
char *arg0; /* value of $0 */
59
struct shparam shellparam; /* current positional parameters */
60
char **argptr; /* argument list for builtin commands */
61
char *shoptarg; /* set by nextopt (like getopt) */
62
char *nextopt_optptr; /* used by nextopt */
63
64
char *minusc; /* argument to -c option */
65
66
67
static int options(int);
68
static void minus_o(char *, int);
69
static void setoption(int, int);
70
static void setoptionbyindex(int, int);
71
static void setparam(int, char **);
72
static int getopts(char *, char *, char **, char ***, char **);
73
74
75
/*
76
* Process the shell command line arguments.
77
*/
78
79
int
80
procargs(int argc, char **argv)
81
{
82
int i, login;
83
char *scriptname;
84
85
argptr = argv;
86
login = argptr[0] != NULL && argptr[0][0] == '-';
87
if (argc > 0)
88
argptr++;
89
for (i = 0; i < NOPTS; i++)
90
optval[i] = 2;
91
privileged = (getuid() != geteuid() || getgid() != getegid());
92
login |= options(1);
93
if (*argptr == NULL && minusc == NULL)
94
sflag = 1;
95
if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) {
96
iflag = 1;
97
if (Eflag == 2)
98
Eflag = 1;
99
}
100
if (mflag == 2)
101
mflag = iflag;
102
for (i = 0; i < NOPTS; i++)
103
if (optval[i] == 2)
104
optval[i] = 0;
105
arg0 = argv[0];
106
if (sflag == 0 && minusc == NULL) {
107
scriptname = *argptr++;
108
setinputfile(scriptname, 0, -1 /* verify */);
109
commandname = arg0 = scriptname;
110
}
111
/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
112
if (argptr && minusc && *argptr)
113
arg0 = *argptr++;
114
115
shellparam.p = argptr;
116
shellparam.reset = 1;
117
/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
118
while (*argptr) {
119
shellparam.nparam++;
120
argptr++;
121
}
122
optschanged();
123
124
return (login);
125
}
126
127
128
void
129
optschanged(void)
130
{
131
setinteractive();
132
#ifndef NO_HISTORY
133
histedit();
134
#endif
135
setjobctl(mflag);
136
}
137
138
/*
139
* Process shell options. The global variable argptr contains a pointer
140
* to the argument list; we advance it past the options.
141
* If cmdline is true, process the shell's argv; otherwise, process arguments
142
* to the set special builtin.
143
*/
144
145
static int
146
options(int cmdline)
147
{
148
char *kp, *p;
149
int val;
150
int c;
151
int login = 0;
152
153
if (cmdline)
154
minusc = NULL;
155
while ((p = *argptr) != NULL) {
156
argptr++;
157
if ((c = *p++) == '-') {
158
val = 1;
159
/* A "-" or "--" terminates options */
160
if (p[0] == '\0')
161
goto end_options1;
162
if (p[0] == '-' && p[1] == '\0')
163
goto end_options2;
164
/**
165
* For the benefit of `#!' lines in shell scripts,
166
* treat a string of '-- *#.*' the same as '--'.
167
* This is needed so that a script starting with:
168
* #!/bin/sh -- # -*- perl -*-
169
* will continue to work after a change is made to
170
* kern/imgact_shell.c to NOT token-ize the options
171
* specified on a '#!' line. A bit of a kludge,
172
* but that trick is recommended in documentation
173
* for some scripting languages, and we might as
174
* well continue to support it.
175
*/
176
if (p[0] == '-') {
177
kp = p + 1;
178
while (*kp == ' ' || *kp == '\t')
179
kp++;
180
if (*kp == '#' || *kp == '\0')
181
goto end_options2;
182
}
183
} else if (c == '+') {
184
val = 0;
185
} else {
186
argptr--;
187
break;
188
}
189
while ((c = *p++) != '\0') {
190
if (c == 'c' && cmdline) {
191
char *q;
192
193
q = *argptr++;
194
if (q == NULL || minusc != NULL)
195
error("Bad -c option");
196
minusc = q;
197
} else if (c == 'l' && cmdline) {
198
login = 1;
199
} else if (c == 'o') {
200
minus_o(*argptr, val);
201
if (*argptr)
202
argptr++;
203
} else
204
setoption(c, val);
205
}
206
}
207
return (login);
208
209
/* When processing `set', a single "-" means turn off -x and -v */
210
end_options1:
211
if (!cmdline) {
212
xflag = vflag = 0;
213
return (login);
214
}
215
216
/*
217
* When processing `set', a "--" means the remaining arguments
218
* replace the positional parameters in the active shell. If
219
* there are no remaining options, then all the positional
220
* parameters are cleared (equivalent to doing ``shift $#'').
221
*/
222
end_options2:
223
if (!cmdline) {
224
if (*argptr == NULL)
225
setparam(0, argptr);
226
return (login);
227
}
228
229
/*
230
* At this point we are processing options given to 'sh' on a command
231
* line. If an end-of-options marker ("-" or "--") is followed by an
232
* arg of "#", then skip over all remaining arguments. Some scripting
233
* languages (e.g.: perl) document that /bin/sh will implement this
234
* behavior, and they recommend that users take advantage of it to
235
* solve certain issues that can come up when writing a perl script.
236
* Yes, this feature is in /bin/sh to help users write perl scripts.
237
*/
238
p = *argptr;
239
if (p != NULL && p[0] == '#' && p[1] == '\0') {
240
while (*argptr != NULL)
241
argptr++;
242
/* We need to keep the final argument */
243
argptr--;
244
}
245
246
return (login);
247
}
248
249
static void
250
minus_o(char *name, int val)
251
{
252
int i;
253
const unsigned char *on;
254
size_t len;
255
256
if (name == NULL) {
257
if (val) {
258
/* "Pretty" output. */
259
out1str("Current option settings\n");
260
for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
261
out1fmt("%-16.*s%s\n", *on, on + 1,
262
optval[i] ? "on" : "off");
263
} else {
264
/* Output suitable for re-input to shell. */
265
for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
266
out1fmt("%s %co %.*s%s",
267
i % 6 == 0 ? "set" : "",
268
optval[i] ? '-' : '+',
269
*on, on + 1,
270
i % 6 == 5 || i == NOPTS - 1 ? "\n" : "");
271
}
272
} else {
273
len = strlen(name);
274
for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
275
if (*on == len && memcmp(on + 1, name, len) == 0) {
276
setoptionbyindex(i, val);
277
return;
278
}
279
error("Illegal option -o %s", name);
280
}
281
}
282
283
284
static void
285
setoptionbyindex(int idx, int val)
286
{
287
if (&optval[idx] == &privileged && !val && privileged) {
288
if (setgid(getgid()) == -1)
289
error("setgid");
290
if (setuid(getuid()) == -1)
291
error("setuid");
292
}
293
optval[idx] = val;
294
if (val) {
295
/* #%$ hack for ksh semantics */
296
if (&optval[idx] == &Vflag)
297
Eflag = 0;
298
else if (&optval[idx] == &Eflag)
299
Vflag = 0;
300
}
301
}
302
303
static void
304
setoption(int flag, int val)
305
{
306
int i;
307
308
for (i = 0; i < NSHORTOPTS; i++)
309
if (optletter[i] == flag) {
310
setoptionbyindex(i, val);
311
return;
312
}
313
error("Illegal option -%c", flag);
314
}
315
316
317
/*
318
* Set the shell parameters.
319
*/
320
321
static void
322
setparam(int argc, char **argv)
323
{
324
char **newparam;
325
char **ap;
326
327
ap = newparam = ckmalloc((argc + 1) * sizeof *ap);
328
while (*argv) {
329
*ap++ = savestr(*argv++);
330
}
331
*ap = NULL;
332
freeparam(&shellparam);
333
shellparam.malloc = 1;
334
shellparam.nparam = argc;
335
shellparam.p = newparam;
336
shellparam.optp = NULL;
337
shellparam.reset = 1;
338
shellparam.optnext = NULL;
339
}
340
341
342
/*
343
* Free the list of positional parameters.
344
*/
345
346
void
347
freeparam(struct shparam *param)
348
{
349
char **ap;
350
351
if (param->malloc) {
352
for (ap = param->p ; *ap ; ap++)
353
ckfree(*ap);
354
ckfree(param->p);
355
}
356
if (param->optp) {
357
for (ap = param->optp ; *ap ; ap++)
358
ckfree(*ap);
359
ckfree(param->optp);
360
}
361
}
362
363
364
365
/*
366
* The shift builtin command.
367
*/
368
369
int
370
shiftcmd(int argc, char **argv)
371
{
372
int i, n;
373
374
n = 1;
375
if (argc > 1)
376
n = number(argv[1]);
377
if (n > shellparam.nparam)
378
return 1;
379
INTOFF;
380
shellparam.nparam -= n;
381
if (shellparam.malloc)
382
for (i = 0; i < n; i++)
383
ckfree(shellparam.p[i]);
384
memmove(shellparam.p, shellparam.p + n,
385
(shellparam.nparam + 1) * sizeof(shellparam.p[0]));
386
shellparam.reset = 1;
387
INTON;
388
return 0;
389
}
390
391
392
393
/*
394
* The set builtin command.
395
*/
396
397
int
398
setcmd(int argc, char **argv)
399
{
400
if (argc == 1)
401
return showvarscmd(argc, argv);
402
INTOFF;
403
options(0);
404
optschanged();
405
if (*argptr != NULL) {
406
setparam(argc - (argptr - argv), argptr);
407
}
408
INTON;
409
return 0;
410
}
411
412
413
void
414
getoptsreset(const char *value)
415
{
416
while (*value == '0')
417
value++;
418
if (strcmp(value, "1") == 0)
419
shellparam.reset = 1;
420
}
421
422
/*
423
* The getopts builtin. Shellparam.optnext points to the next argument
424
* to be processed. Shellparam.optptr points to the next character to
425
* be processed in the current argument. If shellparam.optnext is NULL,
426
* then it's the first time getopts has been called.
427
*/
428
429
int
430
getoptscmd(int argc, char **argv)
431
{
432
char **optbase = NULL, **ap;
433
int i;
434
435
if (argc < 3)
436
error("usage: getopts optstring var [arg]");
437
438
if (shellparam.reset == 1) {
439
INTOFF;
440
if (shellparam.optp) {
441
for (ap = shellparam.optp ; *ap ; ap++)
442
ckfree(*ap);
443
ckfree(shellparam.optp);
444
shellparam.optp = NULL;
445
}
446
if (argc > 3) {
447
shellparam.optp = ckmalloc((argc - 2) * sizeof *ap);
448
memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap);
449
for (i = 0; i < argc - 3; i++)
450
shellparam.optp[i] = savestr(argv[i + 3]);
451
}
452
INTON;
453
optbase = argc == 3 ? shellparam.p : shellparam.optp;
454
shellparam.optnext = optbase;
455
shellparam.optptr = NULL;
456
shellparam.reset = 0;
457
} else
458
optbase = shellparam.optp ? shellparam.optp : shellparam.p;
459
460
return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
461
&shellparam.optptr);
462
}
463
464
static int
465
getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
466
char **optptr)
467
{
468
char *p, *q;
469
char c = '?';
470
int done = 0;
471
int ind = 0;
472
int err = 0;
473
char s[10];
474
const char *newoptarg = NULL;
475
476
if ((p = *optptr) == NULL || *p == '\0') {
477
/* Current word is done, advance */
478
if (*optnext == NULL)
479
return 1;
480
p = **optnext;
481
if (p == NULL || *p != '-' || *++p == '\0') {
482
atend:
483
ind = *optnext - optfirst + 1;
484
*optnext = NULL;
485
p = NULL;
486
done = 1;
487
goto out;
488
}
489
(*optnext)++;
490
if (p[0] == '-' && p[1] == '\0') /* check for "--" */
491
goto atend;
492
}
493
494
c = *p++;
495
for (q = optstr; *q != c; ) {
496
if (*q == '\0') {
497
if (optstr[0] == ':') {
498
s[0] = c;
499
s[1] = '\0';
500
newoptarg = s;
501
}
502
else
503
out2fmt_flush("Illegal option -%c\n", c);
504
c = '?';
505
goto out;
506
}
507
if (*++q == ':')
508
q++;
509
}
510
511
if (*++q == ':') {
512
if (*p == '\0' && (p = **optnext) == NULL) {
513
if (optstr[0] == ':') {
514
s[0] = c;
515
s[1] = '\0';
516
newoptarg = s;
517
c = ':';
518
}
519
else {
520
out2fmt_flush("No arg for -%c option\n", c);
521
c = '?';
522
}
523
goto out;
524
}
525
526
if (p == **optnext)
527
(*optnext)++;
528
newoptarg = p;
529
p = NULL;
530
}
531
532
out:
533
if (*optnext != NULL)
534
ind = *optnext - optfirst + 1;
535
*optptr = p;
536
if (newoptarg != NULL)
537
err |= setvarsafe("OPTARG", newoptarg, 0);
538
else {
539
INTOFF;
540
err |= unsetvar("OPTARG");
541
INTON;
542
}
543
fmtstr(s, sizeof(s), "%d", ind);
544
err |= setvarsafe("OPTIND", s, VNOFUNC);
545
s[0] = c;
546
s[1] = '\0';
547
err |= setvarsafe(optvar, s, 0);
548
if (err) {
549
*optnext = NULL;
550
*optptr = NULL;
551
flushall();
552
exraise(EXERROR);
553
}
554
return done;
555
}
556
557
/*
558
* Standard option processing (a la getopt) for builtin routines. The
559
* only argument that is passed to nextopt is the option string; the
560
* other arguments are unnecessary. It returns the option, or '\0' on
561
* end of input.
562
*/
563
564
int
565
nextopt(const char *optstring)
566
{
567
char *p;
568
const char *q;
569
char c;
570
571
if ((p = nextopt_optptr) == NULL || *p == '\0') {
572
p = *argptr;
573
if (p == NULL || *p != '-' || *++p == '\0')
574
return '\0';
575
argptr++;
576
if (p[0] == '-' && p[1] == '\0') /* check for "--" */
577
return '\0';
578
}
579
c = *p++;
580
for (q = optstring ; *q != c ; ) {
581
if (*q == '\0')
582
error("Illegal option -%c", c);
583
if (*++q == ':')
584
q++;
585
}
586
if (*++q == ':') {
587
if (*p == '\0' && (p = *argptr++) == NULL)
588
error("No arg for -%c option", c);
589
shoptarg = p;
590
p = NULL;
591
}
592
if (p != NULL && *p != '\0')
593
nextopt_optptr = p;
594
else
595
nextopt_optptr = NULL;
596
return c;
597
}
598
599