var.c revision 171268
1139749Simp/*-
237785Smsmith * Copyright (c) 1991, 1993
337785Smsmith *	The Regents of the University of California.  All rights reserved.
437785Smsmith *
537785Smsmith * This code is derived from software contributed to Berkeley by
637785Smsmith * Kenneth Almquist.
737785Smsmith *
837785Smsmith * Redistribution and use in source and binary forms, with or without
937785Smsmith * modification, are permitted provided that the following conditions
1037785Smsmith * are met:
1137785Smsmith * 1. Redistributions of source code must retain the above copyright
1237785Smsmith *    notice, this list of conditions and the following disclaimer.
1337785Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1437785Smsmith *    notice, this list of conditions and the following disclaimer in the
1537785Smsmith *    documentation and/or other materials provided with the distribution.
1637785Smsmith * 4. Neither the name of the University nor the names of its contributors
1737785Smsmith *    may be used to endorse or promote products derived from this software
1837785Smsmith *    without specific prior written permission.
1937785Smsmith *
2037785Smsmith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2137785Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2237785Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2337785Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2437785Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2537785Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2637785Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2737785Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2837785Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2937785Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3050477Speter * SUCH DAMAGE.
3137785Smsmith */
3237785Smsmith
3358816Simp#ifndef lint
3458816Simp#if 0
3537785Smsmithstatic char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
3637785Smsmith#endif
3737785Smsmith#endif /* not lint */
3837785Smsmith#include <sys/cdefs.h>
3937785Smsmith__FBSDID("$FreeBSD: head/bin/sh/var.c 171268 2007-07-06 04:04:58Z scf $");
4037785Smsmith
4137785Smsmith#include <unistd.h>
4237785Smsmith#include <stdlib.h>
4337785Smsmith#include <paths.h>
4437785Smsmith
4537785Smsmith/*
4637785Smsmith * Shell variables.
4737785Smsmith */
4837785Smsmith
4937785Smsmith#include <locale.h>
5037785Smsmith
5137785Smsmith#include "shell.h"
5237785Smsmith#include "output.h"
5337785Smsmith#include "expand.h"
5437785Smsmith#include "nodes.h"	/* for other headers */
5537785Smsmith#include "eval.h"	/* defines cmdenviron */
5637785Smsmith#include "exec.h"
5737785Smsmith#include "syntax.h"
5837785Smsmith#include "options.h"
5937785Smsmith#include "mail.h"
6037785Smsmith#include "var.h"
6137785Smsmith#include "memalloc.h"
6237785Smsmith#include "error.h"
6337785Smsmith#include "mystring.h"
6437785Smsmith#include "parser.h"
6537785Smsmith#ifndef NO_HISTORY
6637785Smsmith#include "myhistedit.h"
6737785Smsmith#endif
6837785Smsmith
6937785Smsmith
7037785Smsmith#define VTABSIZE 39
7137785Smsmith
7237785Smsmith
7337785Smsmithstruct varinit {
7437785Smsmith	struct var *var;
7537785Smsmith	int flags;
7637785Smsmith	char *text;
7737785Smsmith	void (*func)(const char *);
7837785Smsmith};
7937785Smsmith
8037785Smsmith
8137785Smsmith#ifndef NO_HISTORY
8237785Smsmithstruct var vhistsize;
8337785Smsmith#endif
8437785Smsmithstruct var vifs;
8537785Smsmithstruct var vmail;
8637785Smsmithstruct var vmpath;
8737785Smsmithstruct var vpath;
8837785Smsmithstruct var vppid;
8937785Smsmithstruct var vps1;
9037785Smsmithstruct var vps2;
9137785Smsmithstruct var vps4;
9237785Smsmithstruct var vvers;
9337785SmsmithSTATIC struct var voptind;
9437785Smsmith
9537785SmsmithSTATIC const struct varinit varinit[] = {
9637785Smsmith#ifndef NO_HISTORY
9737785Smsmith	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
9837785Smsmith	  sethistsize },
9937785Smsmith#endif
10037785Smsmith	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
10137785Smsmith	  NULL },
10237785Smsmith	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
10337785Smsmith	  NULL },
10437785Smsmith	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
10537785Smsmith	  NULL },
10637785Smsmith	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=" _PATH_DEFPATH,
10737785Smsmith	  changepath },
10837785Smsmith	{ &vppid,	VSTRFIXED|VTEXTFIXED|VUNSET,	"PPID=",
10937785Smsmith	  NULL },
11037785Smsmith	/*
11137785Smsmith	 * vps1 depends on uid
11237785Smsmith	 */
11337785Smsmith	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
11437785Smsmith	  NULL },
11537785Smsmith	{ &vps4,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",
11637785Smsmith	  NULL },
11737785Smsmith	{ &voptind,	VSTRFIXED|VTEXTFIXED,		"OPTIND=1",
11837785Smsmith	  getoptsreset },
11937785Smsmith	{ NULL,	0,				NULL,
12037785Smsmith	  NULL }
12137785Smsmith};
12237785Smsmith
12337785SmsmithSTATIC struct var *vartab[VTABSIZE];
12437785Smsmith
12537785SmsmithSTATIC struct var **hashvar(char *);
12637785SmsmithSTATIC int varequal(char *, char *);
12737785SmsmithSTATIC int localevar(char *);
12837785Smsmith
12937785Smsmith/*
13037785Smsmith * Initialize the variable symbol tables and import the environment.
13137785Smsmith */
13237785Smsmith
13337785Smsmith#ifdef mkinit
13437785SmsmithINCLUDE "var.h"
13537785SmsmithINIT {
13637785Smsmith	char **envp;
13737785Smsmith	extern char **environ;
13837785Smsmith
13937785Smsmith	initvar();
14037785Smsmith	for (envp = environ ; *envp ; envp++) {
14137785Smsmith		if (strchr(*envp, '=')) {
14237785Smsmith			setvareq(*envp, VEXPORT|VTEXTFIXED);
14337785Smsmith		}
14437785Smsmith	}
14537785Smsmith}
14637785Smsmith#endif
14737785Smsmith
14837785Smsmith
14937785Smsmith/*
15037785Smsmith * This routine initializes the builtin variables.  It is called when the
15137785Smsmith * shell is initialized and again when a shell procedure is spawned.
15237785Smsmith */
15337785Smsmith
15437785Smsmithvoid
15537785Smsmithinitvar(void)
15637785Smsmith{
15737785Smsmith	char ppid[20];
15837785Smsmith	const struct varinit *ip;
15937785Smsmith	struct var *vp;
16037785Smsmith	struct var **vpp;
16137785Smsmith
16237785Smsmith	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
16337785Smsmith		if ((vp->flags & VEXPORT) == 0) {
16437785Smsmith			vpp = hashvar(ip->text);
16537785Smsmith			vp->next = *vpp;
16637785Smsmith			*vpp = vp;
16737785Smsmith			vp->text = ip->text;
16837785Smsmith			vp->flags = ip->flags;
16937785Smsmith			vp->func = ip->func;
17037785Smsmith		}
17137785Smsmith	}
17237785Smsmith	/*
17337785Smsmith	 * PS1 depends on uid
17437785Smsmith	 */
17537785Smsmith	if ((vps1.flags & VEXPORT) == 0) {
17637785Smsmith		vpp = hashvar("PS1=");
17737785Smsmith		vps1.next = *vpp;
17837785Smsmith		*vpp = &vps1;
17937785Smsmith		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
18037785Smsmith		vps1.flags = VSTRFIXED|VTEXTFIXED;
18137785Smsmith	}
18237785Smsmith	if ((vppid.flags & VEXPORT) == 0) {
18337785Smsmith		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
18437785Smsmith		setvarsafe("PPID", ppid, 0);
18537785Smsmith	}
18637785Smsmith}
18737785Smsmith
18837785Smsmith/*
18937785Smsmith * Safe version of setvar, returns 1 on success 0 on failure.
19037785Smsmith */
19137785Smsmith
19237785Smsmithint
19337785Smsmithsetvarsafe(char *name, char *val, int flags)
19437785Smsmith{
19537785Smsmith	struct jmploc jmploc;
19637785Smsmith	struct jmploc *volatile savehandler = handler;
19737785Smsmith	int err = 0;
19837785Smsmith#if __GNUC__
19937785Smsmith	/* Avoid longjmp clobbering */
20037785Smsmith	(void) &err;
20137785Smsmith#endif
20237785Smsmith
20337785Smsmith	if (setjmp(jmploc.loc))
20437785Smsmith		err = 1;
20537785Smsmith	else {
20637785Smsmith		handler = &jmploc;
20737785Smsmith		setvar(name, val, flags);
20837785Smsmith	}
20937785Smsmith	handler = savehandler;
21037785Smsmith	return err;
21137785Smsmith}
21237785Smsmith
21337785Smsmith/*
21437785Smsmith * Set the value of a variable.  The flags argument is stored with the
21537785Smsmith * flags of the variable.  If val is NULL, the variable is unset.
21637785Smsmith */
21737785Smsmith
21837785Smsmithvoid
21937785Smsmithsetvar(char *name, char *val, int flags)
22037785Smsmith{
22137785Smsmith	char *p, *q;
22237785Smsmith	int len;
22337785Smsmith	int namelen;
22437785Smsmith	char *nameeq;
22537785Smsmith	int isbad;
22637785Smsmith
22737785Smsmith	isbad = 0;
22837785Smsmith	p = name;
22937785Smsmith	if (! is_name(*p))
23037785Smsmith		isbad = 1;
23137785Smsmith	p++;
23237785Smsmith	for (;;) {
23337785Smsmith		if (! is_in_name(*p)) {
23437785Smsmith			if (*p == '\0' || *p == '=')
23537785Smsmith				break;
23637785Smsmith			isbad = 1;
23737785Smsmith		}
23837785Smsmith		p++;
23937785Smsmith	}
24037785Smsmith	namelen = p - name;
24137785Smsmith	if (isbad)
24237785Smsmith		error("%.*s: bad variable name", namelen, name);
24337785Smsmith	len = namelen + 2;		/* 2 is space for '=' and '\0' */
24437785Smsmith	if (val == NULL) {
24537785Smsmith		flags |= VUNSET;
24637785Smsmith	} else {
24737785Smsmith		len += strlen(val);
24837785Smsmith	}
24937785Smsmith	p = nameeq = ckmalloc(len);
25037785Smsmith	q = name;
25137785Smsmith	while (--namelen >= 0)
25237785Smsmith		*p++ = *q++;
25337785Smsmith	*p++ = '=';
25437785Smsmith	*p = '\0';
25537785Smsmith	if (val)
25637785Smsmith		scopy(val, p);
25737785Smsmith	setvareq(nameeq, flags);
25837785Smsmith}
25937785Smsmith
26037785SmsmithSTATIC int
26137785Smsmithlocalevar(char *s)
26237785Smsmith{
26337785Smsmith	static char *lnames[7] = {
26437785Smsmith		"ALL", "COLLATE", "CTYPE", "MONETARY",
26537785Smsmith		"NUMERIC", "TIME", NULL
26637785Smsmith	};
26737785Smsmith	char **ss;
26837785Smsmith
26937785Smsmith	if (*s != 'L')
27037785Smsmith		return 0;
27137785Smsmith	if (varequal(s + 1, "ANG"))
27237785Smsmith		return 1;
27337785Smsmith	if (strncmp(s + 1, "C_", 2) != 0)
27437785Smsmith		return 0;
27537785Smsmith	for (ss = lnames; *ss ; ss++)
27637785Smsmith		if (varequal(s + 3, *ss))
27737785Smsmith			return 1;
27837785Smsmith	return 0;
27937785Smsmith}
28037785Smsmith
28137785Smsmith
28237785Smsmith/*
28337785Smsmith * Sets/unsets an environment variable from a pointer that may actually be a
28437785Smsmith * pointer into environ where the string should not be manipulated.
28537785Smsmith */
28637785Smsmithstatic void
28737785Smsmithchange_env(char *s, int set)
28837785Smsmith{
28937785Smsmith	char *eqp;
29037785Smsmith	char *ss;
29137785Smsmith
29237785Smsmith	ss = savestr(s);
29337785Smsmith	if ((eqp = strchr(ss, '=')) != NULL)
29437785Smsmith		*eqp = '\0';
29537785Smsmith	if (set && eqp != NULL)
29637785Smsmith		(void) setenv(ss, eqp + 1, 1);
29737785Smsmith	else
29837785Smsmith		(void) unsetenv(ss);
29937785Smsmith	ckfree(ss);
30037785Smsmith
30137785Smsmith	return;
30237785Smsmith}
30337785Smsmith
30437785Smsmith
30537785Smsmith/*
30637785Smsmith * Same as setvar except that the variable and value are passed in
30737785Smsmith * the first argument as name=value.  Since the first argument will
30837785Smsmith * be actually stored in the table, it should not be a string that
30937785Smsmith * will go away.
31037785Smsmith */
31137785Smsmith
31237785Smsmithvoid
31337785Smsmithsetvareq(char *s, int flags)
31437785Smsmith{
31537785Smsmith	struct var *vp, **vpp;
31637785Smsmith	int len;
31737785Smsmith
31837785Smsmith	if (aflag)
31937785Smsmith		flags |= VEXPORT;
32037785Smsmith	vpp = hashvar(s);
32137785Smsmith	for (vp = *vpp ; vp ; vp = vp->next) {
32237785Smsmith		if (varequal(s, vp->text)) {
32337785Smsmith			if (vp->flags & VREADONLY) {
32437785Smsmith				len = strchr(s, '=') - s;
32537785Smsmith				error("%.*s: is read only", len, s);
32637785Smsmith			}
32737785Smsmith			INTOFF;
32837785Smsmith
32937785Smsmith			if (vp->func && (flags & VNOFUNC) == 0)
33037785Smsmith				(*vp->func)(strchr(s, '=') + 1);
33137785Smsmith
33237785Smsmith			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
33337785Smsmith				ckfree(vp->text);
33437785Smsmith
33537785Smsmith			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
33637785Smsmith			vp->flags |= flags;
33737785Smsmith			vp->text = s;
33837785Smsmith
33937785Smsmith			/*
34037785Smsmith			 * We could roll this to a function, to handle it as
34137785Smsmith			 * a regular variable function callback, but why bother?
34237785Smsmith			 */
34337785Smsmith			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
34437785Smsmith				chkmail(1);
34537785Smsmith			if ((vp->flags & VEXPORT) && localevar(s)) {
34637785Smsmith				change_env(s, 1);
34737785Smsmith				(void) setlocale(LC_ALL, "");
34837785Smsmith			}
34937785Smsmith			INTON;
35037785Smsmith			return;
35137785Smsmith		}
35237785Smsmith	}
35337785Smsmith	/* not found */
35437785Smsmith	vp = ckmalloc(sizeof (*vp));
35537785Smsmith	vp->flags = flags;
35637785Smsmith	vp->text = s;
35737785Smsmith	vp->next = *vpp;
35837785Smsmith	vp->func = NULL;
35937785Smsmith	INTOFF;
36037785Smsmith	*vpp = vp;
36137785Smsmith	if ((vp->flags & VEXPORT) && localevar(s)) {
36237785Smsmith		change_env(s, 1);
36337785Smsmith		(void) setlocale(LC_ALL, "");
36437785Smsmith	}
36537785Smsmith	INTON;
36637785Smsmith}
36737785Smsmith
36837785Smsmith
36937785Smsmith
37037785Smsmith/*
37137785Smsmith * Process a linked list of variable assignments.
37237785Smsmith */
37337785Smsmith
37437785Smsmithvoid
37537785Smsmithlistsetvar(struct strlist *list)
37637785Smsmith{
37737785Smsmith	struct strlist *lp;
37837785Smsmith
37937785Smsmith	INTOFF;
38037785Smsmith	for (lp = list ; lp ; lp = lp->next) {
38137785Smsmith		setvareq(savestr(lp->text), 0);
38237785Smsmith	}
38337785Smsmith	INTON;
38437785Smsmith}
38537785Smsmith
38637785Smsmith
38737785Smsmith
38837785Smsmith/*
38937785Smsmith * Find the value of a variable.  Returns NULL if not set.
39037785Smsmith */
39137785Smsmith
39237785Smsmithchar *
39337785Smsmithlookupvar(char *name)
39437785Smsmith{
39537785Smsmith	struct var *v;
39637785Smsmith
39737785Smsmith	for (v = *hashvar(name) ; v ; v = v->next) {
39837785Smsmith		if (varequal(v->text, name)) {
39937785Smsmith			if (v->flags & VUNSET)
40037785Smsmith				return NULL;
40137785Smsmith			return strchr(v->text, '=') + 1;
40237785Smsmith		}
40337785Smsmith	}
40437785Smsmith	return NULL;
40537785Smsmith}
40637785Smsmith
40737785Smsmith
40837785Smsmith
40937785Smsmith/*
41037785Smsmith * Search the environment of a builtin command.  If the second argument
41137785Smsmith * is nonzero, return the value of a variable even if it hasn't been
41237785Smsmith * exported.
41337785Smsmith */
41437785Smsmith
41537785Smsmithchar *
41637785Smsmithbltinlookup(char *name, int doall)
41737785Smsmith{
41837785Smsmith	struct strlist *sp;
41937785Smsmith	struct var *v;
42037785Smsmith
42137785Smsmith	for (sp = cmdenviron ; sp ; sp = sp->next) {
42237785Smsmith		if (varequal(sp->text, name))
42337785Smsmith			return strchr(sp->text, '=') + 1;
42437785Smsmith	}
42537785Smsmith	for (v = *hashvar(name) ; v ; v = v->next) {
42637785Smsmith		if (varequal(v->text, name)) {
42737785Smsmith			if ((v->flags & VUNSET)
42837785Smsmith			 || (!doall && (v->flags & VEXPORT) == 0))
42937785Smsmith				return NULL;
43037785Smsmith			return strchr(v->text, '=') + 1;
43137785Smsmith		}
43237785Smsmith	}
43337785Smsmith	return NULL;
43437785Smsmith}
43537785Smsmith
43637785Smsmith
43737785Smsmith
43837785Smsmith/*
43937785Smsmith * Generate a list of exported variables.  This routine is used to construct
44037785Smsmith * the third argument to execve when executing a program.
44137785Smsmith */
44237785Smsmith
44337785Smsmithchar **
44437785Smsmithenvironment(void)
44537785Smsmith{
44637785Smsmith	int nenv;
44737785Smsmith	struct var **vpp;
44837785Smsmith	struct var *vp;
44937785Smsmith	char **env, **ep;
45037785Smsmith
45137785Smsmith	nenv = 0;
45237785Smsmith	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
45337785Smsmith		for (vp = *vpp ; vp ; vp = vp->next)
45437785Smsmith			if (vp->flags & VEXPORT)
45537785Smsmith				nenv++;
45637785Smsmith	}
45737785Smsmith	ep = env = stalloc((nenv + 1) * sizeof *env);
45837785Smsmith	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
45937785Smsmith		for (vp = *vpp ; vp ; vp = vp->next)
46037785Smsmith			if (vp->flags & VEXPORT)
46137785Smsmith				*ep++ = vp->text;
46237785Smsmith	}
46337785Smsmith	*ep = NULL;
46437785Smsmith	return env;
46537785Smsmith}
46637785Smsmith
46737785Smsmith
46837785Smsmith/*
46937785Smsmith * Called when a shell procedure is invoked to clear out nonexported
47037785Smsmith * variables.  It is also necessary to reallocate variables of with
47137785Smsmith * VSTACK set since these are currently allocated on the stack.
47237785Smsmith */
47337785Smsmith
47437785Smsmith#ifdef mkinit
47537785SmsmithMKINIT void shprocvar(void);
47637785Smsmith
47737785SmsmithSHELLPROC {
47837785Smsmith	shprocvar();
47937785Smsmith}
48037785Smsmith#endif
48137785Smsmith
48237785Smsmithvoid
48337785Smsmithshprocvar(void)
48437785Smsmith{
48537785Smsmith	struct var **vpp;
48637785Smsmith	struct var *vp, **prev;
48737785Smsmith
48837785Smsmith	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
48937785Smsmith		for (prev = vpp ; (vp = *prev) != NULL ; ) {
49037785Smsmith			if ((vp->flags & VEXPORT) == 0) {
49137785Smsmith				*prev = vp->next;
49237785Smsmith				if ((vp->flags & VTEXTFIXED) == 0)
49337785Smsmith					ckfree(vp->text);
49437785Smsmith				if ((vp->flags & VSTRFIXED) == 0)
49537785Smsmith					ckfree(vp);
49637785Smsmith			} else {
49737785Smsmith				if (vp->flags & VSTACK) {
49837785Smsmith					vp->text = savestr(vp->text);
49937785Smsmith					vp->flags &=~ VSTACK;
50037785Smsmith				}
50137785Smsmith				prev = &vp->next;
50237785Smsmith			}
50337785Smsmith		}
50437785Smsmith	}
50537785Smsmith	initvar();
50637785Smsmith}
50737785Smsmith
50837785Smsmith
50937785Smsmithstatic int
51037785Smsmithvar_compare(const void *a, const void *b)
51137785Smsmith{
51237785Smsmith	const char *const *sa, *const *sb;
51337785Smsmith
51437785Smsmith	sa = a;
51537785Smsmith	sb = b;
51637785Smsmith	/*
51737785Smsmith	 * This compares two var=value strings which creates a different
51837785Smsmith	 * order from what you would probably expect.  POSIX is somewhat
51937785Smsmith	 * ambiguous on what should be sorted exactly.
52037785Smsmith	 */
52137785Smsmith	return strcoll(*sa, *sb);
52237785Smsmith}
52337785Smsmith
52472940Simp
52572940Simp/*
52672940Simp * Command to list all variables which are set.  Currently this command
52772940Simp * is invoked from the set command when the set command is called without
52872940Simp * any variables.
52972940Simp */
53072940Simp
53172940Simpint
53272940Simpshowvarscmd(int argc __unused, char **argv __unused)
533122024Simp{
53472940Simp	struct var **vpp;
53572940Simp	struct var *vp;
53672940Simp	const char *s;
53772940Simp	const char **vars;
53872940Simp	int i, n;
53972940Simp
540122024Simp	/*
54172940Simp	 * POSIX requires us to sort the variables.
54272940Simp	 */
54372940Simp	n = 0;
54472940Simp	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
54572940Simp		for (vp = *vpp; vp; vp = vp->next) {
54637785Smsmith			if (!(vp->flags & VUNSET))
54772940Simp				n++;
548122024Simp		}
54972940Simp	}
55072940Simp
55172940Simp	INTON;
55237785Smsmith	vars = ckmalloc(n * sizeof(*vars));
553122024Simp	i = 0;
554122024Simp	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
55572940Simp		for (vp = *vpp; vp; vp = vp->next) {
55672940Simp			if (!(vp->flags & VUNSET))
55772940Simp				vars[i++] = vp->text;
55872940Simp		}
55972940Simp	}
560122024Simp
56172940Simp	qsort(vars, n, sizeof(*vars), var_compare);
56272940Simp	for (i = 0; i < n; i++) {
56372940Simp		for (s = vars[i]; *s != '='; s++)
56472940Simp			out1c(*s);
56572940Simp		out1c('=');
56672940Simp		out1qstr(s + 1);
56772940Simp		out1c('\n');
56872940Simp	}
56972940Simp	ckfree(vars);
57072940Simp	INTOFF;
57172940Simp
57272940Simp	return 0;
57337785Smsmith}
57472940Simp
57537785Smsmith
57637785Smsmith
57772940Simp/*
57837785Smsmith * The export and readonly commands.
579 */
580
581int
582exportcmd(int argc, char **argv)
583{
584	struct var **vpp;
585	struct var *vp;
586	char *name;
587	char *p;
588	char *cmdname;
589	int ch, values;
590	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
591
592	cmdname = argv[0];
593	optreset = optind = 1;
594	opterr = 0;
595	values = 0;
596	while ((ch = getopt(argc, argv, "p")) != -1) {
597		switch (ch) {
598		case 'p':
599			values = 1;
600			break;
601		case '?':
602		default:
603			error("unknown option: -%c", optopt);
604		}
605	}
606	argc -= optind;
607	argv += optind;
608
609	if (values && argc != 0)
610		error("-p requires no arguments");
611	listsetvar(cmdenviron);
612	if (argc != 0) {
613		while ((name = *argv++) != NULL) {
614			if ((p = strchr(name, '=')) != NULL) {
615				p++;
616			} else {
617				vpp = hashvar(name);
618				for (vp = *vpp ; vp ; vp = vp->next) {
619					if (varequal(vp->text, name)) {
620
621						vp->flags |= flag;
622						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
623							change_env(vp->text, 1);
624							(void) setlocale(LC_ALL, "");
625						}
626						goto found;
627					}
628				}
629			}
630			setvar(name, p, flag);
631found:;
632		}
633	} else {
634		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
635			for (vp = *vpp ; vp ; vp = vp->next) {
636				if (vp->flags & flag) {
637					if (values) {
638						out1str(cmdname);
639						out1c(' ');
640					}
641					for (p = vp->text ; *p != '=' ; p++)
642						out1c(*p);
643					if (values && !(vp->flags & VUNSET)) {
644						out1c('=');
645						out1qstr(p + 1);
646					}
647					out1c('\n');
648				}
649			}
650		}
651	}
652	return 0;
653}
654
655
656/*
657 * The "local" command.
658 */
659
660int
661localcmd(int argc __unused, char **argv __unused)
662{
663	char *name;
664
665	if (! in_function())
666		error("Not in a function");
667	while ((name = *argptr++) != NULL) {
668		mklocal(name);
669	}
670	return 0;
671}
672
673
674/*
675 * Make a variable a local variable.  When a variable is made local, it's
676 * value and flags are saved in a localvar structure.  The saved values
677 * will be restored when the shell function returns.  We handle the name
678 * "-" as a special case.
679 */
680
681void
682mklocal(char *name)
683{
684	struct localvar *lvp;
685	struct var **vpp;
686	struct var *vp;
687
688	INTOFF;
689	lvp = ckmalloc(sizeof (struct localvar));
690	if (name[0] == '-' && name[1] == '\0') {
691		lvp->text = ckmalloc(sizeof optlist);
692		memcpy(lvp->text, optlist, sizeof optlist);
693		vp = NULL;
694	} else {
695		vpp = hashvar(name);
696		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
697		if (vp == NULL) {
698			if (strchr(name, '='))
699				setvareq(savestr(name), VSTRFIXED);
700			else
701				setvar(name, NULL, VSTRFIXED);
702			vp = *vpp;	/* the new variable */
703			lvp->text = NULL;
704			lvp->flags = VUNSET;
705		} else {
706			lvp->text = vp->text;
707			lvp->flags = vp->flags;
708			vp->flags |= VSTRFIXED|VTEXTFIXED;
709			if (strchr(name, '='))
710				setvareq(savestr(name), 0);
711		}
712	}
713	lvp->vp = vp;
714	lvp->next = localvars;
715	localvars = lvp;
716	INTON;
717}
718
719
720/*
721 * Called after a function returns.
722 */
723
724void
725poplocalvars(void)
726{
727	struct localvar *lvp;
728	struct var *vp;
729
730	while ((lvp = localvars) != NULL) {
731		localvars = lvp->next;
732		vp = lvp->vp;
733		if (vp == NULL) {	/* $- saved */
734			memcpy(optlist, lvp->text, sizeof optlist);
735			ckfree(lvp->text);
736		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
737			(void)unsetvar(vp->text);
738		} else {
739			if ((vp->flags & VTEXTFIXED) == 0)
740				ckfree(vp->text);
741			vp->flags = lvp->flags;
742			vp->text = lvp->text;
743		}
744		ckfree(lvp);
745	}
746}
747
748
749int
750setvarcmd(int argc, char **argv)
751{
752	if (argc <= 2)
753		return unsetcmd(argc, argv);
754	else if (argc == 3)
755		setvar(argv[1], argv[2], 0);
756	else
757		error("List assignment not implemented");
758	return 0;
759}
760
761
762/*
763 * The unset builtin command.  We unset the function before we unset the
764 * variable to allow a function to be unset when there is a readonly variable
765 * with the same name.
766 */
767
768int
769unsetcmd(int argc __unused, char **argv __unused)
770{
771	char **ap;
772	int i;
773	int flg_func = 0;
774	int flg_var = 0;
775	int ret = 0;
776
777	while ((i = nextopt("vf")) != '\0') {
778		if (i == 'f')
779			flg_func = 1;
780		else
781			flg_var = 1;
782	}
783	if (flg_func == 0 && flg_var == 0)
784		flg_var = 1;
785
786	for (ap = argptr; *ap ; ap++) {
787		if (flg_func)
788			ret |= unsetfunc(*ap);
789		if (flg_var)
790			ret |= unsetvar(*ap);
791	}
792	return ret;
793}
794
795
796/*
797 * Unset the specified variable.
798 */
799
800int
801unsetvar(char *s)
802{
803	struct var **vpp;
804	struct var *vp;
805
806	vpp = hashvar(s);
807	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
808		if (varequal(vp->text, s)) {
809			if (vp->flags & VREADONLY)
810				return (1);
811			INTOFF;
812			if (*(strchr(vp->text, '=') + 1) != '\0')
813				setvar(s, nullstr, 0);
814			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
815				change_env(s, 0);
816				setlocale(LC_ALL, "");
817			}
818			vp->flags &= ~VEXPORT;
819			vp->flags |= VUNSET;
820			if ((vp->flags & VSTRFIXED) == 0) {
821				if ((vp->flags & VTEXTFIXED) == 0)
822					ckfree(vp->text);
823				*vpp = vp->next;
824				ckfree(vp);
825			}
826			INTON;
827			return (0);
828		}
829	}
830
831	return (0);
832}
833
834
835
836/*
837 * Find the appropriate entry in the hash table from the name.
838 */
839
840STATIC struct var **
841hashvar(char *p)
842{
843	unsigned int hashval;
844
845	hashval = ((unsigned char) *p) << 4;
846	while (*p && *p != '=')
847		hashval += (unsigned char) *p++;
848	return &vartab[hashval % VTABSIZE];
849}
850
851
852
853/*
854 * Returns true if the two strings specify the same varable.  The first
855 * variable name is terminated by '='; the second may be terminated by
856 * either '=' or '\0'.
857 */
858
859STATIC int
860varequal(char *p, char *q)
861{
862	while (*p == *q++) {
863		if (*p++ == '=')
864			return 1;
865	}
866	if (*p == '=' && *(q - 1) == '\0')
867		return 1;
868	return 0;
869}
870