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$");
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;
9197689Stjrstruct var vppid;
921556Srgrimesstruct var vps1;
931556Srgrimesstruct var vps2;
94159632Sstefanfstruct var vps4;
951556Srgrimesstruct var vvers;
96213760Sobrienstatic struct var voptind;
97249242Sjillesstruct var vdisvfork;
981556Srgrimes
99223024Sjillesint forcelocal;
100223024Sjilles
101213760Sobrienstatic const struct varinit varinit[] = {
10217987Speter#ifndef NO_HISTORY
103201056Sjilles	{ &vhistsize,	VUNSET,				"HISTSIZE=",
10420425Ssteve	  sethistsize },
10517987Speter#endif
106201056Sjilles	{ &vifs,	0,				"IFS= \t\n",
10720425Ssteve	  NULL },
108201056Sjilles	{ &vmail,	VUNSET,				"MAIL=",
10920425Ssteve	  NULL },
110201056Sjilles	{ &vmpath,	VUNSET,				"MAILPATH=",
11120425Ssteve	  NULL },
112201056Sjilles	{ &vpath,	0,				"PATH=" _PATH_DEFPATH,
11320425Ssteve	  changepath },
114201056Sjilles	{ &vppid,	VUNSET,				"PPID=",
11597689Stjr	  NULL },
1168855Srgrimes	/*
1171556Srgrimes	 * vps1 depends on uid
1181556Srgrimes	 */
119201056Sjilles	{ &vps2,	0,				"PS2=> ",
12020425Ssteve	  NULL },
121201056Sjilles	{ &vps4,	0,				"PS4=+ ",
122159632Sstefanf	  NULL },
123208755Sjilles#ifndef NO_HISTORY
124208755Sjilles	{ &vterm,	VUNSET,				"TERM=",
125208755Sjilles	  setterm },
126208755Sjilles#endif
127201056Sjilles	{ &voptind,	0,				"OPTIND=1",
12820425Ssteve	  getoptsreset },
129249242Sjilles	{ &vdisvfork,	VUNSET,				"SH_DISABLE_VFORK=",
130249242Sjilles	  NULL },
13120425Ssteve	{ NULL,	0,				NULL,
13220425Ssteve	  NULL }
1331556Srgrimes};
1341556Srgrimes
135213760Sobrienstatic struct var *vartab[VTABSIZE];
1361556Srgrimes
137213760Sobrienstatic const char *const locale_names[7] = {
138207678Sjilles	"LC_COLLATE", "LC_CTYPE", "LC_MONETARY",
139207678Sjilles	"LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL
140207678Sjilles};
141213760Sobrienstatic const int locale_categories[7] = {
142207678Sjilles	LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0
143207678Sjilles};
144207678Sjilles
145213811Sobrienstatic int varequal(const char *, const char *);
146221668Sjillesstatic struct var *find_var(const char *, struct var ***, int *);
147213811Sobrienstatic int localevar(const char *);
1481556Srgrimes
1491556Srgrimes/*
150155302Sschweikh * Initialize the variable symbol tables and import the environment.
1511556Srgrimes */
1521556Srgrimes
1531556Srgrimes#ifdef mkinit
1541556SrgrimesINCLUDE "var.h"
155201053SjillesMKINIT char **environ;
1561556SrgrimesINIT {
1571556Srgrimes	char **envp;
1581556Srgrimes
1591556Srgrimes	initvar();
1601556Srgrimes	for (envp = environ ; *envp ; envp++) {
1611556Srgrimes		if (strchr(*envp, '=')) {
1621556Srgrimes			setvareq(*envp, VEXPORT|VTEXTFIXED);
1631556Srgrimes		}
1641556Srgrimes	}
1651556Srgrimes}
1661556Srgrimes#endif
1671556Srgrimes
1681556Srgrimes
1691556Srgrimes/*
1701556Srgrimes * This routine initializes the builtin variables.  It is called when the
171218306Sjilles * shell is initialized.
1721556Srgrimes */
1731556Srgrimes
1741556Srgrimesvoid
17590111Simpinitvar(void)
17690111Simp{
17797689Stjr	char ppid[20];
1781556Srgrimes	const struct varinit *ip;
1791556Srgrimes	struct var *vp;
1801556Srgrimes	struct var **vpp;
1811556Srgrimes
1821556Srgrimes	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
183221668Sjilles		if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
184221668Sjilles			continue;
185221668Sjilles		vp->next = *vpp;
186221668Sjilles		*vpp = vp;
187221668Sjilles		vp->text = __DECONST(char *, ip->text);
188221668Sjilles		vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED;
189221668Sjilles		vp->func = ip->func;
1901556Srgrimes	}
1911556Srgrimes	/*
1921556Srgrimes	 * PS1 depends on uid
1931556Srgrimes	 */
194221668Sjilles	if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
1951556Srgrimes		vps1.next = *vpp;
1961556Srgrimes		*vpp = &vps1;
197201056Sjilles		vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# ");
1981556Srgrimes		vps1.flags = VSTRFIXED|VTEXTFIXED;
1991556Srgrimes	}
20097689Stjr	if ((vppid.flags & VEXPORT) == 0) {
20197689Stjr		fmtstr(ppid, sizeof(ppid), "%d", (int)getppid());
20297689Stjr		setvarsafe("PPID", ppid, 0);
20397689Stjr	}
2041556Srgrimes}
2051556Srgrimes
2061556Srgrimes/*
20720425Ssteve * Safe version of setvar, returns 1 on success 0 on failure.
20820425Ssteve */
20920425Ssteve
21020425Ssteveint
211200956Sjillessetvarsafe(const char *name, const char *val, int flags)
21220425Ssteve{
21320425Ssteve	struct jmploc jmploc;
214194765Sjilles	struct jmploc *const savehandler = handler;
21520425Ssteve	int err = 0;
216199660Sjilles	int inton;
21720425Ssteve
218199660Sjilles	inton = is_int_on();
21920425Ssteve	if (setjmp(jmploc.loc))
22020425Ssteve		err = 1;
22120425Ssteve	else {
22220425Ssteve		handler = &jmploc;
22320425Ssteve		setvar(name, val, flags);
22420425Ssteve	}
22520425Ssteve	handler = savehandler;
226199660Sjilles	SETINTON(inton);
22720425Ssteve	return err;
22820425Ssteve}
22920425Ssteve
23020425Ssteve/*
231155302Sschweikh * Set the value of a variable.  The flags argument is stored with the
2321556Srgrimes * flags of the variable.  If val is NULL, the variable is unset.
2331556Srgrimes */
2341556Srgrimes
2351556Srgrimesvoid
236200956Sjillessetvar(const char *name, const char *val, int flags)
23717987Speter{
238200956Sjilles	const char *p;
2391556Srgrimes	int len;
2401556Srgrimes	int namelen;
2411556Srgrimes	char *nameeq;
2421556Srgrimes	int isbad;
2431556Srgrimes
2441556Srgrimes	isbad = 0;
2451556Srgrimes	p = name;
24617525Sache	if (! is_name(*p))
2471556Srgrimes		isbad = 1;
24817525Sache	p++;
2491556Srgrimes	for (;;) {
2501556Srgrimes		if (! is_in_name(*p)) {
2511556Srgrimes			if (*p == '\0' || *p == '=')
2521556Srgrimes				break;
2531556Srgrimes			isbad = 1;
2541556Srgrimes		}
2551556Srgrimes		p++;
2561556Srgrimes	}
2571556Srgrimes	namelen = p - name;
2581556Srgrimes	if (isbad)
2591556Srgrimes		error("%.*s: bad variable name", namelen, name);
2601556Srgrimes	len = namelen + 2;		/* 2 is space for '=' and '\0' */
2611556Srgrimes	if (val == NULL) {
2621556Srgrimes		flags |= VUNSET;
2631556Srgrimes	} else {
2641556Srgrimes		len += strlen(val);
2651556Srgrimes	}
266264629Sjilles	INTOFF;
267200956Sjilles	nameeq = ckmalloc(len);
268200956Sjilles	memcpy(nameeq, name, namelen);
269200956Sjilles	nameeq[namelen] = '=';
2701556Srgrimes	if (val)
271200956Sjilles		scopy(val, nameeq + namelen + 1);
272200956Sjilles	else
273200956Sjilles		nameeq[namelen + 1] = '\0';
2741556Srgrimes	setvareq(nameeq, flags);
275264629Sjilles	INTON;
2761556Srgrimes}
2771556Srgrimes
278213811Sobrienstatic int
279200956Sjilleslocalevar(const char *s)
28090111Simp{
281207678Sjilles	const char *const *ss;
2821556Srgrimes
28317525Sache	if (*s != 'L')
28417525Sache		return 0;
28517525Sache	if (varequal(s + 1, "ANG"))
28617525Sache		return 1;
28717525Sache	if (strncmp(s + 1, "C_", 2) != 0)
28817525Sache		return 0;
289207678Sjilles	if (varequal(s + 3, "ALL"))
290207678Sjilles		return 1;
291207678Sjilles	for (ss = locale_names; *ss ; ss++)
292207678Sjilles		if (varequal(s + 3, *ss + 3))
29317525Sache			return 1;
29417525Sache	return 0;
29517525Sache}
2961556Srgrimes
297171268Sscf
2981556Srgrimes/*
299171268Sscf * Sets/unsets an environment variable from a pointer that may actually be a
300171268Sscf * pointer into environ where the string should not be manipulated.
301171268Sscf */
302213811Sobrienstatic void
303200956Sjilleschange_env(const char *s, int set)
304171268Sscf{
305171268Sscf	char *eqp;
306171268Sscf	char *ss;
307171268Sscf
308264629Sjilles	INTOFF;
309171268Sscf	ss = savestr(s);
310171268Sscf	if ((eqp = strchr(ss, '=')) != NULL)
311171268Sscf		*eqp = '\0';
312171268Sscf	if (set && eqp != NULL)
313171268Sscf		(void) setenv(ss, eqp + 1, 1);
314171268Sscf	else
315171268Sscf		(void) unsetenv(ss);
316171268Sscf	ckfree(ss);
317264629Sjilles	INTON;
318171268Sscf
319171268Sscf	return;
320171268Sscf}
321171268Sscf
322171268Sscf
323171268Sscf/*
3241556Srgrimes * Same as setvar except that the variable and value are passed in
3251556Srgrimes * the first argument as name=value.  Since the first argument will
3261556Srgrimes * be actually stored in the table, it should not be a string that
3271556Srgrimes * will go away.
3281556Srgrimes */
3291556Srgrimes
3301556Srgrimesvoid
33190111Simpsetvareq(char *s, int flags)
33217987Speter{
3331556Srgrimes	struct var *vp, **vpp;
334221668Sjilles	int nlen;
3351556Srgrimes
33645263Scracauer	if (aflag)
33745263Scracauer		flags |= VEXPORT;
338223024Sjilles	if (forcelocal && !(flags & (VNOSET | VNOLOCAL)))
339223024Sjilles		mklocal(s);
340221668Sjilles	vp = find_var(s, &vpp, &nlen);
341221668Sjilles	if (vp != NULL) {
342264643Sjilles		if (vp->flags & VREADONLY) {
343264643Sjilles			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
344264643Sjilles				ckfree(s);
345221668Sjilles			error("%.*s: is read only", vp->name_len, s);
346264643Sjilles		}
347264644Sjilles		if (flags & VNOSET) {
348264644Sjilles			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
349264644Sjilles				ckfree(s);
350221668Sjilles			return;
351264644Sjilles		}
352221668Sjilles		INTOFF;
35320425Ssteve
354221668Sjilles		if (vp->func && (flags & VNOFUNC) == 0)
355221668Sjilles			(*vp->func)(s + vp->name_len + 1);
35620425Ssteve
357221668Sjilles		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
358221668Sjilles			ckfree(vp->text);
35920425Ssteve
360221668Sjilles		vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET);
361221668Sjilles		vp->flags |= flags;
362221668Sjilles		vp->text = s;
36320425Ssteve
364221668Sjilles		/*
365221668Sjilles		 * We could roll this to a function, to handle it as
366221668Sjilles		 * a regular variable function callback, but why bother?
367221668Sjilles		 *
368221668Sjilles		 * Note: this assumes iflag is not set to 1 initially.
369221668Sjilles		 * As part of init(), this is called before arguments
370221668Sjilles		 * are looked at.
371221668Sjilles		 */
372221668Sjilles		if ((vp == &vmpath || (vp == &vmail && ! mpathset())) &&
373221668Sjilles		    iflag == 1)
374221668Sjilles			chkmail(1);
375221668Sjilles		if ((vp->flags & VEXPORT) && localevar(s)) {
376221668Sjilles			change_env(s, 1);
377221668Sjilles			(void) setlocale(LC_ALL, "");
378221668Sjilles			updatecharset();
3791556Srgrimes		}
380221668Sjilles		INTON;
381221668Sjilles		return;
3821556Srgrimes	}
3831556Srgrimes	/* not found */
384264644Sjilles	if (flags & VNOSET) {
385264644Sjilles		if ((flags & (VTEXTFIXED|VSTACK)) == 0)
386264644Sjilles			ckfree(s);
387216870Sjilles		return;
388264644Sjilles	}
389264629Sjilles	INTOFF;
3901556Srgrimes	vp = ckmalloc(sizeof (*vp));
3911556Srgrimes	vp->flags = flags;
3921556Srgrimes	vp->text = s;
393221668Sjilles	vp->name_len = nlen;
3941556Srgrimes	vp->next = *vpp;
39520425Ssteve	vp->func = NULL;
3961556Srgrimes	*vpp = vp;
39717525Sache	if ((vp->flags & VEXPORT) && localevar(s)) {
398171268Sscf		change_env(s, 1);
39917525Sache		(void) setlocale(LC_ALL, "");
400221559Sjilles		updatecharset();
40117525Sache	}
40217525Sache	INTON;
4031556Srgrimes}
4041556Srgrimes
4051556Srgrimes
4061556Srgrimes
4071556Srgrimes/*
4081556Srgrimes * Process a linked list of variable assignments.
4091556Srgrimes */
4101556Srgrimes
4111556Srgrimesvoid
412216870Sjilleslistsetvar(struct strlist *list, int flags)
41390111Simp{
4141556Srgrimes	struct strlist *lp;
4151556Srgrimes
4161556Srgrimes	INTOFF;
4171556Srgrimes	for (lp = list ; lp ; lp = lp->next) {
418216870Sjilles		setvareq(savestr(lp->text), flags);
4191556Srgrimes	}
4201556Srgrimes	INTON;
4211556Srgrimes}
4221556Srgrimes
4231556Srgrimes
4241556Srgrimes
4251556Srgrimes/*
4261556Srgrimes * Find the value of a variable.  Returns NULL if not set.
4271556Srgrimes */
4281556Srgrimes
4291556Srgrimeschar *
430200956Sjilleslookupvar(const char *name)
43190111Simp{
4321556Srgrimes	struct var *v;
4331556Srgrimes
434221668Sjilles	v = find_var(name, NULL, NULL);
435221668Sjilles	if (v == NULL || v->flags & VUNSET)
436221668Sjilles		return NULL;
437221668Sjilles	return v->text + v->name_len + 1;
4381556Srgrimes}
4391556Srgrimes
4401556Srgrimes
4411556Srgrimes
4421556Srgrimes/*
4431556Srgrimes * Search the environment of a builtin command.  If the second argument
4441556Srgrimes * is nonzero, return the value of a variable even if it hasn't been
4451556Srgrimes * exported.
4461556Srgrimes */
4471556Srgrimes
4481556Srgrimeschar *
449200956Sjillesbltinlookup(const char *name, int doall)
45017987Speter{
4511556Srgrimes	struct strlist *sp;
4521556Srgrimes	struct var *v;
453212467Sjilles	char *result;
4541556Srgrimes
455212467Sjilles	result = NULL;
4561556Srgrimes	for (sp = cmdenviron ; sp ; sp = sp->next) {
4571556Srgrimes		if (varequal(sp->text, name))
458212467Sjilles			result = strchr(sp->text, '=') + 1;
4591556Srgrimes	}
460212467Sjilles	if (result != NULL)
461212467Sjilles		return result;
462221668Sjilles
463221668Sjilles	v = find_var(name, NULL, NULL);
464221668Sjilles	if (v == NULL || v->flags & VUNSET ||
465221668Sjilles	    (!doall && (v->flags & VEXPORT) == 0))
466221668Sjilles		return NULL;
467221668Sjilles	return v->text + v->name_len + 1;
4681556Srgrimes}
4691556Srgrimes
4701556Srgrimes
471207678Sjilles/*
472207678Sjilles * Set up locale for a builtin (LANG/LC_* assignments).
473207678Sjilles */
474207678Sjillesvoid
475207678Sjillesbltinsetlocale(void)
476207678Sjilles{
477207678Sjilles	struct strlist *lp;
478207678Sjilles	int act = 0;
479207678Sjilles	char *loc, *locdef;
480207678Sjilles	int i;
4811556Srgrimes
482207678Sjilles	for (lp = cmdenviron ; lp ; lp = lp->next) {
483207678Sjilles		if (localevar(lp->text)) {
484207678Sjilles			act = 1;
485207678Sjilles			break;
486207678Sjilles		}
487207678Sjilles	}
488207678Sjilles	if (!act)
489207678Sjilles		return;
490207678Sjilles	loc = bltinlookup("LC_ALL", 0);
491207678Sjilles	INTOFF;
492207678Sjilles	if (loc != NULL) {
493207678Sjilles		setlocale(LC_ALL, loc);
494207678Sjilles		INTON;
495221559Sjilles		updatecharset();
496207678Sjilles		return;
497207678Sjilles	}
498207678Sjilles	locdef = bltinlookup("LANG", 0);
499207678Sjilles	for (i = 0; locale_names[i] != NULL; i++) {
500207678Sjilles		loc = bltinlookup(locale_names[i], 0);
501207678Sjilles		if (loc == NULL)
502207678Sjilles			loc = locdef;
503207678Sjilles		if (loc != NULL)
504207678Sjilles			setlocale(locale_categories[i], loc);
505207678Sjilles	}
506207678Sjilles	INTON;
507221559Sjilles	updatecharset();
508207678Sjilles}
509207678Sjilles
5101556Srgrimes/*
511207678Sjilles * Undo the effect of bltinlocaleset().
512207678Sjilles */
513207678Sjillesvoid
514207678Sjillesbltinunsetlocale(void)
515207678Sjilles{
516207678Sjilles	struct strlist *lp;
517207678Sjilles
518207678Sjilles	INTOFF;
519207678Sjilles	for (lp = cmdenviron ; lp ; lp = lp->next) {
520207678Sjilles		if (localevar(lp->text)) {
521207678Sjilles			setlocale(LC_ALL, "");
522221559Sjilles			updatecharset();
523207678Sjilles			return;
524207678Sjilles		}
525207678Sjilles	}
526207678Sjilles	INTON;
527207678Sjilles}
528207678Sjilles
529221559Sjilles/*
530221559Sjilles * Update the localeisutf8 flag.
531221559Sjilles */
532221559Sjillesvoid
533221559Sjillesupdatecharset(void)
534221559Sjilles{
535221559Sjilles	char *charset;
536207678Sjilles
537221559Sjilles	charset = nl_langinfo(CODESET);
538221559Sjilles	localeisutf8 = !strcmp(charset, "UTF-8");
539221559Sjilles}
540221559Sjilles
541221669Sjillesvoid
542221669Sjillesinitcharset(void)
543221669Sjilles{
544221669Sjilles	updatecharset();
545221669Sjilles	initial_localeisutf8 = localeisutf8;
546221669Sjilles}
547221669Sjilles
548207678Sjilles/*
5491556Srgrimes * Generate a list of exported variables.  This routine is used to construct
5501556Srgrimes * the third argument to execve when executing a program.
5511556Srgrimes */
5521556Srgrimes
5531556Srgrimeschar **
55490111Simpenvironment(void)
55590111Simp{
5561556Srgrimes	int nenv;
5571556Srgrimes	struct var **vpp;
5581556Srgrimes	struct var *vp;
5591556Srgrimes	char **env, **ep;
5601556Srgrimes
5611556Srgrimes	nenv = 0;
5621556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5631556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
5641556Srgrimes			if (vp->flags & VEXPORT)
5651556Srgrimes				nenv++;
5661556Srgrimes	}
5671556Srgrimes	ep = env = stalloc((nenv + 1) * sizeof *env);
5681556Srgrimes	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
5691556Srgrimes		for (vp = *vpp ; vp ; vp = vp->next)
5701556Srgrimes			if (vp->flags & VEXPORT)
5711556Srgrimes				*ep++ = vp->text;
5721556Srgrimes	}
5731556Srgrimes	*ep = NULL;
5741556Srgrimes	return env;
5751556Srgrimes}
5761556Srgrimes
5771556Srgrimes
578213811Sobrienstatic int
579158145Sstefanfvar_compare(const void *a, const void *b)
580158145Sstefanf{
581158145Sstefanf	const char *const *sa, *const *sb;
5821556Srgrimes
583158145Sstefanf	sa = a;
584158145Sstefanf	sb = b;
585158145Sstefanf	/*
586158145Sstefanf	 * This compares two var=value strings which creates a different
587158145Sstefanf	 * order from what you would probably expect.  POSIX is somewhat
588158145Sstefanf	 * ambiguous on what should be sorted exactly.
589158145Sstefanf	 */
590158145Sstefanf	return strcoll(*sa, *sb);
591158145Sstefanf}
592158145Sstefanf
593158145Sstefanf
5941556Srgrimes/*
595217847Sjilles * Command to list all variables which are set.  This is invoked from the
596217847Sjilles * set command when it is called without any options or operands.
5971556Srgrimes */
5981556Srgrimes
5991556Srgrimesint
60090111Simpshowvarscmd(int argc __unused, char **argv __unused)
60117987Speter{
6021556Srgrimes	struct var **vpp;
6031556Srgrimes	struct var *vp;
60497915Stjr	const char *s;
605158145Sstefanf	const char **vars;
606158145Sstefanf	int i, n;
6071556Srgrimes
608158145Sstefanf	/*
609158145Sstefanf	 * POSIX requires us to sort the variables.
610158145Sstefanf	 */
611158145Sstefanf	n = 0;
612158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
613158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
614158145Sstefanf			if (!(vp->flags & VUNSET))
615158145Sstefanf				n++;
6161556Srgrimes		}
6171556Srgrimes	}
618158145Sstefanf
619231529Sjilles	INTOFF;
620158145Sstefanf	vars = ckmalloc(n * sizeof(*vars));
621158145Sstefanf	i = 0;
622158145Sstefanf	for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) {
623158145Sstefanf		for (vp = *vpp; vp; vp = vp->next) {
624158145Sstefanf			if (!(vp->flags & VUNSET))
625158145Sstefanf				vars[i++] = vp->text;
626158145Sstefanf		}
627158145Sstefanf	}
628158145Sstefanf
629158145Sstefanf	qsort(vars, n, sizeof(*vars), var_compare);
630158145Sstefanf	for (i = 0; i < n; i++) {
631223183Sjilles		/*
632223183Sjilles		 * Skip improper variable names so the output remains usable as
633223183Sjilles		 * shell input.
634223183Sjilles		 */
635223183Sjilles		if (!isassignment(vars[i]))
636223183Sjilles			continue;
637215567Sjilles		s = strchr(vars[i], '=');
638215567Sjilles		s++;
639215567Sjilles		outbin(vars[i], s - vars[i], out1);
640215567Sjilles		out1qstr(s);
641158145Sstefanf		out1c('\n');
642158145Sstefanf	}
643158145Sstefanf	ckfree(vars);
644231529Sjilles	INTON;
645158145Sstefanf
6461556Srgrimes	return 0;
6471556Srgrimes}
6481556Srgrimes
6491556Srgrimes
6501556Srgrimes
6511556Srgrimes/*
6521556Srgrimes * The export and readonly commands.
6531556Srgrimes */
6541556Srgrimes
6551556Srgrimesint
65690111Simpexportcmd(int argc, char **argv)
65717987Speter{
6581556Srgrimes	struct var **vpp;
6591556Srgrimes	struct var *vp;
6601556Srgrimes	char *name;
6611556Srgrimes	char *p;
66297914Stjr	char *cmdname;
66397914Stjr	int ch, values;
6641556Srgrimes	int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT;
6651556Srgrimes
66697914Stjr	cmdname = argv[0];
66797914Stjr	optreset = optind = 1;
668100663Stjr	opterr = 0;
66997914Stjr	values = 0;
67097914Stjr	while ((ch = getopt(argc, argv, "p")) != -1) {
67197914Stjr		switch (ch) {
67297914Stjr		case 'p':
67397914Stjr			values = 1;
67497914Stjr			break;
67597914Stjr		case '?':
67697914Stjr		default:
67797914Stjr			error("unknown option: -%c", optopt);
67897914Stjr		}
67997914Stjr	}
68097914Stjr	argc -= optind;
68197914Stjr	argv += optind;
68297914Stjr
683149919Sstefanf	if (values && argc != 0)
684149919Sstefanf		error("-p requires no arguments");
68597914Stjr	if (argc != 0) {
686149919Sstefanf		while ((name = *argv++) != NULL) {
6871556Srgrimes			if ((p = strchr(name, '=')) != NULL) {
6881556Srgrimes				p++;
6891556Srgrimes			} else {
690221668Sjilles				vp = find_var(name, NULL, NULL);
691221668Sjilles				if (vp != NULL) {
692221668Sjilles					vp->flags |= flag;
693221668Sjilles					if ((vp->flags & VEXPORT) && localevar(vp->text)) {
694221668Sjilles						change_env(vp->text, 1);
695221668Sjilles						(void) setlocale(LC_ALL, "");
696221668Sjilles						updatecharset();
6971556Srgrimes					}
698221668Sjilles					continue;
6991556Srgrimes				}
7001556Srgrimes			}
7011556Srgrimes			setvar(name, p, flag);
7021556Srgrimes		}
7031556Srgrimes	} else {
7041556Srgrimes		for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
7051556Srgrimes			for (vp = *vpp ; vp ; vp = vp->next) {
7061556Srgrimes				if (vp->flags & flag) {
70797914Stjr					if (values) {
708223183Sjilles						/*
709223183Sjilles						 * Skip improper variable names
710223183Sjilles						 * so the output remains usable
711223183Sjilles						 * as shell input.
712223183Sjilles						 */
713223183Sjilles						if (!isassignment(vp->text))
714223183Sjilles							continue;
71597914Stjr						out1str(cmdname);
71697914Stjr						out1c(' ');
71797914Stjr					}
71897914Stjr					if (values && !(vp->flags & VUNSET)) {
719221975Sjilles						outbin(vp->text,
720221975Sjilles						    vp->name_len + 1, out1);
721221975Sjilles						out1qstr(vp->text +
722221975Sjilles						    vp->name_len + 1);
723215567Sjilles					} else
724221975Sjilles						outbin(vp->text, vp->name_len,
725215567Sjilles						    out1);
7261556Srgrimes					out1c('\n');
7271556Srgrimes				}
7281556Srgrimes			}
7291556Srgrimes		}
7301556Srgrimes	}
7311556Srgrimes	return 0;
7321556Srgrimes}
7331556Srgrimes
7341556Srgrimes
7351556Srgrimes/*
7361556Srgrimes * The "local" command.
7371556Srgrimes */
7381556Srgrimes
73917987Speterint
74090111Simplocalcmd(int argc __unused, char **argv __unused)
74117987Speter{
7421556Srgrimes	char *name;
7431556Srgrimes
7441556Srgrimes	if (! in_function())
7451556Srgrimes		error("Not in a function");
7461556Srgrimes	while ((name = *argptr++) != NULL) {
7471556Srgrimes		mklocal(name);
7481556Srgrimes	}
7491556Srgrimes	return 0;
7501556Srgrimes}
7511556Srgrimes
7521556Srgrimes
7531556Srgrimes/*
7541556Srgrimes * Make a variable a local variable.  When a variable is made local, it's
7551556Srgrimes * value and flags are saved in a localvar structure.  The saved values
7561556Srgrimes * will be restored when the shell function returns.  We handle the name
7571556Srgrimes * "-" as a special case.
7581556Srgrimes */
7591556Srgrimes
7601556Srgrimesvoid
76190111Simpmklocal(char *name)
76290111Simp{
7631556Srgrimes	struct localvar *lvp;
7641556Srgrimes	struct var **vpp;
7651556Srgrimes	struct var *vp;
7661556Srgrimes
7671556Srgrimes	INTOFF;
7681556Srgrimes	lvp = ckmalloc(sizeof (struct localvar));
7691556Srgrimes	if (name[0] == '-' && name[1] == '\0') {
7701556Srgrimes		lvp->text = ckmalloc(sizeof optlist);
77117987Speter		memcpy(lvp->text, optlist, sizeof optlist);
7721556Srgrimes		vp = NULL;
7731556Srgrimes	} else {
774221668Sjilles		vp = find_var(name, &vpp, NULL);
7751556Srgrimes		if (vp == NULL) {
7761556Srgrimes			if (strchr(name, '='))
777223024Sjilles				setvareq(savestr(name), VSTRFIXED | VNOLOCAL);
7781556Srgrimes			else
779223024Sjilles				setvar(name, NULL, VSTRFIXED | VNOLOCAL);
7801556Srgrimes			vp = *vpp;	/* the new variable */
7811556Srgrimes			lvp->text = NULL;
7821556Srgrimes			lvp->flags = VUNSET;
7831556Srgrimes		} else {
7841556Srgrimes			lvp->text = vp->text;
7851556Srgrimes			lvp->flags = vp->flags;
7861556Srgrimes			vp->flags |= VSTRFIXED|VTEXTFIXED;
787221668Sjilles			if (name[vp->name_len] == '=')
788223024Sjilles				setvareq(savestr(name), VNOLOCAL);
7891556Srgrimes		}
7901556Srgrimes	}
7911556Srgrimes	lvp->vp = vp;
7921556Srgrimes	lvp->next = localvars;
7931556Srgrimes	localvars = lvp;
7941556Srgrimes	INTON;
7951556Srgrimes}
7961556Srgrimes
7971556Srgrimes
7981556Srgrimes/*
7991556Srgrimes * Called after a function returns.
8001556Srgrimes */
8011556Srgrimes
8021556Srgrimesvoid
80390111Simppoplocalvars(void)
80490111Simp{
8051556Srgrimes	struct localvar *lvp;
8061556Srgrimes	struct var *vp;
8071556Srgrimes
808264629Sjilles	INTOFF;
8091556Srgrimes	while ((lvp = localvars) != NULL) {
8101556Srgrimes		localvars = lvp->next;
8111556Srgrimes		vp = lvp->vp;
8121556Srgrimes		if (vp == NULL) {	/* $- saved */
81317987Speter			memcpy(optlist, lvp->text, sizeof optlist);
8141556Srgrimes			ckfree(lvp->text);
815215266Sjilles			optschanged();
8161556Srgrimes		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
8171556Srgrimes			(void)unsetvar(vp->text);
8181556Srgrimes		} else {
8191556Srgrimes			if ((vp->flags & VTEXTFIXED) == 0)
8201556Srgrimes				ckfree(vp->text);
8211556Srgrimes			vp->flags = lvp->flags;
8221556Srgrimes			vp->text = lvp->text;
8231556Srgrimes		}
8241556Srgrimes		ckfree(lvp);
8251556Srgrimes	}
826264629Sjilles	INTON;
8271556Srgrimes}
8281556Srgrimes
8291556Srgrimes
83017987Speterint
83190111Simpsetvarcmd(int argc, char **argv)
83217987Speter{
8331556Srgrimes	if (argc <= 2)
8341556Srgrimes		return unsetcmd(argc, argv);
8351556Srgrimes	else if (argc == 3)
8361556Srgrimes		setvar(argv[1], argv[2], 0);
8371556Srgrimes	else
838214538Sjilles		error("too many arguments");
8391556Srgrimes	return 0;
8401556Srgrimes}
8411556Srgrimes
8421556Srgrimes
8431556Srgrimes/*
844217847Sjilles * The unset builtin command.
8451556Srgrimes */
8461556Srgrimes
84717987Speterint
84890111Simpunsetcmd(int argc __unused, char **argv __unused)
84917987Speter{
8501556Srgrimes	char **ap;
8511556Srgrimes	int i;
8521556Srgrimes	int flg_func = 0;
8531556Srgrimes	int flg_var = 0;
8541556Srgrimes	int ret = 0;
8551556Srgrimes
8561556Srgrimes	while ((i = nextopt("vf")) != '\0') {
8571556Srgrimes		if (i == 'f')
8581556Srgrimes			flg_func = 1;
8591556Srgrimes		else
8601556Srgrimes			flg_var = 1;
8611556Srgrimes	}
8621556Srgrimes	if (flg_func == 0 && flg_var == 0)
8631556Srgrimes		flg_var = 1;
8648855Srgrimes
865264629Sjilles	INTOFF;
8661556Srgrimes	for (ap = argptr; *ap ; ap++) {
8671556Srgrimes		if (flg_func)
8681556Srgrimes			ret |= unsetfunc(*ap);
8691556Srgrimes		if (flg_var)
8701556Srgrimes			ret |= unsetvar(*ap);
8711556Srgrimes	}
872264629Sjilles	INTON;
8731556Srgrimes	return ret;
8741556Srgrimes}
8751556Srgrimes
8761556Srgrimes
8771556Srgrimes/*
8781556Srgrimes * Unset the specified variable.
879264629Sjilles * Called with interrupts off.
8801556Srgrimes */
8811556Srgrimes
88220425Ssteveint
883200956Sjillesunsetvar(const char *s)
88490111Simp{
8851556Srgrimes	struct var **vpp;
8861556Srgrimes	struct var *vp;
8871556Srgrimes
888221668Sjilles	vp = find_var(s, &vpp, NULL);
889221668Sjilles	if (vp == NULL)
890221668Sjilles		return (0);
891221668Sjilles	if (vp->flags & VREADONLY)
892221668Sjilles		return (1);
893221668Sjilles	if (vp->text[vp->name_len + 1] != '\0')
894221668Sjilles		setvar(s, nullstr, 0);
895221668Sjilles	if ((vp->flags & VEXPORT) && localevar(vp->text)) {
896221668Sjilles		change_env(s, 0);
897221668Sjilles		setlocale(LC_ALL, "");
898221668Sjilles		updatecharset();
8991556Srgrimes	}
900221668Sjilles	vp->flags &= ~VEXPORT;
901221668Sjilles	vp->flags |= VUNSET;
902221668Sjilles	if ((vp->flags & VSTRFIXED) == 0) {
903221668Sjilles		if ((vp->flags & VTEXTFIXED) == 0)
904221668Sjilles			ckfree(vp->text);
905221668Sjilles		*vpp = vp->next;
906221668Sjilles		ckfree(vp);
907221668Sjilles	}
908135856Sdes	return (0);
9091556Srgrimes}
9101556Srgrimes
9111556Srgrimes
9121556Srgrimes
9131556Srgrimes/*
9141556Srgrimes * Returns true if the two strings specify the same varable.  The first
9151556Srgrimes * variable name is terminated by '='; the second may be terminated by
9161556Srgrimes * either '=' or '\0'.
9171556Srgrimes */
9181556Srgrimes
919213811Sobrienstatic int
920200956Sjillesvarequal(const char *p, const char *q)
92190111Simp{
9221556Srgrimes	while (*p == *q++) {
9231556Srgrimes		if (*p++ == '=')
9241556Srgrimes			return 1;
9251556Srgrimes	}
9261556Srgrimes	if (*p == '=' && *(q - 1) == '\0')
9271556Srgrimes		return 1;
9281556Srgrimes	return 0;
9291556Srgrimes}
930221668Sjilles
931221668Sjilles/*
932221668Sjilles * Search for a variable.
933221668Sjilles * 'name' may be terminated by '=' or a NUL.
934221668Sjilles * vppp is set to the pointer to vp, or the list head if vp isn't found
935221668Sjilles * lenp is set to the number of charactets in 'name'
936221668Sjilles */
937221668Sjilles
938221668Sjillesstatic struct var *
939221668Sjillesfind_var(const char *name, struct var ***vppp, int *lenp)
940221668Sjilles{
941221668Sjilles	unsigned int hashval;
942221668Sjilles	int len;
943221668Sjilles	struct var *vp, **vpp;
944221668Sjilles	const char *p = name;
945221668Sjilles
946221668Sjilles	hashval = 0;
947221668Sjilles	while (*p && *p != '=')
948221668Sjilles		hashval = 2 * hashval + (unsigned char)*p++;
949221668Sjilles	len = p - name;
950221668Sjilles
951221668Sjilles	if (lenp)
952221668Sjilles		*lenp = len;
953221668Sjilles	vpp = &vartab[hashval % VTABSIZE];
954221668Sjilles	if (vppp)
955221668Sjilles		*vppp = vpp;
956221668Sjilles
957221668Sjilles	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
958221668Sjilles		if (vp->name_len != len)
959221668Sjilles			continue;
960221668Sjilles		if (memcmp(vp->text, name, len) != 0)
961221668Sjilles			continue;
962221668Sjilles		if (vppp)
963221668Sjilles			*vppp = vpp;
964221668Sjilles		return vp;
965221668Sjilles	}
966221668Sjilles	return NULL;
967221668Sjilles}
968