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