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