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