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