11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 4. Neither the name of the University nor the names of its contributors
171556Srgrimes *    may be used to endorse or promote products derived from this software
181556Srgrimes *    without specific prior written permission.
191556Srgrimes *
201556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301556Srgrimes * SUCH DAMAGE.
311556Srgrimes */
321556Srgrimes
331556Srgrimes#ifndef lint
3436150Scharnier#if 0
3536150Scharnierstatic char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
3636150Scharnier#endif
371556Srgrimes#endif /* not lint */
3899110Sobrien#include <sys/cdefs.h>
3999110Sobrien__FBSDID("$FreeBSD: stable/11/bin/sh/var.c 318279 2017-05-14 20:17:50Z jilles $");
401556Srgrimes
4117987Speter#include <unistd.h>
4217987Speter#include <stdlib.h>
43114763Sobrien#include <paths.h>
4417987Speter
451556Srgrimes/*
461556Srgrimes * Shell variables.
471556Srgrimes */
481556Srgrimes
4917525Sache#include <locale.h>
50221559Sjilles#include <langinfo.h>
5117525Sache
521556Srgrimes#include "shell.h"
531556Srgrimes#include "output.h"
541556Srgrimes#include "expand.h"
551556Srgrimes#include "nodes.h"	/* for other headers */
561556Srgrimes#include "eval.h"	/* defines cmdenviron */
571556Srgrimes#include "exec.h"
581556Srgrimes#include "syntax.h"
591556Srgrimes#include "options.h"
601556Srgrimes#include "mail.h"
611556Srgrimes#include "var.h"
621556Srgrimes#include "memalloc.h"
631556Srgrimes#include "error.h"
641556Srgrimes#include "mystring.h"
6520425Ssteve#include "parser.h"
66223060Sjilles#include "builtins.h"
6717987Speter#ifndef NO_HISTORY
6817987Speter#include "myhistedit.h"
6917987Speter#endif
701556Srgrimes
711556Srgrimes
721556Srgrimes#define VTABSIZE 39
731556Srgrimes
741556Srgrimes
751556Srgrimesstruct varinit {
761556Srgrimes	struct var *var;
771556Srgrimes	int flags;
78201056Sjilles	const char *text;
7990111Simp	void (*func)(const char *);
801556Srgrimes};
811556Srgrimes
821556Srgrimes
8317987Speter#ifndef NO_HISTORY
841556Srgrimesstruct var vhistsize;
85208755Sjillesstruct var vterm;
8617987Speter#endif
871556Srgrimesstruct var vifs;
881556Srgrimesstruct var vmail;
891556Srgrimesstruct var vmpath;
901556Srgrimesstruct var vpath;
911556Srgrimesstruct var vps1;
921556Srgrimesstruct var vps2;
93159632Sstefanfstruct var vps4;
94213760Sobrienstatic struct var voptind;
95230998Sjillesstruct var vdisvfork;
961556Srgrimes
97279569Sjillesstruct localvar *localvars;
98223024Sjillesint forcelocal;
99223024Sjilles
100213760Sobrienstatic const struct varinit varinit[] = {
10117987Speter#ifndef NO_HISTORY
102201056Sjilles	{ &vhistsize,	VUNSET,				"HISTSIZE=",
10320425Ssteve	  sethistsize },
10417987Speter#endif
105201056Sjilles	{ &vifs,	0,				"IFS= \t\n",
10620425Ssteve	  NULL },
107201056Sjilles	{ &vmail,	VUNSET,				"MAIL=",
10820425Ssteve	  NULL },
109201056Sjilles	{ &vmpath,	VUNSET,				"MAILPATH=",
11020425Ssteve	  NULL },
111201056Sjilles	{ &vpath,	0,				"PATH=" _PATH_DEFPATH,
11220425Ssteve	  changepath },
1138855Srgrimes	/*
1141556Srgrimes	 * vps1 depends on uid
1151556Srgrimes	 */
116201056Sjilles	{ &vps2,	0,				"PS2=> ",
11720425Ssteve	  NULL },
118201056Sjilles	{ &vps4,	0,				"PS4=+ ",
119159632Sstefanf	  NULL },
120208755Sjilles#ifndef NO_HISTORY
121208755Sjilles	{ &vterm,	VUNSET,				"TERM=",
122208755Sjilles	  setterm },
123208755Sjilles#endif
124201056Sjilles	{ &voptind,	0,				"OPTIND=1",
12520425Ssteve	  getoptsreset },
126230998Sjilles	{ &vdisvfork,	VUNSET,				"SH_DISABLE_VFORK=",
127230998Sjilles	  NULL },
12820425Ssteve	{ NULL,	0,				NULL,
12920425Ssteve	  NULL }
1301556Srgrimes};
1311556Srgrimes
132213760Sobrienstatic struct var *vartab[VTABSIZE];
1331556Srgrimes
134213760Sobrienstatic const char *const locale_names[7] = {
135207678Sjilles	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
136207678Sjilles	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
137207678Sjilles};
138213760Sobrienstatic const int locale_categories[7] = {
139207678Sjilles	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
140207678Sjilles};
141207678Sjilles
142213811Sobrienstatic int varequal(const char *, const char *);
143221668Sjillesstatic struct var *find_var(const char *, struct var ***, int *);
144213811Sobrienstatic int localevar(const char *);
145279508Sjillesstatic void setvareq_const(const char *s, int flags);
1461556Srgrimes
147245689Sjillesextern char **environ;
1481556Srgrimes
1491556Srgrimes/*
150245689Sjilles * This routine initializes the builtin variables and imports the environment.
151245689Sjilles * It is called when the shell is initialized.
1521556Srgrimes */
1531556Srgrimes
1541556Srgrimesvoid
15590111Simpinitvar(void)
15690111Simp{
15797689Stjr	char ppid[20];
1581556Srgrimes	const struct varinit *ip;
1591556Srgrimes	struct var *vp;
1601556Srgrimes	struct var **vpp;
161245689Sjilles	char **envp;
1621556Srgrimes
1631556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
164221668Sjilles		if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
165221668Sjilles			continue;
166221668Sjilles		vp->next = *vpp;
167221668Sjilles		*vpp = vp;
168221668Sjilles		vp->text = __DECONST(char *, ip->text);
169221668Sjilles		vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
170221668Sjilles		vp->func = ip->func;
1711556Srgrimes	}
1721556Srgrimes	/*
1731556Srgrimes	 * PS1 depends on uid
1741556Srgrimes	 */
175221668Sjilles	if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
1761556Srgrimes		vps1.next = *vpp;
1771556Srgrimes		*vpp = &vps1;
178201056Sjilles		vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
1791556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1801556Srgrimes	}
181259874Sjilles	fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
182259874Sjilles	setvarsafe("PPID", ppid, 0);
183245689Sjilles	for (envp = environ ; *envp ; envp++) {
184245689Sjilles		if (strchr(*envp, '=')) {
185245689Sjilles			setvareq(*envp, VEXPORT|VTEXTFIXED);
186245689Sjilles		}
187245689Sjilles	}
188279508Sjilles	setvareq_const("OPTIND=1", 0);
1891556Srgrimes}
1901556Srgrimes
1911556Srgrimes/*
19220425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
19320425Ssteve */
19420425Ssteve
19520425Ssteveint
196200956Sjillessetvarsafe(const char *name, const char *val, int flags)
19720425Ssteve{
19820425Ssteve	struct jmploc jmploc;
199194765Sjilles	struct jmploc *const savehandler = handler;
20020425Ssteve	int err = 0;
201199660Sjilles	int inton;
20220425Ssteve
203199660Sjilles	inton = is_int_on();
20420425Ssteve	if (setjmp(jmploc.loc))
20520425Ssteve		err = 1;
20620425Ssteve	else {
20720425Ssteve		handler = &jmploc;
20820425Ssteve		setvar(name, val, flags);
20920425Ssteve	}
21020425Ssteve	handler = savehandler;
211199660Sjilles	SETINTON(inton);
21220425Ssteve	return err;
21320425Ssteve}
21420425Ssteve
21520425Ssteve/*
216155302Sschweikh * Set the value of a variable.  The flags argument is stored with the
2171556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2181556Srgrimes */
2191556Srgrimes
2201556Srgrimesvoid
221200956Sjillessetvar(const char *name, const char *val, int flags)
22217987Speter{
223200956Sjilles	const char *p;
224258776Sjilles	size_t len;
225258776Sjilles	size_t namelen;
226258776Sjilles	size_t vallen;
2271556Srgrimes	char *nameeq;
2281556Srgrimes	int isbad;
2291556Srgrimes
2301556Srgrimes	isbad = 0;
2311556Srgrimes	p = name;
23217525Sache	if (! is_name(*p))
2331556Srgrimes		isbad = 1;
23417525Sache	p++;
2351556Srgrimes	for (;;) {
2361556Srgrimes		if (! is_in_name(*p)) {
2371556Srgrimes			if (*p == '\0' || *p == '=')
2381556Srgrimes				break;
2391556Srgrimes			isbad = 1;
2401556Srgrimes		}
2411556Srgrimes		p++;
2421556Srgrimes	}
2431556Srgrimes	namelen = p - name;
2441556Srgrimes	if (isbad)
245258776Sjilles		error("%.*s: bad variable name", (int)namelen, name);
2461556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2471556Srgrimes	if (val == NULL) {
2481556Srgrimes		flags |= VUNSET;
249258776Sjilles		vallen = 0;
2501556Srgrimes	} else {
251258776Sjilles		vallen = strlen(val);
252258776Sjilles		len += vallen;
2531556Srgrimes	}
254263777Sjilles	INTOFF;
255200956Sjilles	nameeq = ckmalloc(len);
256200956Sjilles	memcpy(nameeq, name, namelen);
257200956Sjilles	nameeq[namelen] = '=';
2581556Srgrimes	if (val)
259258776Sjilles		memcpy(nameeq + namelen + 1, val, vallen + 1);
260200956Sjilles	else
261200956Sjilles		nameeq[namelen + 1] = '\0';
2621556Srgrimes	setvareq(nameeq, flags);
263263777Sjilles	INTON;
2641556Srgrimes}
2651556Srgrimes
266213811Sobrienstatic int
267200956Sjilleslocalevar(const char *s)
26890111Simp{
269207678Sjilles	const char *const *ss;
2701556Srgrimes
27117525Sache	if (*s != 'L')
27217525Sache		return 0;
27317525Sache	if (varequal(s + 1, "ANG"))
27417525Sache		return 1;
27517525Sache	if (strncmp(s + 1, "C_", 2) != 0)
27617525Sache		return 0;
277207678Sjilles	if (varequal(s + 3, "ALL"))
278207678Sjilles		return 1;
279207678Sjilles	for (ss = locale_names; *ss ; ss++)
280207678Sjilles		if (varequal(s + 3, *ss + 3))
28117525Sache			return 1;
28217525Sache	return 0;
28317525Sache}
2841556Srgrimes
285171268Sscf
2861556Srgrimes/*
287171268Sscf * Sets/unsets an environment variable from a pointer that may actually be a
288171268Sscf * pointer into environ where the string should not be manipulated.
289171268Sscf */
290213811Sobrienstatic void
291200956Sjilleschange_env(const char *s, int set)
292171268Sscf{
293171268Sscf	char *eqp;
294171268Sscf	char *ss;
295171268Sscf
296263777Sjilles	INTOFF;
297171268Sscf	ss = savestr(s);
298171268Sscf	if ((eqp = strchr(ss, '=')) != NULL)
299171268Sscf		*eqp = '\0';
300171268Sscf	if (set && eqp != NULL)
301171268Sscf		(void) setenv(ss, eqp + 1, 1);
302171268Sscf	else
303171268Sscf		(void) unsetenv(ss);
304171268Sscf	ckfree(ss);
305263777Sjilles	INTON;
306171268Sscf
307171268Sscf	return;
308171268Sscf}
309171268Sscf
310171268Sscf
311171268Sscf/*
3121556Srgrimes * Same as setvar except that the variable and value are passed in
3131556Srgrimes * the first argument as name=value.  Since the first argument will
3141556Srgrimes * be actually stored in the table, it should not be a string that
3151556Srgrimes * will go away.
3161556Srgrimes */
3171556Srgrimes
3181556Srgrimesvoid
31990111Simpsetvareq(char *s, int flags)
32017987Speter{
3211556Srgrimes	struct var *vp, **vpp;
322221668Sjilles	int nlen;
3231556Srgrimes
32445263Scracauer	if (aflag)
32545263Scracauer		flags |= VEXPORT;
326223024Sjilles	if (forcelocal && !(flags & (VNOSET | VNOLOCAL)))
327223024Sjilles		mklocal(s);
328221668Sjilles	vp = find_var(s, &vpp, &nlen);
329221668Sjilles	if (vp != NULL) {
330263846Sjilles		if (vp->flags & VREADONLY) {
331263846Sjilles			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
332263846Sjilles				ckfree(s);
333292360Sjilles			error("%.*s: is read only", vp->name_len, vp->text);
334263846Sjilles		}
335263847Sjilles		if (flags & VNOSET) {
336263847Sjilles			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
337263847Sjilles				ckfree(s);
338221668Sjilles			return;
339263847Sjilles		}
340221668Sjilles		INTOFF;
34120425Ssteve
342221668Sjilles		if (vp->func && (flags & VNOFUNC) == 0)
343221668Sjilles			(*vp->func)(s + vp->name_len + 1);
34420425Ssteve
345221668Sjilles		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
346221668Sjilles			ckfree(vp->text);
34720425Ssteve
348221668Sjilles		vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
349221668Sjilles		vp->flags |= flags;
350221668Sjilles		vp->text = s;
35120425Ssteve
352221668Sjilles		/*
353221668Sjilles		 * We could roll this to a function, to handle it as
354221668Sjilles		 * a regular variable function callback, but why bother?
355221668Sjilles		 *
356221668Sjilles		 * Note: this assumes iflag is not set to 1 initially.
357245689Sjilles		 * As part of initvar(), this is called before arguments
358221668Sjilles		 * are looked at.
359221668Sjilles		 */
360221668Sjilles		if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
361221668Sjilles		    iflag == 1)
362221668Sjilles			chkmail(1);
363221668Sjilles		if ((vp->flags & VEXPORT) && localevar(s)) {
364221668Sjilles			change_env(s, 1);
365221668Sjilles			(void) setlocale(LC_ALL, "");
366221668Sjilles			updatecharset();
3671556Srgrimes		}
368221668Sjilles		INTON;
369221668Sjilles		return;
3701556Srgrimes	}
3711556Srgrimes	/* not found */
372263847Sjilles	if (flags & VNOSET) {
373263847Sjilles		if ((flags & (VTEXTFIXED|VSTACK)) == 0)
374263847Sjilles			ckfree(s);
375216870Sjilles		return;
376263847Sjilles	}
377263777Sjilles	INTOFF;
3781556Srgrimes	vp = ckmalloc(sizeof (*vp));
3791556Srgrimes	vp->flags = flags;
3801556Srgrimes	vp->text = s;
381221668Sjilles	vp->name_len = nlen;
3821556Srgrimes	vp->next = *vpp;
38320425Ssteve	vp->func = NULL;
3841556Srgrimes	*vpp = vp;
38517525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
386171268Sscf		change_env(s, 1);
38717525Sache		(void) setlocale(LC_ALL, "");
388221559Sjilles		updatecharset();
38917525Sache	}
39017525Sache	INTON;
3911556Srgrimes}
3921556Srgrimes
3931556Srgrimes
394279508Sjillesstatic void
395279508Sjillessetvareq_const(const char *s, int flags)
396279508Sjilles{
397279508Sjilles	setvareq(__DECONST(char *, s), flags | VTEXTFIXED);
398279508Sjilles}
3991556Srgrimes
400279508Sjilles
4011556Srgrimes/*
4021556Srgrimes * Process a linked list of variable assignments.
4031556Srgrimes */
4041556Srgrimes
4051556Srgrimesvoid
406289159Sjilleslistsetvar(struct arglist *list, int flags)
40790111Simp{
408289159Sjilles	int i;
4091556Srgrimes
4101556Srgrimes	INTOFF;
411289159Sjilles	for (i = 0; i < list->count; i++)
412289159Sjilles		setvareq(savestr(list->args[i]), flags);
4131556Srgrimes	INTON;
4141556Srgrimes}
4151556Srgrimes
4161556Srgrimes
4171556Srgrimes
4181556Srgrimes/*
4191556Srgrimes * Find the value of a variable.  Returns NULL if not set.
4201556Srgrimes */
4211556Srgrimes
4221556Srgrimeschar *
423200956Sjilleslookupvar(const char *name)
42490111Simp{
4251556Srgrimes	struct var *v;
4261556Srgrimes
427221668Sjilles	v = find_var(name, NULL, NULL);
428221668Sjilles	if (v == NULL || v->flags & VUNSET)
429221668Sjilles		return NULL;
430221668Sjilles	return v->text + v->name_len + 1;
4311556Srgrimes}
4321556Srgrimes
4331556Srgrimes
4341556Srgrimes
4351556Srgrimes/*
4361556Srgrimes * Search the environment of a builtin command.  If the second argument
4371556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
4381556Srgrimes * exported.
4391556Srgrimes */
4401556Srgrimes
4411556Srgrimeschar *
442200956Sjillesbltinlookup(const char *name, int doall)
44317987Speter{
4441556Srgrimes	struct var *v;
445212467Sjilles	char *result;
446289159Sjilles	int i;
4471556Srgrimes
448212467Sjilles	result = NULL;
449289159Sjilles	if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) {
450289159Sjilles		if (varequal(cmdenviron->args[i], name))
451289159Sjilles			result = strchr(cmdenviron->args[i], '=') + 1;
4521556Srgrimes	}
453212467Sjilles	if (result != NULL)
454212467Sjilles		return result;
455221668Sjilles
456221668Sjilles	v = find_var(name, NULL, NULL);
457221668Sjilles	if (v == NULL || v->flags & VUNSET ||
458221668Sjilles	    (!doall && (v->flags & VEXPORT) == 0))
459221668Sjilles		return NULL;
460221668Sjilles	return v->text + v->name_len + 1;
4611556Srgrimes}
4621556Srgrimes
4631556Srgrimes
464207678Sjilles/*
465207678Sjilles * Set up locale for a builtin (LANG/LC_* assignments).
466207678Sjilles */
467207678Sjillesvoid
468207678Sjillesbltinsetlocale(void)
469207678Sjilles{
470207678Sjilles	int act = 0;
471207678Sjilles	char *loc, *locdef;
472207678Sjilles	int i;
4731556Srgrimes
474289159Sjilles	if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) {
475289159Sjilles		if (localevar(cmdenviron->args[i])) {
476207678Sjilles			act = 1;
477207678Sjilles			break;
478207678Sjilles		}
479207678Sjilles	}
480207678Sjilles	if (!act)
481207678Sjilles		return;
482207678Sjilles	loc = bltinlookup("LC_ALL", 0);
483207678Sjilles	INTOFF;
484207678Sjilles	if (loc != NULL) {
485207678Sjilles		setlocale(LC_ALL, loc);
486207678Sjilles		INTON;
487221559Sjilles		updatecharset();
488207678Sjilles		return;
489207678Sjilles	}
490207678Sjilles	locdef = bltinlookup("LANG", 0);
491207678Sjilles	for (i = 0; locale_names[i] != NULL; i++) {
492207678Sjilles		loc = bltinlookup(locale_names[i], 0);
493207678Sjilles		if (loc == NULL)
494207678Sjilles			loc = locdef;
495207678Sjilles		if (loc != NULL)
496207678Sjilles			setlocale(locale_categories[i], loc);
497207678Sjilles	}
498207678Sjilles	INTON;
499221559Sjilles	updatecharset();
500207678Sjilles}
501207678Sjilles
5021556Srgrimes/*
503207678Sjilles * Undo the effect of bltinlocaleset().
504207678Sjilles */
505207678Sjillesvoid
506207678Sjillesbltinunsetlocale(void)
507207678Sjilles{
508289159Sjilles	int i;
509207678Sjilles
510207678Sjilles	INTOFF;
511289159Sjilles	if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) {
512289159Sjilles		if (localevar(cmdenviron->args[i])) {
513207678Sjilles			setlocale(LC_ALL, "");
514221559Sjilles			updatecharset();
515318279Sjilles			break;
516207678Sjilles		}
517207678Sjilles	}
518207678Sjilles	INTON;
519207678Sjilles}
520207678Sjilles
521221559Sjilles/*
522221559Sjilles * Update the localeisutf8 flag.
523221559Sjilles */
524221559Sjillesvoid
525221559Sjillesupdatecharset(void)
526221559Sjilles{
527221559Sjilles	char *charset;
528207678Sjilles
529221559Sjilles	charset = nl_langinfo(CODESET);
530221559Sjilles	localeisutf8 = !strcmp(charset, "UTF-8");
531221559Sjilles}
532221559Sjilles
533221669Sjillesvoid
534221669Sjillesinitcharset(void)
535221669Sjilles{
536221669Sjilles	updatecharset();
537221669Sjilles	initial_localeisutf8 = localeisutf8;
538221669Sjilles}
539221669Sjilles
540207678Sjilles/*
5411556Srgrimes * Generate a list of exported variables.  This routine is used to construct
5421556Srgrimes * the third argument to execve when executing a program.
5431556Srgrimes */
5441556Srgrimes
5451556Srgrimeschar **
54690111Simpenvironment(void)
54790111Simp{
5481556Srgrimes	int nenv;
5491556Srgrimes	struct var **vpp;
5501556Srgrimes	struct var *vp;
5511556Srgrimes	char **env, **ep;
5521556Srgrimes
5531556Srgrimes	nenv = 0;
5541556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5551556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
5561556Srgrimes			if (vp->flags & VEXPORT)
5571556Srgrimes				nenv++;
5581556Srgrimes	}
5591556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
5601556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5611556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
5621556Srgrimes			if (vp->flags & VEXPORT)
5631556Srgrimes				*ep++ = vp->text;
5641556Srgrimes	}
5651556Srgrimes	*ep = NULL;
5661556Srgrimes	return env;
5671556Srgrimes}
5681556Srgrimes
5691556Srgrimes
570213811Sobrienstatic int
571158145Sstefanfvar_compare(const void *a, const void *b)
572158145Sstefanf{
573158145Sstefanf	const char *const *sa, *const *sb;
5741556Srgrimes
575158145Sstefanf	sa = a;
576158145Sstefanf	sb = b;
577158145Sstefanf	/*
578158145Sstefanf	 * This compares two var=value strings which creates a different
579158145Sstefanf	 * order from what you would probably expect.  POSIX is somewhat
580158145Sstefanf	 * ambiguous on what should be sorted exactly.
581158145Sstefanf	 */
582158145Sstefanf	return strcoll(*sa, *sb);
583158145Sstefanf}
584158145Sstefanf
585158145Sstefanf
5861556Srgrimes/*
587217847Sjilles * Command to list all variables which are set.  This is invoked from the
588217847Sjilles * set command when it is called without any options or operands.
5891556Srgrimes */
5901556Srgrimes
5911556Srgrimesint
59290111Simpshowvarscmd(int argc __unused, char **argv __unused)
59317987Speter{
5941556Srgrimes	struct var **vpp;
5951556Srgrimes	struct var *vp;
59697915Stjr	const char *s;
597158145Sstefanf	const char **vars;
598158145Sstefanf	int i, n;
5991556Srgrimes
600158145Sstefanf	/*
601158145Sstefanf	 * POSIX requires us to sort the variables.
602158145Sstefanf	 */
603158145Sstefanf	n = 0;
604158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
605158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
606158145Sstefanf			if (!(vp->flags & VUNSET))
607158145Sstefanf				n++;
6081556Srgrimes		}
6091556Srgrimes	}
610158145Sstefanf
611231001Sjilles	INTOFF;
612158145Sstefanf	vars = ckmalloc(n * sizeof(*vars));
613158145Sstefanf	i = 0;
614158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
615158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
616158145Sstefanf			if (!(vp->flags & VUNSET))
617158145Sstefanf				vars[i++] = vp->text;
618158145Sstefanf		}
619158145Sstefanf	}
620158145Sstefanf
621158145Sstefanf	qsort(vars, n, sizeof(*vars), var_compare);
622158145Sstefanf	for (i = 0; i < n; i++) {
623223183Sjilles		/*
624223183Sjilles		 * Skip improper variable names so the output remains usable as
625223183Sjilles		 * shell input.
626223183Sjilles		 */
627223183Sjilles		if (!isassignment(vars[i]))
628223183Sjilles			continue;
629215567Sjilles		s = strchr(vars[i], '=');
630215567Sjilles		s++;
631215567Sjilles		outbin(vars[i], s - vars[i], out1);
632215567Sjilles		out1qstr(s);
633158145Sstefanf		out1c('\n');
634158145Sstefanf	}
635158145Sstefanf	ckfree(vars);
636231001Sjilles	INTON;
637158145Sstefanf
6381556Srgrimes	return 0;
6391556Srgrimes}
6401556Srgrimes
6411556Srgrimes
6421556Srgrimes
6431556Srgrimes/*
6441556Srgrimes * The export and readonly commands.
6451556Srgrimes */
6461556Srgrimes
6471556Srgrimesint
648240541Sjillesexportcmd(int argc __unused, char **argv)
64917987Speter{
6501556Srgrimes	struct var **vpp;
6511556Srgrimes	struct var *vp;
652240541Sjilles	char **ap;
6531556Srgrimes	char *name;
6541556Srgrimes	char *p;
65597914Stjr	char *cmdname;
65697914Stjr	int ch, values;
6571556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
6581556Srgrimes
65997914Stjr	cmdname = argv[0];
66097914Stjr	values = 0;
661240541Sjilles	while ((ch = nextopt("p")) != '\0') {
66297914Stjr		switch (ch) {
66397914Stjr		case 'p':
66497914Stjr			values = 1;
66597914Stjr			break;
66697914Stjr		}
66797914Stjr	}
66897914Stjr
669240541Sjilles	if (values && *argptr != NULL)
670149919Sstefanf		error("-p requires no arguments");
671240541Sjilles	if (*argptr != NULL) {
672240541Sjilles		for (ap = argptr; (name = *ap) != NULL; ap++) {
6731556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
6741556Srgrimes				p++;
6751556Srgrimes			} else {
676221668Sjilles				vp = find_var(name, NULL, NULL);
677221668Sjilles				if (vp != NULL) {
678221668Sjilles					vp->flags |= flag;
679221668Sjilles					if ((vp->flags & VEXPORT) && localevar(vp->text)) {
680221668Sjilles						change_env(vp->text, 1);
681221668Sjilles						(void) setlocale(LC_ALL, "");
682221668Sjilles						updatecharset();
6831556Srgrimes					}
684221668Sjilles					continue;
6851556Srgrimes				}
6861556Srgrimes			}
6871556Srgrimes			setvar(name, p, flag);
6881556Srgrimes		}
6891556Srgrimes	} else {
6901556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
6911556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
6921556Srgrimes				if (vp->flags & flag) {
69397914Stjr					if (values) {
694223183Sjilles						/*
695223183Sjilles						 * Skip improper variable names
696223183Sjilles						 * so the output remains usable
697223183Sjilles						 * as shell input.
698223183Sjilles						 */
699223183Sjilles						if (!isassignment(vp->text))
700223183Sjilles							continue;
70197914Stjr						out1str(cmdname);
70297914Stjr						out1c(' ');
70397914Stjr					}
70497914Stjr					if (values && !(vp->flags & VUNSET)) {
705221975Sjilles						outbin(vp->text,
706221975Sjilles						    vp->name_len + 1, out1);
707221975Sjilles						out1qstr(vp->text +
708221975Sjilles						    vp->name_len + 1);
709215567Sjilles					} else
710221975Sjilles						outbin(vp->text, vp->name_len,
711215567Sjilles						    out1);
7121556Srgrimes					out1c('\n');
7131556Srgrimes				}
7141556Srgrimes			}
7151556Srgrimes		}
7161556Srgrimes	}
7171556Srgrimes	return 0;
7181556Srgrimes}
7191556Srgrimes
7201556Srgrimes
7211556Srgrimes/*
7221556Srgrimes * The "local" command.
7231556Srgrimes */
7241556Srgrimes
72517987Speterint
72690111Simplocalcmd(int argc __unused, char **argv __unused)
72717987Speter{
7281556Srgrimes	char *name;
7291556Srgrimes
730254339Sjilles	nextopt("");
7311556Srgrimes	if (! in_function())
7321556Srgrimes		error("Not in a function");
7331556Srgrimes	while ((name = *argptr++) != NULL) {
7341556Srgrimes		mklocal(name);
7351556Srgrimes	}
7361556Srgrimes	return 0;
7371556Srgrimes}
7381556Srgrimes
7391556Srgrimes
7401556Srgrimes/*
7411556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
7421556Srgrimes * value and flags are saved in a localvar structure.  The saved values
7431556Srgrimes * will be restored when the shell function returns.  We handle the name
7441556Srgrimes * "-" as a special case.
7451556Srgrimes */
7461556Srgrimes
7471556Srgrimesvoid
74890111Simpmklocal(char *name)
74990111Simp{
7501556Srgrimes	struct localvar *lvp;
7511556Srgrimes	struct var **vpp;
7521556Srgrimes	struct var *vp;
7531556Srgrimes
7541556Srgrimes	INTOFF;
7551556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
7561556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
757293392Sjilles		lvp->text = ckmalloc(sizeof optval);
758293392Sjilles		memcpy(lvp->text, optval, sizeof optval);
7591556Srgrimes		vp = NULL;
7601556Srgrimes	} else {
761221668Sjilles		vp = find_var(name, &vpp, NULL);
7621556Srgrimes		if (vp == NULL) {
7631556Srgrimes			if (strchr(name, '='))
764223024Sjilles				setvareq(savestr(name), VSTRFIXED | VNOLOCAL);
7651556Srgrimes			else
766223024Sjilles				setvar(name, NULL, VSTRFIXED | VNOLOCAL);
7671556Srgrimes			vp = *vpp;	/* the new variable */
7681556Srgrimes			lvp->text = NULL;
7691556Srgrimes			lvp->flags = VUNSET;
7701556Srgrimes		} else {
7711556Srgrimes			lvp->text = vp->text;
7721556Srgrimes			lvp->flags = vp->flags;
7731556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
774221668Sjilles			if (name[vp->name_len] == '=')
775223024Sjilles				setvareq(savestr(name), VNOLOCAL);
7761556Srgrimes		}
7771556Srgrimes	}
7781556Srgrimes	lvp->vp = vp;
7791556Srgrimes	lvp->next = localvars;
7801556Srgrimes	localvars = lvp;
7811556Srgrimes	INTON;
7821556Srgrimes}
7831556Srgrimes
7841556Srgrimes
7851556Srgrimes/*
7861556Srgrimes * Called after a function returns.
7871556Srgrimes */
7881556Srgrimes
7891556Srgrimesvoid
79090111Simppoplocalvars(void)
79190111Simp{
7921556Srgrimes	struct localvar *lvp;
7931556Srgrimes	struct var *vp;
794293635Sjilles	int islocalevar;
7951556Srgrimes
796263777Sjilles	INTOFF;
7971556Srgrimes	while ((lvp = localvars) != NULL) {
7981556Srgrimes		localvars = lvp->next;
7991556Srgrimes		vp = lvp->vp;
8001556Srgrimes		if (vp == NULL) {	/* $- saved */
801293392Sjilles			memcpy(optval, lvp->text, sizeof optval);
8021556Srgrimes			ckfree(lvp->text);
803215266Sjilles			optschanged();
8041556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
805294593Sjilles			vp->flags &= ~VREADONLY;
8061556Srgrimes			(void)unsetvar(vp->text);
8071556Srgrimes		} else {
808293635Sjilles			islocalevar = (vp->flags | lvp->flags) & VEXPORT &&
809293635Sjilles			    localevar(lvp->text);
8101556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
8111556Srgrimes				ckfree(vp->text);
8121556Srgrimes			vp->flags = lvp->flags;
8131556Srgrimes			vp->text = lvp->text;
814293635Sjilles			if (vp->func)
815293635Sjilles				(*vp->func)(vp->text + vp->name_len + 1);
816293635Sjilles			if (islocalevar) {
817293635Sjilles				change_env(vp->text, vp->flags & VEXPORT &&
818293635Sjilles				    (vp->flags & VUNSET) == 0);
819293635Sjilles				setlocale(LC_ALL, "");
820293635Sjilles				updatecharset();
821293635Sjilles			}
8221556Srgrimes		}
8231556Srgrimes		ckfree(lvp);
8241556Srgrimes	}
825263777Sjilles	INTON;
8261556Srgrimes}
8271556Srgrimes
8281556Srgrimes
82917987Speterint
83090111Simpsetvarcmd(int argc, char **argv)
83117987Speter{
8321556Srgrimes	if (argc <= 2)
8331556Srgrimes		return unsetcmd(argc, argv);
8341556Srgrimes	else if (argc == 3)
8351556Srgrimes		setvar(argv[1], argv[2], 0);
8361556Srgrimes	else
837214538Sjilles		error("too many arguments");
8381556Srgrimes	return 0;
8391556Srgrimes}
8401556Srgrimes
8411556Srgrimes
8421556Srgrimes/*
843217847Sjilles * The unset builtin command.
8441556Srgrimes */
8451556Srgrimes
84617987Speterint
84790111Simpunsetcmd(int argc __unused, char **argv __unused)
84817987Speter{
8491556Srgrimes	char **ap;
8501556Srgrimes	int i;
8511556Srgrimes	int flg_func = 0;
8521556Srgrimes	int flg_var = 0;
8531556Srgrimes	int ret = 0;
8541556Srgrimes
8551556Srgrimes	while ((i = nextopt("vf")) != '\0') {
8561556Srgrimes		if (i == 'f')
8571556Srgrimes			flg_func = 1;
8581556Srgrimes		else
8591556Srgrimes			flg_var = 1;
8601556Srgrimes	}
8611556Srgrimes	if (flg_func == 0 && flg_var == 0)
8621556Srgrimes		flg_var = 1;
8638855Srgrimes
864263777Sjilles	INTOFF;
8651556Srgrimes	for (ap = argptr; *ap ; ap++) {
8661556Srgrimes		if (flg_func)
8671556Srgrimes			ret |= unsetfunc(*ap);
8681556Srgrimes		if (flg_var)
8691556Srgrimes			ret |= unsetvar(*ap);
8701556Srgrimes	}
871263777Sjilles	INTON;
8721556Srgrimes	return ret;
8731556Srgrimes}
8741556Srgrimes
8751556Srgrimes
8761556Srgrimes/*
8771556Srgrimes * Unset the specified variable.
878263777Sjilles * Called with interrupts off.
8791556Srgrimes */
8801556Srgrimes
88120425Ssteveint
882200956Sjillesunsetvar(const char *s)
88390111Simp{
8841556Srgrimes	struct var **vpp;
8851556Srgrimes	struct var *vp;
8861556Srgrimes
887221668Sjilles	vp = find_var(s, &vpp, NULL);
888221668Sjilles	if (vp == NULL)
889221668Sjilles		return (0);
890221668Sjilles	if (vp->flags & VREADONLY)
891221668Sjilles		return (1);
892221668Sjilles	if (vp->text[vp->name_len + 1] != '\0')
893278820Sjilles		setvar(s, "", 0);
894221668Sjilles	if ((vp->flags & VEXPORT) && localevar(vp->text)) {
895221668Sjilles		change_env(s, 0);
896221668Sjilles		setlocale(LC_ALL, "");
897221668Sjilles		updatecharset();
8981556Srgrimes	}
899221668Sjilles	vp->flags &= ~VEXPORT;
900221668Sjilles	vp->flags |= VUNSET;
901221668Sjilles	if ((vp->flags & VSTRFIXED) == 0) {
902221668Sjilles		if ((vp->flags & VTEXTFIXED) == 0)
903221668Sjilles			ckfree(vp->text);
904221668Sjilles		*vpp = vp->next;
905221668Sjilles		ckfree(vp);
906221668Sjilles	}
907135856Sdes	return (0);
9081556Srgrimes}
9091556Srgrimes
9101556Srgrimes
9111556Srgrimes
9121556Srgrimes/*
913250422Seadler * Returns true if the two strings specify the same variable.  The first
9141556Srgrimes * variable name is terminated by '='; the second may be terminated by
9151556Srgrimes * either '=' or '\0'.
9161556Srgrimes */
9171556Srgrimes
918213811Sobrienstatic int
919200956Sjillesvarequal(const char *p, const char *q)
92090111Simp{
9211556Srgrimes	while (*p == *q++) {
9221556Srgrimes		if (*p++ == '=')
9231556Srgrimes			return 1;
9241556Srgrimes	}
9251556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
9261556Srgrimes		return 1;
9271556Srgrimes	return 0;
9281556Srgrimes}
929221668Sjilles
930221668Sjilles/*
931221668Sjilles * Search for a variable.
932221668Sjilles * 'name' may be terminated by '=' or a NUL.
933221668Sjilles * vppp is set to the pointer to vp, or the list head if vp isn't found
934250422Seadler * lenp is set to the number of characters in 'name'
935221668Sjilles */
936221668Sjilles
937221668Sjillesstatic struct var *
938221668Sjillesfind_var(const char *name, struct var ***vppp, int *lenp)
939221668Sjilles{
940221668Sjilles	unsigned int hashval;
941221668Sjilles	int len;
942221668Sjilles	struct var *vp, **vpp;
943221668Sjilles	const char *p = name;
944221668Sjilles
945221668Sjilles	hashval = 0;
946221668Sjilles	while (*p && *p != '=')
947221668Sjilles		hashval = 2 * hashval + (unsigned char)*p++;
948221668Sjilles	len = p - name;
949221668Sjilles
950221668Sjilles	if (lenp)
951221668Sjilles		*lenp = len;
952221668Sjilles	vpp = &vartab[hashval % VTABSIZE];
953221668Sjilles	if (vppp)
954221668Sjilles		*vppp = vpp;
955221668Sjilles
956221668Sjilles	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
957221668Sjilles		if (vp->name_len != len)
958221668Sjilles			continue;
959221668Sjilles		if (memcmp(vp->text, name, len) != 0)
960221668Sjilles			continue;
961221668Sjilles		if (vppp)
962221668Sjilles			*vppp = vpp;
963221668Sjilles		return vp;
964221668Sjilles	}
965221668Sjilles	return NULL;
966221668Sjilles}
967