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 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 40 #endif 41 #endif /* not lint */ 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include <signal.h> 46 #include <unistd.h> 47 #include <stdlib.h> 48 49 #include "shell.h" 50 #define DEFINE_OPTIONS 51 #include "options.h" 52 #undef DEFINE_OPTIONS 53 #include "nodes.h" /* for other header files */ 54 #include "eval.h" 55 #include "jobs.h" 56 #include "input.h" 57 #include "output.h" 58 #include "trap.h" 59 #include "var.h" 60 #include "memalloc.h" 61 #include "error.h" 62 #include "mystring.h" 63 #ifndef NO_HISTORY 64 #include "myhistedit.h" 65 #endif 66 67 char *arg0; /* value of $0 */ 68 struct shparam shellparam; /* current positional parameters */ 69 char **argptr; /* argument list for builtin commands */ 70 char *shoptarg; /* set by nextopt (like getopt) */ 71 char *optptr; /* used by nextopt */ 72 73 char *minusc; /* argument to -c option */ 74 75 76 STATIC void options(int); 77 STATIC void minus_o(char *, int); 78 STATIC void setoption(int, int); 79 STATIC int getopts(char *, char *, char **, char ***, char **); 80 81 82 /* 83 * Process the shell command line arguments. 84 */ 85 86 void 87 procargs(int argc, char **argv) 88 { 89 int i; 90 91 argptr = argv; 92 if (argc > 0) 93 argptr++; 94 for (i = 0; i < NOPTS; i++) 95 optlist[i].val = 2; 96 privileged = (getuid() != geteuid() || getgid() != getegid()); 97 options(1); 98 if (*argptr == NULL && minusc == NULL) 99 sflag = 1; 100 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 101 iflag = 1; 102 if (mflag == 2) 103 mflag = iflag; 104 for (i = 0; i < NOPTS; i++) 105 if (optlist[i].val == 2) 106 optlist[i].val = 0; 107 arg0 = argv[0]; 108 if (sflag == 0 && minusc == NULL) { 109 commandname = arg0 = *argptr++; 110 setinputfile(commandname, 0); 111 } 112 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 113 if (argptr && minusc && *argptr) 114 arg0 = *argptr++; 115 116 shellparam.p = argptr; 117 shellparam.reset = 1; 118 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 119 while (*argptr) { 120 shellparam.nparam++; 121 argptr++; 122 } 123 optschanged(); 124 } 125 126 127 void 128 optschanged(void) 129 { 130 setinteractive(iflag); 131 #ifndef NO_HISTORY 132 histedit(); 133 #endif 134 setjobctl(mflag); 135 } 136 137 /* 138 * Process shell options. The global variable argptr contains a pointer 139 * to the argument list; we advance it past the options. 140 */ 141 142 STATIC void 143 options(int cmdline) 144 { 145 char *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 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 156 if (!cmdline) { 157 /* "-" means turn off -x and -v */ 158 if (p[0] == '\0') 159 xflag = vflag = 0; 160 /* "--" means reset params */ 161 else if (*argptr == NULL) 162 setparam(argptr); 163 } 164 break; /* "-" or "--" terminates options */ 165 } 166 } else if (c == '+') { 167 val = 0; 168 } else { 169 argptr--; 170 break; 171 } 172 while ((c = *p++) != '\0') { 173 if (c == 'c' && cmdline) { 174 char *q; 175 #ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 176 if (*p == '\0') 177 #endif 178 q = *argptr++; 179 if (q == NULL || minusc != NULL) 180 error("Bad -c option"); 181 minusc = q; 182 #ifdef NOHACK 183 break; 184 #endif 185 } else if (c == 'o') { 186 minus_o(*argptr, val); 187 if (*argptr) 188 argptr++; 189 } else { 190 if (c == 'p' && !val && privileged) { 191 (void) setuid(getuid()); 192 (void) setgid(getgid()); 193 } 194 setoption(c, val); 195 } 196 } 197 } 198 } 199 200 STATIC void 201 minus_o(char *name, int val) 202 { 203 int doneset, i; 204 205 if (name == NULL) { 206 if (val) { 207 /* "Pretty" output. */ 208 out1str("Current option settings\n"); 209 for (i = 0; i < NOPTS; i++) 210 out1fmt("%-16s%s\n", optlist[i].name, 211 optlist[i].val ? "on" : "off"); 212 } else { 213 /* Output suitable for re-input to shell. */ 214 for (doneset = i = 0; i < NOPTS; i++) 215 if (optlist[i].val) { 216 if (!doneset) { 217 out1str("set"); 218 doneset = 1; 219 } 220 out1fmt(" -o %s", optlist[i].name); 221 } 222 if (doneset) 223 out1c('\n'); 224 } 225 } else { 226 for (i = 0; i < NOPTS; i++) 227 if (equal(name, optlist[i].name)) { 228 if (!val && privileged && equal(name, "privileged")) { 229 (void) setuid(getuid()); 230 (void) setgid(getgid()); 231 } 232 setoption(optlist[i].letter, val); 233 return; 234 } 235 error("Illegal option -o %s", name); 236 } 237 } 238 239 240 STATIC void 241 setoption(int flag, int val) 242 { 243 int i; 244 245 for (i = 0; i < NOPTS; i++) 246 if (optlist[i].letter == flag) { 247 optlist[i].val = val; 248 if (val) { 249 /* #%$ hack for ksh semantics */ 250 if (flag == 'V') 251 Eflag = 0; 252 else if (flag == 'E') 253 Vflag = 0; 254 } 255 return; 256 } 257 error("Illegal option -%c", flag); 258 } 259 260 261 262 #ifdef mkinit 263 INCLUDE "options.h" 264 265 SHELLPROC { 266 int i; 267 268 for (i = 0; i < NOPTS; i++) 269 optlist[i].val = 0; 270 optschanged(); 271 272 } 273 #endif 274 275 276 /* 277 * Set the shell parameters. 278 */ 279 280 void 281 setparam(char **argv) 282 { 283 char **newparam; 284 char **ap; 285 int nparam; 286 287 for (nparam = 0 ; argv[nparam] ; nparam++); 288 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 289 while (*argv) { 290 *ap++ = savestr(*argv++); 291 } 292 *ap = NULL; 293 freeparam(&shellparam); 294 shellparam.malloc = 1; 295 shellparam.nparam = nparam; 296 shellparam.p = newparam; 297 shellparam.optnext = NULL; 298 } 299 300 301 /* 302 * Free the list of positional parameters. 303 */ 304 305 void 306 freeparam(struct shparam *param) 307 { 308 char **ap; 309 310 if (param->malloc) { 311 for (ap = param->p ; *ap ; ap++) 312 ckfree(*ap); 313 ckfree(param->p); 314 } 315 } 316 317 318 319 /* 320 * The shift builtin command. 321 */ 322 323 int 324 shiftcmd(int argc, char **argv) 325 { 326 int n; 327 char **ap1, **ap2; 328 329 n = 1; 330 if (argc > 1) 331 n = number(argv[1]); 332 if (n > shellparam.nparam) 333 error("can't shift that many"); 334 INTOFF; 335 shellparam.nparam -= n; 336 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 337 if (shellparam.malloc) 338 ckfree(*ap1); 339 } 340 ap2 = shellparam.p; 341 while ((*ap2++ = *ap1++) != NULL); 342 shellparam.optnext = NULL; 343 INTON; 344 return 0; 345 } 346 347 348 349 /* 350 * The set command builtin. 351 */ 352 353 int 354 setcmd(int argc, char **argv) 355 { 356 if (argc == 1) 357 return showvarscmd(argc, argv); 358 INTOFF; 359 options(0); 360 optschanged(); 361 if (*argptr != NULL) { 362 setparam(argptr); 363 } 364 INTON; 365 return 0; 366 } 367 368 369 void 370 getoptsreset(const char *value) 371 { 372 if (number(value) == 1) { 373 shellparam.optnext = NULL; 374 shellparam.reset = 1; 375 } 376 } 377 378 /* 379 * The getopts builtin. Shellparam.optnext points to the next argument 380 * to be processed. Shellparam.optptr points to the next character to 381 * be processed in the current argument. If shellparam.optnext is NULL, 382 * then it's the first time getopts has been called. 383 */ 384 385 int 386 getoptscmd(int argc, char **argv) 387 { 388 char **optbase = NULL; 389 390 if (argc < 3) 391 error("usage: getopts optstring var [arg]"); 392 else if (argc == 3) 393 optbase = shellparam.p; 394 else 395 optbase = &argv[3]; 396 397 if (shellparam.reset == 1) { 398 shellparam.optnext = optbase; 399 shellparam.optptr = NULL; 400 shellparam.reset = 0; 401 } 402 403 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 404 &shellparam.optptr); 405 } 406 407 STATIC int 408 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, 409 char **optptr) 410 { 411 char *p, *q; 412 char c = '?'; 413 int done = 0; 414 int ind = 0; 415 int err = 0; 416 char s[10]; 417 418 if ((p = *optptr) == NULL || *p == '\0') { 419 /* Current word is done, advance */ 420 if (*optnext == NULL) 421 return 1; 422 p = **optnext; 423 if (p == NULL || *p != '-' || *++p == '\0') { 424 atend: 425 ind = *optnext - optfirst + 1; 426 *optnext = NULL; 427 p = NULL; 428 done = 1; 429 goto out; 430 } 431 (*optnext)++; 432 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 433 goto atend; 434 } 435 436 c = *p++; 437 for (q = optstr; *q != c; ) { 438 if (*q == '\0') { 439 if (optstr[0] == ':') { 440 s[0] = c; 441 s[1] = '\0'; 442 err |= setvarsafe("OPTARG", s, 0); 443 } 444 else { 445 out1fmt("Illegal option -%c\n", c); 446 (void) unsetvar("OPTARG"); 447 } 448 c = '?'; 449 goto bad; 450 } 451 if (*++q == ':') 452 q++; 453 } 454 455 if (*++q == ':') { 456 if (*p == '\0' && (p = **optnext) == NULL) { 457 if (optstr[0] == ':') { 458 s[0] = c; 459 s[1] = '\0'; 460 err |= setvarsafe("OPTARG", s, 0); 461 c = ':'; 462 } 463 else { 464 out1fmt("No arg for -%c option\n", c); 465 (void) unsetvar("OPTARG"); 466 c = '?'; 467 } 468 goto bad; 469 } 470 471 if (p == **optnext) 472 (*optnext)++; 473 setvarsafe("OPTARG", p, 0); 474 p = NULL; 475 } 476 else 477 setvarsafe("OPTARG", "", 0); 478 ind = *optnext - optfirst + 1; 479 goto out; 480 481 bad: 482 ind = 1; 483 *optnext = NULL; 484 p = NULL; 485 out: 486 *optptr = p; 487 fmtstr(s, sizeof(s), "%d", ind); 488 err |= setvarsafe("OPTIND", s, VNOFUNC); 489 s[0] = c; 490 s[1] = '\0'; 491 err |= setvarsafe(optvar, s, 0); 492 if (err) { 493 *optnext = NULL; 494 *optptr = NULL; 495 flushall(); 496 exraise(EXERROR); 497 } 498 return done; 499 } 500 501 /* 502 * XXX - should get rid of. have all builtins use getopt(3). the 503 * library getopt must have the BSD extension static variable "optreset" 504 * otherwise it can't be used within the shell safely. 505 * 506 * Standard option processing (a la getopt) for builtin routines. The 507 * only argument that is passed to nextopt is the option string; the 508 * other arguments are unnecessary. It return the character, or '\0' on 509 * end of input. 510 */ 511 512 int 513 nextopt(char *optstring) 514 { 515 char *p, *q; 516 char c; 517 518 if ((p = optptr) == NULL || *p == '\0') { 519 p = *argptr; 520 if (p == NULL || *p != '-' || *++p == '\0') 521 return '\0'; 522 argptr++; 523 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 524 return '\0'; 525 } 526 c = *p++; 527 for (q = optstring ; *q != c ; ) { 528 if (*q == '\0') 529 error("Illegal option -%c", c); 530 if (*++q == ':') 531 q++; 532 } 533 if (*++q == ':') { 534 if (*p == '\0' && (p = *argptr++) == NULL) 535 error("No arg for -%c option", c); 536 shoptarg = p; 537 p = NULL; 538 } 539 optptr = p; 540 return c; 541 } 542