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