var.c revision 240541
198943Sluigi/*-
298943Sluigi * Copyright (c) 1991, 1993
398943Sluigi *	The Regents of the University of California.  All rights reserved.
498943Sluigi *
598943Sluigi * This code is derived from software contributed to Berkeley by
698943Sluigi * Kenneth Almquist.
798943Sluigi *
898943Sluigi * Redistribution and use in source and binary forms, with or without
998943Sluigi * modification, are permitted provided that the following conditions
1098943Sluigi * are met:
1198943Sluigi * 1. Redistributions of source code must retain the above copyright
1298943Sluigi *    notice, this list of conditions and the following disclaimer.
1398943Sluigi * 2. Redistributions in binary form must reproduce the above copyright
1498943Sluigi *    notice, this list of conditions and the following disclaimer in the
1598943Sluigi *    documentation and/or other materials provided with the distribution.
1698943Sluigi * 4. Neither the name of the University nor the names of its contributors
1798943Sluigi *    may be used to endorse or promote products derived from this software
1898943Sluigi *    without specific prior written permission.
1998943Sluigi *
2098943Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2198943Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2298943Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2398943Sluigi * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2498943Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2598943Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2698943Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2798943Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2898943Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2998943Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3098943Sluigi * SUCH DAMAGE.
3198943Sluigi */
3298943Sluigi
3398943Sluigi#ifndef lint
3498943Sluigi#if 0
3598943Sluigistatic char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
3698943Sluigi#endif
3798943Sluigi#endif /* not lint */
3898943Sluigi#include <sys/cdefs.h>
3998943Sluigi__FBSDID("$FreeBSD: head/bin/sh/var.c 240541 2012-09-15 21:56:30Z jilles $");
4098943Sluigi
4198943Sluigi#include <unistd.h>
4298943Sluigi#include <stdlib.h>
4399603Sbde#include <paths.h>
4498943Sluigi
4598943Sluigi/*
4698943Sluigi * Shell variables.
4798943Sluigi */
4898943Sluigi
4998943Sluigi#include <locale.h>
5098943Sluigi#include <langinfo.h>
5198943Sluigi
5298943Sluigi#include "shell.h"
5398943Sluigi#include "output.h"
5498943Sluigi#include "expand.h"
5598943Sluigi#include "nodes.h"	/* for other headers */
5698943Sluigi#include "eval.h"	/* defines cmdenviron */
5798943Sluigi#include "exec.h"
5898943Sluigi#include "syntax.h"
5998943Sluigi#include "options.h"
6098943Sluigi#include "mail.h"
6198943Sluigi#include "var.h"
6298943Sluigi#include "memalloc.h"
6398943Sluigi#include "error.h"
6498943Sluigi#include "mystring.h"
6598943Sluigi#include "parser.h"
6698943Sluigi#include "builtins.h"
6798943Sluigi#ifndef NO_HISTORY
68102098Sluigi#include "myhistedit.h"
69101628Sluigi#endif
7098943Sluigi
7198943Sluigi
7298943Sluigi#define VTABSIZE 39
7398943Sluigi
7498943Sluigi
7598943Sluigistruct varinit {
7698943Sluigi	struct var *var;
7798943Sluigi	int flags;
7898943Sluigi	const char *text;
7998943Sluigi	void (*func)(const char *);
8098943Sluigi};
8198943Sluigi
8298943Sluigi
8398943Sluigi#ifndef NO_HISTORY
8498943Sluigistruct var vhistsize;
8598943Sluigistruct var vterm;
8698943Sluigi#endif
8798943Sluigistruct var vifs;
8898943Sluigistruct var vmail;
8998943Sluigistruct var vmpath;
9098943Sluigistruct var vpath;
9198943Sluigistruct var vppid;
9298943Sluigistruct var vps1;
9398943Sluigistruct var vps2;
9498943Sluigistruct var vps4;
9598943Sluigistruct var vvers;
9698943Sluigistatic struct var voptind;
9798943Sluigistruct var vdisvfork;
9898943Sluigi
9998943Sluigiint forcelocal;
10098943Sluigi
10198943Sluigistatic const struct varinit varinit[] = {
10298943Sluigi#ifndef NO_HISTORY
10398943Sluigi	{ &vhistsize,	VUNSET,				"HISTSIZE=",
10498943Sluigi	  sethistsize },
10598943Sluigi#endif
10698943Sluigi	{ &vifs,	0,				"IFS= \t\n",
10798943Sluigi	  NULL },
10898943Sluigi	{ &vmail,	VUNSET,				"MAIL=",
10998943Sluigi	  NULL },
11098943Sluigi	{ &vmpath,	VUNSET,				"MAILPATH=",
11198943Sluigi	  NULL },
11298943Sluigi	{ &vpath,	0,				"PATH=" _PATH_DEFPATH,
11398943Sluigi	  changepath },
11498943Sluigi	{ &vppid,	VUNSET,				"PPID=",
11598943Sluigi	  NULL },
11698943Sluigi	/*
11798943Sluigi	 * vps1 depends on uid
11898943Sluigi	 */
11998943Sluigi	{ &vps2,	0,				"PS2=> ",
12098943Sluigi	  NULL },
12198943Sluigi	{ &vps4,	0,				"PS4=+ ",
12298943Sluigi	  NULL },
12398943Sluigi#ifndef NO_HISTORY
12498943Sluigi	{ &vterm,	VUNSET,				"TERM=",
12598943Sluigi	  setterm },
12698943Sluigi#endif
12798943Sluigi	{ &voptind,	0,				"OPTIND=1",
12898943Sluigi	  getoptsreset },
12998943Sluigi	{ &vdisvfork,	VUNSET,				"SH_DISABLE_VFORK=",
13098943Sluigi	  NULL },
13198943Sluigi	{ NULL,	0,				NULL,
13298943Sluigi	  NULL }
13398943Sluigi};
13498943Sluigi
13598943Sluigistatic struct var *vartab[VTABSIZE];
13698943Sluigi
13798943Sluigistatic const char *const locale_names[7] = {
13898943Sluigi	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
13998943Sluigi	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
14098943Sluigi};
14198943Sluigistatic const int locale_categories[7] = {
14298943Sluigi	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
14398943Sluigi};
14498943Sluigi
14598943Sluigistatic int varequal(const char *, const char *);
14698943Sluigistatic struct var *find_var(const char *, struct var ***, int *);
14798943Sluigistatic int localevar(const char *);
14898943Sluigi
14998943Sluigi/*
15098943Sluigi * Initialize the variable symbol tables and import the environment.
15198943Sluigi */
15298943Sluigi
15398943Sluigi#ifdef mkinit
15498943SluigiINCLUDE "var.h"
15598943SluigiMKINIT char **environ;
15698943SluigiINIT {
15798943Sluigi	char **envp;
15898943Sluigi
15998943Sluigi	initvar();
16098943Sluigi	for (envp = environ ; *envp ; envp++) {
16198943Sluigi		if (strchr(*envp, '=')) {
16298943Sluigi			setvareq(*envp, VEXPORT|VTEXTFIXED);
16398943Sluigi		}
16498943Sluigi	}
16598943Sluigi}
16698943Sluigi#endif
16798943Sluigi
16898943Sluigi
16998943Sluigi/*
17098943Sluigi * This routine initializes the builtin variables.  It is called when the
17198943Sluigi * shell is initialized.
17298943Sluigi */
17398943Sluigi
17498943Sluigivoid
17598943Sluigiinitvar(void)
17698943Sluigi{
17798943Sluigi	char ppid[20];
17898943Sluigi	const struct varinit *ip;
17998943Sluigi	struct var *vp;
18098943Sluigi	struct var **vpp;
18198943Sluigi
182101641Sluigi	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
183101641Sluigi		if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
18498943Sluigi			continue;
18598943Sluigi		vp->next = *vpp;
18698943Sluigi		*vpp = vp;
18798943Sluigi		vp->text = __DECONST(char *, ip->text);
18898943Sluigi		vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
18998943Sluigi		vp->func = ip->func;
19098943Sluigi	}
19198943Sluigi	/*
19298943Sluigi	 * PS1 depends on uid
19398943Sluigi	 */
19498943Sluigi	if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
19598943Sluigi		vps1.next = *vpp;
19698943Sluigi		*vpp = &vps1;
19798943Sluigi		vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
19898943Sluigi		vps1.flags = VSTRFIXED|VTEXTFIXED;
19998943Sluigi	}
20098943Sluigi	if ((vppid.flags & VEXPORT) == 0) {
20198943Sluigi		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
20298943Sluigi		setvarsafe("PPID", ppid, 0);
20398943Sluigi	}
20498943Sluigi}
20598943Sluigi
20698943Sluigi/*
20798943Sluigi * Safe version of setvar, returns 1 on success 0 on failure.
20898943Sluigi */
20998943Sluigi
21098943Sluigiint
21198943Sluigisetvarsafe(const char *name, const char *val, int flags)
21298943Sluigi{
21398943Sluigi	struct jmploc jmploc;
21498943Sluigi	struct jmploc *const savehandler = handler;
21598943Sluigi	int err = 0;
21698943Sluigi	int inton;
21798943Sluigi
21898943Sluigi	inton = is_int_on();
21998943Sluigi	if (setjmp(jmploc.loc))
22098943Sluigi		err = 1;
22198943Sluigi	else {
22298943Sluigi		handler = &jmploc;
22398943Sluigi		setvar(name, val, flags);
22498943Sluigi	}
225102087Sluigi	handler = savehandler;
226102087Sluigi	SETINTON(inton);
22798943Sluigi	return err;
22898943Sluigi}
229101978Sluigi
23098943Sluigi/*
23198943Sluigi * Set the value of a variable.  The flags argument is stored with the
23298943Sluigi * flags of the variable.  If val is NULL, the variable is unset.
23398943Sluigi */
23498943Sluigi
23598943Sluigivoid
23698943Sluigisetvar(const char *name, const char *val, int flags)
23798943Sluigi{
23898943Sluigi	const char *p;
23998943Sluigi	int len;
24098943Sluigi	int namelen;
24198943Sluigi	char *nameeq;
24298943Sluigi	int isbad;
24398943Sluigi
24498943Sluigi	isbad = 0;
24598943Sluigi	p = name;
24698943Sluigi	if (! is_name(*p))
24798943Sluigi		isbad = 1;
248101978Sluigi	p++;
24998943Sluigi	for (;;) {
25098943Sluigi		if (! is_in_name(*p)) {
25198943Sluigi			if (*p == '\0' || *p == '=')
25298943Sluigi				break;
25398943Sluigi			isbad = 1;
25498943Sluigi		}
25598943Sluigi		p++;
25698943Sluigi	}
25798943Sluigi	namelen = p - name;
25898943Sluigi	if (isbad)
25998943Sluigi		error("%.*s: bad variable name", namelen, name);
26098943Sluigi	len = namelen + 2;		/* 2 is space for '=' and '\0' */
26198943Sluigi	if (val == NULL) {
26298943Sluigi		flags |= VUNSET;
26398943Sluigi	} else {
26499475Sluigi		len += strlen(val);
26598943Sluigi	}
26698943Sluigi	nameeq = ckmalloc(len);
26798943Sluigi	memcpy(nameeq, name, namelen);
26898943Sluigi	nameeq[namelen] = '=';
26998943Sluigi	if (val)
27098943Sluigi		scopy(val, nameeq + namelen + 1);
27198943Sluigi	else
27298943Sluigi		nameeq[namelen + 1] = '\0';
27398943Sluigi	setvareq(nameeq, flags);
27498943Sluigi}
27598943Sluigi
27698943Sluigistatic int
27798943Sluigilocalevar(const char *s)
27898943Sluigi{
27998943Sluigi	const char *const *ss;
28098943Sluigi
28198943Sluigi	if (*s != 'L')
28298943Sluigi		return 0;
28398943Sluigi	if (varequal(s + 1, "ANG"))
28498943Sluigi		return 1;
28598943Sluigi	if (strncmp(s + 1, "C_", 2) != 0)
28698943Sluigi		return 0;
28799475Sluigi	if (varequal(s + 3, "ALL"))
28898943Sluigi		return 1;
28998943Sluigi	for (ss = locale_names; *ss ; ss++)
29098943Sluigi		if (varequal(s + 3, *ss + 3))
29198943Sluigi			return 1;
29298943Sluigi	return 0;
29398943Sluigi}
29498943Sluigi
29598943Sluigi
29698943Sluigi/*
29798943Sluigi * Sets/unsets an environment variable from a pointer that may actually be a
29898943Sluigi * pointer into environ where the string should not be manipulated.
29998943Sluigi */
30098943Sluigistatic void
30198943Sluigichange_env(const char *s, int set)
30298943Sluigi{
30398943Sluigi	char *eqp;
30498943Sluigi	char *ss;
30598943Sluigi
30698943Sluigi	ss = savestr(s);
30798943Sluigi	if ((eqp = strchr(ss, '=')) != NULL)
30898943Sluigi		*eqp = '\0';
30998943Sluigi	if (set && eqp != NULL)
31098943Sluigi		(void) setenv(ss, eqp + 1, 1);
31198943Sluigi	else
31298943Sluigi		(void) unsetenv(ss);
31398943Sluigi	ckfree(ss);
31498943Sluigi
31598943Sluigi	return;
31698943Sluigi}
31798943Sluigi
31898943Sluigi
31998943Sluigi/*
32098943Sluigi * Same as setvar except that the variable and value are passed in
32198943Sluigi * the first argument as name=value.  Since the first argument will
32298943Sluigi * be actually stored in the table, it should not be a string that
32398943Sluigi * will go away.
32498943Sluigi */
32598943Sluigi
32699909Sluigivoid
32798943Sluigisetvareq(char *s, int flags)
328102087Sluigi{
329102087Sluigi	struct var *vp, **vpp;
330102087Sluigi	int nlen;
331102087Sluigi
332102087Sluigi	if (aflag)
333102087Sluigi		flags |= VEXPORT;
334102087Sluigi	if (forcelocal && !(flags & (VNOSET | VNOLOCAL)))
335102087Sluigi		mklocal(s);
33698943Sluigi	vp = find_var(s, &vpp, &nlen);
33798943Sluigi	if (vp != NULL) {
33898943Sluigi		if (vp->flags & VREADONLY)
33998943Sluigi			error("%.*s: is read only", vp->name_len, s);
34098943Sluigi		if (flags & VNOSET)
341101641Sluigi			return;
342101641Sluigi		INTOFF;
343101641Sluigi
344101641Sluigi		if (vp->func && (flags & VNOFUNC) == 0)
34598943Sluigi			(*vp->func)(s + vp->name_len + 1);
34698943Sluigi
34798943Sluigi		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
34898943Sluigi			ckfree(vp->text);
34998943Sluigi
35098943Sluigi		vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
35198943Sluigi		vp->flags |= flags;
35298943Sluigi		vp->text = s;
35398943Sluigi
35498943Sluigi		/*
35598943Sluigi		 * We could roll this to a function, to handle it as
35698943Sluigi		 * a regular variable function callback, but why bother?
35798943Sluigi		 *
35898943Sluigi		 * Note: this assumes iflag is not set to 1 initially.
35998943Sluigi		 * As part of init(), this is called before arguments
36098943Sluigi		 * are looked at.
36198943Sluigi		 */
36298943Sluigi		if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
36398943Sluigi		    iflag == 1)
36498943Sluigi			chkmail(1);
36598943Sluigi		if ((vp->flags & VEXPORT) && localevar(s)) {
36698943Sluigi			change_env(s, 1);
36798943Sluigi			(void) setlocale(LC_ALL, "");
36898943Sluigi			updatecharset();
36998943Sluigi		}
37098943Sluigi		INTON;
37198943Sluigi		return;
37298943Sluigi	}
37398943Sluigi	/* not found */
37498943Sluigi	if (flags & VNOSET)
37598943Sluigi		return;
37698943Sluigi	vp = ckmalloc(sizeof (*vp));
37798943Sluigi	vp->flags = flags;
37898943Sluigi	vp->text = s;
37998943Sluigi	vp->name_len = nlen;
38098943Sluigi	vp->next = *vpp;
38198943Sluigi	vp->func = NULL;
38298943Sluigi	INTOFF;
38398943Sluigi	*vpp = vp;
38498943Sluigi	if ((vp->flags & VEXPORT) && localevar(s)) {
38598943Sluigi		change_env(s, 1);
38698943Sluigi		(void) setlocale(LC_ALL, "");
38798943Sluigi		updatecharset();
38898943Sluigi	}
38998943Sluigi	INTON;
39098943Sluigi}
39198943Sluigi
39298943Sluigi
39398943Sluigi
39498943Sluigi/*
39598943Sluigi * Process a linked list of variable assignments.
39698943Sluigi */
39798943Sluigi
39898943Sluigivoid
39998943Sluigilistsetvar(struct strlist *list, int flags)
40098943Sluigi{
40198943Sluigi	struct strlist *lp;
40298943Sluigi
40398943Sluigi	INTOFF;
40498943Sluigi	for (lp = list ; lp ; lp = lp->next) {
40598943Sluigi		setvareq(savestr(lp->text), flags);
40698943Sluigi	}
407102087Sluigi	INTON;
40898943Sluigi}
40998943Sluigi
41098943Sluigi
41198943Sluigi
41298943Sluigi/*
41398943Sluigi * Find the value of a variable.  Returns NULL if not set.
41498943Sluigi */
415102087Sluigi
416102087Sluigichar *
417102087Sluigilookupvar(const char *name)
41898943Sluigi{
41998943Sluigi	struct var *v;
42098943Sluigi
42198943Sluigi	v = find_var(name, NULL, NULL);
42298943Sluigi	if (v == NULL || v->flags & VUNSET)
42398943Sluigi		return NULL;
42498943Sluigi	return v->text + v->name_len + 1;
42598943Sluigi}
42698943Sluigi
42798943Sluigi
42898943Sluigi
42998943Sluigi/*
43098943Sluigi * Search the environment of a builtin command.  If the second argument
43198943Sluigi * is nonzero, return the value of a variable even if it hasn't been
43298943Sluigi * exported.
43398943Sluigi */
43498943Sluigi
43598943Sluigichar *
436101628Sluigibltinlookup(const char *name, int doall)
43798943Sluigi{
43898943Sluigi	struct strlist *sp;
43998943Sluigi	struct var *v;
44098943Sluigi	char *result;
441101628Sluigi
442101628Sluigi	result = NULL;
44398943Sluigi	for (sp = cmdenviron ; sp ; sp = sp->next) {
44498943Sluigi		if (varequal(sp->text, name))
445101628Sluigi			result = strchr(sp->text, '=') + 1;
44698943Sluigi	}
447101628Sluigi	if (result != NULL)
448106505Smaxim		return result;
44998943Sluigi
45098943Sluigi	v = find_var(name, NULL, NULL);
45198943Sluigi	if (v == NULL || v->flags & VUNSET ||
45298943Sluigi	    (!doall && (v->flags & VEXPORT) == 0))
453101628Sluigi		return NULL;
45498943Sluigi	return v->text + v->name_len + 1;
455101628Sluigi}
456101628Sluigi
457101628Sluigi
45898943Sluigi/*
459101628Sluigi * Set up locale for a builtin (LANG/LC_* assignments).
460101628Sluigi */
461101628Sluigivoid
462101628Sluigibltinsetlocale(void)
463101628Sluigi{
464101628Sluigi	struct strlist *lp;
465101628Sluigi	int act = 0;
466101628Sluigi	char *loc, *locdef;
467101628Sluigi	int i;
468101628Sluigi
469101628Sluigi	for (lp = cmdenviron ; lp ; lp = lp->next) {
470101628Sluigi		if (localevar(lp->text)) {
47198943Sluigi			act = 1;
472101628Sluigi			break;
473101628Sluigi		}
474101628Sluigi	}
47598943Sluigi	if (!act)
47698943Sluigi		return;
47798943Sluigi	loc = bltinlookup("LC_ALL", 0);
47898943Sluigi	INTOFF;
47998943Sluigi	if (loc != NULL) {
48098943Sluigi		setlocale(LC_ALL, loc);
48198943Sluigi		INTON;
48298943Sluigi		updatecharset();
48398943Sluigi		return;
48498943Sluigi	}
485101628Sluigi	locdef = bltinlookup("LANG", 0);
486101628Sluigi	for (i = 0; locale_names[i] != NULL; i++) {
48798943Sluigi		loc = bltinlookup(locale_names[i], 0);
48898943Sluigi		if (loc == NULL)
48998943Sluigi			loc = locdef;
49098943Sluigi		if (loc != NULL)
49198943Sluigi			setlocale(locale_categories[i], loc);
492101628Sluigi	}
49398943Sluigi	INTON;
49498943Sluigi	updatecharset();
49598943Sluigi}
49698943Sluigi
49798943Sluigi/*
49898943Sluigi * Undo the effect of bltinlocaleset().
49998943Sluigi */
50098943Sluigivoid
50198943Sluigibltinunsetlocale(void)
50298943Sluigi{
50398943Sluigi	struct strlist *lp;
504102087Sluigi
50598943Sluigi	INTOFF;
506102087Sluigi	for (lp = cmdenviron ; lp ; lp = lp->next) {
50798943Sluigi		if (localevar(lp->text)) {
50898943Sluigi			setlocale(LC_ALL, "");
50998943Sluigi			updatecharset();
51098943Sluigi			return;
51198943Sluigi		}
51298943Sluigi	}
51398943Sluigi	INTON;
51498943Sluigi}
51598943Sluigi
51698943Sluigi/*
51798943Sluigi * Update the localeisutf8 flag.
51898943Sluigi */
51998943Sluigivoid
52098943Sluigiupdatecharset(void)
521101978Sluigi{
522101978Sluigi	char *charset;
523101978Sluigi
524101978Sluigi	charset = nl_langinfo(CODESET);
525102087Sluigi	localeisutf8 = !strcmp(charset, "UTF-8");
526102087Sluigi}
52798943Sluigi
52898943Sluigivoid
52998943Sluigiinitcharset(void)
53098943Sluigi{
531102087Sluigi	updatecharset();
53298943Sluigi	initial_localeisutf8 = localeisutf8;
53398943Sluigi}
53498943Sluigi
53598943Sluigi/*
53698943Sluigi * Generate a list of exported variables.  This routine is used to construct
53798943Sluigi * the third argument to execve when executing a program.
53898943Sluigi */
53998943Sluigi
54098943Sluigichar **
54198943Sluigienvironment(void)
54298943Sluigi{
54398943Sluigi	int nenv;
54498943Sluigi	struct var **vpp;
54598943Sluigi	struct var *vp;
54698943Sluigi	char **env, **ep;
54798943Sluigi
54898943Sluigi	nenv = 0;
54998943Sluigi	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
55098943Sluigi		for (vp = *vpp ; vp ; vp = vp->next)
55198943Sluigi			if (vp->flags & VEXPORT)
55298943Sluigi				nenv++;
55398943Sluigi	}
55498943Sluigi	ep = env = stalloc((nenv + 1) * sizeof *env);
55598943Sluigi	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
55698943Sluigi		for (vp = *vpp ; vp ; vp = vp->next)
55798943Sluigi			if (vp->flags & VEXPORT)
55898943Sluigi				*ep++ = vp->text;
55998943Sluigi	}
56098943Sluigi	*ep = NULL;
56198943Sluigi	return env;
56298943Sluigi}
56398943Sluigi
56498943Sluigi
56598943Sluigistatic int
566102087Sluigivar_compare(const void *a, const void *b)
56798943Sluigi{
56898943Sluigi	const char *const *sa, *const *sb;
56998943Sluigi
57098943Sluigi	sa = a;
57198943Sluigi	sb = b;
57298943Sluigi	/*
57399475Sluigi	 * This compares two var=value strings which creates a different
57498943Sluigi	 * order from what you would probably expect.  POSIX is somewhat
57598943Sluigi	 * ambiguous on what should be sorted exactly.
57698943Sluigi	 */
57798943Sluigi	return strcoll(*sa, *sb);
57899475Sluigi}
57998943Sluigi
58099475Sluigi
58198943Sluigi/*
58298943Sluigi * Command to list all variables which are set.  This is invoked from the
58398943Sluigi * set command when it is called without any options or operands.
58498943Sluigi */
58598943Sluigi
58698943Sluigiint
58798943Sluigishowvarscmd(int argc __unused, char **argv __unused)
58898943Sluigi{
58998943Sluigi	struct var **vpp;
59098943Sluigi	struct var *vp;
59198943Sluigi	const char *s;
59298943Sluigi	const char **vars;
59398943Sluigi	int i, n;
59498943Sluigi
59598943Sluigi	/*
59698943Sluigi	 * POSIX requires us to sort the variables.
59798943Sluigi	 */
59898943Sluigi	n = 0;
59998943Sluigi	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
60098943Sluigi		for (vp = *vpp; vp; vp = vp->next) {
60198943Sluigi			if (!(vp->flags & VUNSET))
60298943Sluigi				n++;
60398943Sluigi		}
60498943Sluigi	}
60598943Sluigi
60698943Sluigi	INTOFF;
60798943Sluigi	vars = ckmalloc(n * sizeof(*vars));
60898943Sluigi	i = 0;
60998943Sluigi	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
61098943Sluigi		for (vp = *vpp; vp; vp = vp->next) {
61198943Sluigi			if (!(vp->flags & VUNSET))
61298943Sluigi				vars[i++] = vp->text;
61398943Sluigi		}
61498943Sluigi	}
61598943Sluigi
61698943Sluigi	qsort(vars, n, sizeof(*vars), var_compare);
61798943Sluigi	for (i = 0; i < n; i++) {
61898943Sluigi		/*
61998943Sluigi		 * Skip improper variable names so the output remains usable as
62098943Sluigi		 * shell input.
62198943Sluigi		 */
62298943Sluigi		if (!isassignment(vars[i]))
62398943Sluigi			continue;
62498943Sluigi		s = strchr(vars[i], '=');
62598943Sluigi		s++;
62698943Sluigi		outbin(vars[i], s - vars[i], out1);
62798943Sluigi		out1qstr(s);
62898943Sluigi		out1c('\n');
62998943Sluigi	}
63098943Sluigi	ckfree(vars);
63198943Sluigi	INTON;
63298943Sluigi
63398943Sluigi	return 0;
63498943Sluigi}
63598943Sluigi
63698943Sluigi
63798943Sluigi
63898943Sluigi/*
63998943Sluigi * The export and readonly commands.
64098943Sluigi */
64198943Sluigi
64298943Sluigiint
643102087Sluigiexportcmd(int argc __unused, char **argv)
64498943Sluigi{
64598943Sluigi	struct var **vpp;
64698943Sluigi	struct var *vp;
64798943Sluigi	char **ap;
648102087Sluigi	char *name;
64998943Sluigi	char *p;
65098943Sluigi	char *cmdname;
65198943Sluigi	int ch, values;
65298943Sluigi	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
65398943Sluigi
65498943Sluigi	cmdname = argv[0];
65598943Sluigi	values = 0;
65698943Sluigi	while ((ch = nextopt("p")) != '\0') {
65798943Sluigi		switch (ch) {
65898943Sluigi		case 'p':
65998943Sluigi			values = 1;
66098943Sluigi			break;
66198943Sluigi		}
66298943Sluigi	}
66398943Sluigi
66498943Sluigi	if (values && *argptr != NULL)
66598943Sluigi		error("-p requires no arguments");
66698943Sluigi	if (*argptr != NULL) {
66798943Sluigi		for (ap = argptr; (name = *ap) != NULL; ap++) {
66898943Sluigi			if ((p = strchr(name, '=')) != NULL) {
66998943Sluigi				p++;
67098943Sluigi			} else {
67198943Sluigi				vp = find_var(name, NULL, NULL);
67298943Sluigi				if (vp != NULL) {
67398943Sluigi					vp->flags |= flag;
67498943Sluigi					if ((vp->flags & VEXPORT) && localevar(vp->text)) {
67598943Sluigi						change_env(vp->text, 1);
67698943Sluigi						(void) setlocale(LC_ALL, "");
67798943Sluigi						updatecharset();
67898943Sluigi					}
67998943Sluigi					continue;
68098943Sluigi				}
68198943Sluigi			}
68298943Sluigi			setvar(name, p, flag);
68398943Sluigi		}
68498943Sluigi	} else {
68598943Sluigi		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
68698943Sluigi			for (vp = *vpp ; vp ; vp = vp->next) {
68798943Sluigi				if (vp->flags & flag) {
68898943Sluigi					if (values) {
68998943Sluigi						/*
69098943Sluigi						 * Skip improper variable names
69198943Sluigi						 * so the output remains usable
69298943Sluigi						 * as shell input.
69398943Sluigi						 */
69498943Sluigi						if (!isassignment(vp->text))
69598943Sluigi							continue;
69698943Sluigi						out1str(cmdname);
69798943Sluigi						out1c(' ');
69898943Sluigi					}
69998943Sluigi					if (values && !(vp->flags & VUNSET)) {
70098943Sluigi						outbin(vp->text,
70198943Sluigi						    vp->name_len + 1, out1);
70298943Sluigi						out1qstr(vp->text +
70398943Sluigi						    vp->name_len + 1);
70498943Sluigi					} else
70598943Sluigi						outbin(vp->text, vp->name_len,
70698943Sluigi						    out1);
70798943Sluigi					out1c('\n');
70898943Sluigi				}
70998943Sluigi			}
71098943Sluigi		}
71198943Sluigi	}
71298943Sluigi	return 0;
71398943Sluigi}
71498943Sluigi
71598943Sluigi
71698943Sluigi/*
71799475Sluigi * The "local" command.
71899475Sluigi */
71999475Sluigi
72099475Sluigiint
72198943Sluigilocalcmd(int argc __unused, char **argv __unused)
72299475Sluigi{
72399475Sluigi	char *name;
72499475Sluigi
72599475Sluigi	if (! in_function())
72699475Sluigi		error("Not in a function");
72799475Sluigi	while ((name = *argptr++) != NULL) {
72899475Sluigi		mklocal(name);
72999475Sluigi	}
73099475Sluigi	return 0;
73199475Sluigi}
73299475Sluigi
73399475Sluigi
73499475Sluigi/*
73599475Sluigi * Make a variable a local variable.  When a variable is made local, it's
73699475Sluigi * value and flags are saved in a localvar structure.  The saved values
73799475Sluigi * will be restored when the shell function returns.  We handle the name
73899475Sluigi * "-" as a special case.
73999475Sluigi */
74099475Sluigi
74199475Sluigivoid
74299475Sluigimklocal(char *name)
74399475Sluigi{
74499475Sluigi	struct localvar *lvp;
74599475Sluigi	struct var **vpp;
74699475Sluigi	struct var *vp;
74799475Sluigi
74899475Sluigi	INTOFF;
74999475Sluigi	lvp = ckmalloc(sizeof (struct localvar));
75099475Sluigi	if (name[0] == '-' && name[1] == '\0') {
75199475Sluigi		lvp->text = ckmalloc(sizeof optlist);
75299475Sluigi		memcpy(lvp->text, optlist, sizeof optlist);
75399475Sluigi		vp = NULL;
75499475Sluigi	} else {
75599475Sluigi		vp = find_var(name, &vpp, NULL);
75698943Sluigi		if (vp == NULL) {
75798943Sluigi			if (strchr(name, '='))
75898943Sluigi				setvareq(savestr(name), VSTRFIXED | VNOLOCAL);
75998943Sluigi			else
760102087Sluigi				setvar(name, NULL, VSTRFIXED | VNOLOCAL);
761102087Sluigi			vp = *vpp;	/* the new variable */
762101978Sluigi			lvp->text = NULL;
763102087Sluigi			lvp->flags = VUNSET;
764102087Sluigi		} else {
765102087Sluigi			lvp->text = vp->text;
766102087Sluigi			lvp->flags = vp->flags;
767102087Sluigi			vp->flags |= VSTRFIXED|VTEXTFIXED;
76898943Sluigi			if (name[vp->name_len] == '=')
769101978Sluigi				setvareq(savestr(name), VNOLOCAL);
770101978Sluigi		}
771101978Sluigi	}
772101978Sluigi	lvp->vp = vp;
773101978Sluigi	lvp->next = localvars;
774102087Sluigi	localvars = lvp;
77598943Sluigi	INTON;
776101978Sluigi}
77798943Sluigi
778102087Sluigi
77998943Sluigi/*
780102087Sluigi * Called after a function returns.
781102087Sluigi */
782102087Sluigi
783102087Sluigivoid
784102087Sluigipoplocalvars(void)
785102087Sluigi{
786102087Sluigi	struct localvar *lvp;
787102087Sluigi	struct var *vp;
788102087Sluigi
789102087Sluigi	while ((lvp = localvars) != NULL) {
790102087Sluigi		localvars = lvp->next;
791102087Sluigi		vp = lvp->vp;
792101978Sluigi		if (vp == NULL) {	/* $- saved */
793102087Sluigi			memcpy(optlist, lvp->text, sizeof optlist);
794102087Sluigi			ckfree(lvp->text);
795102087Sluigi			optschanged();
796102087Sluigi		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
797102087Sluigi			(void)unsetvar(vp->text);
798102087Sluigi		} else {
799102087Sluigi			if ((vp->flags & VTEXTFIXED) == 0)
800102087Sluigi				ckfree(vp->text);
80198943Sluigi			vp->flags = lvp->flags;
80298943Sluigi			vp->text = lvp->text;
80398943Sluigi		}
80498943Sluigi		ckfree(lvp);
80598943Sluigi	}
80698943Sluigi}
807107291Skeramida
80898943Sluigi
80998943Sluigiint
81098943Sluigisetvarcmd(int argc, char **argv)
81198943Sluigi{
81298943Sluigi	if (argc <= 2)
81398943Sluigi		return unsetcmd(argc, argv);
81498943Sluigi	else if (argc == 3)
815105887Smux		setvar(argv[1], argv[2], 0);
816101628Sluigi	else
817101628Sluigi		error("too many arguments");
818101628Sluigi	return 0;
819101628Sluigi}
820101628Sluigi
821101628Sluigi
822101628Sluigi/*
82398943Sluigi * The unset builtin command.
82498943Sluigi */
82598943Sluigi
82698943Sluigiint
82798943Sluigiunsetcmd(int argc __unused, char **argv __unused)
82898943Sluigi{
829107291Skeramida	char **ap;
830107291Skeramida	int i;
831107291Skeramida	int flg_func = 0;
832107291Skeramida	int flg_var = 0;
833107291Skeramida	int ret = 0;
834107291Skeramida
835107291Skeramida	while ((i = nextopt("vf")) != '\0') {
836107291Skeramida		if (i == 'f')
83798943Sluigi			flg_func = 1;
838107291Skeramida		else
83998943Sluigi			flg_var = 1;
84098943Sluigi	}
84198943Sluigi	if (flg_func == 0 && flg_var == 0)
84298943Sluigi		flg_var = 1;
84398943Sluigi
844107291Skeramida	for (ap = argptr; *ap ; ap++) {
84598943Sluigi		if (flg_func)
84698943Sluigi			ret |= unsetfunc(*ap);
84798943Sluigi		if (flg_var)
848101628Sluigi			ret |= unsetvar(*ap);
849101628Sluigi	}
850101628Sluigi	return ret;
85198943Sluigi}
852107289Sluigi
853107289Sluigi
854107289Sluigi/*
855107289Sluigi * Unset the specified variable.
856107289Sluigi */
857107289Sluigi
858107289Sluigiint
859107289Sluigiunsetvar(const char *s)
860107289Sluigi{
861107289Sluigi	struct var **vpp;
862107289Sluigi	struct var *vp;
863107289Sluigi
864107289Sluigi	vp = find_var(s, &vpp, NULL);
865107289Sluigi	if (vp == NULL)
86698943Sluigi		return (0);
86798943Sluigi	if (vp->flags & VREADONLY)
86898943Sluigi		return (1);
86998943Sluigi	INTOFF;
87098943Sluigi	if (vp->text[vp->name_len + 1] != '\0')
87198943Sluigi		setvar(s, nullstr, 0);
87298943Sluigi	if ((vp->flags & VEXPORT) && localevar(vp->text)) {
873102087Sluigi		change_env(s, 0);
87498943Sluigi		setlocale(LC_ALL, "");
87598943Sluigi		updatecharset();
87698943Sluigi	}
87798943Sluigi	vp->flags &= ~VEXPORT;
87898943Sluigi	vp->flags |= VUNSET;
87998943Sluigi	if ((vp->flags & VSTRFIXED) == 0) {
88098943Sluigi		if ((vp->flags & VTEXTFIXED) == 0)
88198943Sluigi			ckfree(vp->text);
88298943Sluigi		*vpp = vp->next;
88398943Sluigi		ckfree(vp);
88498943Sluigi	}
88598943Sluigi	INTON;
88698943Sluigi	return (0);
88798943Sluigi}
88899475Sluigi
88999475Sluigi
89099475Sluigi
89199475Sluigi/*
89299475Sluigi * Returns true if the two strings specify the same varable.  The first
89399475Sluigi * variable name is terminated by '='; the second may be terminated by
89499475Sluigi * either '=' or '\0'.
89599475Sluigi */
89699475Sluigi
89798943Sluigistatic int
89898943Sluigivarequal(const char *p, const char *q)
89998943Sluigi{
90098943Sluigi	while (*p == *q++) {
90198943Sluigi		if (*p++ == '=')
90298943Sluigi			return 1;
90398943Sluigi	}
90498943Sluigi	if (*p == '=' && *(q - 1) == '\0')
90598943Sluigi		return 1;
90698943Sluigi	return 0;
90798943Sluigi}
90898943Sluigi
90998943Sluigi/*
91098943Sluigi * Search for a variable.
91198943Sluigi * 'name' may be terminated by '=' or a NUL.
91298943Sluigi * vppp is set to the pointer to vp, or the list head if vp isn't found
91398943Sluigi * lenp is set to the number of charactets in 'name'
91498943Sluigi */
91598943Sluigi
91698943Sluigistatic struct var *
91798943Sluigifind_var(const char *name, struct var ***vppp, int *lenp)
91898943Sluigi{
91998943Sluigi	unsigned int hashval;
92098943Sluigi	int len;
92198943Sluigi	struct var *vp, **vpp;
92298943Sluigi	const char *p = name;
923103241Sluigi
92498943Sluigi	hashval = 0;
92598943Sluigi	while (*p && *p != '=')
92698943Sluigi		hashval = 2 * hashval + (unsigned char)*p++;
92798943Sluigi	len = p - name;
92898943Sluigi
92998943Sluigi	if (lenp)
93098943Sluigi		*lenp = len;
93198943Sluigi	vpp = &vartab[hashval % VTABSIZE];
93298943Sluigi	if (vppp)
93398943Sluigi		*vppp = vpp;
93498943Sluigi
93598943Sluigi	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
93698943Sluigi		if (vp->name_len != len)
93798943Sluigi			continue;
93899909Sluigi		if (memcmp(vp->text, name, len) != 0)
93998943Sluigi			continue;
94099909Sluigi		if (vppp)
94198943Sluigi			*vppp = vpp;
942102087Sluigi		return vp;
94398943Sluigi	}
944102087Sluigi	return NULL;
94598943Sluigi}
946102087Sluigi