0,0 → 1,777 |
/* $NetBSD: var.c,v 1.26 2000/12/20 00:15:10 cgd 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. 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. |
*/ |
|
#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.26 2000/12/20 00:15:10 cgd Exp $"); |
#endif |
#endif /* not lint */ |
|
#include <unistd.h> |
#include <stdlib.h> |
#include <paths.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 "mail.h" |
#include "var.h" |
#include "memalloc.h" |
#include "error.h" |
#include "mystring.h" |
#include "parser.h" |
#ifndef SMALL |
#include "myhistedit.h" |
#endif |
|
|
#define VTABSIZE 39 |
|
|
struct varinit { |
struct var *var; |
int flags; |
const char *text; |
void (*func) (const char *); |
}; |
|
|
#if ATTY |
struct var vatty; |
#endif |
#ifndef SMALL |
struct var vhistsize; |
struct var vterm; |
#endif |
struct var vifs; |
struct var vmail; |
struct var vmpath; |
struct var vpath; |
struct var vps1; |
struct var vps2; |
struct var vvers; |
struct var voptind; |
|
const struct varinit varinit[] = { |
#if ATTY |
{ &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", |
NULL }, |
#endif |
#ifndef SMALL |
{ &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", |
sethistsize }, |
#endif |
{ &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", |
NULL }, |
{ &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", |
NULL }, |
{ &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", |
NULL }, |
{ &vpath, VSTRFIXED|VTEXTFIXED, "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", |
changepath }, |
/* |
* vps1 depends on uid |
*/ |
{ &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", |
NULL }, |
#ifndef SMALL |
{ &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=", |
setterm }, |
#endif |
{ &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", |
getoptsreset }, |
{ NULL, 0, NULL, |
NULL } |
}; |
|
struct var *vartab[VTABSIZE]; |
|
STATIC struct var **hashvar (const char *); |
STATIC int varequal (const char *, const char *); |
|
/* |
* Initialize the varable symbol tables and import the environment |
* Setting PWD added by herbert |
*/ |
|
#ifdef mkinit |
INCLUDE "cd.h" |
INCLUDE "var.h" |
INIT { |
char **envp; |
extern char **environ; |
extern char *curdir; |
|
initvar(); |
for (envp = environ ; *envp ; envp++) { |
if (strchr(*envp, '=')) { |
setvareq(*envp, VEXPORT|VTEXTFIXED); |
} |
} |
|
getpwd(); |
setvar("PWD", curdir, 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; |
char ppid[30]; |
|
for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { |
if ((vp->flags & VEXPORT) == 0) { |
vpp = hashvar(ip->text); |
vp->next = *vpp; |
*vpp = vp; |
vp->text = strdup(ip->text); |
vp->flags = ip->flags; |
vp->func = ip->func; |
} |
} |
/* |
* PS1 depends on uid |
*/ |
if ((vps1.flags & VEXPORT) == 0) { |
vpp = hashvar("PS1="); |
vps1.next = *vpp; |
*vpp = &vps1; |
vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); |
vps1.flags = VSTRFIXED|VTEXTFIXED; |
} |
|
snprintf(ppid, 29, "%ld", (long)getppid()); |
setvar("PPID", ppid, VREADONLY|VNOFUNC); |
} |
|
/* |
* Safe version of setvar, returns 1 on success 0 on failure. |
*/ |
|
int |
setvarsafe(name, val, flags) |
const char *name, *val; |
int flags; |
{ |
struct jmploc jmploc; |
struct jmploc *volatile savehandler = handler; |
int err = 0; |
#ifdef __GNUC__ |
(void) &err; |
#endif |
|
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. |
*/ |
|
void |
setvar(name, val, flags) |
const char *name, *val; |
int flags; |
{ |
const char *p; |
const char *q; |
char *d; |
int len; |
int namelen; |
char *nameeq; |
int isbad; |
|
isbad = 0; |
p = name; |
if (! is_name(*p)) |
isbad = 1; |
p++; |
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); |
} |
d = nameeq = ckmalloc(len); |
q = name; |
while (--namelen >= 0) |
*d++ = *q++; |
*d++ = '='; |
*d = '\0'; |
if (val) |
scopy(val, d); |
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; |
int flags; |
{ |
struct var *vp, **vpp; |
|
vpp = hashvar(s); |
flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); |
for (vp = *vpp ; vp ; vp = vp->next) { |
if (varequal(s, vp->text)) { |
if (vp->flags & VREADONLY) { |
size_t len = strchr(s, '=') - s; |
error("%.*s: is read only", len, s); |
} |
INTOFF; |
|
if (vp->func && (flags & VNOFUNC) == 0) |
(*vp->func)(strchr(s, '=') + 1); |
|
if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) |
ckfree(vp->text); |
|
vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); |
vp->flags |= flags; |
vp->text = s; |
|
/* |
* We could roll this to a function, to handle it as |
* a regular variable function callback, but why bother? |
*/ |
if (iflag && |
(vp == &vmpath || (vp == &vmail && ! mpathset()))) |
chkmail(1); |
INTON; |
return; |
} |
} |
/* not found */ |
vp = ckmalloc(sizeof (*vp)); |
vp->flags = flags; |
vp->text = s; |
vp->next = *vpp; |
vp->func = NULL; |
*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) |
const 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) |
const char *name; |
int doall; |
{ |
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; |
char **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 (void); |
|
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) |
int argc; |
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) |
int argc; |
char **argv; |
{ |
struct var **vpp; |
struct var *vp; |
char *name; |
const char *p; |
int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; |
int pflag; |
|
listsetvar(cmdenviron); |
pflag = (nextopt("p") == 'p'); |
if (argc > 1 && !pflag) { |
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) == 0) |
continue; |
if (pflag) { |
out1fmt("%s %s\n", argv[0], vp->text); |
} else { |
for (p = vp->text ; *p != '=' ; p++) |
out1c(*p); |
out1c('\n'); |
} |
} |
} |
} |
return 0; |
} |
|
|
/* |
* The "local" command. |
*/ |
|
int |
localcmd(argc, argv) |
int argc; |
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') { |
char *p; |
p = ckmalloc(sizeof optlist); |
lvp->text = memcpy(p, optlist, 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 */ |
memcpy(optlist, lvp->text, 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); |
} |
} |
|
|
int |
setvarcmd(argc, argv) |
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(argc, argv) |
int argc; |
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. |
*/ |
|
int |
unsetvar(s) |
const 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) |
const char *p; |
{ |
unsigned int hashval; |
|
hashval = ((unsigned char) *p) << 4; |
while (*p && *p != '=') |
hashval += (unsigned char) *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) |
const char *p, *q; |
{ |
while (*p == *q++) { |
if (*p++ == '=') |
return 1; |
} |
if (*p == '=' && *(q - 1) == '\0') |
return 1; |
return 0; |
} |