var.c revision 99110
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
411556Srgrimes#endif /* not lint */
4299110Sobrien#include <sys/cdefs.h>
4399110Sobrien__FBSDID("$FreeBSD: head/bin/sh/var.c 99110 2002-06-30 05:15:05Z obrien $");
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;
51197915Stjr	const char *s;
5121556Srgrimes
5131556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5141556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next) {
51597915Stjr			if (vp->flags & VUNSET)
51697915Stjr				continue;
51797915Stjr			for (s = vp->text; *s != '='; s++)
51897915Stjr				out1c(*s);
51997915Stjr			out1c('=');
52097915Stjr			out1qstr(s + 1);
52197915Stjr			out1c('\n');
5221556Srgrimes		}
5231556Srgrimes	}
5241556Srgrimes	return 0;
5251556Srgrimes}
5261556Srgrimes
5271556Srgrimes
5281556Srgrimes
5291556Srgrimes/*
5301556Srgrimes * The export and readonly commands.
5311556Srgrimes */
5321556Srgrimes
5331556Srgrimesint
53490111Simpexportcmd(int argc, char **argv)
53517987Speter{
5361556Srgrimes	struct var **vpp;
5371556Srgrimes	struct var *vp;
5381556Srgrimes	char *name;
5391556Srgrimes	char *p;
54097914Stjr	char *cmdname;
54197914Stjr	int ch, values;
5421556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
5431556Srgrimes
54497914Stjr	cmdname = argv[0];
54597914Stjr	optreset = optind = 1;
54697914Stjr	values = 0;
54797914Stjr	while ((ch = getopt(argc, argv, "p")) != -1) {
54897914Stjr		switch (ch) {
54997914Stjr		case 'p':
55097914Stjr			values = 1;
55197914Stjr			break;
55297914Stjr		case '?':
55397914Stjr		default:
55497914Stjr			error("unknown option: -%c", optopt);
55597914Stjr		}
55697914Stjr	}
55797914Stjr	argc -= optind;
55897914Stjr	argv += optind;
55997914Stjr
5601556Srgrimes	listsetvar(cmdenviron);
56197914Stjr	if (argc != 0) {
5621556Srgrimes		while ((name = *argptr++) != NULL) {
5631556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
5641556Srgrimes				p++;
5651556Srgrimes			} else {
5661556Srgrimes				vpp = hashvar(name);
5671556Srgrimes				for (vp = *vpp ; vp ; vp = vp->next) {
5681556Srgrimes					if (varequal(vp->text, name)) {
56990111Simp
5701556Srgrimes						vp->flags |= flag;
57117525Sache						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
57217525Sache							putenv(vp->text);
57317525Sache							(void) setlocale(LC_ALL, "");
57417525Sache						}
5751556Srgrimes						goto found;
5761556Srgrimes					}
5771556Srgrimes				}
5781556Srgrimes			}
5791556Srgrimes			setvar(name, p, flag);
5801556Srgrimesfound:;
5811556Srgrimes		}
5821556Srgrimes	} else {
5831556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5841556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
5851556Srgrimes				if (vp->flags & flag) {
58697914Stjr					if (values) {
58797914Stjr						out1str(cmdname);
58897914Stjr						out1c(' ');
58997914Stjr					}
5901556Srgrimes					for (p = vp->text ; *p != '=' ; p++)
5911556Srgrimes						out1c(*p);
59297914Stjr					if (values && !(vp->flags & VUNSET)) {
59397914Stjr						out1c('=');
59497914Stjr						out1qstr(p + 1);
59597914Stjr					}
5961556Srgrimes					out1c('\n');
5971556Srgrimes				}
5981556Srgrimes			}
5991556Srgrimes		}
6001556Srgrimes	}
6011556Srgrimes	return 0;
6021556Srgrimes}
6031556Srgrimes
6041556Srgrimes
6051556Srgrimes/*
6061556Srgrimes * The "local" command.
6071556Srgrimes */
6081556Srgrimes
60917987Speterint
61090111Simplocalcmd(int argc __unused, char **argv __unused)
61117987Speter{
6121556Srgrimes	char *name;
6131556Srgrimes
6141556Srgrimes	if (! in_function())
6151556Srgrimes		error("Not in a function");
6161556Srgrimes	while ((name = *argptr++) != NULL) {
6171556Srgrimes		mklocal(name);
6181556Srgrimes	}
6191556Srgrimes	return 0;
6201556Srgrimes}
6211556Srgrimes
6221556Srgrimes
6231556Srgrimes/*
6241556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
6251556Srgrimes * value and flags are saved in a localvar structure.  The saved values
6261556Srgrimes * will be restored when the shell function returns.  We handle the name
6271556Srgrimes * "-" as a special case.
6281556Srgrimes */
6291556Srgrimes
6301556Srgrimesvoid
63190111Simpmklocal(char *name)
63290111Simp{
6331556Srgrimes	struct localvar *lvp;
6341556Srgrimes	struct var **vpp;
6351556Srgrimes	struct var *vp;
6361556Srgrimes
6371556Srgrimes	INTOFF;
6381556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
6391556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
6401556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
64117987Speter		memcpy(lvp->text, optlist, sizeof optlist);
6421556Srgrimes		vp = NULL;
6431556Srgrimes	} else {
6441556Srgrimes		vpp = hashvar(name);
6451556Srgrimes		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
6461556Srgrimes		if (vp == NULL) {
6471556Srgrimes			if (strchr(name, '='))
6481556Srgrimes				setvareq(savestr(name), VSTRFIXED);
6491556Srgrimes			else
6501556Srgrimes				setvar(name, NULL, VSTRFIXED);
6511556Srgrimes			vp = *vpp;	/* the new variable */
6521556Srgrimes			lvp->text = NULL;
6531556Srgrimes			lvp->flags = VUNSET;
6541556Srgrimes		} else {
6551556Srgrimes			lvp->text = vp->text;
6561556Srgrimes			lvp->flags = vp->flags;
6571556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
6581556Srgrimes			if (strchr(name, '='))
6591556Srgrimes				setvareq(savestr(name), 0);
6601556Srgrimes		}
6611556Srgrimes	}
6621556Srgrimes	lvp->vp = vp;
6631556Srgrimes	lvp->next = localvars;
6641556Srgrimes	localvars = lvp;
6651556Srgrimes	INTON;
6661556Srgrimes}
6671556Srgrimes
6681556Srgrimes
6691556Srgrimes/*
6701556Srgrimes * Called after a function returns.
6711556Srgrimes */
6721556Srgrimes
6731556Srgrimesvoid
67490111Simppoplocalvars(void)
67590111Simp{
6761556Srgrimes	struct localvar *lvp;
6771556Srgrimes	struct var *vp;
6781556Srgrimes
6791556Srgrimes	while ((lvp = localvars) != NULL) {
6801556Srgrimes		localvars = lvp->next;
6811556Srgrimes		vp = lvp->vp;
6821556Srgrimes		if (vp == NULL) {	/* $- saved */
68317987Speter			memcpy(optlist, lvp->text, sizeof optlist);
6841556Srgrimes			ckfree(lvp->text);
6851556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
6861556Srgrimes			(void)unsetvar(vp->text);
6871556Srgrimes		} else {
6881556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
6891556Srgrimes				ckfree(vp->text);
6901556Srgrimes			vp->flags = lvp->flags;
6911556Srgrimes			vp->text = lvp->text;
6921556Srgrimes		}
6931556Srgrimes		ckfree(lvp);
6941556Srgrimes	}
6951556Srgrimes}
6961556Srgrimes
6971556Srgrimes
69817987Speterint
69990111Simpsetvarcmd(int argc, char **argv)
70017987Speter{
7011556Srgrimes	if (argc <= 2)
7021556Srgrimes		return unsetcmd(argc, argv);
7031556Srgrimes	else if (argc == 3)
7041556Srgrimes		setvar(argv[1], argv[2], 0);
7051556Srgrimes	else
7061556Srgrimes		error("List assignment not implemented");
7071556Srgrimes	return 0;
7081556Srgrimes}
7091556Srgrimes
7101556Srgrimes
7111556Srgrimes/*
7121556Srgrimes * The unset builtin command.  We unset the function before we unset the
7131556Srgrimes * variable to allow a function to be unset when there is a readonly variable
7141556Srgrimes * with the same name.
7151556Srgrimes */
7161556Srgrimes
71717987Speterint
71890111Simpunsetcmd(int argc __unused, char **argv __unused)
71917987Speter{
7201556Srgrimes	char **ap;
7211556Srgrimes	int i;
7221556Srgrimes	int flg_func = 0;
7231556Srgrimes	int flg_var = 0;
7241556Srgrimes	int ret = 0;
7251556Srgrimes
7261556Srgrimes	while ((i = nextopt("vf")) != '\0') {
7271556Srgrimes		if (i == 'f')
7281556Srgrimes			flg_func = 1;
7291556Srgrimes		else
7301556Srgrimes			flg_var = 1;
7311556Srgrimes	}
7321556Srgrimes	if (flg_func == 0 && flg_var == 0)
7331556Srgrimes		flg_var = 1;
7348855Srgrimes
7351556Srgrimes	for (ap = argptr; *ap ; ap++) {
7361556Srgrimes		if (flg_func)
7371556Srgrimes			ret |= unsetfunc(*ap);
7381556Srgrimes		if (flg_var)
7391556Srgrimes			ret |= unsetvar(*ap);
7401556Srgrimes	}
7411556Srgrimes	return ret;
7421556Srgrimes}
7431556Srgrimes
7441556Srgrimes
7451556Srgrimes/*
7461556Srgrimes * Unset the specified variable.
7471556Srgrimes */
7481556Srgrimes
74920425Ssteveint
75090111Simpunsetvar(char *s)
75190111Simp{
7521556Srgrimes	struct var **vpp;
7531556Srgrimes	struct var *vp;
7541556Srgrimes
7551556Srgrimes	vpp = hashvar(s);
7561556Srgrimes	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
7571556Srgrimes		if (varequal(vp->text, s)) {
7581556Srgrimes			if (vp->flags & VREADONLY)
7591556Srgrimes				return (1);
7601556Srgrimes			INTOFF;
7611556Srgrimes			if (*(strchr(vp->text, '=') + 1) != '\0')
7621556Srgrimes				setvar(s, nullstr, 0);
76317525Sache			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
76417525Sache				unsetenv(s);
76517525Sache				setlocale(LC_ALL, "");
76617525Sache			}
76720425Ssteve			vp->flags &= ~VEXPORT;
7681556Srgrimes			vp->flags |= VUNSET;
7691556Srgrimes			if ((vp->flags & VSTRFIXED) == 0) {
7701556Srgrimes				if ((vp->flags & VTEXTFIXED) == 0)
7711556Srgrimes					ckfree(vp->text);
7721556Srgrimes				*vpp = vp->next;
7731556Srgrimes				ckfree(vp);
7741556Srgrimes			}
7751556Srgrimes			INTON;
7761556Srgrimes			return (0);
7771556Srgrimes		}
7781556Srgrimes	}
7791556Srgrimes
7801556Srgrimes	return (1);
7811556Srgrimes}
7821556Srgrimes
7831556Srgrimes
7841556Srgrimes
7851556Srgrimes/*
7861556Srgrimes * Find the appropriate entry in the hash table from the name.
7871556Srgrimes */
7881556Srgrimes
7891556SrgrimesSTATIC struct var **
79090111Simphashvar(char *p)
79190111Simp{
7921556Srgrimes	unsigned int hashval;
7931556Srgrimes
79420425Ssteve	hashval = ((unsigned char) *p) << 4;
7951556Srgrimes	while (*p && *p != '=')
79620425Ssteve		hashval += (unsigned char) *p++;
7971556Srgrimes	return &vartab[hashval % VTABSIZE];
7981556Srgrimes}
7991556Srgrimes
8001556Srgrimes
8011556Srgrimes
8021556Srgrimes/*
8031556Srgrimes * Returns true if the two strings specify the same varable.  The first
8041556Srgrimes * variable name is terminated by '='; the second may be terminated by
8051556Srgrimes * either '=' or '\0'.
8061556Srgrimes */
8071556Srgrimes
8081556SrgrimesSTATIC int
80990111Simpvarequal(char *p, char *q)
81090111Simp{
8111556Srgrimes	while (*p == *q++) {
8121556Srgrimes		if (*p++ == '=')
8131556Srgrimes			return 1;
8141556Srgrimes	}
8151556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
8161556Srgrimes		return 1;
8171556Srgrimes	return 0;
8181556Srgrimes}
819