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