var.c revision 114763
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[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
4036150Scharnier#endif
411556Srgrimes#endif /* not lint */
4299110Sobrien#include <sys/cdefs.h>
4399110Sobrien__FBSDID("$FreeBSD: head/bin/sh/var.c 114763 2003-05-05 22:49:23Z obrien $");
441556Srgrimes
4517987Speter#include <unistd.h>
4617987Speter#include <stdlib.h>
47114763Sobrien#include <paths.h>
4817987Speter
491556Srgrimes/*
501556Srgrimes * Shell variables.
511556Srgrimes */
521556Srgrimes
5317525Sache#include <locale.h>
5417525Sache
551556Srgrimes#include "shell.h"
561556Srgrimes#include "output.h"
571556Srgrimes#include "expand.h"
581556Srgrimes#include "nodes.h"	/* for other headers */
591556Srgrimes#include "eval.h"	/* defines cmdenviron */
601556Srgrimes#include "exec.h"
611556Srgrimes#include "syntax.h"
621556Srgrimes#include "options.h"
631556Srgrimes#include "mail.h"
641556Srgrimes#include "var.h"
651556Srgrimes#include "memalloc.h"
661556Srgrimes#include "error.h"
671556Srgrimes#include "mystring.h"
6820425Ssteve#include "parser.h"
6917987Speter#ifndef NO_HISTORY
7017987Speter#include "myhistedit.h"
7117987Speter#endif
721556Srgrimes
731556Srgrimes
741556Srgrimes#define VTABSIZE 39
751556Srgrimes
761556Srgrimes
771556Srgrimesstruct varinit {
781556Srgrimes	struct var *var;
791556Srgrimes	int flags;
801556Srgrimes	char *text;
8190111Simp	void (*func)(const char *);
821556Srgrimes};
831556Srgrimes
841556Srgrimes
8517987Speter#ifndef NO_HISTORY
861556Srgrimesstruct var vhistsize;
8717987Speter#endif
881556Srgrimesstruct var vifs;
891556Srgrimesstruct var vmail;
901556Srgrimesstruct var vmpath;
911556Srgrimesstruct var vpath;
9297689Stjrstruct var vppid;
931556Srgrimesstruct var vps1;
941556Srgrimesstruct var vps2;
951556Srgrimesstruct var vvers;
9620425Sstevestruct var voptind;
971556Srgrimes
981556Srgrimesconst struct varinit varinit[] = {
9917987Speter#ifndef NO_HISTORY
10020425Ssteve	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
10120425Ssteve	  sethistsize },
10217987Speter#endif
10320425Ssteve	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
10420425Ssteve	  NULL },
10520425Ssteve	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
10620425Ssteve	  NULL },
10720425Ssteve	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
10820425Ssteve	  NULL },
109114763Sobrien	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=" _PATH_DEFPATH,
11020425Ssteve	  changepath },
11197689Stjr	{ &vppid,	VSTRFIXED|VTEXTFIXED|VUNSET,	"PPID=",
11297689Stjr	  NULL },
1138855Srgrimes	/*
1141556Srgrimes	 * vps1 depends on uid
1151556Srgrimes	 */
11620425Ssteve	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
11720425Ssteve	  NULL },
11820425Ssteve	{ &voptind,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1",
11920425Ssteve	  getoptsreset },
12020425Ssteve	{ NULL,	0,				NULL,
12120425Ssteve	  NULL }
1221556Srgrimes};
1231556Srgrimes
1241556Srgrimesstruct var *vartab[VTABSIZE];
1251556Srgrimes
12690111SimpSTATIC struct var **hashvar(char *);
12790111SimpSTATIC int varequal(char *, char *);
12890111SimpSTATIC int localevar(char *);
1291556Srgrimes
1301556Srgrimes/*
1311556Srgrimes * Initialize the varable symbol tables and import the environment
1321556Srgrimes */
1331556Srgrimes
1341556Srgrimes#ifdef mkinit
1351556SrgrimesINCLUDE "var.h"
1361556SrgrimesINIT {
1371556Srgrimes	char **envp;
1381556Srgrimes	extern char **environ;
1391556Srgrimes
1401556Srgrimes	initvar();
1411556Srgrimes	for (envp = environ ; *envp ; envp++) {
1421556Srgrimes		if (strchr(*envp, '=')) {
1431556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1441556Srgrimes		}
1451556Srgrimes	}
1461556Srgrimes}
1471556Srgrimes#endif
1481556Srgrimes
1491556Srgrimes
1501556Srgrimes/*
1511556Srgrimes * This routine initializes the builtin variables.  It is called when the
1521556Srgrimes * shell is initialized and again when a shell procedure is spawned.
1531556Srgrimes */
1541556Srgrimes
1551556Srgrimesvoid
15690111Simpinitvar(void)
15790111Simp{
15897689Stjr	char ppid[20];
1591556Srgrimes	const struct varinit *ip;
1601556Srgrimes	struct var *vp;
1611556Srgrimes	struct var **vpp;
1621556Srgrimes
1631556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1641556Srgrimes		if ((vp->flags & VEXPORT) == 0) {
1651556Srgrimes			vpp = hashvar(ip->text);
1661556Srgrimes			vp->next = *vpp;
1671556Srgrimes			*vpp = vp;
1681556Srgrimes			vp->text = ip->text;
1691556Srgrimes			vp->flags = ip->flags;
17020425Ssteve			vp->func = ip->func;
1711556Srgrimes		}
1721556Srgrimes	}
1731556Srgrimes	/*
1741556Srgrimes	 * PS1 depends on uid
1751556Srgrimes	 */
1761556Srgrimes	if ((vps1.flags & VEXPORT) == 0) {
1771556Srgrimes		vpp = hashvar("PS1=");
1781556Srgrimes		vps1.next = *vpp;
1791556Srgrimes		*vpp = &vps1;
1801556Srgrimes		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
1811556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1821556Srgrimes	}
18397689Stjr	if ((vppid.flags & VEXPORT) == 0) {
18497689Stjr		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
18597689Stjr		setvarsafe("PPID", ppid, 0);
18697689Stjr	}
1871556Srgrimes}
1881556Srgrimes
1891556Srgrimes/*
19020425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
19120425Ssteve */
19220425Ssteve
19320425Ssteveint
19490111Simpsetvarsafe(char *name, char *val, int flags)
19520425Ssteve{
19620425Ssteve	struct jmploc jmploc;
19720425Ssteve	struct jmploc *volatile savehandler = handler;
19820425Ssteve	int err = 0;
19920425Ssteve#if __GNUC__
20020425Ssteve	/* Avoid longjmp clobbering */
20120425Ssteve	(void) &err;
20220425Ssteve#endif
20320425Ssteve
20420425Ssteve	if (setjmp(jmploc.loc))
20520425Ssteve		err = 1;
20620425Ssteve	else {
20720425Ssteve		handler = &jmploc;
20820425Ssteve		setvar(name, val, flags);
20920425Ssteve	}
21020425Ssteve	handler = savehandler;
21120425Ssteve	return err;
21220425Ssteve}
21320425Ssteve
21420425Ssteve/*
21545621Scracauer * Set the value of a variable.  The flags argument is tored with the
2161556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2171556Srgrimes */
2181556Srgrimes
2191556Srgrimesvoid
22090111Simpsetvar(char *name, char *val, int flags)
22117987Speter{
2221556Srgrimes	char *p, *q;
2231556Srgrimes	int len;
2241556Srgrimes	int namelen;
2251556Srgrimes	char *nameeq;
2261556Srgrimes	int isbad;
2271556Srgrimes
2281556Srgrimes	isbad = 0;
2291556Srgrimes	p = name;
23017525Sache	if (! is_name(*p))
2311556Srgrimes		isbad = 1;
23217525Sache	p++;
2331556Srgrimes	for (;;) {
2341556Srgrimes		if (! is_in_name(*p)) {
2351556Srgrimes			if (*p == '\0' || *p == '=')
2361556Srgrimes				break;
2371556Srgrimes			isbad = 1;
2381556Srgrimes		}
2391556Srgrimes		p++;
2401556Srgrimes	}
2411556Srgrimes	namelen = p - name;
2421556Srgrimes	if (isbad)
2431556Srgrimes		error("%.*s: bad variable name", namelen, name);
2441556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2451556Srgrimes	if (val == NULL) {
2461556Srgrimes		flags |= VUNSET;
2471556Srgrimes	} else {
2481556Srgrimes		len += strlen(val);
2491556Srgrimes	}
2501556Srgrimes	p = nameeq = ckmalloc(len);
2511556Srgrimes	q = name;
2521556Srgrimes	while (--namelen >= 0)
2531556Srgrimes		*p++ = *q++;
2541556Srgrimes	*p++ = '=';
2551556Srgrimes	*p = '\0';
2561556Srgrimes	if (val)
2571556Srgrimes		scopy(val, p);
2581556Srgrimes	setvareq(nameeq, flags);
2591556Srgrimes}
2601556Srgrimes
26117525SacheSTATIC int
26290111Simplocalevar(char *s)
26390111Simp{
26417525Sache	static char *lnames[7] = {
26517525Sache		"ALL", "COLLATE", "CTYPE", "MONETARY",
26617525Sache		"NUMERIC", "TIME", NULL
26717525Sache	};
26817525Sache	char **ss;
2691556Srgrimes
27017525Sache	if (*s != 'L')
27117525Sache		return 0;
27217525Sache	if (varequal(s + 1, "ANG"))
27317525Sache		return 1;
27417525Sache	if (strncmp(s + 1, "C_", 2) != 0)
27517525Sache		return 0;
27617525Sache	for (ss = lnames; *ss ; ss++)
27717525Sache		if (varequal(s + 3, *ss))
27817525Sache			return 1;
27917525Sache	return 0;
28017525Sache}
2811556Srgrimes
2821556Srgrimes/*
2831556Srgrimes * Same as setvar except that the variable and value are passed in
2841556Srgrimes * the first argument as name=value.  Since the first argument will
2851556Srgrimes * be actually stored in the table, it should not be a string that
2861556Srgrimes * will go away.
2871556Srgrimes */
2881556Srgrimes
2891556Srgrimesvoid
29090111Simpsetvareq(char *s, int flags)
29117987Speter{
2921556Srgrimes	struct var *vp, **vpp;
29390112Simp	int len;
2941556Srgrimes
29545263Scracauer	if (aflag)
29645263Scracauer		flags |= VEXPORT;
2971556Srgrimes	vpp = hashvar(s);
2981556Srgrimes	for (vp = *vpp ; vp ; vp = vp->next) {
2991556Srgrimes		if (varequal(s, vp->text)) {
3001556Srgrimes			if (vp->flags & VREADONLY) {
30190112Simp				len = strchr(s, '=') - s;
3021556Srgrimes				error("%.*s: is read only", len, s);
3031556Srgrimes			}
3041556Srgrimes			INTOFF;
30520425Ssteve
30620425Ssteve			if (vp->func && (flags & VNOFUNC) == 0)
30720425Ssteve				(*vp->func)(strchr(s, '=') + 1);
30820425Ssteve
3091556Srgrimes			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
3101556Srgrimes				ckfree(vp->text);
31120425Ssteve
31220425Ssteve			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
3131556Srgrimes			vp->flags |= flags;
3141556Srgrimes			vp->text = s;
31520425Ssteve
31620425Ssteve			/*
31720425Ssteve			 * We could roll this to a function, to handle it as
31820425Ssteve			 * a regular variable function callback, but why bother?
31920425Ssteve			 */
3201556Srgrimes			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
3211556Srgrimes				chkmail(1);
32217525Sache			if ((vp->flags & VEXPORT) && localevar(s)) {
32317525Sache				putenv(s);
32417525Sache				(void) setlocale(LC_ALL, "");
32517525Sache			}
3261556Srgrimes			INTON;
3271556Srgrimes			return;
3281556Srgrimes		}
3291556Srgrimes	}
3301556Srgrimes	/* not found */
3311556Srgrimes	vp = ckmalloc(sizeof (*vp));
3321556Srgrimes	vp->flags = flags;
3331556Srgrimes	vp->text = s;
3341556Srgrimes	vp->next = *vpp;
33520425Ssteve	vp->func = NULL;
33617525Sache	INTOFF;
3371556Srgrimes	*vpp = vp;
33817525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
33917525Sache		putenv(s);
34017525Sache		(void) setlocale(LC_ALL, "");
34117525Sache	}
34217525Sache	INTON;
3431556Srgrimes}
3441556Srgrimes
3451556Srgrimes
3461556Srgrimes
3471556Srgrimes/*
3481556Srgrimes * Process a linked list of variable assignments.
3491556Srgrimes */
3501556Srgrimes
3511556Srgrimesvoid
35290111Simplistsetvar(struct strlist *list)
35390111Simp{
3541556Srgrimes	struct strlist *lp;
3551556Srgrimes
3561556Srgrimes	INTOFF;
3571556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
3581556Srgrimes		setvareq(savestr(lp->text), 0);
3591556Srgrimes	}
3601556Srgrimes	INTON;
3611556Srgrimes}
3621556Srgrimes
3631556Srgrimes
3641556Srgrimes
3651556Srgrimes/*
3661556Srgrimes * Find the value of a variable.  Returns NULL if not set.
3671556Srgrimes */
3681556Srgrimes
3691556Srgrimeschar *
37090111Simplookupvar(char *name)
37190111Simp{
3721556Srgrimes	struct var *v;
3731556Srgrimes
3741556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
3751556Srgrimes		if (varequal(v->text, name)) {
3761556Srgrimes			if (v->flags & VUNSET)
3771556Srgrimes				return NULL;
3781556Srgrimes			return strchr(v->text, '=') + 1;
3791556Srgrimes		}
3801556Srgrimes	}
3811556Srgrimes	return NULL;
3821556Srgrimes}
3831556Srgrimes
3841556Srgrimes
3851556Srgrimes
3861556Srgrimes/*
3871556Srgrimes * Search the environment of a builtin command.  If the second argument
3881556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
3891556Srgrimes * exported.
3901556Srgrimes */
3911556Srgrimes
3921556Srgrimeschar *
39390111Simpbltinlookup(char *name, int doall)
39417987Speter{
3951556Srgrimes	struct strlist *sp;
3961556Srgrimes	struct var *v;
3971556Srgrimes
3981556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
3991556Srgrimes		if (varequal(sp->text, name))
4001556Srgrimes			return strchr(sp->text, '=') + 1;
4011556Srgrimes	}
4021556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
4031556Srgrimes		if (varequal(v->text, name)) {
40417987Speter			if ((v->flags & VUNSET)
40517987Speter			 || (!doall && (v->flags & VEXPORT) == 0))
4061556Srgrimes				return NULL;
4071556Srgrimes			return strchr(v->text, '=') + 1;
4081556Srgrimes		}
4091556Srgrimes	}
4101556Srgrimes	return NULL;
4111556Srgrimes}
4121556Srgrimes
4131556Srgrimes
4141556Srgrimes
4151556Srgrimes/*
4161556Srgrimes * Generate a list of exported variables.  This routine is used to construct
4171556Srgrimes * the third argument to execve when executing a program.
4181556Srgrimes */
4191556Srgrimes
4201556Srgrimeschar **
42190111Simpenvironment(void)
42290111Simp{
4231556Srgrimes	int nenv;
4241556Srgrimes	struct var **vpp;
4251556Srgrimes	struct var *vp;
4261556Srgrimes	char **env, **ep;
4271556Srgrimes
4281556Srgrimes	nenv = 0;
4291556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4301556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4311556Srgrimes			if (vp->flags & VEXPORT)
4321556Srgrimes				nenv++;
4331556Srgrimes	}
4341556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
4351556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4361556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4371556Srgrimes			if (vp->flags & VEXPORT)
4381556Srgrimes				*ep++ = vp->text;
4391556Srgrimes	}
4401556Srgrimes	*ep = NULL;
4411556Srgrimes	return env;
4421556Srgrimes}
4431556Srgrimes
4441556Srgrimes
4451556Srgrimes/*
4461556Srgrimes * Called when a shell procedure is invoked to clear out nonexported
4471556Srgrimes * variables.  It is also necessary to reallocate variables of with
4481556Srgrimes * VSTACK set since these are currently allocated on the stack.
4491556Srgrimes */
4501556Srgrimes
4511556Srgrimes#ifdef mkinit
4521556SrgrimesMKINIT void shprocvar();
4531556Srgrimes
4541556SrgrimesSHELLPROC {
4551556Srgrimes	shprocvar();
4561556Srgrimes}
4571556Srgrimes#endif
4581556Srgrimes
4591556Srgrimesvoid
46090111Simpshprocvar(void)
46190111Simp{
4621556Srgrimes	struct var **vpp;
4631556Srgrimes	struct var *vp, **prev;
4641556Srgrimes
4651556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4661556Srgrimes		for (prev = vpp ; (vp = *prev) != NULL ; ) {
4671556Srgrimes			if ((vp->flags & VEXPORT) == 0) {
4681556Srgrimes				*prev = vp->next;
4691556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
4701556Srgrimes					ckfree(vp->text);
4711556Srgrimes				if ((vp->flags & VSTRFIXED) == 0)
4721556Srgrimes					ckfree(vp);
4731556Srgrimes			} else {
4741556Srgrimes				if (vp->flags & VSTACK) {
4751556Srgrimes					vp->text = savestr(vp->text);
4761556Srgrimes					vp->flags &=~ VSTACK;
4771556Srgrimes				}
4781556Srgrimes				prev = &vp->next;
4791556Srgrimes			}
4801556Srgrimes		}
4811556Srgrimes	}
4821556Srgrimes	initvar();
4831556Srgrimes}
4841556Srgrimes
4851556Srgrimes
4861556Srgrimes
4871556Srgrimes/*
4881556Srgrimes * Command to list all variables which are set.  Currently this command
4891556Srgrimes * is invoked from the set command when the set command is called without
4901556Srgrimes * any variables.
4911556Srgrimes */
4921556Srgrimes
4931556Srgrimesint
49490111Simpshowvarscmd(int argc __unused, char **argv __unused)
49517987Speter{
4961556Srgrimes	struct var **vpp;
4971556Srgrimes	struct var *vp;
49897915Stjr	const char *s;
4991556Srgrimes
5001556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5011556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next) {
50297915Stjr			if (vp->flags & VUNSET)
50397915Stjr				continue;
50497915Stjr			for (s = vp->text; *s != '='; s++)
50597915Stjr				out1c(*s);
50697915Stjr			out1c('=');
50797915Stjr			out1qstr(s + 1);
50897915Stjr			out1c('\n');
5091556Srgrimes		}
5101556Srgrimes	}
5111556Srgrimes	return 0;
5121556Srgrimes}
5131556Srgrimes
5141556Srgrimes
5151556Srgrimes
5161556Srgrimes/*
5171556Srgrimes * The export and readonly commands.
5181556Srgrimes */
5191556Srgrimes
5201556Srgrimesint
52190111Simpexportcmd(int argc, char **argv)
52217987Speter{
5231556Srgrimes	struct var **vpp;
5241556Srgrimes	struct var *vp;
5251556Srgrimes	char *name;
5261556Srgrimes	char *p;
52797914Stjr	char *cmdname;
52897914Stjr	int ch, values;
5291556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
5301556Srgrimes
53197914Stjr	cmdname = argv[0];
53297914Stjr	optreset = optind = 1;
533100663Stjr	opterr = 0;
53497914Stjr	values = 0;
53597914Stjr	while ((ch = getopt(argc, argv, "p")) != -1) {
53697914Stjr		switch (ch) {
53797914Stjr		case 'p':
53897914Stjr			values = 1;
53997914Stjr			break;
54097914Stjr		case '?':
54197914Stjr		default:
54297914Stjr			error("unknown option: -%c", optopt);
54397914Stjr		}
54497914Stjr	}
54597914Stjr	argc -= optind;
54697914Stjr	argv += optind;
54797914Stjr
5481556Srgrimes	listsetvar(cmdenviron);
54997914Stjr	if (argc != 0) {
5501556Srgrimes		while ((name = *argptr++) != NULL) {
5511556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
5521556Srgrimes				p++;
5531556Srgrimes			} else {
5541556Srgrimes				vpp = hashvar(name);
5551556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
5561556Srgrimes					if (varequal(vp->text, name)) {
55790111Simp
5581556Srgrimes						vp->flags |= flag;
55917525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
56017525Sache							putenv(vp->text);
56117525Sache							(void) setlocale(LC_ALL, "");
56217525Sache						}
5631556Srgrimes						goto found;
5641556Srgrimes					}
5651556Srgrimes				}
5661556Srgrimes			}
5671556Srgrimes			setvar(name, p, flag);
5681556Srgrimesfound:;
5691556Srgrimes		}
5701556Srgrimes	} else {
5711556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5721556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
5731556Srgrimes				if (vp->flags & flag) {
57497914Stjr					if (values) {
57597914Stjr						out1str(cmdname);
57697914Stjr						out1c(' ');
57797914Stjr					}
5781556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
5791556Srgrimes						out1c(*p);
58097914Stjr					if (values && !(vp->flags & VUNSET)) {
58197914Stjr						out1c('=');
58297914Stjr						out1qstr(p + 1);
58397914Stjr					}
5841556Srgrimes					out1c('\n');
5851556Srgrimes				}
5861556Srgrimes			}
5871556Srgrimes		}
5881556Srgrimes	}
5891556Srgrimes	return 0;
5901556Srgrimes}
5911556Srgrimes
5921556Srgrimes
5931556Srgrimes/*
5941556Srgrimes * The "local" command.
5951556Srgrimes */
5961556Srgrimes
59717987Speterint
59890111Simplocalcmd(int argc __unused, char **argv __unused)
59917987Speter{
6001556Srgrimes	char *name;
6011556Srgrimes
6021556Srgrimes	if (! in_function())
6031556Srgrimes		error("Not in a function");
6041556Srgrimes	while ((name = *argptr++) != NULL) {
6051556Srgrimes		mklocal(name);
6061556Srgrimes	}
6071556Srgrimes	return 0;
6081556Srgrimes}
6091556Srgrimes
6101556Srgrimes
6111556Srgrimes/*
6121556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
6131556Srgrimes * value and flags are saved in a localvar structure.  The saved values
6141556Srgrimes * will be restored when the shell function returns.  We handle the name
6151556Srgrimes * "-" as a special case.
6161556Srgrimes */
6171556Srgrimes
6181556Srgrimesvoid
61990111Simpmklocal(char *name)
62090111Simp{
6211556Srgrimes	struct localvar *lvp;
6221556Srgrimes	struct var **vpp;
6231556Srgrimes	struct var *vp;
6241556Srgrimes
6251556Srgrimes	INTOFF;
6261556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
6271556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
6281556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
62917987Speter		memcpy(lvp->text, optlist, sizeof optlist);
6301556Srgrimes		vp = NULL;
6311556Srgrimes	} else {
6321556Srgrimes		vpp = hashvar(name);
6331556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
6341556Srgrimes		if (vp == NULL) {
6351556Srgrimes			if (strchr(name, '='))
6361556Srgrimes				setvareq(savestr(name), VSTRFIXED);
6371556Srgrimes			else
6381556Srgrimes				setvar(name, NULL, VSTRFIXED);
6391556Srgrimes			vp = *vpp;	/* the new variable */
6401556Srgrimes			lvp->text = NULL;
6411556Srgrimes			lvp->flags = VUNSET;
6421556Srgrimes		} else {
6431556Srgrimes			lvp->text = vp->text;
6441556Srgrimes			lvp->flags = vp->flags;
6451556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
6461556Srgrimes			if (strchr(name, '='))
6471556Srgrimes				setvareq(savestr(name), 0);
6481556Srgrimes		}
6491556Srgrimes	}
6501556Srgrimes	lvp->vp = vp;
6511556Srgrimes	lvp->next = localvars;
6521556Srgrimes	localvars = lvp;
6531556Srgrimes	INTON;
6541556Srgrimes}
6551556Srgrimes
6561556Srgrimes
6571556Srgrimes/*
6581556Srgrimes * Called after a function returns.
6591556Srgrimes */
6601556Srgrimes
6611556Srgrimesvoid
66290111Simppoplocalvars(void)
66390111Simp{
6641556Srgrimes	struct localvar *lvp;
6651556Srgrimes	struct var *vp;
6661556Srgrimes
6671556Srgrimes	while ((lvp = localvars) != NULL) {
6681556Srgrimes		localvars = lvp->next;
6691556Srgrimes		vp = lvp->vp;
6701556Srgrimes		if (vp == NULL) {	/* $- saved */
67117987Speter			memcpy(optlist, lvp->text, sizeof optlist);
6721556Srgrimes			ckfree(lvp->text);
6731556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
6741556Srgrimes			(void)unsetvar(vp->text);
6751556Srgrimes		} else {
6761556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
6771556Srgrimes				ckfree(vp->text);
6781556Srgrimes			vp->flags = lvp->flags;
6791556Srgrimes			vp->text = lvp->text;
6801556Srgrimes		}
6811556Srgrimes		ckfree(lvp);
6821556Srgrimes	}
6831556Srgrimes}
6841556Srgrimes
6851556Srgrimes
68617987Speterint
68790111Simpsetvarcmd(int argc, char **argv)
68817987Speter{
6891556Srgrimes	if (argc <= 2)
6901556Srgrimes		return unsetcmd(argc, argv);
6911556Srgrimes	else if (argc == 3)
6921556Srgrimes		setvar(argv[1], argv[2], 0);
6931556Srgrimes	else
6941556Srgrimes		error("List assignment not implemented");
6951556Srgrimes	return 0;
6961556Srgrimes}
6971556Srgrimes
6981556Srgrimes
6991556Srgrimes/*
7001556Srgrimes * The unset builtin command.  We unset the function before we unset the
7011556Srgrimes * variable to allow a function to be unset when there is a readonly variable
7021556Srgrimes * with the same name.
7031556Srgrimes */
7041556Srgrimes
70517987Speterint
70690111Simpunsetcmd(int argc __unused, char **argv __unused)
70717987Speter{
7081556Srgrimes	char **ap;
7091556Srgrimes	int i;
7101556Srgrimes	int flg_func = 0;
7111556Srgrimes	int flg_var = 0;
7121556Srgrimes	int ret = 0;
7131556Srgrimes
7141556Srgrimes	while ((i = nextopt("vf")) != '\0') {
7151556Srgrimes		if (i == 'f')
7161556Srgrimes			flg_func = 1;
7171556Srgrimes		else
7181556Srgrimes			flg_var = 1;
7191556Srgrimes	}
7201556Srgrimes	if (flg_func == 0 && flg_var == 0)
7211556Srgrimes		flg_var = 1;
7228855Srgrimes
7231556Srgrimes	for (ap = argptr; *ap ; ap++) {
7241556Srgrimes		if (flg_func)
7251556Srgrimes			ret |= unsetfunc(*ap);
7261556Srgrimes		if (flg_var)
7271556Srgrimes			ret |= unsetvar(*ap);
7281556Srgrimes	}
7291556Srgrimes	return ret;
7301556Srgrimes}
7311556Srgrimes
7321556Srgrimes
7331556Srgrimes/*
7341556Srgrimes * Unset the specified variable.
7351556Srgrimes */
7361556Srgrimes
73720425Ssteveint
73890111Simpunsetvar(char *s)
73990111Simp{
7401556Srgrimes	struct var **vpp;
7411556Srgrimes	struct var *vp;
7421556Srgrimes
7431556Srgrimes	vpp = hashvar(s);
7441556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
7451556Srgrimes		if (varequal(vp->text, s)) {
7461556Srgrimes			if (vp->flags & VREADONLY)
7471556Srgrimes				return (1);
7481556Srgrimes			INTOFF;
7491556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
7501556Srgrimes				setvar(s, nullstr, 0);
75117525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
75217525Sache				unsetenv(s);
75317525Sache				setlocale(LC_ALL, "");
75417525Sache			}
75520425Ssteve			vp->flags &= ~VEXPORT;
7561556Srgrimes			vp->flags |= VUNSET;
7571556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
7581556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
7591556Srgrimes					ckfree(vp->text);
7601556Srgrimes				*vpp = vp->next;
7611556Srgrimes				ckfree(vp);
7621556Srgrimes			}
7631556Srgrimes			INTON;
7641556Srgrimes			return (0);
7651556Srgrimes		}
7661556Srgrimes	}
7671556Srgrimes
7681556Srgrimes	return (1);
7691556Srgrimes}
7701556Srgrimes
7711556Srgrimes
7721556Srgrimes
7731556Srgrimes/*
7741556Srgrimes * Find the appropriate entry in the hash table from the name.
7751556Srgrimes */
7761556Srgrimes
7771556SrgrimesSTATIC struct var **
77890111Simphashvar(char *p)
77990111Simp{
7801556Srgrimes	unsigned int hashval;
7811556Srgrimes
78220425Ssteve	hashval = ((unsigned char) *p) << 4;
7831556Srgrimes	while (*p && *p != '=')
78420425Ssteve		hashval += (unsigned char) *p++;
7851556Srgrimes	return &vartab[hashval % VTABSIZE];
7861556Srgrimes}
7871556Srgrimes
7881556Srgrimes
7891556Srgrimes
7901556Srgrimes/*
7911556Srgrimes * Returns true if the two strings specify the same varable.  The first
7921556Srgrimes * variable name is terminated by '='; the second may be terminated by
7931556Srgrimes * either '=' or '\0'.
7941556Srgrimes */
7951556Srgrimes
7961556SrgrimesSTATIC int
79790111Simpvarequal(char *p, char *q)
79890111Simp{
7991556Srgrimes	while (*p == *q++) {
8001556Srgrimes		if (*p++ == '=')
8011556Srgrimes			return 1;
8021556Srgrimes	}
8031556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
8041556Srgrimes		return 1;
8051556Srgrimes	return 0;
8061556Srgrimes}
807