]> git.saurik.com Git - apple/shell_cmds.git/blob - sh/var.c
f60dea16dfdf6096eec484966ea2c69549350677
[apple/shell_cmds.git] / 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 }