diff options
Diffstat (limited to 'bin/catsh/var.c')
-rw-r--r-- | bin/catsh/var.c | 982 |
1 files changed, 0 insertions, 982 deletions
diff --git a/bin/catsh/var.c b/bin/catsh/var.c deleted file mode 100644 index 435d3307..00000000 --- a/bin/catsh/var.c +++ /dev/null @@ -1,982 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * 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. - */ - -#ifndef lint -#if 0 -static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; -#endif -#endif /* not lint */ -#include <sys/cdefs.h> -__FBSDID("$FreeBSD: releng/12.0/bin/sh/var.c 329221 2018-02-13 16:48:57Z bdrewery $"); - -#include <unistd.h> -#include <stdlib.h> -#include <paths.h> - -/* - * Shell variables. - */ - -#include <locale.h> -#include <langinfo.h> - -#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" -#include "builtins.h" -#ifndef NO_HISTORY -#include "myhistedit.h" -#endif - - -#ifndef VTABSIZE -#define VTABSIZE 39 -#endif - - -struct varinit { - struct var *var; - int flags; - const char *text; - void (*func)(const char *); -}; - - -#ifndef NO_HISTORY -struct var vhistfile; -struct var vhistsize; -struct var vpslit; -struct var vterm; -#endif -struct var venv; -struct var vifs; -struct var vmail; -struct var vmpath; -struct var vpath; -struct var vps0; -struct var vps1; -struct var vps2; -struct var vps4; -struct var vrps1; -struct var vrps2; -static struct var voptind; -struct var vdisvfork; - -struct localvar *localvars; -int forcelocal; - -#define XDG_CONFIG_HOME "${XDG_CONFIG_HOME:-${HOME}/.config}" -#define XDG_DATA_HOME "${XDG_DATA_HOME:-${HOME}/.local/share}" - -static const struct varinit varinit[] = { - { &venv, 0, "ENV=" XDG_CONFIG_HOME "/catsh/env.sh", - NULL }, -#ifndef NO_HISTORY - { &vhistfile, 0, "HISTFILE=" XDG_DATA_HOME "/catsh/history", - sethistfile }, - { &vhistsize, VUNSET, "HISTSIZE=", - sethistsize }, -#endif - { &vifs, 0, "IFS= \t\n", - NULL }, - { &vmail, VUNSET, "MAIL=", - NULL }, - { &vmpath, VUNSET, "MAILPATH=", - NULL }, - { &vpath, 0, "PATH=" _PATH_DEFPATH, - changepath }, - { &vps0, VUNSET, "PS0=", - NULL }, - { &vps1, 0, "PS1=\\$ ", - NULL }, - { &vps2, 0, "PS2=> ", - NULL }, - { &vps4, 0, "PS4=+ ", - NULL }, - { &vrps1, VUNSET, "RPS1=", - NULL }, - { &vrps2, VUNSET, "RPS2=", - NULL }, -#ifndef NO_HISTORY - { &vpslit, VUNSET, "PSlit=", - setpslit }, - { &vterm, VUNSET, "TERM=", - setterm }, -#endif - { &voptind, 0, "OPTIND=1", - getoptsreset }, - { &vdisvfork, VUNSET, "SH_DISABLE_VFORK=", - NULL }, - { NULL, 0, NULL, - NULL } -}; - -static struct var *vartab[VTABSIZE]; - -static const char *const locale_names[7] = { - "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", - "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL -}; -static const int locale_categories[7] = { - LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0 -}; - -static int varequal(const char *, const char *); -static struct var *find_var(const char *, struct var ***, int *); -static int localevar(const char *); -static void setvareq_const(const char *s, int flags); - -extern char **environ; - -/* - * This routine initializes the builtin variables and imports the environment. - * It is called when the shell is initialized. - */ - -void -initvar(void) -{ - char ppid[20]; - const struct varinit *ip; - struct var *vp; - struct var **vpp; - char **envp; - - 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 = __DECONST(char *, ip->text); - vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED; - vp->func = ip->func; - } - fmtstr(ppid, sizeof(ppid), "%d", (int)getppid()); - setvarsafe("PPID", ppid, 0); - for (envp = environ ; *envp ; envp++) { - if (strchr(*envp, '=')) { - setvareq(*envp, VEXPORT|VTEXTFIXED); - } - } - setvareq_const("OPTIND=1", 0); - setvareq_const("IFS= \t\n", 0); -} - -/* - * 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 err = 0; - int inton; - - inton = is_int_on(); - if (setjmp(jmploc.loc)) - err = 1; - else { - handler = &jmploc; - setvar(name, val, flags); - } - handler = savehandler; - SETINTON(inton); - return err; -} - -/* - * Set the value of a variable. The flags argument is stored with the - * flags of the variable. If val is NULL, the variable is unset. - */ - -void -setvar(const char *name, const char *val, int flags) -{ - const char *p; - size_t len; - size_t namelen; - size_t vallen; - 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", (int)namelen, name); - len = namelen + 2; /* 2 is space for '=' and '\0' */ - if (val == NULL) { - flags |= VUNSET; - vallen = 0; - } else { - vallen = strlen(val); - len += vallen; - } - INTOFF; - nameeq = ckmalloc(len); - memcpy(nameeq, name, namelen); - nameeq[namelen] = '='; - if (val) - memcpy(nameeq + namelen + 1, val, vallen + 1); - else - nameeq[namelen + 1] = '\0'; - setvareq(nameeq, flags); - INTON; -} - -static int -localevar(const char *s) -{ - const char *const *ss; - - if (*s != 'L') - return 0; - if (varequal(s + 1, "ANG")) - return 1; - if (strncmp(s + 1, "C_", 2) != 0) - return 0; - if (varequal(s + 3, "ALL")) - return 1; - for (ss = locale_names; *ss ; ss++) - if (varequal(s + 3, *ss + 3)) - return 1; - return 0; -} - - -/* - * Sets/unsets an environment variable from a pointer that may actually be a - * pointer into environ where the string should not be manipulated. - */ -static void -change_env(const char *s, int set) -{ - char *eqp; - char *ss; - - INTOFF; - ss = savestr(s); - if ((eqp = strchr(ss, '=')) != NULL) - *eqp = '\0'; - if (set && eqp != NULL) - (void) setenv(ss, eqp + 1, 1); - else - (void) unsetenv(ss); - ckfree(ss); - INTON; - - return; -} - - -/* - * 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(char *s, int flags) -{ - struct var *vp, **vpp; - int nlen; - - if (aflag) - flags |= VEXPORT; - if (forcelocal && !(flags & (VNOSET | VNOLOCAL))) - mklocal(s); - vp = find_var(s, &vpp, &nlen); - if (vp != NULL) { - if (vp->flags & VREADONLY) { - if ((flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(s); - 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 && (flags & VNOFUNC) == 0) - (*vp->func)(s + vp->name_len + 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? - * - * Note: this assumes iflag is not set to 1 initially. - * As part of initvar(), this is called before arguments - * are looked at. - */ - if ((vp == &vmpath || (vp == &vmail && ! mpathset())) && - iflag == 1) - chkmail(1); - if ((vp->flags & VEXPORT) && localevar(s)) { - change_env(s, 1); - (void) setlocale(LC_ALL, ""); - updatecharset(); - } - INTON; - return; - } - /* not found */ - if (flags & VNOSET) { - if ((flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(s); - return; - } - INTOFF; - vp = ckmalloc(sizeof (*vp)); - vp->flags = flags; - vp->text = s; - vp->name_len = nlen; - vp->next = *vpp; - vp->func = NULL; - *vpp = vp; - if ((vp->flags & VEXPORT) && localevar(s)) { - change_env(s, 1); - (void) setlocale(LC_ALL, ""); - updatecharset(); - } - INTON; -} - - -static void -setvareq_const(const char *s, int flags) -{ - setvareq(__DECONST(char *, s), flags | VTEXTFIXED); -} - - -/* - * Process a linked list of variable assignments. - */ - -void -listsetvar(struct arglist *list, int flags) -{ - int i; - - INTOFF; - for (i = 0; i < list->count; i++) - setvareq(savestr(list->args[i]), flags); - INTON; -} - - - -/* - * 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; - 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 var *v; - char *result; - int i; - - result = NULL; - if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) { - if (varequal(cmdenviron->args[i], name)) - result = strchr(cmdenviron->args[i], '=') + 1; - } - if (result != NULL) - return result; - - v = find_var(name, NULL, NULL); - if (v == NULL || v->flags & VUNSET || - (!doall && (v->flags & VEXPORT) == 0)) - return NULL; - return v->text + v->name_len + 1; -} - - -/* - * Set up locale for a builtin (LANG/LC_* assignments). - */ -void -bltinsetlocale(void) -{ - int act = 0; - char *loc, *locdef; - int i; - - if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) { - if (localevar(cmdenviron->args[i])) { - act = 1; - break; - } - } - if (!act) - return; - loc = bltinlookup("LC_ALL", 0); - INTOFF; - if (loc != NULL) { - setlocale(LC_ALL, loc); - INTON; - updatecharset(); - return; - } - locdef = bltinlookup("LANG", 0); - for (i = 0; locale_names[i] != NULL; i++) { - loc = bltinlookup(locale_names[i], 0); - if (loc == NULL) - loc = locdef; - if (loc != NULL) - setlocale(locale_categories[i], loc); - } - INTON; - updatecharset(); -} - -/* - * Undo the effect of bltinlocaleset(). - */ -void -bltinunsetlocale(void) -{ - int i; - - INTOFF; - if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) { - if (localevar(cmdenviron->args[i])) { - setlocale(LC_ALL, ""); - updatecharset(); - break; - } - } - INTON; -} - -/* - * Update the localeisutf8 flag. - */ -void -updatecharset(void) -{ - char *charset; - - charset = nl_langinfo(CODESET); - localeisutf8 = !strcmp(charset, "UTF-8"); -} - -void -initcharset(void) -{ - updatecharset(); - initial_localeisutf8 = localeisutf8; -} - -/* - * 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, **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; -} - - -static int -var_compare(const void *a, const void *b) -{ - const char *const *sa, *const *sb; - - sa = a; - sb = b; - /* - * This compares two var=value strings which creates a different - * order from what you would probably expect. POSIX is somewhat - * ambiguous on what should be sorted exactly. - */ - return strcoll(*sa, *sb); -} - - -/* - * Command to list all variables which are set. This is invoked from the - * set command when it is called without any options or operands. - */ - -int -showvarscmd(int argc __unused, char **argv __unused) -{ - struct var **vpp; - struct var *vp; - const char *s; - const char **vars; - int i, n; - - /* - * POSIX requires us to sort the variables. - */ - n = 0; - for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) { - for (vp = *vpp; vp; vp = vp->next) { - if (!(vp->flags & VUNSET)) - n++; - } - } - - INTOFF; - vars = ckmalloc(n * sizeof(*vars)); - i = 0; - for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) { - for (vp = *vpp; vp; vp = vp->next) { - if (!(vp->flags & VUNSET)) - vars[i++] = vp->text; - } - } - - qsort(vars, n, sizeof(*vars), var_compare); - for (i = 0; i < n; i++) { - /* - * Skip improper variable names so the output remains usable as - * shell input. - */ - if (!isassignment(vars[i])) - continue; - s = strchr(vars[i], '='); - s++; - outbin(vars[i], s - vars[i], out1); - out1qstr(s); - out1c('\n'); - } - ckfree(vars); - INTON; - - return 0; -} - - - -/* - * The export and readonly commands. - */ - -int -exportcmd(int argc __unused, char **argv) -{ - struct var **vpp; - struct var *vp; - char **ap; - char *name; - char *p; - char *cmdname; - int ch, values; - int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; - - cmdname = argv[0]; - values = 0; - while ((ch = nextopt("p")) != '\0') { - switch (ch) { - case 'p': - values = 1; - break; - } - } - - if (values && *argptr != NULL) - error("-p requires no arguments"); - if (*argptr != NULL) { - for (ap = argptr; (name = *ap) != NULL; ap++) { - if ((p = strchr(name, '=')) != NULL) { - p++; - } else { - vp = find_var(name, NULL, NULL); - if (vp != NULL) { - vp->flags |= flag; - if ((vp->flags & VEXPORT) && localevar(vp->text)) { - change_env(vp->text, 1); - (void) setlocale(LC_ALL, ""); - updatecharset(); - } - continue; - } - } - setvar(name, p, flag); - } - } else { - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) { - if (vp->flags & flag) { - if (values) { - /* - * Skip improper variable names - * so the output remains usable - * as shell input. - */ - if (!isassignment(vp->text)) - continue; - out1str(cmdname); - out1c(' '); - } - if (values && !(vp->flags & VUNSET)) { - outbin(vp->text, - vp->name_len + 1, out1); - out1qstr(vp->text + - vp->name_len + 1); - } else - outbin(vp->text, vp->name_len, - out1); - out1c('\n'); - } - } - } - } - return 0; -} - - -/* - * The "local" command. - */ - -int -localcmd(int argc __unused, char **argv __unused) -{ - char *name; - - nextopt(""); - 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(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); - memcpy(lvp->text, optval, sizeof optval); - vp = NULL; - } else { - vp = find_var(name, &vpp, NULL); - if (vp == NULL) { - if (strchr(name, '=')) - setvareq(savestr(name), VSTRFIXED | VNOLOCAL); - else - setvar(name, NULL, VSTRFIXED | VNOLOCAL); - 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 (name[vp->name_len] == '=') - setvareq(savestr(name), VNOLOCAL); - } - } - lvp->vp = vp; - lvp->next = localvars; - localvars = lvp; - INTON; -} - - -/* - * Called after a function returns. - */ - -void -poplocalvars(void) -{ - struct localvar *lvp; - struct var *vp; - int islocalevar; - - INTOFF; - while ((lvp = localvars) != NULL) { - localvars = lvp->next; - vp = lvp->vp; - if (vp == NULL) { /* $- saved */ - memcpy(optval, lvp->text, sizeof optval); - ckfree(lvp->text); - optschanged(); - } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { - vp->flags &= ~VREADONLY; - (void)unsetvar(vp->text); - } else { - islocalevar = (vp->flags | lvp->flags) & VEXPORT && - localevar(lvp->text); - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - vp->flags = lvp->flags; - vp->text = lvp->text; - if (vp->func) - (*vp->func)(vp->text + vp->name_len + 1); - if (islocalevar) { - change_env(vp->text, vp->flags & VEXPORT && - (vp->flags & VUNSET) == 0); - setlocale(LC_ALL, ""); - updatecharset(); - } - } - ckfree(lvp); - } - INTON; -} - - -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("too many arguments"); - return 0; -} - - -/* - * The unset builtin command. - */ - -int -unsetcmd(int argc __unused, char **argv __unused) -{ - 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; - - INTOFF; - for (ap = argptr; *ap ; ap++) { - if (flg_func) - ret |= unsetfunc(*ap); - if (flg_var) - ret |= unsetvar(*ap); - } - INTON; - return ret; -} - - -/* - * Unset the specified variable. - * Called with interrupts off. - */ - -int -unsetvar(const char *s) -{ - struct var **vpp; - struct var *vp; - - vp = find_var(s, &vpp, NULL); - if (vp == NULL) - return (0); - if (vp->flags & VREADONLY) - return (1); - if (vp->text[vp->name_len + 1] != '\0') - setvar(s, "", 0); - if ((vp->flags & VEXPORT) && localevar(vp->text)) { - change_env(s, 0); - setlocale(LC_ALL, ""); - updatecharset(); - } - vp->flags &= ~VEXPORT; - vp->flags |= VUNSET; - if ((vp->flags & VSTRFIXED) == 0) { - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - *vpp = vp->next; - ckfree(vp); - } - return (0); -} - - - -/* - * Returns true if the two strings specify the same variable. The first - * variable name is terminated by '='; the second may be terminated by - * either '=' or '\0'. - */ - -static int -varequal(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; -} |