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