var.c revision 97914
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
4136150Scharnierstatic const char rcsid[] =
4250471Speter  "$FreeBSD: head/bin/sh/var.c 97914 2002-06-06 03:57:22Z tjr $";
431556Srgrimes#endif /* not lint */
441556Srgrimes
4517987Speter#include <unistd.h>
4617987Speter#include <stdlib.h>
4717987Speter
481556Srgrimes/*
491556Srgrimes * Shell variables.
501556Srgrimes */
511556Srgrimes
5217525Sache#include <locale.h>
5317525Sache
541556Srgrimes#include "shell.h"
551556Srgrimes#include "output.h"
561556Srgrimes#include "expand.h"
571556Srgrimes#include "nodes.h"	/* for other headers */
581556Srgrimes#include "eval.h"	/* defines cmdenviron */
591556Srgrimes#include "exec.h"
601556Srgrimes#include "syntax.h"
611556Srgrimes#include "options.h"
621556Srgrimes#include "mail.h"
631556Srgrimes#include "var.h"
641556Srgrimes#include "memalloc.h"
651556Srgrimes#include "error.h"
661556Srgrimes#include "mystring.h"
6720425Ssteve#include "parser.h"
6817987Speter#ifndef NO_HISTORY
6917987Speter#include "myhistedit.h"
7017987Speter#endif
711556Srgrimes
721556Srgrimes
731556Srgrimes#define VTABSIZE 39
741556Srgrimes
751556Srgrimes
761556Srgrimesstruct varinit {
771556Srgrimes	struct var *var;
781556Srgrimes	int flags;
791556Srgrimes	char *text;
8090111Simp	void (*func)(const char *);
811556Srgrimes};
821556Srgrimes
831556Srgrimes
841556Srgrimes#if ATTY
851556Srgrimesstruct var vatty;
861556Srgrimes#endif
8717987Speter#ifndef NO_HISTORY
881556Srgrimesstruct var vhistsize;
8917987Speter#endif
901556Srgrimesstruct var vifs;
911556Srgrimesstruct var vmail;
921556Srgrimesstruct var vmpath;
931556Srgrimesstruct var vpath;
9497689Stjrstruct var vppid;
951556Srgrimesstruct var vps1;
961556Srgrimesstruct var vps2;
971556Srgrimesstruct var vvers;
981556Srgrimes#if ATTY
991556Srgrimesstruct var vterm;
1001556Srgrimes#endif
10120425Sstevestruct var voptind;
1021556Srgrimes
1031556Srgrimesconst struct varinit varinit[] = {
1041556Srgrimes#if ATTY
10520425Ssteve	{ &vatty,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY=",
10620425Ssteve	  NULL },
1071556Srgrimes#endif
10817987Speter#ifndef NO_HISTORY
10920425Ssteve	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
11020425Ssteve	  sethistsize },
11117987Speter#endif
11220425Ssteve	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
11320425Ssteve	  NULL },
11420425Ssteve	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
11520425Ssteve	  NULL },
11620425Ssteve	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
11720425Ssteve	  NULL },
11820425Ssteve	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=/bin:/usr/bin",
11920425Ssteve	  changepath },
12097689Stjr	{ &vppid,	VSTRFIXED|VTEXTFIXED|VUNSET,	"PPID=",
12197689Stjr	  NULL },
1228855Srgrimes	/*
1231556Srgrimes	 * vps1 depends on uid
1241556Srgrimes	 */
12520425Ssteve	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
12620425Ssteve	  NULL },
1271556Srgrimes#if ATTY
12820425Ssteve	{ &vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM=",
12920425Ssteve	  NULL },
1301556Srgrimes#endif
13120425Ssteve	{ &voptind,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1",
13220425Ssteve	  getoptsreset },
13320425Ssteve	{ NULL,	0,				NULL,
13420425Ssteve	  NULL }
1351556Srgrimes};
1361556Srgrimes
1371556Srgrimesstruct var *vartab[VTABSIZE];
1381556Srgrimes
13990111SimpSTATIC struct var **hashvar(char *);
14090111SimpSTATIC int varequal(char *, char *);
14190111SimpSTATIC int localevar(char *);
1421556Srgrimes
1431556Srgrimes/*
1441556Srgrimes * Initialize the varable symbol tables and import the environment
1451556Srgrimes */
1461556Srgrimes
1471556Srgrimes#ifdef mkinit
1481556SrgrimesINCLUDE "var.h"
1491556SrgrimesINIT {
1501556Srgrimes	char **envp;
1511556Srgrimes	extern char **environ;
1521556Srgrimes
1531556Srgrimes	initvar();
1541556Srgrimes	for (envp = environ ; *envp ; envp++) {
1551556Srgrimes		if (strchr(*envp, '=')) {
1561556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1571556Srgrimes		}
1581556Srgrimes	}
1591556Srgrimes}
1601556Srgrimes#endif
1611556Srgrimes
1621556Srgrimes
1631556Srgrimes/*
1641556Srgrimes * This routine initializes the builtin variables.  It is called when the
1651556Srgrimes * shell is initialized and again when a shell procedure is spawned.
1661556Srgrimes */
1671556Srgrimes
1681556Srgrimesvoid
16990111Simpinitvar(void)
17090111Simp{
17197689Stjr	char ppid[20];
1721556Srgrimes	const struct varinit *ip;
1731556Srgrimes	struct var *vp;
1741556Srgrimes	struct var **vpp;
1751556Srgrimes
1761556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1771556Srgrimes		if ((vp->flags & VEXPORT) == 0) {
1781556Srgrimes			vpp = hashvar(ip->text);
1791556Srgrimes			vp->next = *vpp;
1801556Srgrimes			*vpp = vp;
1811556Srgrimes			vp->text = ip->text;
1821556Srgrimes			vp->flags = ip->flags;
18320425Ssteve			vp->func = ip->func;
1841556Srgrimes		}
1851556Srgrimes	}
1861556Srgrimes	/*
1871556Srgrimes	 * PS1 depends on uid
1881556Srgrimes	 */
1891556Srgrimes	if ((vps1.flags & VEXPORT) == 0) {
1901556Srgrimes		vpp = hashvar("PS1=");
1911556Srgrimes		vps1.next = *vpp;
1921556Srgrimes		*vpp = &vps1;
1931556Srgrimes		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
1941556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1951556Srgrimes	}
19697689Stjr	if ((vppid.flags & VEXPORT) == 0) {
19797689Stjr		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
19897689Stjr		setvarsafe("PPID", ppid, 0);
19997689Stjr	}
2001556Srgrimes}
2011556Srgrimes
2021556Srgrimes/*
20320425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
20420425Ssteve */
20520425Ssteve
20620425Ssteveint
20790111Simpsetvarsafe(char *name, char *val, int flags)
20820425Ssteve{
20920425Ssteve	struct jmploc jmploc;
21020425Ssteve	struct jmploc *volatile savehandler = handler;
21120425Ssteve	int err = 0;
21220425Ssteve#if __GNUC__
21320425Ssteve	/* Avoid longjmp clobbering */
21420425Ssteve	(void) &err;
21520425Ssteve#endif
21620425Ssteve
21720425Ssteve	if (setjmp(jmploc.loc))
21820425Ssteve		err = 1;
21920425Ssteve	else {
22020425Ssteve		handler = &jmploc;
22120425Ssteve		setvar(name, val, flags);
22220425Ssteve	}
22320425Ssteve	handler = savehandler;
22420425Ssteve	return err;
22520425Ssteve}
22620425Ssteve
22720425Ssteve/*
22845621Scracauer * Set the value of a variable.  The flags argument is tored with the
2291556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2301556Srgrimes */
2311556Srgrimes
2321556Srgrimesvoid
23390111Simpsetvar(char *name, char *val, int flags)
23417987Speter{
2351556Srgrimes	char *p, *q;
2361556Srgrimes	int len;
2371556Srgrimes	int namelen;
2381556Srgrimes	char *nameeq;
2391556Srgrimes	int isbad;
2401556Srgrimes
2411556Srgrimes	isbad = 0;
2421556Srgrimes	p = name;
24317525Sache	if (! is_name(*p))
2441556Srgrimes		isbad = 1;
24517525Sache	p++;
2461556Srgrimes	for (;;) {
2471556Srgrimes		if (! is_in_name(*p)) {
2481556Srgrimes			if (*p == '\0' || *p == '=')
2491556Srgrimes				break;
2501556Srgrimes			isbad = 1;
2511556Srgrimes		}
2521556Srgrimes		p++;
2531556Srgrimes	}
2541556Srgrimes	namelen = p - name;
2551556Srgrimes	if (isbad)
2561556Srgrimes		error("%.*s: bad variable name", namelen, name);
2571556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2581556Srgrimes	if (val == NULL) {
2591556Srgrimes		flags |= VUNSET;
2601556Srgrimes	} else {
2611556Srgrimes		len += strlen(val);
2621556Srgrimes	}
2631556Srgrimes	p = nameeq = ckmalloc(len);
2641556Srgrimes	q = name;
2651556Srgrimes	while (--namelen >= 0)
2661556Srgrimes		*p++ = *q++;
2671556Srgrimes	*p++ = '=';
2681556Srgrimes	*p = '\0';
2691556Srgrimes	if (val)
2701556Srgrimes		scopy(val, p);
2711556Srgrimes	setvareq(nameeq, flags);
2721556Srgrimes}
2731556Srgrimes
27417525SacheSTATIC int
27590111Simplocalevar(char *s)
27690111Simp{
27717525Sache	static char *lnames[7] = {
27817525Sache		"ALL", "COLLATE", "CTYPE", "MONETARY",
27917525Sache		"NUMERIC", "TIME", NULL
28017525Sache	};
28117525Sache	char **ss;
2821556Srgrimes
28317525Sache	if (*s != 'L')
28417525Sache		return 0;
28517525Sache	if (varequal(s + 1, "ANG"))
28617525Sache		return 1;
28717525Sache	if (strncmp(s + 1, "C_", 2) != 0)
28817525Sache		return 0;
28917525Sache	for (ss = lnames; *ss ; ss++)
29017525Sache		if (varequal(s + 3, *ss))
29117525Sache			return 1;
29217525Sache	return 0;
29317525Sache}
2941556Srgrimes
2951556Srgrimes/*
2961556Srgrimes * Same as setvar except that the variable and value are passed in
2971556Srgrimes * the first argument as name=value.  Since the first argument will
2981556Srgrimes * be actually stored in the table, it should not be a string that
2991556Srgrimes * will go away.
3001556Srgrimes */
3011556Srgrimes
3021556Srgrimesvoid
30390111Simpsetvareq(char *s, int flags)
30417987Speter{
3051556Srgrimes	struct var *vp, **vpp;
30690112Simp	int len;
3071556Srgrimes
30845263Scracauer	if (aflag)
30945263Scracauer		flags |= VEXPORT;
3101556Srgrimes	vpp = hashvar(s);
3111556Srgrimes	for (vp = *vpp ; vp ; vp = vp->next) {
3121556Srgrimes		if (varequal(s, vp->text)) {
3131556Srgrimes			if (vp->flags & VREADONLY) {
31490112Simp				len = strchr(s, '=') - s;
3151556Srgrimes				error("%.*s: is read only", len, s);
3161556Srgrimes			}
3171556Srgrimes			INTOFF;
31820425Ssteve
31920425Ssteve			if (vp->func && (flags & VNOFUNC) == 0)
32020425Ssteve				(*vp->func)(strchr(s, '=') + 1);
32120425Ssteve
3221556Srgrimes			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
3231556Srgrimes				ckfree(vp->text);
32420425Ssteve
32520425Ssteve			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
3261556Srgrimes			vp->flags |= flags;
3271556Srgrimes			vp->text = s;
32820425Ssteve
32920425Ssteve			/*
33020425Ssteve			 * We could roll this to a function, to handle it as
33120425Ssteve			 * a regular variable function callback, but why bother?
33220425Ssteve			 */
3331556Srgrimes			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
3341556Srgrimes				chkmail(1);
33517525Sache			if ((vp->flags & VEXPORT) && localevar(s)) {
33617525Sache				putenv(s);
33717525Sache				(void) setlocale(LC_ALL, "");
33817525Sache			}
3391556Srgrimes			INTON;
3401556Srgrimes			return;
3411556Srgrimes		}
3421556Srgrimes	}
3431556Srgrimes	/* not found */
3441556Srgrimes	vp = ckmalloc(sizeof (*vp));
3451556Srgrimes	vp->flags = flags;
3461556Srgrimes	vp->text = s;
3471556Srgrimes	vp->next = *vpp;
34820425Ssteve	vp->func = NULL;
34917525Sache	INTOFF;
3501556Srgrimes	*vpp = vp;
35117525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
35217525Sache		putenv(s);
35317525Sache		(void) setlocale(LC_ALL, "");
35417525Sache	}
35517525Sache	INTON;
3561556Srgrimes}
3571556Srgrimes
3581556Srgrimes
3591556Srgrimes
3601556Srgrimes/*
3611556Srgrimes * Process a linked list of variable assignments.
3621556Srgrimes */
3631556Srgrimes
3641556Srgrimesvoid
36590111Simplistsetvar(struct strlist *list)
36690111Simp{
3671556Srgrimes	struct strlist *lp;
3681556Srgrimes
3691556Srgrimes	INTOFF;
3701556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
3711556Srgrimes		setvareq(savestr(lp->text), 0);
3721556Srgrimes	}
3731556Srgrimes	INTON;
3741556Srgrimes}
3751556Srgrimes
3761556Srgrimes
3771556Srgrimes
3781556Srgrimes/*
3791556Srgrimes * Find the value of a variable.  Returns NULL if not set.
3801556Srgrimes */
3811556Srgrimes
3821556Srgrimeschar *
38390111Simplookupvar(char *name)
38490111Simp{
3851556Srgrimes	struct var *v;
3861556Srgrimes
3871556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
3881556Srgrimes		if (varequal(v->text, name)) {
3891556Srgrimes			if (v->flags & VUNSET)
3901556Srgrimes				return NULL;
3911556Srgrimes			return strchr(v->text, '=') + 1;
3921556Srgrimes		}
3931556Srgrimes	}
3941556Srgrimes	return NULL;
3951556Srgrimes}
3961556Srgrimes
3971556Srgrimes
3981556Srgrimes
3991556Srgrimes/*
4001556Srgrimes * Search the environment of a builtin command.  If the second argument
4011556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
4021556Srgrimes * exported.
4031556Srgrimes */
4041556Srgrimes
4051556Srgrimeschar *
40690111Simpbltinlookup(char *name, int doall)
40717987Speter{
4081556Srgrimes	struct strlist *sp;
4091556Srgrimes	struct var *v;
4101556Srgrimes
4111556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
4121556Srgrimes		if (varequal(sp->text, name))
4131556Srgrimes			return strchr(sp->text, '=') + 1;
4141556Srgrimes	}
4151556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
4161556Srgrimes		if (varequal(v->text, name)) {
41717987Speter			if ((v->flags & VUNSET)
41817987Speter			 || (!doall && (v->flags & VEXPORT) == 0))
4191556Srgrimes				return NULL;
4201556Srgrimes			return strchr(v->text, '=') + 1;
4211556Srgrimes		}
4221556Srgrimes	}
4231556Srgrimes	return NULL;
4241556Srgrimes}
4251556Srgrimes
4261556Srgrimes
4271556Srgrimes
4281556Srgrimes/*
4291556Srgrimes * Generate a list of exported variables.  This routine is used to construct
4301556Srgrimes * the third argument to execve when executing a program.
4311556Srgrimes */
4321556Srgrimes
4331556Srgrimeschar **
43490111Simpenvironment(void)
43590111Simp{
4361556Srgrimes	int nenv;
4371556Srgrimes	struct var **vpp;
4381556Srgrimes	struct var *vp;
4391556Srgrimes	char **env, **ep;
4401556Srgrimes
4411556Srgrimes	nenv = 0;
4421556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4431556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4441556Srgrimes			if (vp->flags & VEXPORT)
4451556Srgrimes				nenv++;
4461556Srgrimes	}
4471556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
4481556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4491556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4501556Srgrimes			if (vp->flags & VEXPORT)
4511556Srgrimes				*ep++ = vp->text;
4521556Srgrimes	}
4531556Srgrimes	*ep = NULL;
4541556Srgrimes	return env;
4551556Srgrimes}
4561556Srgrimes
4571556Srgrimes
4581556Srgrimes/*
4591556Srgrimes * Called when a shell procedure is invoked to clear out nonexported
4601556Srgrimes * variables.  It is also necessary to reallocate variables of with
4611556Srgrimes * VSTACK set since these are currently allocated on the stack.
4621556Srgrimes */
4631556Srgrimes
4641556Srgrimes#ifdef mkinit
4651556SrgrimesMKINIT void shprocvar();
4661556Srgrimes
4671556SrgrimesSHELLPROC {
4681556Srgrimes	shprocvar();
4691556Srgrimes}
4701556Srgrimes#endif
4711556Srgrimes
4721556Srgrimesvoid
47390111Simpshprocvar(void)
47490111Simp{
4751556Srgrimes	struct var **vpp;
4761556Srgrimes	struct var *vp, **prev;
4771556Srgrimes
4781556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4791556Srgrimes		for (prev = vpp ; (vp = *prev) != NULL ; ) {
4801556Srgrimes			if ((vp->flags & VEXPORT) == 0) {
4811556Srgrimes				*prev = vp->next;
4821556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
4831556Srgrimes					ckfree(vp->text);
4841556Srgrimes				if ((vp->flags & VSTRFIXED) == 0)
4851556Srgrimes					ckfree(vp);
4861556Srgrimes			} else {
4871556Srgrimes				if (vp->flags & VSTACK) {
4881556Srgrimes					vp->text = savestr(vp->text);
4891556Srgrimes					vp->flags &=~ VSTACK;
4901556Srgrimes				}
4911556Srgrimes				prev = &vp->next;
4921556Srgrimes			}
4931556Srgrimes		}
4941556Srgrimes	}
4951556Srgrimes	initvar();
4961556Srgrimes}
4971556Srgrimes
4981556Srgrimes
4991556Srgrimes
5001556Srgrimes/*
5011556Srgrimes * Command to list all variables which are set.  Currently this command
5021556Srgrimes * is invoked from the set command when the set command is called without
5031556Srgrimes * any variables.
5041556Srgrimes */
5051556Srgrimes
5061556Srgrimesint
50790111Simpshowvarscmd(int argc __unused, char **argv __unused)
50817987Speter{
5091556Srgrimes	struct var **vpp;
5101556Srgrimes	struct var *vp;
5111556Srgrimes
5121556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5131556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next) {
5141556Srgrimes			if ((vp->flags & VUNSET) == 0)
5151556Srgrimes				out1fmt("%s\n", vp->text);
5161556Srgrimes		}
5171556Srgrimes	}
5181556Srgrimes	return 0;
5191556Srgrimes}
5201556Srgrimes
5211556Srgrimes
5221556Srgrimes
5231556Srgrimes/*
5241556Srgrimes * The export and readonly commands.
5251556Srgrimes */
5261556Srgrimes
5271556Srgrimesint
52890111Simpexportcmd(int argc, char **argv)
52917987Speter{
5301556Srgrimes	struct var **vpp;
5311556Srgrimes	struct var *vp;
5321556Srgrimes	char *name;
5331556Srgrimes	char *p;
53497914Stjr	char *cmdname;
53597914Stjr	int ch, values;
5361556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
5371556Srgrimes
53897914Stjr	cmdname = argv[0];
53997914Stjr	optreset = optind = 1;
54097914Stjr	values = 0;
54197914Stjr	while ((ch = getopt(argc, argv, "p")) != -1) {
54297914Stjr		switch (ch) {
54397914Stjr		case 'p':
54497914Stjr			values = 1;
54597914Stjr			break;
54697914Stjr		case '?':
54797914Stjr		default:
54897914Stjr			error("unknown option: -%c", optopt);
54997914Stjr		}
55097914Stjr	}
55197914Stjr	argc -= optind;
55297914Stjr	argv += optind;
55397914Stjr
5541556Srgrimes	listsetvar(cmdenviron);
55597914Stjr	if (argc != 0) {
5561556Srgrimes		while ((name = *argptr++) != NULL) {
5571556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
5581556Srgrimes				p++;
5591556Srgrimes			} else {
5601556Srgrimes				vpp = hashvar(name);
5611556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
5621556Srgrimes					if (varequal(vp->text, name)) {
56390111Simp
5641556Srgrimes						vp->flags |= flag;
56517525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
56617525Sache							putenv(vp->text);
56717525Sache							(void) setlocale(LC_ALL, "");
56817525Sache						}
5691556Srgrimes						goto found;
5701556Srgrimes					}
5711556Srgrimes				}
5721556Srgrimes			}
5731556Srgrimes			setvar(name, p, flag);
5741556Srgrimesfound:;
5751556Srgrimes		}
5761556Srgrimes	} else {
5771556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5781556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
5791556Srgrimes				if (vp->flags & flag) {
58097914Stjr					if (values) {
58197914Stjr						out1str(cmdname);
58297914Stjr						out1c(' ');
58397914Stjr					}
5841556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
5851556Srgrimes						out1c(*p);
58697914Stjr					if (values && !(vp->flags & VUNSET)) {
58797914Stjr						out1c('=');
58897914Stjr						out1qstr(p + 1);
58997914Stjr					}
5901556Srgrimes					out1c('\n');
5911556Srgrimes				}
5921556Srgrimes			}
5931556Srgrimes		}
5941556Srgrimes	}
5951556Srgrimes	return 0;
5961556Srgrimes}
5971556Srgrimes
5981556Srgrimes
5991556Srgrimes/*
6001556Srgrimes * The "local" command.
6011556Srgrimes */
6021556Srgrimes
60317987Speterint
60490111Simplocalcmd(int argc __unused, char **argv __unused)
60517987Speter{
6061556Srgrimes	char *name;
6071556Srgrimes
6081556Srgrimes	if (! in_function())
6091556Srgrimes		error("Not in a function");
6101556Srgrimes	while ((name = *argptr++) != NULL) {
6111556Srgrimes		mklocal(name);
6121556Srgrimes	}
6131556Srgrimes	return 0;
6141556Srgrimes}
6151556Srgrimes
6161556Srgrimes
6171556Srgrimes/*
6181556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
6191556Srgrimes * value and flags are saved in a localvar structure.  The saved values
6201556Srgrimes * will be restored when the shell function returns.  We handle the name
6211556Srgrimes * "-" as a special case.
6221556Srgrimes */
6231556Srgrimes
6241556Srgrimesvoid
62590111Simpmklocal(char *name)
62690111Simp{
6271556Srgrimes	struct localvar *lvp;
6281556Srgrimes	struct var **vpp;
6291556Srgrimes	struct var *vp;
6301556Srgrimes
6311556Srgrimes	INTOFF;
6321556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
6331556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
6341556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
63517987Speter		memcpy(lvp->text, optlist, sizeof optlist);
6361556Srgrimes		vp = NULL;
6371556Srgrimes	} else {
6381556Srgrimes		vpp = hashvar(name);
6391556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
6401556Srgrimes		if (vp == NULL) {
6411556Srgrimes			if (strchr(name, '='))
6421556Srgrimes				setvareq(savestr(name), VSTRFIXED);
6431556Srgrimes			else
6441556Srgrimes				setvar(name, NULL, VSTRFIXED);
6451556Srgrimes			vp = *vpp;	/* the new variable */
6461556Srgrimes			lvp->text = NULL;
6471556Srgrimes			lvp->flags = VUNSET;
6481556Srgrimes		} else {
6491556Srgrimes			lvp->text = vp->text;
6501556Srgrimes			lvp->flags = vp->flags;
6511556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
6521556Srgrimes			if (strchr(name, '='))
6531556Srgrimes				setvareq(savestr(name), 0);
6541556Srgrimes		}
6551556Srgrimes	}
6561556Srgrimes	lvp->vp = vp;
6571556Srgrimes	lvp->next = localvars;
6581556Srgrimes	localvars = lvp;
6591556Srgrimes	INTON;
6601556Srgrimes}
6611556Srgrimes
6621556Srgrimes
6631556Srgrimes/*
6641556Srgrimes * Called after a function returns.
6651556Srgrimes */
6661556Srgrimes
6671556Srgrimesvoid
66890111Simppoplocalvars(void)
66990111Simp{
6701556Srgrimes	struct localvar *lvp;
6711556Srgrimes	struct var *vp;
6721556Srgrimes
6731556Srgrimes	while ((lvp = localvars) != NULL) {
6741556Srgrimes		localvars = lvp->next;
6751556Srgrimes		vp = lvp->vp;
6761556Srgrimes		if (vp == NULL) {	/* $- saved */
67717987Speter			memcpy(optlist, lvp->text, sizeof optlist);
6781556Srgrimes			ckfree(lvp->text);
6791556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
6801556Srgrimes			(void)unsetvar(vp->text);
6811556Srgrimes		} else {
6821556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
6831556Srgrimes				ckfree(vp->text);
6841556Srgrimes			vp->flags = lvp->flags;
6851556Srgrimes			vp->text = lvp->text;
6861556Srgrimes		}
6871556Srgrimes		ckfree(lvp);
6881556Srgrimes	}
6891556Srgrimes}
6901556Srgrimes
6911556Srgrimes
69217987Speterint
69390111Simpsetvarcmd(int argc, char **argv)
69417987Speter{
6951556Srgrimes	if (argc <= 2)
6961556Srgrimes		return unsetcmd(argc, argv);
6971556Srgrimes	else if (argc == 3)
6981556Srgrimes		setvar(argv[1], argv[2], 0);
6991556Srgrimes	else
7001556Srgrimes		error("List assignment not implemented");
7011556Srgrimes	return 0;
7021556Srgrimes}
7031556Srgrimes
7041556Srgrimes
7051556Srgrimes/*
7061556Srgrimes * The unset builtin command.  We unset the function before we unset the
7071556Srgrimes * variable to allow a function to be unset when there is a readonly variable
7081556Srgrimes * with the same name.
7091556Srgrimes */
7101556Srgrimes
71117987Speterint
71290111Simpunsetcmd(int argc __unused, char **argv __unused)
71317987Speter{
7141556Srgrimes	char **ap;
7151556Srgrimes	int i;
7161556Srgrimes	int flg_func = 0;
7171556Srgrimes	int flg_var = 0;
7181556Srgrimes	int ret = 0;
7191556Srgrimes
7201556Srgrimes	while ((i = nextopt("vf")) != '\0') {
7211556Srgrimes		if (i == 'f')
7221556Srgrimes			flg_func = 1;
7231556Srgrimes		else
7241556Srgrimes			flg_var = 1;
7251556Srgrimes	}
7261556Srgrimes	if (flg_func == 0 && flg_var == 0)
7271556Srgrimes		flg_var = 1;
7288855Srgrimes
7291556Srgrimes	for (ap = argptr; *ap ; ap++) {
7301556Srgrimes		if (flg_func)
7311556Srgrimes			ret |= unsetfunc(*ap);
7321556Srgrimes		if (flg_var)
7331556Srgrimes			ret |= unsetvar(*ap);
7341556Srgrimes	}
7351556Srgrimes	return ret;
7361556Srgrimes}
7371556Srgrimes
7381556Srgrimes
7391556Srgrimes/*
7401556Srgrimes * Unset the specified variable.
7411556Srgrimes */
7421556Srgrimes
74320425Ssteveint
74490111Simpunsetvar(char *s)
74590111Simp{
7461556Srgrimes	struct var **vpp;
7471556Srgrimes	struct var *vp;
7481556Srgrimes
7491556Srgrimes	vpp = hashvar(s);
7501556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
7511556Srgrimes		if (varequal(vp->text, s)) {
7521556Srgrimes			if (vp->flags & VREADONLY)
7531556Srgrimes				return (1);
7541556Srgrimes			INTOFF;
7551556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
7561556Srgrimes				setvar(s, nullstr, 0);
75717525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
75817525Sache				unsetenv(s);
75917525Sache				setlocale(LC_ALL, "");
76017525Sache			}
76120425Ssteve			vp->flags &= ~VEXPORT;
7621556Srgrimes			vp->flags |= VUNSET;
7631556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
7641556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
7651556Srgrimes					ckfree(vp->text);
7661556Srgrimes				*vpp = vp->next;
7671556Srgrimes				ckfree(vp);
7681556Srgrimes			}
7691556Srgrimes			INTON;
7701556Srgrimes			return (0);
7711556Srgrimes		}
7721556Srgrimes	}
7731556Srgrimes
7741556Srgrimes	return (1);
7751556Srgrimes}
7761556Srgrimes
7771556Srgrimes
7781556Srgrimes
7791556Srgrimes/*
7801556Srgrimes * Find the appropriate entry in the hash table from the name.
7811556Srgrimes */
7821556Srgrimes
7831556SrgrimesSTATIC struct var **
78490111Simphashvar(char *p)
78590111Simp{
7861556Srgrimes	unsigned int hashval;
7871556Srgrimes
78820425Ssteve	hashval = ((unsigned char) *p) << 4;
7891556Srgrimes	while (*p && *p != '=')
79020425Ssteve		hashval += (unsigned char) *p++;
7911556Srgrimes	return &vartab[hashval % VTABSIZE];
7921556Srgrimes}
7931556Srgrimes
7941556Srgrimes
7951556Srgrimes
7961556Srgrimes/*
7971556Srgrimes * Returns true if the two strings specify the same varable.  The first
7981556Srgrimes * variable name is terminated by '='; the second may be terminated by
7991556Srgrimes * either '=' or '\0'.
8001556Srgrimes */
8011556Srgrimes
8021556SrgrimesSTATIC int
80390111Simpvarequal(char *p, char *q)
80490111Simp{
8051556Srgrimes	while (*p == *q++) {
8061556Srgrimes		if (*p++ == '=')
8071556Srgrimes			return 1;
8081556Srgrimes	}
8091556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
8101556Srgrimes		return 1;
8111556Srgrimes	return 0;
8121556Srgrimes}
813