var.c revision 20425
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.
353044Sdg *
3620425Ssteve *	$Id: var.c,v 1.6 1996/09/01 10:21:52 peter Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
4020425Sstevestatic char const sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
411556Srgrimes#endif /* not lint */
421556Srgrimes
4317987Speter#include <unistd.h>
4417987Speter#include <stdlib.h>
4517987Speter
461556Srgrimes/*
471556Srgrimes * Shell variables.
481556Srgrimes */
491556Srgrimes
5017525Sache#include <locale.h>
5117525Sache
521556Srgrimes#include "shell.h"
531556Srgrimes#include "output.h"
541556Srgrimes#include "expand.h"
551556Srgrimes#include "nodes.h"	/* for other headers */
561556Srgrimes#include "eval.h"	/* defines cmdenviron */
571556Srgrimes#include "exec.h"
581556Srgrimes#include "syntax.h"
591556Srgrimes#include "options.h"
601556Srgrimes#include "mail.h"
611556Srgrimes#include "var.h"
621556Srgrimes#include "memalloc.h"
631556Srgrimes#include "error.h"
641556Srgrimes#include "mystring.h"
6520425Ssteve#include "parser.h"
6617987Speter#ifndef NO_HISTORY
6717987Speter#include "myhistedit.h"
6817987Speter#endif
691556Srgrimes
701556Srgrimes
711556Srgrimes#define VTABSIZE 39
721556Srgrimes
731556Srgrimes
741556Srgrimesstruct varinit {
751556Srgrimes	struct var *var;
761556Srgrimes	int flags;
771556Srgrimes	char *text;
7820425Ssteve	void (*func) __P((const char *));
791556Srgrimes};
801556Srgrimes
811556Srgrimes
821556Srgrimes#if ATTY
831556Srgrimesstruct var vatty;
841556Srgrimes#endif
8517987Speter#ifndef NO_HISTORY
861556Srgrimesstruct var vhistsize;
8717987Speter#endif
881556Srgrimesstruct var vifs;
891556Srgrimesstruct var vmail;
901556Srgrimesstruct var vmpath;
911556Srgrimesstruct var vpath;
921556Srgrimesstruct var vps1;
931556Srgrimesstruct var vps2;
941556Srgrimesstruct var vvers;
951556Srgrimes#if ATTY
961556Srgrimesstruct var vterm;
971556Srgrimes#endif
9820425Sstevestruct var voptind;
991556Srgrimes
1001556Srgrimesconst struct varinit varinit[] = {
1011556Srgrimes#if ATTY
10220425Ssteve	{ &vatty,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY=",
10320425Ssteve	  NULL },
1041556Srgrimes#endif
10517987Speter#ifndef NO_HISTORY
10620425Ssteve	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
10720425Ssteve	  sethistsize },
10817987Speter#endif
10920425Ssteve	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
11020425Ssteve	  NULL },
11120425Ssteve	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
11220425Ssteve	  NULL },
11320425Ssteve	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
11420425Ssteve	  NULL },
11520425Ssteve	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=/bin:/usr/bin",
11620425Ssteve	  changepath },
1178855Srgrimes	/*
1181556Srgrimes	 * vps1 depends on uid
1191556Srgrimes	 */
12020425Ssteve	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
12120425Ssteve	  NULL },
1221556Srgrimes#if ATTY
12320425Ssteve	{ &vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM=",
12420425Ssteve	  NULL },
1251556Srgrimes#endif
12620425Ssteve	{ &voptind,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1",
12720425Ssteve	  getoptsreset },
12820425Ssteve	{ NULL,	0,				NULL,
12920425Ssteve	  NULL }
1301556Srgrimes};
1311556Srgrimes
1321556Srgrimesstruct var *vartab[VTABSIZE];
1331556Srgrimes
1341556SrgrimesSTATIC struct var **hashvar __P((char *));
1351556SrgrimesSTATIC int varequal __P((char *, char *));
13617525SacheSTATIC int localevar __P((char *));
1371556Srgrimes
1381556Srgrimes/*
1391556Srgrimes * Initialize the varable symbol tables and import the environment
1401556Srgrimes */
1411556Srgrimes
1421556Srgrimes#ifdef mkinit
1431556SrgrimesINCLUDE "var.h"
1441556SrgrimesINIT {
1451556Srgrimes	char **envp;
1461556Srgrimes	extern char **environ;
1471556Srgrimes
1481556Srgrimes	initvar();
1491556Srgrimes	for (envp = environ ; *envp ; envp++) {
1501556Srgrimes		if (strchr(*envp, '=')) {
1511556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1521556Srgrimes		}
1531556Srgrimes	}
1541556Srgrimes}
1551556Srgrimes#endif
1561556Srgrimes
1571556Srgrimes
1581556Srgrimes/*
1591556Srgrimes * This routine initializes the builtin variables.  It is called when the
1601556Srgrimes * shell is initialized and again when a shell procedure is spawned.
1611556Srgrimes */
1621556Srgrimes
1631556Srgrimesvoid
1641556Srgrimesinitvar() {
1651556Srgrimes	const struct varinit *ip;
1661556Srgrimes	struct var *vp;
1671556Srgrimes	struct var **vpp;
1681556Srgrimes
1691556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1701556Srgrimes		if ((vp->flags & VEXPORT) == 0) {
1711556Srgrimes			vpp = hashvar(ip->text);
1721556Srgrimes			vp->next = *vpp;
1731556Srgrimes			*vpp = vp;
1741556Srgrimes			vp->text = ip->text;
1751556Srgrimes			vp->flags = ip->flags;
17620425Ssteve			vp->func = ip->func;
1771556Srgrimes		}
1781556Srgrimes	}
1791556Srgrimes	/*
1801556Srgrimes	 * PS1 depends on uid
1811556Srgrimes	 */
1821556Srgrimes	if ((vps1.flags & VEXPORT) == 0) {
1831556Srgrimes		vpp = hashvar("PS1=");
1841556Srgrimes		vps1.next = *vpp;
1851556Srgrimes		*vpp = &vps1;
1861556Srgrimes		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
1871556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1881556Srgrimes	}
1891556Srgrimes}
1901556Srgrimes
1911556Srgrimes/*
19220425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
19320425Ssteve */
19420425Ssteve
19520425Ssteveint
19620425Sstevesetvarsafe(name, val, flags)
19720425Ssteve	char *name, *val;
19820425Ssteve	int flags;
19920425Ssteve{
20020425Ssteve	struct jmploc jmploc;
20120425Ssteve	struct jmploc *volatile savehandler = handler;
20220425Ssteve	int err = 0;
20320425Ssteve#if __GNUC__
20420425Ssteve	/* Avoid longjmp clobbering */
20520425Ssteve	(void) &err;
20620425Ssteve#endif
20720425Ssteve
20820425Ssteve	if (setjmp(jmploc.loc))
20920425Ssteve		err = 1;
21020425Ssteve	else {
21120425Ssteve		handler = &jmploc;
21220425Ssteve		setvar(name, val, flags);
21320425Ssteve	}
21420425Ssteve	handler = savehandler;
21520425Ssteve	return err;
21620425Ssteve}
21720425Ssteve
21820425Ssteve/*
2191556Srgrimes * Set the value of a variable.  The flags argument is ored with the
2201556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2211556Srgrimes */
2221556Srgrimes
2231556Srgrimesvoid
2241556Srgrimessetvar(name, val, flags)
2251556Srgrimes	char *name, *val;
22617987Speter	int flags;
22717987Speter{
2281556Srgrimes	char *p, *q;
2291556Srgrimes	int len;
2301556Srgrimes	int namelen;
2311556Srgrimes	char *nameeq;
2321556Srgrimes	int isbad;
2331556Srgrimes
2341556Srgrimes	isbad = 0;
2351556Srgrimes	p = name;
23617525Sache	if (! is_name(*p))
2371556Srgrimes		isbad = 1;
23817525Sache	p++;
2391556Srgrimes	for (;;) {
2401556Srgrimes		if (! is_in_name(*p)) {
2411556Srgrimes			if (*p == '\0' || *p == '=')
2421556Srgrimes				break;
2431556Srgrimes			isbad = 1;
2441556Srgrimes		}
2451556Srgrimes		p++;
2461556Srgrimes	}
2471556Srgrimes	namelen = p - name;
2481556Srgrimes	if (isbad)
2491556Srgrimes		error("%.*s: bad variable name", namelen, name);
2501556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2511556Srgrimes	if (val == NULL) {
2521556Srgrimes		flags |= VUNSET;
2531556Srgrimes	} else {
2541556Srgrimes		len += strlen(val);
2551556Srgrimes	}
2561556Srgrimes	p = nameeq = ckmalloc(len);
2571556Srgrimes	q = name;
2581556Srgrimes	while (--namelen >= 0)
2591556Srgrimes		*p++ = *q++;
2601556Srgrimes	*p++ = '=';
2611556Srgrimes	*p = '\0';
2621556Srgrimes	if (val)
2631556Srgrimes		scopy(val, p);
2641556Srgrimes	setvareq(nameeq, flags);
2651556Srgrimes}
2661556Srgrimes
26717525SacheSTATIC int
26817525Sachelocalevar(s)
26917525Sache	char *s;
27017525Sache	{
27117525Sache	static char *lnames[7] = {
27217525Sache		"ALL", "COLLATE", "CTYPE", "MONETARY",
27317525Sache		"NUMERIC", "TIME", NULL
27417525Sache	};
27517525Sache	char **ss;
2761556Srgrimes
27717525Sache	if (*s != 'L')
27817525Sache		return 0;
27917525Sache	if (varequal(s + 1, "ANG"))
28017525Sache		return 1;
28117525Sache	if (strncmp(s + 1, "C_", 2) != 0)
28217525Sache		return 0;
28317525Sache	for (ss = lnames; *ss ; ss++)
28417525Sache		if (varequal(s + 3, *ss))
28517525Sache			return 1;
28617525Sache	return 0;
28717525Sache}
2881556Srgrimes
2891556Srgrimes/*
2901556Srgrimes * Same as setvar except that the variable and value are passed in
2911556Srgrimes * the first argument as name=value.  Since the first argument will
2921556Srgrimes * be actually stored in the table, it should not be a string that
2931556Srgrimes * will go away.
2941556Srgrimes */
2951556Srgrimes
2961556Srgrimesvoid
2971556Srgrimessetvareq(s, flags)
2981556Srgrimes	char *s;
29917987Speter	int flags;
30017987Speter{
3011556Srgrimes	struct var *vp, **vpp;
3021556Srgrimes
3031556Srgrimes	vpp = hashvar(s);
3041556Srgrimes	for (vp = *vpp ; vp ; vp = vp->next) {
3051556Srgrimes		if (varequal(s, vp->text)) {
3061556Srgrimes			if (vp->flags & VREADONLY) {
30720425Ssteve				size_t len = strchr(s, '=') - s;
3081556Srgrimes				error("%.*s: is read only", len, s);
3091556Srgrimes			}
3101556Srgrimes			INTOFF;
31120425Ssteve
31220425Ssteve			if (vp->func && (flags & VNOFUNC) == 0)
31320425Ssteve				(*vp->func)(strchr(s, '=') + 1);
31420425Ssteve
3151556Srgrimes			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
3161556Srgrimes				ckfree(vp->text);
31720425Ssteve
31820425Ssteve			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
3191556Srgrimes			vp->flags |= flags;
3201556Srgrimes			vp->text = s;
32120425Ssteve
32220425Ssteve			/*
32320425Ssteve			 * We could roll this to a function, to handle it as
32420425Ssteve			 * a regular variable function callback, but why bother?
32520425Ssteve			 */
3261556Srgrimes			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
3271556Srgrimes				chkmail(1);
32817525Sache			if ((vp->flags & VEXPORT) && localevar(s)) {
32917525Sache				putenv(s);
33017525Sache				(void) setlocale(LC_ALL, "");
33117525Sache			}
3321556Srgrimes			INTON;
3331556Srgrimes			return;
3341556Srgrimes		}
3351556Srgrimes	}
3361556Srgrimes	/* not found */
3371556Srgrimes	vp = ckmalloc(sizeof (*vp));
3381556Srgrimes	vp->flags = flags;
3391556Srgrimes	vp->text = s;
3401556Srgrimes	vp->next = *vpp;
34120425Ssteve	vp->func = NULL;
34217525Sache	INTOFF;
3431556Srgrimes	*vpp = vp;
34417525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
34517525Sache		putenv(s);
34617525Sache		(void) setlocale(LC_ALL, "");
34717525Sache	}
34817525Sache	INTON;
3491556Srgrimes}
3501556Srgrimes
3511556Srgrimes
3521556Srgrimes
3531556Srgrimes/*
3541556Srgrimes * Process a linked list of variable assignments.
3551556Srgrimes */
3561556Srgrimes
3571556Srgrimesvoid
3581556Srgrimeslistsetvar(list)
3591556Srgrimes	struct strlist *list;
3601556Srgrimes	{
3611556Srgrimes	struct strlist *lp;
3621556Srgrimes
3631556Srgrimes	INTOFF;
3641556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
3651556Srgrimes		setvareq(savestr(lp->text), 0);
3661556Srgrimes	}
3671556Srgrimes	INTON;
3681556Srgrimes}
3691556Srgrimes
3701556Srgrimes
3711556Srgrimes
3721556Srgrimes/*
3731556Srgrimes * Find the value of a variable.  Returns NULL if not set.
3741556Srgrimes */
3751556Srgrimes
3761556Srgrimeschar *
3771556Srgrimeslookupvar(name)
3781556Srgrimes	char *name;
3791556Srgrimes	{
3801556Srgrimes	struct var *v;
3811556Srgrimes
3821556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
3831556Srgrimes		if (varequal(v->text, name)) {
3841556Srgrimes			if (v->flags & VUNSET)
3851556Srgrimes				return NULL;
3861556Srgrimes			return strchr(v->text, '=') + 1;
3871556Srgrimes		}
3881556Srgrimes	}
3891556Srgrimes	return NULL;
3901556Srgrimes}
3911556Srgrimes
3921556Srgrimes
3931556Srgrimes
3941556Srgrimes/*
3951556Srgrimes * Search the environment of a builtin command.  If the second argument
3961556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
3971556Srgrimes * exported.
3981556Srgrimes */
3991556Srgrimes
4001556Srgrimeschar *
4011556Srgrimesbltinlookup(name, doall)
4021556Srgrimes	char *name;
40317987Speter	int doall;
40417987Speter{
4051556Srgrimes	struct strlist *sp;
4061556Srgrimes	struct var *v;
4071556Srgrimes
4081556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
4091556Srgrimes		if (varequal(sp->text, name))
4101556Srgrimes			return strchr(sp->text, '=') + 1;
4111556Srgrimes	}
4121556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
4131556Srgrimes		if (varequal(v->text, name)) {
41417987Speter			if ((v->flags & VUNSET)
41517987Speter			 || (!doall && (v->flags & VEXPORT) == 0))
4161556Srgrimes				return NULL;
4171556Srgrimes			return strchr(v->text, '=') + 1;
4181556Srgrimes		}
4191556Srgrimes	}
4201556Srgrimes	return NULL;
4211556Srgrimes}
4221556Srgrimes
4231556Srgrimes
4241556Srgrimes
4251556Srgrimes/*
4261556Srgrimes * Generate a list of exported variables.  This routine is used to construct
4271556Srgrimes * the third argument to execve when executing a program.
4281556Srgrimes */
4291556Srgrimes
4301556Srgrimeschar **
4311556Srgrimesenvironment() {
4321556Srgrimes	int nenv;
4331556Srgrimes	struct var **vpp;
4341556Srgrimes	struct var *vp;
4351556Srgrimes	char **env, **ep;
4361556Srgrimes
4371556Srgrimes	nenv = 0;
4381556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4391556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4401556Srgrimes			if (vp->flags & VEXPORT)
4411556Srgrimes				nenv++;
4421556Srgrimes	}
4431556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
4441556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4451556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4461556Srgrimes			if (vp->flags & VEXPORT)
4471556Srgrimes				*ep++ = vp->text;
4481556Srgrimes	}
4491556Srgrimes	*ep = NULL;
4501556Srgrimes	return env;
4511556Srgrimes}
4521556Srgrimes
4531556Srgrimes
4541556Srgrimes/*
4551556Srgrimes * Called when a shell procedure is invoked to clear out nonexported
4561556Srgrimes * variables.  It is also necessary to reallocate variables of with
4571556Srgrimes * VSTACK set since these are currently allocated on the stack.
4581556Srgrimes */
4591556Srgrimes
4601556Srgrimes#ifdef mkinit
4611556SrgrimesMKINIT void shprocvar();
4621556Srgrimes
4631556SrgrimesSHELLPROC {
4641556Srgrimes	shprocvar();
4651556Srgrimes}
4661556Srgrimes#endif
4671556Srgrimes
4681556Srgrimesvoid
4691556Srgrimesshprocvar() {
4701556Srgrimes	struct var **vpp;
4711556Srgrimes	struct var *vp, **prev;
4721556Srgrimes
4731556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4741556Srgrimes		for (prev = vpp ; (vp = *prev) != NULL ; ) {
4751556Srgrimes			if ((vp->flags & VEXPORT) == 0) {
4761556Srgrimes				*prev = vp->next;
4771556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
4781556Srgrimes					ckfree(vp->text);
4791556Srgrimes				if ((vp->flags & VSTRFIXED) == 0)
4801556Srgrimes					ckfree(vp);
4811556Srgrimes			} else {
4821556Srgrimes				if (vp->flags & VSTACK) {
4831556Srgrimes					vp->text = savestr(vp->text);
4841556Srgrimes					vp->flags &=~ VSTACK;
4851556Srgrimes				}
4861556Srgrimes				prev = &vp->next;
4871556Srgrimes			}
4881556Srgrimes		}
4891556Srgrimes	}
4901556Srgrimes	initvar();
4911556Srgrimes}
4921556Srgrimes
4931556Srgrimes
4941556Srgrimes
4951556Srgrimes/*
4961556Srgrimes * Command to list all variables which are set.  Currently this command
4971556Srgrimes * is invoked from the set command when the set command is called without
4981556Srgrimes * any variables.
4991556Srgrimes */
5001556Srgrimes
5011556Srgrimesint
50217987Spetershowvarscmd(argc, argv)
50317987Speter	int argc;
50420425Ssteve	char **argv;
50517987Speter{
5061556Srgrimes	struct var **vpp;
5071556Srgrimes	struct var *vp;
5081556Srgrimes
5091556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5101556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next) {
5111556Srgrimes			if ((vp->flags & VUNSET) == 0)
5121556Srgrimes				out1fmt("%s\n", vp->text);
5131556Srgrimes		}
5141556Srgrimes	}
5151556Srgrimes	return 0;
5161556Srgrimes}
5171556Srgrimes
5181556Srgrimes
5191556Srgrimes
5201556Srgrimes/*
5211556Srgrimes * The export and readonly commands.
5221556Srgrimes */
5231556Srgrimes
5241556Srgrimesint
52517987Speterexportcmd(argc, argv)
52617987Speter	int argc;
52720425Ssteve	char **argv;
52817987Speter{
5291556Srgrimes	struct var **vpp;
5301556Srgrimes	struct var *vp;
5311556Srgrimes	char *name;
5321556Srgrimes	char *p;
5331556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
5341556Srgrimes
5351556Srgrimes	listsetvar(cmdenviron);
5361556Srgrimes	if (argc > 1) {
5371556Srgrimes		while ((name = *argptr++) != NULL) {
5381556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
5391556Srgrimes				p++;
5401556Srgrimes			} else {
5411556Srgrimes				vpp = hashvar(name);
5421556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
5431556Srgrimes					if (varequal(vp->text, name)) {
5441556Srgrimes						vp->flags |= flag;
54517525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
54617525Sache							putenv(vp->text);
54717525Sache							(void) setlocale(LC_ALL, "");
54817525Sache						}
5491556Srgrimes						goto found;
5501556Srgrimes					}
5511556Srgrimes				}
5521556Srgrimes			}
5531556Srgrimes			setvar(name, p, flag);
5541556Srgrimesfound:;
5551556Srgrimes		}
5561556Srgrimes	} else {
5571556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5581556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
5591556Srgrimes				if (vp->flags & flag) {
5601556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
5611556Srgrimes						out1c(*p);
5621556Srgrimes					out1c('\n');
5631556Srgrimes				}
5641556Srgrimes			}
5651556Srgrimes		}
5661556Srgrimes	}
5671556Srgrimes	return 0;
5681556Srgrimes}
5691556Srgrimes
5701556Srgrimes
5711556Srgrimes/*
5721556Srgrimes * The "local" command.
5731556Srgrimes */
5741556Srgrimes
57517987Speterint
57617987Speterlocalcmd(argc, argv)
57717987Speter	int argc;
57820425Ssteve	char **argv;
57917987Speter{
5801556Srgrimes	char *name;
5811556Srgrimes
5821556Srgrimes	if (! in_function())
5831556Srgrimes		error("Not in a function");
5841556Srgrimes	while ((name = *argptr++) != NULL) {
5851556Srgrimes		mklocal(name);
5861556Srgrimes	}
5871556Srgrimes	return 0;
5881556Srgrimes}
5891556Srgrimes
5901556Srgrimes
5911556Srgrimes/*
5921556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
5931556Srgrimes * value and flags are saved in a localvar structure.  The saved values
5941556Srgrimes * will be restored when the shell function returns.  We handle the name
5951556Srgrimes * "-" as a special case.
5961556Srgrimes */
5971556Srgrimes
5981556Srgrimesvoid
5991556Srgrimesmklocal(name)
6001556Srgrimes	char *name;
6011556Srgrimes	{
6021556Srgrimes	struct localvar *lvp;
6031556Srgrimes	struct var **vpp;
6041556Srgrimes	struct var *vp;
6051556Srgrimes
6061556Srgrimes	INTOFF;
6071556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
6081556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
6091556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
61017987Speter		memcpy(lvp->text, optlist, sizeof optlist);
6111556Srgrimes		vp = NULL;
6121556Srgrimes	} else {
6131556Srgrimes		vpp = hashvar(name);
6141556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
6151556Srgrimes		if (vp == NULL) {
6161556Srgrimes			if (strchr(name, '='))
6171556Srgrimes				setvareq(savestr(name), VSTRFIXED);
6181556Srgrimes			else
6191556Srgrimes				setvar(name, NULL, VSTRFIXED);
6201556Srgrimes			vp = *vpp;	/* the new variable */
6211556Srgrimes			lvp->text = NULL;
6221556Srgrimes			lvp->flags = VUNSET;
6231556Srgrimes		} else {
6241556Srgrimes			lvp->text = vp->text;
6251556Srgrimes			lvp->flags = vp->flags;
6261556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
6271556Srgrimes			if (strchr(name, '='))
6281556Srgrimes				setvareq(savestr(name), 0);
6291556Srgrimes		}
6301556Srgrimes	}
6311556Srgrimes	lvp->vp = vp;
6321556Srgrimes	lvp->next = localvars;
6331556Srgrimes	localvars = lvp;
6341556Srgrimes	INTON;
6351556Srgrimes}
6361556Srgrimes
6371556Srgrimes
6381556Srgrimes/*
6391556Srgrimes * Called after a function returns.
6401556Srgrimes */
6411556Srgrimes
6421556Srgrimesvoid
6431556Srgrimespoplocalvars() {
6441556Srgrimes	struct localvar *lvp;
6451556Srgrimes	struct var *vp;
6461556Srgrimes
6471556Srgrimes	while ((lvp = localvars) != NULL) {
6481556Srgrimes		localvars = lvp->next;
6491556Srgrimes		vp = lvp->vp;
6501556Srgrimes		if (vp == NULL) {	/* $- saved */
65117987Speter			memcpy(optlist, lvp->text, sizeof optlist);
6521556Srgrimes			ckfree(lvp->text);
6531556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
6541556Srgrimes			(void)unsetvar(vp->text);
6551556Srgrimes		} else {
6561556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
6571556Srgrimes				ckfree(vp->text);
6581556Srgrimes			vp->flags = lvp->flags;
6591556Srgrimes			vp->text = lvp->text;
6601556Srgrimes		}
6611556Srgrimes		ckfree(lvp);
6621556Srgrimes	}
6631556Srgrimes}
6641556Srgrimes
6651556Srgrimes
66617987Speterint
66717987Spetersetvarcmd(argc, argv)
66817987Speter	int argc;
66920425Ssteve	char **argv;
67017987Speter{
6711556Srgrimes	if (argc <= 2)
6721556Srgrimes		return unsetcmd(argc, argv);
6731556Srgrimes	else if (argc == 3)
6741556Srgrimes		setvar(argv[1], argv[2], 0);
6751556Srgrimes	else
6761556Srgrimes		error("List assignment not implemented");
6771556Srgrimes	return 0;
6781556Srgrimes}
6791556Srgrimes
6801556Srgrimes
6811556Srgrimes/*
6821556Srgrimes * The unset builtin command.  We unset the function before we unset the
6831556Srgrimes * variable to allow a function to be unset when there is a readonly variable
6841556Srgrimes * with the same name.
6851556Srgrimes */
6861556Srgrimes
68717987Speterint
68817987Speterunsetcmd(argc, argv)
68917987Speter	int argc;
69020425Ssteve	char **argv;
69117987Speter{
6921556Srgrimes	char **ap;
6931556Srgrimes	int i;
6941556Srgrimes	int flg_func = 0;
6951556Srgrimes	int flg_var = 0;
6961556Srgrimes	int ret = 0;
6971556Srgrimes
6981556Srgrimes	while ((i = nextopt("vf")) != '\0') {
6991556Srgrimes		if (i == 'f')
7001556Srgrimes			flg_func = 1;
7011556Srgrimes		else
7021556Srgrimes			flg_var = 1;
7031556Srgrimes	}
7041556Srgrimes	if (flg_func == 0 && flg_var == 0)
7051556Srgrimes		flg_var = 1;
7068855Srgrimes
7071556Srgrimes	for (ap = argptr; *ap ; ap++) {
7081556Srgrimes		if (flg_func)
7091556Srgrimes			ret |= unsetfunc(*ap);
7101556Srgrimes		if (flg_var)
7111556Srgrimes			ret |= unsetvar(*ap);
7121556Srgrimes	}
7131556Srgrimes	return ret;
7141556Srgrimes}
7151556Srgrimes
7161556Srgrimes
7171556Srgrimes/*
7181556Srgrimes * Unset the specified variable.
7191556Srgrimes */
7201556Srgrimes
72120425Ssteveint
7221556Srgrimesunsetvar(s)
7231556Srgrimes	char *s;
7241556Srgrimes	{
7251556Srgrimes	struct var **vpp;
7261556Srgrimes	struct var *vp;
7271556Srgrimes
7281556Srgrimes	vpp = hashvar(s);
7291556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
7301556Srgrimes		if (varequal(vp->text, s)) {
7311556Srgrimes			if (vp->flags & VREADONLY)
7321556Srgrimes				return (1);
7331556Srgrimes			INTOFF;
7341556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
7351556Srgrimes				setvar(s, nullstr, 0);
73617525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
73717525Sache				unsetenv(s);
73817525Sache				setlocale(LC_ALL, "");
73917525Sache			}
74020425Ssteve			vp->flags &= ~VEXPORT;
7411556Srgrimes			vp->flags |= VUNSET;
7421556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
7431556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
7441556Srgrimes					ckfree(vp->text);
7451556Srgrimes				*vpp = vp->next;
7461556Srgrimes				ckfree(vp);
7471556Srgrimes			}
7481556Srgrimes			INTON;
7491556Srgrimes			return (0);
7501556Srgrimes		}
7511556Srgrimes	}
7521556Srgrimes
7531556Srgrimes	return (1);
7541556Srgrimes}
7551556Srgrimes
7561556Srgrimes
7571556Srgrimes
7581556Srgrimes/*
7591556Srgrimes * Find the appropriate entry in the hash table from the name.
7601556Srgrimes */
7611556Srgrimes
7621556SrgrimesSTATIC struct var **
7631556Srgrimeshashvar(p)
7641556Srgrimes	register char *p;
7651556Srgrimes	{
7661556Srgrimes	unsigned int hashval;
7671556Srgrimes
76820425Ssteve	hashval = ((unsigned char) *p) << 4;
7691556Srgrimes	while (*p && *p != '=')
77020425Ssteve		hashval += (unsigned char) *p++;
7711556Srgrimes	return &vartab[hashval % VTABSIZE];
7721556Srgrimes}
7731556Srgrimes
7741556Srgrimes
7751556Srgrimes
7761556Srgrimes/*
7771556Srgrimes * Returns true if the two strings specify the same varable.  The first
7781556Srgrimes * variable name is terminated by '='; the second may be terminated by
7791556Srgrimes * either '=' or '\0'.
7801556Srgrimes */
7811556Srgrimes
7821556SrgrimesSTATIC int
7831556Srgrimesvarequal(p, q)
7841556Srgrimes	register char *p, *q;
7851556Srgrimes	{
7861556Srgrimes	while (*p == *q++) {
7871556Srgrimes		if (*p++ == '=')
7881556Srgrimes			return 1;
7891556Srgrimes	}
7901556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
7911556Srgrimes		return 1;
7921556Srgrimes	return 0;
7931556Srgrimes}
794