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 void 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 void
procargs(int argc,char ** argv)80 procargs(int argc, char **argv)
81 {
82 int i;
83 char *scriptname;
84
85 argptr = argv;
86 if (argc > 0)
87 argptr++;
88 for (i = 0; i < NOPTS; i++)
89 optval[i] = 2;
90 privileged = (getuid() != geteuid() || getgid() != getegid());
91 options(1);
92 if (*argptr == NULL && minusc == NULL)
93 sflag = 1;
94 if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) {
95 iflag = 1;
96 if (Eflag == 2)
97 Eflag = 1;
98 }
99 if (mflag == 2)
100 mflag = iflag;
101 for (i = 0; i < NOPTS; i++)
102 if (optval[i] == 2)
103 optval[i] = 0;
104 arg0 = argv[0];
105 if (sflag == 0 && minusc == NULL) {
106 scriptname = *argptr++;
107 setinputfile(scriptname, 0, -1 /* verify */);
108 commandname = arg0 = scriptname;
109 }
110 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
111 if (argptr && minusc && *argptr)
112 arg0 = *argptr++;
113
114 shellparam.p = argptr;
115 shellparam.reset = 1;
116 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
117 while (*argptr) {
118 shellparam.nparam++;
119 argptr++;
120 }
121 optschanged();
122 }
123
124
125 void
optschanged(void)126 optschanged(void)
127 {
128 setinteractive();
129 #ifndef NO_HISTORY
130 histedit();
131 #endif
132 setjobctl(mflag);
133 }
134
135 /*
136 * Process shell options. The global variable argptr contains a pointer
137 * to the argument list; we advance it past the options.
138 * If cmdline is true, process the shell's argv; otherwise, process arguments
139 * to the set special builtin.
140 */
141
142 static void
options(int cmdline)143 options(int cmdline)
144 {
145 char *kp, *p;
146 int val;
147 int c;
148
149 if (cmdline)
150 minusc = NULL;
151 while ((p = *argptr) != NULL) {
152 argptr++;
153 if ((c = *p++) == '-') {
154 val = 1;
155 /* A "-" or "--" terminates options */
156 if (p[0] == '\0')
157 goto end_options1;
158 if (p[0] == '-' && p[1] == '\0')
159 goto end_options2;
160 /**
161 * For the benefit of `#!' lines in shell scripts,
162 * treat a string of '-- *#.*' the same as '--'.
163 * This is needed so that a script starting with:
164 * #!/bin/sh -- # -*- perl -*-
165 * will continue to work after a change is made to
166 * kern/imgact_shell.c to NOT token-ize the options
167 * specified on a '#!' line. A bit of a kludge,
168 * but that trick is recommended in documentation
169 * for some scripting languages, and we might as
170 * well continue to support it.
171 */
172 if (p[0] == '-') {
173 kp = p + 1;
174 while (*kp == ' ' || *kp == '\t')
175 kp++;
176 if (*kp == '#' || *kp == '\0')
177 goto end_options2;
178 }
179 } else if (c == '+') {
180 val = 0;
181 } else {
182 argptr--;
183 break;
184 }
185 while ((c = *p++) != '\0') {
186 if (c == 'c' && cmdline) {
187 char *q;
188
189 q = *argptr++;
190 if (q == NULL || minusc != NULL)
191 error("Bad -c option");
192 minusc = q;
193 } else if (c == 'o') {
194 minus_o(*argptr, val);
195 if (*argptr)
196 argptr++;
197 } else
198 setoption(c, val);
199 }
200 }
201 return;
202
203 /* When processing `set', a single "-" means turn off -x and -v */
204 end_options1:
205 if (!cmdline) {
206 xflag = vflag = 0;
207 return;
208 }
209
210 /*
211 * When processing `set', a "--" means the remaining arguments
212 * replace the positional parameters in the active shell. If
213 * there are no remaining options, then all the positional
214 * parameters are cleared (equivalent to doing ``shift $#'').
215 */
216 end_options2:
217 if (!cmdline) {
218 if (*argptr == NULL)
219 setparam(0, argptr);
220 return;
221 }
222
223 /*
224 * At this point we are processing options given to 'sh' on a command
225 * line. If an end-of-options marker ("-" or "--") is followed by an
226 * arg of "#", then skip over all remaining arguments. Some scripting
227 * languages (e.g.: perl) document that /bin/sh will implement this
228 * behavior, and they recommend that users take advantage of it to
229 * solve certain issues that can come up when writing a perl script.
230 * Yes, this feature is in /bin/sh to help users write perl scripts.
231 */
232 p = *argptr;
233 if (p != NULL && p[0] == '#' && p[1] == '\0') {
234 while (*argptr != NULL)
235 argptr++;
236 /* We need to keep the final argument */
237 argptr--;
238 }
239 }
240
241 static void
minus_o(char * name,int val)242 minus_o(char *name, int val)
243 {
244 int i;
245 const unsigned char *on;
246 size_t len;
247
248 if (name == NULL) {
249 if (val) {
250 /* "Pretty" output. */
251 out1str("Current option settings\n");
252 for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
253 out1fmt("%-16.*s%s\n", *on, on + 1,
254 optval[i] ? "on" : "off");
255 } else {
256 /* Output suitable for re-input to shell. */
257 for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
258 out1fmt("%s %co %.*s%s",
259 i % 6 == 0 ? "set" : "",
260 optval[i] ? '-' : '+',
261 *on, on + 1,
262 i % 6 == 5 || i == NOPTS - 1 ? "\n" : "");
263 }
264 } else {
265 len = strlen(name);
266 for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1)
267 if (*on == len && memcmp(on + 1, name, len) == 0) {
268 setoptionbyindex(i, val);
269 return;
270 }
271 error("Illegal option -o %s", name);
272 }
273 }
274
275
276 static void
setoptionbyindex(int idx,int val)277 setoptionbyindex(int idx, int val)
278 {
279 if (&optval[idx] == &privileged && !val && privileged) {
280 if (setgid(getgid()) == -1)
281 error("setgid");
282 if (setuid(getuid()) == -1)
283 error("setuid");
284 }
285 optval[idx] = val;
286 if (val) {
287 /* #%$ hack for ksh semantics */
288 if (&optval[idx] == &Vflag)
289 Eflag = 0;
290 else if (&optval[idx] == &Eflag)
291 Vflag = 0;
292 }
293 }
294
295 static void
setoption(int flag,int val)296 setoption(int flag, int val)
297 {
298 int i;
299
300 for (i = 0; i < NSHORTOPTS; i++)
301 if (optletter[i] == flag) {
302 setoptionbyindex(i, val);
303 return;
304 }
305 error("Illegal option -%c", flag);
306 }
307
308
309 /*
310 * Set the shell parameters.
311 */
312
313 static void
setparam(int argc,char ** argv)314 setparam(int argc, char **argv)
315 {
316 char **newparam;
317 char **ap;
318
319 ap = newparam = ckmalloc((argc + 1) * sizeof *ap);
320 while (*argv) {
321 *ap++ = savestr(*argv++);
322 }
323 *ap = NULL;
324 freeparam(&shellparam);
325 shellparam.malloc = 1;
326 shellparam.nparam = argc;
327 shellparam.p = newparam;
328 shellparam.optp = NULL;
329 shellparam.reset = 1;
330 shellparam.optnext = NULL;
331 }
332
333
334 /*
335 * Free the list of positional parameters.
336 */
337
338 void
freeparam(struct shparam * param)339 freeparam(struct shparam *param)
340 {
341 char **ap;
342
343 if (param->malloc) {
344 for (ap = param->p ; *ap ; ap++)
345 ckfree(*ap);
346 ckfree(param->p);
347 }
348 if (param->optp) {
349 for (ap = param->optp ; *ap ; ap++)
350 ckfree(*ap);
351 ckfree(param->optp);
352 }
353 }
354
355
356
357 /*
358 * The shift builtin command.
359 */
360
361 int
shiftcmd(int argc,char ** argv)362 shiftcmd(int argc, char **argv)
363 {
364 int i, n;
365
366 n = 1;
367 if (argc > 1)
368 n = number(argv[1]);
369 if (n > shellparam.nparam)
370 return 1;
371 INTOFF;
372 shellparam.nparam -= n;
373 if (shellparam.malloc)
374 for (i = 0; i < n; i++)
375 ckfree(shellparam.p[i]);
376 memmove(shellparam.p, shellparam.p + n,
377 (shellparam.nparam + 1) * sizeof(shellparam.p[0]));
378 shellparam.reset = 1;
379 INTON;
380 return 0;
381 }
382
383
384
385 /*
386 * The set builtin command.
387 */
388
389 int
setcmd(int argc,char ** argv)390 setcmd(int argc, char **argv)
391 {
392 if (argc == 1)
393 return showvarscmd(argc, argv);
394 INTOFF;
395 options(0);
396 optschanged();
397 if (*argptr != NULL) {
398 setparam(argc - (argptr - argv), argptr);
399 }
400 INTON;
401 return 0;
402 }
403
404
405 void
getoptsreset(const char * value)406 getoptsreset(const char *value)
407 {
408 while (*value == '0')
409 value++;
410 if (strcmp(value, "1") == 0)
411 shellparam.reset = 1;
412 }
413
414 /*
415 * The getopts builtin. Shellparam.optnext points to the next argument
416 * to be processed. Shellparam.optptr points to the next character to
417 * be processed in the current argument. If shellparam.optnext is NULL,
418 * then it's the first time getopts has been called.
419 */
420
421 int
getoptscmd(int argc,char ** argv)422 getoptscmd(int argc, char **argv)
423 {
424 char **optbase = NULL, **ap;
425 int i;
426
427 if (argc < 3)
428 error("usage: getopts optstring var [arg]");
429
430 if (shellparam.reset == 1) {
431 INTOFF;
432 if (shellparam.optp) {
433 for (ap = shellparam.optp ; *ap ; ap++)
434 ckfree(*ap);
435 ckfree(shellparam.optp);
436 shellparam.optp = NULL;
437 }
438 if (argc > 3) {
439 shellparam.optp = ckmalloc((argc - 2) * sizeof *ap);
440 memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap);
441 for (i = 0; i < argc - 3; i++)
442 shellparam.optp[i] = savestr(argv[i + 3]);
443 }
444 INTON;
445 optbase = argc == 3 ? shellparam.p : shellparam.optp;
446 shellparam.optnext = optbase;
447 shellparam.optptr = NULL;
448 shellparam.reset = 0;
449 } else
450 optbase = shellparam.optp ? shellparam.optp : shellparam.p;
451
452 return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
453 &shellparam.optptr);
454 }
455
456 static int
getopts(char * optstr,char * optvar,char ** optfirst,char *** optnext,char ** optptr)457 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
458 char **optptr)
459 {
460 char *p, *q;
461 char c = '?';
462 int done = 0;
463 int ind = 0;
464 int err = 0;
465 char s[10];
466 const char *newoptarg = NULL;
467
468 if ((p = *optptr) == NULL || *p == '\0') {
469 /* Current word is done, advance */
470 if (*optnext == NULL)
471 return 1;
472 p = **optnext;
473 if (p == NULL || *p != '-' || *++p == '\0') {
474 atend:
475 ind = *optnext - optfirst + 1;
476 *optnext = NULL;
477 p = NULL;
478 done = 1;
479 goto out;
480 }
481 (*optnext)++;
482 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
483 goto atend;
484 }
485
486 c = *p++;
487 for (q = optstr; *q != c; ) {
488 if (*q == '\0') {
489 if (optstr[0] == ':') {
490 s[0] = c;
491 s[1] = '\0';
492 newoptarg = s;
493 }
494 else
495 out2fmt_flush("Illegal option -%c\n", c);
496 c = '?';
497 goto out;
498 }
499 if (*++q == ':')
500 q++;
501 }
502
503 if (*++q == ':') {
504 if (*p == '\0' && (p = **optnext) == NULL) {
505 if (optstr[0] == ':') {
506 s[0] = c;
507 s[1] = '\0';
508 newoptarg = s;
509 c = ':';
510 }
511 else {
512 out2fmt_flush("No arg for -%c option\n", c);
513 c = '?';
514 }
515 goto out;
516 }
517
518 if (p == **optnext)
519 (*optnext)++;
520 newoptarg = p;
521 p = NULL;
522 }
523
524 out:
525 if (*optnext != NULL)
526 ind = *optnext - optfirst + 1;
527 *optptr = p;
528 if (newoptarg != NULL)
529 err |= setvarsafe("OPTARG", newoptarg, 0);
530 else {
531 INTOFF;
532 err |= unsetvar("OPTARG");
533 INTON;
534 }
535 fmtstr(s, sizeof(s), "%d", ind);
536 err |= setvarsafe("OPTIND", s, VNOFUNC);
537 s[0] = c;
538 s[1] = '\0';
539 err |= setvarsafe(optvar, s, 0);
540 if (err) {
541 *optnext = NULL;
542 *optptr = NULL;
543 flushall();
544 exraise(EXERROR);
545 }
546 return done;
547 }
548
549 /*
550 * Standard option processing (a la getopt) for builtin routines. The
551 * only argument that is passed to nextopt is the option string; the
552 * other arguments are unnecessary. It returns the option, or '\0' on
553 * end of input.
554 */
555
556 int
nextopt(const char * optstring)557 nextopt(const char *optstring)
558 {
559 char *p;
560 const char *q;
561 char c;
562
563 if ((p = nextopt_optptr) == NULL || *p == '\0') {
564 p = *argptr;
565 if (p == NULL || *p != '-' || *++p == '\0')
566 return '\0';
567 argptr++;
568 if (p[0] == '-' && p[1] == '\0') /* check for "--" */
569 return '\0';
570 }
571 c = *p++;
572 for (q = optstring ; *q != c ; ) {
573 if (*q == '\0')
574 error("Illegal option -%c", c);
575 if (*++q == ':')
576 q++;
577 }
578 if (*++q == ':') {
579 if (*p == '\0' && (p = *argptr++) == NULL)
580 error("No arg for -%c option", c);
581 shoptarg = p;
582 p = NULL;
583 }
584 if (p != NULL && *p != '\0')
585 nextopt_optptr = p;
586 else
587 nextopt_optptr = NULL;
588 return c;
589 }
590