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 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 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 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 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 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 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 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 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 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 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 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 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 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 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