1 /* $NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. 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 <sys/cdefs.h> 36 #ifndef lint 37 #if 0 38 static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 39 #else 40 __RCSID("$NetBSD: options.c,v 1.37 2004/10/30 19:29:27 christos Exp $"); 41 #endif 42 #endif /* not lint */ 43 44 #include <signal.h> 45 #include <unistd.h> 46 #include <stdlib.h> 47 48 #include "shell.h" 49 #define DEFINE_OPTIONS 50 #include "options.h" 51 #undef DEFINE_OPTIONS 52 #include "nodes.h" /* for other header files */ 53 #include "eval.h" 54 #include "jobs.h" 55 #include "input.h" 56 #include "output.h" 57 #include "trap.h" 58 #include "var.h" 59 #include "memalloc.h" 60 #include "error.h" 61 #include "mystring.h" 62 #ifndef SMALL 63 #include "myhistedit.h" 64 #endif 65 #include "show.h" 66 67 char *arg0; /* value of $0 */ 68 struct shparam shellparam; /* current positional parameters */ 69 char **argptr; /* argument list for builtin commands */ 70 char *optionarg; /* 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 options(1); 97 if (*argptr == NULL && minusc == NULL) 98 sflag = 1; 99 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 100 iflag = 1; 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 #if DEBUG == 2 107 debug = 1; 108 #endif 109 arg0 = argv[0]; 110 if (sflag == 0 && minusc == NULL) { 111 commandname = argv[0]; 112 arg0 = *argptr++; 113 setinputfile(arg0, 0); 114 commandname = arg0; 115 } 116 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 117 if (minusc != NULL) { 118 if (argptr == NULL || *argptr == NULL) 119 error("Bad -c option"); 120 minusc = *argptr++; 121 if (*argptr != 0) 122 arg0 = *argptr++; 123 } 124 125 shellparam.p = argptr; 126 shellparam.reset = 1; 127 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 128 while (*argptr) { 129 shellparam.nparam++; 130 argptr++; 131 } 132 optschanged(); 133 } 134 135 136 void 137 optschanged(void) 138 { 139 setinteractive(iflag); 140 #ifdef WITH_HISTORY 141 histedit(); 142 #endif 143 setjobctl(mflag); 144 } 145 146 /* 147 * Process shell options. The global variable argptr contains a pointer 148 * to the argument list; we advance it past the options. 149 */ 150 151 STATIC void 152 options(int cmdline) 153 { 154 static char empty[] = ""; 155 char *p; 156 int val; 157 int c; 158 159 if (cmdline) 160 minusc = NULL; 161 while ((p = *argptr) != NULL) { 162 argptr++; 163 if ((c = *p++) == '-') { 164 val = 1; 165 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 166 if (!cmdline) { 167 /* "-" means turn off -x and -v */ 168 if (p[0] == '\0') 169 xflag = vflag = 0; 170 /* "--" means reset params */ 171 else if (*argptr == NULL) 172 setparam(argptr); 173 } 174 break; /* "-" or "--" terminates options */ 175 } 176 } else if (c == '+') { 177 val = 0; 178 } else { 179 argptr--; 180 break; 181 } 182 while ((c = *p++) != '\0') { 183 if (c == 'c' && cmdline) { 184 /* command is after shell args*/ 185 minusc = empty; 186 } else if (c == 'o') { 187 minus_o(*argptr, val); 188 if (*argptr) 189 argptr++; 190 } else { 191 setoption(c, val); 192 } 193 } 194 } 195 } 196 197 static void 198 set_opt_val(int i, int val) 199 { 200 int j; 201 int flag; 202 203 if (val && (flag = optlist[i].opt_set)) { 204 /* some options (eg vi/emacs) are mutually exclusive */ 205 for (j = 0; j < NOPTS; j++) 206 if (optlist[j].opt_set == flag) 207 optlist[j].val = 0; 208 } 209 optlist[i].val = val; 210 #ifdef DEBUG 211 if (&optlist[i].val == &debug) 212 opentrace(); 213 #endif 214 } 215 216 STATIC void 217 minus_o(char *name, int val) 218 { 219 int i; 220 221 if (name == NULL) { 222 out1str("Current option settings\n"); 223 for (i = 0; i < NOPTS; i++) 224 out1fmt("%-16s%s\n", optlist[i].name, 225 optlist[i].val ? "on" : "off"); 226 } else { 227 for (i = 0; i < NOPTS; i++) 228 if (equal(name, optlist[i].name)) { 229 set_opt_val(i, val); 230 return; 231 } 232 error("Illegal option -o %s", name); 233 } 234 } 235 236 237 STATIC void 238 setoption(int flag, int val) 239 { 240 int i; 241 242 for (i = 0; i < NOPTS; i++) 243 if (optlist[i].letter == flag) { 244 set_opt_val( i, val ); 245 return; 246 } 247 error("Illegal option -%c", flag); 248 /* NOTREACHED */ 249 } 250 251 252 253 #ifdef mkinit 254 INCLUDE "options.h" 255 256 SHELLPROC { 257 int i; 258 259 for (i = 0; optlist[i].name; i++) 260 optlist[i].val = 0; 261 optschanged(); 262 263 } 264 #endif 265 266 267 /* 268 * Set the shell parameters. 269 */ 270 271 void 272 setparam(char **argv) 273 { 274 char **newparam; 275 char **ap; 276 int nparam; 277 278 for (nparam = 0 ; argv[nparam] ; nparam++); 279 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 280 while (*argv) { 281 *ap++ = savestr(*argv++); 282 } 283 *ap = NULL; 284 freeparam(&shellparam); 285 shellparam.malloc = 1; 286 shellparam.nparam = nparam; 287 shellparam.p = newparam; 288 shellparam.optnext = NULL; 289 } 290 291 292 /* 293 * Free the list of positional parameters. 294 */ 295 296 void 297 freeparam(volatile struct shparam *param) 298 { 299 char **ap; 300 301 if (param->malloc) { 302 for (ap = param->p ; *ap ; ap++) 303 ckfree(*ap); 304 ckfree(param->p); 305 } 306 } 307 308 309 310 /* 311 * The shift builtin command. 312 */ 313 314 int 315 shiftcmd(int argc, char **argv) 316 { 317 int n; 318 char **ap1, **ap2; 319 320 n = 1; 321 if (argc > 1) 322 n = number(argv[1]); 323 if (n > shellparam.nparam) 324 error("can't shift that many"); 325 INTOFF; 326 shellparam.nparam -= n; 327 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 328 if (shellparam.malloc) 329 ckfree(*ap1); 330 } 331 ap2 = shellparam.p; 332 while ((*ap2++ = *ap1++) != NULL); 333 shellparam.optnext = NULL; 334 INTON; 335 return 0; 336 } 337 338 339 340 /* 341 * The set command builtin. 342 */ 343 344 int 345 setcmd(int argc, char **argv) 346 { 347 if (argc == 1) 348 return showvars(0, 0, 1); 349 INTOFF; 350 options(0); 351 optschanged(); 352 if (*argptr != NULL) { 353 setparam(argptr); 354 } 355 INTON; 356 return 0; 357 } 358 359 360 void 361 getoptsreset(value) 362 const char *value; 363 { 364 if (number(value) == 1) { 365 shellparam.optnext = NULL; 366 shellparam.reset = 1; 367 } 368 } 369 370 /* 371 * The getopts builtin. Shellparam.optnext points to the next argument 372 * to be processed. Shellparam.optptr points to the next character to 373 * be processed in the current argument. If shellparam.optnext is NULL, 374 * then it's the first time getopts has been called. 375 */ 376 377 int 378 getoptscmd(int argc, char **argv) 379 { 380 char **optbase; 381 382 if (argc < 3) 383 error("usage: getopts optstring var [arg]"); 384 else if (argc == 3) 385 optbase = shellparam.p; 386 else 387 optbase = &argv[3]; 388 389 if (shellparam.reset == 1) { 390 shellparam.optnext = optbase; 391 shellparam.optptr = NULL; 392 shellparam.reset = 0; 393 } 394 395 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 396 &shellparam.optptr); 397 } 398 399 STATIC int 400 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr) 401 { 402 char *p, *q; 403 char c = '?'; 404 int done = 0; 405 int ind = 0; 406 int err = 0; 407 char s[12]; 408 409 if ((p = *optpptr) == NULL || *p == '\0') { 410 /* Current word is done, advance */ 411 if (*optnext == NULL) 412 return 1; 413 p = **optnext; 414 if (p == NULL || *p != '-' || *++p == '\0') { 415 atend: 416 ind = *optnext - optfirst + 1; 417 *optnext = NULL; 418 p = NULL; 419 done = 1; 420 goto out; 421 } 422 (*optnext)++; 423 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 424 goto atend; 425 } 426 427 c = *p++; 428 for (q = optstr; *q != c; ) { 429 if (*q == '\0') { 430 if (optstr[0] == ':') { 431 s[0] = c; 432 s[1] = '\0'; 433 err |= setvarsafe("OPTARG", s, 0); 434 } else { 435 outfmt(&errout, "Illegal option -%c\n", c); 436 (void) unsetvar("OPTARG", 0); 437 } 438 c = '?'; 439 goto bad; 440 } 441 if (*++q == ':') 442 q++; 443 } 444 445 if (*++q == ':') { 446 if (*p == '\0' && (p = **optnext) == NULL) { 447 if (optstr[0] == ':') { 448 s[0] = c; 449 s[1] = '\0'; 450 err |= setvarsafe("OPTARG", s, 0); 451 c = ':'; 452 } else { 453 outfmt(&errout, "No arg for -%c option\n", c); 454 (void) unsetvar("OPTARG", 0); 455 c = '?'; 456 } 457 goto bad; 458 } 459 460 if (p == **optnext) 461 (*optnext)++; 462 err |= setvarsafe("OPTARG", p, 0); 463 p = NULL; 464 } else 465 err |= setvarsafe("OPTARG", "", 0); 466 ind = *optnext - optfirst + 1; 467 goto out; 468 469 bad: 470 ind = 1; 471 *optnext = NULL; 472 p = NULL; 473 out: 474 *optpptr = p; 475 fmtstr(s, sizeof(s), "%d", ind); 476 err |= setvarsafe("OPTIND", s, VNOFUNC); 477 s[0] = c; 478 s[1] = '\0'; 479 err |= setvarsafe(optvar, s, 0); 480 if (err) { 481 *optnext = NULL; 482 *optpptr = NULL; 483 flushall(); 484 exraise(EXERROR); 485 } 486 return done; 487 } 488 489 /* 490 * XXX - should get rid of. have all builtins use getopt(3). the 491 * library getopt must have the BSD extension static variable "optreset" 492 * otherwise it can't be used within the shell safely. 493 * 494 * Standard option processing (a la getopt) for builtin routines. The 495 * only argument that is passed to nextopt is the option string; the 496 * other arguments are unnecessary. It return the character, or '\0' on 497 * end of input. 498 */ 499 500 int 501 nextopt(const char *optstring) 502 { 503 char *p; 504 const char *q; 505 char c; 506 507 if ((p = optptr) == NULL || *p == '\0') { 508 p = *argptr; 509 if (p == NULL || *p != '-' || *++p == '\0') 510 return '\0'; 511 argptr++; 512 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 513 return '\0'; 514 } 515 c = *p++; 516 for (q = optstring ; *q != c ; ) { 517 if (*q == '\0') 518 error("Illegal option -%c", c); 519 if (*++q == ':') 520 q++; 521 } 522 if (*++q == ':') { 523 if (*p == '\0' && (p = *argptr++) == NULL) 524 error("No arg for -%c option", c); 525 optionarg = p; 526 p = NULL; 527 } 528 optptr = p; 529 return c; 530 } 531