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(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 i; 204 205 if (name == NULL) { 206 out1str("Current option settings\n"); 207 for (i = 0; i < NOPTS; i++) 208 out1fmt("%-16s%s\n", optlist[i].name, 209 optlist[i].val ? "on" : "off"); 210 } else { 211 for (i = 0; i < NOPTS; i++) 212 if (equal(name, optlist[i].name)) { 213 if (!val && privileged && equal(name, "privileged")) { 214 (void) setuid(getuid()); 215 (void) setgid(getgid()); 216 } 217 setoption(optlist[i].letter, val); 218 return; 219 } 220 error("Illegal option -o %s", name); 221 } 222 } 223 224 225 STATIC void 226 setoption(int flag, int val) 227 { 228 int i; 229 230 for (i = 0; i < NOPTS; i++) 231 if (optlist[i].letter == flag) { 232 optlist[i].val = val; 233 if (val) { 234 /* #%$ hack for ksh semantics */ 235 if (flag == 'V') 236 Eflag = 0; 237 else if (flag == 'E') 238 Vflag = 0; 239 } 240 return; 241 } 242 error("Illegal option -%c", flag); 243 } 244 245 246 247 #ifdef mkinit 248 INCLUDE "options.h" 249 250 SHELLPROC { 251 int i; 252 253 for (i = 0; i < NOPTS; i++) 254 optlist[i].val = 0; 255 optschanged(); 256 257 } 258 #endif 259 260 261 /* 262 * Set the shell parameters. 263 */ 264 265 void 266 setparam(char **argv) 267 { 268 char **newparam; 269 char **ap; 270 int nparam; 271 272 for (nparam = 0 ; argv[nparam] ; nparam++); 273 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 274 while (*argv) { 275 *ap++ = savestr(*argv++); 276 } 277 *ap = NULL; 278 freeparam(&shellparam); 279 shellparam.malloc = 1; 280 shellparam.nparam = nparam; 281 shellparam.p = newparam; 282 shellparam.optnext = NULL; 283 } 284 285 286 /* 287 * Free the list of positional parameters. 288 */ 289 290 void 291 freeparam(struct shparam *param) 292 { 293 char **ap; 294 295 if (param->malloc) { 296 for (ap = param->p ; *ap ; ap++) 297 ckfree(*ap); 298 ckfree(param->p); 299 } 300 } 301 302 303 304 /* 305 * The shift builtin command. 306 */ 307 308 int 309 shiftcmd(int argc, char **argv) 310 { 311 int n; 312 char **ap1, **ap2; 313 314 n = 1; 315 if (argc > 1) 316 n = number(argv[1]); 317 if (n > shellparam.nparam) 318 error("can't shift that many"); 319 INTOFF; 320 shellparam.nparam -= n; 321 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 322 if (shellparam.malloc) 323 ckfree(*ap1); 324 } 325 ap2 = shellparam.p; 326 while ((*ap2++ = *ap1++) != NULL); 327 shellparam.optnext = NULL; 328 INTON; 329 return 0; 330 } 331 332 333 334 /* 335 * The set command builtin. 336 */ 337 338 int 339 setcmd(int argc, char **argv) 340 { 341 if (argc == 1) 342 return showvarscmd(argc, argv); 343 INTOFF; 344 options(0); 345 optschanged(); 346 if (*argptr != NULL) { 347 setparam(argptr); 348 } 349 INTON; 350 return 0; 351 } 352 353 354 void 355 getoptsreset(const char *value) 356 { 357 if (number(value) == 1) { 358 shellparam.optnext = NULL; 359 shellparam.reset = 1; 360 } 361 } 362 363 /* 364 * The getopts builtin. Shellparam.optnext points to the next argument 365 * to be processed. Shellparam.optptr points to the next character to 366 * be processed in the current argument. If shellparam.optnext is NULL, 367 * then it's the first time getopts has been called. 368 */ 369 370 int 371 getoptscmd(int argc, char **argv) 372 { 373 char **optbase = NULL; 374 375 if (argc < 3) 376 error("usage: getopts optstring var [arg]"); 377 else if (argc == 3) 378 optbase = shellparam.p; 379 else 380 optbase = &argv[3]; 381 382 if (shellparam.reset == 1) { 383 shellparam.optnext = optbase; 384 shellparam.optptr = NULL; 385 shellparam.reset = 0; 386 } 387 388 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 389 &shellparam.optptr); 390 } 391 392 STATIC int 393 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, 394 char **optptr) 395 { 396 char *p, *q; 397 char c = '?'; 398 int done = 0; 399 int ind = 0; 400 int err = 0; 401 char s[10]; 402 403 if ((p = *optptr) == NULL || *p == '\0') { 404 /* Current word is done, advance */ 405 if (*optnext == NULL) 406 return 1; 407 p = **optnext; 408 if (p == NULL || *p != '-' || *++p == '\0') { 409 atend: 410 ind = *optnext - optfirst + 1; 411 *optnext = NULL; 412 p = NULL; 413 done = 1; 414 goto out; 415 } 416 (*optnext)++; 417 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 418 goto atend; 419 } 420 421 c = *p++; 422 for (q = optstr; *q != c; ) { 423 if (*q == '\0') { 424 if (optstr[0] == ':') { 425 s[0] = c; 426 s[1] = '\0'; 427 err |= setvarsafe("OPTARG", s, 0); 428 } 429 else { 430 out1fmt("Illegal option -%c\n", c); 431 (void) unsetvar("OPTARG"); 432 } 433 c = '?'; 434 goto bad; 435 } 436 if (*++q == ':') 437 q++; 438 } 439 440 if (*++q == ':') { 441 if (*p == '\0' && (p = **optnext) == NULL) { 442 if (optstr[0] == ':') { 443 s[0] = c; 444 s[1] = '\0'; 445 err |= setvarsafe("OPTARG", s, 0); 446 c = ':'; 447 } 448 else { 449 out1fmt("No arg for -%c option\n", c); 450 (void) unsetvar("OPTARG"); 451 c = '?'; 452 } 453 goto bad; 454 } 455 456 if (p == **optnext) 457 (*optnext)++; 458 setvarsafe("OPTARG", p, 0); 459 p = NULL; 460 } 461 else 462 setvarsafe("OPTARG", "", 0); 463 ind = *optnext - optfirst + 1; 464 goto out; 465 466 bad: 467 ind = 1; 468 *optnext = NULL; 469 p = NULL; 470 out: 471 *optptr = p; 472 fmtstr(s, sizeof(s), "%d", ind); 473 err |= setvarsafe("OPTIND", s, VNOFUNC); 474 s[0] = c; 475 s[1] = '\0'; 476 err |= setvarsafe(optvar, s, 0); 477 if (err) { 478 *optnext = NULL; 479 *optptr = NULL; 480 flushall(); 481 exraise(EXERROR); 482 } 483 return done; 484 } 485 486 /* 487 * XXX - should get rid of. have all builtins use getopt(3). the 488 * library getopt must have the BSD extension static variable "optreset" 489 * otherwise it can't be used within the shell safely. 490 * 491 * Standard option processing (a la getopt) for builtin routines. The 492 * only argument that is passed to nextopt is the option string; the 493 * other arguments are unnecessary. It return the character, or '\0' on 494 * end of input. 495 */ 496 497 int 498 nextopt(char *optstring) 499 { 500 char *p, *q; 501 char c; 502 503 if ((p = optptr) == NULL || *p == '\0') { 504 p = *argptr; 505 if (p == NULL || *p != '-' || *++p == '\0') 506 return '\0'; 507 argptr++; 508 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 509 return '\0'; 510 } 511 c = *p++; 512 for (q = optstring ; *q != c ; ) { 513 if (*q == '\0') 514 error("Illegal option -%c", c); 515 if (*++q == ':') 516 q++; 517 } 518 if (*++q == ':') { 519 if (*p == '\0' && (p = *argptr++) == NULL) 520 error("No arg for -%c option", c); 521 shoptarg = p; 522 p = NULL; 523 } 524 optptr = p; 525 return c; 526 } 527