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.reset = 1; 329 shellparam.optnext = NULL; 330 } 331 332 333 /* 334 * Free the list of positional parameters. 335 */ 336 337 void 338 freeparam(struct shparam *param) 339 { 340 char **ap; 341 342 if (param->malloc) { 343 for (ap = param->p ; *ap ; ap++) 344 ckfree(*ap); 345 ckfree(param->p); 346 } 347 } 348 349 350 351 /* 352 * The shift builtin command. 353 */ 354 355 int 356 shiftcmd(int argc, char **argv) 357 { 358 int n; 359 char **ap1, **ap2; 360 361 n = 1; 362 if (argc > 1) 363 n = number(argv[1]); 364 if (n > shellparam.nparam) 365 return 1; 366 INTOFF; 367 shellparam.nparam -= n; 368 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 369 if (shellparam.malloc) 370 ckfree(*ap1); 371 } 372 ap2 = shellparam.p; 373 while ((*ap2++ = *ap1++) != NULL); 374 shellparam.reset = 1; 375 INTON; 376 return 0; 377 } 378 379 380 381 /* 382 * The set command builtin. 383 */ 384 385 int 386 setcmd(int argc, char **argv) 387 { 388 if (argc == 1) 389 return showvarscmd(argc, argv); 390 INTOFF; 391 options(0); 392 optschanged(); 393 if (*argptr != NULL) { 394 setparam(argptr); 395 } 396 INTON; 397 return 0; 398 } 399 400 401 void 402 getoptsreset(const char *value) 403 { 404 while (*value == '0') 405 value++; 406 if (strcmp(value, "1") == 0) 407 shellparam.reset = 1; 408 } 409 410 /* 411 * The getopts builtin. Shellparam.optnext points to the next argument 412 * to be processed. Shellparam.optptr points to the next character to 413 * be processed in the current argument. If shellparam.optnext is NULL, 414 * then it's the first time getopts has been called. 415 */ 416 417 int 418 getoptscmd(int argc, char **argv) 419 { 420 char **optbase = NULL; 421 422 if (argc < 3) 423 error("usage: getopts optstring var [arg]"); 424 else if (argc == 3) 425 optbase = shellparam.p; 426 else 427 optbase = &argv[3]; 428 429 if (shellparam.reset == 1) { 430 shellparam.optnext = optbase; 431 shellparam.optptr = NULL; 432 shellparam.reset = 0; 433 } 434 435 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 436 &shellparam.optptr); 437 } 438 439 static int 440 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, 441 char **optptr) 442 { 443 char *p, *q; 444 char c = '?'; 445 int done = 0; 446 int ind = 0; 447 int err = 0; 448 char s[10]; 449 450 if ((p = *optptr) == NULL || *p == '\0') { 451 /* Current word is done, advance */ 452 if (*optnext == NULL) 453 return 1; 454 p = **optnext; 455 if (p == NULL || *p != '-' || *++p == '\0') { 456 atend: 457 ind = *optnext - optfirst + 1; 458 *optnext = NULL; 459 p = NULL; 460 done = 1; 461 goto out; 462 } 463 (*optnext)++; 464 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 465 goto atend; 466 } 467 468 c = *p++; 469 for (q = optstr; *q != c; ) { 470 if (*q == '\0') { 471 if (optstr[0] == ':') { 472 s[0] = c; 473 s[1] = '\0'; 474 err |= setvarsafe("OPTARG", s, 0); 475 } 476 else { 477 out1fmt("Illegal option -%c\n", c); 478 (void) unsetvar("OPTARG"); 479 } 480 c = '?'; 481 goto bad; 482 } 483 if (*++q == ':') 484 q++; 485 } 486 487 if (*++q == ':') { 488 if (*p == '\0' && (p = **optnext) == NULL) { 489 if (optstr[0] == ':') { 490 s[0] = c; 491 s[1] = '\0'; 492 err |= setvarsafe("OPTARG", s, 0); 493 c = ':'; 494 } 495 else { 496 out1fmt("No arg for -%c option\n", c); 497 (void) unsetvar("OPTARG"); 498 c = '?'; 499 } 500 goto bad; 501 } 502 503 if (p == **optnext) 504 (*optnext)++; 505 setvarsafe("OPTARG", p, 0); 506 p = NULL; 507 } 508 else 509 setvarsafe("OPTARG", "", 0); 510 ind = *optnext - optfirst + 1; 511 goto out; 512 513 bad: 514 ind = 1; 515 *optnext = NULL; 516 p = NULL; 517 out: 518 *optptr = p; 519 fmtstr(s, sizeof(s), "%d", ind); 520 err |= setvarsafe("OPTIND", s, VNOFUNC); 521 s[0] = c; 522 s[1] = '\0'; 523 err |= setvarsafe(optvar, s, 0); 524 if (err) { 525 *optnext = NULL; 526 *optptr = NULL; 527 flushall(); 528 exraise(EXERROR); 529 } 530 return done; 531 } 532 533 /* 534 * Standard option processing (a la getopt) for builtin routines. The 535 * only argument that is passed to nextopt is the option string; the 536 * other arguments are unnecessary. It return the character, or '\0' on 537 * end of input. 538 */ 539 540 int 541 nextopt(const char *optstring) 542 { 543 char *p; 544 const char *q; 545 char c; 546 547 if ((p = nextopt_optptr) == NULL || *p == '\0') { 548 p = *argptr; 549 if (p == NULL || *p != '-' || *++p == '\0') 550 return '\0'; 551 argptr++; 552 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 553 return '\0'; 554 } 555 c = *p++; 556 for (q = optstring ; *q != c ; ) { 557 if (*q == '\0') 558 error("Illegal option -%c", c); 559 if (*++q == ':') 560 q++; 561 } 562 if (*++q == ':') { 563 if (*p == '\0' && (p = *argptr++) == NULL) 564 error("No arg for -%c option", c); 565 shoptarg = p; 566 p = NULL; 567 } 568 nextopt_optptr = p; 569 return c; 570 } 571