master xplshn/aruu / cmd / posix / sh / var.c
  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}