var.c revision 17525
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 *
3617525Sache *	$Id: var.c,v 1.3 1995/05/30 00:07:24 rgrimes Exp $
371556Srgrimes */
381556Srgrimes
391556Srgrimes#ifndef lint
401556Srgrimesstatic char sccsid[] = "@(#)var.c	8.1 (Berkeley) 5/31/93";
411556Srgrimes#endif /* not lint */
421556Srgrimes
431556Srgrimes/*
441556Srgrimes * Shell variables.
451556Srgrimes */
461556Srgrimes
4717525Sache#include <locale.h>
4817525Sache
491556Srgrimes#include "shell.h"
501556Srgrimes#include "output.h"
511556Srgrimes#include "expand.h"
521556Srgrimes#include "nodes.h"	/* for other headers */
531556Srgrimes#include "eval.h"	/* defines cmdenviron */
541556Srgrimes#include "exec.h"
551556Srgrimes#include "syntax.h"
561556Srgrimes#include "options.h"
571556Srgrimes#include "mail.h"
581556Srgrimes#include "var.h"
591556Srgrimes#include "memalloc.h"
601556Srgrimes#include "error.h"
611556Srgrimes#include "mystring.h"
621556Srgrimes
631556Srgrimes
641556Srgrimes#define VTABSIZE 39
651556Srgrimes
661556Srgrimes
671556Srgrimesstruct varinit {
681556Srgrimes	struct var *var;
691556Srgrimes	int flags;
701556Srgrimes	char *text;
711556Srgrimes};
721556Srgrimes
731556Srgrimes
741556Srgrimes#if ATTY
751556Srgrimesstruct var vatty;
761556Srgrimes#endif
771556Srgrimesstruct var vhistsize;
781556Srgrimesstruct var vifs;
791556Srgrimesstruct var vmail;
801556Srgrimesstruct var vmpath;
811556Srgrimesstruct var vpath;
821556Srgrimesstruct var vps1;
831556Srgrimesstruct var vps2;
841556Srgrimesstruct var vvers;
851556Srgrimes#if ATTY
861556Srgrimesstruct var vterm;
871556Srgrimes#endif
881556Srgrimes
891556Srgrimesconst struct varinit varinit[] = {
901556Srgrimes#if ATTY
911556Srgrimes	{&vatty,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY="},
921556Srgrimes#endif
931556Srgrimes	{&vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE="},
941556Srgrimes	{&vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n"},
951556Srgrimes	{&vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL="},
961556Srgrimes	{&vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH="},
971556Srgrimes	{&vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=:/bin:/usr/bin"},
988855Srgrimes	/*
991556Srgrimes	 * vps1 depends on uid
1001556Srgrimes	 */
1011556Srgrimes	{&vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> "},
1021556Srgrimes#if ATTY
1031556Srgrimes	{&vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM="},
1041556Srgrimes#endif
1051556Srgrimes	{NULL,	0,				NULL}
1061556Srgrimes};
1071556Srgrimes
1081556Srgrimesstruct var *vartab[VTABSIZE];
1091556Srgrimes
1101556SrgrimesSTATIC int unsetvar __P((char *));
1111556SrgrimesSTATIC struct var **hashvar __P((char *));
1121556SrgrimesSTATIC int varequal __P((char *, char *));
11317525SacheSTATIC int localevar __P((char *));
1141556Srgrimes
1151556Srgrimes/*
1161556Srgrimes * Initialize the varable symbol tables and import the environment
1171556Srgrimes */
1181556Srgrimes
1191556Srgrimes#ifdef mkinit
1201556SrgrimesINCLUDE "var.h"
1211556SrgrimesINIT {
1221556Srgrimes	char **envp;
1231556Srgrimes	extern char **environ;
1241556Srgrimes
1251556Srgrimes	initvar();
1261556Srgrimes	for (envp = environ ; *envp ; envp++) {
1271556Srgrimes		if (strchr(*envp, '=')) {
1281556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1291556Srgrimes		}
1301556Srgrimes	}
1311556Srgrimes}
1321556Srgrimes#endif
1331556Srgrimes
1341556Srgrimes
1351556Srgrimes/*
1361556Srgrimes * This routine initializes the builtin variables.  It is called when the
1371556Srgrimes * shell is initialized and again when a shell procedure is spawned.
1381556Srgrimes */
1391556Srgrimes
1401556Srgrimesvoid
1411556Srgrimesinitvar() {
1421556Srgrimes	const struct varinit *ip;
1431556Srgrimes	struct var *vp;
1441556Srgrimes	struct var **vpp;
1451556Srgrimes
1461556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1471556Srgrimes		if ((vp->flags & VEXPORT) == 0) {
1481556Srgrimes			vpp = hashvar(ip->text);
1491556Srgrimes			vp->next = *vpp;
1501556Srgrimes			*vpp = vp;
1511556Srgrimes			vp->text = ip->text;
1521556Srgrimes			vp->flags = ip->flags;
1531556Srgrimes		}
1541556Srgrimes	}
1551556Srgrimes	/*
1561556Srgrimes	 * PS1 depends on uid
1571556Srgrimes	 */
1581556Srgrimes	if ((vps1.flags & VEXPORT) == 0) {
1591556Srgrimes		vpp = hashvar("PS1=");
1601556Srgrimes		vps1.next = *vpp;
1611556Srgrimes		*vpp = &vps1;
1621556Srgrimes		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
1631556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1641556Srgrimes	}
1651556Srgrimes}
1661556Srgrimes
1671556Srgrimes/*
1681556Srgrimes * Set the value of a variable.  The flags argument is ored with the
1691556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
1701556Srgrimes */
1711556Srgrimes
1721556Srgrimesvoid
1731556Srgrimessetvar(name, val, flags)
1741556Srgrimes	char *name, *val;
1751556Srgrimes	{
1761556Srgrimes	char *p, *q;
1771556Srgrimes	int len;
1781556Srgrimes	int namelen;
1791556Srgrimes	char *nameeq;
1801556Srgrimes	int isbad;
1811556Srgrimes
1821556Srgrimes	isbad = 0;
1831556Srgrimes	p = name;
18417525Sache	if (! is_name(*p))
1851556Srgrimes		isbad = 1;
18617525Sache	p++;
1871556Srgrimes	for (;;) {
1881556Srgrimes		if (! is_in_name(*p)) {
1891556Srgrimes			if (*p == '\0' || *p == '=')
1901556Srgrimes				break;
1911556Srgrimes			isbad = 1;
1921556Srgrimes		}
1931556Srgrimes		p++;
1941556Srgrimes	}
1951556Srgrimes	namelen = p - name;
1961556Srgrimes	if (isbad)
1971556Srgrimes		error("%.*s: bad variable name", namelen, name);
1981556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
1991556Srgrimes	if (val == NULL) {
2001556Srgrimes		flags |= VUNSET;
2011556Srgrimes	} else {
2021556Srgrimes		len += strlen(val);
2031556Srgrimes	}
2041556Srgrimes	p = nameeq = ckmalloc(len);
2051556Srgrimes	q = name;
2061556Srgrimes	while (--namelen >= 0)
2071556Srgrimes		*p++ = *q++;
2081556Srgrimes	*p++ = '=';
2091556Srgrimes	*p = '\0';
2101556Srgrimes	if (val)
2111556Srgrimes		scopy(val, p);
2121556Srgrimes	setvareq(nameeq, flags);
2131556Srgrimes}
2141556Srgrimes
21517525SacheSTATIC int
21617525Sachelocalevar(s)
21717525Sache	char *s;
21817525Sache	{
21917525Sache	static char *lnames[7] = {
22017525Sache		"ALL", "COLLATE", "CTYPE", "MONETARY",
22117525Sache		"NUMERIC", "TIME", NULL
22217525Sache	};
22317525Sache	char **ss;
2241556Srgrimes
22517525Sache	if (*s != 'L')
22617525Sache		return 0;
22717525Sache	if (varequal(s + 1, "ANG"))
22817525Sache		return 1;
22917525Sache	if (strncmp(s + 1, "C_", 2) != 0)
23017525Sache		return 0;
23117525Sache	for (ss = lnames; *ss ; ss++)
23217525Sache		if (varequal(s + 3, *ss))
23317525Sache			return 1;
23417525Sache	return 0;
23517525Sache}
2361556Srgrimes
2371556Srgrimes/*
2381556Srgrimes * Same as setvar except that the variable and value are passed in
2391556Srgrimes * the first argument as name=value.  Since the first argument will
2401556Srgrimes * be actually stored in the table, it should not be a string that
2411556Srgrimes * will go away.
2421556Srgrimes */
2431556Srgrimes
2441556Srgrimesvoid
2451556Srgrimessetvareq(s, flags)
2461556Srgrimes	char *s;
2471556Srgrimes	{
2481556Srgrimes	struct var *vp, **vpp;
2491556Srgrimes
2501556Srgrimes	vpp = hashvar(s);
2511556Srgrimes	for (vp = *vpp ; vp ; vp = vp->next) {
2521556Srgrimes		if (varequal(s, vp->text)) {
2531556Srgrimes			if (vp->flags & VREADONLY) {
2541556Srgrimes				int len = strchr(s, '=') - s;
2551556Srgrimes				error("%.*s: is read only", len, s);
2561556Srgrimes			}
2571556Srgrimes			INTOFF;
2581556Srgrimes			if (vp == &vpath)
2591556Srgrimes				changepath(s + 5);	/* 5 = strlen("PATH=") */
2601556Srgrimes			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
2611556Srgrimes				ckfree(vp->text);
2621556Srgrimes			vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET);
2631556Srgrimes			vp->flags |= flags;
2641556Srgrimes			vp->text = s;
2651556Srgrimes			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
2661556Srgrimes				chkmail(1);
2671556Srgrimes			if (vp == &vhistsize)
2681556Srgrimes				sethistsize();
26917525Sache			if ((vp->flags & VEXPORT) && localevar(s)) {
27017525Sache				putenv(s);
27117525Sache				(void) setlocale(LC_ALL, "");
27217525Sache			}
2731556Srgrimes			INTON;
2741556Srgrimes			return;
2751556Srgrimes		}
2761556Srgrimes	}
2771556Srgrimes	/* not found */
2781556Srgrimes	vp = ckmalloc(sizeof (*vp));
2791556Srgrimes	vp->flags = flags;
2801556Srgrimes	vp->text = s;
2811556Srgrimes	vp->next = *vpp;
28217525Sache	INTOFF;
2831556Srgrimes	*vpp = vp;
28417525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
28517525Sache		putenv(s);
28617525Sache		(void) setlocale(LC_ALL, "");
28717525Sache	}
28817525Sache	INTON;
2891556Srgrimes}
2901556Srgrimes
2911556Srgrimes
2921556Srgrimes
2931556Srgrimes/*
2941556Srgrimes * Process a linked list of variable assignments.
2951556Srgrimes */
2961556Srgrimes
2971556Srgrimesvoid
2981556Srgrimeslistsetvar(list)
2991556Srgrimes	struct strlist *list;
3001556Srgrimes	{
3011556Srgrimes	struct strlist *lp;
3021556Srgrimes
3031556Srgrimes	INTOFF;
3041556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
3051556Srgrimes		setvareq(savestr(lp->text), 0);
3061556Srgrimes	}
3071556Srgrimes	INTON;
3081556Srgrimes}
3091556Srgrimes
3101556Srgrimes
3111556Srgrimes
3121556Srgrimes/*
3131556Srgrimes * Find the value of a variable.  Returns NULL if not set.
3141556Srgrimes */
3151556Srgrimes
3161556Srgrimeschar *
3171556Srgrimeslookupvar(name)
3181556Srgrimes	char *name;
3191556Srgrimes	{
3201556Srgrimes	struct var *v;
3211556Srgrimes
3221556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
3231556Srgrimes		if (varequal(v->text, name)) {
3241556Srgrimes			if (v->flags & VUNSET)
3251556Srgrimes				return NULL;
3261556Srgrimes			return strchr(v->text, '=') + 1;
3271556Srgrimes		}
3281556Srgrimes	}
3291556Srgrimes	return NULL;
3301556Srgrimes}
3311556Srgrimes
3321556Srgrimes
3331556Srgrimes
3341556Srgrimes/*
3351556Srgrimes * Search the environment of a builtin command.  If the second argument
3361556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
3371556Srgrimes * exported.
3381556Srgrimes */
3391556Srgrimes
3401556Srgrimeschar *
3411556Srgrimesbltinlookup(name, doall)
3421556Srgrimes	char *name;
3431556Srgrimes	{
3441556Srgrimes	struct strlist *sp;
3451556Srgrimes	struct var *v;
3461556Srgrimes
3471556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
3481556Srgrimes		if (varequal(sp->text, name))
3491556Srgrimes			return strchr(sp->text, '=') + 1;
3501556Srgrimes	}
3511556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
3521556Srgrimes		if (varequal(v->text, name)) {
3531556Srgrimes			if (v->flags & VUNSET
3541556Srgrimes			 || ! doall && (v->flags & VEXPORT) == 0)
3551556Srgrimes				return NULL;
3561556Srgrimes			return strchr(v->text, '=') + 1;
3571556Srgrimes		}
3581556Srgrimes	}
3591556Srgrimes	return NULL;
3601556Srgrimes}
3611556Srgrimes
3621556Srgrimes
3631556Srgrimes
3641556Srgrimes/*
3651556Srgrimes * Generate a list of exported variables.  This routine is used to construct
3661556Srgrimes * the third argument to execve when executing a program.
3671556Srgrimes */
3681556Srgrimes
3691556Srgrimeschar **
3701556Srgrimesenvironment() {
3711556Srgrimes	int nenv;
3721556Srgrimes	struct var **vpp;
3731556Srgrimes	struct var *vp;
3741556Srgrimes	char **env, **ep;
3751556Srgrimes
3761556Srgrimes	nenv = 0;
3771556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
3781556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
3791556Srgrimes			if (vp->flags & VEXPORT)
3801556Srgrimes				nenv++;
3811556Srgrimes	}
3821556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
3831556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
3841556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
3851556Srgrimes			if (vp->flags & VEXPORT)
3861556Srgrimes				*ep++ = vp->text;
3871556Srgrimes	}
3881556Srgrimes	*ep = NULL;
3891556Srgrimes	return env;
3901556Srgrimes}
3911556Srgrimes
3921556Srgrimes
3931556Srgrimes/*
3941556Srgrimes * Called when a shell procedure is invoked to clear out nonexported
3951556Srgrimes * variables.  It is also necessary to reallocate variables of with
3961556Srgrimes * VSTACK set since these are currently allocated on the stack.
3971556Srgrimes */
3981556Srgrimes
3991556Srgrimes#ifdef mkinit
4001556SrgrimesMKINIT void shprocvar();
4011556Srgrimes
4021556SrgrimesSHELLPROC {
4031556Srgrimes	shprocvar();
4041556Srgrimes}
4051556Srgrimes#endif
4061556Srgrimes
4071556Srgrimesvoid
4081556Srgrimesshprocvar() {
4091556Srgrimes	struct var **vpp;
4101556Srgrimes	struct var *vp, **prev;
4111556Srgrimes
4121556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4131556Srgrimes		for (prev = vpp ; (vp = *prev) != NULL ; ) {
4141556Srgrimes			if ((vp->flags & VEXPORT) == 0) {
4151556Srgrimes				*prev = vp->next;
4161556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
4171556Srgrimes					ckfree(vp->text);
4181556Srgrimes				if ((vp->flags & VSTRFIXED) == 0)
4191556Srgrimes					ckfree(vp);
4201556Srgrimes			} else {
4211556Srgrimes				if (vp->flags & VSTACK) {
4221556Srgrimes					vp->text = savestr(vp->text);
4231556Srgrimes					vp->flags &=~ VSTACK;
4241556Srgrimes				}
4251556Srgrimes				prev = &vp->next;
4261556Srgrimes			}
4271556Srgrimes		}
4281556Srgrimes	}
4291556Srgrimes	initvar();
4301556Srgrimes}
4311556Srgrimes
4321556Srgrimes
4331556Srgrimes
4341556Srgrimes/*
4351556Srgrimes * Command to list all variables which are set.  Currently this command
4361556Srgrimes * is invoked from the set command when the set command is called without
4371556Srgrimes * any variables.
4381556Srgrimes */
4391556Srgrimes
4401556Srgrimesint
4411556Srgrimesshowvarscmd(argc, argv)  char **argv; {
4421556Srgrimes	struct var **vpp;
4431556Srgrimes	struct var *vp;
4441556Srgrimes
4451556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4461556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next) {
4471556Srgrimes			if ((vp->flags & VUNSET) == 0)
4481556Srgrimes				out1fmt("%s\n", vp->text);
4491556Srgrimes		}
4501556Srgrimes	}
4511556Srgrimes	return 0;
4521556Srgrimes}
4531556Srgrimes
4541556Srgrimes
4551556Srgrimes
4561556Srgrimes/*
4571556Srgrimes * The export and readonly commands.
4581556Srgrimes */
4591556Srgrimes
4601556Srgrimesint
4611556Srgrimesexportcmd(argc, argv)  char **argv; {
4621556Srgrimes	struct var **vpp;
4631556Srgrimes	struct var *vp;
4641556Srgrimes	char *name;
4651556Srgrimes	char *p;
4661556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
4671556Srgrimes
4681556Srgrimes	listsetvar(cmdenviron);
4691556Srgrimes	if (argc > 1) {
4701556Srgrimes		while ((name = *argptr++) != NULL) {
4711556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
4721556Srgrimes				p++;
4731556Srgrimes			} else {
4741556Srgrimes				vpp = hashvar(name);
4751556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
4761556Srgrimes					if (varequal(vp->text, name)) {
4771556Srgrimes						vp->flags |= flag;
47817525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
47917525Sache							putenv(vp->text);
48017525Sache							(void) setlocale(LC_ALL, "");
48117525Sache						}
4821556Srgrimes						goto found;
4831556Srgrimes					}
4841556Srgrimes				}
4851556Srgrimes			}
4861556Srgrimes			setvar(name, p, flag);
4871556Srgrimesfound:;
4881556Srgrimes		}
4891556Srgrimes	} else {
4901556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4911556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
4921556Srgrimes				if (vp->flags & flag) {
4931556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
4941556Srgrimes						out1c(*p);
4951556Srgrimes					out1c('\n');
4961556Srgrimes				}
4971556Srgrimes			}
4981556Srgrimes		}
4991556Srgrimes	}
5001556Srgrimes	return 0;
5011556Srgrimes}
5021556Srgrimes
5031556Srgrimes
5041556Srgrimes/*
5051556Srgrimes * The "local" command.
5061556Srgrimes */
5071556Srgrimes
5081556Srgrimeslocalcmd(argc, argv)  char **argv; {
5091556Srgrimes	char *name;
5101556Srgrimes
5111556Srgrimes	if (! in_function())
5121556Srgrimes		error("Not in a function");
5131556Srgrimes	while ((name = *argptr++) != NULL) {
5141556Srgrimes		mklocal(name);
5151556Srgrimes	}
5161556Srgrimes	return 0;
5171556Srgrimes}
5181556Srgrimes
5191556Srgrimes
5201556Srgrimes/*
5211556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
5221556Srgrimes * value and flags are saved in a localvar structure.  The saved values
5231556Srgrimes * will be restored when the shell function returns.  We handle the name
5241556Srgrimes * "-" as a special case.
5251556Srgrimes */
5261556Srgrimes
5271556Srgrimesvoid
5281556Srgrimesmklocal(name)
5291556Srgrimes	char *name;
5301556Srgrimes	{
5311556Srgrimes	struct localvar *lvp;
5321556Srgrimes	struct var **vpp;
5331556Srgrimes	struct var *vp;
5341556Srgrimes
5351556Srgrimes	INTOFF;
5361556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
5371556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
5381556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
5391556Srgrimes		bcopy(optlist, lvp->text, sizeof optlist);
5401556Srgrimes		vp = NULL;
5411556Srgrimes	} else {
5421556Srgrimes		vpp = hashvar(name);
5431556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
5441556Srgrimes		if (vp == NULL) {
5451556Srgrimes			if (strchr(name, '='))
5461556Srgrimes				setvareq(savestr(name), VSTRFIXED);
5471556Srgrimes			else
5481556Srgrimes				setvar(name, NULL, VSTRFIXED);
5491556Srgrimes			vp = *vpp;	/* the new variable */
5501556Srgrimes			lvp->text = NULL;
5511556Srgrimes			lvp->flags = VUNSET;
5521556Srgrimes		} else {
5531556Srgrimes			lvp->text = vp->text;
5541556Srgrimes			lvp->flags = vp->flags;
5551556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
5561556Srgrimes			if (strchr(name, '='))
5571556Srgrimes				setvareq(savestr(name), 0);
5581556Srgrimes		}
5591556Srgrimes	}
5601556Srgrimes	lvp->vp = vp;
5611556Srgrimes	lvp->next = localvars;
5621556Srgrimes	localvars = lvp;
5631556Srgrimes	INTON;
5641556Srgrimes}
5651556Srgrimes
5661556Srgrimes
5671556Srgrimes/*
5681556Srgrimes * Called after a function returns.
5691556Srgrimes */
5701556Srgrimes
5711556Srgrimesvoid
5721556Srgrimespoplocalvars() {
5731556Srgrimes	struct localvar *lvp;
5741556Srgrimes	struct var *vp;
5751556Srgrimes
5761556Srgrimes	while ((lvp = localvars) != NULL) {
5771556Srgrimes		localvars = lvp->next;
5781556Srgrimes		vp = lvp->vp;
5791556Srgrimes		if (vp == NULL) {	/* $- saved */
5801556Srgrimes			bcopy(lvp->text, optlist, sizeof optlist);
5811556Srgrimes			ckfree(lvp->text);
5821556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
5831556Srgrimes			(void)unsetvar(vp->text);
5841556Srgrimes		} else {
5851556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
5861556Srgrimes				ckfree(vp->text);
5871556Srgrimes			vp->flags = lvp->flags;
5881556Srgrimes			vp->text = lvp->text;
5891556Srgrimes		}
5901556Srgrimes		ckfree(lvp);
5911556Srgrimes	}
5921556Srgrimes}
5931556Srgrimes
5941556Srgrimes
5951556Srgrimessetvarcmd(argc, argv)  char **argv; {
5961556Srgrimes	if (argc <= 2)
5971556Srgrimes		return unsetcmd(argc, argv);
5981556Srgrimes	else if (argc == 3)
5991556Srgrimes		setvar(argv[1], argv[2], 0);
6001556Srgrimes	else
6011556Srgrimes		error("List assignment not implemented");
6021556Srgrimes	return 0;
6031556Srgrimes}
6041556Srgrimes
6051556Srgrimes
6061556Srgrimes/*
6071556Srgrimes * The unset builtin command.  We unset the function before we unset the
6081556Srgrimes * variable to allow a function to be unset when there is a readonly variable
6091556Srgrimes * with the same name.
6101556Srgrimes */
6111556Srgrimes
6121556Srgrimesunsetcmd(argc, argv)  char **argv; {
6131556Srgrimes	char **ap;
6141556Srgrimes	int i;
6151556Srgrimes	int flg_func = 0;
6161556Srgrimes	int flg_var = 0;
6171556Srgrimes	int ret = 0;
6181556Srgrimes
6191556Srgrimes	while ((i = nextopt("vf")) != '\0') {
6201556Srgrimes		if (i == 'f')
6211556Srgrimes			flg_func = 1;
6221556Srgrimes		else
6231556Srgrimes			flg_var = 1;
6241556Srgrimes	}
6251556Srgrimes	if (flg_func == 0 && flg_var == 0)
6261556Srgrimes		flg_var = 1;
6278855Srgrimes
6281556Srgrimes	for (ap = argptr; *ap ; ap++) {
6291556Srgrimes		if (flg_func)
6301556Srgrimes			ret |= unsetfunc(*ap);
6311556Srgrimes		if (flg_var)
6321556Srgrimes			ret |= unsetvar(*ap);
6331556Srgrimes	}
6341556Srgrimes	return ret;
6351556Srgrimes}
6361556Srgrimes
6371556Srgrimes
6381556Srgrimes/*
6391556Srgrimes * Unset the specified variable.
6401556Srgrimes */
6411556Srgrimes
6421556SrgrimesSTATIC int
6431556Srgrimesunsetvar(s)
6441556Srgrimes	char *s;
6451556Srgrimes	{
6461556Srgrimes	struct var **vpp;
6471556Srgrimes	struct var *vp;
6481556Srgrimes
6491556Srgrimes	vpp = hashvar(s);
6501556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
6511556Srgrimes		if (varequal(vp->text, s)) {
6521556Srgrimes			if (vp->flags & VREADONLY)
6531556Srgrimes				return (1);
6541556Srgrimes			INTOFF;
6551556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
6561556Srgrimes				setvar(s, nullstr, 0);
65717525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
65817525Sache				unsetenv(s);
65917525Sache				setlocale(LC_ALL, "");
66017525Sache			}
6611556Srgrimes			vp->flags &=~ VEXPORT;
6621556Srgrimes			vp->flags |= VUNSET;
6631556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
6641556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
6651556Srgrimes					ckfree(vp->text);
6661556Srgrimes				*vpp = vp->next;
6671556Srgrimes				ckfree(vp);
6681556Srgrimes			}
6691556Srgrimes			INTON;
6701556Srgrimes			return (0);
6711556Srgrimes		}
6721556Srgrimes	}
6731556Srgrimes
6741556Srgrimes	return (1);
6751556Srgrimes}
6761556Srgrimes
6771556Srgrimes
6781556Srgrimes
6791556Srgrimes/*
6801556Srgrimes * Find the appropriate entry in the hash table from the name.
6811556Srgrimes */
6821556Srgrimes
6831556SrgrimesSTATIC struct var **
6841556Srgrimeshashvar(p)
6851556Srgrimes	register char *p;
6861556Srgrimes	{
6871556Srgrimes	unsigned int hashval;
6881556Srgrimes
68917525Sache	hashval = ((unsigned char)*p) << 4;
6901556Srgrimes	while (*p && *p != '=')
69117525Sache		hashval += (unsigned char)*p++;
6921556Srgrimes	return &vartab[hashval % VTABSIZE];
6931556Srgrimes}
6941556Srgrimes
6951556Srgrimes
6961556Srgrimes
6971556Srgrimes/*
6981556Srgrimes * Returns true if the two strings specify the same varable.  The first
6991556Srgrimes * variable name is terminated by '='; the second may be terminated by
7001556Srgrimes * either '=' or '\0'.
7011556Srgrimes */
7021556Srgrimes
7031556SrgrimesSTATIC int
7041556Srgrimesvarequal(p, q)
7051556Srgrimes	register char *p, *q;
7061556Srgrimes	{
7071556Srgrimes	while (*p == *q++) {
7081556Srgrimes		if (*p++ == '=')
7091556Srgrimes			return 1;
7101556Srgrimes	}
7111556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
7121556Srgrimes		return 1;
7131556Srgrimes	return 0;
7141556Srgrimes}
715