options.c revision 25227
150397Sobrien/*-
290075Sobrien * Copyright (c) 1991, 1993
390075Sobrien *	The Regents of the University of California.  All rights reserved.
450397Sobrien *
550397Sobrien * This code is derived from software contributed to Berkeley by
650397Sobrien * Kenneth Almquist.
750397Sobrien *
890075Sobrien * Redistribution and use in source and binary forms, with or without
950397Sobrien * modification, are permitted provided that the following conditions
1090075Sobrien * are met:
1190075Sobrien * 1. Redistributions of source code must retain the above copyright
1290075Sobrien *    notice, this list of conditions and the following disclaimer.
1390075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1450397Sobrien *    notice, this list of conditions and the following disclaimer in the
1590075Sobrien *    documentation and/or other materials provided with the distribution.
1690075Sobrien * 3. All advertising materials mentioning features or use of this software
1790075Sobrien *    must display the following acknowledgement:
1890075Sobrien *	This product includes software developed by the University of
1950397Sobrien *	California, Berkeley and its contributors.
2050397Sobrien * 4. Neither the name of the University nor the names of its contributors
2190075Sobrien *    may be used to endorse or promote products derived from this software
2290075Sobrien *    without specific prior written permission.
2390075Sobrien *
2450397Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2790075Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2890075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3090075Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3150397Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3250397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3350397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3450397Sobrien * SUCH DAMAGE.
3550397Sobrien *
3650397Sobrien *	$Id: options.c,v 1.12 1997/02/22 13:58:40 peter Exp $
3750397Sobrien */
3850397Sobrien
3950397Sobrien#ifndef lint
4050397Sobrienstatic char const sccsid[] = "@(#)options.c	8.2 (Berkeley) 5/4/95";
41117395Skan#endif /* not lint */
4250397Sobrien
4350397Sobrien#include <signal.h>
4450397Sobrien#include <unistd.h>
4550397Sobrien#include <stdlib.h>
4650397Sobrien
4790075Sobrien#include "shell.h"
4850397Sobrien#define DEFINE_OPTIONS
4950397Sobrien#include "options.h"
5090075Sobrien#undef DEFINE_OPTIONS
5150397Sobrien#include "nodes.h"	/* for other header files */
5250397Sobrien#include "eval.h"
5350397Sobrien#include "jobs.h"
5490075Sobrien#include "input.h"
5550397Sobrien#include "output.h"
5690075Sobrien#include "trap.h"
5790075Sobrien#include "var.h"
5890075Sobrien#include "memalloc.h"
5990075Sobrien#include "error.h"
6090075Sobrien#include "mystring.h"
6190075Sobrien#ifndef NO_HISTORY
6290075Sobrien#include "myhistedit.h"
6390075Sobrien#endif
6490075Sobrien
65117395Skanchar *arg0;			/* value of $0 */
6650397Sobrienstruct shparam shellparam;	/* current positional parameters */
6790075Sobrienchar **argptr;			/* argument list for builtin commands */
6890075Sobrienchar *optarg;			/* set by nextopt (like getopt) */
6950397Sobrienchar *optptr;			/* used by nextopt */
7050397Sobrien
7190075Sobrienchar *minusc;			/* argument to -c option */
7290075Sobrien
7390075Sobrien
7490075SobrienSTATIC void options __P((int));
7590075SobrienSTATIC void minus_o __P((char *, int));
7690075SobrienSTATIC void setoption __P((int, int));
7790075SobrienSTATIC int getopts __P((char *, char *, char **, char ***, char **));
7890075Sobrien
7990075Sobrien
8090075Sobrien/*
8190075Sobrien * Process the shell command line arguments.
8290075Sobrien */
8390075Sobrien
8490075Sobrienvoid
8590075Sobrienprocargs(argc, argv)
8690075Sobrien	int argc;
8790075Sobrien	char **argv;
8890075Sobrien{
8950397Sobrien	int i;
9050397Sobrien
9150397Sobrien	argptr = argv;
9250397Sobrien	if (argc > 0)
9350397Sobrien		argptr++;
9450397Sobrien	for (i = 0; i < NOPTS; i++)
9550397Sobrien		optlist[i].val = 2;
9690075Sobrien	privileged = (getuid() != geteuid() || getgid() != getegid());
9750397Sobrien	options(1);
9890075Sobrien	if (*argptr == NULL && minusc == NULL)
9950397Sobrien		sflag = 1;
10050397Sobrien	if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1))
10190075Sobrien		iflag = 1;
10290075Sobrien	if (mflag == 2)
10350397Sobrien		mflag = iflag;
10450397Sobrien	for (i = 0; i < NOPTS; i++)
10550397Sobrien		if (optlist[i].val == 2)
10650397Sobrien			optlist[i].val = 0;
10790075Sobrien	arg0 = argv[0];
10890075Sobrien	if (sflag == 0 && minusc == NULL) {
10990075Sobrien		commandname = arg0 = *argptr++;
11090075Sobrien		setinputfile(commandname, 0);
11190075Sobrien	}
11290075Sobrien	/* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */
11390075Sobrien	if (argptr && minusc && *argptr)
11490075Sobrien		arg0 = *argptr++;
11590075Sobrien
11690075Sobrien	shellparam.p = argptr;
11790075Sobrien	shellparam.reset = 1;
11890075Sobrien	/* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */
11990075Sobrien	while (*argptr) {
120117395Skan		shellparam.nparam++;
121117395Skan		argptr++;
122117395Skan	}
123117395Skan	optschanged();
124117395Skan}
125117395Skan
126117395Skan
127117395Skanvoid
128117395Skanoptschanged()
129117395Skan{
130117395Skan	setinteractive(iflag);
131117395Skan#ifndef NO_HISTORY
132117395Skan	histedit();
133117395Skan#endif
134117395Skan	setjobctl(mflag);
135117395Skan}
13690075Sobrien
137117395Skan/*
13890075Sobrien * Process shell options.  The global variable argptr contains a pointer
13990075Sobrien * to the argument list; we advance it past the options.
14090075Sobrien */
14190075Sobrien
14290075SobrienSTATIC void
143117395Skanoptions(cmdline)
14490075Sobrien	int cmdline;
14590075Sobrien{
14690075Sobrien	char *p;
14790075Sobrien	int val;
148117395Skan	int c;
149117395Skan
150117395Skan	if (cmdline)
151117395Skan		minusc = NULL;
152117395Skan	while ((p = *argptr) != NULL) {
153117395Skan		argptr++;
154117395Skan		if ((c = *p++) == '-') {
155117395Skan			val = 1;
156117395Skan                        if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) {
157117395Skan                                if (!cmdline) {
158117395Skan                                        /* "-" means turn off -x and -v */
159117395Skan                                        if (p[0] == '\0')
160117395Skan                                                xflag = vflag = 0;
161117395Skan                                        /* "--" means reset params */
162117395Skan                                        else if (*argptr == NULL)
163117395Skan						setparam(argptr);
16450397Sobrien                                }
16550397Sobrien				break;	  /* "-" or  "--" terminates options */
16650397Sobrien			}
16750397Sobrien		} else if (c == '+') {
16850397Sobrien			val = 0;
16950397Sobrien		} else {
17050397Sobrien			argptr--;
17150397Sobrien			break;
17250397Sobrien		}
17350397Sobrien		while ((c = *p++) != '\0') {
17450397Sobrien			if (c == 'c' && cmdline) {
17550397Sobrien				char *q;
17650397Sobrien#ifdef NOHACK	/* removing this code allows sh -ce 'foo' for compat */
17750397Sobrien				if (*p == '\0')
17850397Sobrien#endif
17950397Sobrien					q = *argptr++;
18050397Sobrien				if (q == NULL || minusc != NULL)
18150397Sobrien					error("Bad -c option");
18250397Sobrien				minusc = q;
18350397Sobrien#ifdef NOHACK
18490075Sobrien				break;
18590075Sobrien#endif
18650397Sobrien			} else if (c == 'o') {
18750397Sobrien				minus_o(*argptr, val);
18850397Sobrien				if (*argptr)
18950397Sobrien					argptr++;
19050397Sobrien			} else {
19150397Sobrien				if (c == 'p' && !val && privileged) {
19250397Sobrien					(void) setuid(getuid());
19350397Sobrien					(void) setgid(getgid());
19450397Sobrien				}
19550397Sobrien				setoption(c, val);
19650397Sobrien			}
19750397Sobrien		}
19890075Sobrien	}
19990075Sobrien}
20090075Sobrien
20190075SobrienSTATIC void
20290075Sobrienminus_o(name, val)
20390075Sobrien	char *name;
20490075Sobrien	int val;
20590075Sobrien{
20690075Sobrien	int i;
20790075Sobrien
20890075Sobrien	if (name == NULL) {
20990075Sobrien		out1str("Current option settings\n");
21090075Sobrien		for (i = 0; i < NOPTS; i++)
21150397Sobrien			out1fmt("%-16s%s\n", optlist[i].name,
21250397Sobrien				optlist[i].val ? "on" : "off");
21390075Sobrien	} else {
21450397Sobrien		for (i = 0; i < NOPTS; i++)
21550397Sobrien			if (equal(name, optlist[i].name)) {
21650397Sobrien				if (!val && privileged && equal(name, "privileged")) {
21750397Sobrien					(void) setuid(getuid());
21850397Sobrien					(void) setgid(getgid());
21990075Sobrien				}
22090075Sobrien				setoption(optlist[i].letter, val);
22190075Sobrien				return;
22250397Sobrien			}
22390075Sobrien		error("Illegal option -o %s", name);
224117395Skan	}
22590075Sobrien}
22690075Sobrien
22750397Sobrien
22850397SobrienSTATIC void
22950397Sobriensetoption(flag, val)
23090075Sobrien	char flag;
23150397Sobrien	int val;
23250397Sobrien	{
23390075Sobrien	int i;
23490075Sobrien
23590075Sobrien	for (i = 0; i < NOPTS; i++)
23690075Sobrien		if (optlist[i].letter == flag) {
23790075Sobrien			optlist[i].val = val;
23890075Sobrien			if (val) {
23990075Sobrien				/* #%$ hack for ksh semantics */
24050397Sobrien				if (flag == 'V')
24150397Sobrien					Eflag = 0;
24250397Sobrien				else if (flag == 'E')
24390075Sobrien					Vflag = 0;
24490075Sobrien			}
24590075Sobrien			return;
24650397Sobrien		}
24750397Sobrien	error("Illegal option -%c", flag);
24850397Sobrien}
24950397Sobrien
25050397Sobrien
25150397Sobrien
25250397Sobrien#ifdef mkinit
25350397SobrienINCLUDE "options.h"
25450397Sobrien
25590075SobrienSHELLPROC {
25650397Sobrien	int i;
25750397Sobrien
25890075Sobrien	for (i = 0; i < NOPTS; i++)
25950397Sobrien		optlist[i].val = 0;
26090075Sobrien	optschanged();
26150397Sobrien
26290075Sobrien}
26350397Sobrien#endif
26490075Sobrien
26550397Sobrien
26650397Sobrien/*
26750397Sobrien * Set the shell parameters.
26850397Sobrien */
26950397Sobrien
27050397Sobrienvoid
27150397Sobriensetparam(argv)
27250397Sobrien	char **argv;
27350397Sobrien	{
27450397Sobrien	char **newparam;
27550397Sobrien	char **ap;
27650397Sobrien	int nparam;
27750397Sobrien
27850397Sobrien	for (nparam = 0 ; argv[nparam] ; nparam++);
27950397Sobrien	ap = newparam = ckmalloc((nparam + 1) * sizeof *ap);
28050397Sobrien	while (*argv) {
28150397Sobrien		*ap++ = savestr(*argv++);
28250397Sobrien	}
28350397Sobrien	*ap = NULL;
28450397Sobrien	freeparam(&shellparam);
28590075Sobrien	shellparam.malloc = 1;
28650397Sobrien	shellparam.nparam = nparam;
28750397Sobrien	shellparam.p = newparam;
28850397Sobrien	shellparam.optnext = NULL;
28990075Sobrien}
29090075Sobrien
29190075Sobrien
29290075Sobrien/*
29390075Sobrien * Free the list of positional parameters.
29490075Sobrien */
29590075Sobrien
29690075Sobrienvoid
29790075Sobrienfreeparam(param)
29890075Sobrien	struct shparam *param;
29950397Sobrien	{
30050397Sobrien	char **ap;
30190075Sobrien
30290075Sobrien	if (param->malloc) {
30390075Sobrien		for (ap = param->p ; *ap ; ap++)
30490075Sobrien			ckfree(*ap);
30590075Sobrien		ckfree(param->p);
30690075Sobrien	}
30790075Sobrien}
30890075Sobrien
30990075Sobrien
31090075Sobrien
31190075Sobrien/*
31290075Sobrien * The shift builtin command.
31390075Sobrien */
31490075Sobrien
31590075Sobrienint
31690075Sobrienshiftcmd(argc, argv)
31790075Sobrien	int argc;
31890075Sobrien	char **argv;
31990075Sobrien{
32050397Sobrien	int n;
32190075Sobrien	char **ap1, **ap2;
32290075Sobrien
32390075Sobrien	n = 1;
32490075Sobrien	if (argc > 1)
32590075Sobrien		n = number(argv[1]);
32690075Sobrien	if (n > shellparam.nparam)
32790075Sobrien		error("can't shift that many");
32890075Sobrien	INTOFF;
32950397Sobrien	shellparam.nparam -= n;
33090075Sobrien	for (ap1 = shellparam.p ; --n >= 0 ; ap1++) {
33190075Sobrien		if (shellparam.malloc)
33290075Sobrien			ckfree(*ap1);
33350397Sobrien	}
33450397Sobrien	ap2 = shellparam.p;
33550397Sobrien	while ((*ap2++ = *ap1++) != NULL);
33650397Sobrien	shellparam.optnext = NULL;
33750397Sobrien	INTON;
33850397Sobrien	return 0;
33990075Sobrien}
34090075Sobrien
34150397Sobrien
34250397Sobrien
34350397Sobrien/*
34450397Sobrien * The set command builtin.
34550397Sobrien */
34690075Sobrien
34750397Sobrienint
34850397Sobriensetcmd(argc, argv)
34950397Sobrien	int argc;
35090075Sobrien	char **argv;
35190075Sobrien{
35250397Sobrien	if (argc == 1)
35350397Sobrien		return showvarscmd(argc, argv);
35490075Sobrien	INTOFF;
35590075Sobrien	options(0);
35650397Sobrien	optschanged();
35790075Sobrien	if (*argptr != NULL) {
35890075Sobrien		setparam(argptr);
35990075Sobrien	}
36090075Sobrien	INTON;
36190075Sobrien	return 0;
36250397Sobrien}
36350397Sobrien
36450397Sobrien
36550397Sobrienvoid
36650397Sobriengetoptsreset(value)
36750397Sobrien	const char *value;
36850397Sobrien{
36950397Sobrien	if (number(value) == 1) {
37090075Sobrien		shellparam.optnext = NULL;
37150397Sobrien		shellparam.reset = 1;
37250397Sobrien	}
37350397Sobrien}
37450397Sobrien
37550397Sobrien/*
37650397Sobrien * The getopts builtin.  Shellparam.optnext points to the next argument
37750397Sobrien * to be processed.  Shellparam.optptr points to the next character to
37850397Sobrien * be processed in the current argument.  If shellparam.optnext is NULL,
37950397Sobrien * then it's the first time getopts has been called.
38090075Sobrien */
38190075Sobrien
38290075Sobrienint
38390075Sobriengetoptscmd(argc, argv)
38490075Sobrien	int argc;
38590075Sobrien	char **argv;
38650397Sobrien{
38750397Sobrien	char **optbase = NULL;
38850397Sobrien
389117395Skan	if (argc < 3)
39050397Sobrien		error("Usage: getopts optstring var [arg]");
391117395Skan	else if (argc == 3)
39250397Sobrien		optbase = shellparam.p;
39350397Sobrien	else
39450397Sobrien		optbase = &argv[3];
39550397Sobrien
39650397Sobrien	if (shellparam.reset == 1) {
39750397Sobrien		shellparam.optnext = optbase;
39850397Sobrien		shellparam.optptr = NULL;
39990075Sobrien		shellparam.reset = 0;
40050397Sobrien	}
40150397Sobrien
40250397Sobrien	return getopts(argv[1], argv[2], optbase, &shellparam.optnext,
40350397Sobrien		       &shellparam.optptr);
40450397Sobrien}
40550397Sobrien
40650397SobrienSTATIC int
40750397Sobriengetopts(optstr, optvar, optfirst, optnext, optptr)
40850397Sobrien	char *optstr;
40950397Sobrien	char *optvar;
41050397Sobrien	char **optfirst;
41150397Sobrien	char ***optnext;
41250397Sobrien	char **optptr;
41390075Sobrien{
41450397Sobrien	char *p, *q;
41590075Sobrien	char c = '?';
41690075Sobrien	int done = 0;
41790075Sobrien	int ind = 0;
41850397Sobrien	int err = 0;
41990075Sobrien	char s[10];
42090075Sobrien
42190075Sobrien	if ((p = *optptr) == NULL || *p == '\0') {
42290075Sobrien		/* Current word is done, advance */
42350397Sobrien		if (*optnext == NULL)
424102780Skan			return 1;
425102780Skan		p = **optnext;
426102780Skan		if (p == NULL || *p != '-' || *++p == '\0') {
427102780Skanatend:
428102780Skan			ind = *optnext - optfirst + 1;
42950397Sobrien			*optnext = NULL;
430102780Skan			p = NULL;
431102780Skan			done = 1;
43250397Sobrien			goto out;
433102780Skan		}
434102780Skan		(*optnext)++;
435122180Skan		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
436122180Skan			goto atend;
437122180Skan	}
438122180Skan
439122180Skan	c = *p++;
440122180Skan	for (q = optstr; *q != c; ) {
441122180Skan		if (*q == '\0') {
442122180Skan			if (optstr[0] == ':') {
443122180Skan				s[0] = c;
44450397Sobrien				s[1] = '\0';
44550397Sobrien				err |= setvarsafe("OPTARG", s, 0);
44650397Sobrien			}
44750397Sobrien			else {
44890075Sobrien				out1fmt("Illegal option -%c\n", c);
44950397Sobrien				(void) unsetvar("OPTARG");
45090075Sobrien			}
45150397Sobrien			c = '?';
45250397Sobrien			goto bad;
45350397Sobrien		}
45450397Sobrien		if (*++q == ':')
45550397Sobrien			q++;
45650397Sobrien	}
45750397Sobrien
45850397Sobrien	if (*++q == ':') {
45950397Sobrien		if (*p == '\0' && (p = **optnext) == NULL) {
46050397Sobrien			if (optstr[0] == ':') {
46150397Sobrien				s[0] = c;
46250397Sobrien				s[1] = '\0';
46350397Sobrien				err |= setvarsafe("OPTARG", s, 0);
46450397Sobrien				c = ':';
46550397Sobrien			}
46650397Sobrien			else {
46750397Sobrien				out1fmt("No arg for -%c option\n", c);
46850397Sobrien				(void) unsetvar("OPTARG");
46950397Sobrien				c = '?';
47050397Sobrien			}
47150397Sobrien			goto bad;
47250397Sobrien		}
47350397Sobrien
47450397Sobrien		if (p == **optnext)
47550397Sobrien			(*optnext)++;
47650397Sobrien		setvarsafe("OPTARG", p, 0);
47750397Sobrien		p = NULL;
47850397Sobrien	}
47950397Sobrien	else
48050397Sobrien		setvarsafe("OPTARG", "", 0);
48150397Sobrien	ind = *optnext - optfirst + 1;
48250397Sobrien	goto out;
48350397Sobrien
48450397Sobrienbad:
48550397Sobrien	ind = 1;
48650397Sobrien	*optnext = NULL;
48750397Sobrien	p = NULL;
48850397Sobrienout:
48950397Sobrien	*optptr = p;
49050397Sobrien	fmtstr(s, sizeof(s), "%d", ind);
49190075Sobrien	err |= setvarsafe("OPTIND", s, VNOFUNC);
49290075Sobrien	s[0] = c;
49390075Sobrien	s[1] = '\0';
49490075Sobrien	err |= setvarsafe(optvar, s, 0);
49590075Sobrien	if (err) {
49690075Sobrien		*optnext = NULL;
49790075Sobrien		*optptr = NULL;
49890075Sobrien		flushall();
49990075Sobrien		exraise(EXERROR);
50090075Sobrien	}
50190075Sobrien	return done;
50290075Sobrien}
50350397Sobrien
50450397Sobrien/*
50550397Sobrien * XXX - should get rid of.  have all builtins use getopt(3).  the
50650397Sobrien * library getopt must have the BSD extension static variable "optreset"
50750397Sobrien * otherwise it can't be used within the shell safely.
50850397Sobrien *
50950397Sobrien * Standard option processing (a la getopt) for builtin routines.  The
51050397Sobrien * only argument that is passed to nextopt is the option string; the
51150397Sobrien * other arguments are unnecessary.  It return the character, or '\0' on
51270635Sobrien * end of input.
51370635Sobrien */
51450397Sobrien
51550397Sobrienint
51650397Sobriennextopt(optstring)
51750397Sobrien	char *optstring;
51850397Sobrien	{
51950397Sobrien	char *p, *q;
52050397Sobrien	char c;
52150397Sobrien
52250397Sobrien	if ((p = optptr) == NULL || *p == '\0') {
52350397Sobrien		p = *argptr;
52450397Sobrien		if (p == NULL || *p != '-' || *++p == '\0')
52590075Sobrien			return '\0';
52650397Sobrien		argptr++;
52750397Sobrien		if (p[0] == '-' && p[1] == '\0')	/* check for "--" */
52850397Sobrien			return '\0';
52950397Sobrien	}
53050397Sobrien	c = *p++;
53150397Sobrien	for (q = optstring ; *q != c ; ) {
53250397Sobrien		if (*q == '\0')
53350397Sobrien			error("Illegal option -%c", c);
53450397Sobrien		if (*++q == ':')
53550397Sobrien			q++;
53650397Sobrien	}
53750397Sobrien	if (*++q == ':') {
53890075Sobrien		if (*p == '\0' && (p = *argptr++) == NULL)
53990075Sobrien			error("No arg for -%c option", c);
54050397Sobrien		optarg = p;
54190075Sobrien		p = NULL;
54250397Sobrien	}
54350397Sobrien	optptr = p;
54450397Sobrien	return c;
54550397Sobrien}
54650397Sobrien