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