options.c revision 206182
1321964Ssjg/*- 2310299Ssjg * Copyright (c) 1991, 1993 3310299Ssjg * The Regents of the University of California. All rights reserved. 4310299Ssjg * 5310299Ssjg * This code is derived from software contributed to Berkeley by 6310299Ssjg * Kenneth Almquist. 7310299Ssjg * 8310299Ssjg * Redistribution and use in source and binary forms, with or without 9310299Ssjg * modification, are permitted provided that the following conditions 10310299Ssjg * are met: 11310299Ssjg * 1. Redistributions of source code must retain the above copyright 12310299Ssjg * notice, this list of conditions and the following disclaimer. 13310299Ssjg * 2. Redistributions in binary form must reproduce the above copyright 14310299Ssjg * notice, this list of conditions and the following disclaimer in the 15310299Ssjg * documentation and/or other materials provided with the distribution. 16310299Ssjg * 4. Neither the name of the University nor the names of its contributors 17310299Ssjg * may be used to endorse or promote products derived from this software 18310299Ssjg * without specific prior written permission. 19310299Ssjg * 20310299Ssjg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21310299Ssjg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22310299Ssjg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23310299Ssjg * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24310299Ssjg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25310299Ssjg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26310299Ssjg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27310299Ssjg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28310299Ssjg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29310299Ssjg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30310299Ssjg * SUCH DAMAGE. 31310299Ssjg */ 32310299Ssjg 33310299Ssjg#ifndef lint 34310299Ssjg#if 0 35310299Ssjgstatic char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 36310299Ssjg#endif 37310299Ssjg#endif /* not lint */ 38310299Ssjg#include <sys/cdefs.h> 39310299Ssjg__FBSDID("$FreeBSD: head/bin/sh/options.c 206182 2010-04-05 14:15:51Z jilles $"); 40310299Ssjg 41310299Ssjg#include <signal.h> 42310299Ssjg#include <unistd.h> 43310299Ssjg#include <stdlib.h> 44310299Ssjg 45310299Ssjg#include "shell.h" 46310299Ssjg#define DEFINE_OPTIONS 47310299Ssjg#include "options.h" 48310299Ssjg#undef DEFINE_OPTIONS 49310299Ssjg#include "nodes.h" /* for other header files */ 50310299Ssjg#include "eval.h" 51310299Ssjg#include "jobs.h" 52310299Ssjg#include "input.h" 53310299Ssjg#include "output.h" 54310299Ssjg#include "trap.h" 55310299Ssjg#include "var.h" 56310299Ssjg#include "memalloc.h" 57310299Ssjg#include "error.h" 58310299Ssjg#include "mystring.h" 59310299Ssjg#ifndef NO_HISTORY 60310299Ssjg#include "myhistedit.h" 61310299Ssjg#endif 62310299Ssjg 63310299Ssjgchar *arg0; /* value of $0 */ 64310299Ssjgstruct shparam shellparam; /* current positional parameters */ 65310299Ssjgchar **argptr; /* argument list for builtin commands */ 66310299Ssjgchar *shoptarg; /* set by nextopt (like getopt) */ 67310299Ssjgchar *nextopt_optptr; /* used by nextopt */ 68310299Ssjg 69310299Ssjgchar *minusc; /* argument to -c option */ 70321964Ssjg 71321964Ssjg 72321964SsjgSTATIC void options(int); 73321964SsjgSTATIC void minus_o(char *, int); 74321964SsjgSTATIC void setoption(int, int); 75321964SsjgSTATIC int getopts(char *, char *, char **, char ***, char **); 76321964Ssjg 77321964Ssjg 78321964Ssjg/* 79310299Ssjg * Process the shell command line arguments. 80310299Ssjg */ 81 82void 83procargs(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 != 0 && sflag == 1 && isatty(0) && isatty(1)) { 97 iflag = 1; 98 if (Eflag == 2) 99 Eflag = 1; 100 } 101 if (mflag == 2) 102 mflag = iflag; 103 for (i = 0; i < NOPTS; i++) 104 if (optlist[i].val == 2) 105 optlist[i].val = 0; 106 arg0 = argv[0]; 107 if (sflag == 0 && minusc == NULL) { 108 commandname = arg0 = *argptr++; 109 setinputfile(commandname, 0); 110 } 111 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 112 if (argptr && minusc && *argptr) 113 arg0 = *argptr++; 114 115 shellparam.p = argptr; 116 shellparam.reset = 1; 117 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 118 while (*argptr) { 119 shellparam.nparam++; 120 argptr++; 121 } 122 optschanged(); 123} 124 125 126void 127optschanged(void) 128{ 129 setinteractive(iflag); 130#ifndef NO_HISTORY 131 histedit(); 132#endif 133 setjobctl(mflag); 134} 135 136/* 137 * Process shell options. The global variable argptr contains a pointer 138 * to the argument list; we advance it past the options. 139 */ 140 141STATIC void 142options(int cmdline) 143{ 144 char *kp, *p; 145 int val; 146 int c; 147 148 if (cmdline) 149 minusc = NULL; 150 while ((p = *argptr) != NULL) { 151 argptr++; 152 if ((c = *p++) == '-') { 153 val = 1; 154 /* A "-" or "--" terminates options */ 155 if (p[0] == '\0') 156 goto end_options1; 157 if (p[0] == '-' && p[1] == '\0') 158 goto end_options2; 159 /** 160 * For the benefit of `#!' lines in shell scripts, 161 * treat a string of '-- *#.*' the same as '--'. 162 * This is needed so that a script starting with: 163 * #!/bin/sh -- # -*- perl -*- 164 * will continue to work after a change is made to 165 * kern/imgact_shell.c to NOT token-ize the options 166 * specified on a '#!' line. A bit of a kludge, 167 * but that trick is recommended in documentation 168 * for some scripting languages, and we might as 169 * well continue to support it. 170 */ 171 if (p[0] == '-') { 172 kp = p + 1; 173 while (*kp == ' ' || *kp == '\t') 174 kp++; 175 if (*kp == '#' || *kp == '\0') 176 goto end_options2; 177 } 178 } else if (c == '+') { 179 val = 0; 180 } else { 181 argptr--; 182 break; 183 } 184 while ((c = *p++) != '\0') { 185 if (c == 'c' && cmdline) { 186 char *q; 187#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 188 if (*p == '\0') 189#endif 190 q = *argptr++; 191 if (q == NULL || minusc != NULL) 192 error("Bad -c option"); 193 minusc = q; 194#ifdef NOHACK 195 break; 196#endif 197 } else if (c == 'o') { 198 minus_o(*argptr, val); 199 if (*argptr) 200 argptr++; 201 } else { 202 if (c == 'p' && !val && privileged) { 203 (void) setuid(getuid()); 204 (void) setgid(getgid()); 205 } 206 setoption(c, val); 207 } 208 } 209 } 210 return; 211 212 /* When processing `set', a single "-" means turn off -x and -v */ 213end_options1: 214 if (!cmdline) { 215 xflag = vflag = 0; 216 return; 217 } 218 219 /* 220 * When processing `set', a "--" means the remaining arguments 221 * replace the positional parameters in the active shell. If 222 * there are no remaining options, then all the positional 223 * parameters are cleared (equivalent to doing ``shift $#''). 224 */ 225end_options2: 226 if (!cmdline) { 227 if (*argptr == NULL) 228 setparam(argptr); 229 return; 230 } 231 232 /* 233 * At this point we are processing options given to 'sh' on a command 234 * line. If an end-of-options marker ("-" or "--") is followed by an 235 * arg of "#", then skip over all remaining arguments. Some scripting 236 * languages (e.g.: perl) document that /bin/sh will implement this 237 * behavior, and they recommend that users take advantage of it to 238 * solve certain issues that can come up when writing a perl script. 239 * Yes, this feature is in /bin/sh to help users write perl scripts. 240 */ 241 p = *argptr; 242 if (p != NULL && p[0] == '#' && p[1] == '\0') { 243 while (*argptr != NULL) 244 argptr++; 245 /* We need to keep the final argument */ 246 argptr--; 247 } 248} 249 250STATIC void 251minus_o(char *name, int val) 252{ 253 int i; 254 255 if (name == NULL) { 256 if (val) { 257 /* "Pretty" output. */ 258 out1str("Current option settings\n"); 259 for (i = 0; i < NOPTS; i++) 260 out1fmt("%-16s%s\n", optlist[i].name, 261 optlist[i].val ? "on" : "off"); 262 } else { 263 /* Output suitable for re-input to shell. */ 264 for (i = 0; i < NOPTS; i++) { 265 if (i % 6 == 0) 266 out1str(i == 0 ? "set" : "\nset"); 267 out1fmt(" %co %s", optlist[i].val ? '-' : '+', 268 optlist[i].name); 269 } 270 out1c('\n'); 271 } 272 } else { 273 for (i = 0; i < NOPTS; i++) 274 if (equal(name, optlist[i].name)) { 275 if (!val && privileged && equal(name, "privileged")) { 276 (void) setuid(getuid()); 277 (void) setgid(getgid()); 278 } 279 setoption(optlist[i].letter, val); 280 return; 281 } 282 error("Illegal option -o %s", name); 283 } 284} 285 286 287STATIC void 288setoption(int flag, int val) 289{ 290 int i; 291 292 for (i = 0; i < NOPTS; i++) 293 if (optlist[i].letter == flag) { 294 optlist[i].val = val; 295 if (val) { 296 /* #%$ hack for ksh semantics */ 297 if (flag == 'V') 298 Eflag = 0; 299 else if (flag == 'E') 300 Vflag = 0; 301 } 302 return; 303 } 304 error("Illegal option -%c", flag); 305} 306 307 308 309#ifdef mkinit 310INCLUDE "options.h" 311 312SHELLPROC { 313 int i; 314 315 for (i = 0; i < NOPTS; i++) 316 optlist[i].val = 0; 317 optschanged(); 318 319} 320#endif 321 322 323/* 324 * Set the shell parameters. 325 */ 326 327void 328setparam(char **argv) 329{ 330 char **newparam; 331 char **ap; 332 int nparam; 333 334 for (nparam = 0 ; argv[nparam] ; nparam++); 335 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 336 while (*argv) { 337 *ap++ = savestr(*argv++); 338 } 339 *ap = NULL; 340 freeparam(&shellparam); 341 shellparam.malloc = 1; 342 shellparam.nparam = nparam; 343 shellparam.p = newparam; 344 shellparam.reset = 1; 345 shellparam.optnext = NULL; 346} 347 348 349/* 350 * Free the list of positional parameters. 351 */ 352 353void 354freeparam(struct shparam *param) 355{ 356 char **ap; 357 358 if (param->malloc) { 359 for (ap = param->p ; *ap ; ap++) 360 ckfree(*ap); 361 ckfree(param->p); 362 } 363} 364 365 366 367/* 368 * The shift builtin command. 369 */ 370 371int 372shiftcmd(int argc, char **argv) 373{ 374 int n; 375 char **ap1, **ap2; 376 377 n = 1; 378 if (argc > 1) 379 n = number(argv[1]); 380 if (n > shellparam.nparam) 381 return 1; 382 INTOFF; 383 shellparam.nparam -= n; 384 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 385 if (shellparam.malloc) 386 ckfree(*ap1); 387 } 388 ap2 = shellparam.p; 389 while ((*ap2++ = *ap1++) != NULL); 390 shellparam.reset = 1; 391 INTON; 392 return 0; 393} 394 395 396 397/* 398 * The set command builtin. 399 */ 400 401int 402setcmd(int argc, char **argv) 403{ 404 if (argc == 1) 405 return showvarscmd(argc, argv); 406 INTOFF; 407 options(0); 408 optschanged(); 409 if (*argptr != NULL) { 410 setparam(argptr); 411 } 412 INTON; 413 return 0; 414} 415 416 417void 418getoptsreset(const char *value) 419{ 420 if (number(value) == 1) { 421 shellparam.reset = 1; 422 } 423} 424 425/* 426 * The getopts builtin. Shellparam.optnext points to the next argument 427 * to be processed. Shellparam.optptr points to the next character to 428 * be processed in the current argument. If shellparam.optnext is NULL, 429 * then it's the first time getopts has been called. 430 */ 431 432int 433getoptscmd(int argc, char **argv) 434{ 435 char **optbase = NULL; 436 437 if (argc < 3) 438 error("usage: getopts optstring var [arg]"); 439 else if (argc == 3) 440 optbase = shellparam.p; 441 else 442 optbase = &argv[3]; 443 444 if (shellparam.reset == 1) { 445 shellparam.optnext = optbase; 446 shellparam.optptr = NULL; 447 shellparam.reset = 0; 448 } 449 450 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 451 &shellparam.optptr); 452} 453 454STATIC int 455getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, 456 char **optptr) 457{ 458 char *p, *q; 459 char c = '?'; 460 int done = 0; 461 int ind = 0; 462 int err = 0; 463 char s[10]; 464 465 if ((p = *optptr) == NULL || *p == '\0') { 466 /* Current word is done, advance */ 467 if (*optnext == NULL) 468 return 1; 469 p = **optnext; 470 if (p == NULL || *p != '-' || *++p == '\0') { 471atend: 472 ind = *optnext - optfirst + 1; 473 *optnext = NULL; 474 p = NULL; 475 done = 1; 476 goto out; 477 } 478 (*optnext)++; 479 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 480 goto atend; 481 } 482 483 c = *p++; 484 for (q = optstr; *q != c; ) { 485 if (*q == '\0') { 486 if (optstr[0] == ':') { 487 s[0] = c; 488 s[1] = '\0'; 489 err |= setvarsafe("OPTARG", s, 0); 490 } 491 else { 492 out1fmt("Illegal option -%c\n", c); 493 (void) unsetvar("OPTARG"); 494 } 495 c = '?'; 496 goto bad; 497 } 498 if (*++q == ':') 499 q++; 500 } 501 502 if (*++q == ':') { 503 if (*p == '\0' && (p = **optnext) == NULL) { 504 if (optstr[0] == ':') { 505 s[0] = c; 506 s[1] = '\0'; 507 err |= setvarsafe("OPTARG", s, 0); 508 c = ':'; 509 } 510 else { 511 out1fmt("No arg for -%c option\n", c); 512 (void) unsetvar("OPTARG"); 513 c = '?'; 514 } 515 goto bad; 516 } 517 518 if (p == **optnext) 519 (*optnext)++; 520 setvarsafe("OPTARG", p, 0); 521 p = NULL; 522 } 523 else 524 setvarsafe("OPTARG", "", 0); 525 ind = *optnext - optfirst + 1; 526 goto out; 527 528bad: 529 ind = 1; 530 *optnext = NULL; 531 p = NULL; 532out: 533 *optptr = p; 534 fmtstr(s, sizeof(s), "%d", ind); 535 err |= setvarsafe("OPTIND", s, VNOFUNC); 536 s[0] = c; 537 s[1] = '\0'; 538 err |= setvarsafe(optvar, s, 0); 539 if (err) { 540 *optnext = NULL; 541 *optptr = NULL; 542 flushall(); 543 exraise(EXERROR); 544 } 545 return done; 546} 547 548/* 549 * XXX - should get rid of. have all builtins use getopt(3). the 550 * library getopt must have the BSD extension static variable "optreset" 551 * otherwise it can't be used within the shell safely. 552 * 553 * Standard option processing (a la getopt) for builtin routines. The 554 * only argument that is passed to nextopt is the option string; the 555 * other arguments are unnecessary. It return the character, or '\0' on 556 * end of input. 557 */ 558 559int 560nextopt(const char *optstring) 561{ 562 char *p; 563 const char *q; 564 char c; 565 566 if ((p = nextopt_optptr) == NULL || *p == '\0') { 567 p = *argptr; 568 if (p == NULL || *p != '-' || *++p == '\0') 569 return '\0'; 570 argptr++; 571 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 572 return '\0'; 573 } 574 c = *p++; 575 for (q = optstring ; *q != c ; ) { 576 if (*q == '\0') 577 error("Illegal option -%c", c); 578 if (*++q == ':') 579 q++; 580 } 581 if (*++q == ':') { 582 if (*p == '\0' && (p = *argptr++) == NULL) 583 error("No arg for -%c option", c); 584 shoptarg = p; 585 p = NULL; 586 } 587 nextopt_optptr = p; 588 return c; 589} 590