var.c revision 212467
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 * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD: head/bin/sh/var.c 212467 2010-09-11 14:15:50Z jilles $");
401556Srgrimes
4117987Speter#include <unistd.h>
4217987Speter#include <stdlib.h>
43114763Sobrien#include <paths.h>
4417987Speter
451556Srgrimes/*
461556Srgrimes * Shell variables.
471556Srgrimes */
481556Srgrimes
4917525Sache#include <locale.h>
5017525Sache
511556Srgrimes#include "shell.h"
521556Srgrimes#include "output.h"
531556Srgrimes#include "expand.h"
541556Srgrimes#include "nodes.h"	/* for other headers */
551556Srgrimes#include "eval.h"	/* defines cmdenviron */
561556Srgrimes#include "exec.h"
571556Srgrimes#include "syntax.h"
581556Srgrimes#include "options.h"
591556Srgrimes#include "mail.h"
601556Srgrimes#include "var.h"
611556Srgrimes#include "memalloc.h"
621556Srgrimes#include "error.h"
631556Srgrimes#include "mystring.h"
6420425Ssteve#include "parser.h"
6517987Speter#ifndef NO_HISTORY
6617987Speter#include "myhistedit.h"
6717987Speter#endif
681556Srgrimes
691556Srgrimes
701556Srgrimes#define VTABSIZE 39
711556Srgrimes
721556Srgrimes
731556Srgrimesstruct varinit {
741556Srgrimes	struct var *var;
751556Srgrimes	int flags;
76201056Sjilles	const char *text;
7790111Simp	void (*func)(const char *);
781556Srgrimes};
791556Srgrimes
801556Srgrimes
8117987Speter#ifndef NO_HISTORY
821556Srgrimesstruct var vhistsize;
83208755Sjillesstruct var vterm;
8417987Speter#endif
851556Srgrimesstruct var vifs;
861556Srgrimesstruct var vmail;
871556Srgrimesstruct var vmpath;
881556Srgrimesstruct var vpath;
8997689Stjrstruct var vppid;
901556Srgrimesstruct var vps1;
911556Srgrimesstruct var vps2;
92159632Sstefanfstruct var vps4;
931556Srgrimesstruct var vvers;
94117261SddsSTATIC struct var voptind;
951556Srgrimes
96117261SddsSTATIC const struct varinit varinit[] = {
9717987Speter#ifndef NO_HISTORY
98201056Sjilles	{ &vhistsize,	VUNSET,				"HISTSIZE=",
9920425Ssteve	  sethistsize },
10017987Speter#endif
101201056Sjilles	{ &vifs,	0,				"IFS= \t\n",
10220425Ssteve	  NULL },
103201056Sjilles	{ &vmail,	VUNSET,				"MAIL=",
10420425Ssteve	  NULL },
105201056Sjilles	{ &vmpath,	VUNSET,				"MAILPATH=",
10620425Ssteve	  NULL },
107201056Sjilles	{ &vpath,	0,				"PATH=" _PATH_DEFPATH,
10820425Ssteve	  changepath },
109201056Sjilles	{ &vppid,	VUNSET,				"PPID=",
11097689Stjr	  NULL },
1118855Srgrimes	/*
1121556Srgrimes	 * vps1 depends on uid
1131556Srgrimes	 */
114201056Sjilles	{ &vps2,	0,				"PS2=> ",
11520425Ssteve	  NULL },
116201056Sjilles	{ &vps4,	0,				"PS4=+ ",
117159632Sstefanf	  NULL },
118208755Sjilles#ifndef NO_HISTORY
119208755Sjilles	{ &vterm,	VUNSET,				"TERM=",
120208755Sjilles	  setterm },
121208755Sjilles#endif
122201056Sjilles	{ &voptind,	0,				"OPTIND=1",
12320425Ssteve	  getoptsreset },
12420425Ssteve	{ NULL,	0,				NULL,
12520425Ssteve	  NULL }
1261556Srgrimes};
1271556Srgrimes
128117261SddsSTATIC struct var *vartab[VTABSIZE];
1291556Srgrimes
130207678SjillesSTATIC const char *const locale_names[7] = {
131207678Sjilles	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
132207678Sjilles	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
133207678Sjilles};
134207678SjillesSTATIC const int locale_categories[7] = {
135207678Sjilles	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
136207678Sjilles};
137207678Sjilles
138200956SjillesSTATIC struct var **hashvar(const char *);
139200956SjillesSTATIC int varequal(const char *, const char *);
140200956SjillesSTATIC int localevar(const char *);
1411556Srgrimes
1421556Srgrimes/*
143155302Sschweikh * Initialize the variable symbol tables and import the environment.
1441556Srgrimes */
1451556Srgrimes
1461556Srgrimes#ifdef mkinit
1471556SrgrimesINCLUDE "var.h"
148201053SjillesMKINIT char **environ;
1491556SrgrimesINIT {
1501556Srgrimes	char **envp;
1511556Srgrimes
1521556Srgrimes	initvar();
1531556Srgrimes	for (envp = environ ; *envp ; envp++) {
1541556Srgrimes		if (strchr(*envp, '=')) {
1551556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1561556Srgrimes		}
1571556Srgrimes	}
1581556Srgrimes}
1591556Srgrimes#endif
1601556Srgrimes
1611556Srgrimes
1621556Srgrimes/*
1631556Srgrimes * This routine initializes the builtin variables.  It is called when the
1641556Srgrimes * shell is initialized and again when a shell procedure is spawned.
1651556Srgrimes */
1661556Srgrimes
1671556Srgrimesvoid
16890111Simpinitvar(void)
16990111Simp{
17097689Stjr	char ppid[20];
1711556Srgrimes	const struct varinit *ip;
1721556Srgrimes	struct var *vp;
1731556Srgrimes	struct var **vpp;
1741556Srgrimes
1751556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1761556Srgrimes		if ((vp->flags & VEXPORT) == 0) {
1771556Srgrimes			vpp = hashvar(ip->text);
1781556Srgrimes			vp->next = *vpp;
1791556Srgrimes			*vpp = vp;
180201056Sjilles			vp->text = __DECONST(char *, ip->text);
181201056Sjilles			vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
18220425Ssteve			vp->func = ip->func;
1831556Srgrimes		}
1841556Srgrimes	}
1851556Srgrimes	/*
1861556Srgrimes	 * PS1 depends on uid
1871556Srgrimes	 */
1881556Srgrimes	if ((vps1.flags & VEXPORT) == 0) {
1891556Srgrimes		vpp = hashvar("PS1=");
1901556Srgrimes		vps1.next = *vpp;
1911556Srgrimes		*vpp = &vps1;
192201056Sjilles		vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
1931556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1941556Srgrimes	}
19597689Stjr	if ((vppid.flags & VEXPORT) == 0) {
19697689Stjr		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
19797689Stjr		setvarsafe("PPID", ppid, 0);
19897689Stjr	}
1991556Srgrimes}
2001556Srgrimes
2011556Srgrimes/*
20220425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
20320425Ssteve */
20420425Ssteve
20520425Ssteveint
206200956Sjillessetvarsafe(const char *name, const char *val, int flags)
20720425Ssteve{
20820425Ssteve	struct jmploc jmploc;
209194765Sjilles	struct jmploc *const savehandler = handler;
21020425Ssteve	int err = 0;
211199660Sjilles	int inton;
21220425Ssteve
213199660Sjilles	inton = is_int_on();
21420425Ssteve	if (setjmp(jmploc.loc))
21520425Ssteve		err = 1;
21620425Ssteve	else {
21720425Ssteve		handler = &jmploc;
21820425Ssteve		setvar(name, val, flags);
21920425Ssteve	}
22020425Ssteve	handler = savehandler;
221199660Sjilles	SETINTON(inton);
22220425Ssteve	return err;
22320425Ssteve}
22420425Ssteve
22520425Ssteve/*
226155302Sschweikh * Set the value of a variable.  The flags argument is stored with the
2271556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2281556Srgrimes */
2291556Srgrimes
2301556Srgrimesvoid
231200956Sjillessetvar(const char *name, const char *val, int flags)
23217987Speter{
233200956Sjilles	const char *p;
2341556Srgrimes	int len;
2351556Srgrimes	int namelen;
2361556Srgrimes	char *nameeq;
2371556Srgrimes	int isbad;
2381556Srgrimes
2391556Srgrimes	isbad = 0;
2401556Srgrimes	p = name;
24117525Sache	if (! is_name(*p))
2421556Srgrimes		isbad = 1;
24317525Sache	p++;
2441556Srgrimes	for (;;) {
2451556Srgrimes		if (! is_in_name(*p)) {
2461556Srgrimes			if (*p == '\0' || *p == '=')
2471556Srgrimes				break;
2481556Srgrimes			isbad = 1;
2491556Srgrimes		}
2501556Srgrimes		p++;
2511556Srgrimes	}
2521556Srgrimes	namelen = p - name;
2531556Srgrimes	if (isbad)
2541556Srgrimes		error("%.*s: bad variable name", namelen, name);
2551556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2561556Srgrimes	if (val == NULL) {
2571556Srgrimes		flags |= VUNSET;
2581556Srgrimes	} else {
2591556Srgrimes		len += strlen(val);
2601556Srgrimes	}
261200956Sjilles	nameeq = ckmalloc(len);
262200956Sjilles	memcpy(nameeq, name, namelen);
263200956Sjilles	nameeq[namelen] = '=';
2641556Srgrimes	if (val)
265200956Sjilles		scopy(val, nameeq + namelen + 1);
266200956Sjilles	else
267200956Sjilles		nameeq[namelen + 1] = '\0';
2681556Srgrimes	setvareq(nameeq, flags);
2691556Srgrimes}
2701556Srgrimes
27117525SacheSTATIC int
272200956Sjilleslocalevar(const char *s)
27390111Simp{
274207678Sjilles	const char *const *ss;
2751556Srgrimes
27617525Sache	if (*s != 'L')
27717525Sache		return 0;
27817525Sache	if (varequal(s + 1, "ANG"))
27917525Sache		return 1;
28017525Sache	if (strncmp(s + 1, "C_", 2) != 0)
28117525Sache		return 0;
282207678Sjilles	if (varequal(s + 3, "ALL"))
283207678Sjilles		return 1;
284207678Sjilles	for (ss = locale_names; *ss ; ss++)
285207678Sjilles		if (varequal(s + 3, *ss + 3))
28617525Sache			return 1;
28717525Sache	return 0;
28817525Sache}
2891556Srgrimes
290171268Sscf
2911556Srgrimes/*
292171268Sscf * Sets/unsets an environment variable from a pointer that may actually be a
293171268Sscf * pointer into environ where the string should not be manipulated.
294171268Sscf */
295171268Sscfstatic void
296200956Sjilleschange_env(const char *s, int set)
297171268Sscf{
298171268Sscf	char *eqp;
299171268Sscf	char *ss;
300171268Sscf
301171268Sscf	ss = savestr(s);
302171268Sscf	if ((eqp = strchr(ss, '=')) != NULL)
303171268Sscf		*eqp = '\0';
304171268Sscf	if (set && eqp != NULL)
305171268Sscf		(void) setenv(ss, eqp + 1, 1);
306171268Sscf	else
307171268Sscf		(void) unsetenv(ss);
308171268Sscf	ckfree(ss);
309171268Sscf
310171268Sscf	return;
311171268Sscf}
312171268Sscf
313171268Sscf
314171268Sscf/*
3151556Srgrimes * Same as setvar except that the variable and value are passed in
3161556Srgrimes * the first argument as name=value.  Since the first argument will
3171556Srgrimes * be actually stored in the table, it should not be a string that
3181556Srgrimes * will go away.
3191556Srgrimes */
3201556Srgrimes
3211556Srgrimesvoid
32290111Simpsetvareq(char *s, int flags)
32317987Speter{
3241556Srgrimes	struct var *vp, **vpp;
32590112Simp	int len;
3261556Srgrimes
32745263Scracauer	if (aflag)
32845263Scracauer		flags |= VEXPORT;
3291556Srgrimes	vpp = hashvar(s);
3301556Srgrimes	for (vp = *vpp ; vp ; vp = vp->next) {
3311556Srgrimes		if (varequal(s, vp->text)) {
3321556Srgrimes			if (vp->flags & VREADONLY) {
33390112Simp				len = strchr(s, '=') - s;
3341556Srgrimes				error("%.*s: is read only", len, s);
3351556Srgrimes			}
3361556Srgrimes			INTOFF;
33720425Ssteve
33820425Ssteve			if (vp->func && (flags & VNOFUNC) == 0)
33920425Ssteve				(*vp->func)(strchr(s, '=') + 1);
34020425Ssteve
3411556Srgrimes			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
3421556Srgrimes				ckfree(vp->text);
34320425Ssteve
34420425Ssteve			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
3451556Srgrimes			vp->flags |= flags;
3461556Srgrimes			vp->text = s;
34720425Ssteve
34820425Ssteve			/*
34920425Ssteve			 * We could roll this to a function, to handle it as
35020425Ssteve			 * a regular variable function callback, but why bother?
351203576Sjilles			 *
352203576Sjilles			 * Note: this assumes iflag is not set to 1 initially.
353203576Sjilles			 * As part of init(), this is called before arguments
354203576Sjilles			 * are looked at.
35520425Ssteve			 */
356203576Sjilles			if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
357203576Sjilles			    iflag == 1)
3581556Srgrimes				chkmail(1);
35917525Sache			if ((vp->flags & VEXPORT) && localevar(s)) {
360171268Sscf				change_env(s, 1);
36117525Sache				(void) setlocale(LC_ALL, "");
36217525Sache			}
3631556Srgrimes			INTON;
3641556Srgrimes			return;
3651556Srgrimes		}
3661556Srgrimes	}
3671556Srgrimes	/* not found */
3681556Srgrimes	vp = ckmalloc(sizeof (*vp));
3691556Srgrimes	vp->flags = flags;
3701556Srgrimes	vp->text = s;
3711556Srgrimes	vp->next = *vpp;
37220425Ssteve	vp->func = NULL;
37317525Sache	INTOFF;
3741556Srgrimes	*vpp = vp;
37517525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
376171268Sscf		change_env(s, 1);
37717525Sache		(void) setlocale(LC_ALL, "");
37817525Sache	}
37917525Sache	INTON;
3801556Srgrimes}
3811556Srgrimes
3821556Srgrimes
3831556Srgrimes
3841556Srgrimes/*
3851556Srgrimes * Process a linked list of variable assignments.
3861556Srgrimes */
3871556Srgrimes
3881556Srgrimesvoid
38990111Simplistsetvar(struct strlist *list)
39090111Simp{
3911556Srgrimes	struct strlist *lp;
3921556Srgrimes
3931556Srgrimes	INTOFF;
3941556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
3951556Srgrimes		setvareq(savestr(lp->text), 0);
3961556Srgrimes	}
3971556Srgrimes	INTON;
3981556Srgrimes}
3991556Srgrimes
4001556Srgrimes
4011556Srgrimes
4021556Srgrimes/*
4031556Srgrimes * Find the value of a variable.  Returns NULL if not set.
4041556Srgrimes */
4051556Srgrimes
4061556Srgrimeschar *
407200956Sjilleslookupvar(const char *name)
40890111Simp{
4091556Srgrimes	struct var *v;
4101556Srgrimes
4111556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
4121556Srgrimes		if (varequal(v->text, name)) {
4131556Srgrimes			if (v->flags & VUNSET)
4141556Srgrimes				return NULL;
4151556Srgrimes			return strchr(v->text, '=') + 1;
4161556Srgrimes		}
4171556Srgrimes	}
4181556Srgrimes	return NULL;
4191556Srgrimes}
4201556Srgrimes
4211556Srgrimes
4221556Srgrimes
4231556Srgrimes/*
4241556Srgrimes * Search the environment of a builtin command.  If the second argument
4251556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
4261556Srgrimes * exported.
4271556Srgrimes */
4281556Srgrimes
4291556Srgrimeschar *
430200956Sjillesbltinlookup(const char *name, int doall)
43117987Speter{
4321556Srgrimes	struct strlist *sp;
4331556Srgrimes	struct var *v;
434212467Sjilles	char *result;
4351556Srgrimes
436212467Sjilles	result = NULL;
4371556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
4381556Srgrimes		if (varequal(sp->text, name))
439212467Sjilles			result = strchr(sp->text, '=') + 1;
4401556Srgrimes	}
441212467Sjilles	if (result != NULL)
442212467Sjilles		return result;
4431556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
4441556Srgrimes		if (varequal(v->text, name)) {
44517987Speter			if ((v->flags & VUNSET)
44617987Speter			 || (!doall && (v->flags & VEXPORT) == 0))
4471556Srgrimes				return NULL;
4481556Srgrimes			return strchr(v->text, '=') + 1;
4491556Srgrimes		}
4501556Srgrimes	}
4511556Srgrimes	return NULL;
4521556Srgrimes}
4531556Srgrimes
4541556Srgrimes
455207678Sjilles/*
456207678Sjilles * Set up locale for a builtin (LANG/LC_* assignments).
457207678Sjilles */
458207678Sjillesvoid
459207678Sjillesbltinsetlocale(void)
460207678Sjilles{
461207678Sjilles	struct strlist *lp;
462207678Sjilles	int act = 0;
463207678Sjilles	char *loc, *locdef;
464207678Sjilles	int i;
4651556Srgrimes
466207678Sjilles	for (lp = cmdenviron ; lp ; lp = lp->next) {
467207678Sjilles		if (localevar(lp->text)) {
468207678Sjilles			act = 1;
469207678Sjilles			break;
470207678Sjilles		}
471207678Sjilles	}
472207678Sjilles	if (!act)
473207678Sjilles		return;
474207678Sjilles	loc = bltinlookup("LC_ALL", 0);
475207678Sjilles	INTOFF;
476207678Sjilles	if (loc != NULL) {
477207678Sjilles		setlocale(LC_ALL, loc);
478207678Sjilles		INTON;
479207678Sjilles		return;
480207678Sjilles	}
481207678Sjilles	locdef = bltinlookup("LANG", 0);
482207678Sjilles	for (i = 0; locale_names[i] != NULL; i++) {
483207678Sjilles		loc = bltinlookup(locale_names[i], 0);
484207678Sjilles		if (loc == NULL)
485207678Sjilles			loc = locdef;
486207678Sjilles		if (loc != NULL)
487207678Sjilles			setlocale(locale_categories[i], loc);
488207678Sjilles	}
489207678Sjilles	INTON;
490207678Sjilles}
491207678Sjilles
4921556Srgrimes/*
493207678Sjilles * Undo the effect of bltinlocaleset().
494207678Sjilles */
495207678Sjillesvoid
496207678Sjillesbltinunsetlocale(void)
497207678Sjilles{
498207678Sjilles	struct strlist *lp;
499207678Sjilles
500207678Sjilles	INTOFF;
501207678Sjilles	for (lp = cmdenviron ; lp ; lp = lp->next) {
502207678Sjilles		if (localevar(lp->text)) {
503207678Sjilles			setlocale(LC_ALL, "");
504207678Sjilles			return;
505207678Sjilles		}
506207678Sjilles	}
507207678Sjilles	INTON;
508207678Sjilles}
509207678Sjilles
510207678Sjilles
511207678Sjilles/*
5121556Srgrimes * Generate a list of exported variables.  This routine is used to construct
5131556Srgrimes * the third argument to execve when executing a program.
5141556Srgrimes */
5151556Srgrimes
5161556Srgrimeschar **
51790111Simpenvironment(void)
51890111Simp{
5191556Srgrimes	int nenv;
5201556Srgrimes	struct var **vpp;
5211556Srgrimes	struct var *vp;
5221556Srgrimes	char **env, **ep;
5231556Srgrimes
5241556Srgrimes	nenv = 0;
5251556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5261556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
5271556Srgrimes			if (vp->flags & VEXPORT)
5281556Srgrimes				nenv++;
5291556Srgrimes	}
5301556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
5311556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5321556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
5331556Srgrimes			if (vp->flags & VEXPORT)
5341556Srgrimes				*ep++ = vp->text;
5351556Srgrimes	}
5361556Srgrimes	*ep = NULL;
5371556Srgrimes	return env;
5381556Srgrimes}
5391556Srgrimes
5401556Srgrimes
5411556Srgrimes/*
5421556Srgrimes * Called when a shell procedure is invoked to clear out nonexported
5431556Srgrimes * variables.  It is also necessary to reallocate variables of with
5441556Srgrimes * VSTACK set since these are currently allocated on the stack.
5451556Srgrimes */
5461556Srgrimes
547149016SstefanfMKINIT void shprocvar(void);
5481556Srgrimes
549201053Sjilles#ifdef mkinit
5501556SrgrimesSHELLPROC {
5511556Srgrimes	shprocvar();
5521556Srgrimes}
5531556Srgrimes#endif
5541556Srgrimes
5551556Srgrimesvoid
55690111Simpshprocvar(void)
55790111Simp{
5581556Srgrimes	struct var **vpp;
5591556Srgrimes	struct var *vp, **prev;
5601556Srgrimes
5611556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5621556Srgrimes		for (prev = vpp ; (vp = *prev) != NULL ; ) {
5631556Srgrimes			if ((vp->flags & VEXPORT) == 0) {
5641556Srgrimes				*prev = vp->next;
5651556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
5661556Srgrimes					ckfree(vp->text);
5671556Srgrimes				if ((vp->flags & VSTRFIXED) == 0)
5681556Srgrimes					ckfree(vp);
5691556Srgrimes			} else {
5701556Srgrimes				if (vp->flags & VSTACK) {
5711556Srgrimes					vp->text = savestr(vp->text);
5721556Srgrimes					vp->flags &=~ VSTACK;
5731556Srgrimes				}
5741556Srgrimes				prev = &vp->next;
5751556Srgrimes			}
5761556Srgrimes		}
5771556Srgrimes	}
5781556Srgrimes	initvar();
5791556Srgrimes}
5801556Srgrimes
5811556Srgrimes
582158145Sstefanfstatic int
583158145Sstefanfvar_compare(const void *a, const void *b)
584158145Sstefanf{
585158145Sstefanf	const char *const *sa, *const *sb;
5861556Srgrimes
587158145Sstefanf	sa = a;
588158145Sstefanf	sb = b;
589158145Sstefanf	/*
590158145Sstefanf	 * This compares two var=value strings which creates a different
591158145Sstefanf	 * order from what you would probably expect.  POSIX is somewhat
592158145Sstefanf	 * ambiguous on what should be sorted exactly.
593158145Sstefanf	 */
594158145Sstefanf	return strcoll(*sa, *sb);
595158145Sstefanf}
596158145Sstefanf
597158145Sstefanf
5981556Srgrimes/*
5991556Srgrimes * Command to list all variables which are set.  Currently this command
6001556Srgrimes * is invoked from the set command when the set command is called without
6011556Srgrimes * any variables.
6021556Srgrimes */
6031556Srgrimes
6041556Srgrimesint
60590111Simpshowvarscmd(int argc __unused, char **argv __unused)
60617987Speter{
6071556Srgrimes	struct var **vpp;
6081556Srgrimes	struct var *vp;
60997915Stjr	const char *s;
610158145Sstefanf	const char **vars;
611158145Sstefanf	int i, n;
6121556Srgrimes
613158145Sstefanf	/*
614158145Sstefanf	 * POSIX requires us to sort the variables.
615158145Sstefanf	 */
616158145Sstefanf	n = 0;
617158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
618158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
619158145Sstefanf			if (!(vp->flags & VUNSET))
620158145Sstefanf				n++;
6211556Srgrimes		}
6221556Srgrimes	}
623158145Sstefanf
624158145Sstefanf	INTON;
625158145Sstefanf	vars = ckmalloc(n * sizeof(*vars));
626158145Sstefanf	i = 0;
627158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
628158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
629158145Sstefanf			if (!(vp->flags & VUNSET))
630158145Sstefanf				vars[i++] = vp->text;
631158145Sstefanf		}
632158145Sstefanf	}
633158145Sstefanf
634158145Sstefanf	qsort(vars, n, sizeof(*vars), var_compare);
635158145Sstefanf	for (i = 0; i < n; i++) {
636158145Sstefanf		for (s = vars[i]; *s != '='; s++)
637158145Sstefanf			out1c(*s);
638158145Sstefanf		out1c('=');
639158145Sstefanf		out1qstr(s + 1);
640158145Sstefanf		out1c('\n');
641158145Sstefanf	}
642158145Sstefanf	ckfree(vars);
643158145Sstefanf	INTOFF;
644158145Sstefanf
6451556Srgrimes	return 0;
6461556Srgrimes}
6471556Srgrimes
6481556Srgrimes
6491556Srgrimes
6501556Srgrimes/*
6511556Srgrimes * The export and readonly commands.
6521556Srgrimes */
6531556Srgrimes
6541556Srgrimesint
65590111Simpexportcmd(int argc, char **argv)
65617987Speter{
6571556Srgrimes	struct var **vpp;
6581556Srgrimes	struct var *vp;
6591556Srgrimes	char *name;
6601556Srgrimes	char *p;
66197914Stjr	char *cmdname;
66297914Stjr	int ch, values;
6631556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
6641556Srgrimes
66597914Stjr	cmdname = argv[0];
66697914Stjr	optreset = optind = 1;
667100663Stjr	opterr = 0;
66897914Stjr	values = 0;
66997914Stjr	while ((ch = getopt(argc, argv, "p")) != -1) {
67097914Stjr		switch (ch) {
67197914Stjr		case 'p':
67297914Stjr			values = 1;
67397914Stjr			break;
67497914Stjr		case '?':
67597914Stjr		default:
67697914Stjr			error("unknown option: -%c", optopt);
67797914Stjr		}
67897914Stjr	}
67997914Stjr	argc -= optind;
68097914Stjr	argv += optind;
68197914Stjr
682149919Sstefanf	if (values && argc != 0)
683149919Sstefanf		error("-p requires no arguments");
68497914Stjr	if (argc != 0) {
685149919Sstefanf		while ((name = *argv++) != NULL) {
6861556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
6871556Srgrimes				p++;
6881556Srgrimes			} else {
6891556Srgrimes				vpp = hashvar(name);
6901556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
6911556Srgrimes					if (varequal(vp->text, name)) {
69290111Simp
6931556Srgrimes						vp->flags |= flag;
69417525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
695171268Sscf							change_env(vp->text, 1);
69617525Sache							(void) setlocale(LC_ALL, "");
69717525Sache						}
6981556Srgrimes						goto found;
6991556Srgrimes					}
7001556Srgrimes				}
7011556Srgrimes			}
7021556Srgrimes			setvar(name, p, flag);
7031556Srgrimesfound:;
7041556Srgrimes		}
7051556Srgrimes	} else {
7061556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
7071556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
7081556Srgrimes				if (vp->flags & flag) {
70997914Stjr					if (values) {
71097914Stjr						out1str(cmdname);
71197914Stjr						out1c(' ');
71297914Stjr					}
7131556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
7141556Srgrimes						out1c(*p);
71597914Stjr					if (values && !(vp->flags & VUNSET)) {
71697914Stjr						out1c('=');
71797914Stjr						out1qstr(p + 1);
71897914Stjr					}
7191556Srgrimes					out1c('\n');
7201556Srgrimes				}
7211556Srgrimes			}
7221556Srgrimes		}
7231556Srgrimes	}
7241556Srgrimes	return 0;
7251556Srgrimes}
7261556Srgrimes
7271556Srgrimes
7281556Srgrimes/*
7291556Srgrimes * The "local" command.
7301556Srgrimes */
7311556Srgrimes
73217987Speterint
73390111Simplocalcmd(int argc __unused, char **argv __unused)
73417987Speter{
7351556Srgrimes	char *name;
7361556Srgrimes
7371556Srgrimes	if (! in_function())
7381556Srgrimes		error("Not in a function");
7391556Srgrimes	while ((name = *argptr++) != NULL) {
7401556Srgrimes		mklocal(name);
7411556Srgrimes	}
7421556Srgrimes	return 0;
7431556Srgrimes}
7441556Srgrimes
7451556Srgrimes
7461556Srgrimes/*
7471556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
7481556Srgrimes * value and flags are saved in a localvar structure.  The saved values
7491556Srgrimes * will be restored when the shell function returns.  We handle the name
7501556Srgrimes * "-" as a special case.
7511556Srgrimes */
7521556Srgrimes
7531556Srgrimesvoid
75490111Simpmklocal(char *name)
75590111Simp{
7561556Srgrimes	struct localvar *lvp;
7571556Srgrimes	struct var **vpp;
7581556Srgrimes	struct var *vp;
7591556Srgrimes
7601556Srgrimes	INTOFF;
7611556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
7621556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
7631556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
76417987Speter		memcpy(lvp->text, optlist, sizeof optlist);
7651556Srgrimes		vp = NULL;
7661556Srgrimes	} else {
7671556Srgrimes		vpp = hashvar(name);
7681556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
7691556Srgrimes		if (vp == NULL) {
7701556Srgrimes			if (strchr(name, '='))
7711556Srgrimes				setvareq(savestr(name), VSTRFIXED);
7721556Srgrimes			else
7731556Srgrimes				setvar(name, NULL, VSTRFIXED);
7741556Srgrimes			vp = *vpp;	/* the new variable */
7751556Srgrimes			lvp->text = NULL;
7761556Srgrimes			lvp->flags = VUNSET;
7771556Srgrimes		} else {
7781556Srgrimes			lvp->text = vp->text;
7791556Srgrimes			lvp->flags = vp->flags;
7801556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
7811556Srgrimes			if (strchr(name, '='))
7821556Srgrimes				setvareq(savestr(name), 0);
7831556Srgrimes		}
7841556Srgrimes	}
7851556Srgrimes	lvp->vp = vp;
7861556Srgrimes	lvp->next = localvars;
7871556Srgrimes	localvars = lvp;
7881556Srgrimes	INTON;
7891556Srgrimes}
7901556Srgrimes
7911556Srgrimes
7921556Srgrimes/*
7931556Srgrimes * Called after a function returns.
7941556Srgrimes */
7951556Srgrimes
7961556Srgrimesvoid
79790111Simppoplocalvars(void)
79890111Simp{
7991556Srgrimes	struct localvar *lvp;
8001556Srgrimes	struct var *vp;
8011556Srgrimes
8021556Srgrimes	while ((lvp = localvars) != NULL) {
8031556Srgrimes		localvars = lvp->next;
8041556Srgrimes		vp = lvp->vp;
8051556Srgrimes		if (vp == NULL) {	/* $- saved */
80617987Speter			memcpy(optlist, lvp->text, sizeof optlist);
8071556Srgrimes			ckfree(lvp->text);
8081556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8091556Srgrimes			(void)unsetvar(vp->text);
8101556Srgrimes		} else {
8111556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
8121556Srgrimes				ckfree(vp->text);
8131556Srgrimes			vp->flags = lvp->flags;
8141556Srgrimes			vp->text = lvp->text;
8151556Srgrimes		}
8161556Srgrimes		ckfree(lvp);
8171556Srgrimes	}
8181556Srgrimes}
8191556Srgrimes
8201556Srgrimes
82117987Speterint
82290111Simpsetvarcmd(int argc, char **argv)
82317987Speter{
8241556Srgrimes	if (argc <= 2)
8251556Srgrimes		return unsetcmd(argc, argv);
8261556Srgrimes	else if (argc == 3)
8271556Srgrimes		setvar(argv[1], argv[2], 0);
8281556Srgrimes	else
8291556Srgrimes		error("List assignment not implemented");
8301556Srgrimes	return 0;
8311556Srgrimes}
8321556Srgrimes
8331556Srgrimes
8341556Srgrimes/*
8351556Srgrimes * The unset builtin command.  We unset the function before we unset the
8361556Srgrimes * variable to allow a function to be unset when there is a readonly variable
8371556Srgrimes * with the same name.
8381556Srgrimes */
8391556Srgrimes
84017987Speterint
84190111Simpunsetcmd(int argc __unused, char **argv __unused)
84217987Speter{
8431556Srgrimes	char **ap;
8441556Srgrimes	int i;
8451556Srgrimes	int flg_func = 0;
8461556Srgrimes	int flg_var = 0;
8471556Srgrimes	int ret = 0;
8481556Srgrimes
8491556Srgrimes	while ((i = nextopt("vf")) != '\0') {
8501556Srgrimes		if (i == 'f')
8511556Srgrimes			flg_func = 1;
8521556Srgrimes		else
8531556Srgrimes			flg_var = 1;
8541556Srgrimes	}
8551556Srgrimes	if (flg_func == 0 && flg_var == 0)
8561556Srgrimes		flg_var = 1;
8578855Srgrimes
8581556Srgrimes	for (ap = argptr; *ap ; ap++) {
8591556Srgrimes		if (flg_func)
8601556Srgrimes			ret |= unsetfunc(*ap);
8611556Srgrimes		if (flg_var)
8621556Srgrimes			ret |= unsetvar(*ap);
8631556Srgrimes	}
8641556Srgrimes	return ret;
8651556Srgrimes}
8661556Srgrimes
8671556Srgrimes
8681556Srgrimes/*
8691556Srgrimes * Unset the specified variable.
8701556Srgrimes */
8711556Srgrimes
87220425Ssteveint
873200956Sjillesunsetvar(const char *s)
87490111Simp{
8751556Srgrimes	struct var **vpp;
8761556Srgrimes	struct var *vp;
8771556Srgrimes
8781556Srgrimes	vpp = hashvar(s);
8791556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
8801556Srgrimes		if (varequal(vp->text, s)) {
8811556Srgrimes			if (vp->flags & VREADONLY)
8821556Srgrimes				return (1);
8831556Srgrimes			INTOFF;
8841556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
8851556Srgrimes				setvar(s, nullstr, 0);
88617525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
887171268Sscf				change_env(s, 0);
88817525Sache				setlocale(LC_ALL, "");
88917525Sache			}
89020425Ssteve			vp->flags &= ~VEXPORT;
8911556Srgrimes			vp->flags |= VUNSET;
8921556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
8931556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
8941556Srgrimes					ckfree(vp->text);
8951556Srgrimes				*vpp = vp->next;
8961556Srgrimes				ckfree(vp);
8971556Srgrimes			}
8981556Srgrimes			INTON;
8991556Srgrimes			return (0);
9001556Srgrimes		}
9011556Srgrimes	}
9021556Srgrimes
903135856Sdes	return (0);
9041556Srgrimes}
9051556Srgrimes
9061556Srgrimes
9071556Srgrimes
9081556Srgrimes/*
9091556Srgrimes * Find the appropriate entry in the hash table from the name.
9101556Srgrimes */
9111556Srgrimes
9121556SrgrimesSTATIC struct var **
913200956Sjilleshashvar(const char *p)
91490111Simp{
9151556Srgrimes	unsigned int hashval;
9161556Srgrimes
91720425Ssteve	hashval = ((unsigned char) *p) << 4;
9181556Srgrimes	while (*p && *p != '=')
91920425Ssteve		hashval += (unsigned char) *p++;
9201556Srgrimes	return &vartab[hashval % VTABSIZE];
9211556Srgrimes}
9221556Srgrimes
9231556Srgrimes
9241556Srgrimes
9251556Srgrimes/*
9261556Srgrimes * Returns true if the two strings specify the same varable.  The first
9271556Srgrimes * variable name is terminated by '='; the second may be terminated by
9281556Srgrimes * either '=' or '\0'.
9291556Srgrimes */
9301556Srgrimes
9311556SrgrimesSTATIC int
932200956Sjillesvarequal(const char *p, const char *q)
93390111Simp{
9341556Srgrimes	while (*p == *q++) {
9351556Srgrimes		if (*p++ == '=')
9361556Srgrimes			return 1;
9371556Srgrimes	}
9381556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
9391556Srgrimes		return 1;
9401556Srgrimes	return 0;
9411556Srgrimes}
942