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