var.c revision 240541
198943Sluigi/*- 298943Sluigi * Copyright (c) 1991, 1993 398943Sluigi * The Regents of the University of California. All rights reserved. 498943Sluigi * 598943Sluigi * This code is derived from software contributed to Berkeley by 698943Sluigi * Kenneth Almquist. 798943Sluigi * 898943Sluigi * Redistribution and use in source and binary forms, with or without 998943Sluigi * modification, are permitted provided that the following conditions 1098943Sluigi * are met: 1198943Sluigi * 1. Redistributions of source code must retain the above copyright 1298943Sluigi * notice, this list of conditions and the following disclaimer. 1398943Sluigi * 2. Redistributions in binary form must reproduce the above copyright 1498943Sluigi * notice, this list of conditions and the following disclaimer in the 1598943Sluigi * documentation and/or other materials provided with the distribution. 1698943Sluigi * 4. Neither the name of the University nor the names of its contributors 1798943Sluigi * may be used to endorse or promote products derived from this software 1898943Sluigi * without specific prior written permission. 1998943Sluigi * 2098943Sluigi * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2198943Sluigi * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2298943Sluigi * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2398943Sluigi * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2498943Sluigi * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2598943Sluigi * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2698943Sluigi * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2798943Sluigi * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2898943Sluigi * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2998943Sluigi * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3098943Sluigi * SUCH DAMAGE. 3198943Sluigi */ 3298943Sluigi 3398943Sluigi#ifndef lint 3498943Sluigi#if 0 3598943Sluigistatic char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; 3698943Sluigi#endif 3798943Sluigi#endif /* not lint */ 3898943Sluigi#include <sys/cdefs.h> 3998943Sluigi__FBSDID("$FreeBSD: head/bin/sh/var.c 240541 2012-09-15 21:56:30Z jilles $"); 4098943Sluigi 4198943Sluigi#include <unistd.h> 4298943Sluigi#include <stdlib.h> 4399603Sbde#include <paths.h> 4498943Sluigi 4598943Sluigi/* 4698943Sluigi * Shell variables. 4798943Sluigi */ 4898943Sluigi 4998943Sluigi#include <locale.h> 5098943Sluigi#include <langinfo.h> 5198943Sluigi 5298943Sluigi#include "shell.h" 5398943Sluigi#include "output.h" 5498943Sluigi#include "expand.h" 5598943Sluigi#include "nodes.h" /* for other headers */ 5698943Sluigi#include "eval.h" /* defines cmdenviron */ 5798943Sluigi#include "exec.h" 5898943Sluigi#include "syntax.h" 5998943Sluigi#include "options.h" 6098943Sluigi#include "mail.h" 6198943Sluigi#include "var.h" 6298943Sluigi#include "memalloc.h" 6398943Sluigi#include "error.h" 6498943Sluigi#include "mystring.h" 6598943Sluigi#include "parser.h" 6698943Sluigi#include "builtins.h" 6798943Sluigi#ifndef NO_HISTORY 68102098Sluigi#include "myhistedit.h" 69101628Sluigi#endif 7098943Sluigi 7198943Sluigi 7298943Sluigi#define VTABSIZE 39 7398943Sluigi 7498943Sluigi 7598943Sluigistruct varinit { 7698943Sluigi struct var *var; 7798943Sluigi int flags; 7898943Sluigi const char *text; 7998943Sluigi void (*func)(const char *); 8098943Sluigi}; 8198943Sluigi 8298943Sluigi 8398943Sluigi#ifndef NO_HISTORY 8498943Sluigistruct var vhistsize; 8598943Sluigistruct var vterm; 8698943Sluigi#endif 8798943Sluigistruct var vifs; 8898943Sluigistruct var vmail; 8998943Sluigistruct var vmpath; 9098943Sluigistruct var vpath; 9198943Sluigistruct var vppid; 9298943Sluigistruct var vps1; 9398943Sluigistruct var vps2; 9498943Sluigistruct var vps4; 9598943Sluigistruct var vvers; 9698943Sluigistatic struct var voptind; 9798943Sluigistruct var vdisvfork; 9898943Sluigi 9998943Sluigiint forcelocal; 10098943Sluigi 10198943Sluigistatic const struct varinit varinit[] = { 10298943Sluigi#ifndef NO_HISTORY 10398943Sluigi { &vhistsize, VUNSET, "HISTSIZE=", 10498943Sluigi sethistsize }, 10598943Sluigi#endif 10698943Sluigi { &vifs, 0, "IFS= \t\n", 10798943Sluigi NULL }, 10898943Sluigi { &vmail, VUNSET, "MAIL=", 10998943Sluigi NULL }, 11098943Sluigi { &vmpath, VUNSET, "MAILPATH=", 11198943Sluigi NULL }, 11298943Sluigi { &vpath, 0, "PATH=" _PATH_DEFPATH, 11398943Sluigi changepath }, 11498943Sluigi { &vppid, VUNSET, "PPID=", 11598943Sluigi NULL }, 11698943Sluigi /* 11798943Sluigi * vps1 depends on uid 11898943Sluigi */ 11998943Sluigi { &vps2, 0, "PS2=> ", 12098943Sluigi NULL }, 12198943Sluigi { &vps4, 0, "PS4=+ ", 12298943Sluigi NULL }, 12398943Sluigi#ifndef NO_HISTORY 12498943Sluigi { &vterm, VUNSET, "TERM=", 12598943Sluigi setterm }, 12698943Sluigi#endif 12798943Sluigi { &voptind, 0, "OPTIND=1", 12898943Sluigi getoptsreset }, 12998943Sluigi { &vdisvfork, VUNSET, "SH_DISABLE_VFORK=", 13098943Sluigi NULL }, 13198943Sluigi { NULL, 0, NULL, 13298943Sluigi NULL } 13398943Sluigi}; 13498943Sluigi 13598943Sluigistatic struct var *vartab[VTABSIZE]; 13698943Sluigi 13798943Sluigistatic const char *const locale_names[7] = { 13898943Sluigi "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", 13998943Sluigi "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL 14098943Sluigi}; 14198943Sluigistatic const int locale_categories[7] = { 14298943Sluigi LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0 14398943Sluigi}; 14498943Sluigi 14598943Sluigistatic int varequal(const char *, const char *); 14698943Sluigistatic struct var *find_var(const char *, struct var ***, int *); 14798943Sluigistatic int localevar(const char *); 14898943Sluigi 14998943Sluigi/* 15098943Sluigi * Initialize the variable symbol tables and import the environment. 15198943Sluigi */ 15298943Sluigi 15398943Sluigi#ifdef mkinit 15498943SluigiINCLUDE "var.h" 15598943SluigiMKINIT char **environ; 15698943SluigiINIT { 15798943Sluigi char **envp; 15898943Sluigi 15998943Sluigi initvar(); 16098943Sluigi for (envp = environ ; *envp ; envp++) { 16198943Sluigi if (strchr(*envp, '=')) { 16298943Sluigi setvareq(*envp, VEXPORT|VTEXTFIXED); 16398943Sluigi } 16498943Sluigi } 16598943Sluigi} 16698943Sluigi#endif 16798943Sluigi 16898943Sluigi 16998943Sluigi/* 17098943Sluigi * This routine initializes the builtin variables. It is called when the 17198943Sluigi * shell is initialized. 17298943Sluigi */ 17398943Sluigi 17498943Sluigivoid 17598943Sluigiinitvar(void) 17698943Sluigi{ 17798943Sluigi char ppid[20]; 17898943Sluigi const struct varinit *ip; 17998943Sluigi struct var *vp; 18098943Sluigi struct var **vpp; 18198943Sluigi 182101641Sluigi for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { 183101641Sluigi if (find_var(ip->text, &vpp, &vp->name_len) != NULL) 18498943Sluigi continue; 18598943Sluigi vp->next = *vpp; 18698943Sluigi *vpp = vp; 18798943Sluigi vp->text = __DECONST(char *, ip->text); 18898943Sluigi vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED; 18998943Sluigi vp->func = ip->func; 19098943Sluigi } 19198943Sluigi /* 19298943Sluigi * PS1 depends on uid 19398943Sluigi */ 19498943Sluigi if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { 19598943Sluigi vps1.next = *vpp; 19698943Sluigi *vpp = &vps1; 19798943Sluigi vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# "); 19898943Sluigi vps1.flags = VSTRFIXED|VTEXTFIXED; 19998943Sluigi } 20098943Sluigi if ((vppid.flags & VEXPORT) == 0) { 20198943Sluigi fmtstr(ppid, sizeof(ppid), "%d", (int)getppid()); 20298943Sluigi setvarsafe("PPID", ppid, 0); 20398943Sluigi } 20498943Sluigi} 20598943Sluigi 20698943Sluigi/* 20798943Sluigi * Safe version of setvar, returns 1 on success 0 on failure. 20898943Sluigi */ 20998943Sluigi 21098943Sluigiint 21198943Sluigisetvarsafe(const char *name, const char *val, int flags) 21298943Sluigi{ 21398943Sluigi struct jmploc jmploc; 21498943Sluigi struct jmploc *const savehandler = handler; 21598943Sluigi int err = 0; 21698943Sluigi int inton; 21798943Sluigi 21898943Sluigi inton = is_int_on(); 21998943Sluigi if (setjmp(jmploc.loc)) 22098943Sluigi err = 1; 22198943Sluigi else { 22298943Sluigi handler = &jmploc; 22398943Sluigi setvar(name, val, flags); 22498943Sluigi } 225102087Sluigi handler = savehandler; 226102087Sluigi SETINTON(inton); 22798943Sluigi return err; 22898943Sluigi} 229101978Sluigi 23098943Sluigi/* 23198943Sluigi * Set the value of a variable. The flags argument is stored with the 23298943Sluigi * flags of the variable. If val is NULL, the variable is unset. 23398943Sluigi */ 23498943Sluigi 23598943Sluigivoid 23698943Sluigisetvar(const char *name, const char *val, int flags) 23798943Sluigi{ 23898943Sluigi const char *p; 23998943Sluigi int len; 24098943Sluigi int namelen; 24198943Sluigi char *nameeq; 24298943Sluigi int isbad; 24398943Sluigi 24498943Sluigi isbad = 0; 24598943Sluigi p = name; 24698943Sluigi if (! is_name(*p)) 24798943Sluigi isbad = 1; 248101978Sluigi p++; 24998943Sluigi for (;;) { 25098943Sluigi if (! is_in_name(*p)) { 25198943Sluigi if (*p == '\0' || *p == '=') 25298943Sluigi break; 25398943Sluigi isbad = 1; 25498943Sluigi } 25598943Sluigi p++; 25698943Sluigi } 25798943Sluigi namelen = p - name; 25898943Sluigi if (isbad) 25998943Sluigi error("%.*s: bad variable name", namelen, name); 26098943Sluigi len = namelen + 2; /* 2 is space for '=' and '\0' */ 26198943Sluigi if (val == NULL) { 26298943Sluigi flags |= VUNSET; 26398943Sluigi } else { 26499475Sluigi len += strlen(val); 26598943Sluigi } 26698943Sluigi nameeq = ckmalloc(len); 26798943Sluigi memcpy(nameeq, name, namelen); 26898943Sluigi nameeq[namelen] = '='; 26998943Sluigi if (val) 27098943Sluigi scopy(val, nameeq + namelen + 1); 27198943Sluigi else 27298943Sluigi nameeq[namelen + 1] = '\0'; 27398943Sluigi setvareq(nameeq, flags); 27498943Sluigi} 27598943Sluigi 27698943Sluigistatic int 27798943Sluigilocalevar(const char *s) 27898943Sluigi{ 27998943Sluigi const char *const *ss; 28098943Sluigi 28198943Sluigi if (*s != 'L') 28298943Sluigi return 0; 28398943Sluigi if (varequal(s + 1, "ANG")) 28498943Sluigi return 1; 28598943Sluigi if (strncmp(s + 1, "C_", 2) != 0) 28698943Sluigi return 0; 28799475Sluigi if (varequal(s + 3, "ALL")) 28898943Sluigi return 1; 28998943Sluigi for (ss = locale_names; *ss ; ss++) 29098943Sluigi if (varequal(s + 3, *ss + 3)) 29198943Sluigi return 1; 29298943Sluigi return 0; 29398943Sluigi} 29498943Sluigi 29598943Sluigi 29698943Sluigi/* 29798943Sluigi * Sets/unsets an environment variable from a pointer that may actually be a 29898943Sluigi * pointer into environ where the string should not be manipulated. 29998943Sluigi */ 30098943Sluigistatic void 30198943Sluigichange_env(const char *s, int set) 30298943Sluigi{ 30398943Sluigi char *eqp; 30498943Sluigi char *ss; 30598943Sluigi 30698943Sluigi ss = savestr(s); 30798943Sluigi if ((eqp = strchr(ss, '=')) != NULL) 30898943Sluigi *eqp = '\0'; 30998943Sluigi if (set && eqp != NULL) 31098943Sluigi (void) setenv(ss, eqp + 1, 1); 31198943Sluigi else 31298943Sluigi (void) unsetenv(ss); 31398943Sluigi ckfree(ss); 31498943Sluigi 31598943Sluigi return; 31698943Sluigi} 31798943Sluigi 31898943Sluigi 31998943Sluigi/* 32098943Sluigi * Same as setvar except that the variable and value are passed in 32198943Sluigi * the first argument as name=value. Since the first argument will 32298943Sluigi * be actually stored in the table, it should not be a string that 32398943Sluigi * will go away. 32498943Sluigi */ 32598943Sluigi 32699909Sluigivoid 32798943Sluigisetvareq(char *s, int flags) 328102087Sluigi{ 329102087Sluigi struct var *vp, **vpp; 330102087Sluigi int nlen; 331102087Sluigi 332102087Sluigi if (aflag) 333102087Sluigi flags |= VEXPORT; 334102087Sluigi if (forcelocal && !(flags & (VNOSET | VNOLOCAL))) 335102087Sluigi mklocal(s); 33698943Sluigi vp = find_var(s, &vpp, &nlen); 33798943Sluigi if (vp != NULL) { 33898943Sluigi if (vp->flags & VREADONLY) 33998943Sluigi error("%.*s: is read only", vp->name_len, s); 34098943Sluigi if (flags & VNOSET) 341101641Sluigi return; 342101641Sluigi INTOFF; 343101641Sluigi 344101641Sluigi if (vp->func && (flags & VNOFUNC) == 0) 34598943Sluigi (*vp->func)(s + vp->name_len + 1); 34698943Sluigi 34798943Sluigi if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) 34898943Sluigi ckfree(vp->text); 34998943Sluigi 35098943Sluigi vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); 35198943Sluigi vp->flags |= flags; 35298943Sluigi vp->text = s; 35398943Sluigi 35498943Sluigi /* 35598943Sluigi * We could roll this to a function, to handle it as 35698943Sluigi * a regular variable function callback, but why bother? 35798943Sluigi * 35898943Sluigi * Note: this assumes iflag is not set to 1 initially. 35998943Sluigi * As part of init(), this is called before arguments 36098943Sluigi * are looked at. 36198943Sluigi */ 36298943Sluigi if ((vp == &vmpath || (vp == &vmail && ! mpathset())) && 36398943Sluigi iflag == 1) 36498943Sluigi chkmail(1); 36598943Sluigi if ((vp->flags & VEXPORT) && localevar(s)) { 36698943Sluigi change_env(s, 1); 36798943Sluigi (void) setlocale(LC_ALL, ""); 36898943Sluigi updatecharset(); 36998943Sluigi } 37098943Sluigi INTON; 37198943Sluigi return; 37298943Sluigi } 37398943Sluigi /* not found */ 37498943Sluigi if (flags & VNOSET) 37598943Sluigi return; 37698943Sluigi vp = ckmalloc(sizeof (*vp)); 37798943Sluigi vp->flags = flags; 37898943Sluigi vp->text = s; 37998943Sluigi vp->name_len = nlen; 38098943Sluigi vp->next = *vpp; 38198943Sluigi vp->func = NULL; 38298943Sluigi INTOFF; 38398943Sluigi *vpp = vp; 38498943Sluigi if ((vp->flags & VEXPORT) && localevar(s)) { 38598943Sluigi change_env(s, 1); 38698943Sluigi (void) setlocale(LC_ALL, ""); 38798943Sluigi updatecharset(); 38898943Sluigi } 38998943Sluigi INTON; 39098943Sluigi} 39198943Sluigi 39298943Sluigi 39398943Sluigi 39498943Sluigi/* 39598943Sluigi * Process a linked list of variable assignments. 39698943Sluigi */ 39798943Sluigi 39898943Sluigivoid 39998943Sluigilistsetvar(struct strlist *list, int flags) 40098943Sluigi{ 40198943Sluigi struct strlist *lp; 40298943Sluigi 40398943Sluigi INTOFF; 40498943Sluigi for (lp = list ; lp ; lp = lp->next) { 40598943Sluigi setvareq(savestr(lp->text), flags); 40698943Sluigi } 407102087Sluigi INTON; 40898943Sluigi} 40998943Sluigi 41098943Sluigi 41198943Sluigi 41298943Sluigi/* 41398943Sluigi * Find the value of a variable. Returns NULL if not set. 41498943Sluigi */ 415102087Sluigi 416102087Sluigichar * 417102087Sluigilookupvar(const char *name) 41898943Sluigi{ 41998943Sluigi struct var *v; 42098943Sluigi 42198943Sluigi v = find_var(name, NULL, NULL); 42298943Sluigi if (v == NULL || v->flags & VUNSET) 42398943Sluigi return NULL; 42498943Sluigi return v->text + v->name_len + 1; 42598943Sluigi} 42698943Sluigi 42798943Sluigi 42898943Sluigi 42998943Sluigi/* 43098943Sluigi * Search the environment of a builtin command. If the second argument 43198943Sluigi * is nonzero, return the value of a variable even if it hasn't been 43298943Sluigi * exported. 43398943Sluigi */ 43498943Sluigi 43598943Sluigichar * 436101628Sluigibltinlookup(const char *name, int doall) 43798943Sluigi{ 43898943Sluigi struct strlist *sp; 43998943Sluigi struct var *v; 44098943Sluigi char *result; 441101628Sluigi 442101628Sluigi result = NULL; 44398943Sluigi for (sp = cmdenviron ; sp ; sp = sp->next) { 44498943Sluigi if (varequal(sp->text, name)) 445101628Sluigi result = strchr(sp->text, '=') + 1; 44698943Sluigi } 447101628Sluigi if (result != NULL) 448106505Smaxim return result; 44998943Sluigi 45098943Sluigi v = find_var(name, NULL, NULL); 45198943Sluigi if (v == NULL || v->flags & VUNSET || 45298943Sluigi (!doall && (v->flags & VEXPORT) == 0)) 453101628Sluigi return NULL; 45498943Sluigi return v->text + v->name_len + 1; 455101628Sluigi} 456101628Sluigi 457101628Sluigi 45898943Sluigi/* 459101628Sluigi * Set up locale for a builtin (LANG/LC_* assignments). 460101628Sluigi */ 461101628Sluigivoid 462101628Sluigibltinsetlocale(void) 463101628Sluigi{ 464101628Sluigi struct strlist *lp; 465101628Sluigi int act = 0; 466101628Sluigi char *loc, *locdef; 467101628Sluigi int i; 468101628Sluigi 469101628Sluigi for (lp = cmdenviron ; lp ; lp = lp->next) { 470101628Sluigi if (localevar(lp->text)) { 47198943Sluigi act = 1; 472101628Sluigi break; 473101628Sluigi } 474101628Sluigi } 47598943Sluigi if (!act) 47698943Sluigi return; 47798943Sluigi loc = bltinlookup("LC_ALL", 0); 47898943Sluigi INTOFF; 47998943Sluigi if (loc != NULL) { 48098943Sluigi setlocale(LC_ALL, loc); 48198943Sluigi INTON; 48298943Sluigi updatecharset(); 48398943Sluigi return; 48498943Sluigi } 485101628Sluigi locdef = bltinlookup("LANG", 0); 486101628Sluigi for (i = 0; locale_names[i] != NULL; i++) { 48798943Sluigi loc = bltinlookup(locale_names[i], 0); 48898943Sluigi if (loc == NULL) 48998943Sluigi loc = locdef; 49098943Sluigi if (loc != NULL) 49198943Sluigi setlocale(locale_categories[i], loc); 492101628Sluigi } 49398943Sluigi INTON; 49498943Sluigi updatecharset(); 49598943Sluigi} 49698943Sluigi 49798943Sluigi/* 49898943Sluigi * Undo the effect of bltinlocaleset(). 49998943Sluigi */ 50098943Sluigivoid 50198943Sluigibltinunsetlocale(void) 50298943Sluigi{ 50398943Sluigi struct strlist *lp; 504102087Sluigi 50598943Sluigi INTOFF; 506102087Sluigi for (lp = cmdenviron ; lp ; lp = lp->next) { 50798943Sluigi if (localevar(lp->text)) { 50898943Sluigi setlocale(LC_ALL, ""); 50998943Sluigi updatecharset(); 51098943Sluigi return; 51198943Sluigi } 51298943Sluigi } 51398943Sluigi INTON; 51498943Sluigi} 51598943Sluigi 51698943Sluigi/* 51798943Sluigi * Update the localeisutf8 flag. 51898943Sluigi */ 51998943Sluigivoid 52098943Sluigiupdatecharset(void) 521101978Sluigi{ 522101978Sluigi char *charset; 523101978Sluigi 524101978Sluigi charset = nl_langinfo(CODESET); 525102087Sluigi localeisutf8 = !strcmp(charset, "UTF-8"); 526102087Sluigi} 52798943Sluigi 52898943Sluigivoid 52998943Sluigiinitcharset(void) 53098943Sluigi{ 531102087Sluigi updatecharset(); 53298943Sluigi initial_localeisutf8 = localeisutf8; 53398943Sluigi} 53498943Sluigi 53598943Sluigi/* 53698943Sluigi * Generate a list of exported variables. This routine is used to construct 53798943Sluigi * the third argument to execve when executing a program. 53898943Sluigi */ 53998943Sluigi 54098943Sluigichar ** 54198943Sluigienvironment(void) 54298943Sluigi{ 54398943Sluigi int nenv; 54498943Sluigi struct var **vpp; 54598943Sluigi struct var *vp; 54698943Sluigi char **env, **ep; 54798943Sluigi 54898943Sluigi nenv = 0; 54998943Sluigi for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 55098943Sluigi for (vp = *vpp ; vp ; vp = vp->next) 55198943Sluigi if (vp->flags & VEXPORT) 55298943Sluigi nenv++; 55398943Sluigi } 55498943Sluigi ep = env = stalloc((nenv + 1) * sizeof *env); 55598943Sluigi for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 55698943Sluigi for (vp = *vpp ; vp ; vp = vp->next) 55798943Sluigi if (vp->flags & VEXPORT) 55898943Sluigi *ep++ = vp->text; 55998943Sluigi } 56098943Sluigi *ep = NULL; 56198943Sluigi return env; 56298943Sluigi} 56398943Sluigi 56498943Sluigi 56598943Sluigistatic int 566102087Sluigivar_compare(const void *a, const void *b) 56798943Sluigi{ 56898943Sluigi const char *const *sa, *const *sb; 56998943Sluigi 57098943Sluigi sa = a; 57198943Sluigi sb = b; 57298943Sluigi /* 57399475Sluigi * This compares two var=value strings which creates a different 57498943Sluigi * order from what you would probably expect. POSIX is somewhat 57598943Sluigi * ambiguous on what should be sorted exactly. 57698943Sluigi */ 57798943Sluigi return strcoll(*sa, *sb); 57899475Sluigi} 57998943Sluigi 58099475Sluigi 58198943Sluigi/* 58298943Sluigi * Command to list all variables which are set. This is invoked from the 58398943Sluigi * set command when it is called without any options or operands. 58498943Sluigi */ 58598943Sluigi 58698943Sluigiint 58798943Sluigishowvarscmd(int argc __unused, char **argv __unused) 58898943Sluigi{ 58998943Sluigi struct var **vpp; 59098943Sluigi struct var *vp; 59198943Sluigi const char *s; 59298943Sluigi const char **vars; 59398943Sluigi int i, n; 59498943Sluigi 59598943Sluigi /* 59698943Sluigi * POSIX requires us to sort the variables. 59798943Sluigi */ 59898943Sluigi n = 0; 59998943Sluigi for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) { 60098943Sluigi for (vp = *vpp; vp; vp = vp->next) { 60198943Sluigi if (!(vp->flags & VUNSET)) 60298943Sluigi n++; 60398943Sluigi } 60498943Sluigi } 60598943Sluigi 60698943Sluigi INTOFF; 60798943Sluigi vars = ckmalloc(n * sizeof(*vars)); 60898943Sluigi i = 0; 60998943Sluigi for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) { 61098943Sluigi for (vp = *vpp; vp; vp = vp->next) { 61198943Sluigi if (!(vp->flags & VUNSET)) 61298943Sluigi vars[i++] = vp->text; 61398943Sluigi } 61498943Sluigi } 61598943Sluigi 61698943Sluigi qsort(vars, n, sizeof(*vars), var_compare); 61798943Sluigi for (i = 0; i < n; i++) { 61898943Sluigi /* 61998943Sluigi * Skip improper variable names so the output remains usable as 62098943Sluigi * shell input. 62198943Sluigi */ 62298943Sluigi if (!isassignment(vars[i])) 62398943Sluigi continue; 62498943Sluigi s = strchr(vars[i], '='); 62598943Sluigi s++; 62698943Sluigi outbin(vars[i], s - vars[i], out1); 62798943Sluigi out1qstr(s); 62898943Sluigi out1c('\n'); 62998943Sluigi } 63098943Sluigi ckfree(vars); 63198943Sluigi INTON; 63298943Sluigi 63398943Sluigi return 0; 63498943Sluigi} 63598943Sluigi 63698943Sluigi 63798943Sluigi 63898943Sluigi/* 63998943Sluigi * The export and readonly commands. 64098943Sluigi */ 64198943Sluigi 64298943Sluigiint 643102087Sluigiexportcmd(int argc __unused, char **argv) 64498943Sluigi{ 64598943Sluigi struct var **vpp; 64698943Sluigi struct var *vp; 64798943Sluigi char **ap; 648102087Sluigi char *name; 64998943Sluigi char *p; 65098943Sluigi char *cmdname; 65198943Sluigi int ch, values; 65298943Sluigi int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; 65398943Sluigi 65498943Sluigi cmdname = argv[0]; 65598943Sluigi values = 0; 65698943Sluigi while ((ch = nextopt("p")) != '\0') { 65798943Sluigi switch (ch) { 65898943Sluigi case 'p': 65998943Sluigi values = 1; 66098943Sluigi break; 66198943Sluigi } 66298943Sluigi } 66398943Sluigi 66498943Sluigi if (values && *argptr != NULL) 66598943Sluigi error("-p requires no arguments"); 66698943Sluigi if (*argptr != NULL) { 66798943Sluigi for (ap = argptr; (name = *ap) != NULL; ap++) { 66898943Sluigi if ((p = strchr(name, '=')) != NULL) { 66998943Sluigi p++; 67098943Sluigi } else { 67198943Sluigi vp = find_var(name, NULL, NULL); 67298943Sluigi if (vp != NULL) { 67398943Sluigi vp->flags |= flag; 67498943Sluigi if ((vp->flags & VEXPORT) && localevar(vp->text)) { 67598943Sluigi change_env(vp->text, 1); 67698943Sluigi (void) setlocale(LC_ALL, ""); 67798943Sluigi updatecharset(); 67898943Sluigi } 67998943Sluigi continue; 68098943Sluigi } 68198943Sluigi } 68298943Sluigi setvar(name, p, flag); 68398943Sluigi } 68498943Sluigi } else { 68598943Sluigi for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { 68698943Sluigi for (vp = *vpp ; vp ; vp = vp->next) { 68798943Sluigi if (vp->flags & flag) { 68898943Sluigi if (values) { 68998943Sluigi /* 69098943Sluigi * Skip improper variable names 69198943Sluigi * so the output remains usable 69298943Sluigi * as shell input. 69398943Sluigi */ 69498943Sluigi if (!isassignment(vp->text)) 69598943Sluigi continue; 69698943Sluigi out1str(cmdname); 69798943Sluigi out1c(' '); 69898943Sluigi } 69998943Sluigi if (values && !(vp->flags & VUNSET)) { 70098943Sluigi outbin(vp->text, 70198943Sluigi vp->name_len + 1, out1); 70298943Sluigi out1qstr(vp->text + 70398943Sluigi vp->name_len + 1); 70498943Sluigi } else 70598943Sluigi outbin(vp->text, vp->name_len, 70698943Sluigi out1); 70798943Sluigi out1c('\n'); 70898943Sluigi } 70998943Sluigi } 71098943Sluigi } 71198943Sluigi } 71298943Sluigi return 0; 71398943Sluigi} 71498943Sluigi 71598943Sluigi 71698943Sluigi/* 71799475Sluigi * The "local" command. 71899475Sluigi */ 71999475Sluigi 72099475Sluigiint 72198943Sluigilocalcmd(int argc __unused, char **argv __unused) 72299475Sluigi{ 72399475Sluigi char *name; 72499475Sluigi 72599475Sluigi if (! in_function()) 72699475Sluigi error("Not in a function"); 72799475Sluigi while ((name = *argptr++) != NULL) { 72899475Sluigi mklocal(name); 72999475Sluigi } 73099475Sluigi return 0; 73199475Sluigi} 73299475Sluigi 73399475Sluigi 73499475Sluigi/* 73599475Sluigi * Make a variable a local variable. When a variable is made local, it's 73699475Sluigi * value and flags are saved in a localvar structure. The saved values 73799475Sluigi * will be restored when the shell function returns. We handle the name 73899475Sluigi * "-" as a special case. 73999475Sluigi */ 74099475Sluigi 74199475Sluigivoid 74299475Sluigimklocal(char *name) 74399475Sluigi{ 74499475Sluigi struct localvar *lvp; 74599475Sluigi struct var **vpp; 74699475Sluigi struct var *vp; 74799475Sluigi 74899475Sluigi INTOFF; 74999475Sluigi lvp = ckmalloc(sizeof (struct localvar)); 75099475Sluigi if (name[0] == '-' && name[1] == '\0') { 75199475Sluigi lvp->text = ckmalloc(sizeof optlist); 75299475Sluigi memcpy(lvp->text, optlist, sizeof optlist); 75399475Sluigi vp = NULL; 75499475Sluigi } else { 75599475Sluigi vp = find_var(name, &vpp, NULL); 75698943Sluigi if (vp == NULL) { 75798943Sluigi if (strchr(name, '=')) 75898943Sluigi setvareq(savestr(name), VSTRFIXED | VNOLOCAL); 75998943Sluigi else 760102087Sluigi setvar(name, NULL, VSTRFIXED | VNOLOCAL); 761102087Sluigi vp = *vpp; /* the new variable */ 762101978Sluigi lvp->text = NULL; 763102087Sluigi lvp->flags = VUNSET; 764102087Sluigi } else { 765102087Sluigi lvp->text = vp->text; 766102087Sluigi lvp->flags = vp->flags; 767102087Sluigi vp->flags |= VSTRFIXED|VTEXTFIXED; 76898943Sluigi if (name[vp->name_len] == '=') 769101978Sluigi setvareq(savestr(name), VNOLOCAL); 770101978Sluigi } 771101978Sluigi } 772101978Sluigi lvp->vp = vp; 773101978Sluigi lvp->next = localvars; 774102087Sluigi localvars = lvp; 77598943Sluigi INTON; 776101978Sluigi} 77798943Sluigi 778102087Sluigi 77998943Sluigi/* 780102087Sluigi * Called after a function returns. 781102087Sluigi */ 782102087Sluigi 783102087Sluigivoid 784102087Sluigipoplocalvars(void) 785102087Sluigi{ 786102087Sluigi struct localvar *lvp; 787102087Sluigi struct var *vp; 788102087Sluigi 789102087Sluigi while ((lvp = localvars) != NULL) { 790102087Sluigi localvars = lvp->next; 791102087Sluigi vp = lvp->vp; 792101978Sluigi if (vp == NULL) { /* $- saved */ 793102087Sluigi memcpy(optlist, lvp->text, sizeof optlist); 794102087Sluigi ckfree(lvp->text); 795102087Sluigi optschanged(); 796102087Sluigi } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { 797102087Sluigi (void)unsetvar(vp->text); 798102087Sluigi } else { 799102087Sluigi if ((vp->flags & VTEXTFIXED) == 0) 800102087Sluigi ckfree(vp->text); 80198943Sluigi vp->flags = lvp->flags; 80298943Sluigi vp->text = lvp->text; 80398943Sluigi } 80498943Sluigi ckfree(lvp); 80598943Sluigi } 80698943Sluigi} 807107291Skeramida 80898943Sluigi 80998943Sluigiint 81098943Sluigisetvarcmd(int argc, char **argv) 81198943Sluigi{ 81298943Sluigi if (argc <= 2) 81398943Sluigi return unsetcmd(argc, argv); 81498943Sluigi else if (argc == 3) 815105887Smux setvar(argv[1], argv[2], 0); 816101628Sluigi else 817101628Sluigi error("too many arguments"); 818101628Sluigi return 0; 819101628Sluigi} 820101628Sluigi 821101628Sluigi 822101628Sluigi/* 82398943Sluigi * The unset builtin command. 82498943Sluigi */ 82598943Sluigi 82698943Sluigiint 82798943Sluigiunsetcmd(int argc __unused, char **argv __unused) 82898943Sluigi{ 829107291Skeramida char **ap; 830107291Skeramida int i; 831107291Skeramida int flg_func = 0; 832107291Skeramida int flg_var = 0; 833107291Skeramida int ret = 0; 834107291Skeramida 835107291Skeramida while ((i = nextopt("vf")) != '\0') { 836107291Skeramida if (i == 'f') 83798943Sluigi flg_func = 1; 838107291Skeramida else 83998943Sluigi flg_var = 1; 84098943Sluigi } 84198943Sluigi if (flg_func == 0 && flg_var == 0) 84298943Sluigi flg_var = 1; 84398943Sluigi 844107291Skeramida for (ap = argptr; *ap ; ap++) { 84598943Sluigi if (flg_func) 84698943Sluigi ret |= unsetfunc(*ap); 84798943Sluigi if (flg_var) 848101628Sluigi ret |= unsetvar(*ap); 849101628Sluigi } 850101628Sluigi return ret; 85198943Sluigi} 852107289Sluigi 853107289Sluigi 854107289Sluigi/* 855107289Sluigi * Unset the specified variable. 856107289Sluigi */ 857107289Sluigi 858107289Sluigiint 859107289Sluigiunsetvar(const char *s) 860107289Sluigi{ 861107289Sluigi struct var **vpp; 862107289Sluigi struct var *vp; 863107289Sluigi 864107289Sluigi vp = find_var(s, &vpp, NULL); 865107289Sluigi if (vp == NULL) 86698943Sluigi return (0); 86798943Sluigi if (vp->flags & VREADONLY) 86898943Sluigi return (1); 86998943Sluigi INTOFF; 87098943Sluigi if (vp->text[vp->name_len + 1] != '\0') 87198943Sluigi setvar(s, nullstr, 0); 87298943Sluigi if ((vp->flags & VEXPORT) && localevar(vp->text)) { 873102087Sluigi change_env(s, 0); 87498943Sluigi setlocale(LC_ALL, ""); 87598943Sluigi updatecharset(); 87698943Sluigi } 87798943Sluigi vp->flags &= ~VEXPORT; 87898943Sluigi vp->flags |= VUNSET; 87998943Sluigi if ((vp->flags & VSTRFIXED) == 0) { 88098943Sluigi if ((vp->flags & VTEXTFIXED) == 0) 88198943Sluigi ckfree(vp->text); 88298943Sluigi *vpp = vp->next; 88398943Sluigi ckfree(vp); 88498943Sluigi } 88598943Sluigi INTON; 88698943Sluigi return (0); 88798943Sluigi} 88899475Sluigi 88999475Sluigi 89099475Sluigi 89199475Sluigi/* 89299475Sluigi * Returns true if the two strings specify the same varable. The first 89399475Sluigi * variable name is terminated by '='; the second may be terminated by 89499475Sluigi * either '=' or '\0'. 89599475Sluigi */ 89699475Sluigi 89798943Sluigistatic int 89898943Sluigivarequal(const char *p, const char *q) 89998943Sluigi{ 90098943Sluigi while (*p == *q++) { 90198943Sluigi if (*p++ == '=') 90298943Sluigi return 1; 90398943Sluigi } 90498943Sluigi if (*p == '=' && *(q - 1) == '\0') 90598943Sluigi return 1; 90698943Sluigi return 0; 90798943Sluigi} 90898943Sluigi 90998943Sluigi/* 91098943Sluigi * Search for a variable. 91198943Sluigi * 'name' may be terminated by '=' or a NUL. 91298943Sluigi * vppp is set to the pointer to vp, or the list head if vp isn't found 91398943Sluigi * lenp is set to the number of charactets in 'name' 91498943Sluigi */ 91598943Sluigi 91698943Sluigistatic struct var * 91798943Sluigifind_var(const char *name, struct var ***vppp, int *lenp) 91898943Sluigi{ 91998943Sluigi unsigned int hashval; 92098943Sluigi int len; 92198943Sluigi struct var *vp, **vpp; 92298943Sluigi const char *p = name; 923103241Sluigi 92498943Sluigi hashval = 0; 92598943Sluigi while (*p && *p != '=') 92698943Sluigi hashval = 2 * hashval + (unsigned char)*p++; 92798943Sluigi len = p - name; 92898943Sluigi 92998943Sluigi if (lenp) 93098943Sluigi *lenp = len; 93198943Sluigi vpp = &vartab[hashval % VTABSIZE]; 93298943Sluigi if (vppp) 93398943Sluigi *vppp = vpp; 93498943Sluigi 93598943Sluigi for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { 93698943Sluigi if (vp->name_len != len) 93798943Sluigi continue; 93899909Sluigi if (memcmp(vp->text, name, len) != 0) 93998943Sluigi continue; 94099909Sluigi if (vppp) 94198943Sluigi *vppp = vpp; 942102087Sluigi return vp; 94398943Sluigi } 944102087Sluigi return NULL; 94598943Sluigi} 946102087Sluigi