var.c revision 1.54 1 /* $NetBSD: var.c,v 1.54 2017/05/27 06:32:12 kre Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: var.c,v 1.54 2017/05/27 06:32:12 kre Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <unistd.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <paths.h>
48 #include <limits.h>
49
50 /*
51 * Shell variables.
52 */
53
54 #include "shell.h"
55 #include "output.h"
56 #include "expand.h"
57 #include "nodes.h" /* for other headers */
58 #include "eval.h" /* defines cmdenviron */
59 #include "exec.h"
60 #include "syntax.h"
61 #include "options.h"
62 #include "builtins.h"
63 #include "mail.h"
64 #include "var.h"
65 #include "memalloc.h"
66 #include "error.h"
67 #include "mystring.h"
68 #include "parser.h"
69 #include "show.h"
70 #ifndef SMALL
71 #include "myhistedit.h"
72 #endif
73
74 #ifdef SMALL
75 #define VTABSIZE 39
76 #else
77 #define VTABSIZE 517
78 #endif
79
80
81 struct varinit {
82 struct var *var;
83 int flags;
84 const char *text;
85 void (*func)(const char *);
86 };
87
88 struct localvar *localvars;
89
90 #ifndef SMALL
91 struct var vhistsize;
92 struct var vterm;
93 #endif
94 struct var vifs;
95 struct var vmail;
96 struct var vmpath;
97 struct var vpath;
98 struct var vps1;
99 struct var vps2;
100 struct var vps4;
101 struct var vvers;
102 struct var voptind;
103
104 char ifs_default[] = " \t\n";
105
106 const struct varinit varinit[] = {
107 #ifndef SMALL
108 { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=",
109 sethistsize },
110 #endif
111 { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n",
112 NULL },
113 { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
114 NULL },
115 { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
116 NULL },
117 { &vvers, VSTRFIXED|VTEXTFIXED|VNOEXPORT, "NETBSD_SHELL=",
118 NULL },
119 { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH,
120 changepath },
121 /*
122 * vps1 depends on uid
123 */
124 { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
125 NULL },
126 { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ",
127 NULL },
128 #ifndef SMALL
129 { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=",
130 setterm },
131 #endif
132 { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1",
133 getoptsreset },
134 { NULL, 0, NULL,
135 NULL }
136 };
137
138 struct var *vartab[VTABSIZE];
139
140 STATIC int strequal(const char *, const char *);
141 STATIC struct var *find_var(const char *, struct var ***, int *);
142
143 /*
144 * Initialize the varable symbol tables and import the environment
145 */
146
147 #ifdef mkinit
148 INCLUDE <stdio.h>
149 INCLUDE <unistd.h>
150 INCLUDE "var.h"
151 INCLUDE "version.h"
152 MKINIT char **environ;
153 INIT {
154 char **envp;
155 char buf[64];
156
157 initvar();
158 for (envp = environ ; *envp ; envp++) {
159 if (strchr(*envp, '=')) {
160 setvareq(*envp, VEXPORT|VTEXTFIXED);
161 }
162 }
163
164 /*
165 * Set variables which override anything read from environment.
166 *
167 * PPID is readonly
168 * Always default IFS
169 * NETBSD_SHELL is a constant (readonly), and is never exported
170 */
171 snprintf(buf, sizeof(buf), "%d", (int)getppid());
172 setvar("PPID", buf, VREADONLY);
173 setvar("IFS", ifs_default, VTEXTFIXED);
174
175 setvar("NETBSD_SHELL", NETBSD_SHELL
176 #ifdef BUILD_DATE
177 " BUILD:" BUILD_DATE
178 #endif
179 #ifdef DEBUG
180 " DEBUG"
181 #endif
182 #if !defined(JOBS) || JOBS == 0
183 " -JOBS"
184 #endif
185 #ifndef DO_SHAREDVFORK
186 " -VFORK"
187 #endif
188 #ifdef SMALL
189 " SMALL"
190 #endif
191 #ifdef TINY
192 " TINY"
193 #endif
194 #ifdef OLD_TTY_DRIVER
195 " OLD_TTY"
196 #endif
197 #ifdef SYSV
198 " SYSV"
199 #endif
200 #ifndef BSD
201 " -BSD"
202 #endif
203 , VTEXTFIXED|VREADONLY|VNOEXPORT);
204 }
205 #endif
206
207
208 /*
209 * This routine initializes the builtin variables. It is called when the
210 * shell is initialized and again when a shell procedure is spawned.
211 */
212
213 void
214 initvar(void)
215 {
216 const struct varinit *ip;
217 struct var *vp;
218 struct var **vpp;
219
220 for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
221 if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
222 continue;
223 vp->next = *vpp;
224 *vpp = vp;
225 vp->text = strdup(ip->text);
226 vp->flags = ip->flags;
227 vp->func = ip->func;
228 }
229 /*
230 * PS1 depends on uid
231 */
232 if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
233 vps1.next = *vpp;
234 *vpp = &vps1;
235 vps1.flags = VSTRFIXED|VTEXTFIXED;
236 vps1.text = NULL;
237 choose_ps1();
238 }
239 }
240
241 void
242 choose_ps1(void)
243 {
244 free(vps1.text);
245 vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# ");
246 }
247
248 /*
249 * Safe version of setvar, returns 1 on success 0 on failure.
250 */
251
252 int
253 setvarsafe(const char *name, const char *val, int flags)
254 {
255 struct jmploc jmploc;
256 struct jmploc *volatile savehandler = handler;
257 int volatile err = 0;
258
259 if (setjmp(jmploc.loc))
260 err = 1;
261 else {
262 handler = &jmploc;
263 setvar(name, val, flags);
264 }
265 handler = savehandler;
266 return err;
267 }
268
269 /*
270 * Set the value of a variable. The flags argument is ored with the
271 * flags of the variable. If val is NULL, the variable is unset.
272 */
273
274 void
275 setvar(const char *name, const char *val, int flags)
276 {
277 const char *p;
278 const char *q;
279 char *d;
280 int len;
281 int namelen;
282 char *nameeq;
283 int isbad;
284
285 isbad = 0;
286 p = name;
287 if (! is_name(*p))
288 isbad = 1;
289 p++;
290 for (;;) {
291 if (! is_in_name(*p)) {
292 if (*p == '\0' || *p == '=')
293 break;
294 isbad = 1;
295 }
296 p++;
297 }
298 namelen = p - name;
299 if (isbad)
300 error("%.*s: bad variable name", namelen, name);
301 len = namelen + 2; /* 2 is space for '=' and '\0' */
302 if (val == NULL) {
303 flags |= VUNSET;
304 } else {
305 len += strlen(val);
306 }
307 d = nameeq = ckmalloc(len);
308 q = name;
309 while (--namelen >= 0)
310 *d++ = *q++;
311 *d++ = '=';
312 *d = '\0';
313 if (val)
314 scopy(val, d);
315 setvareq(nameeq, flags);
316 }
317
318
319
320 /*
321 * Same as setvar except that the variable and value are passed in
322 * the first argument as name=value. Since the first argument will
323 * be actually stored in the table, it should not be a string that
324 * will go away.
325 */
326
327 void
328 setvareq(char *s, int flags)
329 {
330 struct var *vp, **vpp;
331 int nlen;
332
333 if (aflag && !(flags & VNOEXPORT))
334 flags |= VEXPORT;
335 vp = find_var(s, &vpp, &nlen);
336 if (vp != NULL) {
337 if (vp->flags & VREADONLY)
338 error("%.*s: is read only", vp->name_len, s);
339 if (flags & VNOSET)
340 return;
341 INTOFF;
342
343 if (vp->func && (flags & VNOFUNC) == 0)
344 (*vp->func)(s + vp->name_len + 1);
345
346 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
347 ckfree(vp->text);
348
349 vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
350 if (flags & VNOEXPORT)
351 vp->flags &= ~VEXPORT;
352 if (vp->flags & VNOEXPORT)
353 flags &= ~VEXPORT;
354 vp->flags |= flags & ~VNOFUNC;
355 vp->text = s;
356
357 /*
358 * We could roll this to a function, to handle it as
359 * a regular variable function callback, but why bother?
360 */
361 if (vp == &vmpath || (vp == &vmail && ! mpathset()))
362 chkmail(1);
363 INTON;
364 return;
365 }
366 /* not found */
367 if (flags & VNOSET)
368 return;
369 vp = ckmalloc(sizeof (*vp));
370 vp->flags = flags & ~VNOFUNC;
371 vp->text = s;
372 vp->name_len = nlen;
373 vp->next = *vpp;
374 vp->func = NULL;
375 *vpp = vp;
376 }
377
378
379
380 /*
381 * Process a linked list of variable assignments.
382 */
383
384 void
385 listsetvar(struct strlist *list, int flags)
386 {
387 struct strlist *lp;
388
389 INTOFF;
390 for (lp = list ; lp ; lp = lp->next) {
391 setvareq(savestr(lp->text), flags);
392 }
393 INTON;
394 }
395
396 void
397 listmklocal(struct strlist *list, int flags)
398 {
399 struct strlist *lp;
400
401 for (lp = list ; lp ; lp = lp->next)
402 mklocal(lp->text, flags);
403 }
404
405
406 /*
407 * Find the value of a variable. Returns NULL if not set.
408 */
409
410 char *
411 lookupvar(const char *name)
412 {
413 struct var *v;
414
415 v = find_var(name, NULL, NULL);
416 if (v == NULL || v->flags & VUNSET)
417 return NULL;
418 return v->text + v->name_len + 1;
419 }
420
421
422
423 /*
424 * Search the environment of a builtin command. If the second argument
425 * is nonzero, return the value of a variable even if it hasn't been
426 * exported.
427 */
428
429 char *
430 bltinlookup(const char *name, int doall)
431 {
432 struct strlist *sp;
433 struct var *v;
434
435 for (sp = cmdenviron ; sp ; sp = sp->next) {
436 if (strequal(sp->text, name))
437 return strchr(sp->text, '=') + 1;
438 }
439
440 v = find_var(name, NULL, NULL);
441
442 if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT)))
443 return NULL;
444 return v->text + v->name_len + 1;
445 }
446
447
448
449 /*
450 * Generate a list of exported variables. This routine is used to construct
451 * the third argument to execve when executing a program.
452 */
453
454 char **
455 environment(void)
456 {
457 int nenv;
458 struct var **vpp;
459 struct var *vp;
460 char **env;
461 char **ep;
462
463 nenv = 0;
464 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
465 for (vp = *vpp ; vp ; vp = vp->next)
466 if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
467 nenv++;
468 }
469 ep = env = stalloc((nenv + 1) * sizeof *env);
470 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
471 for (vp = *vpp ; vp ; vp = vp->next)
472 if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
473 *ep++ = vp->text;
474 }
475 *ep = NULL;
476 return env;
477 }
478
479
480 /*
481 * Called when a shell procedure is invoked to clear out nonexported
482 * variables. It is also necessary to reallocate variables of with
483 * VSTACK set since these are currently allocated on the stack.
484 */
485
486 #ifdef mkinit
487 void shprocvar(void);
488
489 SHELLPROC {
490 shprocvar();
491 }
492 #endif
493
494 void
495 shprocvar(void)
496 {
497 struct var **vpp;
498 struct var *vp, **prev;
499
500 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
501 for (prev = vpp ; (vp = *prev) != NULL ; ) {
502 if ((vp->flags & VEXPORT) == 0) {
503 *prev = vp->next;
504 if ((vp->flags & VTEXTFIXED) == 0)
505 ckfree(vp->text);
506 if ((vp->flags & VSTRFIXED) == 0)
507 ckfree(vp);
508 } else {
509 if (vp->flags & VSTACK) {
510 vp->text = savestr(vp->text);
511 vp->flags &=~ VSTACK;
512 }
513 prev = &vp->next;
514 }
515 }
516 }
517 initvar();
518 }
519
520
521
522 /*
523 * Command to list all variables which are set. Currently this command
524 * is invoked from the set command when the set command is called without
525 * any variables.
526 */
527
528 void
529 print_quoted(const char *p)
530 {
531 const char *q;
532
533 if (p[0] == '\0') {
534 out1fmt("''");
535 return;
536 }
537 if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) {
538 out1fmt("%s", p);
539 return;
540 }
541 while (*p) {
542 if (*p == '\'') {
543 out1fmt("\\'");
544 p++;
545 continue;
546 }
547 q = strchr(p, '\'');
548 if (!q) {
549 out1fmt("'%s'", p );
550 return;
551 }
552 out1fmt("'%.*s'", (int)(q - p), p );
553 p = q;
554 }
555 }
556
557 static int
558 sort_var(const void *v_v1, const void *v_v2)
559 {
560 const struct var * const *v1 = v_v1;
561 const struct var * const *v2 = v_v2;
562 char *t1 = (*v1)->text, *t2 = (*v2)->text;
563
564 if (*t1 == *t2) {
565 char *p, *s;
566
567 STARTSTACKSTR(p);
568
569 /*
570 * note: if lengths are equal, strings must be different
571 * so we don't care which string we pick for the \0 in
572 * that case.
573 */
574 if ((strchr(t1, '=') - t1) <= (strchr(t2, '=') - t2)) {
575 s = t1;
576 t1 = p;
577 } else {
578 s = t2;
579 t2 = p;
580 }
581
582 while (*s && *s != '=') {
583 STPUTC(*s, p);
584 s++;
585 }
586 STPUTC('\0', p);
587 }
588
589 return strcoll(t1, t2);
590 }
591
592 /*
593 * POSIX requires that 'set' (but not export or readonly) output the
594 * variables in lexicographic order - by the locale's collating order (sigh).
595 * Maybe we could keep them in an ordered balanced binary tree
596 * instead of hashed lists.
597 * For now just roll 'em through qsort for printing...
598 */
599
600 int
601 showvars(const char *name, int flag, int show_value, const char *xtra)
602 {
603 struct var **vpp;
604 struct var *vp;
605 const char *p;
606
607 static struct var **list; /* static in case we are interrupted */
608 static int list_len;
609 int count = 0;
610
611 if (!list) {
612 list_len = 32;
613 list = ckmalloc(list_len * sizeof *list);
614 }
615
616 for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
617 for (vp = *vpp ; vp ; vp = vp->next) {
618 if (flag && !(vp->flags & flag))
619 continue;
620 if (vp->flags & VUNSET && !(show_value & 2))
621 continue;
622 if (count >= list_len) {
623 list = ckrealloc(list,
624 (list_len << 1) * sizeof *list);
625 list_len <<= 1;
626 }
627 list[count++] = vp;
628 }
629 }
630
631 qsort(list, count, sizeof *list, sort_var);
632
633 for (vpp = list; count--; vpp++) {
634 vp = *vpp;
635 if (name)
636 out1fmt("%s ", name);
637 if (xtra)
638 out1fmt("%s ", xtra);
639 for (p = vp->text ; *p != '=' ; p++)
640 out1c(*p);
641 if (!(vp->flags & VUNSET) && show_value) {
642 out1fmt("=");
643 print_quoted(++p);
644 }
645 out1c('\n');
646 }
647 return 0;
648 }
649
650
651
652 /*
653 * The export and readonly commands.
654 */
655
656 int
657 exportcmd(int argc, char **argv)
658 {
659 struct var *vp;
660 char *name;
661 const char *p;
662 int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
663 int pflg = 0;
664 int nflg = 0;
665 int xflg = 0;
666 int res;
667 int c;
668 int f;
669
670 while ((c = nextopt("npx")) != '\0') {
671 switch (c) {
672 case 'p':
673 if (nflg)
674 return 1;
675 pflg = 3;
676 break;
677 case 'n':
678 if (pflg || xflg || flag == VREADONLY)
679 return 1;
680 nflg = 1;
681 break;
682 case 'x':
683 if (nflg || flag == VREADONLY)
684 return 1;
685 flag = VNOEXPORT;
686 xflg = 1;
687 break;
688 default:
689 return 1;
690 }
691 }
692
693 if (nflg && *argptr == NULL)
694 return 1;
695
696 if (pflg || *argptr == NULL) {
697 showvars( pflg ? argv[0] : 0, flag, pflg,
698 pflg && xflg ? "-x" : NULL );
699 return 0;
700 }
701
702 res = 0;
703 while ((name = *argptr++) != NULL) {
704 f = flag;
705 if ((p = strchr(name, '=')) != NULL) {
706 p++;
707 } else {
708 vp = find_var(name, NULL, NULL);
709 if (vp != NULL) {
710 if (nflg)
711 vp->flags &= ~flag;
712 else if (flag&VEXPORT && vp->flags&VNOEXPORT)
713 res = 1;
714 else {
715 vp->flags |= flag;
716 if (flag == VNOEXPORT)
717 vp->flags &= ~VEXPORT;
718 }
719 continue;
720 } else
721 f |= VUNSET;
722 }
723 if (!nflg)
724 setvar(name, p, f);
725 }
726 return res;
727 }
728
729
730 /*
731 * The "local" command.
732 */
733
734 int
735 localcmd(int argc, char **argv)
736 {
737 char *name;
738 int c;
739 int flags = 0; /*XXX perhaps VUNSET from a -o option value */
740
741 if (! in_function())
742 error("Not in a function");
743
744 /* upper case options, as bash stole all the good ones ... */
745 while ((c = nextopt("INx")) != '\0')
746 switch (c) {
747 case 'I': flags &= ~VUNSET; break;
748 case 'N': flags |= VUNSET; break;
749 case 'x': flags |= VEXPORT; break;
750 }
751
752 while ((name = *argptr++) != NULL) {
753 mklocal(name, flags);
754 }
755 return 0;
756 }
757
758
759 /*
760 * Make a variable a local variable. When a variable is made local, its
761 * value and flags are saved in a localvar structure. The saved values
762 * will be restored when the shell function returns. We handle the name
763 * "-" as a special case.
764 */
765
766 void
767 mklocal(const char *name, int flags)
768 {
769 struct localvar *lvp;
770 struct var **vpp;
771 struct var *vp;
772
773 INTOFF;
774 lvp = ckmalloc(sizeof (struct localvar));
775 if (name[0] == '-' && name[1] == '\0') {
776 char *p;
777 p = ckmalloc(sizeof_optlist);
778 lvp->text = memcpy(p, optlist, sizeof_optlist);
779 vp = NULL;
780 } else {
781 vp = find_var(name, &vpp, NULL);
782 if (vp == NULL) {
783 flags &= ~VNOEXPORT;
784 if (strchr(name, '='))
785 setvareq(savestr(name),
786 VSTRFIXED | (flags & ~VUNSET));
787 else
788 setvar(name, NULL, VSTRFIXED|flags);
789 vp = *vpp; /* the new variable */
790 lvp->text = NULL;
791 lvp->flags = VUNSET;
792 } else {
793 lvp->text = vp->text;
794 lvp->flags = vp->flags;
795 vp->flags |= VSTRFIXED|VTEXTFIXED;
796 if (vp->flags & VNOEXPORT)
797 flags &= ~VEXPORT;
798 if (flags & (VNOEXPORT | VUNSET))
799 vp->flags &= ~VEXPORT;
800 flags &= ~VNOEXPORT;
801 if (name[vp->name_len] == '=')
802 setvareq(savestr(name), flags & ~VUNSET);
803 else if (flags & VUNSET)
804 unsetvar(name, 0);
805 else
806 vp->flags |= flags & (VUNSET|VEXPORT);
807 }
808 }
809 lvp->vp = vp;
810 lvp->next = localvars;
811 localvars = lvp;
812 INTON;
813 }
814
815
816 /*
817 * Called after a function returns.
818 */
819
820 void
821 poplocalvars(void)
822 {
823 struct localvar *lvp;
824 struct var *vp;
825
826 while ((lvp = localvars) != NULL) {
827 localvars = lvp->next;
828 vp = lvp->vp;
829 TRACE(("poplocalvar %s", vp ? vp->text : "-"));
830 if (vp == NULL) { /* $- saved */
831 memcpy(optlist, lvp->text, sizeof_optlist);
832 ckfree(lvp->text);
833 } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
834 (void)unsetvar(vp->text, 0);
835 } else {
836 if (vp->func && (vp->flags & VNOFUNC) == 0)
837 (*vp->func)(lvp->text + vp->name_len + 1);
838 if ((vp->flags & VTEXTFIXED) == 0)
839 ckfree(vp->text);
840 vp->flags = lvp->flags;
841 vp->text = lvp->text;
842 }
843 ckfree(lvp);
844 }
845 }
846
847
848 int
849 setvarcmd(int argc, char **argv)
850 {
851 if (argc <= 2)
852 return unsetcmd(argc, argv);
853 else if (argc == 3)
854 setvar(argv[1], argv[2], 0);
855 else
856 error("List assignment not implemented");
857 return 0;
858 }
859
860
861 /*
862 * The unset builtin command. We unset the function before we unset the
863 * variable to allow a function to be unset when there is a readonly variable
864 * with the same name.
865 */
866
867 int
868 unsetcmd(int argc, char **argv)
869 {
870 char **ap;
871 int i;
872 int flg_func = 0;
873 int flg_var = 0;
874 int flg_x = 0;
875 int ret = 0;
876
877 while ((i = nextopt("efvx")) != '\0') {
878 switch (i) {
879 case 'f':
880 flg_func = 1;
881 break;
882 case 'e':
883 case 'x':
884 flg_x = (2 >> (i == 'e'));
885 /* FALLTHROUGH */
886 case 'v':
887 flg_var = 1;
888 break;
889 }
890 }
891
892 if (flg_func == 0 && flg_var == 0)
893 flg_var = 1;
894
895 for (ap = argptr; *ap ; ap++) {
896 if (flg_func)
897 ret |= unsetfunc(*ap);
898 if (flg_var)
899 ret |= unsetvar(*ap, flg_x);
900 }
901 return ret;
902 }
903
904
905 /*
906 * Unset the specified variable.
907 */
908
909 int
910 unsetvar(const char *s, int unexport)
911 {
912 struct var **vpp;
913 struct var *vp;
914
915 vp = find_var(s, &vpp, NULL);
916 if (vp == NULL)
917 return 0;
918
919 if (vp->flags & VREADONLY && !(unexport & 1))
920 return 1;
921
922 INTOFF;
923 if (unexport & 1) {
924 vp->flags &= ~VEXPORT;
925 } else {
926 if (vp->text[vp->name_len + 1] != '\0')
927 setvar(s, nullstr, 0);
928 if (!(unexport & 2))
929 vp->flags &= ~VEXPORT;
930 vp->flags |= VUNSET;
931 if ((vp->flags&(VEXPORT|VSTRFIXED|VREADONLY|VNOEXPORT)) == 0) {
932 if ((vp->flags & VTEXTFIXED) == 0)
933 ckfree(vp->text);
934 *vpp = vp->next;
935 ckfree(vp);
936 }
937 }
938 INTON;
939 return 0;
940 }
941
942
943 /*
944 * Returns true if the two strings specify the same varable. The first
945 * variable name is terminated by '='; the second may be terminated by
946 * either '=' or '\0'.
947 */
948
949 STATIC int
950 strequal(const char *p, const char *q)
951 {
952 while (*p == *q++) {
953 if (*p++ == '=')
954 return 1;
955 }
956 if (*p == '=' && *(q - 1) == '\0')
957 return 1;
958 return 0;
959 }
960
961 /*
962 * Search for a variable.
963 * 'name' may be terminated by '=' or a NUL.
964 * vppp is set to the pointer to vp, or the list head if vp isn't found
965 * lenp is set to the number of charactets in 'name'
966 */
967
968 STATIC struct var *
969 find_var(const char *name, struct var ***vppp, int *lenp)
970 {
971 unsigned int hashval;
972 int len;
973 struct var *vp, **vpp;
974 const char *p = name;
975
976 hashval = 0;
977 while (*p && *p != '=')
978 hashval = 2 * hashval + (unsigned char)*p++;
979 len = p - name;
980
981 if (lenp)
982 *lenp = len;
983 vpp = &vartab[hashval % VTABSIZE];
984 if (vppp)
985 *vppp = vpp;
986
987 for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
988 if (vp->name_len != len)
989 continue;
990 if (memcmp(vp->text, name, len) != 0)
991 continue;
992 if (vppp)
993 *vppp = vpp;
994 return vp;
995 }
996 return NULL;
997 }
998