1 /* $NetBSD: options.c,v 1.14 1995/05/11 21:29:46 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.14 1995/05/11 21:29:46 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 82 83 /* 84 * Process the shell command line arguments. 85 */ 86 87 void 88 procargs(argc, argv) 89 int argc; 90 char **argv; 91 { 92 int i; 93 94 argptr = argv; 95 if (argc > 0) 96 argptr++; 97 for (i = 0; i < NOPTS; i++) 98 optlist[i].val = 2; 99 options(1); 100 if (*argptr == NULL && minusc == NULL) 101 sflag = 1; 102 if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) 103 iflag = 1; 104 if (mflag == 2) 105 mflag = iflag; 106 for (i = 0; i < NOPTS; i++) 107 if (optlist[i].val == 2) 108 optlist[i].val = 0; 109 arg0 = argv[0]; 110 if (sflag == 0 && minusc == NULL) { 111 commandname = arg0 = *argptr++; 112 setinputfile(commandname, 0); 113 } 114 shellparam.p = argptr; 115 /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ 116 while (*argptr) { 117 shellparam.nparam++; 118 argptr++; 119 } 120 optschanged(); 121 } 122 123 124 void 125 optschanged() 126 { 127 setinteractive(iflag); 128 #ifndef NO_HISTORY 129 histedit(); 130 #endif 131 setjobctl(mflag); 132 } 133 134 /* 135 * Process shell options. The global variable argptr contains a pointer 136 * to the argument list; we advance it past the options. 137 */ 138 139 STATIC void 140 options(cmdline) 141 int cmdline; 142 { 143 register char *p; 144 int val; 145 int c; 146 147 if (cmdline) 148 minusc = NULL; 149 while ((p = *argptr) != NULL) { 150 argptr++; 151 if ((c = *p++) == '-') { 152 val = 1; 153 if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { 154 if (!cmdline) { 155 /* "-" means turn off -x and -v */ 156 if (p[0] == '\0') 157 xflag = vflag = 0; 158 /* "--" means reset params */ 159 else if (*argptr == NULL) 160 setparam(argptr); 161 } 162 break; /* "-" or "--" terminates options */ 163 } 164 } else if (c == '+') { 165 val = 0; 166 } else { 167 argptr--; 168 break; 169 } 170 while ((c = *p++) != '\0') { 171 if (c == 'c' && cmdline) { 172 char *q; 173 #ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ 174 if (*p == '\0') 175 #endif 176 q = *argptr++; 177 if (q == NULL || minusc != NULL) 178 error("Bad -c option"); 179 minusc = q; 180 #ifdef NOHACK 181 break; 182 #endif 183 } else if (c == 'o') { 184 minus_o(*argptr, val); 185 if (*argptr) 186 argptr++; 187 } else { 188 setoption(c, val); 189 } 190 } 191 } 192 } 193 194 STATIC void 195 minus_o(name, val) 196 char *name; 197 int val; 198 { 199 int i; 200 201 if (name == NULL) { 202 out1str("Current option settings\n"); 203 for (i = 0; i < NOPTS; i++) 204 out1fmt("%-16s%s\n", optlist[i].name, 205 optlist[i].val ? "on" : "off"); 206 } else { 207 for (i = 0; i < NOPTS; i++) 208 if (equal(name, optlist[i].name)) { 209 setoption(optlist[i].letter, val); 210 return; 211 } 212 error("Illegal option -o %s", name); 213 } 214 } 215 216 217 STATIC void 218 setoption(flag, val) 219 char flag; 220 int val; 221 { 222 int i; 223 224 for (i = 0; i < NOPTS; i++) 225 if (optlist[i].letter == flag) { 226 optlist[i].val = val; 227 if (val) { 228 /* #%$ hack for ksh semantics */ 229 if (flag == 'V') 230 Eflag = 0; 231 else if (flag == 'E') 232 Vflag = 0; 233 } 234 return; 235 } 236 error("Illegal option -%c", flag); 237 } 238 239 240 241 #ifdef mkinit 242 INCLUDE "options.h" 243 244 SHELLPROC { 245 int i; 246 247 for (i = 0; i < NOPTS; i++) 248 optlist[i].val = 0; 249 optschanged(); 250 251 } 252 #endif 253 254 255 /* 256 * Set the shell parameters. 257 */ 258 259 void 260 setparam(argv) 261 char **argv; 262 { 263 char **newparam; 264 char **ap; 265 int nparam; 266 267 for (nparam = 0 ; argv[nparam] ; nparam++); 268 ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); 269 while (*argv) { 270 *ap++ = savestr(*argv++); 271 } 272 *ap = NULL; 273 freeparam(&shellparam); 274 shellparam.malloc = 1; 275 shellparam.nparam = nparam; 276 shellparam.p = newparam; 277 shellparam.optnext = NULL; 278 } 279 280 281 /* 282 * Free the list of positional parameters. 283 */ 284 285 void 286 freeparam(param) 287 struct shparam *param; 288 { 289 char **ap; 290 291 if (param->malloc) { 292 for (ap = param->p ; *ap ; ap++) 293 ckfree(*ap); 294 ckfree(param->p); 295 } 296 } 297 298 299 300 /* 301 * The shift builtin command. 302 */ 303 304 int 305 shiftcmd(argc, argv) 306 int argc; 307 char **argv; 308 { 309 int n; 310 char **ap1, **ap2; 311 312 n = 1; 313 if (argc > 1) 314 n = number(argv[1]); 315 if (n > shellparam.nparam) 316 error("can't shift that many"); 317 INTOFF; 318 shellparam.nparam -= n; 319 for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { 320 if (shellparam.malloc) 321 ckfree(*ap1); 322 } 323 ap2 = shellparam.p; 324 while ((*ap2++ = *ap1++) != NULL); 325 shellparam.optnext = NULL; 326 INTON; 327 return 0; 328 } 329 330 331 332 /* 333 * The set command builtin. 334 */ 335 336 int 337 setcmd(argc, argv) 338 int argc; 339 char **argv; 340 { 341 if (argc == 1) 342 return showvarscmd(argc, argv); 343 INTOFF; 344 options(0); 345 optschanged(); 346 if (*argptr != NULL) { 347 setparam(argptr); 348 } 349 INTON; 350 return 0; 351 } 352 353 354 /* 355 * The getopts builtin. Shellparam.optnext points to the next argument 356 * to be processed. Shellparam.optptr points to the next character to 357 * be processed in the current argument. If shellparam.optnext is NULL, 358 * then it's the first time getopts has been called. 359 */ 360 361 int 362 getoptscmd(argc, argv) 363 int argc; 364 char **argv; 365 { 366 register char *p, *q; 367 char c; 368 char s[10]; 369 370 if (argc != 3) 371 error("Usage: getopts optstring var"); 372 if (shellparam.optnext == NULL) { 373 shellparam.optnext = shellparam.p; 374 shellparam.optptr = NULL; 375 } 376 if ((p = shellparam.optptr) == NULL || *p == '\0') { 377 p = *shellparam.optnext; 378 if (p == NULL || *p != '-' || *++p == '\0') { 379 atend: 380 fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1); 381 setvar("OPTIND", s, 0); 382 shellparam.optnext = NULL; 383 return 1; 384 } 385 shellparam.optnext++; 386 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 387 goto atend; 388 } 389 c = *p++; 390 for (q = argv[1] ; *q != c ; ) { 391 if (*q == '\0') { 392 out1fmt("Illegal option -%c\n", c); 393 c = '?'; 394 goto out; 395 } 396 if (*++q == ':') 397 q++; 398 } 399 if (*++q == ':') { 400 if (*p == '\0' && (p = *shellparam.optnext) == NULL) { 401 out1fmt("No arg for -%c option\n", c); 402 c = '?'; 403 goto out; 404 } 405 shellparam.optnext++; 406 setvar("OPTARG", p, 0); 407 p = NULL; 408 } 409 out: 410 shellparam.optptr = p; 411 s[0] = c; 412 s[1] = '\0'; 413 setvar(argv[2], s, 0); 414 return 0; 415 } 416 417 /* 418 * XXX - should get rid of. have all builtins use getopt(3). the 419 * library getopt must have the BSD extension static variable "optreset" 420 * otherwise it can't be used within the shell safely. 421 * 422 * Standard option processing (a la getopt) for builtin routines. The 423 * only argument that is passed to nextopt is the option string; the 424 * other arguments are unnecessary. It return the character, or '\0' on 425 * end of input. 426 */ 427 428 int 429 nextopt(optstring) 430 char *optstring; 431 { 432 register char *p, *q; 433 char c; 434 435 if ((p = optptr) == NULL || *p == '\0') { 436 p = *argptr; 437 if (p == NULL || *p != '-' || *++p == '\0') 438 return '\0'; 439 argptr++; 440 if (p[0] == '-' && p[1] == '\0') /* check for "--" */ 441 return '\0'; 442 } 443 c = *p++; 444 for (q = optstring ; *q != c ; ) { 445 if (*q == '\0') 446 error("Illegal option -%c", c); 447 if (*++q == ':') 448 q++; 449 } 450 if (*++q == ':') { 451 if (*p == '\0' && (p = *argptr++) == NULL) 452 error("No arg for -%c option", c); 453 optarg = p; 454 p = NULL; 455 } 456 optptr = p; 457 return c; 458 } 459