1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34#if 0
35static char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
36#endif
37#endif /* not lint */
38#include <sys/cdefs.h>
39__FBSDID("$FreeBSD$");
40
41#include <unistd.h>
42#include <stdlib.h>
43#include <paths.h>
44
45/*
46 * Shell variables.
47 */
48
49#include <locale.h>
50#include <langinfo.h>
51
52#include "shell.h"
53#include "output.h"
54#include "expand.h"
55#include "nodes.h"	/* for other headers */
56#include "eval.h"	/* defines cmdenviron */
57#include "exec.h"
58#include "syntax.h"
59#include "options.h"
60#include "mail.h"
61#include "var.h"
62#include "memalloc.h"
63#include "error.h"
64#include "mystring.h"
65#include "parser.h"
66#include "builtins.h"
67#ifndef NO_HISTORY
68#include "myhistedit.h"
69#endif
70
71
72#define VTABSIZE 39
73
74
75struct varinit {
76	struct var *var;
77	int flags;
78	const char *text;
79	void (*func)(const char *);
80};
81
82
83#ifndef NO_HISTORY
84struct var vhistsize;
85struct var vterm;
86#endif
87struct var vifs;
88struct var vmail;
89struct var vmpath;
90struct var vpath;
91struct var vppid;
92struct var vps1;
93struct var vps2;
94struct var vps4;
95struct var vvers;
96static struct var voptind;
97struct var vdisvfork;
98
99int forcelocal;
100
101static const struct varinit varinit[] = {
102#ifndef NO_HISTORY
103	{ &vhistsize,	VUNSET,				"HISTSIZE=",
104	  sethistsize },
105#endif
106	{ &vifs,	0,				"IFS= \t\n",
107	  NULL },
108	{ &vmail,	VUNSET,				"MAIL=",
109	  NULL },
110	{ &vmpath,	VUNSET,				"MAILPATH=",
111	  NULL },
112	{ &vpath,	0,				"PATH=" _PATH_DEFPATH,
113	  changepath },
114	{ &vppid,	VUNSET,				"PPID=",
115	  NULL },
116	/*
117	 * vps1 depends on uid
118	 */
119	{ &vps2,	0,				"PS2=> ",
120	  NULL },
121	{ &vps4,	0,				"PS4=+ ",
122	  NULL },
123#ifndef NO_HISTORY
124	{ &vterm,	VUNSET,				"TERM=",
125	  setterm },
126#endif
127	{ &voptind,	0,				"OPTIND=1",
128	  getoptsreset },
129	{ &vdisvfork,	VUNSET,				"SH_DISABLE_VFORK=",
130	  NULL },
131	{ NULL,	0,				NULL,
132	  NULL }
133};
134
135static struct var *vartab[VTABSIZE];
136
137static const char *const locale_names[7] = {
138	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
139	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
140};
141static const int locale_categories[7] = {
142	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
143};
144
145static int varequal(const char *, const char *);
146static struct var *find_var(const char *, struct var ***, int *);
147static int localevar(const char *);
148
149/*
150 * Initialize the variable symbol tables and import the environment.
151 */
152
153#ifdef mkinit
154INCLUDE "var.h"
155MKINIT char **environ;
156INIT {
157	char **envp;
158
159	initvar();
160	for (envp = environ ; *envp ; envp++) {
161		if (strchr(*envp, '=')) {
162			setvareq(*envp, VEXPORT|VTEXTFIXED);
163		}
164	}
165}
166#endif
167
168
169/*
170 * This routine initializes the builtin variables.  It is called when the
171 * shell is initialized.
172 */
173
174void
175initvar(void)
176{
177	char ppid[20];
178	const struct varinit *ip;
179	struct var *vp;
180	struct var **vpp;
181
182	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
183		if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
184			continue;
185		vp->next = *vpp;
186		*vpp = vp;
187		vp->text = __DECONST(char *, ip->text);
188		vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
189		vp->func = ip->func;
190	}
191	/*
192	 * PS1 depends on uid
193	 */
194	if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
195		vps1.next = *vpp;
196		*vpp = &vps1;
197		vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
198		vps1.flags = VSTRFIXED|VTEXTFIXED;
199	}
200	if ((vppid.flags & VEXPORT) == 0) {
201		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
202		setvarsafe("PPID", ppid, 0);
203	}
204}
205
206/*
207 * Safe version of setvar, returns 1 on success 0 on failure.
208 */
209
210int
211setvarsafe(const char *name, const char *val, int flags)
212{
213	struct jmploc jmploc;
214	struct jmploc *const savehandler = handler;
215	int err = 0;
216	int inton;
217
218	inton = is_int_on();
219	if (setjmp(jmploc.loc))
220		err = 1;
221	else {
222		handler = &jmploc;
223		setvar(name, val, flags);
224	}
225	handler = savehandler;
226	SETINTON(inton);
227	return err;
228}
229
230/*
231 * Set the value of a variable.  The flags argument is stored with the
232 * flags of the variable.  If val is NULL, the variable is unset.
233 */
234
235void
236setvar(const char *name, const char *val, int flags)
237{
238	const char *p;
239	int len;
240	int namelen;
241	char *nameeq;
242	int isbad;
243
244	isbad = 0;
245	p = name;
246	if (! is_name(*p))
247		isbad = 1;
248	p++;
249	for (;;) {
250		if (! is_in_name(*p)) {
251			if (*p == '\0' || *p == '=')
252				break;
253			isbad = 1;
254		}
255		p++;
256	}
257	namelen = p - name;
258	if (isbad)
259		error("%.*s: bad variable name", namelen, name);
260	len = namelen + 2;		/* 2 is space for '=' and '\0' */
261	if (val == NULL) {
262		flags |= VUNSET;
263	} else {
264		len += strlen(val);
265	}
266	INTOFF;
267	nameeq = ckmalloc(len);
268	memcpy(nameeq, name, namelen);
269	nameeq[namelen] = '=';
270	if (val)
271		scopy(val, nameeq + namelen + 1);
272	else
273		nameeq[namelen + 1] = '\0';
274	setvareq(nameeq, flags);
275	INTON;
276}
277
278static int
279localevar(const char *s)
280{
281	const char *const *ss;
282
283	if (*s != 'L')
284		return 0;
285	if (varequal(s + 1, "ANG"))
286		return 1;
287	if (strncmp(s + 1, "C_", 2) != 0)
288		return 0;
289	if (varequal(s + 3, "ALL"))
290		return 1;
291	for (ss = locale_names; *ss ; ss++)
292		if (varequal(s + 3, *ss + 3))
293			return 1;
294	return 0;
295}
296
297
298/*
299 * Sets/unsets an environment variable from a pointer that may actually be a
300 * pointer into environ where the string should not be manipulated.
301 */
302static void
303change_env(const char *s, int set)
304{
305	char *eqp;
306	char *ss;
307
308	INTOFF;
309	ss = savestr(s);
310	if ((eqp = strchr(ss, '=')) != NULL)
311		*eqp = '\0';
312	if (set && eqp != NULL)
313		(void) setenv(ss, eqp + 1, 1);
314	else
315		(void) unsetenv(ss);
316	ckfree(ss);
317	INTON;
318
319	return;
320}
321
322
323/*
324 * Same as setvar except that the variable and value are passed in
325 * the first argument as name=value.  Since the first argument will
326 * be actually stored in the table, it should not be a string that
327 * will go away.
328 */
329
330void
331setvareq(char *s, int flags)
332{
333	struct var *vp, **vpp;
334	int nlen;
335
336	if (aflag)
337		flags |= VEXPORT;
338	if (forcelocal && !(flags & (VNOSET | VNOLOCAL)))
339		mklocal(s);
340	vp = find_var(s, &vpp, &nlen);
341	if (vp != NULL) {
342		if (vp->flags & VREADONLY) {
343			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
344				ckfree(s);
345			error("%.*s: is read only", vp->name_len, s);
346		}
347		if (flags & VNOSET) {
348			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
349				ckfree(s);
350			return;
351		}
352		INTOFF;
353
354		if (vp->func && (flags & VNOFUNC) == 0)
355			(*vp->func)(s + vp->name_len + 1);
356
357		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
358			ckfree(vp->text);
359
360		vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
361		vp->flags |= flags;
362		vp->text = s;
363
364		/*
365		 * We could roll this to a function, to handle it as
366		 * a regular variable function callback, but why bother?
367		 *
368		 * Note: this assumes iflag is not set to 1 initially.
369		 * As part of init(), this is called before arguments
370		 * are looked at.
371		 */
372		if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
373		    iflag == 1)
374			chkmail(1);
375		if ((vp->flags & VEXPORT) && localevar(s)) {
376			change_env(s, 1);
377			(void) setlocale(LC_ALL, "");
378			updatecharset();
379		}
380		INTON;
381		return;
382	}
383	/* not found */
384	if (flags & VNOSET) {
385		if ((flags & (VTEXTFIXED|VSTACK)) == 0)
386			ckfree(s);
387		return;
388	}
389	INTOFF;
390	vp = ckmalloc(sizeof (*vp));
391	vp->flags = flags;
392	vp->text = s;
393	vp->name_len = nlen;
394	vp->next = *vpp;
395	vp->func = NULL;
396	*vpp = vp;
397	if ((vp->flags & VEXPORT) && localevar(s)) {
398		change_env(s, 1);
399		(void) setlocale(LC_ALL, "");
400		updatecharset();
401	}
402	INTON;
403}
404
405
406
407/*
408 * Process a linked list of variable assignments.
409 */
410
411void
412listsetvar(struct strlist *list, int flags)
413{
414	struct strlist *lp;
415
416	INTOFF;
417	for (lp = list ; lp ; lp = lp->next) {
418		setvareq(savestr(lp->text), flags);
419	}
420	INTON;
421}
422
423
424
425/*
426 * Find the value of a variable.  Returns NULL if not set.
427 */
428
429char *
430lookupvar(const char *name)
431{
432	struct var *v;
433
434	v = find_var(name, NULL, NULL);
435	if (v == NULL || v->flags & VUNSET)
436		return NULL;
437	return v->text + v->name_len + 1;
438}
439
440
441
442/*
443 * Search the environment of a builtin command.  If the second argument
444 * is nonzero, return the value of a variable even if it hasn't been
445 * exported.
446 */
447
448char *
449bltinlookup(const char *name, int doall)
450{
451	struct strlist *sp;
452	struct var *v;
453	char *result;
454
455	result = NULL;
456	for (sp = cmdenviron ; sp ; sp = sp->next) {
457		if (varequal(sp->text, name))
458			result = strchr(sp->text, '=') + 1;
459	}
460	if (result != NULL)
461		return result;
462
463	v = find_var(name, NULL, NULL);
464	if (v == NULL || v->flags & VUNSET ||
465	    (!doall && (v->flags & VEXPORT) == 0))
466		return NULL;
467	return v->text + v->name_len + 1;
468}
469
470
471/*
472 * Set up locale for a builtin (LANG/LC_* assignments).
473 */
474void
475bltinsetlocale(void)
476{
477	struct strlist *lp;
478	int act = 0;
479	char *loc, *locdef;
480	int i;
481
482	for (lp = cmdenviron ; lp ; lp = lp->next) {
483		if (localevar(lp->text)) {
484			act = 1;
485			break;
486		}
487	}
488	if (!act)
489		return;
490	loc = bltinlookup("LC_ALL", 0);
491	INTOFF;
492	if (loc != NULL) {
493		setlocale(LC_ALL, loc);
494		INTON;
495		updatecharset();
496		return;
497	}
498	locdef = bltinlookup("LANG", 0);
499	for (i = 0; locale_names[i] != NULL; i++) {
500		loc = bltinlookup(locale_names[i], 0);
501		if (loc == NULL)
502			loc = locdef;
503		if (loc != NULL)
504			setlocale(locale_categories[i], loc);
505	}
506	INTON;
507	updatecharset();
508}
509
510/*
511 * Undo the effect of bltinlocaleset().
512 */
513void
514bltinunsetlocale(void)
515{
516	struct strlist *lp;
517
518	INTOFF;
519	for (lp = cmdenviron ; lp ; lp = lp->next) {
520		if (localevar(lp->text)) {
521			setlocale(LC_ALL, "");
522			updatecharset();
523			return;
524		}
525	}
526	INTON;
527}
528
529/*
530 * Update the localeisutf8 flag.
531 */
532void
533updatecharset(void)
534{
535	char *charset;
536
537	charset = nl_langinfo(CODESET);
538	localeisutf8 = !strcmp(charset, "UTF-8");
539}
540
541void
542initcharset(void)
543{
544	updatecharset();
545	initial_localeisutf8 = localeisutf8;
546}
547
548/*
549 * Generate a list of exported variables.  This routine is used to construct
550 * the third argument to execve when executing a program.
551 */
552
553char **
554environment(void)
555{
556	int nenv;
557	struct var **vpp;
558	struct var *vp;
559	char **env, **ep;
560
561	nenv = 0;
562	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
563		for (vp = *vpp ; vp ; vp = vp->next)
564			if (vp->flags & VEXPORT)
565				nenv++;
566	}
567	ep = env = stalloc((nenv + 1) * sizeof *env);
568	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
569		for (vp = *vpp ; vp ; vp = vp->next)
570			if (vp->flags & VEXPORT)
571				*ep++ = vp->text;
572	}
573	*ep = NULL;
574	return env;
575}
576
577
578static int
579var_compare(const void *a, const void *b)
580{
581	const char *const *sa, *const *sb;
582
583	sa = a;
584	sb = b;
585	/*
586	 * This compares two var=value strings which creates a different
587	 * order from what you would probably expect.  POSIX is somewhat
588	 * ambiguous on what should be sorted exactly.
589	 */
590	return strcoll(*sa, *sb);
591}
592
593
594/*
595 * Command to list all variables which are set.  This is invoked from the
596 * set command when it is called without any options or operands.
597 */
598
599int
600showvarscmd(int argc __unused, char **argv __unused)
601{
602	struct var **vpp;
603	struct var *vp;
604	const char *s;
605	const char **vars;
606	int i, n;
607
608	/*
609	 * POSIX requires us to sort the variables.
610	 */
611	n = 0;
612	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
613		for (vp = *vpp; vp; vp = vp->next) {
614			if (!(vp->flags & VUNSET))
615				n++;
616		}
617	}
618
619	INTOFF;
620	vars = ckmalloc(n * sizeof(*vars));
621	i = 0;
622	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
623		for (vp = *vpp; vp; vp = vp->next) {
624			if (!(vp->flags & VUNSET))
625				vars[i++] = vp->text;
626		}
627	}
628
629	qsort(vars, n, sizeof(*vars), var_compare);
630	for (i = 0; i < n; i++) {
631		/*
632		 * Skip improper variable names so the output remains usable as
633		 * shell input.
634		 */
635		if (!isassignment(vars[i]))
636			continue;
637		s = strchr(vars[i], '=');
638		s++;
639		outbin(vars[i], s - vars[i], out1);
640		out1qstr(s);
641		out1c('\n');
642	}
643	ckfree(vars);
644	INTON;
645
646	return 0;
647}
648
649
650
651/*
652 * The export and readonly commands.
653 */
654
655int
656exportcmd(int argc, char **argv)
657{
658	struct var **vpp;
659	struct var *vp;
660	char *name;
661	char *p;
662	char *cmdname;
663	int ch, values;
664	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
665
666	cmdname = argv[0];
667	optreset = optind = 1;
668	opterr = 0;
669	values = 0;
670	while ((ch = getopt(argc, argv, "p")) != -1) {
671		switch (ch) {
672		case 'p':
673			values = 1;
674			break;
675		case '?':
676		default:
677			error("unknown option: -%c", optopt);
678		}
679	}
680	argc -= optind;
681	argv += optind;
682
683	if (values && argc != 0)
684		error("-p requires no arguments");
685	if (argc != 0) {
686		while ((name = *argv++) != NULL) {
687			if ((p = strchr(name, '=')) != NULL) {
688				p++;
689			} else {
690				vp = find_var(name, NULL, NULL);
691				if (vp != NULL) {
692					vp->flags |= flag;
693					if ((vp->flags & VEXPORT) && localevar(vp->text)) {
694						change_env(vp->text, 1);
695						(void) setlocale(LC_ALL, "");
696						updatecharset();
697					}
698					continue;
699				}
700			}
701			setvar(name, p, flag);
702		}
703	} else {
704		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
705			for (vp = *vpp ; vp ; vp = vp->next) {
706				if (vp->flags & flag) {
707					if (values) {
708						/*
709						 * Skip improper variable names
710						 * so the output remains usable
711						 * as shell input.
712						 */
713						if (!isassignment(vp->text))
714							continue;
715						out1str(cmdname);
716						out1c(' ');
717					}
718					if (values && !(vp->flags & VUNSET)) {
719						outbin(vp->text,
720						    vp->name_len + 1, out1);
721						out1qstr(vp->text +
722						    vp->name_len + 1);
723					} else
724						outbin(vp->text, vp->name_len,
725						    out1);
726					out1c('\n');
727				}
728			}
729		}
730	}
731	return 0;
732}
733
734
735/*
736 * The "local" command.
737 */
738
739int
740localcmd(int argc __unused, char **argv __unused)
741{
742	char *name;
743
744	if (! in_function())
745		error("Not in a function");
746	while ((name = *argptr++) != NULL) {
747		mklocal(name);
748	}
749	return 0;
750}
751
752
753/*
754 * Make a variable a local variable.  When a variable is made local, it's
755 * value and flags are saved in a localvar structure.  The saved values
756 * will be restored when the shell function returns.  We handle the name
757 * "-" as a special case.
758 */
759
760void
761mklocal(char *name)
762{
763	struct localvar *lvp;
764	struct var **vpp;
765	struct var *vp;
766
767	INTOFF;
768	lvp = ckmalloc(sizeof (struct localvar));
769	if (name[0] == '-' && name[1] == '\0') {
770		lvp->text = ckmalloc(sizeof optlist);
771		memcpy(lvp->text, optlist, sizeof optlist);
772		vp = NULL;
773	} else {
774		vp = find_var(name, &vpp, NULL);
775		if (vp == NULL) {
776			if (strchr(name, '='))
777				setvareq(savestr(name), VSTRFIXED | VNOLOCAL);
778			else
779				setvar(name, NULL, VSTRFIXED | VNOLOCAL);
780			vp = *vpp;	/* the new variable */
781			lvp->text = NULL;
782			lvp->flags = VUNSET;
783		} else {
784			lvp->text = vp->text;
785			lvp->flags = vp->flags;
786			vp->flags |= VSTRFIXED|VTEXTFIXED;
787			if (name[vp->name_len] == '=')
788				setvareq(savestr(name), VNOLOCAL);
789		}
790	}
791	lvp->vp = vp;
792	lvp->next = localvars;
793	localvars = lvp;
794	INTON;
795}
796
797
798/*
799 * Called after a function returns.
800 */
801
802void
803poplocalvars(void)
804{
805	struct localvar *lvp;
806	struct var *vp;
807
808	INTOFF;
809	while ((lvp = localvars) != NULL) {
810		localvars = lvp->next;
811		vp = lvp->vp;
812		if (vp == NULL) {	/* $- saved */
813			memcpy(optlist, lvp->text, sizeof optlist);
814			ckfree(lvp->text);
815			optschanged();
816		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
817			(void)unsetvar(vp->text);
818		} else {
819			if ((vp->flags & VTEXTFIXED) == 0)
820				ckfree(vp->text);
821			vp->flags = lvp->flags;
822			vp->text = lvp->text;
823		}
824		ckfree(lvp);
825	}
826	INTON;
827}
828
829
830int
831setvarcmd(int argc, char **argv)
832{
833	if (argc <= 2)
834		return unsetcmd(argc, argv);
835	else if (argc == 3)
836		setvar(argv[1], argv[2], 0);
837	else
838		error("too many arguments");
839	return 0;
840}
841
842
843/*
844 * The unset builtin command.
845 */
846
847int
848unsetcmd(int argc __unused, char **argv __unused)
849{
850	char **ap;
851	int i;
852	int flg_func = 0;
853	int flg_var = 0;
854	int ret = 0;
855
856	while ((i = nextopt("vf")) != '\0') {
857		if (i == 'f')
858			flg_func = 1;
859		else
860			flg_var = 1;
861	}
862	if (flg_func == 0 && flg_var == 0)
863		flg_var = 1;
864
865	INTOFF;
866	for (ap = argptr; *ap ; ap++) {
867		if (flg_func)
868			ret |= unsetfunc(*ap);
869		if (flg_var)
870			ret |= unsetvar(*ap);
871	}
872	INTON;
873	return ret;
874}
875
876
877/*
878 * Unset the specified variable.
879 * Called with interrupts off.
880 */
881
882int
883unsetvar(const char *s)
884{
885	struct var **vpp;
886	struct var *vp;
887
888	vp = find_var(s, &vpp, NULL);
889	if (vp == NULL)
890		return (0);
891	if (vp->flags & VREADONLY)
892		return (1);
893	if (vp->text[vp->name_len + 1] != '\0')
894		setvar(s, nullstr, 0);
895	if ((vp->flags & VEXPORT) && localevar(vp->text)) {
896		change_env(s, 0);
897		setlocale(LC_ALL, "");
898		updatecharset();
899	}
900	vp->flags &= ~VEXPORT;
901	vp->flags |= VUNSET;
902	if ((vp->flags & VSTRFIXED) == 0) {
903		if ((vp->flags & VTEXTFIXED) == 0)
904			ckfree(vp->text);
905		*vpp = vp->next;
906		ckfree(vp);
907	}
908	return (0);
909}
910
911
912
913/*
914 * Returns true if the two strings specify the same varable.  The first
915 * variable name is terminated by '='; the second may be terminated by
916 * either '=' or '\0'.
917 */
918
919static int
920varequal(const char *p, const char *q)
921{
922	while (*p == *q++) {
923		if (*p++ == '=')
924			return 1;
925	}
926	if (*p == '=' && *(q - 1) == '\0')
927		return 1;
928	return 0;
929}
930
931/*
932 * Search for a variable.
933 * 'name' may be terminated by '=' or a NUL.
934 * vppp is set to the pointer to vp, or the list head if vp isn't found
935 * lenp is set to the number of charactets in 'name'
936 */
937
938static struct var *
939find_var(const char *name, struct var ***vppp, int *lenp)
940{
941	unsigned int hashval;
942	int len;
943	struct var *vp, **vpp;
944	const char *p = name;
945
946	hashval = 0;
947	while (*p && *p != '=')
948		hashval = 2 * hashval + (unsigned char)*p++;
949	len = p - name;
950
951	if (lenp)
952		*lenp = len;
953	vpp = &vartab[hashval % VTABSIZE];
954	if (vppp)
955		*vppp = vpp;
956
957	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
958		if (vp->name_len != len)
959			continue;
960		if (memcmp(vp->text, name, len) != 0)
961			continue;
962		if (vppp)
963			*vppp = vpp;
964		return vp;
965	}
966	return NULL;
967}
968