var.c revision 17562
1273796Sbrooks/*-
2244541Sbrooks * Copyright (c) 1991, 1993
3244541Sbrooks *	The Regents of the University of California.  All rights reserved.
4244541Sbrooks *
5244541Sbrooks * This code is derived from software contributed to Berkeley by
6244541Sbrooks * Kenneth Almquist.
7244541Sbrooks *
8244541Sbrooks * Redistribution and use in source and binary forms, with or without
9244541Sbrooks * modification, are permitted provided that the following conditions
10244541Sbrooks * are met:
11244541Sbrooks * 1. Redistributions of source code must retain the above copyright
12244541Sbrooks *    notice, this list of conditions and the following disclaimer.
13244541Sbrooks * 2. Redistributions in binary form must reproduce the above copyright
14244541Sbrooks *    notice, this list of conditions and the following disclaimer in the
15244541Sbrooks *    documentation and/or other materials provided with the distribution.
16244541Sbrooks * 3. All advertising materials mentioning features or use of this software
17244541Sbrooks *    must display the following acknowledgement:
18244541Sbrooks *	This product includes software developed by the University of
19244541Sbrooks *	California, Berkeley and its contributors.
20244541Sbrooks * 4. Neither the name of the University nor the names of its contributors
21244541Sbrooks *    may be used to endorse or promote products derived from this software
22244541Sbrooks *    without specific prior written permission.
23244541Sbrooks *
24244541Sbrooks * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25244541Sbrooks * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26244541Sbrooks * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27244541Sbrooks * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28244541Sbrooks * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29244541Sbrooks * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30244541Sbrooks * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31244541Sbrooks * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32244541Sbrooks * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33244541Sbrooks * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34244541Sbrooks * SUCH DAMAGE.
35244541Sbrooks *
36244541Sbrooks *	$Id: var.c,v 1.4 1996/08/11 22:51:00 ache Exp $
37244541Sbrooks */
38244541Sbrooks
39244541Sbrooks#ifndef lint
40244541Sbrooksstatic char sccsid[] = "@(#)var.c	8.1 (Berkeley) 5/31/93";
41244541Sbrooks#endif /* not lint */
42244541Sbrooks
43244541Sbrooks/*
44244541Sbrooks * Shell variables.
45244541Sbrooks */
46273796Sbrooks
47244541Sbrooks#include <locale.h>
48244541Sbrooks
49244541Sbrooks#include "shell.h"
50244541Sbrooks#include "output.h"
51244541Sbrooks#include "expand.h"
52244541Sbrooks#include "nodes.h"	/* for other headers */
53244541Sbrooks#include "eval.h"	/* defines cmdenviron */
54244541Sbrooks#include "exec.h"
55244541Sbrooks#include "syntax.h"
56244541Sbrooks#include "options.h"
57244541Sbrooks#include "mail.h"
58244541Sbrooks#include "parser.h"
59244541Sbrooks#include "var.h"
60244541Sbrooks#include "memalloc.h"
61244541Sbrooks#include "error.h"
62249293Sed#include "mystring.h"
63249293Sed
64244541Sbrooks
65244541Sbrooks#define VTABSIZE 39
66244541Sbrooks
67244541Sbrooks
68244541Sbrooksstruct varinit {
69244541Sbrooks	struct var *var;
70244541Sbrooks	int flags;
71244541Sbrooks	char *text;
72244541Sbrooks};
73244541Sbrooks
74244541Sbrooks
75244541Sbrooks#if ATTY
76244541Sbrooksstruct var vatty;
77244541Sbrooks#endif
78244541Sbrooksstruct var vhistsize;
79244541Sbrooksstruct var vifs;
80244541Sbrooksstruct var vmail;
81244541Sbrooksstruct var vmpath;
82249293Sedstruct var vpath;
83244541Sbrooksstruct var vps1;
84244541Sbrooksstruct var vps2;
85244541Sbrooksstruct var vvers;
86244541Sbrooks#if ATTY
87244541Sbrooksstruct var vterm;
88249293Sed#endif
89244541Sbrooks
90244541Sbrooksconst struct varinit varinit[] = {
91244541Sbrooks#if ATTY
92244541Sbrooks	{&vatty,	VSTRFIXED|VTEXTFIXED|VUNSET,	"ATTY="},
93244541Sbrooks#endif
94244541Sbrooks	{&vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE="},
95249293Sed	{&vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n"},
96244541Sbrooks	{&vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL="},
97244541Sbrooks	{&vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH="},
98244541Sbrooks	{&vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=:/bin:/usr/bin"},
99244541Sbrooks	/*
100244541Sbrooks	 * vps1 depends on uid
101244541Sbrooks	 */
102244541Sbrooks	{&vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> "},
103244541Sbrooks#if ATTY
104244541Sbrooks	{&vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM="},
105244541Sbrooks#endif
106244541Sbrooks	{NULL,	0,				NULL}
107244541Sbrooks};
108244541Sbrooks
109244541Sbrooksstruct var *vartab[VTABSIZE];
110244541Sbrooks
111244541SbrooksSTATIC int unsetvar __P((char *));
112244541SbrooksSTATIC struct var **hashvar __P((char *));
113244541SbrooksSTATIC int varequal __P((char *, char *));
114244541SbrooksSTATIC int localevar __P((char *));
115244541Sbrooks
116244541Sbrooks/*
117244541Sbrooks * Initialize the varable symbol tables and import the environment
118244541Sbrooks */
119244541Sbrooks
120244541Sbrooks#ifdef mkinit
121244541SbrooksINCLUDE "var.h"
122244541SbrooksINIT {
123244541Sbrooks	char **envp;
124244541Sbrooks	extern char **environ;
125244541Sbrooks
126244541Sbrooks	initvar();
127244541Sbrooks	for (envp = environ ; *envp ; envp++) {
128244541Sbrooks		if (strchr(*envp, '=')) {
129244541Sbrooks			setvareq(*envp, VEXPORT|VTEXTFIXED);
130244541Sbrooks		}
131244541Sbrooks	}
132244541Sbrooks}
133244541Sbrooks#endif
134244541Sbrooks
135244541Sbrooks
136244541Sbrooks/*
137244541Sbrooks * This routine initializes the builtin variables.  It is called when the
138244541Sbrooks * shell is initialized and again when a shell procedure is spawned.
139244541Sbrooks */
140244541Sbrooks
141244541Sbrooksvoid
142244541Sbrooksinitvar() {
143244541Sbrooks	const struct varinit *ip;
144244541Sbrooks	struct var *vp;
145244541Sbrooks	struct var **vpp;
146244541Sbrooks
147244541Sbrooks	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
148244541Sbrooks		if ((vp->flags & VEXPORT) == 0) {
149244541Sbrooks			vpp = hashvar(ip->text);
150244541Sbrooks			vp->next = *vpp;
151244541Sbrooks			*vpp = vp;
152244541Sbrooks			vp->text = ip->text;
153244541Sbrooks			vp->flags = ip->flags;
154244541Sbrooks		}
155244541Sbrooks	}
156244541Sbrooks	/*
157244541Sbrooks	 * PS1 depends on uid
158244541Sbrooks	 */
159244541Sbrooks	if ((vps1.flags & VEXPORT) == 0) {
160244541Sbrooks		vpp = hashvar("PS1=");
161244541Sbrooks		vps1.next = *vpp;
162244541Sbrooks		*vpp = &vps1;
163244541Sbrooks		vps1.text = geteuid() ? "PS1=$ " : "PS1=# ";
164244541Sbrooks		vps1.flags = VSTRFIXED|VTEXTFIXED;
165244541Sbrooks	}
166244541Sbrooks}
167244541Sbrooks
168244541Sbrooks/*
169244541Sbrooks * Set the value of a variable.  The flags argument is ored with the
170244541Sbrooks * flags of the variable.  If val is NULL, the variable is unset.
171244541Sbrooks */
172244541Sbrooks
173244541Sbrooksvoid
174244541Sbrookssetvar(name, val, flags)
175244541Sbrooks	char *name, *val;
176244541Sbrooks	{
177244541Sbrooks	char *p, *q;
178244541Sbrooks	int len;
179244541Sbrooks	int namelen;
180244541Sbrooks	char *nameeq;
181244541Sbrooks	int isbad;
182244541Sbrooks
183244541Sbrooks	isbad = 0;
184249293Sed	p = name;
185249293Sed	if (! is_name(*p))
186249293Sed		isbad = 1;
187244541Sbrooks	p++;
188244541Sbrooks	for (;;) {
189244541Sbrooks		if (! is_in_name(*p)) {
190244541Sbrooks			if (*p == '\0' || *p == '=')
191244541Sbrooks				break;
192244541Sbrooks			isbad = 1;
193244541Sbrooks		}
194244541Sbrooks		p++;
195244541Sbrooks	}
196244541Sbrooks	namelen = p - name;
197244541Sbrooks	if (isbad)
198244541Sbrooks		error("%.*s: bad variable name", namelen, name);
199244541Sbrooks	len = namelen + 2;		/* 2 is space for '=' and '\0' */
200244541Sbrooks	if (val == NULL) {
201244541Sbrooks		flags |= VUNSET;
202244541Sbrooks	} else {
203244541Sbrooks		len += strlen(val);
204244541Sbrooks	}
205244541Sbrooks	p = nameeq = ckmalloc(len);
206244541Sbrooks	q = name;
207244541Sbrooks	while (--namelen >= 0)
208244541Sbrooks		*p++ = *q++;
209244541Sbrooks	*p++ = '=';
210244541Sbrooks	*p = '\0';
211244541Sbrooks	if (val)
212244541Sbrooks		scopy(val, p);
213244541Sbrooks	setvareq(nameeq, flags);
214244541Sbrooks}
215244541Sbrooks
216244541SbrooksSTATIC int
217244541Sbrookslocalevar(s)
218244541Sbrooks	char *s;
219244541Sbrooks	{
220244541Sbrooks	static char *lnames[7] = {
221244541Sbrooks		"ALL", "COLLATE", "CTYPE", "MONETARY",
222244541Sbrooks		"NUMERIC", "TIME", NULL
223244541Sbrooks	};
224244541Sbrooks	char **ss;
225244541Sbrooks
226244541Sbrooks	if (*s != 'L')
227244541Sbrooks		return 0;
228244541Sbrooks	if (varequal(s + 1, "ANG"))
229244541Sbrooks		return 1;
230244541Sbrooks	if (strncmp(s + 1, "C_", 2) != 0)
231244541Sbrooks		return 0;
232244541Sbrooks	for (ss = lnames; *ss ; ss++)
233244541Sbrooks		if (varequal(s + 3, *ss))
234244541Sbrooks			return 1;
235244541Sbrooks	return 0;
236244541Sbrooks}
237244541Sbrooks
238244541Sbrooks/*
239244541Sbrooks * Same as setvar except that the variable and value are passed in
240244541Sbrooks * the first argument as name=value.  Since the first argument will
241244541Sbrooks * be actually stored in the table, it should not be a string that
242244541Sbrooks * will go away.
243244541Sbrooks */
244244541Sbrooks
245244541Sbrooksvoid
246244541Sbrookssetvareq(s, flags)
247244541Sbrooks	char *s;
248244541Sbrooks	{
249244541Sbrooks	struct var *vp, **vpp;
250244541Sbrooks
251244541Sbrooks	vpp = hashvar(s);
252244541Sbrooks	for (vp = *vpp ; vp ; vp = vp->next) {
253244541Sbrooks		if (varequal(s, vp->text)) {
254244541Sbrooks			if (vp->flags & VREADONLY) {
255244541Sbrooks				int len = strchr(s, '=') - s;
256244541Sbrooks				error("%.*s: is read only", len, s);
257244541Sbrooks			}
258244541Sbrooks			INTOFF;
259244541Sbrooks			if (vp == &vpath)
260244541Sbrooks				changepath(s + 5);	/* 5 = strlen("PATH=") */
261244541Sbrooks			if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
262244541Sbrooks				ckfree(vp->text);
263244541Sbrooks			vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET);
264244541Sbrooks			vp->flags |= flags;
265244541Sbrooks			vp->text = s;
266244541Sbrooks			if (vp == &vmpath || (vp == &vmail && ! mpathset()))
267244541Sbrooks				chkmail(1);
268244541Sbrooks			if (vp == &vhistsize)
269244541Sbrooks				sethistsize();
270244541Sbrooks			if ((vp->flags & VEXPORT) && localevar(s)) {
271244541Sbrooks				putenv(s);
272244541Sbrooks				(void) setlocale(LC_ALL, "");
273244541Sbrooks			}
274244541Sbrooks			INTON;
275244541Sbrooks			return;
276244541Sbrooks		}
277244541Sbrooks	}
278244541Sbrooks	/* not found */
279244541Sbrooks	vp = ckmalloc(sizeof (*vp));
280244541Sbrooks	vp->flags = flags;
281244541Sbrooks	vp->text = s;
282244541Sbrooks	vp->next = *vpp;
283244541Sbrooks	INTOFF;
284244541Sbrooks	*vpp = vp;
285244541Sbrooks	if ((vp->flags & VEXPORT) && localevar(s)) {
286244541Sbrooks		putenv(s);
287244541Sbrooks		(void) setlocale(LC_ALL, "");
288244541Sbrooks	}
289244541Sbrooks	INTON;
290244541Sbrooks}
291244541Sbrooks
292244541Sbrooks
293244541Sbrooks
294244541Sbrooks/*
295244541Sbrooks * Process a linked list of variable assignments.
296244541Sbrooks */
297244541Sbrooks
298244541Sbrooksvoid
299273796Sbrookslistsetvar(list)
300244541Sbrooks	struct strlist *list;
301244541Sbrooks	{
302244541Sbrooks	struct strlist *lp;
303273796Sbrooks
304244541Sbrooks	INTOFF;
305244541Sbrooks	for (lp = list ; lp ; lp = lp->next) {
306244541Sbrooks		setvareq(savestr(lp->text), 0);
307244541Sbrooks	}
308244541Sbrooks	INTON;
309244541Sbrooks}
310244541Sbrooks
311244541Sbrooks
312244541Sbrooks
313244541Sbrooks/*
314244541Sbrooks * Find the value of a variable.  Returns NULL if not set.
315244541Sbrooks */
316244541Sbrooks
317244541Sbrookschar *
318244541Sbrookslookupvar(name)
319244541Sbrooks	char *name;
320244541Sbrooks	{
321244541Sbrooks	struct var *v;
322244541Sbrooks
323244541Sbrooks	for (v = *hashvar(name) ; v ; v = v->next) {
324244541Sbrooks		if (varequal(v->text, name)) {
325244541Sbrooks			if (v->flags & VUNSET)
326244541Sbrooks				return NULL;
327244541Sbrooks			return strchr(v->text, '=') + 1;
328244541Sbrooks		}
329244541Sbrooks	}
330244541Sbrooks	return NULL;
331244541Sbrooks}
332244541Sbrooks
333
334
335/*
336 * Search the environment of a builtin command.  If the second argument
337 * is nonzero, return the value of a variable even if it hasn't been
338 * exported.
339 */
340
341char *
342bltinlookup(name, doall)
343	char *name;
344	{
345	struct strlist *sp;
346	struct var *v;
347
348	for (sp = cmdenviron ; sp ; sp = sp->next) {
349		if (varequal(sp->text, name))
350			return strchr(sp->text, '=') + 1;
351	}
352	for (v = *hashvar(name) ; v ; v = v->next) {
353		if (varequal(v->text, name)) {
354			if (v->flags & VUNSET
355			 || ! doall && (v->flags & VEXPORT) == 0)
356				return NULL;
357			return strchr(v->text, '=') + 1;
358		}
359	}
360	return NULL;
361}
362
363
364
365/*
366 * Generate a list of exported variables.  This routine is used to construct
367 * the third argument to execve when executing a program.
368 */
369
370char **
371environment() {
372	int nenv;
373	struct var **vpp;
374	struct var *vp;
375	char **env, **ep;
376
377	nenv = 0;
378	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
379		for (vp = *vpp ; vp ; vp = vp->next)
380			if (vp->flags & VEXPORT)
381				nenv++;
382	}
383	ep = env = stalloc((nenv + 1) * sizeof *env);
384	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
385		for (vp = *vpp ; vp ; vp = vp->next)
386			if (vp->flags & VEXPORT)
387				*ep++ = vp->text;
388	}
389	*ep = NULL;
390	return env;
391}
392
393
394/*
395 * Called when a shell procedure is invoked to clear out nonexported
396 * variables.  It is also necessary to reallocate variables of with
397 * VSTACK set since these are currently allocated on the stack.
398 */
399
400#ifdef mkinit
401MKINIT void shprocvar();
402
403SHELLPROC {
404	shprocvar();
405}
406#endif
407
408void
409shprocvar() {
410	struct var **vpp;
411	struct var *vp, **prev;
412
413	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
414		for (prev = vpp ; (vp = *prev) != NULL ; ) {
415			if ((vp->flags & VEXPORT) == 0) {
416				*prev = vp->next;
417				if ((vp->flags & VTEXTFIXED) == 0)
418					ckfree(vp->text);
419				if ((vp->flags & VSTRFIXED) == 0)
420					ckfree(vp);
421			} else {
422				if (vp->flags & VSTACK) {
423					vp->text = savestr(vp->text);
424					vp->flags &=~ VSTACK;
425				}
426				prev = &vp->next;
427			}
428		}
429	}
430	initvar();
431}
432
433
434
435/*
436 * Command to list all variables which are set.  Currently this command
437 * is invoked from the set command when the set command is called without
438 * any variables.
439 */
440
441int
442showvarscmd(argc, argv)  char **argv; {
443	struct var **vpp;
444	struct var *vp;
445
446	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
447		for (vp = *vpp ; vp ; vp = vp->next) {
448			if ((vp->flags & VUNSET) == 0)
449				out1fmt("%s\n", vp->text);
450		}
451	}
452	return 0;
453}
454
455
456
457/*
458 * The export and readonly commands.
459 */
460
461int
462exportcmd(argc, argv)  char **argv; {
463	struct var **vpp;
464	struct var *vp;
465	char *name;
466	char *p;
467	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
468
469	listsetvar(cmdenviron);
470	if (argc > 1) {
471		while ((name = *argptr++) != NULL) {
472			if ((p = strchr(name, '=')) != NULL) {
473				p++;
474			} else {
475				vpp = hashvar(name);
476				for (vp = *vpp ; vp ; vp = vp->next) {
477					if (varequal(vp->text, name)) {
478						vp->flags |= flag;
479						if ((vp->flags & VEXPORT) && localevar(vp->text)) {
480							putenv(vp->text);
481							(void) setlocale(LC_ALL, "");
482						}
483						goto found;
484					}
485				}
486			}
487			setvar(name, p, flag);
488found:;
489		}
490	} else {
491		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
492			for (vp = *vpp ; vp ; vp = vp->next) {
493				if (vp->flags & flag) {
494					for (p = vp->text ; *p != '=' ; p++)
495						out1c(*p);
496					out1c('\n');
497				}
498			}
499		}
500	}
501	return 0;
502}
503
504
505/*
506 * The "local" command.
507 */
508
509localcmd(argc, argv)  char **argv; {
510	char *name;
511
512	if (! in_function())
513		error("Not in a function");
514	while ((name = *argptr++) != NULL) {
515		mklocal(name);
516	}
517	return 0;
518}
519
520
521/*
522 * Make a variable a local variable.  When a variable is made local, it's
523 * value and flags are saved in a localvar structure.  The saved values
524 * will be restored when the shell function returns.  We handle the name
525 * "-" as a special case.
526 */
527
528void
529mklocal(name)
530	char *name;
531	{
532	struct localvar *lvp;
533	struct var **vpp;
534	struct var *vp;
535
536	INTOFF;
537	lvp = ckmalloc(sizeof (struct localvar));
538	if (name[0] == '-' && name[1] == '\0') {
539		lvp->text = ckmalloc(sizeof optlist);
540		bcopy(optlist, lvp->text, sizeof optlist);
541		vp = NULL;
542	} else {
543		vpp = hashvar(name);
544		for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next);
545		if (vp == NULL) {
546			if (strchr(name, '='))
547				setvareq(savestr(name), VSTRFIXED);
548			else
549				setvar(name, NULL, VSTRFIXED);
550			vp = *vpp;	/* the new variable */
551			lvp->text = NULL;
552			lvp->flags = VUNSET;
553		} else {
554			lvp->text = vp->text;
555			lvp->flags = vp->flags;
556			vp->flags |= VSTRFIXED|VTEXTFIXED;
557			if (strchr(name, '='))
558				setvareq(savestr(name), 0);
559		}
560	}
561	lvp->vp = vp;
562	lvp->next = localvars;
563	localvars = lvp;
564	INTON;
565}
566
567
568/*
569 * Called after a function returns.
570 */
571
572void
573poplocalvars() {
574	struct localvar *lvp;
575	struct var *vp;
576
577	while ((lvp = localvars) != NULL) {
578		localvars = lvp->next;
579		vp = lvp->vp;
580		if (vp == NULL) {	/* $- saved */
581			bcopy(lvp->text, optlist, sizeof optlist);
582			ckfree(lvp->text);
583		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
584			(void)unsetvar(vp->text);
585		} else {
586			if ((vp->flags & VTEXTFIXED) == 0)
587				ckfree(vp->text);
588			vp->flags = lvp->flags;
589			vp->text = lvp->text;
590		}
591		ckfree(lvp);
592	}
593}
594
595
596setvarcmd(argc, argv)  char **argv; {
597	if (argc <= 2)
598		return unsetcmd(argc, argv);
599	else if (argc == 3)
600		setvar(argv[1], argv[2], 0);
601	else
602		error("List assignment not implemented");
603	return 0;
604}
605
606
607/*
608 * The unset builtin command.  We unset the function before we unset the
609 * variable to allow a function to be unset when there is a readonly variable
610 * with the same name.
611 */
612
613unsetcmd(argc, argv)  char **argv; {
614	char **ap;
615	int i;
616	int flg_func = 0;
617	int flg_var = 0;
618	int ret = 0;
619
620	while ((i = nextopt("vf")) != '\0') {
621		if (i == 'f')
622			flg_func = 1;
623		else
624			flg_var = 1;
625	}
626	if (flg_func == 0 && flg_var == 0)
627		flg_var = 1;
628
629	for (ap = argptr; *ap ; ap++) {
630		if (flg_func)
631			ret |= unsetfunc(*ap);
632		if (flg_var)
633			ret |= unsetvar(*ap);
634	}
635	return ret;
636}
637
638
639/*
640 * Unset the specified variable.
641 */
642
643STATIC int
644unsetvar(s)
645	char *s;
646	{
647	struct var **vpp;
648	struct var *vp;
649
650	vpp = hashvar(s);
651	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
652		if (varequal(vp->text, s)) {
653			if (vp->flags & VREADONLY)
654				return (1);
655			INTOFF;
656			if (*(strchr(vp->text, '=') + 1) != '\0')
657				setvar(s, nullstr, 0);
658			if ((vp->flags & VEXPORT) && localevar(vp->text)) {
659				unsetenv(s);
660				setlocale(LC_ALL, "");
661			}
662			vp->flags &=~ VEXPORT;
663			vp->flags |= VUNSET;
664			if ((vp->flags & VSTRFIXED) == 0) {
665				if ((vp->flags & VTEXTFIXED) == 0)
666					ckfree(vp->text);
667				*vpp = vp->next;
668				ckfree(vp);
669			}
670			INTON;
671			return (0);
672		}
673	}
674
675	return (1);
676}
677
678
679
680/*
681 * Find the appropriate entry in the hash table from the name.
682 */
683
684STATIC struct var **
685hashvar(p)
686	register char *p;
687	{
688	unsigned int hashval;
689
690	hashval = ((unsigned char)*p) << 4;
691	while (*p && *p != '=')
692		hashval += (unsigned char)*p++;
693	return &vartab[hashval % VTABSIZE];
694}
695
696
697
698/*
699 * Returns true if the two strings specify the same varable.  The first
700 * variable name is terminated by '='; the second may be terminated by
701 * either '=' or '\0'.
702 */
703
704STATIC int
705varequal(p, q)
706	register char *p, *q;
707	{
708	while (*p == *q++) {
709		if (*p++ == '=')
710			return 1;
711	}
712	if (*p == '=' && *(q - 1) == '\0')
713		return 1;
714	return 0;
715}
716