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