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