home *** CD-ROM | disk | FTP | other *** search
- /*-
- * Copyright (c) 1991 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 5.3 (Berkeley) 4/12/91";
- #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 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
- {&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 void 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 = getuid() ? "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: is read only", 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);
- 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 optval);
- bcopy(optval, lvp->text, sizeof optval);
- 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, optval, sizeof optval);
- ckfree(lvp->text);
- } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
- 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;
-
- for (ap = argv + 1 ; *ap ; ap++) {
- unsetfunc(*ap);
- unsetvar(*ap);
- }
- return 0;
- }
-
-
- /*
- * Unset the specified variable.
- */
-
- STATIC void
- 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)) {
- INTOFF;
- if (*(strchr(vp->text, '=') + 1) != '\0'
- || vp->flags & VREADONLY) {
- 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;
- }
- }
- }
-
-
-
- /*
- * 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;
- }
-