var.c revision 194765
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 194765 2009-06-23 20:45:12Z 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;
761556Srgrimes	char *text;
7790111Simp	void (*func)(const char *);
781556Srgrimes};
791556Srgrimes
801556Srgrimes
8117987Speter#ifndef NO_HISTORY
821556Srgrimesstruct var vhistsize;
8317987Speter#endif
841556Srgrimesstruct var vifs;
851556Srgrimesstruct var vmail;
861556Srgrimesstruct var vmpath;
871556Srgrimesstruct var vpath;
8897689Stjrstruct var vppid;
891556Srgrimesstruct var vps1;
901556Srgrimesstruct var vps2;
91159632Sstefanfstruct var vps4;
921556Srgrimesstruct var vvers;
93117261SddsSTATIC struct var voptind;
941556Srgrimes
95117261SddsSTATIC const struct varinit varinit[] = {
9617987Speter#ifndef NO_HISTORY
9720425Ssteve	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
9820425Ssteve	  sethistsize },
9917987Speter#endif
10020425Ssteve	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
10120425Ssteve	  NULL },
10220425Ssteve	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
10320425Ssteve	  NULL },
10420425Ssteve	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
10520425Ssteve	  NULL },
106114763Sobrien	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=" _PATH_DEFPATH,
10720425Ssteve	  changepath },
10897689Stjr	{ &vppid,	VSTRFIXED|VTEXTFIXED|VUNSET,	"PPID=",
10997689Stjr	  NULL },
1108855Srgrimes	/*
1111556Srgrimes	 * vps1 depends on uid
1121556Srgrimes	 */
11320425Ssteve	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
11420425Ssteve	  NULL },
115159632Sstefanf	{ &vps4,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",
116159632Sstefanf	  NULL },
11720425Ssteve	{ &voptind,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1",
11820425Ssteve	  getoptsreset },
11920425Ssteve	{ NULL,	0,				NULL,
12020425Ssteve	  NULL }
1211556Srgrimes};
1221556Srgrimes
123117261SddsSTATIC struct var *vartab[VTABSIZE];
1241556Srgrimes
12590111SimpSTATIC struct var **hashvar(char *);
12690111SimpSTATIC int varequal(char *, char *);
12790111SimpSTATIC int localevar(char *);
1281556Srgrimes
1291556Srgrimes/*
130155302Sschweikh * Initialize the variable symbol tables and import the environment.
1311556Srgrimes */
1321556Srgrimes
1331556Srgrimes#ifdef mkinit
1341556SrgrimesINCLUDE "var.h"
1351556SrgrimesINIT {
1361556Srgrimes	char **envp;
1371556Srgrimes	extern char **environ;
1381556Srgrimes
1391556Srgrimes	initvar();
1401556Srgrimes	for (envp = environ ; *envp ; envp++) {
1411556Srgrimes		if (strchr(*envp, '=')) {
1421556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1431556Srgrimes		}
1441556Srgrimes	}
1451556Srgrimes}
1461556Srgrimes#endif
1471556Srgrimes
1481556Srgrimes
1491556Srgrimes/*
1501556Srgrimes * This routine initializes the builtin variables.  It is called when the
1511556Srgrimes * shell is initialized and again when a shell procedure is spawned.
1521556Srgrimes */
1531556Srgrimes
1541556Srgrimesvoid
15590111Simpinitvar(void)
15690111Simp{
15797689Stjr	char ppid[20];
1581556Srgrimes	const struct varinit *ip;
1591556Srgrimes	struct var *vp;
1601556Srgrimes	struct var **vpp;
1611556Srgrimes
1621556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1631556Srgrimes		if ((vp->flags & VEXPORT) == 0) {
1641556Srgrimes			vpp = hashvar(ip->text);
1651556Srgrimes			vp->next = *vpp;
1661556Srgrimes			*vpp = vp;
1671556Srgrimes			vp->text = ip->text;
1681556Srgrimes			vp->flags = ip->flags;
16920425Ssteve			vp->func = ip->func;
1701556Srgrimes		}
1711556Srgrimes	}
1721556Srgrimes	/*
1731556Srgrimes	 * PS1 depends on uid
1741556Srgrimes	 */
1751556Srgrimes	if ((vps1.flags & VEXPORT) == 0) {
1761556Srgrimes		vpp = hashvar("PS1=");
1771556Srgrimes		vps1.next = *vpp;
1781556Srgrimes		*vpp = &vps1;
1791556Srgrimes		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
1801556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1811556Srgrimes	}
18297689Stjr	if ((vppid.flags & VEXPORT) == 0) {
18397689Stjr		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
18497689Stjr		setvarsafe("PPID", ppid, 0);
18597689Stjr	}
1861556Srgrimes}
1871556Srgrimes
1881556Srgrimes/*
18920425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
19020425Ssteve */
19120425Ssteve
19220425Ssteveint
19390111Simpsetvarsafe(char *name, char *val, int flags)
19420425Ssteve{
19520425Ssteve	struct jmploc jmploc;
196194765Sjilles	struct jmploc *const savehandler = handler;
19720425Ssteve	int err = 0;
19820425Ssteve
19920425Ssteve	if (setjmp(jmploc.loc))
20020425Ssteve		err = 1;
20120425Ssteve	else {
20220425Ssteve		handler = &jmploc;
20320425Ssteve		setvar(name, val, flags);
20420425Ssteve	}
20520425Ssteve	handler = savehandler;
20620425Ssteve	return err;
20720425Ssteve}
20820425Ssteve
20920425Ssteve/*
210155302Sschweikh * Set the value of a variable.  The flags argument is stored with the
2111556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2121556Srgrimes */
2131556Srgrimes
2141556Srgrimesvoid
21590111Simpsetvar(char *name, char *val, int flags)
21617987Speter{
2171556Srgrimes	char *p, *q;
2181556Srgrimes	int len;
2191556Srgrimes	int namelen;
2201556Srgrimes	char *nameeq;
2211556Srgrimes	int isbad;
2221556Srgrimes
2231556Srgrimes	isbad = 0;
2241556Srgrimes	p = name;
22517525Sache	if (! is_name(*p))
2261556Srgrimes		isbad = 1;
22717525Sache	p++;
2281556Srgrimes	for (;;) {
2291556Srgrimes		if (! is_in_name(*p)) {
2301556Srgrimes			if (*p == '\0' || *p == '=')
2311556Srgrimes				break;
2321556Srgrimes			isbad = 1;
2331556Srgrimes		}
2341556Srgrimes		p++;
2351556Srgrimes	}
2361556Srgrimes	namelen = p - name;
2371556Srgrimes	if (isbad)
2381556Srgrimes		error("%.*s: bad variable name", namelen, name);
2391556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2401556Srgrimes	if (val == NULL) {
2411556Srgrimes		flags |= VUNSET;
2421556Srgrimes	} else {
2431556Srgrimes		len += strlen(val);
2441556Srgrimes	}
2451556Srgrimes	p = nameeq = ckmalloc(len);
2461556Srgrimes	q = name;
2471556Srgrimes	while (--namelen >= 0)
2481556Srgrimes		*p++ = *q++;
2491556Srgrimes	*p++ = '=';
2501556Srgrimes	*p = '\0';
2511556Srgrimes	if (val)
2521556Srgrimes		scopy(val, p);
2531556Srgrimes	setvareq(nameeq, flags);
2541556Srgrimes}
2551556Srgrimes
25617525SacheSTATIC int
25790111Simplocalevar(char *s)
25890111Simp{
25917525Sache	static char *lnames[7] = {
26017525Sache		"ALL", "COLLATE", "CTYPE", "MONETARY",
26117525Sache		"NUMERIC", "TIME", NULL
26217525Sache	};
26317525Sache	char **ss;
2641556Srgrimes
26517525Sache	if (*s != 'L')
26617525Sache		return 0;
26717525Sache	if (varequal(s + 1, "ANG"))
26817525Sache		return 1;
26917525Sache	if (strncmp(s + 1, "C_", 2) != 0)
27017525Sache		return 0;
27117525Sache	for (ss = lnames; *ss ; ss++)
27217525Sache		if (varequal(s + 3, *ss))
27317525Sache			return 1;
27417525Sache	return 0;
27517525Sache}
2761556Srgrimes
277171268Sscf
2781556Srgrimes/*
279171268Sscf * Sets/unsets an environment variable from a pointer that may actually be a
280171268Sscf * pointer into environ where the string should not be manipulated.
281171268Sscf */
282171268Sscfstatic void
283171268Sscfchange_env(char *s, int set)
284171268Sscf{
285171268Sscf	char *eqp;
286171268Sscf	char *ss;
287171268Sscf
288171268Sscf	ss = savestr(s);
289171268Sscf	if ((eqp = strchr(ss, '=')) != NULL)
290171268Sscf		*eqp = '\0';
291171268Sscf	if (set && eqp != NULL)
292171268Sscf		(void) setenv(ss, eqp + 1, 1);
293171268Sscf	else
294171268Sscf		(void) unsetenv(ss);
295171268Sscf	ckfree(ss);
296171268Sscf
297171268Sscf	return;
298171268Sscf}
299171268Sscf
300171268Sscf
301171268Sscf/*
3021556Srgrimes * Same as setvar except that the variable and value are passed in
3031556Srgrimes * the first argument as name=value.  Since the first argument will
3041556Srgrimes * be actually stored in the table, it should not be a string that
3051556Srgrimes * will go away.
3061556Srgrimes */
3071556Srgrimes
3081556Srgrimesvoid
30990111Simpsetvareq(char *s, int flags)
31017987Speter{
3111556Srgrimes	struct var *vp, **vpp;
31290112Simp	int len;
3131556Srgrimes
31445263Scracauer	if (aflag)
31545263Scracauer		flags |= VEXPORT;
3161556Srgrimes	vpp = hashvar(s);
3171556Srgrimes	for (vp = *vpp ; vp ; vp = vp->next) {
3181556Srgrimes		if (varequal(s, vp->text)) {
3191556Srgrimes			if (vp->flags & VREADONLY) {
32090112Simp				len = strchr(s, '=') - s;
3211556Srgrimes				error("%.*s: is read only", len, s);
3221556Srgrimes			}
3231556Srgrimes			INTOFF;
32420425Ssteve
32520425Ssteve			if (vp->func && (flags & VNOFUNC) == 0)
32620425Ssteve				(*vp->func)(strchr(s, '=') + 1);
32720425Ssteve
3281556Srgrimes			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
3291556Srgrimes				ckfree(vp->text);
33020425Ssteve
33120425Ssteve			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
3321556Srgrimes			vp->flags |= flags;
3331556Srgrimes			vp->text = s;
33420425Ssteve
33520425Ssteve			/*
33620425Ssteve			 * We could roll this to a function, to handle it as
33720425Ssteve			 * a regular variable function callback, but why bother?
33820425Ssteve			 */
3391556Srgrimes			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
3401556Srgrimes				chkmail(1);
34117525Sache			if ((vp->flags & VEXPORT) && localevar(s)) {
342171268Sscf				change_env(s, 1);
34317525Sache				(void) setlocale(LC_ALL, "");
34417525Sache			}
3451556Srgrimes			INTON;
3461556Srgrimes			return;
3471556Srgrimes		}
3481556Srgrimes	}
3491556Srgrimes	/* not found */
3501556Srgrimes	vp = ckmalloc(sizeof (*vp));
3511556Srgrimes	vp->flags = flags;
3521556Srgrimes	vp->text = s;
3531556Srgrimes	vp->next = *vpp;
35420425Ssteve	vp->func = NULL;
35517525Sache	INTOFF;
3561556Srgrimes	*vpp = vp;
35717525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
358171268Sscf		change_env(s, 1);
35917525Sache		(void) setlocale(LC_ALL, "");
36017525Sache	}
36117525Sache	INTON;
3621556Srgrimes}
3631556Srgrimes
3641556Srgrimes
3651556Srgrimes
3661556Srgrimes/*
3671556Srgrimes * Process a linked list of variable assignments.
3681556Srgrimes */
3691556Srgrimes
3701556Srgrimesvoid
37190111Simplistsetvar(struct strlist *list)
37290111Simp{
3731556Srgrimes	struct strlist *lp;
3741556Srgrimes
3751556Srgrimes	INTOFF;
3761556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
3771556Srgrimes		setvareq(savestr(lp->text), 0);
3781556Srgrimes	}
3791556Srgrimes	INTON;
3801556Srgrimes}
3811556Srgrimes
3821556Srgrimes
3831556Srgrimes
3841556Srgrimes/*
3851556Srgrimes * Find the value of a variable.  Returns NULL if not set.
3861556Srgrimes */
3871556Srgrimes
3881556Srgrimeschar *
38990111Simplookupvar(char *name)
39090111Simp{
3911556Srgrimes	struct var *v;
3921556Srgrimes
3931556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
3941556Srgrimes		if (varequal(v->text, name)) {
3951556Srgrimes			if (v->flags & VUNSET)
3961556Srgrimes				return NULL;
3971556Srgrimes			return strchr(v->text, '=') + 1;
3981556Srgrimes		}
3991556Srgrimes	}
4001556Srgrimes	return NULL;
4011556Srgrimes}
4021556Srgrimes
4031556Srgrimes
4041556Srgrimes
4051556Srgrimes/*
4061556Srgrimes * Search the environment of a builtin command.  If the second argument
4071556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
4081556Srgrimes * exported.
4091556Srgrimes */
4101556Srgrimes
4111556Srgrimeschar *
41290111Simpbltinlookup(char *name, int doall)
41317987Speter{
4141556Srgrimes	struct strlist *sp;
4151556Srgrimes	struct var *v;
4161556Srgrimes
4171556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
4181556Srgrimes		if (varequal(sp->text, name))
4191556Srgrimes			return strchr(sp->text, '=') + 1;
4201556Srgrimes	}
4211556Srgrimes	for (v = *hashvar(name) ; v ; v = v->next) {
4221556Srgrimes		if (varequal(v->text, name)) {
42317987Speter			if ((v->flags & VUNSET)
42417987Speter			 || (!doall && (v->flags & VEXPORT) == 0))
4251556Srgrimes				return NULL;
4261556Srgrimes			return strchr(v->text, '=') + 1;
4271556Srgrimes		}
4281556Srgrimes	}
4291556Srgrimes	return NULL;
4301556Srgrimes}
4311556Srgrimes
4321556Srgrimes
4331556Srgrimes
4341556Srgrimes/*
4351556Srgrimes * Generate a list of exported variables.  This routine is used to construct
4361556Srgrimes * the third argument to execve when executing a program.
4371556Srgrimes */
4381556Srgrimes
4391556Srgrimeschar **
44090111Simpenvironment(void)
44190111Simp{
4421556Srgrimes	int nenv;
4431556Srgrimes	struct var **vpp;
4441556Srgrimes	struct var *vp;
4451556Srgrimes	char **env, **ep;
4461556Srgrimes
4471556Srgrimes	nenv = 0;
4481556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4491556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4501556Srgrimes			if (vp->flags & VEXPORT)
4511556Srgrimes				nenv++;
4521556Srgrimes	}
4531556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
4541556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4551556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
4561556Srgrimes			if (vp->flags & VEXPORT)
4571556Srgrimes				*ep++ = vp->text;
4581556Srgrimes	}
4591556Srgrimes	*ep = NULL;
4601556Srgrimes	return env;
4611556Srgrimes}
4621556Srgrimes
4631556Srgrimes
4641556Srgrimes/*
4651556Srgrimes * Called when a shell procedure is invoked to clear out nonexported
4661556Srgrimes * variables.  It is also necessary to reallocate variables of with
4671556Srgrimes * VSTACK set since these are currently allocated on the stack.
4681556Srgrimes */
4691556Srgrimes
4701556Srgrimes#ifdef mkinit
471149016SstefanfMKINIT void shprocvar(void);
4721556Srgrimes
4731556SrgrimesSHELLPROC {
4741556Srgrimes	shprocvar();
4751556Srgrimes}
4761556Srgrimes#endif
4771556Srgrimes
4781556Srgrimesvoid
47990111Simpshprocvar(void)
48090111Simp{
4811556Srgrimes	struct var **vpp;
4821556Srgrimes	struct var *vp, **prev;
4831556Srgrimes
4841556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
4851556Srgrimes		for (prev = vpp ; (vp = *prev) != NULL ; ) {
4861556Srgrimes			if ((vp->flags & VEXPORT) == 0) {
4871556Srgrimes				*prev = vp->next;
4881556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
4891556Srgrimes					ckfree(vp->text);
4901556Srgrimes				if ((vp->flags & VSTRFIXED) == 0)
4911556Srgrimes					ckfree(vp);
4921556Srgrimes			} else {
4931556Srgrimes				if (vp->flags & VSTACK) {
4941556Srgrimes					vp->text = savestr(vp->text);
4951556Srgrimes					vp->flags &=~ VSTACK;
4961556Srgrimes				}
4971556Srgrimes				prev = &vp->next;
4981556Srgrimes			}
4991556Srgrimes		}
5001556Srgrimes	}
5011556Srgrimes	initvar();
5021556Srgrimes}
5031556Srgrimes
5041556Srgrimes
505158145Sstefanfstatic int
506158145Sstefanfvar_compare(const void *a, const void *b)
507158145Sstefanf{
508158145Sstefanf	const char *const *sa, *const *sb;
5091556Srgrimes
510158145Sstefanf	sa = a;
511158145Sstefanf	sb = b;
512158145Sstefanf	/*
513158145Sstefanf	 * This compares two var=value strings which creates a different
514158145Sstefanf	 * order from what you would probably expect.  POSIX is somewhat
515158145Sstefanf	 * ambiguous on what should be sorted exactly.
516158145Sstefanf	 */
517158145Sstefanf	return strcoll(*sa, *sb);
518158145Sstefanf}
519158145Sstefanf
520158145Sstefanf
5211556Srgrimes/*
5221556Srgrimes * Command to list all variables which are set.  Currently this command
5231556Srgrimes * is invoked from the set command when the set command is called without
5241556Srgrimes * any variables.
5251556Srgrimes */
5261556Srgrimes
5271556Srgrimesint
52890111Simpshowvarscmd(int argc __unused, char **argv __unused)
52917987Speter{
5301556Srgrimes	struct var **vpp;
5311556Srgrimes	struct var *vp;
53297915Stjr	const char *s;
533158145Sstefanf	const char **vars;
534158145Sstefanf	int i, n;
5351556Srgrimes
536158145Sstefanf	/*
537158145Sstefanf	 * POSIX requires us to sort the variables.
538158145Sstefanf	 */
539158145Sstefanf	n = 0;
540158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
541158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
542158145Sstefanf			if (!(vp->flags & VUNSET))
543158145Sstefanf				n++;
5441556Srgrimes		}
5451556Srgrimes	}
546158145Sstefanf
547158145Sstefanf	INTON;
548158145Sstefanf	vars = ckmalloc(n * sizeof(*vars));
549158145Sstefanf	i = 0;
550158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
551158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
552158145Sstefanf			if (!(vp->flags & VUNSET))
553158145Sstefanf				vars[i++] = vp->text;
554158145Sstefanf		}
555158145Sstefanf	}
556158145Sstefanf
557158145Sstefanf	qsort(vars, n, sizeof(*vars), var_compare);
558158145Sstefanf	for (i = 0; i < n; i++) {
559158145Sstefanf		for (s = vars[i]; *s != '='; s++)
560158145Sstefanf			out1c(*s);
561158145Sstefanf		out1c('=');
562158145Sstefanf		out1qstr(s + 1);
563158145Sstefanf		out1c('\n');
564158145Sstefanf	}
565158145Sstefanf	ckfree(vars);
566158145Sstefanf	INTOFF;
567158145Sstefanf
5681556Srgrimes	return 0;
5691556Srgrimes}
5701556Srgrimes
5711556Srgrimes
5721556Srgrimes
5731556Srgrimes/*
5741556Srgrimes * The export and readonly commands.
5751556Srgrimes */
5761556Srgrimes
5771556Srgrimesint
57890111Simpexportcmd(int argc, char **argv)
57917987Speter{
5801556Srgrimes	struct var **vpp;
5811556Srgrimes	struct var *vp;
5821556Srgrimes	char *name;
5831556Srgrimes	char *p;
58497914Stjr	char *cmdname;
58597914Stjr	int ch, values;
5861556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
5871556Srgrimes
58897914Stjr	cmdname = argv[0];
58997914Stjr	optreset = optind = 1;
590100663Stjr	opterr = 0;
59197914Stjr	values = 0;
59297914Stjr	while ((ch = getopt(argc, argv, "p")) != -1) {
59397914Stjr		switch (ch) {
59497914Stjr		case 'p':
59597914Stjr			values = 1;
59697914Stjr			break;
59797914Stjr		case '?':
59897914Stjr		default:
59997914Stjr			error("unknown option: -%c", optopt);
60097914Stjr		}
60197914Stjr	}
60297914Stjr	argc -= optind;
60397914Stjr	argv += optind;
60497914Stjr
605149919Sstefanf	if (values && argc != 0)
606149919Sstefanf		error("-p requires no arguments");
6071556Srgrimes	listsetvar(cmdenviron);
60897914Stjr	if (argc != 0) {
609149919Sstefanf		while ((name = *argv++) != NULL) {
6101556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
6111556Srgrimes				p++;
6121556Srgrimes			} else {
6131556Srgrimes				vpp = hashvar(name);
6141556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
6151556Srgrimes					if (varequal(vp->text, name)) {
61690111Simp
6171556Srgrimes						vp->flags |= flag;
61817525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
619171268Sscf							change_env(vp->text, 1);
62017525Sache							(void) setlocale(LC_ALL, "");
62117525Sache						}
6221556Srgrimes						goto found;
6231556Srgrimes					}
6241556Srgrimes				}
6251556Srgrimes			}
6261556Srgrimes			setvar(name, p, flag);
6271556Srgrimesfound:;
6281556Srgrimes		}
6291556Srgrimes	} else {
6301556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
6311556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
6321556Srgrimes				if (vp->flags & flag) {
63397914Stjr					if (values) {
63497914Stjr						out1str(cmdname);
63597914Stjr						out1c(' ');
63697914Stjr					}
6371556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
6381556Srgrimes						out1c(*p);
63997914Stjr					if (values && !(vp->flags & VUNSET)) {
64097914Stjr						out1c('=');
64197914Stjr						out1qstr(p + 1);
64297914Stjr					}
6431556Srgrimes					out1c('\n');
6441556Srgrimes				}
6451556Srgrimes			}
6461556Srgrimes		}
6471556Srgrimes	}
6481556Srgrimes	return 0;
6491556Srgrimes}
6501556Srgrimes
6511556Srgrimes
6521556Srgrimes/*
6531556Srgrimes * The "local" command.
6541556Srgrimes */
6551556Srgrimes
65617987Speterint
65790111Simplocalcmd(int argc __unused, char **argv __unused)
65817987Speter{
6591556Srgrimes	char *name;
6601556Srgrimes
6611556Srgrimes	if (! in_function())
6621556Srgrimes		error("Not in a function");
6631556Srgrimes	while ((name = *argptr++) != NULL) {
6641556Srgrimes		mklocal(name);
6651556Srgrimes	}
6661556Srgrimes	return 0;
6671556Srgrimes}
6681556Srgrimes
6691556Srgrimes
6701556Srgrimes/*
6711556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
6721556Srgrimes * value and flags are saved in a localvar structure.  The saved values
6731556Srgrimes * will be restored when the shell function returns.  We handle the name
6741556Srgrimes * "-" as a special case.
6751556Srgrimes */
6761556Srgrimes
6771556Srgrimesvoid
67890111Simpmklocal(char *name)
67990111Simp{
6801556Srgrimes	struct localvar *lvp;
6811556Srgrimes	struct var **vpp;
6821556Srgrimes	struct var *vp;
6831556Srgrimes
6841556Srgrimes	INTOFF;
6851556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
6861556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
6871556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
68817987Speter		memcpy(lvp->text, optlist, sizeof optlist);
6891556Srgrimes		vp = NULL;
6901556Srgrimes	} else {
6911556Srgrimes		vpp = hashvar(name);
6921556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
6931556Srgrimes		if (vp == NULL) {
6941556Srgrimes			if (strchr(name, '='))
6951556Srgrimes				setvareq(savestr(name), VSTRFIXED);
6961556Srgrimes			else
6971556Srgrimes				setvar(name, NULL, VSTRFIXED);
6981556Srgrimes			vp = *vpp;	/* the new variable */
6991556Srgrimes			lvp->text = NULL;
7001556Srgrimes			lvp->flags = VUNSET;
7011556Srgrimes		} else {
7021556Srgrimes			lvp->text = vp->text;
7031556Srgrimes			lvp->flags = vp->flags;
7041556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
7051556Srgrimes			if (strchr(name, '='))
7061556Srgrimes				setvareq(savestr(name), 0);
7071556Srgrimes		}
7081556Srgrimes	}
7091556Srgrimes	lvp->vp = vp;
7101556Srgrimes	lvp->next = localvars;
7111556Srgrimes	localvars = lvp;
7121556Srgrimes	INTON;
7131556Srgrimes}
7141556Srgrimes
7151556Srgrimes
7161556Srgrimes/*
7171556Srgrimes * Called after a function returns.
7181556Srgrimes */
7191556Srgrimes
7201556Srgrimesvoid
72190111Simppoplocalvars(void)
72290111Simp{
7231556Srgrimes	struct localvar *lvp;
7241556Srgrimes	struct var *vp;
7251556Srgrimes
7261556Srgrimes	while ((lvp = localvars) != NULL) {
7271556Srgrimes		localvars = lvp->next;
7281556Srgrimes		vp = lvp->vp;
7291556Srgrimes		if (vp == NULL) {	/* $- saved */
73017987Speter			memcpy(optlist, lvp->text, sizeof optlist);
7311556Srgrimes			ckfree(lvp->text);
7321556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
7331556Srgrimes			(void)unsetvar(vp->text);
7341556Srgrimes		} else {
7351556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
7361556Srgrimes				ckfree(vp->text);
7371556Srgrimes			vp->flags = lvp->flags;
7381556Srgrimes			vp->text = lvp->text;
7391556Srgrimes		}
7401556Srgrimes		ckfree(lvp);
7411556Srgrimes	}
7421556Srgrimes}
7431556Srgrimes
7441556Srgrimes
74517987Speterint
74690111Simpsetvarcmd(int argc, char **argv)
74717987Speter{
7481556Srgrimes	if (argc <= 2)
7491556Srgrimes		return unsetcmd(argc, argv);
7501556Srgrimes	else if (argc == 3)
7511556Srgrimes		setvar(argv[1], argv[2], 0);
7521556Srgrimes	else
7531556Srgrimes		error("List assignment not implemented");
7541556Srgrimes	return 0;
7551556Srgrimes}
7561556Srgrimes
7571556Srgrimes
7581556Srgrimes/*
7591556Srgrimes * The unset builtin command.  We unset the function before we unset the
7601556Srgrimes * variable to allow a function to be unset when there is a readonly variable
7611556Srgrimes * with the same name.
7621556Srgrimes */
7631556Srgrimes
76417987Speterint
76590111Simpunsetcmd(int argc __unused, char **argv __unused)
76617987Speter{
7671556Srgrimes	char **ap;
7681556Srgrimes	int i;
7691556Srgrimes	int flg_func = 0;
7701556Srgrimes	int flg_var = 0;
7711556Srgrimes	int ret = 0;
7721556Srgrimes
7731556Srgrimes	while ((i = nextopt("vf")) != '\0') {
7741556Srgrimes		if (i == 'f')
7751556Srgrimes			flg_func = 1;
7761556Srgrimes		else
7771556Srgrimes			flg_var = 1;
7781556Srgrimes	}
7791556Srgrimes	if (flg_func == 0 && flg_var == 0)
7801556Srgrimes		flg_var = 1;
7818855Srgrimes
7821556Srgrimes	for (ap = argptr; *ap ; ap++) {
7831556Srgrimes		if (flg_func)
7841556Srgrimes			ret |= unsetfunc(*ap);
7851556Srgrimes		if (flg_var)
7861556Srgrimes			ret |= unsetvar(*ap);
7871556Srgrimes	}
7881556Srgrimes	return ret;
7891556Srgrimes}
7901556Srgrimes
7911556Srgrimes
7921556Srgrimes/*
7931556Srgrimes * Unset the specified variable.
7941556Srgrimes */
7951556Srgrimes
79620425Ssteveint
79790111Simpunsetvar(char *s)
79890111Simp{
7991556Srgrimes	struct var **vpp;
8001556Srgrimes	struct var *vp;
8011556Srgrimes
8021556Srgrimes	vpp = hashvar(s);
8031556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
8041556Srgrimes		if (varequal(vp->text, s)) {
8051556Srgrimes			if (vp->flags & VREADONLY)
8061556Srgrimes				return (1);
8071556Srgrimes			INTOFF;
8081556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
8091556Srgrimes				setvar(s, nullstr, 0);
81017525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
811171268Sscf				change_env(s, 0);
81217525Sache				setlocale(LC_ALL, "");
81317525Sache			}
81420425Ssteve			vp->flags &= ~VEXPORT;
8151556Srgrimes			vp->flags |= VUNSET;
8161556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
8171556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
8181556Srgrimes					ckfree(vp->text);
8191556Srgrimes				*vpp = vp->next;
8201556Srgrimes				ckfree(vp);
8211556Srgrimes			}
8221556Srgrimes			INTON;
8231556Srgrimes			return (0);
8241556Srgrimes		}
8251556Srgrimes	}
8261556Srgrimes
827135856Sdes	return (0);
8281556Srgrimes}
8291556Srgrimes
8301556Srgrimes
8311556Srgrimes
8321556Srgrimes/*
8331556Srgrimes * Find the appropriate entry in the hash table from the name.
8341556Srgrimes */
8351556Srgrimes
8361556SrgrimesSTATIC struct var **
83790111Simphashvar(char *p)
83890111Simp{
8391556Srgrimes	unsigned int hashval;
8401556Srgrimes
84120425Ssteve	hashval = ((unsigned char) *p) << 4;
8421556Srgrimes	while (*p && *p != '=')
84320425Ssteve		hashval += (unsigned char) *p++;
8441556Srgrimes	return &vartab[hashval % VTABSIZE];
8451556Srgrimes}
8461556Srgrimes
8471556Srgrimes
8481556Srgrimes
8491556Srgrimes/*
8501556Srgrimes * Returns true if the two strings specify the same varable.  The first
8511556Srgrimes * variable name is terminated by '='; the second may be terminated by
8521556Srgrimes * either '=' or '\0'.
8531556Srgrimes */
8541556Srgrimes
8551556SrgrimesSTATIC int
85690111Simpvarequal(char *p, char *q)
85790111Simp{
8581556Srgrimes	while (*p == *q++) {
8591556Srgrimes		if (*p++ == '=')
8601556Srgrimes			return 1;
8611556Srgrimes	}
8621556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
8631556Srgrimes		return 1;
8641556Srgrimes	return 0;
8651556Srgrimes}
866