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