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