1 /* $NetBSD: options.c,v 1.19 1996/11/06 01:17:11 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. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #ifndef lint 40 #if 0 41 static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; 42 #else 43 static char rcsid[] = "$NetBSD: options.c,v 1.19 1996/11/06 01:17:11 christos Exp $"; 44 #endif 45 #endif /* not lint */ 46 47 #include <signal.h> 48 #include <unistd.h> 49 #include <stdlib.h> 50 51 #include "shell.h" 52 #define DEFINE_OPTIONS 53 #include "options.h" 54 #undef DEFINE_OPTIONS 55 #include "nodes.h" /* for other header files */ 56 #include "eval.h" 57 #include "jobs.h" 58 #include "input.h" 59 #include "output.h" 60 #include "trap.h" 61 #include "var.h" 62 #include "memalloc.h" 63 #include "error.h" 64 #include "mystring.h" 65 #ifndef NO_HISTORY 66 #include "myhistedit.h" 67 #endif 68 69 char *arg0; /* value of $0 */ 70 struct shparam shellparam; /* current positional parameters */ 71 char **argptr; /* argument list for builtin commands */ 72 char *optarg; /* set by nextopt (like getopt) */ 73 char *optptr; /* used by nextopt */ 74 75 char *minusc; /* argument to -c option */ 76 77 78 STATIC void options __P((int)); 79 STATIC void minus_o __P((char *, int)); 80 STATIC void setoption __P((int, int)); 81 STATIC int getopts __P((char *, char *, char **, char ***, char **)); 82 83 84 /* 85 * Process the shell command line arguments. 86 */ 87 88 void 89 procargs(argc, argv) 90 int argc; 91 char **argv; 92 { 93 int i; 94 95 argptr = argv; 96 if (argc > 0) 97 argptr++; 98 for (i = 0; i < NOPTS; i++) 99 optlist[i].val = 2; 100 options(1); 101 if (*argptr == NULL && minusc == NULL) 102 sflag = 1; 103 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 104 iflag = 1; 105 if (mflag == 2) 106 mflag = iflag; 107 for (i = 0; i < NOPTS; i++) 108 if (optlist[i].val == 2) 109 optlist[i].val = 0; 110 arg0 = argv[0]; 111 if (sflag == 0 && minusc == NULL) { 112 commandname = arg0 = *argptr++; 113 setinputfile(commandname, 0); 114 } 115 /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ 116 if (argptr && minusc) 117 arg0 = *argptr++; 118 119 shellparam.p = argptr; 120 shellparam.reset = 1; 121 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 122 while (*argptr) { 123 shellparam.nparam++; 124 argptr++; 125 } 126 optschanged(); 127 } 128 129 130 void 131 optschanged() 132 { 133 setinteractive(iflag); 134 #ifndef NO_HISTORY 135 histedit(); 136 #endif 137 setjobctl(mflag); 138 } 139 140 /* 141 * Process shell options. The global variable argptr contains a pointer 142 * to the argument list; we advance it past the options. 143 */ 144 145 STATIC void 146 options(cmdline) 147 int cmdline; 148 { 149 register char *p; 150 int val; 151 int c; 152 153 if (cmdline) 154 minusc = NULL; 155 while ((p = *argptr) != NULL) { 156 argptr++; 157 if ((c = *p++) == '-') { 158 val = 1; 159 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 160 if (!cmdline) { 161 /* "-" means turn off -x and -v */ 162 if (p[0] == '\0') 163 xflag = vflag = 0; 164 /* "--" means reset params */ 165 else if (*argptr == NULL) 166 setparam(argptr); 167 } 168 break; /* "-" or "--" terminates options */ 169 } 170 } else if (c == '+') { 171 val = 0; 172 } else { 173 argptr--; 174 break; 175 } 176 while ((c = *p++) != '\0') { 177 if (c == 'c' && cmdline) { 178 char *q; 179 #ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 180 if (*p == '\0') 181 #endif 182 q = *argptr++; 183 if (q == NULL || minusc != NULL) 184 error("Bad -c option"); 185 minusc = q; 186 #ifdef NOHACK 187 break; 188 #endif 189 } else if (c == 'o') { 190 minus_o(*argptr, val); 191 if (*argptr) 192 argptr++; 193 } else { 194 setoption(c, val); 195 } 196 } 197 } 198 } 199 200 STATIC void 201 minus_o(name, val) 202 char *name; 203 int val; 204 { 205 int i; 206 207 if (name == NULL) { 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 for (i = 0; i < NOPTS; i++) 214 if (equal(name, optlist[i].name)) { 215 setoption(optlist[i].letter, val); 216 return; 217 } 218 error("Illegal option -o %s", name); 219 } 220 } 221 222 223 STATIC void 224 setoption(flag, val) 225 char flag; 226 int val; 227 { 228 int i; 229 230 for (i = 0; i < NOPTS; i++) 231 if (optlist[i].letter == flag) { 232 optlist[i].val = val; 233 if (val) { 234 /* #%$ hack for ksh semantics */ 235 if (flag == 'V') 236 Eflag = 0; 237 else if (flag == 'E') 238 Vflag = 0; 239 } 240 return; 241 } 242 error("Illegal option -%c", flag); 243 } 244 245 246 247 #ifdef mkinit 248 INCLUDE "options.h" 249 250 SHELLPROC { 251 int i; 252 253 for (i = 0; i < NOPTS; i++) 254 optlist[i].val = 0; 255 optschanged(); 256 257 } 258 #endif 259 260 261 /* 262 * Set the shell parameters. 263 */ 264 265 void 266 setparam(argv) 267 char **argv; 268 { 269 char **newparam; 270 char **ap; 271 int nparam; 272 273 for (nparam = 0 ; argv[nparam] ; nparam++); 274 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 275 while (*argv) { 276 *ap++ = savestr(*argv++); 277 } 278 *ap = NULL; 279 freeparam(&shellparam); 280 shellparam.malloc = 1; 281 shellparam.nparam = nparam; 282 shellparam.p = newparam; 283 shellparam.optnext = NULL; 284 } 285 286 287 /* 288 * Free the list of positional parameters. 289 */ 290 291 void 292 freeparam(param) 293 struct shparam *param; 294 { 295 char **ap; 296 297 if (param->malloc) { 298 for (ap = param->p ; *ap ; ap++) 299 ckfree(*ap); 300 ckfree(param->p); 301 } 302 } 303 304 305 306 /* 307 * The shift builtin command. 308 */ 309 310 int 311 shiftcmd(argc, argv) 312 int argc; 313 char **argv; 314 { 315 int n; 316 char **ap1, **ap2; 317 318 n = 1; 319 if (argc > 1) 320 n = number(argv[1]); 321 if (n > shellparam.nparam) 322 error("can't shift that many"); 323 INTOFF; 324 shellparam.nparam -= n; 325 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 326 if (shellparam.malloc) 327 ckfree(*ap1); 328 } 329 ap2 = shellparam.p; 330 while ((*ap2++ = *ap1++) != NULL); 331 shellparam.optnext = NULL; 332 INTON; 333 return 0; 334 } 335 336 337 338 /* 339 * The set command builtin. 340 */ 341 342 int 343 setcmd(argc, argv) 344 int argc; 345 char **argv; 346 { 347 if (argc == 1) 348 return showvarscmd(argc, argv); 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(argc, argv) 379 int argc; 380 char **argv; 381 { 382 char **optbase; 383 384 if (argc < 3) 385 error("Usage: getopts optstring var [arg]"); 386 else if (argc == 3) 387 optbase = shellparam.p; 388 else 389 optbase = &argv[3]; 390 391 if (shellparam.reset == 1) { 392 shellparam.optnext = optbase; 393 shellparam.optptr = NULL; 394 shellparam.reset = 0; 395 } 396 397 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 398 &shellparam.optptr); 399 } 400 401 STATIC int 402 getopts(optstr, optvar, optfirst, optnext, optptr) 403 char *optstr; 404 char *optvar; 405 char **optfirst; 406 char ***optnext; 407 char **optptr; 408 { 409 register char *p, *q; 410 char c = '?'; 411 int done = 0; 412 int ind = 0; 413 int err = 0; 414 char s[10]; 415 416 if ((p = *optptr) == NULL || *p == '\0') { 417 /* Current word is done, advance */ 418 if (*optnext == NULL) 419 return 1; 420 p = **optnext; 421 if (p == NULL || *p != '-' || *++p == '\0') { 422 atend: 423 **optnext = NULL; 424 ind = *optnext - optfirst + 1; 425 done = 1; 426 goto out; 427 } 428 (*optnext)++; 429 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 430 goto atend; 431 } 432 433 c = *p++; 434 for (q = optstr; *q != c; ) { 435 if (*q == '\0') { 436 if (optstr[0] == ':') { 437 s[0] = c; 438 s[1] = '\0'; 439 err |= setvarsafe("OPTARG", s, 0); 440 } 441 else { 442 out1fmt("Illegal option -%c\n", c); 443 (void) unsetvar("OPTARG"); 444 } 445 c = '?'; 446 goto bad; 447 } 448 if (*++q == ':') 449 q++; 450 } 451 452 if (*++q == ':') { 453 if (*p == '\0' && (p = **optnext) == NULL) { 454 if (optstr[0] == ':') { 455 s[0] = c; 456 s[1] = '\0'; 457 err |= setvarsafe("OPTARG", s, 0); 458 c = ':'; 459 } 460 else { 461 out1fmt("No arg for -%c option\n", c); 462 (void) unsetvar("OPTARG"); 463 c = '?'; 464 } 465 goto bad; 466 } 467 468 if (p == **optnext) 469 (*optnext)++; 470 setvarsafe("OPTARG", p, 0); 471 p = NULL; 472 } 473 else 474 setvarsafe("OPTARG", "", 0); 475 ind = *optnext - optfirst + 1; 476 goto out; 477 478 bad: 479 ind = 1; 480 *optnext = NULL; 481 p = NULL; 482 out: 483 *optptr = p; 484 fmtstr(s, sizeof(s), "%d", ind); 485 err |= setvarsafe("OPTIND", s, VNOFUNC); 486 s[0] = c; 487 s[1] = '\0'; 488 err |= setvarsafe(optvar, s, 0); 489 if (err) { 490 *optnext = NULL; 491 *optptr = NULL; 492 flushall(); 493 exraise(EXERROR); 494 } 495 return done; 496 } 497 498 /* 499 * XXX - should get rid of. have all builtins use getopt(3). the 500 * library getopt must have the BSD extension static variable "optreset" 501 * otherwise it can't be used within the shell safely. 502 * 503 * Standard option processing (a la getopt) for builtin routines. The 504 * only argument that is passed to nextopt is the option string; the 505 * other arguments are unnecessary. It return the character, or '\0' on 506 * end of input. 507 */ 508 509 int 510 nextopt(optstring) 511 char *optstring; 512 { 513 register char *p, *q; 514 char c; 515 516 if ((p = optptr) == NULL || *p == '\0') { 517 p = *argptr; 518 if (p == NULL || *p != '-' || *++p == '\0') 519 return '\0'; 520 argptr++; 521 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 522 return '\0'; 523 } 524 c = *p++; 525 for (q = optstring ; *q != c ; ) { 526 if (*q == '\0') 527 error("Illegal option -%c", c); 528 if (*++q == ':') 529 q++; 530 } 531 if (*++q == ':') { 532 if (*p == '\0' && (p = *argptr++) == NULL) 533 error("No arg for -%c option", c); 534 optarg = p; 535 p = NULL; 536 } 537 optptr = p; 538 return c; 539 } 540