summaryrefslogtreecommitdiff
path: root/bin/sh/var.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/sh/var.c')
-rw-r--r--bin/sh/var.c1587
1 files changed, 0 insertions, 1587 deletions
diff --git a/bin/sh/var.c b/bin/sh/var.c
deleted file mode 100644
index 378598c..0000000
--- a/bin/sh/var.c
+++ /dev/null
@@ -1,1587 +0,0 @@
-/* $NetBSD: var.c,v 1.75 2019/01/21 13:27:29 kre Exp $ */
-
-/*-
- * Copyright (c) 1991, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Kenneth Almquist.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#ifndef lint
-#if 0
-static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95";
-#else
-__RCSID("$NetBSD: var.c,v 1.75 2019/01/21 13:27:29 kre Exp $");
-#endif
-#endif /* not lint */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <paths.h>
-#include <limits.h>
-#include <time.h>
-#include <pwd.h>
-#include <fcntl.h>
-#include <inttypes.h>
-
-/*
- * Shell variables.
- */
-
-#include "shell.h"
-#include "output.h"
-#include "expand.h"
-#include "nodes.h" /* for other headers */
-#include "eval.h" /* defines cmdenviron */
-#include "exec.h"
-#include "syntax.h"
-#include "options.h"
-#include "builtins.h"
-#include "mail.h"
-#include "var.h"
-#include "memalloc.h"
-#include "error.h"
-#include "mystring.h"
-#include "parser.h"
-#include "show.h"
-#include "machdep.h"
-#ifndef SMALL
-#include "myhistedit.h"
-#endif
-
-#ifdef SMALL
-#define VTABSIZE 39
-#else
-#define VTABSIZE 517
-#endif
-
-
-struct varinit {
- struct var *var;
- int flags;
- const char *text;
- union var_func_union v_u;
-};
-#define func v_u.set_func
-#define rfunc v_u.ref_func
-
-char *get_lineno(struct var *);
-
-#ifndef SMALL
-char *get_tod(struct var *);
-char *get_hostname(struct var *);
-char *get_seconds(struct var *);
-char *get_euser(struct var *);
-char *get_random(struct var *);
-#endif
-
-struct localvar *localvars;
-
-#ifndef SMALL
-struct var vhistsize;
-struct var vterm;
-struct var editrc;
-struct var ps_lit;
-#endif
-struct var vifs;
-struct var vmail;
-struct var vmpath;
-struct var vpath;
-struct var vps1;
-struct var vps2;
-struct var vps4;
-struct var vvers;
-struct var voptind;
-struct var line_num;
-#ifndef SMALL
-struct var tod;
-struct var host_name;
-struct var seconds;
-struct var euname;
-struct var random_num;
-
-intmax_t sh_start_time;
-#endif
-
-struct var line_num;
-int line_number;
-int funclinebase = 0;
-int funclineabs = 0;
-
-char ifs_default[] = " \t\n";
-
-const struct varinit varinit[] = {
-#ifndef SMALL
- { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=",
- { .set_func= sethistsize } },
-#endif
- { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n",
- { NULL } },
- { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
- { NULL } },
- { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
- { NULL } },
- { &vvers, VSTRFIXED|VTEXTFIXED|VNOEXPORT, "NETBSD_SHELL=",
- { NULL } },
- { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH,
- { .set_func= changepath } },
- /*
- * vps1 depends on uid
- */
- { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
- { NULL } },
- { &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ",
- { NULL } },
-#ifndef SMALL
- { &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=",
- { .set_func= setterm } },
- { &editrc, VSTRFIXED|VTEXTFIXED|VUNSET, "EDITRC=",
- { .set_func= set_editrc } },
- { &ps_lit, VSTRFIXED|VTEXTFIXED|VUNSET, "PSlit=",
- { .set_func= set_prompt_lit } },
-#endif
- { &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1",
- { .set_func= getoptsreset } },
- { &line_num, VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL, "LINENO=1",
- { .ref_func= get_lineno } },
-#ifndef SMALL
- { &tod, VSTRFIXED|VTEXTFIXED|VFUNCREF, "ToD=",
- { .ref_func= get_tod } },
- { &host_name, VSTRFIXED|VTEXTFIXED|VFUNCREF, "HOSTNAME=",
- { .ref_func= get_hostname } },
- { &seconds, VSTRFIXED|VTEXTFIXED|VFUNCREF, "SECONDS=",
- { .ref_func= get_seconds } },
- { &euname, VSTRFIXED|VTEXTFIXED|VFUNCREF, "EUSER=",
- { .ref_func= get_euser } },
- { &random_num, VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL, "RANDOM=",
- { .ref_func= get_random } },
-#endif
- { NULL, 0, NULL,
- { NULL } }
-};
-
-struct var *vartab[VTABSIZE];
-
-STATIC int strequal(const char *, const char *);
-STATIC struct var *find_var(const char *, struct var ***, int *);
-STATIC void showvar(struct var *, const char *, const char *, int);
-static void export_usage(const char *) __dead;
-
-/*
- * Initialize the varable symbol tables and import the environment
- */
-
-#ifdef mkinit
-INCLUDE <stdio.h>
-INCLUDE <unistd.h>
-INCLUDE <time.h>
-INCLUDE "var.h"
-INCLUDE "version.h"
-MKINIT char **environ;
-INIT {
- char **envp;
- char buf[64];
-
-#ifndef SMALL
- sh_start_time = (intmax_t)time((time_t *)0);
-#endif
- /*
- * Set up our default variables and their values.
- */
- initvar();
-
- /*
- * Import variables from the environment, which will
- * override anything initialised just previously.
- */
- for (envp = environ ; *envp ; envp++) {
- if (strchr(*envp, '=')) {
- setvareq(*envp, VEXPORT|VTEXTFIXED);
- }
- }
-
- /*
- * Set variables which override anything read from environment.
- *
- * PPID is readonly
- * Always default IFS
- * POSIX: "Whenever the shell is invoked, OPTIND shall
- * be initialized to 1."
- * PSc indicates the root/non-root status of this shell.
- * START_TIME belongs only to this shell.
- * NETBSD_SHELL is a constant (readonly), and is never exported
- * LINENO is simply magic...
- */
- snprintf(buf, sizeof(buf), "%d", (int)getppid());
- setvar("PPID", buf, VREADONLY);
- setvar("IFS", ifs_default, VTEXTFIXED);
- setvar("OPTIND", "1", VTEXTFIXED);
- setvar("PSc", (geteuid() == 0 ? "#" : "$"), VTEXTFIXED);
-
-#ifndef SMALL
- snprintf(buf, sizeof(buf), "%jd", sh_start_time);
- setvar("START_TIME", buf, VTEXTFIXED);
-#endif
-
- setvar("NETBSD_SHELL", NETBSD_SHELL
-#ifdef BUILD_DATE
- " BUILD:" BUILD_DATE
-#endif
-#ifdef DEBUG
- " DEBUG"
-#endif
-#if !defined(JOBS) || JOBS == 0
- " -JOBS"
-#endif
-#ifndef DO_SHAREDVFORK
- " -VFORK"
-#endif
-#ifdef SMALL
- " SMALL"
-#endif
-#ifdef TINY
- " TINY"
-#endif
-#ifdef OLD_TTY_DRIVER
- " OLD_TTY"
-#endif
-#ifdef SYSV
- " SYSV"
-#endif
-#ifndef BSD
- " -BSD"
-#endif
-#ifdef BOGUS_NOT_COMMAND
- " BOGUS_NOT"
-#endif
- , VTEXTFIXED|VREADONLY|VNOEXPORT);
-
- setvar("LINENO", "1", VTEXTFIXED);
-}
-#endif
-
-
-/*
- * This routine initializes the builtin variables. It is called when the
- * shell is initialized and again when a shell procedure is spawned.
- */
-
-void
-initvar(void)
-{
- const struct varinit *ip;
- struct var *vp;
- struct var **vpp;
-
- for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
- if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
- continue;
- vp->next = *vpp;
- *vpp = vp;
- vp->text = strdup(ip->text);
- vp->flags = (ip->flags & ~VTEXTFIXED) | VSTRFIXED;
- vp->v_u = ip->v_u;
- }
- /*
- * PS1 depends on uid
- */
- if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
- vps1.next = *vpp;
- *vpp = &vps1;
- vps1.flags = VSTRFIXED;
- vps1.text = NULL;
- choose_ps1();
- }
-}
-
-void
-choose_ps1(void)
-{
- uid_t u = geteuid();
-
- if ((vps1.flags & (VTEXTFIXED|VSTACK)) == 0)
- free(vps1.text);
- vps1.text = strdup(u != 0 ? "PS1=$ " : "PS1=# ");
- vps1.flags &= ~(VTEXTFIXED|VSTACK);
-
- /*
- * Update PSc whenever we feel the need to update PS1
- */
- setvarsafe("PSc", (u == 0 ? "#" : "$"), 0);
-}
-
-/*
- * Validate a string as a valid variable name
- * nb: not parameter - special params and such are "invalid" here.
- * Name terminated by either \0 or the term param (usually '=' or '\0').
- *
- * If not NULL, the length of the (intended) name is returned via len
- */
-
-int
-validname(const char *name, int term, int *len)
-{
- const char *p = name;
- int ok = 1;
-
- if (p == NULL || *p == '\0' || *p == term) {
- if (len != NULL)
- *len = 0;
- return 0;
- }
-
- if (!is_name(*p))
- ok = 0;
- p++;
- for (;;) {
- if (*p == '\0' || *p == term)
- break;
- if (!is_in_name(*p))
- ok = 0;
- p++;
- }
- if (len != NULL)
- *len = p - name;
-
- return ok;
-}
-
-/*
- * Safe version of setvar, returns 1 on success 0 on failure.
- */
-
-int
-setvarsafe(const char *name, const char *val, int flags)
-{
- struct jmploc jmploc;
- struct jmploc * const savehandler = handler;
- int volatile err = 0;
-
- if (setjmp(jmploc.loc))
- err = 1;
- else {
- handler = &jmploc;
- setvar(name, val, flags);
- }
- handler = savehandler;
- return err;
-}
-
-/*
- * Set the value of a variable. The flags argument is ored with the
- * flags of the variable. If val is NULL, the variable is unset.
- *
- * This always copies name and val when setting a variable, so
- * the source strings can be from anywhere, and are no longer needed
- * after this function returns. The VTEXTFIXED and VSTACK flags should
- * not be used (but just in case they were, clear them.)
- */
-
-void
-setvar(const char *name, const char *val, int flags)
-{
- const char *p;
- const char *q;
- char *d;
- int len;
- int namelen;
- char *nameeq;
-
- p = name;
-
- if (!validname(p, '=', &namelen))
- error("%.*s: bad variable name", namelen, name);
- len = namelen + 2; /* 2 is space for '=' and '\0' */
- if (val == NULL) {
- flags |= VUNSET;
- } else {
- len += strlen(val);
- }
- d = nameeq = ckmalloc(len);
- q = name;
- while (--namelen >= 0)
- *d++ = *q++;
- *d++ = '=';
- *d = '\0';
- if (val)
- scopy(val, d);
- setvareq(nameeq, flags & ~(VTEXTFIXED | VSTACK));
-}
-
-
-
-/*
- * Same as setvar except that the variable and value are passed in
- * the first argument as name=value. Since the first argument will
- * be actually stored in the table, it should not be a string that
- * will go away. The flags (VTEXTFIXED or VSTACK) can be used to
- * indicate the source of the string (if neither is set, the string will
- * eventually be free()d when a replacement value is assigned.)
- */
-
-void
-setvareq(char *s, int flags)
-{
- struct var *vp, **vpp;
- int nlen;
-
- VTRACE(DBG_VARS, ("setvareq([%s],%#x) aflag=%d ", s, flags, aflag));
- if (aflag && !(flags & VNOEXPORT))
- flags |= VEXPORT;
- vp = find_var(s, &vpp, &nlen);
- if (vp != NULL) {
- VTRACE(DBG_VARS, ("was [%s] fl:%#x\n", vp->text,
- vp->flags));
- if (vp->flags & VREADONLY) {
- if ((flags & (VTEXTFIXED|VSTACK)) == 0)
- ckfree(s);
- if (flags & VNOERROR)
- return;
- error("%.*s: is read only", vp->name_len, vp->text);
- }
- if (flags & VNOSET) {
- if ((flags & (VTEXTFIXED|VSTACK)) == 0)
- ckfree(s);
- return;
- }
-
- INTOFF;
-
- if (vp->func && !(vp->flags & VFUNCREF) && !(flags & VNOFUNC))
- (*vp->func)(s + vp->name_len + 1);
-
- if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
- ckfree(vp->text);
-
- /*
- * if we set a magic var, the magic dissipates,
- * unless it is very special indeed.
- */
- if (vp->rfunc && (vp->flags & (VFUNCREF|VSPECIAL)) == VFUNCREF)
- vp->rfunc = NULL;
-
- vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
- if (flags & VNOEXPORT)
- vp->flags &= ~VEXPORT;
- if (flags & VDOEXPORT)
- vp->flags &= ~VNOEXPORT;
- if (vp->flags & VNOEXPORT)
- flags &= ~VEXPORT;
- vp->flags |= flags & ~(VNOFUNC | VDOEXPORT);
- vp->text = s;
-
- /*
- * We could roll this to a function, to handle it as
- * a regular variable function callback, but why bother?
- */
- if (vp == &vmpath || (vp == &vmail && ! mpathset()))
- chkmail(1);
-
- INTON;
- return;
- }
- /* not found */
- if (flags & VNOSET) {
- VTRACE(DBG_VARS, ("new noset\n"));
- if ((flags & (VTEXTFIXED|VSTACK)) == 0)
- ckfree(s);
- return;
- }
- vp = ckmalloc(sizeof (*vp));
- vp->flags = flags & ~(VNOFUNC|VFUNCREF|VDOEXPORT);
- vp->text = s;
- vp->name_len = nlen;
- vp->func = NULL;
- vp->next = *vpp;
- *vpp = vp;
-
- VTRACE(DBG_VARS, ("new [%s] (%d) %#x\n", s, nlen, vp->flags));
-}
-
-
-
-/*
- * Process a linked list of variable assignments.
- */
-
-void
-listsetvar(struct strlist *list, int flags)
-{
- struct strlist *lp;
-
- INTOFF;
- for (lp = list ; lp ; lp = lp->next) {
- setvareq(savestr(lp->text), flags);
- }
- INTON;
-}
-
-void
-listmklocal(struct strlist *list, int flags)
-{
- struct strlist *lp;
-
- for (lp = list ; lp ; lp = lp->next)
- mklocal(lp->text, flags);
-}
-
-
-/*
- * Find the value of a variable. Returns NULL if not set.
- */
-
-char *
-lookupvar(const char *name)
-{
- struct var *v;
-
- v = find_var(name, NULL, NULL);
- if (v == NULL || v->flags & VUNSET)
- return NULL;
- if (v->rfunc && (v->flags & VFUNCREF) != 0)
- return (*v->rfunc)(v) + v->name_len + 1;
- return v->text + v->name_len + 1;
-}
-
-
-
-/*
- * Search the environment of a builtin command. If the second argument
- * is nonzero, return the value of a variable even if it hasn't been
- * exported.
- */
-
-char *
-bltinlookup(const char *name, int doall)
-{
- struct strlist *sp;
- struct var *v;
-
- for (sp = cmdenviron ; sp ; sp = sp->next) {
- if (strequal(sp->text, name))
- return strchr(sp->text, '=') + 1;
- }
-
- v = find_var(name, NULL, NULL);
-
- if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT)))
- return NULL;
- if (v->rfunc && (v->flags & VFUNCREF) != 0)
- return (*v->rfunc)(v) + v->name_len + 1;
- return v->text + v->name_len + 1;
-}
-
-
-
-/*
- * Generate a list of exported variables. This routine is used to construct
- * the third argument to execve when executing a program.
- */
-
-char **
-environment(void)
-{
- int nenv;
- struct var **vpp;
- struct var *vp;
- char **env;
- char **ep;
-
- nenv = 0;
- for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
- for (vp = *vpp ; vp ; vp = vp->next)
- if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
- nenv++;
- }
- CTRACE(DBG_VARS, ("environment: %d vars to export\n", nenv));
- ep = env = stalloc((nenv + 1) * sizeof *env);
- for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
- for (vp = *vpp ; vp ; vp = vp->next)
- if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) {
- if (vp->rfunc && (vp->flags & VFUNCREF))
- *ep++ = (*vp->rfunc)(vp);
- else
- *ep++ = vp->text;
- VTRACE(DBG_VARS, ("environment: %s\n", ep[-1]));
- }
- }
- *ep = NULL;
- return env;
-}
-
-
-/*
- * Called when a shell procedure is invoked to clear out nonexported
- * variables. It is also necessary to reallocate variables of with
- * VSTACK set since these are currently allocated on the stack.
- */
-
-#ifdef mkinit
-void shprocvar(void);
-
-SHELLPROC {
- shprocvar();
-}
-#endif
-
-void
-shprocvar(void)
-{
- struct var **vpp;
- struct var *vp, **prev;
-
- for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
- for (prev = vpp ; (vp = *prev) != NULL ; ) {
- if ((vp->flags & VEXPORT) == 0) {
- *prev = vp->next;
- if ((vp->flags & VTEXTFIXED) == 0)
- ckfree(vp->text);
- if ((vp->flags & VSTRFIXED) == 0)
- ckfree(vp);
- } else {
- if (vp->flags & VSTACK) {
- vp->text = savestr(vp->text);
- vp->flags &=~ VSTACK;
- }
- prev = &vp->next;
- }
- }
- }
- initvar();
-}
-
-
-
-/*
- * Command to list all variables which are set. Currently this command
- * is invoked from the set command when the set command is called without
- * any variables.
- */
-
-void
-print_quoted(const char *p)
-{
- const char *q;
-
- if (p[0] == '\0') {
- out1fmt("''");
- return;
- }
- if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) {
- out1fmt("%s", p);
- return;
- }
- while (*p) {
- if (*p == '\'') {
- out1fmt("\\'");
- p++;
- continue;
- }
- q = strchr(p, '\'');
- if (!q) {
- out1fmt("'%s'", p );
- return;
- }
- out1fmt("'%.*s'", (int)(q - p), p );
- p = q;
- }
-}
-
-static int
-sort_var(const void *v_v1, const void *v_v2)
-{
- const struct var * const *v1 = v_v1;
- const struct var * const *v2 = v_v2;
- char *t1 = (*v1)->text, *t2 = (*v2)->text;
-
- if (*t1 == *t2) {
- char *p, *s;
-
- STARTSTACKSTR(p);
-
- /*
- * note: if lengths are equal, strings must be different
- * so we don't care which string we pick for the \0 in
- * that case.
- */
- if ((strchr(t1, '=') - t1) <= (strchr(t2, '=') - t2)) {
- s = t1;
- t1 = p;
- } else {
- s = t2;
- t2 = p;
- }
-
- while (*s && *s != '=') {
- STPUTC(*s, p);
- s++;
- }
- STPUTC('\0', p);
- }
-
- return strcoll(t1, t2);
-}
-
-/*
- * POSIX requires that 'set' (but not export or readonly) output the
- * variables in lexicographic order - by the locale's collating order (sigh).
- * Maybe we could keep them in an ordered balanced binary tree
- * instead of hashed lists.
- * For now just roll 'em through qsort for printing...
- */
-
-STATIC void
-showvar(struct var *vp, const char *cmd, const char *xtra, int show_value)
-{
- const char *p;
-
- if (cmd)
- out1fmt("%s ", cmd);
- if (xtra)
- out1fmt("%s ", xtra);
- p = vp->text;
- if (vp->rfunc && (vp->flags & VFUNCREF) != 0) {
- p = (*vp->rfunc)(vp);
- if (p == NULL)
- p = vp->text;
- }
- for ( ; *p != '=' ; p++)
- out1c(*p);
- if (!(vp->flags & VUNSET) && show_value) {
- out1fmt("=");
- print_quoted(++p);
- }
- out1c('\n');
-}
-
-int
-showvars(const char *cmd, int flag, int show_value, const char *xtra)
-{
- struct var **vpp;
- struct var *vp;
-
- static struct var **list; /* static in case we are interrupted */
- static int list_len;
- int count = 0;
-
- if (!list) {
- list_len = 32;
- list = ckmalloc(list_len * sizeof *list);
- }
-
- for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
- for (vp = *vpp ; vp ; vp = vp->next) {
- if (flag && !(vp->flags & flag))
- continue;
- if (vp->flags & VUNSET && !(show_value & 2))
- continue;
- if (count >= list_len) {
- list = ckrealloc(list,
- (list_len << 1) * sizeof *list);
- list_len <<= 1;
- }
- list[count++] = vp;
- }
- }
-
- qsort(list, count, sizeof *list, sort_var);
-
- for (vpp = list; count--; vpp++)
- showvar(*vpp, cmd, xtra, show_value);
-
- /* no free(list), will be used again next time ... */
-
- return 0;
-}
-
-
-
-/*
- * The export and readonly commands.
- */
-
-static void __dead
-export_usage(const char *cmd)
-{
-#ifdef SMALL
- if (*cmd == 'r')
- error("Usage: %s [ -p | var[=val]... ]", cmd);
- else
- error("Usage: %s [ -p | [-n] var[=val]... ]", cmd);
-#else
- if (*cmd == 'r')
- error("Usage: %s [-p [var...] | -q var... | var[=val]... ]", cmd);
- else
- error(
- "Usage: %s [ -px [var...] | -q[x] var... | [-n|x] var[=val]... ]",
- cmd);
-#endif
-}
-
-int
-exportcmd(int argc, char **argv)
-{
- struct var *vp;
- char *name;
- const char *p = argv[0];
- int flag = p[0] == 'r'? VREADONLY : VEXPORT;
- int pflg = 0;
- int nflg = 0;
-#ifndef SMALL
- int xflg = 0;
- int qflg = 0;
-#endif
- int res;
- int c;
- int f;
-
-#ifdef SMALL
-#define EXPORT_OPTS "np"
-#else
-#define EXPORT_OPTS "npqx"
-#endif
-
- while ((c = nextopt(EXPORT_OPTS)) != '\0') {
-
-#undef EXPORT_OPTS
-
- switch (c) {
- case 'n':
- if (pflg || flag == VREADONLY
-#ifndef SMALL
- || qflg || xflg
-#endif
- )
- export_usage(p);
- nflg = 1;
- break;
- case 'p':
- if (nflg
-#ifndef SMALL
- || qflg
-#endif
- )
- export_usage(p);
- pflg = 3;
- break;
-#ifndef SMALL
- case 'q':
- if (nflg || pflg)
- export_usage(p);
- qflg = 1;
- break;
- case 'x':
- if (nflg || flag == VREADONLY)
- export_usage(p);
- flag = VNOEXPORT;
- xflg = 1;
- break;
-#endif
- }
- }
-
- if ((nflg
-#ifndef SMALL
- || qflg
-#endif
- ) && *argptr == NULL)
- export_usage(p);
-
-#ifndef SMALL
- if (pflg && *argptr != NULL) {
- while ((name = *argptr++) != NULL) {
- int len;
-
- vp = find_var(name, NULL, &len);
- if (name[len] == '=')
- export_usage(p);
- if (!goodname(name))
- error("%s: bad variable name", name);
-
- if (vp && vp->flags & flag)
- showvar(vp, p, xflg ? "-x" : NULL, 1);
- }
- return 0;
- }
-#endif
-
- if (pflg || *argptr == NULL)
- return showvars( pflg ? p : 0, flag, pflg,
-#ifndef SMALL
- pflg && xflg ? "-x" :
-#endif
- NULL );
-
- res = 0;
-#ifndef SMALL
- if (qflg) {
- while ((name = *argptr++) != NULL) {
- int len;
-
- vp = find_var(name, NULL, &len);
- if (name[len] == '=')
- export_usage(p);
- if (!goodname(name))
- error("%s: bad variable name", name);
-
- if (vp == NULL || !(vp->flags & flag))
- res = 1;
- }
- return res;
- }
-#endif
-
- while ((name = *argptr++) != NULL) {
- int len;
-
- f = flag;
-
- vp = find_var(name, NULL, &len);
- p = name + len;
- if (*p++ != '=')
- p = NULL;
-
- if (vp != NULL) {
- if (nflg)
- vp->flags &= ~flag;
- else if (flag&VEXPORT && vp->flags&VNOEXPORT) {
- /* note we go ahead and do any assignment */
- sh_warnx("%.*s: not available for export",
- len, name);
- res = 1;
- } else {
- if (flag == VNOEXPORT)
- vp->flags &= ~VEXPORT;
-
- /* if not NULL will be done in setvar below */
- if (p == NULL)
- vp->flags |= flag;
- }
- if (p == NULL)
- continue;
- } else if (nflg && p == NULL && !goodname(name))
- error("%s: bad variable name", name);
-
- if (!nflg || p != NULL)
- setvar(name, p, f);
- }
- return res;
-}
-
-
-/*
- * The "local" command.
- */
-
-int
-localcmd(int argc, char **argv)
-{
- char *name;
- int c;
- int flags = 0; /*XXX perhaps VUNSET from a -o option value */
-
- if (! in_function())
- error("Not in a function");
-
- /* upper case options, as bash stole all the good ones ... */
- while ((c = nextopt("INx")) != '\0')
- switch (c) {
- case 'I': flags &= ~VUNSET; break;
- case 'N': flags |= VUNSET; break;
- case 'x': flags |= VEXPORT; break;
- }
-
- while ((name = *argptr++) != NULL) {
- mklocal(name, flags);
- }
- return 0;
-}
-
-
-/*
- * Make a variable a local variable. When a variable is made local, its
- * value and flags are saved in a localvar structure. The saved values
- * will be restored when the shell function returns. We handle the name
- * "-" as a special case.
- */
-
-void
-mklocal(const char *name, int flags)
-{
- struct localvar *lvp;
- struct var **vpp;
- struct var *vp;
-
- INTOFF;
- lvp = ckmalloc(sizeof (struct localvar));
- if (name[0] == '-' && name[1] == '\0') {
- char *p;
- p = ckmalloc(sizeof_optlist);
- lvp->text = memcpy(p, optlist, sizeof_optlist);
- lvp->rfunc = NULL;
- vp = NULL;
- xtrace_clone(0);
- } else {
- vp = find_var(name, &vpp, NULL);
- if (vp == NULL) {
- flags &= ~VNOEXPORT;
- if (strchr(name, '='))
- setvareq(savestr(name),
- VSTRFIXED | (flags & ~VUNSET));
- else
- setvar(name, NULL, VSTRFIXED|flags);
- vp = *vpp; /* the new variable */
- lvp->text = NULL;
- lvp->flags = VUNSET;
- lvp->rfunc = NULL;
- } else {
- lvp->text = vp->text;
- lvp->flags = vp->flags;
- lvp->v_u = vp->v_u;
- vp->flags |= VSTRFIXED|VTEXTFIXED;
- if (flags & (VDOEXPORT | VUNSET))
- vp->flags &= ~VNOEXPORT;
- if (vp->flags & VNOEXPORT &&
- (flags & (VEXPORT|VDOEXPORT|VUNSET)) == VEXPORT)
- flags &= ~VEXPORT;
- if (flags & (VNOEXPORT | VUNSET))
- vp->flags &= ~VEXPORT;
- flags &= ~VNOEXPORT;
- if (name[vp->name_len] == '=')
- setvareq(savestr(name), flags & ~VUNSET);
- else if (flags & VUNSET)
- unsetvar(name, 0);
- else
- vp->flags |= flags & (VUNSET|VEXPORT);
-
- if (vp == &line_num) {
- if (name[vp->name_len] == '=')
- funclinebase = funclineabs -1;
- else
- funclinebase = 0;
- }
- }
- }
- lvp->vp = vp;
- lvp->next = localvars;
- localvars = lvp;
- INTON;
-}
-
-
-/*
- * Called after a function returns.
- */
-
-void
-poplocalvars(void)
-{
- struct localvar *lvp;
- struct var *vp;
-
- while ((lvp = localvars) != NULL) {
- localvars = lvp->next;
- vp = lvp->vp;
- VTRACE(DBG_VARS, ("poplocalvar %s\n", vp ? vp->text : "-"));
- if (vp == NULL) { /* $- saved */
- memcpy(optlist, lvp->text, sizeof_optlist);
- ckfree(lvp->text);
- xtrace_pop();
- optschanged();
- } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
- (void)unsetvar(vp->text, 0);
- } else {
- if (lvp->func && (lvp->flags & (VNOFUNC|VFUNCREF)) == 0)
- (*lvp->func)(lvp->text + vp->name_len + 1);
- if ((vp->flags & VTEXTFIXED) == 0)
- ckfree(vp->text);
- vp->flags = lvp->flags;
- vp->text = lvp->text;
- vp->v_u = lvp->v_u;
- }
- ckfree(lvp);
- }
-}
-
-
-int
-setvarcmd(int argc, char **argv)
-{
- if (argc <= 2)
- return unsetcmd(argc, argv);
- else if (argc == 3)
- setvar(argv[1], argv[2], 0);
- else
- error("List assignment not implemented");
- return 0;
-}
-
-
-/*
- * The unset builtin command. We unset the function before we unset the
- * variable to allow a function to be unset when there is a readonly variable
- * with the same name.
- */
-
-int
-unsetcmd(int argc, char **argv)
-{
- char **ap;
- int i;
- int flg_func = 0;
- int flg_var = 0;
- int flg_x = 0;
- int ret = 0;
-
- while ((i = nextopt("efvx")) != '\0') {
- switch (i) {
- case 'f':
- flg_func = 1;
- break;
- case 'e':
- case 'x':
- flg_x = (2 >> (i == 'e'));
- /* FALLTHROUGH */
- case 'v':
- flg_var = 1;
- break;
- }
- }
-
- if (flg_func == 0 && flg_var == 0)
- flg_var = 1;
-
- for (ap = argptr; *ap ; ap++) {
- if (flg_func)
- ret |= unsetfunc(*ap);
- if (flg_var)
- ret |= unsetvar(*ap, flg_x);
- }
- return ret;
-}
-
-
-/*
- * Unset the specified variable.
- */
-
-int
-unsetvar(const char *s, int unexport)
-{
- struct var **vpp;
- struct var *vp;
-
- vp = find_var(s, &vpp, NULL);
- if (vp == NULL)
- return 0;
-
- if (vp->flags & VREADONLY && !(unexport & 1))
- return 1;
-
- INTOFF;
- if (unexport & 1) {
- vp->flags &= ~VEXPORT;
- } else {
- if (vp->text[vp->name_len + 1] != '\0')
- setvar(s, nullstr, 0);
- if (!(unexport & 2))
- vp->flags &= ~VEXPORT;
- vp->flags |= VUNSET;
- if ((vp->flags&(VEXPORT|VSTRFIXED|VREADONLY|VNOEXPORT)) == 0) {
- if ((vp->flags & VTEXTFIXED) == 0)
- ckfree(vp->text);
- *vpp = vp->next;
- ckfree(vp);
- }
- }
- INTON;
- return 0;
-}
-
-
-/*
- * Returns true if the two strings specify the same varable. The first
- * variable name is terminated by '='; the second may be terminated by
- * either '=' or '\0'.
- */
-
-STATIC int
-strequal(const char *p, const char *q)
-{
- while (*p == *q++) {
- if (*p++ == '=')
- return 1;
- }
- if (*p == '=' && *(q - 1) == '\0')
- return 1;
- return 0;
-}
-
-/*
- * Search for a variable.
- * 'name' may be terminated by '=' or a NUL.
- * vppp is set to the pointer to vp, or the list head if vp isn't found
- * lenp is set to the number of characters in 'name'
- */
-
-STATIC struct var *
-find_var(const char *name, struct var ***vppp, int *lenp)
-{
- unsigned int hashval;
- int len;
- struct var *vp, **vpp;
- const char *p = name;
-
- hashval = 0;
- while (*p && *p != '=')
- hashval = 2 * hashval + (unsigned char)*p++;
-
- len = p - name;
- if (lenp)
- *lenp = len;
-
- vpp = &vartab[hashval % VTABSIZE];
- if (vppp)
- *vppp = vpp;
-
- for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
- if (vp->name_len != len)
- continue;
- if (memcmp(vp->text, name, len) != 0)
- continue;
- if (vppp)
- *vppp = vpp;
- return vp;
- }
- return NULL;
-}
-
-/*
- * The following are the functions that create the values for
- * shell variables that are dynamically produced when needed.
- *
- * The output strings cannot be malloc'd as there is nothing to
- * free them - callers assume these are ordinary variables where
- * the value returned is vp->text
- *
- * Each function needs its own storage space, as the results are
- * used to create processes' environment, and (if exported) all
- * the values will (might) be needed simultaneously.
- *
- * It is not a problem if a var is updated while nominally in use
- * somewhere, all these are intended to be dynamic, the value they
- * return is not guaranteed, an updated vaue is just as good.
- *
- * So, malloc a single buffer for the result of each function,
- * grow, and even shrink, it as needed, but once we have one that
- * is a suitable size for the actual usage, simply hold it forever.
- *
- * For a SMALL shell we implement only LINENO, none of the others,
- * and give it just a fixed length static buffer for its result.
- */
-
-#ifndef SMALL
-
-struct space_reserved { /* record of space allocated for results */
- char *b;
- int len;
-};
-
-/* rough (over-)estimate of the number of bytes needed to hold a number */
-static int
-digits_in(intmax_t number)
-{
- int res = 0;
-
- if (number & ~((1LL << 62) - 1))
- res = 64; /* enough for 2^200 and a bit more */
- else if (number & ~((1LL << 32) - 1))
- res = 20; /* enough for 2^64 */
- else if (number & ~((1 << 23) - 1))
- res = 10; /* enough for 2^32 */
- else
- res = 8; /* enough for 2^23 or smaller */
-
- return res;
-}
-
-static int
-make_space(struct space_reserved *m, int bytes)
-{
- void *p;
-
- if (m->len >= bytes && m->len <= (bytes<<2))
- return 1;
-
- bytes = SHELL_ALIGN(bytes);
- /* not ckrealloc() - we want failure, not error() here */
- p = realloc(m->b, bytes);
- if (p == NULL) /* what we had should still be there */
- return 0;
-
- m->b = p;
- m->len = bytes;
- m->b[bytes - 1] = '\0';
-
- return 1;
-}
-#endif
-
-char *
-get_lineno(struct var *vp)
-{
-#ifdef SMALL
-#define length (8 + 10) /* 10 digits is enough for a 32 bit line num */
- static char result[length];
-#else
- static struct space_reserved buf;
-#define result buf.b
-#define length buf.len
-#endif
- int ln = line_number;
-
- if (vp->flags & VUNSET)
- return NULL;
-
- ln -= funclinebase;
-
-#ifndef SMALL
- if (!make_space(&buf, vp->name_len + 2 + digits_in(ln)))
- return vp->text;
-#endif
-
- snprintf(result, length, "%.*s=%d", vp->name_len, vp->text, ln);
- return result;
-}
-#undef result
-#undef length
-
-#ifndef SMALL
-
-char *
-get_hostname(struct var *vp)
-{
- static struct space_reserved buf;
-
- if (vp->flags & VUNSET)
- return NULL;
-
- if (!make_space(&buf, vp->name_len + 2 + 256))
- return vp->text;
-
- memcpy(buf.b, vp->text, vp->name_len + 1); /* include '=' */
- (void)gethostname(buf.b + vp->name_len + 1,
- buf.len - vp->name_len - 3);
- return buf.b;
-}
-
-char *
-get_tod(struct var *vp)
-{
- static struct space_reserved buf; /* space for answers */
- static struct space_reserved tzs; /* remember TZ last used */
- static timezone_t last_zone; /* timezone data for tzs zone */
- const char *fmt;
- char *tz;
- time_t now;
- struct tm tm_now, *tmp;
- timezone_t zone = NULL;
- static char t_err[] = "time error";
- int len;
-
- if (vp->flags & VUNSET)
- return NULL;
-
- fmt = lookupvar("ToD_FORMAT");
- if (fmt == NULL)
- fmt="%T";
- tz = lookupvar("TZ");
- (void)time(&now);
-
- if (tz != NULL) {
- if (tzs.b == NULL || strcmp(tzs.b, tz) != 0) {
- if (make_space(&tzs, strlen(tz) + 1)) {
- INTOFF;
- strcpy(tzs.b, tz);
- if (last_zone)
- tzfree(last_zone);
- last_zone = zone = tzalloc(tz);
- INTON;
- } else
- zone = tzalloc(tz);
- } else
- zone = last_zone;
-
- tmp = localtime_rz(zone, &now, &tm_now);
- } else
- tmp = localtime_r(&now, &tm_now);
-
- len = (strlen(fmt) * 4) + vp->name_len + 2;
- while (make_space(&buf, len)) {
- memcpy(buf.b, vp->text, vp->name_len+1);
- if (tmp == NULL) {
- if (buf.len >= vp->name_len+2+(int)(sizeof t_err - 1)) {
- strcpy(buf.b + vp->name_len + 1, t_err);
- if (zone && zone != last_zone)
- tzfree(zone);
- return buf.b;
- }
- len = vp->name_len + 4 + sizeof t_err - 1;
- continue;
- }
- if (strftime_z(zone, buf.b + vp->name_len + 1,
- buf.len - vp->name_len - 2, fmt, tmp)) {
- if (zone && zone != last_zone)
- tzfree(zone);
- return buf.b;
- }
- if (len >= 4096) /* Let's be reasonable */
- break;
- len <<= 1;
- }
- if (zone && zone != last_zone)
- tzfree(zone);
- return vp->text;
-}
-
-char *
-get_seconds(struct var *vp)
-{
- static struct space_reserved buf;
- intmax_t secs;
-
- if (vp->flags & VUNSET)
- return NULL;
-
- secs = (intmax_t)time((time_t *)0) - sh_start_time;
- if (!make_space(&buf, vp->name_len + 2 + digits_in(secs)))
- return vp->text;
-
- snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text, secs);
- return buf.b;
-}
-
-char *
-get_euser(struct var *vp)
-{
- static struct space_reserved buf;
- static uid_t lastuid = 0;
- uid_t euid;
- struct passwd *pw;
-
- if (vp->flags & VUNSET)
- return NULL;
-
- euid = geteuid();
- if (buf.b != NULL && lastuid == euid)
- return buf.b;
-
- pw = getpwuid(euid);
- if (pw == NULL)
- return vp->text;
-
- if (make_space(&buf, vp->name_len + 2 + strlen(pw->pw_name))) {
- lastuid = euid;
- snprintf(buf.b, buf.len, "%.*s=%s", vp->name_len, vp->text,
- pw->pw_name);
- return buf.b;
- }
-
- return vp->text;
-}
-
-char *
-get_random(struct var *vp)
-{
- static struct space_reserved buf;
- static intmax_t random_val = 0;
-
-#ifdef USE_LRAND48
-#define random lrand48
-#define srandom srand48
-#endif
-
- if (vp->flags & VUNSET)
- return NULL;
-
- if (vp->text != buf.b) {
- /*
- * Either initialisation, or a new seed has been set
- */
- if (vp->text[vp->name_len + 1] == '\0') {
- int fd;
-
- /*
- * initialisation (without pre-seeding),
- * or explictly requesting a truly random seed.
- */
- fd = open("/dev/urandom", 0);
- if (fd == -1) {
- out2str("RANDOM initialisation failed\n");
- random_val = (getpid()<<3) ^ time((time_t *)0);
- } else {
- int n;
-
- do {
- n = read(fd,&random_val,sizeof random_val);
- } while (n != sizeof random_val);
- close(fd);
- }
- } else
- /* good enough for today */
- random_val = strtoimax(vp->text+vp->name_len+1,NULL,0);
-
- srandom((long)random_val);
- }
-
-#if 0
- random_val = (random_val + 1) & 0x7FFF; /* 15 bit "random" numbers */
-#else
- random_val = (random() >> 5) & 0x7FFF;
-#endif
-
- if (!make_space(&buf, vp->name_len + 2 + digits_in(random_val)))
- return vp->text;
-
- snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text,
- random_val);
-
- if (buf.b != vp->text && (vp->flags & (VTEXTFIXED|VSTACK)) == 0)
- free(vp->text);
- vp->flags |= VTEXTFIXED;
- vp->text = buf.b;
-
- return vp->text;
-#undef random
-#undef srandom
-}
-
-#endif /* SMALL */