xref: /freebsd/bin/sh/var.c (revision f5463265955b829775bbb32e1fd0bc11dafc36ce)
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 
71 struct varinit {
72 	struct var *var;
73 	int flags;
74 	const char *text;
75 	void (*func)(const char *);
76 };
77 
78 
79 #ifndef NO_HISTORY
80 struct var vhistsize;
81 struct var vterm;
82 #endif
83 struct var vifs;
84 struct var vmail;
85 struct var vmpath;
86 struct var vpath;
87 struct var vps1;
88 struct var vps2;
89 struct var vps4;
90 static struct var voptind;
91 struct var vdisvfork;
92 
93 struct localvar *localvars;
94 int forcelocal;
95 
96 static 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 
128 static struct var *vartab[VTABSIZE];
129 
130 static const char *const locale_names[7] = {
131 	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
132 	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
133 };
134 static const int locale_categories[7] = {
135 	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
136 };
137 
138 static int varequal(const char *, const char *);
139 static struct var *find_var(const char *, struct var ***, int *);
140 static int localevar(const char *);
141 static void setvareq_const(const char *s, int flags);
142 
143 extern 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 
150 void
151 initvar(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 
192 int
193 setvarsafe(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 
217 void
218 setvar(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 
263 static int
264 localevar(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  */
287 static void
288 change_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 
315 void
316 setvareq(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 
391 static void
392 setvareq_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 
402 void
403 listsetvar(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 
419 char *
420 lookupvar(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 
438 char *
439 bltinlookup(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  */
464 void
465 bltinsetlocale(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  */
502 void
503 bltinunsetlocale(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  */
521 void
522 updatecharset(void)
523 {
524 	char *charset;
525 
526 	charset = nl_langinfo(CODESET);
527 	localeisutf8 = !strcmp(charset, "UTF-8");
528 }
529 
530 void
531 initcharset(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 
542 char **
543 environment(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 
567 static int
568 var_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 
588 int
589 showvarscmd(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 
644 int
645 exportcmd(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 
722 int
723 localcmd(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 
744 void
745 mklocal(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 
786 void
787 poplocalvars(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 
826 int
827 setvarcmd(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 
843 int
844 unsetcmd(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 
878 int
879 unsetvar(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 
915 static int
916 varequal(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 
934 static struct var *
935 find_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