1 /* $NetBSD: options.c,v 1.16 1996/06/25 16:47:43 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.16 1996/06/25 16:47:43 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 shellparam.p = argptr; 116 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 117 while (*argptr) { 118 shellparam.nparam++; 119 argptr++; 120 } 121 optschanged(); 122 } 123 124 125 void 126 optschanged() 127 { 128 setinteractive(iflag); 129 #ifndef NO_HISTORY 130 histedit(); 131 #endif 132 setjobctl(mflag); 133 } 134 135 /* 136 * Process shell options. The global variable argptr contains a pointer 137 * to the argument list; we advance it past the options. 138 */ 139 140 STATIC void 141 options(cmdline) 142 int cmdline; 143 { 144 register char *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 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 155 if (!cmdline) { 156 /* "-" means turn off -x and -v */ 157 if (p[0] == '\0') 158 xflag = vflag = 0; 159 /* "--" means reset params */ 160 else if (*argptr == NULL) 161 setparam(argptr); 162 } 163 break; /* "-" or "--" terminates options */ 164 } 165 } else if (c == '+') { 166 val = 0; 167 } else { 168 argptr--; 169 break; 170 } 171 while ((c = *p++) != '\0') { 172 if (c == 'c' && cmdline) { 173 char *q; 174 #ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 175 if (*p == '\0') 176 #endif 177 q = *argptr++; 178 if (q == NULL || minusc != NULL) 179 error("Bad -c option"); 180 minusc = q; 181 #ifdef NOHACK 182 break; 183 #endif 184 } else if (c == 'o') { 185 minus_o(*argptr, val); 186 if (*argptr) 187 argptr++; 188 } else { 189 setoption(c, val); 190 } 191 } 192 } 193 } 194 195 STATIC void 196 minus_o(name, val) 197 char *name; 198 int val; 199 { 200 int i; 201 202 if (name == NULL) { 203 out1str("Current option settings\n"); 204 for (i = 0; i < NOPTS; i++) 205 out1fmt("%-16s%s\n", optlist[i].name, 206 optlist[i].val ? "on" : "off"); 207 } else { 208 for (i = 0; i < NOPTS; i++) 209 if (equal(name, optlist[i].name)) { 210 setoption(optlist[i].letter, val); 211 return; 212 } 213 error("Illegal option -o %s", name); 214 } 215 } 216 217 218 STATIC void 219 setoption(flag, val) 220 char flag; 221 int val; 222 { 223 int i; 224 225 for (i = 0; i < NOPTS; i++) 226 if (optlist[i].letter == flag) { 227 optlist[i].val = val; 228 if (val) { 229 /* #%$ hack for ksh semantics */ 230 if (flag == 'V') 231 Eflag = 0; 232 else if (flag == 'E') 233 Vflag = 0; 234 } 235 return; 236 } 237 error("Illegal option -%c", flag); 238 } 239 240 241 242 #ifdef mkinit 243 INCLUDE "options.h" 244 245 SHELLPROC { 246 int i; 247 248 for (i = 0; i < NOPTS; i++) 249 optlist[i].val = 0; 250 optschanged(); 251 252 } 253 #endif 254 255 256 /* 257 * Set the shell parameters. 258 */ 259 260 void 261 setparam(argv) 262 char **argv; 263 { 264 char **newparam; 265 char **ap; 266 int nparam; 267 268 for (nparam = 0 ; argv[nparam] ; nparam++); 269 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 270 while (*argv) { 271 *ap++ = savestr(*argv++); 272 } 273 *ap = NULL; 274 freeparam(&shellparam); 275 shellparam.malloc = 1; 276 shellparam.nparam = nparam; 277 shellparam.p = newparam; 278 shellparam.optnext = NULL; 279 } 280 281 282 /* 283 * Free the list of positional parameters. 284 */ 285 286 void 287 freeparam(param) 288 struct shparam *param; 289 { 290 char **ap; 291 292 if (param->malloc) { 293 for (ap = param->p ; *ap ; ap++) 294 ckfree(*ap); 295 ckfree(param->p); 296 } 297 } 298 299 300 301 /* 302 * The shift builtin command. 303 */ 304 305 int 306 shiftcmd(argc, argv) 307 int argc; 308 char **argv; 309 { 310 int n; 311 char **ap1, **ap2; 312 313 n = 1; 314 if (argc > 1) 315 n = number(argv[1]); 316 if (n > shellparam.nparam) 317 error("can't shift that many"); 318 INTOFF; 319 shellparam.nparam -= n; 320 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 321 if (shellparam.malloc) 322 ckfree(*ap1); 323 } 324 ap2 = shellparam.p; 325 while ((*ap2++ = *ap1++) != NULL); 326 shellparam.optnext = NULL; 327 INTON; 328 return 0; 329 } 330 331 332 333 /* 334 * The set command builtin. 335 */ 336 337 int 338 setcmd(argc, argv) 339 int argc; 340 char **argv; 341 { 342 if (argc == 1) 343 return showvarscmd(argc, argv); 344 INTOFF; 345 options(0); 346 optschanged(); 347 if (*argptr != NULL) { 348 setparam(argptr); 349 } 350 INTON; 351 return 0; 352 } 353 354 355 void 356 getoptsreset(value) 357 const char *value; 358 { 359 if (number(value) == 1) 360 shellparam.optnext; 361 } 362 363 /* 364 * The getopts builtin. Shellparam.optnext points to the next argument 365 * to be processed. Shellparam.optptr points to the next character to 366 * be processed in the current argument. If shellparam.optnext is NULL, 367 * then it's the first time getopts has been called. 368 */ 369 370 int 371 getoptscmd(argc, argv) 372 int argc; 373 char **argv; 374 { 375 char **optbase; 376 377 if (argc < 3) 378 error("Usage: getopts optstring var [arg]"); 379 else if (argc == 3) 380 optbase = shellparam.p; 381 else 382 optbase = &argv[3]; 383 384 if (shellparam.optnext == NULL) { 385 shellparam.optnext = optbase; 386 shellparam.optptr = NULL; 387 } 388 389 return getopts(argv[1], argv[2], optbase, &shellparam.optnext, 390 &shellparam.optptr); 391 } 392 393 STATIC int 394 getopts(optstr, optvar, optfirst, optnext, optptr) 395 char *optstr; 396 char *optvar; 397 char **optfirst; 398 char ***optnext; 399 char **optptr; 400 { 401 register char *p, *q; 402 char c = '?'; 403 int done = 0; 404 int ind = 0; 405 int err = 0; 406 char s[10]; 407 408 if ((p = *optptr) == NULL || *p == '\0') { 409 /* Current word is done, advance */ 410 p = **optnext; 411 if (p == NULL || *p != '-' || *++p == '\0') { 412 atend: 413 **optnext = NULL; 414 ind = *optnext - optfirst + 1; 415 done = 1; 416 goto out; 417 } 418 (*optnext)++; 419 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 420 goto atend; 421 } 422 423 c = *p++; 424 for (q = optstr; *q != c; ) { 425 if (*q == '\0') { 426 if (optstr[0] == ':') { 427 s[0] = c; 428 s[1] = '\0'; 429 err |= setvarsafe("OPTARG", s, 0); 430 } 431 else { 432 out1fmt("Illegal option -%c\n", c); 433 (void) unsetvar("OPTARG"); 434 } 435 c = '?'; 436 goto bad; 437 } 438 if (*++q == ':') 439 q++; 440 } 441 442 if (*++q == ':') { 443 if (*p == '\0' && (p = **optnext) == NULL) { 444 if (optstr[0] == ':') { 445 s[0] = c; 446 s[1] = '\0'; 447 err |= setvarsafe("OPTARG", s, 0); 448 c = ':'; 449 } 450 else { 451 out1fmt("No arg for -%c option\n", c); 452 (void) unsetvar("OPTARG"); 453 c = '?'; 454 } 455 ind = 1; 456 *optnext = NULL; 457 goto bad; 458 } 459 460 if (p == **optnext) 461 (*optnext)++; 462 ind = *optnext - optfirst + 1; 463 setvarsafe("OPTARG", p, 0); 464 p = NULL; 465 goto out; 466 } 467 bad: 468 ind = 1; 469 *optnext = NULL; 470 p = NULL; 471 out: 472 *optptr = p; 473 fmtstr(s, sizeof(s), "%d", ind); 474 err |= setvarsafe("OPTIND", s, VNOFUNC); 475 s[0] = c; 476 s[1] = '\0'; 477 err |= setvarsafe(optvar, s, 0); 478 if (err) { 479 *optnext = NULL; 480 *optptr = NULL; 481 flushall(); 482 exraise(EXERROR); 483 } 484 return done; 485 } 486 487 /* 488 * XXX - should get rid of. have all builtins use getopt(3). the 489 * library getopt must have the BSD extension static variable "optreset" 490 * otherwise it can't be used within the shell safely. 491 * 492 * Standard option processing (a la getopt) for builtin routines. The 493 * only argument that is passed to nextopt is the option string; the 494 * other arguments are unnecessary. It return the character, or '\0' on 495 * end of input. 496 */ 497 498 int 499 nextopt(optstring) 500 char *optstring; 501 { 502 register char *p, *q; 503 char c; 504 505 if ((p = optptr) == NULL || *p == '\0') { 506 p = *argptr; 507 if (p == NULL || *p != '-' || *++p == '\0') 508 return '\0'; 509 argptr++; 510 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 511 return '\0'; 512 } 513 c = *p++; 514 for (q = optstring ; *q != c ; ) { 515 if (*q == '\0') 516 error("Illegal option -%c", c); 517 if (*++q == ':') 518 q++; 519 } 520 if (*++q == ':') { 521 if (*p == '\0' && (p = *argptr++) == NULL) 522 error("No arg for -%c option", c); 523 optarg = p; 524 p = NULL; 525 } 526 optptr = p; 527 return c; 528 } 529