Home | History | Annotate | Line # | Download | only in sh
options.c revision 1.51
      1 /*	$NetBSD: options.c,v 1.51 2017/11/19 03:23:01 kre 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.51 2017/11/19 03:23:01 kre 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 "builtins.h"
     53 #include "nodes.h"	/* for other header files */
     54 #include "eval.h"
     55 #include "jobs.h"
     56 #include "input.h"
     57 #include "output.h"
     58 #include "trap.h"
     59 #include "var.h"
     60 #include "memalloc.h"
     61 #include "error.h"
     62 #include "mystring.h"
     63 #ifndef SMALL
     64 #include "myhistedit.h"
     65 #endif
     66 #include "show.h"
     67 
     68 char *arg0;			/* value of $0 */
     69 struct shparam shellparam;	/* current positional parameters */
     70 char **argptr;			/* argument list for builtin commands */
     71 char *optionarg;		/* set by nextopt (like getopt) */
     72 char *optptr;			/* used by nextopt */
     73 
     74 char *minusc;			/* argument to -c option */
     75 
     76 
     77 STATIC void options(int);
     78 STATIC void minus_o(char *, int);
     79 STATIC void setoption(int, int);
     80 STATIC int getopts(char *, char *, char **, char ***, char **);
     81 
     82 
     83 /*
     84  * Process the shell command line arguments.
     85  */
     86 
     87 void
     88 procargs(int argc, char **argv)
     89 {
     90 	size_t i;
     91 	int psx;
     92 
     93 	argptr = argv;
     94 	if (argc > 0)
     95 		argptr++;
     96 
     97 	psx = posix;		/* save what we set it to earlier */
     98 	/*
     99 	 * option values are mostly boolean 0:off 1:on
    100 	 * we use 2 (just in this routine) to mean "unknown yet"
    101 	 */
    102 	for (i = 0; i < NOPTS; i++)
    103 		optlist[i].val = 2;
    104 	posix = psx;		/* restore before processing -o ... */
    105 
    106 	options(1);
    107 
    108 	if (*argptr == NULL && minusc == NULL)
    109 		sflag = 1;
    110 	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(2))
    111 		iflag = 1;
    112 	if (iflag == 1 && sflag == 2)
    113 		iflag = 2;
    114 	if (mflag == 2)
    115 		mflag = iflag;
    116 #ifndef DO_SHAREDVFORK
    117 	if (usefork == 2)
    118 		usefork = 1;
    119 #endif
    120 #if DEBUG >= 2
    121 	if (debug == 2)
    122 		debug = 1;
    123 #endif
    124 	/*
    125 	 * Any options not dealt with as special cases just above,
    126 	 * and which were not set on the command line, are set to
    127 	 * their expected default values (mostly "off")
    128 	 *
    129 	 * then as each option is initialised, save its setting now
    130 	 * as its "default" value for future use ("set -o default").
    131 	 */
    132 	for (i = 0; i < NOPTS; i++) {
    133 		if (optlist[i].val == 2)
    134 			optlist[i].val = optlist[i].dflt;
    135 		optlist[i].dflt = optlist[i].val;
    136 	}
    137 
    138 	arg0 = argv[0];
    139 	if (sflag == 0 && minusc == NULL) {
    140 		commandname = argv[0];
    141 		arg0 = *argptr++;
    142 		setinputfile(arg0, 0);
    143 		commandname = arg0;
    144 	}
    145 	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
    146 	if (minusc != NULL) {
    147 		if (argptr == NULL || *argptr == NULL)
    148 			error("Bad -c option");
    149 		minusc = *argptr++;
    150 		if (*argptr != 0)
    151 			arg0 = *argptr++;
    152 	}
    153 
    154 	shellparam.p = argptr;
    155 	shellparam.reset = 1;
    156 	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
    157 	while (*argptr) {
    158 		shellparam.nparam++;
    159 		argptr++;
    160 	}
    161 	optschanged();
    162 }
    163 
    164 
    165 void
    166 optschanged(void)
    167 {
    168 	setinteractive(iflag);
    169 #ifndef SMALL
    170 	histedit();
    171 #endif
    172 	setjobctl(mflag);
    173 }
    174 
    175 /*
    176  * Process shell options.  The global variable argptr contains a pointer
    177  * to the argument list; we advance it past the options.
    178  */
    179 
    180 STATIC void
    181 options(int cmdline)
    182 {
    183 	static char empty[] = "";
    184 	char *p;
    185 	int val;
    186 	int c;
    187 
    188 	if (cmdline)
    189 		minusc = NULL;
    190 	while ((p = *argptr) != NULL) {
    191 		argptr++;
    192 		if ((c = *p++) == '-') {
    193 			val = 1;
    194                         if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
    195                                 if (!cmdline) {
    196                                         /* "-" means turn off -x and -v */
    197                                         if (p[0] == '\0')
    198                                                 xflag = vflag = 0;
    199                                         /* "--" means reset params */
    200                                         else if (*argptr == NULL)
    201 						setparam(argptr);
    202                                 }
    203 				break;	  /* "-" or  "--" terminates options */
    204 			}
    205 		} else if (c == '+') {
    206 			val = 0;
    207 		} else {
    208 			argptr--;
    209 			break;
    210 		}
    211 		while ((c = *p++) != '\0') {
    212 			if (val == 1 && c == 'c' && cmdline) {
    213 				/* command is after shell args*/
    214 				minusc = empty;
    215 			} else if (c == 'o') {
    216 				if (*p != '\0')
    217 					minus_o(p, val + (cmdline ? val : 0));
    218 				else if (*argptr)
    219 					minus_o(*argptr++,
    220 					    val + (cmdline ? val : 0));
    221 				else if (!cmdline)
    222 					minus_o(NULL, val);
    223 				else
    224 					error("arg for %co missing", "+-"[val]);
    225 				break;
    226 #ifdef DEBUG
    227 			} else if (c == 'D') {
    228 				if (*p) {
    229 					set_debug(p, val);
    230 					break;
    231 				} else if (*argptr)
    232 					set_debug(*argptr++, val);
    233 				else
    234 					set_debug("*$", val);
    235 #endif
    236 			} else {
    237 				setoption(c, val);
    238 			}
    239 		}
    240 	}
    241 }
    242 
    243 static void
    244 set_opt_val(size_t i, int val)
    245 {
    246 	size_t j;
    247 	int flag;
    248 
    249 	if (val && (flag = optlist[i].opt_set)) {
    250 		/* some options (eg vi/emacs) are mutually exclusive */
    251 		for (j = 0; j < NOPTS; j++)
    252 		    if (optlist[j].opt_set == flag)
    253 			optlist[j].val = 0;
    254 	}
    255 	if (i == _SH_OPT_Xflag)
    256 		xtracefdsetup(val);
    257 	optlist[i].val = val;
    258 #ifdef DEBUG
    259 	if (&optlist[i].val == &debug)
    260 		opentrace();	/* different "trace" than the -x one... */
    261 #endif
    262 }
    263 
    264 STATIC void
    265 minus_o(char *name, int val)
    266 {
    267 	size_t i;
    268 	const char *sep = ": ";
    269 
    270 	if (name == NULL) {
    271 		if (val) {
    272 			out1str("Current option settings");
    273 			for (i = 0; i < NOPTS; i++) {
    274 				if (optlist[i].name == NULL)  {
    275 					out1fmt("%s%c%c", sep,
    276 					    "+-"[optlist[i].val],
    277 					    optlist[i].letter);
    278 					sep = ", ";
    279 				}
    280 			}
    281 			out1c('\n');
    282 			for (i = 0; i < NOPTS; i++) {
    283 				if (optlist[i].name)
    284 				    out1fmt("%-19s %s\n", optlist[i].name,
    285 					optlist[i].val ? "on" : "off");
    286 			}
    287 		} else {
    288 			out1str("set -o default");
    289 			for (i = 0; i < NOPTS; i++) {
    290 				if (optlist[i].val == optlist[i].dflt)
    291 					continue;
    292 				if (optlist[i].name)
    293 				    out1fmt(" %co %s",
    294 					"+-"[optlist[i].val], optlist[i].name);
    295 				else
    296 				    out1fmt(" %c%c", "+-"[optlist[i].val],
    297 					optlist[i].letter);
    298 			}
    299 			out1c('\n');
    300 		}
    301 	} else {
    302 		if (val == 1 && equal(name, "default")) { /* special case */
    303 			for (i = 0; i < NOPTS; i++)
    304 				set_opt_val(i, optlist[i].dflt);
    305 			return;
    306 		}
    307 		if (val)
    308 			val = 1;
    309 		for (i = 0; i < NOPTS; i++)
    310 			if (optlist[i].name && equal(name, optlist[i].name)) {
    311 				set_opt_val(i, val);
    312 				if (i == _SH_OPT_Xflag)
    313 					set_opt_val(_SH_OPT_xflag, val);
    314 				return;
    315 			}
    316 		error("Illegal option %co %s", "+-"[val], name);
    317 	}
    318 }
    319 
    320 
    321 STATIC void
    322 setoption(int flag, int val)
    323 {
    324 	size_t i;
    325 
    326 	for (i = 0; i < NOPTS; i++)
    327 		if (optlist[i].letter == flag) {
    328 			set_opt_val(i, val);
    329 			if (i == _SH_OPT_Xflag)
    330 				set_opt_val(_SH_OPT_xflag, val);
    331 			return;
    332 		}
    333 	error("Illegal option %c%c", "+-"[val], flag);
    334 	/* NOTREACHED */
    335 }
    336 
    337 
    338 
    339 #ifdef mkinit
    340 INCLUDE "options.h"
    341 
    342 SHELLPROC {
    343 	int i;
    344 
    345 	for (i = 0; optlist[i].name; i++)
    346 		optlist[i].val = 0;
    347 	optschanged();
    348 
    349 }
    350 #endif
    351 
    352 
    353 /*
    354  * Set the shell parameters.
    355  */
    356 
    357 void
    358 setparam(char **argv)
    359 {
    360 	char **newparam;
    361 	char **ap;
    362 	int nparam;
    363 
    364 	for (nparam = 0 ; argv[nparam] ; nparam++)
    365 		continue;
    366 	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
    367 	while (*argv) {
    368 		*ap++ = savestr(*argv++);
    369 	}
    370 	*ap = NULL;
    371 	freeparam(&shellparam);
    372 	shellparam.malloc = 1;
    373 	shellparam.nparam = nparam;
    374 	shellparam.p = newparam;
    375 	shellparam.optnext = NULL;
    376 }
    377 
    378 
    379 /*
    380  * Free the list of positional parameters.
    381  */
    382 
    383 void
    384 freeparam(volatile struct shparam *param)
    385 {
    386 	char **ap;
    387 
    388 	if (param->malloc) {
    389 		for (ap = param->p ; *ap ; ap++)
    390 			ckfree(*ap);
    391 		ckfree(param->p);
    392 	}
    393 }
    394 
    395 
    396 
    397 /*
    398  * The shift builtin command.
    399  */
    400 
    401 int
    402 shiftcmd(int argc, char **argv)
    403 {
    404 	int n;
    405 	char **ap1, **ap2;
    406 
    407 	if (argc > 2)
    408 		error("Usage: shift [n]");
    409 	n = 1;
    410 	if (argc > 1)
    411 		n = number(argv[1]);
    412 	if (n > shellparam.nparam)
    413 		error("can't shift that many");
    414 	INTOFF;
    415 	shellparam.nparam -= n;
    416 	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
    417 		if (shellparam.malloc)
    418 			ckfree(*ap1);
    419 	}
    420 	ap2 = shellparam.p;
    421 	while ((*ap2++ = *ap1++) != NULL)
    422 		continue;
    423 	shellparam.optnext = NULL;
    424 	INTON;
    425 	return 0;
    426 }
    427 
    428 
    429 
    430 /*
    431  * The set command builtin.
    432  */
    433 
    434 int
    435 setcmd(int argc, char **argv)
    436 {
    437 	if (argc == 1)
    438 		return showvars(0, 0, 1, 0);
    439 	INTOFF;
    440 	options(0);
    441 	optschanged();
    442 	if (*argptr != NULL) {
    443 		setparam(argptr);
    444 	}
    445 	INTON;
    446 	return 0;
    447 }
    448 
    449 
    450 void
    451 getoptsreset(const char *value)
    452 {
    453 	if (number(value) == 1) {
    454 		shellparam.optnext = NULL;
    455 		shellparam.reset = 1;
    456 	}
    457 }
    458 
    459 /*
    460  * The getopts builtin.  Shellparam.optnext points to the next argument
    461  * to be processed.  Shellparam.optptr points to the next character to
    462  * be processed in the current argument.  If shellparam.optnext is NULL,
    463  * then it's the first time getopts has been called.
    464  */
    465 
    466 int
    467 getoptscmd(int argc, char **argv)
    468 {
    469 	char **optbase;
    470 
    471 	if (argc < 3)
    472 		error("usage: getopts optstring var [arg]");
    473 	else if (argc == 3)
    474 		optbase = shellparam.p;
    475 	else
    476 		optbase = &argv[3];
    477 
    478 	if (shellparam.reset == 1) {
    479 		shellparam.optnext = optbase;
    480 		shellparam.optptr = NULL;
    481 		shellparam.reset = 0;
    482 	}
    483 
    484 	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
    485 		       &shellparam.optptr);
    486 }
    487 
    488 STATIC int
    489 getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, char **optpptr)
    490 {
    491 	char *p, *q;
    492 	char c = '?';
    493 	int done = 0;
    494 	int ind = 0;
    495 	int err = 0;
    496 	char s[12];
    497 
    498 	if ((p = *optpptr) == NULL || *p == '\0') {
    499 		/* Current word is done, advance */
    500 		if (*optnext == NULL)
    501 			return 1;
    502 		p = **optnext;
    503 		if (p == NULL || *p != '-' || *++p == '\0') {
    504 atend:
    505 			ind = *optnext - optfirst + 1;
    506 			*optnext = NULL;
    507 			p = NULL;
    508 			done = 1;
    509 			goto out;
    510 		}
    511 		(*optnext)++;
    512 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
    513 			goto atend;
    514 	}
    515 
    516 	c = *p++;
    517 	for (q = optstr; *q != c; ) {
    518 		if (*q == '\0') {
    519 			if (optstr[0] == ':') {
    520 				s[0] = c;
    521 				s[1] = '\0';
    522 				err |= setvarsafe("OPTARG", s, 0);
    523 			} else {
    524 				outfmt(&errout, "Illegal option -%c\n", c);
    525 				(void) unsetvar("OPTARG", 0);
    526 			}
    527 			c = '?';
    528 			goto bad;
    529 		}
    530 		if (*++q == ':')
    531 			q++;
    532 	}
    533 
    534 	if (*++q == ':') {
    535 		if (*p == '\0' && (p = **optnext) == NULL) {
    536 			if (optstr[0] == ':') {
    537 				s[0] = c;
    538 				s[1] = '\0';
    539 				err |= setvarsafe("OPTARG", s, 0);
    540 				c = ':';
    541 			} else {
    542 				outfmt(&errout, "No arg for -%c option\n", c);
    543 				(void) unsetvar("OPTARG", 0);
    544 				c = '?';
    545 			}
    546 			goto bad;
    547 		}
    548 
    549 		if (p == **optnext)
    550 			(*optnext)++;
    551 		err |= setvarsafe("OPTARG", p, 0);
    552 		p = NULL;
    553 	} else
    554 		err |= setvarsafe("OPTARG", "", 0);
    555 	ind = *optnext - optfirst + 1;
    556 	goto out;
    557 
    558 bad:
    559 	ind = 1;
    560 	*optnext = NULL;
    561 	p = NULL;
    562 out:
    563 	*optpptr = p;
    564 	fmtstr(s, sizeof(s), "%d", ind);
    565 	err |= setvarsafe("OPTIND", s, VNOFUNC);
    566 	s[0] = c;
    567 	s[1] = '\0';
    568 	err |= setvarsafe(optvar, s, 0);
    569 	if (err) {
    570 		*optnext = NULL;
    571 		*optpptr = NULL;
    572 		flushall();
    573 		exraise(EXERROR);
    574 	}
    575 	return done;
    576 }
    577 
    578 /*
    579  * XXX - should get rid of.  have all builtins use getopt(3).  the
    580  * library getopt must have the BSD extension static variable "optreset"
    581  * otherwise it can't be used within the shell safely.
    582  *
    583  * Standard option processing (a la getopt) for builtin routines.  The
    584  * only argument that is passed to nextopt is the option string; the
    585  * other arguments are unnecessary.  It return the character, or '\0' on
    586  * end of input.
    587  */
    588 
    589 int
    590 nextopt(const char *optstring)
    591 {
    592 	char *p;
    593 	const char *q;
    594 	char c;
    595 
    596 	if ((p = optptr) == NULL || *p == '\0') {
    597 		p = *argptr;
    598 		if (p == NULL || *p != '-' || *++p == '\0')
    599 			return '\0';
    600 		argptr++;
    601 		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
    602 			return '\0';
    603 	}
    604 	c = *p++;
    605 	for (q = optstring ; *q != c ; ) {
    606 		if (*q == '\0')
    607 			error("Illegal option -%c", c);
    608 		if (*++q == ':')
    609 			q++;
    610 	}
    611 	if (*++q == ':') {
    612 		if (*p == '\0' && (p = *argptr++) == NULL)
    613 			error("No arg for -%c option", c);
    614 		optionarg = p;
    615 		p = NULL;
    616 	}
    617 	optptr = p;
    618 	return c;
    619 }
    620