options.c revision 90111
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3836150Scharnier#if 0
3936150Scharnierstatic char sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
4036150Scharnier#endif
4136150Scharnierstatic const char rcsid[] =
4250471Speter  "$FreeBSD: head/bin/sh/options.c 90111 2002-02-02 06:50:57Z imp $";
431556Srgrimes#endif /* not lint */
441556Srgrimes
4517987Speter#include <signal.h>
4617987Speter#include <unistd.h>
4717987Speter#include <stdlib.h>
4817987Speter
491556Srgrimes#include "shell.h"
501556Srgrimes#define DEFINE_OPTIONS
511556Srgrimes#include "options.h"
521556Srgrimes#undef DEFINE_OPTIONS
531556Srgrimes#include "nodes.h"	/* for other header files */
541556Srgrimes#include "eval.h"
551556Srgrimes#include "jobs.h"
561556Srgrimes#include "input.h"
571556Srgrimes#include "output.h"
581556Srgrimes#include "trap.h"
591556Srgrimes#include "var.h"
601556Srgrimes#include "memalloc.h"
611556Srgrimes#include "error.h"
621556Srgrimes#include "mystring.h"
6317987Speter#ifndef NO_HISTORY
6417987Speter#include "myhistedit.h"
6517987Speter#endif
661556Srgrimes
671556Srgrimeschar *arg0;			/* value of $0 */
681556Srgrimesstruct shparam shellparam;	/* current positional parameters */
691556Srgrimeschar **argptr;			/* argument list for builtin commands */
7059436Scracauerchar *shoptarg;			/* set by nextopt (like getopt) */
711556Srgrimeschar *optptr;			/* used by nextopt */
721556Srgrimes
731556Srgrimeschar *minusc;			/* argument to -c option */
741556Srgrimes
751556Srgrimes
7690111SimpSTATIC void options(int);
7790111SimpSTATIC void minus_o(char *, int);
7890111SimpSTATIC void setoption(int, int);
7990111SimpSTATIC int getopts(char *, char *, char **, char ***, char **);
801556Srgrimes
811556Srgrimes
821556Srgrimes/*
831556Srgrimes * Process the shell command line arguments.
841556Srgrimes */
851556Srgrimes
861556Srgrimesvoid
8790111Simpprocargs(int argc, char **argv)
8817987Speter{
891556Srgrimes	int i;
901556Srgrimes
911556Srgrimes	argptr = argv;
921556Srgrimes	if (argc > 0)
931556Srgrimes		argptr++;
941556Srgrimes	for (i = 0; i < NOPTS; i++)
951556Srgrimes		optlist[i].val = 2;
9619240Ssteve	privileged = (getuid() != geteuid() || getgid() != getegid());
971556Srgrimes	options(1);
981556Srgrimes	if (*argptr == NULL && minusc == NULL)
991556Srgrimes		sflag = 1;
1001556Srgrimes	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
1011556Srgrimes		iflag = 1;
1021556Srgrimes	if (mflag == 2)
1031556Srgrimes		mflag = iflag;
1041556Srgrimes	for (i = 0; i < NOPTS; i++)
1051556Srgrimes		if (optlist[i].val == 2)
1061556Srgrimes			optlist[i].val = 0;
1071556Srgrimes	arg0 = argv[0];
1081556Srgrimes	if (sflag == 0 && minusc == NULL) {
1091556Srgrimes		commandname = arg0 = *argptr++;
1101556Srgrimes		setinputfile(commandname, 0);
1111556Srgrimes	}
11220425Ssteve	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
11325227Ssteve	if (argptr && minusc && *argptr)
11411111Sjoerg		arg0 = *argptr++;
11520742Ssteve
1161556Srgrimes	shellparam.p = argptr;
11720742Ssteve	shellparam.reset = 1;
1181556Srgrimes	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
1191556Srgrimes	while (*argptr) {
1201556Srgrimes		shellparam.nparam++;
1211556Srgrimes		argptr++;
1221556Srgrimes	}
1231556Srgrimes	optschanged();
1241556Srgrimes}
1251556Srgrimes
1261556Srgrimes
12717987Spetervoid
12890111Simpoptschanged(void)
12917987Speter{
1301556Srgrimes	setinteractive(iflag);
13117987Speter#ifndef NO_HISTORY
1321556Srgrimes	histedit();
13317987Speter#endif
1341556Srgrimes	setjobctl(mflag);
1351556Srgrimes}
1361556Srgrimes
1371556Srgrimes/*
1381556Srgrimes * Process shell options.  The global variable argptr contains a pointer
1391556Srgrimes * to the argument list; we advance it past the options.
1401556Srgrimes */
1411556Srgrimes
1421556SrgrimesSTATIC void
14390111Simpoptions(int cmdline)
14417987Speter{
14525227Ssteve	char *p;
1461556Srgrimes	int val;
1471556Srgrimes	int c;
1481556Srgrimes
1491556Srgrimes	if (cmdline)
1501556Srgrimes		minusc = NULL;
1511556Srgrimes	while ((p = *argptr) != NULL) {
1521556Srgrimes		argptr++;
1531556Srgrimes		if ((c = *p++) == '-') {
1541556Srgrimes			val = 1;
15517987Speter                        if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
1561556Srgrimes                                if (!cmdline) {
1571556Srgrimes                                        /* "-" means turn off -x and -v */
1581556Srgrimes                                        if (p[0] == '\0')
1591556Srgrimes                                                xflag = vflag = 0;
1601556Srgrimes                                        /* "--" means reset params */
1611556Srgrimes                                        else if (*argptr == NULL)
16217987Speter						setparam(argptr);
1631556Srgrimes                                }
1641556Srgrimes				break;	  /* "-" or  "--" terminates options */
1651556Srgrimes			}
1661556Srgrimes		} else if (c == '+') {
1671556Srgrimes			val = 0;
1681556Srgrimes		} else {
1691556Srgrimes			argptr--;
1701556Srgrimes			break;
1711556Srgrimes		}
1721556Srgrimes		while ((c = *p++) != '\0') {
1731556Srgrimes			if (c == 'c' && cmdline) {
1741556Srgrimes				char *q;
1751556Srgrimes#ifdef NOHACK	/* removing this code allows sh -ce 'foo' for compat */
1761556Srgrimes				if (*p == '\0')
1771556Srgrimes#endif
1781556Srgrimes					q = *argptr++;
1791556Srgrimes				if (q == NULL || minusc != NULL)
1801556Srgrimes					error("Bad -c option");
1811556Srgrimes				minusc = q;
1821556Srgrimes#ifdef NOHACK
1831556Srgrimes				break;
1841556Srgrimes#endif
1851556Srgrimes			} else if (c == 'o') {
1861556Srgrimes				minus_o(*argptr, val);
1871556Srgrimes				if (*argptr)
1881556Srgrimes					argptr++;
1891556Srgrimes			} else {
19019240Ssteve				if (c == 'p' && !val && privileged) {
19119240Ssteve					(void) setuid(getuid());
19219240Ssteve					(void) setgid(getgid());
19319240Ssteve				}
1941556Srgrimes				setoption(c, val);
1951556Srgrimes			}
1961556Srgrimes		}
1971556Srgrimes	}
1981556Srgrimes}
1991556Srgrimes
2001556SrgrimesSTATIC void
20190111Simpminus_o(char *name, int val)
2021556Srgrimes{
2031556Srgrimes	int i;
2041556Srgrimes
2051556Srgrimes	if (name == NULL) {
2061556Srgrimes		out1str("Current option settings\n");
2071556Srgrimes		for (i = 0; i < NOPTS; i++)
2081556Srgrimes			out1fmt("%-16s%s\n", optlist[i].name,
2091556Srgrimes				optlist[i].val ? "on" : "off");
2101556Srgrimes	} else {
2111556Srgrimes		for (i = 0; i < NOPTS; i++)
2121556Srgrimes			if (equal(name, optlist[i].name)) {
21319240Ssteve				if (!val && privileged && equal(name, "privileged")) {
21419240Ssteve					(void) setuid(getuid());
21519240Ssteve					(void) setgid(getgid());
21619240Ssteve				}
2171556Srgrimes				setoption(optlist[i].letter, val);
2181556Srgrimes				return;
2191556Srgrimes			}
2201556Srgrimes		error("Illegal option -o %s", name);
2211556Srgrimes	}
2221556Srgrimes}
2231556Srgrimes
2248855Srgrimes
2251556SrgrimesSTATIC void
22690111Simpsetoption(int flag, int val)
22790111Simp{
2281556Srgrimes	int i;
2291556Srgrimes
2301556Srgrimes	for (i = 0; i < NOPTS; i++)
2311556Srgrimes		if (optlist[i].letter == flag) {
2321556Srgrimes			optlist[i].val = val;
2331556Srgrimes			if (val) {
2341556Srgrimes				/* #%$ hack for ksh semantics */
2351556Srgrimes				if (flag == 'V')
2361556Srgrimes					Eflag = 0;
2371556Srgrimes				else if (flag == 'E')
2381556Srgrimes					Vflag = 0;
2391556Srgrimes			}
2401556Srgrimes			return;
2411556Srgrimes		}
2421556Srgrimes	error("Illegal option -%c", flag);
2431556Srgrimes}
2441556Srgrimes
2451556Srgrimes
2461556Srgrimes
2471556Srgrimes#ifdef mkinit
2481556SrgrimesINCLUDE "options.h"
2491556Srgrimes
2501556SrgrimesSHELLPROC {
2511556Srgrimes	int i;
2521556Srgrimes
2531556Srgrimes	for (i = 0; i < NOPTS; i++)
2541556Srgrimes		optlist[i].val = 0;
2551556Srgrimes	optschanged();
2561556Srgrimes
2571556Srgrimes}
2581556Srgrimes#endif
2591556Srgrimes
2601556Srgrimes
2611556Srgrimes/*
2621556Srgrimes * Set the shell parameters.
2631556Srgrimes */
2641556Srgrimes
2651556Srgrimesvoid
26690111Simpsetparam(char **argv)
26790111Simp{
2681556Srgrimes	char **newparam;
2691556Srgrimes	char **ap;
2701556Srgrimes	int nparam;
2711556Srgrimes
2721556Srgrimes	for (nparam = 0 ; argv[nparam] ; nparam++);
2731556Srgrimes	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
2741556Srgrimes	while (*argv) {
2751556Srgrimes		*ap++ = savestr(*argv++);
2761556Srgrimes	}
2771556Srgrimes	*ap = NULL;
2781556Srgrimes	freeparam(&shellparam);
2791556Srgrimes	shellparam.malloc = 1;
2801556Srgrimes	shellparam.nparam = nparam;
2811556Srgrimes	shellparam.p = newparam;
2821556Srgrimes	shellparam.optnext = NULL;
2831556Srgrimes}
2841556Srgrimes
2851556Srgrimes
2861556Srgrimes/*
2871556Srgrimes * Free the list of positional parameters.
2881556Srgrimes */
2891556Srgrimes
2901556Srgrimesvoid
29190111Simpfreeparam(struct shparam *param)
29290111Simp{
2931556Srgrimes	char **ap;
2941556Srgrimes
2951556Srgrimes	if (param->malloc) {
2961556Srgrimes		for (ap = param->p ; *ap ; ap++)
2971556Srgrimes			ckfree(*ap);
2981556Srgrimes		ckfree(param->p);
2991556Srgrimes	}
3001556Srgrimes}
3011556Srgrimes
3021556Srgrimes
3031556Srgrimes
3041556Srgrimes/*
3051556Srgrimes * The shift builtin command.
3061556Srgrimes */
3071556Srgrimes
30817987Speterint
30990111Simpshiftcmd(int argc, char **argv)
31017987Speter{
3111556Srgrimes	int n;
3121556Srgrimes	char **ap1, **ap2;
3131556Srgrimes
3141556Srgrimes	n = 1;
3151556Srgrimes	if (argc > 1)
3161556Srgrimes		n = number(argv[1]);
3171556Srgrimes	if (n > shellparam.nparam)
3181556Srgrimes		error("can't shift that many");
3191556Srgrimes	INTOFF;
3201556Srgrimes	shellparam.nparam -= n;
3211556Srgrimes	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
3221556Srgrimes		if (shellparam.malloc)
3231556Srgrimes			ckfree(*ap1);
3241556Srgrimes	}
3251556Srgrimes	ap2 = shellparam.p;
3261556Srgrimes	while ((*ap2++ = *ap1++) != NULL);
3271556Srgrimes	shellparam.optnext = NULL;
3281556Srgrimes	INTON;
3291556Srgrimes	return 0;
3301556Srgrimes}
3311556Srgrimes
3321556Srgrimes
3331556Srgrimes
3341556Srgrimes/*
3351556Srgrimes * The set command builtin.
3361556Srgrimes */
3371556Srgrimes
33817987Speterint
33990111Simpsetcmd(int argc, char **argv)
34017987Speter{
3411556Srgrimes	if (argc == 1)
3421556Srgrimes		return showvarscmd(argc, argv);
3431556Srgrimes	INTOFF;
3441556Srgrimes	options(0);
3451556Srgrimes	optschanged();
3461556Srgrimes	if (*argptr != NULL) {
3471556Srgrimes		setparam(argptr);
3481556Srgrimes	}
3491556Srgrimes	INTON;
3501556Srgrimes	return 0;
3511556Srgrimes}
3521556Srgrimes
3531556Srgrimes
35420425Sstevevoid
35590111Simpgetoptsreset(const char *value)
35620425Ssteve{
35720425Ssteve	if (number(value) == 1) {
35820425Ssteve		shellparam.optnext = NULL;
35920425Ssteve		shellparam.reset = 1;
36020425Ssteve	}
36120425Ssteve}
36220425Ssteve
3631556Srgrimes/*
3641556Srgrimes * The getopts builtin.  Shellparam.optnext points to the next argument
3651556Srgrimes * to be processed.  Shellparam.optptr points to the next character to
3661556Srgrimes * be processed in the current argument.  If shellparam.optnext is NULL,
3671556Srgrimes * then it's the first time getopts has been called.
3681556Srgrimes */
3691556Srgrimes
37017987Speterint
37190111Simpgetoptscmd(int argc, char **argv)
37217987Speter{
37320425Ssteve	char **optbase = NULL;
37420425Ssteve
37520425Ssteve	if (argc < 3)
37620425Ssteve		error("Usage: getopts optstring var [arg]");
37720425Ssteve	else if (argc == 3)
37820425Ssteve		optbase = shellparam.p;
37920425Ssteve	else
38020425Ssteve		optbase = &argv[3];
38120425Ssteve
38220425Ssteve	if (shellparam.reset == 1) {
38320425Ssteve		shellparam.optnext = optbase;
38420425Ssteve		shellparam.optptr = NULL;
38520425Ssteve		shellparam.reset = 0;
38620425Ssteve	}
38720425Ssteve
38820425Ssteve	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
38920425Ssteve		       &shellparam.optptr);
39020425Ssteve}
39120425Ssteve
39220425SsteveSTATIC int
39390111Simpgetopts(char *optstr, char *optvar, char **optfirst, char ***optnext,
39490111Simp    char **optptr)
39520425Ssteve{
39625227Ssteve	char *p, *q;
39720425Ssteve	char c = '?';
39820425Ssteve	int done = 0;
39920425Ssteve	int ind = 0;
40020425Ssteve	int err = 0;
4011556Srgrimes	char s[10];
4021556Srgrimes
40320425Ssteve	if ((p = *optptr) == NULL || *p == '\0') {
40420425Ssteve		/* Current word is done, advance */
40520425Ssteve		if (*optnext == NULL)
40620425Ssteve			return 1;
40720425Ssteve		p = **optnext;
4081556Srgrimes		if (p == NULL || *p != '-' || *++p == '\0') {
4091556Srgrimesatend:
41020742Ssteve			ind = *optnext - optfirst + 1;
41120425Ssteve			*optnext = NULL;
41220742Ssteve			p = NULL;
41320425Ssteve			done = 1;
41420425Ssteve			goto out;
4151556Srgrimes		}
41620425Ssteve		(*optnext)++;
4171556Srgrimes		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
4181556Srgrimes			goto atend;
4191556Srgrimes	}
42020425Ssteve
4211556Srgrimes	c = *p++;
42220425Ssteve	for (q = optstr; *q != c; ) {
4231556Srgrimes		if (*q == '\0') {
42420425Ssteve			if (optstr[0] == ':') {
42520425Ssteve				s[0] = c;
42620425Ssteve				s[1] = '\0';
42720425Ssteve				err |= setvarsafe("OPTARG", s, 0);
42820425Ssteve			}
42920425Ssteve			else {
43020425Ssteve				out1fmt("Illegal option -%c\n", c);
43120425Ssteve				(void) unsetvar("OPTARG");
43220425Ssteve			}
4331556Srgrimes			c = '?';
43420425Ssteve			goto bad;
4351556Srgrimes		}
4361556Srgrimes		if (*++q == ':')
4371556Srgrimes			q++;
4381556Srgrimes	}
43920425Ssteve
4401556Srgrimes	if (*++q == ':') {
44120425Ssteve		if (*p == '\0' && (p = **optnext) == NULL) {
44220425Ssteve			if (optstr[0] == ':') {
44320425Ssteve				s[0] = c;
44420425Ssteve				s[1] = '\0';
44520425Ssteve				err |= setvarsafe("OPTARG", s, 0);
44620425Ssteve				c = ':';
44720425Ssteve			}
44820425Ssteve			else {
44920425Ssteve				out1fmt("No arg for -%c option\n", c);
45020425Ssteve				(void) unsetvar("OPTARG");
45120425Ssteve				c = '?';
45220425Ssteve			}
45320425Ssteve			goto bad;
4541556Srgrimes		}
45520425Ssteve
45620425Ssteve		if (p == **optnext)
45720425Ssteve			(*optnext)++;
45820425Ssteve		setvarsafe("OPTARG", p, 0);
4591556Srgrimes		p = NULL;
4601556Srgrimes	}
46120425Ssteve	else
46220425Ssteve		setvarsafe("OPTARG", "", 0);
46320425Ssteve	ind = *optnext - optfirst + 1;
46420425Ssteve	goto out;
46520425Ssteve
46620425Sstevebad:
46720425Ssteve	ind = 1;
46820425Ssteve	*optnext = NULL;
46920425Ssteve	p = NULL;
4701556Srgrimesout:
47120425Ssteve	*optptr = p;
47220425Ssteve	fmtstr(s, sizeof(s), "%d", ind);
47320425Ssteve	err |= setvarsafe("OPTIND", s, VNOFUNC);
4741556Srgrimes	s[0] = c;
4751556Srgrimes	s[1] = '\0';
47620425Ssteve	err |= setvarsafe(optvar, s, 0);
47720425Ssteve	if (err) {
47820425Ssteve		*optnext = NULL;
47920425Ssteve		*optptr = NULL;
48020425Ssteve		flushall();
48120425Ssteve		exraise(EXERROR);
48220425Ssteve	}
48320425Ssteve	return done;
4841556Srgrimes}
4851556Srgrimes
4861556Srgrimes/*
4871556Srgrimes * XXX - should get rid of.  have all builtins use getopt(3).  the
4881556Srgrimes * library getopt must have the BSD extension static variable "optreset"
4891556Srgrimes * otherwise it can't be used within the shell safely.
4901556Srgrimes *
4911556Srgrimes * Standard option processing (a la getopt) for builtin routines.  The
4921556Srgrimes * only argument that is passed to nextopt is the option string; the
4931556Srgrimes * other arguments are unnecessary.  It return the character, or '\0' on
4941556Srgrimes * end of input.
4951556Srgrimes */
4961556Srgrimes
4971556Srgrimesint
49890111Simpnextopt(char *optstring)
49990111Simp{
50025227Ssteve	char *p, *q;
5011556Srgrimes	char c;
5021556Srgrimes
5031556Srgrimes	if ((p = optptr) == NULL || *p == '\0') {
5041556Srgrimes		p = *argptr;
5051556Srgrimes		if (p == NULL || *p != '-' || *++p == '\0')
5061556Srgrimes			return '\0';
5071556Srgrimes		argptr++;
5081556Srgrimes		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
5091556Srgrimes			return '\0';
5101556Srgrimes	}
5111556Srgrimes	c = *p++;
5121556Srgrimes	for (q = optstring ; *q != c ; ) {
5131556Srgrimes		if (*q == '\0')
5141556Srgrimes			error("Illegal option -%c", c);
5151556Srgrimes		if (*++q == ':')
5161556Srgrimes			q++;
5171556Srgrimes	}
5181556Srgrimes	if (*++q == ':') {
5191556Srgrimes		if (*p == '\0' && (p = *argptr++) == NULL)
5201556Srgrimes			error("No arg for -%c option", c);
52159436Scracauer		shoptarg = p;
5221556Srgrimes		p = NULL;
5231556Srgrimes	}
5241556Srgrimes	optptr = p;
5251556Srgrimes	return c;
5261556Srgrimes}
527