diff options
Diffstat (limited to 'bin/sh/var.c')
-rw-r--r-- | bin/sh/var.c | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/bin/sh/var.c b/bin/sh/var.c new file mode 100644 index 0000000..0629de8 --- /dev/null +++ b/bin/sh/var.c @@ -0,0 +1,670 @@ +/*- + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef lint +static char sccsid[] = "@(#)var.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ + +/* + * 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 "mail.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + + +#define VTABSIZE 39 + + +struct varinit { + struct var *var; + int flags; + char *text; +}; + + +#if ATTY +struct var vatty; +#endif +struct var vhistsize; +struct var vifs; +struct var vmail; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vvers; +#if ATTY +struct var vterm; +#endif + +const struct varinit varinit[] = { +#if ATTY + {&vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY="}, +#endif + {&vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE="}, + {&vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n"}, + {&vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL="}, + {&vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH="}, + {&vpath, VSTRFIXED|VTEXTFIXED, "PATH=:/bin:/usr/bin"}, + /* + * vps1 depends on uid + */ + {&vps2, VSTRFIXED|VTEXTFIXED, "PS2=> "}, +#if ATTY + {&vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM="}, +#endif + {NULL, 0, NULL} +}; + +struct var *vartab[VTABSIZE]; + +STATIC int unsetvar __P((char *)); +STATIC struct var **hashvar __P((char *)); +STATIC int varequal __P((char *, char *)); + +/* + * Initialize the varable symbol tables and import the environment + */ + +#ifdef mkinit +INCLUDE "var.h" +INIT { + char **envp; + extern char **environ; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|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() { + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if ((vp->flags & VEXPORT) == 0) { + vpp = hashvar(ip->text); + vp->next = *vpp; + *vpp = vp; + vp->text = ip->text; + vp->flags = ip->flags; + } + } + /* + * PS1 depends on uid + */ + if ((vps1.flags & VEXPORT) == 0) { + vpp = hashvar("PS1="); + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = geteuid() ? "PS1=$ " : "PS1=# "; + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * 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. + */ + +void +setvar(name, val, flags) + char *name, *val; + { + char *p, *q; + int len; + int namelen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p++)) + isbad = 1; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + 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); + } + p = nameeq = ckmalloc(len); + q = name; + while (--namelen >= 0) + *p++ = *q++; + *p++ = '='; + *p = '\0'; + if (val) + scopy(val, p); + setvareq(nameeq, flags); +} + + + +/* + * 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. + */ + +void +setvareq(s, flags) + char *s; + { + struct var *vp, **vpp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(s, vp->text)) { + if (vp->flags & VREADONLY) { + int len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + if (vp == &vpath) + changepath(s + 5); /* 5 = strlen("PATH=") */ + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + if (vp == &vmpath || (vp == &vmail && ! mpathset())) + chkmail(1); + if (vp == &vhistsize) + sethistsize(); + INTON; + return; + } + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(list) + struct strlist *list; + { + struct strlist *lp; + + INTOFF; + for (lp = list ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), 0); + } + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(name) + char *name; + { + struct var *v; + + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * 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(name, doall) + char *name; + { + struct strlist *sp; + struct var *v; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET + || ! doall && (v->flags & VEXPORT) == 0) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +char ** +environment() { + int nenv; + struct var **vpp; + struct var *vp; + char **env, **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + 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) + *ep++ = vp->text; + } + *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 +MKINIT void shprocvar(); + +SHELLPROC { + shprocvar(); +} +#endif + +void +shprocvar() { + 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. + */ + +int +showvarscmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if ((vp->flags & VUNSET) == 0) + out1fmt("%s\n", vp->text); + } + } + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + char *name; + char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + + listsetvar(cmdenviron); + if (argc > 1) { + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(vp->text, name)) { + vp->flags |= flag; + goto found; + } + } + } + setvar(name, p, flag); +found:; + } + } else { + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if (vp->flags & flag) { + for (p = vp->text ; *p != '=' ; p++) + out1c(*p); + out1c('\n'); + } + } + } + } + return 0; +} + + +/* + * The "local" command. + */ + +localcmd(argc, argv) char **argv; { + char *name; + + if (! in_function()) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * 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(name) + char *name; + { + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + lvp->text = ckmalloc(sizeof optlist); + bcopy(optlist, lvp->text, sizeof optlist); + vp = NULL; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (strchr(name, '=')) + setvareq(savestr(name), 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +void +poplocalvars() { + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + bcopy(lvp->text, optlist, sizeof optlist); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + (void)unsetvar(vp->text); + } else { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +setvarcmd(argc, argv) 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. + */ + +unsetcmd(argc, argv) char **argv; { + char **ap; + int i; + int flg_func = 0; + int flg_var = 0; + int ret = 0; + + while ((i = nextopt("vf")) != '\0') { + if (i == 'f') + flg_func = 1; + else + flg_var = 1; + } + 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); + } + return ret; +} + + +/* + * Unset the specified variable. + */ + +STATIC int +unsetvar(s) + char *s; + { + struct var **vpp; + struct var *vp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (varequal(vp->text, s)) { + if (vp->flags & VREADONLY) + return (1); + INTOFF; + if (*(strchr(vp->text, '=') + 1) != '\0') + setvar(s, nullstr, 0); + vp->flags &=~ VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + INTON; + return (0); + } + } + + return (1); +} + + + +/* + * Find the appropriate entry in the hash table from the name. + */ + +STATIC struct var ** +hashvar(p) + register char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p && *p != '=') + hashval += *p++; + return &vartab[hashval % VTABSIZE]; +} + + + +/* + * 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 +varequal(p, q) + register char *p, *q; + { + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} |