var.c revision 213811
1114402Sru/*-
2151497Sru * Copyright (c) 1991, 1993
3114402Sru *	The Regents of the University of California.  All rights reserved.
4114402Sru *
5114402Sru * This code is derived from software contributed to Berkeley by
6114402Sru * Kenneth Almquist.
7114402Sru *
8114402Sru * Redistribution and use in source and binary forms, with or without
9114402Sru * modification, are permitted provided that the following conditions
10114402Sru * are met:
11114402Sru * 1. Redistributions of source code must retain the above copyright
12114402Sru *    notice, this list of conditions and the following disclaimer.
13114402Sru * 2. Redistributions in binary form must reproduce the above copyright
14114402Sru *    notice, this list of conditions and the following disclaimer in the
15114402Sru *    documentation and/or other materials provided with the distribution.
16114402Sru * 4. Neither the name of the University nor the names of its contributors
17114402Sru *    may be used to endorse or promote products derived from this software
18114402Sru *    without specific prior written permission.
19114402Sru *
20151497Sru * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21114402Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22114402Sru * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23114402Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24114402Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25114402Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26114402Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27114402Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28151497Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29151497Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30114402Sru * SUCH DAMAGE.
31114402Sru */
32114402Sru
33114402Sru#ifndef lint
34114402Sru#if 0
35114402Srustatic char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
36114402Sru#endif
37151497Sru#endif /* not lint */
38151497Sru#include <sys/cdefs.h>
39114402Sru__FBSDID("$FreeBSD: head/bin/sh/var.c 213811 2010-10-13 22:18:03Z obrien $");
40114402Sru
41114402Sru#include <unistd.h>
42114402Sru#include <stdlib.h>
43114402Sru#include <paths.h>
44114402Sru
45114402Sru/*
46114402Sru * Shell variables.
47114402Sru */
48114402Sru
49114402Sru#include <locale.h>
50114402Sru
51114402Sru#include "shell.h"
52114402Sru#include "output.h"
53114402Sru#include "expand.h"
54114402Sru#include "nodes.h"	/* for other headers */
55114402Sru#include "eval.h"	/* defines cmdenviron */
56151497Sru#include "exec.h"
57151497Sru#include "syntax.h"
58151497Sru#include "options.h"
59114402Sru#include "mail.h"
60114402Sru#include "var.h"
61114402Sru#include "memalloc.h"
62114402Sru#include "error.h"
63114402Sru#include "mystring.h"
64114402Sru#include "parser.h"
65114402Sru#ifndef NO_HISTORY
66114402Sru#include "myhistedit.h"
67114402Sru#endif
68114402Sru
69114402Sru
70114402Sru#define VTABSIZE 39
71114402Sru
72114402Sru
73114402Srustruct varinit {
74114402Sru	struct var *var;
75114402Sru	int flags;
76114402Sru	const char *text;
77114402Sru	void (*func)(const char *);
78114402Sru};
79114402Sru
80114402Sru
81114402Sru#ifndef NO_HISTORY
82114402Srustruct var vhistsize;
83114402Srustruct var vterm;
84114402Sru#endif
85114402Srustruct var vifs;
86114402Srustruct var vmail;
87114402Srustruct var vmpath;
88114402Srustruct var vpath;
89114402Srustruct var vppid;
90114402Srustruct var vps1;
91114402Srustruct var vps2;
92114402Srustruct var vps4;
93114402Srustruct var vvers;
94114402Srustatic struct var voptind;
95114402Sru
96114402Srustatic const struct varinit varinit[] = {
97114402Sru#ifndef NO_HISTORY
98114402Sru	{ &vhistsize,	VUNSET,				"HISTSIZE=",
99114402Sru	  sethistsize },
100114402Sru#endif
101114402Sru	{ &vifs,	0,				"IFS= \t\n",
102114402Sru	  NULL },
103114402Sru	{ &vmail,	VUNSET,				"MAIL=",
104151497Sru	  NULL },
105151497Sru	{ &vmpath,	VUNSET,				"MAILPATH=",
106151497Sru	  NULL },
107151497Sru	{ &vpath,	0,				"PATH=" _PATH_DEFPATH,
108114402Sru	  changepath },
109114402Sru	{ &vppid,	VUNSET,				"PPID=",
110114402Sru	  NULL },
111114402Sru	/*
112114402Sru	 * vps1 depends on uid
113114402Sru	 */
114114402Sru	{ &vps2,	0,				"PS2=> ",
115114402Sru	  NULL },
116114402Sru	{ &vps4,	0,				"PS4=+ ",
117114402Sru	  NULL },
118114402Sru#ifndef NO_HISTORY
119114402Sru	{ &vterm,	VUNSET,				"TERM=",
120114402Sru	  setterm },
121114402Sru#endif
122114402Sru	{ &voptind,	0,				"OPTIND=1",
123114402Sru	  getoptsreset },
124114402Sru	{ NULL,	0,				NULL,
125114402Sru	  NULL }
126114402Sru};
127151497Sru
128151497Srustatic struct var *vartab[VTABSIZE];
129151497Sru
130151497Srustatic const char *const locale_names[7] = {
131151497Sru	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
132151497Sru	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
133151497Sru};
134114402Srustatic const int locale_categories[7] = {
135114402Sru	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
136114402Sru};
137114402Sru
138114402Srustatic struct var **hashvar(const char *);
139114402Srustatic int varequal(const char *, const char *);
140114402Srustatic int localevar(const char *);
141114402Sru
142114402Sru/*
143114402Sru * Initialize the variable symbol tables and import the environment.
144114402Sru */
145114402Sru
146114402Sru#ifdef mkinit
147114402SruINCLUDE "var.h"
148114402SruMKINIT char **environ;
149114402SruINIT {
150114402Sru	char **envp;
151114402Sru
152114402Sru	initvar();
153114402Sru	for (envp = environ ; *envp ; envp++) {
154114402Sru		if (strchr(*envp, '=')) {
155114402Sru			setvareq(*envp, VEXPORT|VTEXTFIXED);
156114402Sru		}
157114402Sru	}
158114402Sru}
159114402Sru#endif
160114402Sru
161114402Sru
162114402Sru/*
163114402Sru * This routine initializes the builtin variables.  It is called when the
164114402Sru * shell is initialized and again when a shell procedure is spawned.
165114402Sru */
166114402Sru
167114402Sruvoid
168114402Sruinitvar(void)
169114402Sru{
170114402Sru	char ppid[20];
171114402Sru	const struct varinit *ip;
172114402Sru	struct var *vp;
173114402Sru	struct var **vpp;
174114402Sru
175114402Sru	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
176114402Sru		if ((vp->flags & VEXPORT) == 0) {
177114402Sru			vpp = hashvar(ip->text);
178114402Sru			vp->next = *vpp;
179114402Sru			*vpp = vp;
180114402Sru			vp->text = __DECONST(char *, ip->text);
181114402Sru			vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
182114402Sru			vp->func = ip->func;
183114402Sru		}
184114402Sru	}
185114402Sru	/*
186114402Sru	 * PS1 depends on uid
187114402Sru	 */
188114402Sru	if ((vps1.flags & VEXPORT) == 0) {
189114402Sru		vpp = hashvar("PS1=");
190114402Sru		vps1.next = *vpp;
191114402Sru		*vpp = &vps1;
192114402Sru		vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
193114402Sru		vps1.flags = VSTRFIXED|VTEXTFIXED;
194114402Sru	}
195114402Sru	if ((vppid.flags & VEXPORT) == 0) {
196114402Sru		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
197114402Sru		setvarsafe("PPID", ppid, 0);
198114402Sru	}
199114402Sru}
200114402Sru
201114402Sru/*
202114402Sru * Safe version of setvar, returns 1 on success 0 on failure.
203114402Sru */
204114402Sru
205114402Sruint
206114402Srusetvarsafe(const char *name, const char *val, int flags)
207114402Sru{
208114402Sru	struct jmploc jmploc;
209114402Sru	struct jmploc *const savehandler = handler;
210114402Sru	int err = 0;
211114402Sru	int inton;
212114402Sru
213114402Sru	inton = is_int_on();
214114402Sru	if (setjmp(jmploc.loc))
215114402Sru		err = 1;
216114402Sru	else {
217114402Sru		handler = &jmploc;
218114402Sru		setvar(name, val, flags);
219114402Sru	}
220151497Sru	handler = savehandler;
221114402Sru	SETINTON(inton);
222114402Sru	return err;
223114402Sru}
224114402Sru
225114402Sru/*
226114402Sru * Set the value of a variable.  The flags argument is stored with the
227114402Sru * flags of the variable.  If val is NULL, the variable is unset.
228114402Sru */
229114402Sru
230114402Sruvoid
231114402Srusetvar(const char *name, const char *val, int flags)
232114402Sru{
233114402Sru	const char *p;
234114402Sru	int len;
235114402Sru	int namelen;
236114402Sru	char *nameeq;
237114402Sru	int isbad;
238114402Sru
239114402Sru	isbad = 0;
240114402Sru	p = name;
241114402Sru	if (! is_name(*p))
242114402Sru		isbad = 1;
243114402Sru	p++;
244114402Sru	for (;;) {
245114402Sru		if (! is_in_name(*p)) {
246114402Sru			if (*p == '\0' || *p == '=')
247114402Sru				break;
248114402Sru			isbad = 1;
249114402Sru		}
250114402Sru		p++;
251114402Sru	}
252114402Sru	namelen = p - name;
253114402Sru	if (isbad)
254114402Sru		error("%.*s: bad variable name", namelen, name);
255114402Sru	len = namelen + 2;		/* 2 is space for '=' and '\0' */
256114402Sru	if (val == NULL) {
257114402Sru		flags |= VUNSET;
258114402Sru	} else {
259114402Sru		len += strlen(val);
260114402Sru	}
261114402Sru	nameeq = ckmalloc(len);
262114402Sru	memcpy(nameeq, name, namelen);
263114402Sru	nameeq[namelen] = '=';
264114402Sru	if (val)
265114402Sru		scopy(val, nameeq + namelen + 1);
266114402Sru	else
267114402Sru		nameeq[namelen + 1] = '\0';
268114402Sru	setvareq(nameeq, flags);
269114402Sru}
270114402Sru
271114402Srustatic int
272114402Srulocalevar(const char *s)
273114402Sru{
274114402Sru	const char *const *ss;
275151497Sru
276114402Sru	if (*s != 'L')
277114402Sru		return 0;
278114402Sru	if (varequal(s + 1, "ANG"))
279114402Sru		return 1;
280114402Sru	if (strncmp(s + 1, "C_", 2) != 0)
281114402Sru		return 0;
282114402Sru	if (varequal(s + 3, "ALL"))
283114402Sru		return 1;
284114402Sru	for (ss = locale_names; *ss ; ss++)
285114402Sru		if (varequal(s + 3, *ss + 3))
286114402Sru			return 1;
287114402Sru	return 0;
288114402Sru}
289114402Sru
290114402Sru
291114402Sru/*
292114402Sru * Sets/unsets an environment variable from a pointer that may actually be a
293114402Sru * pointer into environ where the string should not be manipulated.
294114402Sru */
295114402Srustatic void
296114402Sruchange_env(const char *s, int set)
297114402Sru{
298114402Sru	char *eqp;
299114402Sru	char *ss;
300114402Sru
301114402Sru	ss = savestr(s);
302114402Sru	if ((eqp = strchr(ss, '=')) != NULL)
303114402Sru		*eqp = '\0';
304114402Sru	if (set && eqp != NULL)
305114402Sru		(void) setenv(ss, eqp + 1, 1);
306114402Sru	else
307114402Sru		(void) unsetenv(ss);
308114402Sru	ckfree(ss);
309114402Sru
310114402Sru	return;
311114402Sru}
312114402Sru
313114402Sru
314114402Sru/*
315114402Sru * Same as setvar except that the variable and value are passed in
316114402Sru * the first argument as name=value.  Since the first argument will
317114402Sru * be actually stored in the table, it should not be a string that
318114402Sru * will go away.
319114402Sru */
320114402Sru
321114402Sruvoid
322114402Srusetvareq(char *s, int flags)
323114402Sru{
324114402Sru	struct var *vp, **vpp;
325114402Sru	int len;
326114402Sru
327114402Sru	if (aflag)
328114402Sru		flags |= VEXPORT;
329114402Sru	vpp = hashvar(s);
330114402Sru	for (vp = *vpp ; vp ; vp = vp->next) {
331114402Sru		if (varequal(s, vp->text)) {
332114402Sru			if (vp->flags & VREADONLY) {
333114402Sru				len = strchr(s, '=') - s;
334114402Sru				error("%.*s: is read only", len, s);
335114402Sru			}
336114402Sru			INTOFF;
337114402Sru
338114402Sru			if (vp->func && (flags & VNOFUNC) == 0)
339114402Sru				(*vp->func)(strchr(s, '=') + 1);
340114402Sru
341114402Sru			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
342114402Sru				ckfree(vp->text);
343114402Sru
344114402Sru			vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
345114402Sru			vp->flags |= flags;
346114402Sru			vp->text = s;
347114402Sru
348114402Sru			/*
349114402Sru			 * We could roll this to a function, to handle it as
350114402Sru			 * a regular variable function callback, but why bother?
351114402Sru			 *
352114402Sru			 * Note: this assumes iflag is not set to 1 initially.
353114402Sru			 * As part of init(), this is called before arguments
354114402Sru			 * are looked at.
355114402Sru			 */
356114402Sru			if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
357114402Sru			    iflag == 1)
358114402Sru				chkmail(1);
359114402Sru			if ((vp->flags & VEXPORT) && localevar(s)) {
360114402Sru				change_env(s, 1);
361114402Sru				(void) setlocale(LC_ALL, "");
362114402Sru			}
363114402Sru			INTON;
364114402Sru			return;
365114402Sru		}
366114402Sru	}
367114402Sru	/* not found */
368114402Sru	vp = ckmalloc(sizeof (*vp));
369114402Sru	vp->flags = flags;
370114402Sru	vp->text = s;
371114402Sru	vp->next = *vpp;
372114402Sru	vp->func = NULL;
373114402Sru	INTOFF;
374114402Sru	*vpp = vp;
375114402Sru	if ((vp->flags & VEXPORT) && localevar(s)) {
376114402Sru		change_env(s, 1);
377114402Sru		(void) setlocale(LC_ALL, "");
378114402Sru	}
379114402Sru	INTON;
380114402Sru}
381114402Sru
382114402Sru
383114402Sru
384114402Sru/*
385114402Sru * Process a linked list of variable assignments.
386114402Sru */
387114402Sru
388114402Sruvoid
389114402Srulistsetvar(struct strlist *list)
390114402Sru{
391114402Sru	struct strlist *lp;
392114402Sru
393114402Sru	INTOFF;
394114402Sru	for (lp = list ; lp ; lp = lp->next) {
395114402Sru		setvareq(savestr(lp->text), 0);
396114402Sru	}
397114402Sru	INTON;
398114402Sru}
399114402Sru
400114402Sru
401114402Sru
402114402Sru/*
403114402Sru * Find the value of a variable.  Returns NULL if not set.
404114402Sru */
405114402Sru
406114402Sruchar *
407114402Srulookupvar(const char *name)
408114402Sru{
409114402Sru	struct var *v;
410114402Sru
411114402Sru	for (v = *hashvar(name) ; v ; v = v->next) {
412114402Sru		if (varequal(v->text, name)) {
413114402Sru			if (v->flags & VUNSET)
414114402Sru				return NULL;
415114402Sru			return strchr(v->text, '=') + 1;
416114402Sru		}
417114402Sru	}
418114402Sru	return NULL;
419114402Sru}
420114402Sru
421114402Sru
422114402Sru
423114402Sru/*
424114402Sru * Search the environment of a builtin command.  If the second argument
425114402Sru * is nonzero, return the value of a variable even if it hasn't been
426114402Sru * exported.
427114402Sru */
428114402Sru
429114402Sruchar *
430114402Srubltinlookup(const char *name, int doall)
431114402Sru{
432114402Sru	struct strlist *sp;
433114402Sru	struct var *v;
434114402Sru	char *result;
435114402Sru
436114402Sru	result = NULL;
437114402Sru	for (sp = cmdenviron ; sp ; sp = sp->next) {
438114402Sru		if (varequal(sp->text, name))
439114402Sru			result = strchr(sp->text, '=') + 1;
440114402Sru	}
441114402Sru	if (result != NULL)
442114402Sru		return result;
443114402Sru	for (v = *hashvar(name) ; v ; v = v->next) {
444114402Sru		if (varequal(v->text, name)) {
445114402Sru			if ((v->flags & VUNSET)
446114402Sru			 || (!doall && (v->flags & VEXPORT) == 0))
447114402Sru				return NULL;
448114402Sru			return strchr(v->text, '=') + 1;
449114402Sru		}
450114402Sru	}
451114402Sru	return NULL;
452114402Sru}
453114402Sru
454114402Sru
455114402Sru/*
456114402Sru * Set up locale for a builtin (LANG/LC_* assignments).
457114402Sru */
458114402Sruvoid
459114402Srubltinsetlocale(void)
460114402Sru{
461114402Sru	struct strlist *lp;
462151497Sru	int act = 0;
463151497Sru	char *loc, *locdef;
464151497Sru	int i;
465114402Sru
466114402Sru	for (lp = cmdenviron ; lp ; lp = lp->next) {
467114402Sru		if (localevar(lp->text)) {
468114402Sru			act = 1;
469114402Sru			break;
470114402Sru		}
471114402Sru	}
472114402Sru	if (!act)
473114402Sru		return;
474114402Sru	loc = bltinlookup("LC_ALL", 0);
475114402Sru	INTOFF;
476114402Sru	if (loc != NULL) {
477114402Sru		setlocale(LC_ALL, loc);
478114402Sru		INTON;
479114402Sru		return;
480114402Sru	}
481114402Sru	locdef = bltinlookup("LANG", 0);
482114402Sru	for (i = 0; locale_names[i] != NULL; i++) {
483114402Sru		loc = bltinlookup(locale_names[i], 0);
484114402Sru		if (loc == NULL)
485114402Sru			loc = locdef;
486114402Sru		if (loc != NULL)
487114402Sru			setlocale(locale_categories[i], loc);
488151497Sru	}
489114402Sru	INTON;
490114402Sru}
491114402Sru
492114402Sru/*
493114402Sru * Undo the effect of bltinlocaleset().
494114402Sru */
495114402Sruvoid
496114402Srubltinunsetlocale(void)
497114402Sru{
498151497Sru	struct strlist *lp;
499114402Sru
500114402Sru	INTOFF;
501114402Sru	for (lp = cmdenviron ; lp ; lp = lp->next) {
502114402Sru		if (localevar(lp->text)) {
503151497Sru			setlocale(LC_ALL, "");
504114402Sru			return;
505114402Sru		}
506114402Sru	}
507151497Sru	INTON;
508114402Sru}
509114402Sru
510151497Sru
511114402Sru/*
512114402Sru * Generate a list of exported variables.  This routine is used to construct
513151497Sru * the third argument to execve when executing a program.
514114402Sru */
515114402Sru
516114402Sruchar **
517114402Sruenvironment(void)
518114402Sru{
519114402Sru	int nenv;
520114402Sru	struct var **vpp;
521114402Sru	struct var *vp;
522114402Sru	char **env, **ep;
523114402Sru
524114402Sru	nenv = 0;
525114402Sru	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
526114402Sru		for (vp = *vpp ; vp ; vp = vp->next)
527114402Sru			if (vp->flags & VEXPORT)
528151497Sru				nenv++;
529114402Sru	}
530114402Sru	ep = env = stalloc((nenv + 1) * sizeof *env);
531151497Sru	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
532114402Sru		for (vp = *vpp ; vp ; vp = vp->next)
533114402Sru			if (vp->flags & VEXPORT)
534114402Sru				*ep++ = vp->text;
535114402Sru	}
536114402Sru	*ep = NULL;
537114402Sru	return env;
538114402Sru}
539114402Sru
540114402Sru
541114402Sru/*
542114402Sru * Called when a shell procedure is invoked to clear out nonexported
543114402Sru * variables.  It is also necessary to reallocate variables of with
544114402Sru * VSTACK set since these are currently allocated on the stack.
545114402Sru */
546114402Sru
547114402SruMKINIT void shprocvar(void);
548114402Sru
549114402Sru#ifdef mkinit
550114402SruSHELLPROC {
551114402Sru	shprocvar();
552114402Sru}
553114402Sru#endif
554114402Sru
555114402Sruvoid
556114402Srushprocvar(void)
557114402Sru{
558114402Sru	struct var **vpp;
559114402Sru	struct var *vp, **prev;
560114402Sru
561114402Sru	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
562114402Sru		for (prev = vpp ; (vp = *prev) != NULL ; ) {
563114402Sru			if ((vp->flags & VEXPORT) == 0) {
564151497Sru				*prev = vp->next;
565114402Sru				if ((vp->flags & VTEXTFIXED) == 0)
566114402Sru					ckfree(vp->text);
567114402Sru				if ((vp->flags & VSTRFIXED) == 0)
568114402Sru					ckfree(vp);
569114402Sru			} else {
570114402Sru				if (vp->flags & VSTACK) {
571114402Sru					vp->text = savestr(vp->text);
572114402Sru					vp->flags &=~ VSTACK;
573114402Sru				}
574114402Sru				prev = &vp->next;
575114402Sru			}
576114402Sru		}
577114402Sru	}
578114402Sru	initvar();
579114402Sru}
580114402Sru
581114402Sru
582114402Srustatic int
583114402Sruvar_compare(const void *a, const void *b)
584114402Sru{
585114402Sru	const char *const *sa, *const *sb;
586114402Sru
587114402Sru	sa = a;
588114402Sru	sb = b;
589114402Sru	/*
590114402Sru	 * This compares two var=value strings which creates a different
591114402Sru	 * order from what you would probably expect.  POSIX is somewhat
592114402Sru	 * ambiguous on what should be sorted exactly.
593114402Sru	 */
594114402Sru	return strcoll(*sa, *sb);
595114402Sru}
596114402Sru
597114402Sru
598114402Sru/*
599114402Sru * Command to list all variables which are set.  Currently this command
600114402Sru * is invoked from the set command when the set command is called without
601114402Sru * any variables.
602114402Sru */
603114402Sru
604114402Sruint
605114402Srushowvarscmd(int argc __unused, char **argv __unused)
606114402Sru{
607114402Sru	struct var **vpp;
608114402Sru	struct var *vp;
609114402Sru	const char *s;
610114402Sru	const char **vars;
611114402Sru	int i, n;
612114402Sru
613114402Sru	/*
614114402Sru	 * POSIX requires us to sort the variables.
615114402Sru	 */
616114402Sru	n = 0;
617114402Sru	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
618114402Sru		for (vp = *vpp; vp; vp = vp->next) {
619114402Sru			if (!(vp->flags & VUNSET))
620114402Sru				n++;
621114402Sru		}
622114402Sru	}
623114402Sru
624114402Sru	INTON;
625114402Sru	vars = ckmalloc(n * sizeof(*vars));
626114402Sru	i = 0;
627114402Sru	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
628114402Sru		for (vp = *vpp; vp; vp = vp->next) {
629114402Sru			if (!(vp->flags & VUNSET))
630114402Sru				vars[i++] = vp->text;
631114402Sru		}
632114402Sru	}
633114402Sru
634114402Sru	qsort(vars, n, sizeof(*vars), var_compare);
635114402Sru	for (i = 0; i < n; i++) {
636114402Sru		for (s = vars[i]; *s != '='; s++)
637114402Sru			out1c(*s);
638114402Sru		out1c('=');
639114402Sru		out1qstr(s + 1);
640114402Sru		out1c('\n');
641114402Sru	}
642114402Sru	ckfree(vars);
643114402Sru	INTOFF;
644114402Sru
645114402Sru	return 0;
646114402Sru}
647114402Sru
648114402Sru
649114402Sru
650114402Sru/*
651114402Sru * The export and readonly commands.
652114402Sru */
653114402Sru
654114402Sruint
655114402Sruexportcmd(int argc, char **argv)
656114402Sru{
657114402Sru	struct var **vpp;
658114402Sru	struct var *vp;
659151497Sru	char *name;
660114402Sru	char *p;
661114402Sru	char *cmdname;
662114402Sru	int ch, values;
663114402Sru	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
664114402Sru
665114402Sru	cmdname = argv[0];
666114402Sru	optreset = optind = 1;
667114402Sru	opterr = 0;
668114402Sru	values = 0;
669114402Sru	while ((ch = getopt(argc, argv, "p")) != -1) {
670114402Sru		switch (ch) {
671114402Sru		case 'p':
672114402Sru			values = 1;
673114402Sru			break;
674114402Sru		case '?':
675114402Sru		default:
676114402Sru			error("unknown option: -%c", optopt);
677114402Sru		}
678114402Sru	}
679114402Sru	argc -= optind;
680114402Sru	argv += optind;
681114402Sru
682114402Sru	if (values && argc != 0)
683114402Sru		error("-p requires no arguments");
684114402Sru	if (argc != 0) {
685114402Sru		while ((name = *argv++) != NULL) {
686114402Sru			if ((p = strchr(name, '=')) != NULL) {
687114402Sru				p++;
688114402Sru			} else {
689151497Sru				vpp = hashvar(name);
690114402Sru				for (vp = *vpp ; vp ; vp = vp->next) {
691114402Sru					if (varequal(vp->text, name)) {
692114402Sru
693114402Sru						vp->flags |= flag;
694114402Sru						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
695114402Sru							change_env(vp->text, 1);
696114402Sru							(void) setlocale(LC_ALL, "");
697114402Sru						}
698114402Sru						goto found;
699114402Sru					}
700114402Sru				}
701114402Sru			}
702114402Sru			setvar(name, p, flag);
703114402Srufound:;
704114402Sru		}
705114402Sru	} else {
706114402Sru		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
707114402Sru			for (vp = *vpp ; vp ; vp = vp->next) {
708114402Sru				if (vp->flags & flag) {
709114402Sru					if (values) {
710114402Sru						out1str(cmdname);
711114402Sru						out1c(' ');
712114402Sru					}
713114402Sru					for (p = vp->text ; *p != '=' ; p++)
714114402Sru						out1c(*p);
715114402Sru					if (values && !(vp->flags & VUNSET)) {
716114402Sru						out1c('=');
717114402Sru						out1qstr(p + 1);
718114402Sru					}
719114402Sru					out1c('\n');
720114402Sru				}
721114402Sru			}
722114402Sru		}
723114402Sru	}
724114402Sru	return 0;
725114402Sru}
726114402Sru
727114402Sru
728114402Sru/*
729114402Sru * The "local" command.
730114402Sru */
731114402Sru
732114402Sruint
733114402Srulocalcmd(int argc __unused, char **argv __unused)
734114402Sru{
735114402Sru	char *name;
736114402Sru
737114402Sru	if (! in_function())
738114402Sru		error("Not in a function");
739114402Sru	while ((name = *argptr++) != NULL) {
740114402Sru		mklocal(name);
741114402Sru	}
742114402Sru	return 0;
743114402Sru}
744114402Sru
745114402Sru
746114402Sru/*
747114402Sru * Make a variable a local variable.  When a variable is made local, it's
748114402Sru * value and flags are saved in a localvar structure.  The saved values
749114402Sru * will be restored when the shell function returns.  We handle the name
750114402Sru * "-" as a special case.
751114402Sru */
752151497Sru
753151497Sruvoid
754114402Srumklocal(char *name)
755114402Sru{
756151497Sru	struct localvar *lvp;
757114402Sru	struct var **vpp;
758114402Sru	struct var *vp;
759114402Sru
760114402Sru	INTOFF;
761114402Sru	lvp = ckmalloc(sizeof (struct localvar));
762114402Sru	if (name[0] == '-' && name[1] == '\0') {
763114402Sru		lvp->text = ckmalloc(sizeof optlist);
764114402Sru		memcpy(lvp->text, optlist, sizeof optlist);
765114402Sru		vp = NULL;
766114402Sru	} else {
767114402Sru		vpp = hashvar(name);
768114402Sru		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
769114402Sru		if (vp == NULL) {
770114402Sru			if (strchr(name, '='))
771114402Sru				setvareq(savestr(name), VSTRFIXED);
772114402Sru			else
773114402Sru				setvar(name, NULL, VSTRFIXED);
774114402Sru			vp = *vpp;	/* the new variable */
775114402Sru			lvp->text = NULL;
776114402Sru			lvp->flags = VUNSET;
777114402Sru		} else {
778151497Sru			lvp->text = vp->text;
779114402Sru			lvp->flags = vp->flags;
780114402Sru			vp->flags |= VSTRFIXED|VTEXTFIXED;
781114402Sru			if (strchr(name, '='))
782114402Sru				setvareq(savestr(name), 0);
783114402Sru		}
784114402Sru	}
785151497Sru	lvp->vp = vp;
786114402Sru	lvp->next = localvars;
787114402Sru	localvars = lvp;
788114402Sru	INTON;
789114402Sru}
790114402Sru
791114402Sru
792114402Sru/*
793114402Sru * Called after a function returns.
794114402Sru */
795114402Sru
796114402Sruvoid
797114402Srupoplocalvars(void)
798114402Sru{
799114402Sru	struct localvar *lvp;
800114402Sru	struct var *vp;
801114402Sru
802114402Sru	while ((lvp = localvars) != NULL) {
803114402Sru		localvars = lvp->next;
804114402Sru		vp = lvp->vp;
805114402Sru		if (vp == NULL) {	/* $- saved */
806114402Sru			memcpy(optlist, lvp->text, sizeof optlist);
807114402Sru			ckfree(lvp->text);
808114402Sru		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
809114402Sru			(void)unsetvar(vp->text);
810114402Sru		} else {
811114402Sru			if ((vp->flags & VTEXTFIXED) == 0)
812114402Sru				ckfree(vp->text);
813114402Sru			vp->flags = lvp->flags;
814114402Sru			vp->text = lvp->text;
815114402Sru		}
816114402Sru		ckfree(lvp);
817114402Sru	}
818114402Sru}
819114402Sru
820114402Sru
821114402Sruint
822114402Srusetvarcmd(int argc, char **argv)
823114402Sru{
824114402Sru	if (argc <= 2)
825114402Sru		return unsetcmd(argc, argv);
826114402Sru	else if (argc == 3)
827114402Sru		setvar(argv[1], argv[2], 0);
828114402Sru	else
829114402Sru		error("List assignment not implemented");
830114402Sru	return 0;
831114402Sru}
832114402Sru
833114402Sru
834114402Sru/*
835114402Sru * The unset builtin command.  We unset the function before we unset the
836114402Sru * variable to allow a function to be unset when there is a readonly variable
837114402Sru * with the same name.
838114402Sru */
839114402Sru
840114402Sruint
841114402Sruunsetcmd(int argc __unused, char **argv __unused)
842114402Sru{
843114402Sru	char **ap;
844114402Sru	int i;
845114402Sru	int flg_func = 0;
846114402Sru	int flg_var = 0;
847114402Sru	int ret = 0;
848114402Sru
849114402Sru	while ((i = nextopt("vf")) != '\0') {
850114402Sru		if (i == 'f')
851114402Sru			flg_func = 1;
852114402Sru		else
853114402Sru			flg_var = 1;
854114402Sru	}
855114402Sru	if (flg_func == 0 && flg_var == 0)
856114402Sru		flg_var = 1;
857114402Sru
858114402Sru	for (ap = argptr; *ap ; ap++) {
859114402Sru		if (flg_func)
860114402Sru			ret |= unsetfunc(*ap);
861114402Sru		if (flg_var)
862114402Sru			ret |= unsetvar(*ap);
863114402Sru	}
864114402Sru	return ret;
865114402Sru}
866114402Sru
867114402Sru
868114402Sru/*
869114402Sru * Unset the specified variable.
870114402Sru */
871114402Sru
872114402Sruint
873114402Sruunsetvar(const char *s)
874114402Sru{
875114402Sru	struct var **vpp;
876114402Sru	struct var *vp;
877114402Sru
878114402Sru	vpp = hashvar(s);
879114402Sru	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
880114402Sru		if (varequal(vp->text, s)) {
881114402Sru			if (vp->flags & VREADONLY)
882114402Sru				return (1);
883114402Sru			INTOFF;
884114402Sru			if (*(strchr(vp->text, '=') + 1) != '\0')
885114402Sru				setvar(s, nullstr, 0);
886114402Sru			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
887114402Sru				change_env(s, 0);
888114402Sru				setlocale(LC_ALL, "");
889114402Sru			}
890114402Sru			vp->flags &= ~VEXPORT;
891114402Sru			vp->flags |= VUNSET;
892114402Sru			if ((vp->flags & VSTRFIXED) == 0) {
893114402Sru				if ((vp->flags & VTEXTFIXED) == 0)
894114402Sru					ckfree(vp->text);
895114402Sru				*vpp = vp->next;
896114402Sru				ckfree(vp);
897114402Sru			}
898114402Sru			INTON;
899114402Sru			return (0);
900114402Sru		}
901114402Sru	}
902114402Sru
903114402Sru	return (0);
904114402Sru}
905114402Sru
906114402Sru
907114402Sru
908114402Sru/*
909114402Sru * Find the appropriate entry in the hash table from the name.
910114402Sru */
911114402Sru
912114402Srustatic struct var **
913114402Sruhashvar(const char *p)
914114402Sru{
915114402Sru	unsigned int hashval;
916114402Sru
917114402Sru	hashval = ((unsigned char) *p) << 4;
918114402Sru	while (*p && *p != '=')
919114402Sru		hashval += (unsigned char) *p++;
920114402Sru	return &vartab[hashval % VTABSIZE];
921114402Sru}
922114402Sru
923114402Sru
924114402Sru
925114402Sru/*
926114402Sru * Returns true if the two strings specify the same varable.  The first
927114402Sru * variable name is terminated by '='; the second may be terminated by
928114402Sru * either '=' or '\0'.
929114402Sru */
930114402Sru
931114402Srustatic int
932114402Sruvarequal(const char *p, const char *q)
933114402Sru{
934114402Sru	while (*p == *q++) {
935114402Sru		if (*p++ == '=')
936114402Sru			return 1;
937114402Sru	}
938114402Sru	if (*p == '=' && *(q - 1) == '\0')
939114402Sru		return 1;
940114402Sru	return 0;
941114402Sru}
942114402Sru