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 #ifndef NO_HISTORY 60 #include "myhistedit.h" 61 #endif 62 63 char *arg0; /* value of $0 */ 64 struct shparam shellparam; /* current positional parameters */ 65 char **argptr; /* argument list for builtin commands */ 66 char *shoptarg; /* set by nextopt (like getopt) */ 67 char *optptr; /* used by nextopt */ 68 69 char *minusc; /* argument to -c option */ 70 71 72 STATIC void options(int); 73 STATIC void minus_o(char *, int); 74 STATIC void setoption(int, int); 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 87 argptr = argv; 88 if (argc > 0) 89 argptr++; 90 for (i = 0; i < NOPTS; i++) 91 optlist[i].val = 2; 92 privileged = (getuid() != geteuid() || getgid() != getegid()); 93 options(1); 94 if (*argptr == NULL && minusc == NULL) 95 sflag = 1; 96 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 97 iflag = 1; 98 if (mflag == 2) 99 mflag = iflag; 100 for (i = 0; i < NOPTS; i++) 101 if (optlist[i].val == 2) 102 optlist[i].val = 0; 103 arg0 = argv[0]; 104 if (sflag == 0 && minusc == NULL) { 105 commandname = arg0 = *argptr++; 106 setinputfile(commandname, 0); 107 } 108 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 109 if (argptr && minusc && *argptr) 110 arg0 = *argptr++; 111 112 shellparam.p = argptr; 113 shellparam.reset = 1; 114 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 115 while (*argptr) { 116 shellparam.nparam++; 117 argptr++; 118 } 119 optschanged(); 120 } 121 122 123 void 124 optschanged(void) 125 { 126 setinteractive(iflag); 127 #ifndef NO_HISTORY 128 histedit(); 129 #endif 130 setjobctl(mflag); 131 } 132 133 /* 134 * Process shell options. The global variable argptr contains a pointer 135 * to the argument list; we advance it past the options. 136 */ 137 138 STATIC void 139 options(int cmdline) 140 { 141 char *p; 142 int val; 143 int c; 144 145 if (cmdline) 146 minusc = NULL; 147 while ((p = *argptr) != NULL) { 148 argptr++; 149 if ((c = *p++) == '-') { 150 val = 1; 151 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 152 if (!cmdline) { 153 /* "-" means turn off -x and -v */ 154 if (p[0] == '\0') 155 xflag = vflag = 0; 156 /* "--" means reset params */ 157 else if (*argptr == NULL) 158 setparam(argptr); 159 } 160 break; /* "-" or "--" terminates options */ 161 } 162 } else if (c == '+') { 163 val = 0; 164 } else { 165 argptr--; 166 break; 167 } 168 while ((c = *p++) != '\0') { 169 if (c == 'c' && cmdline) { 170 char *q; 171 #ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 172 if (*p == '\0') 173 #endif 174 q = *argptr++; 175 if (q == NULL || minusc != NULL) 176 error("Bad -c option"); 177 minusc = q; 178 #ifdef NOHACK 179 break; 180 #endif 181 } else if (c == 'o') { 182 minus_o(*argptr, val); 183 if (*argptr) 184 argptr++; 185 } else { 186 if (c == 'p' && !val && privileged) { 187 (void) setuid(getuid()); 188 (void) setgid(getgid()); 189 } 190 setoption(c, val); 191 } 192 } 193 } 194 } 195 196 STATIC void 197 minus_o(char *name, int val) 198 { 199 int doneset, i; 200 201 if (name == NULL) { 202 if (val) { 203 /* "Pretty" output. */ 204 out1str("Current option settings\n"); 205 for (i = 0; i < NOPTS; i++) 206 out1fmt("%-16s%s\n", optlist[i].name, 207 optlist[i].val ? "on" : "off"); 208 } else { 209 /* Output suitable for re-input to shell. */ 210 for (doneset = i = 0; i < NOPTS; i++) 211 if (optlist[i].val) { 212 if (!doneset) { 213 out1str("set"); 214 doneset = 1; 215 } 216 out1fmt(" -o %s", optlist[i].name); 217 } 218 if (doneset) 219 out1c('\n'); 220 } 221 } else { 222 for (i = 0; i < NOPTS; i++) 223 if (equal(name, optlist[i].name)) { 224 if (!val && privileged && equal(name, "privileged")) { 225 (void) setuid(getuid()); 226 (void) setgid(getgid()); 227 } 228 setoption(optlist[i].letter, val); 229 return; 230 } 231 error("Illegal option -o %s", name); 232 } 233 } 234 235 236 STATIC void 237 setoption(int flag, int val) 238 { 239 int i; 240 241 for (i = 0; i < NOPTS; i++) 242 if (optlist[i].letter == flag) { 243 optlist[i].val = val; 244 if (val) { 245 /* #%$ hack for ksh semantics */ 246 if (flag == 'V') 247 Eflag = 0; 248 else if (flag == 'E') 249 Vflag = 0; 250 } 251 return; 252 } 253 error("Illegal option -%c", flag); 254 } 255 256 257 258 #ifdef mkinit 259 INCLUDE "options.h" 260 261 SHELLPROC { 262 int i; 263 264 for (i = 0; i < NOPTS; i++) 265 optlist[i].val = 0; 266 optschanged(); 267 268 } 269 #endif 270 271 272 /* 273 * Set the shell parameters. 274 */ 275 276 void 277 setparam(char **argv) 278 { 279 char **newparam; 280 char **ap; 281 int nparam; 282 283 for (nparam = 0 ; argv[nparam] ; nparam++); 284 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 285 while (*argv) { 286 *ap++ = savestr(*argv++); 287 } 288 *ap = NULL; 289 freeparam(&shellparam); 290 shellparam.malloc = 1; 291 shellparam.nparam = nparam; 292 shellparam.p = newparam; 293 shellparam.optnext = NULL; 294 } 295 296 297 /* 298 * Free the list of positional parameters. 299 */ 300 301 void 302 freeparam(struct shparam *param) 303 { 304 char **ap; 305 306 if (param->malloc) { 307 for (ap = param->p ; *ap ; ap++) 308 ckfree(*ap); 309 ckfree(param->p); 310 } 311 } 312 313 314 315 /* 316 * The shift builtin command. 317 */ 318 319 int 320 shiftcmd(int argc, char **argv) 321 { 322 int n; 323 char **ap1, **ap2; 324 325 n = 1; 326 if (argc > 1) 327 n = number(argv[1]); 328 if (n > shellparam.nparam) 329 error("can't shift that many"); 330 INTOFF; 331 shellparam.nparam -= n; 332 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 333 if (shellparam.malloc) 334 ckfree(*ap1); 335 } 336 ap2 = shellparam.p; 337 while ((*ap2++ = *ap1++) != NULL); 338 shellparam.optnext = NULL; 339 INTON; 340 return 0; 341 } 342 343 344 345 /* 346 * The set command builtin. 347 */ 348 349 int 350 setcmd(int argc, char **argv) 351 { 352 if (argc == 1) 353 return showvarscmd(argc, argv); 354 INTOFF; 355 options(0); 356 optschanged(); 357 if (*argptr != NULL) { 358 setparam(argptr); 359 } 360 INTON; 361 return 0; 362 } 363 364 365 void 366 getoptsreset(const char *value) 367 { 368 if (number(value) == 1) { 369 shellparam.optnext = NULL; 370 shellparam.reset = 1; 371 } 372 } 373 374 /* 375 * The getopts builtin. Shellparam.optnext points to the next argument 376 * to be processed. Shellparam.optptr points to the next character to 377 * be processed in the current argument. If shellparam.optnext is NULL, 378 * then it's the first time getopts has been called. 379 */ 380 381 int 382 getoptscmd(int argc, char **argv) 383 { 384 char **optbase = NULL; 385 386 if (argc < 3) 387 error("usage: getopts optstring var [arg]"); 388 else if (argc == 3) 389 optbase = shellparam.p; 390 else 391 optbase = &argv[3]; 392 393 if (shellparam.reset == 1) { 394 shellparam.optnext = optbase; 395 shellparam.optptr = NULL; 396 shellparam.reset = 0; 397 } 398 399 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 400 &shellparam.optptr); 401 } 402 403 STATIC int 404 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, 405 char **optptr) 406 { 407 char *p, *q; 408 char c = '?'; 409 int done = 0; 410 int ind = 0; 411 int err = 0; 412 char s[10]; 413 414 if ((p = *optptr) == NULL || *p == '\0') { 415 /* Current word is done, advance */ 416 if (*optnext == NULL) 417 return 1; 418 p = **optnext; 419 if (p == NULL || *p != '-' || *++p == '\0') { 420 atend: 421 ind = *optnext - optfirst + 1; 422 *optnext = NULL; 423 p = NULL; 424 done = 1; 425 goto out; 426 } 427 (*optnext)++; 428 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 429 goto atend; 430 } 431 432 c = *p++; 433 for (q = optstr; *q != c; ) { 434 if (*q == '\0') { 435 if (optstr[0] == ':') { 436 s[0] = c; 437 s[1] = '\0'; 438 err |= setvarsafe("OPTARG", s, 0); 439 } 440 else { 441 out1fmt("Illegal option -%c\n", c); 442 (void) unsetvar("OPTARG"); 443 } 444 c = '?'; 445 goto bad; 446 } 447 if (*++q == ':') 448 q++; 449 } 450 451 if (*++q == ':') { 452 if (*p == '\0' && (p = **optnext) == NULL) { 453 if (optstr[0] == ':') { 454 s[0] = c; 455 s[1] = '\0'; 456 err |= setvarsafe("OPTARG", s, 0); 457 c = ':'; 458 } 459 else { 460 out1fmt("No arg for -%c option\n", c); 461 (void) unsetvar("OPTARG"); 462 c = '?'; 463 } 464 goto bad; 465 } 466 467 if (p == **optnext) 468 (*optnext)++; 469 setvarsafe("OPTARG", p, 0); 470 p = NULL; 471 } 472 else 473 setvarsafe("OPTARG", "", 0); 474 ind = *optnext - optfirst + 1; 475 goto out; 476 477 bad: 478 ind = 1; 479 *optnext = NULL; 480 p = NULL; 481 out: 482 *optptr = p; 483 fmtstr(s, sizeof(s), "%d", ind); 484 err |= setvarsafe("OPTIND", s, VNOFUNC); 485 s[0] = c; 486 s[1] = '\0'; 487 err |= setvarsafe(optvar, s, 0); 488 if (err) { 489 *optnext = NULL; 490 *optptr = NULL; 491 flushall(); 492 exraise(EXERROR); 493 } 494 return done; 495 } 496 497 /* 498 * XXX - should get rid of. have all builtins use getopt(3). the 499 * library getopt must have the BSD extension static variable "optreset" 500 * otherwise it can't be used within the shell safely. 501 * 502 * Standard option processing (a la getopt) for builtin routines. The 503 * only argument that is passed to nextopt is the option string; the 504 * other arguments are unnecessary. It return the character, or '\0' on 505 * end of input. 506 */ 507 508 int 509 nextopt(char *optstring) 510 { 511 char *p, *q; 512 char c; 513 514 if ((p = optptr) == NULL || *p == '\0') { 515 p = *argptr; 516 if (p == NULL || *p != '-' || *++p == '\0') 517 return '\0'; 518 argptr++; 519 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 520 return '\0'; 521 } 522 c = *p++; 523 for (q = optstring ; *q != c ; ) { 524 if (*q == '\0') 525 error("Illegal option -%c", c); 526 if (*++q == ':') 527 q++; 528 } 529 if (*++q == ':') { 530 if (*p == '\0' && (p = *argptr++) == NULL) 531 error("No arg for -%c option", c); 532 shoptarg = p; 533 p = NULL; 534 } 535 optptr = p; 536 return c; 537 } 538