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