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