xref: /netbsd-src/bin/sh/var.c (revision ccb9728fb7dbc071e68b6e6e74ffdd4dd798307d)
1*ccb9728fSkre /*	$NetBSD: var.c,v 1.88 2024/12/26 03:23:28 kre Exp $	*/
249f0ad86Scgd 
361f28255Scgd /*-
437ed7877Sjtc  * Copyright (c) 1991, 1993
537ed7877Sjtc  *	The Regents of the University of California.  All rights reserved.
661f28255Scgd  *
761f28255Scgd  * This code is derived from software contributed to Berkeley by
861f28255Scgd  * Kenneth Almquist.
961f28255Scgd  *
1061f28255Scgd  * Redistribution and use in source and binary forms, with or without
1161f28255Scgd  * modification, are permitted provided that the following conditions
1261f28255Scgd  * are met:
1361f28255Scgd  * 1. Redistributions of source code must retain the above copyright
1461f28255Scgd  *    notice, this list of conditions and the following disclaimer.
1561f28255Scgd  * 2. Redistributions in binary form must reproduce the above copyright
1661f28255Scgd  *    notice, this list of conditions and the following disclaimer in the
1761f28255Scgd  *    documentation and/or other materials provided with the distribution.
18b5b29542Sagc  * 3. Neither the name of the University nor the names of its contributors
1961f28255Scgd  *    may be used to endorse or promote products derived from this software
2061f28255Scgd  *    without specific prior written permission.
2161f28255Scgd  *
2261f28255Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2361f28255Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2461f28255Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2561f28255Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2661f28255Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2761f28255Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2861f28255Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2961f28255Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3061f28255Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3161f28255Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3261f28255Scgd  * SUCH DAMAGE.
3361f28255Scgd  */
3461f28255Scgd 
35cd799663Schristos #include <sys/cdefs.h>
3661f28255Scgd #ifndef lint
3749f0ad86Scgd #if 0
3807bae7edSchristos static char sccsid[] = "@(#)var.c	8.3 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*ccb9728fSkre __RCSID("$NetBSD: var.c,v 1.88 2024/12/26 03:23:28 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
44ce6f66ceSkre #include <stdio.h>
4507bae7edSchristos #include <unistd.h>
4607bae7edSchristos #include <stdlib.h>
476814c65dSchristos #include <string.h>
48667e4d4cSfair #include <paths.h>
49a080d612Schristos #include <limits.h>
50dc833851Skre #include <time.h>
51dc833851Skre #include <pwd.h>
52dc833851Skre #include <fcntl.h>
53c6c29888Skre #include <inttypes.h>
5407bae7edSchristos 
5561f28255Scgd /*
5661f28255Scgd  * Shell variables.
5761f28255Scgd  */
5861f28255Scgd 
5961f28255Scgd #include "shell.h"
6061f28255Scgd #include "output.h"
6161f28255Scgd #include "expand.h"
6261f28255Scgd #include "nodes.h"	/* for other headers */
6361f28255Scgd #include "eval.h"	/* defines cmdenviron */
6461f28255Scgd #include "exec.h"
6561f28255Scgd #include "syntax.h"
6661f28255Scgd #include "options.h"
674fc4fe2eSchristos #include "builtins.h"
6861f28255Scgd #include "mail.h"
6961f28255Scgd #include "var.h"
7061f28255Scgd #include "memalloc.h"
7161f28255Scgd #include "error.h"
7261f28255Scgd #include "mystring.h"
731a523dd5Schristos #include "parser.h"
74e314f958Sdsl #include "show.h"
75dc833851Skre #include "machdep.h"
767accaec4Schristos #ifndef SMALL
7707bae7edSchristos #include "myhistedit.h"
7807bae7edSchristos #endif
7961f28255Scgd 
805f9b9101Sdsl #ifdef SMALL
8161f28255Scgd #define VTABSIZE 39
825f9b9101Sdsl #else
835f9b9101Sdsl #define VTABSIZE 517
845f9b9101Sdsl #endif
8561f28255Scgd 
8661f28255Scgd 
8761f28255Scgd struct varinit {
8861f28255Scgd 	struct var *var;
8961f28255Scgd 	int flags;
903d424690Schristos 	const char *text;
91727a69dcSkre 	union var_func_union v_u;
9261f28255Scgd };
93727a69dcSkre #define	func v_u.set_func
94727a69dcSkre #define	rfunc v_u.ref_func
95727a69dcSkre 
96727a69dcSkre char *get_lineno(struct var *);
9761f28255Scgd 
98dc833851Skre #ifndef SMALL
99dc833851Skre char *get_tod(struct var *);
100dc833851Skre char *get_hostname(struct var *);
101dc833851Skre char *get_seconds(struct var *);
102dc833851Skre char *get_euser(struct var *);
103dc833851Skre char *get_random(struct var *);
104dc833851Skre #endif
105dc833851Skre 
1060faa1734Sdholland struct localvar *localvars;
10761f28255Scgd 
1087accaec4Schristos #ifndef SMALL
10937ed7877Sjtc struct var vhistsize;
110c591669fSkre struct var vhistfile;
111c591669fSkre struct var vhistappend;
1127efa5addSchristos struct var vterm;
1133c2922e7Skre struct var editrc;
1144754b1e8Skre struct var ps_lit;
115e3c63ad9Scgd #endif
11661f28255Scgd struct var vifs;
11761f28255Scgd struct var vmail;
11861f28255Scgd struct var vmpath;
11961f28255Scgd struct var vpath;
12061f28255Scgd struct var vps1;
12161f28255Scgd struct var vps2;
122e314f958Sdsl struct var vps4;
1239302f8efSchristos struct var vvers;
124beb57fb3Schristos struct var voptind;
125fd38bbe2Skre struct var line_num;
126dc833851Skre #ifndef SMALL
127dc833851Skre struct var tod;
128dc833851Skre struct var host_name;
129dc833851Skre struct var seconds;
130dc833851Skre struct var euname;
131dc833851Skre struct var random_num;
132dc833851Skre 
133dc833851Skre intmax_t sh_start_time;
134dc833851Skre #endif
13561f28255Scgd 
136727a69dcSkre struct var line_num;
137727a69dcSkre int line_number;
138727a69dcSkre int funclinebase = 0;
139727a69dcSkre int funclineabs = 0;
140727a69dcSkre 
141ca12a0b8Schristos char ifs_default[] = " \t\n";
142ca12a0b8Schristos 
14361f28255Scgd const struct varinit varinit[] = {
1447accaec4Schristos #ifndef SMALL
145beb57fb3Schristos 	{ &vhistsize,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTSIZE=",
146727a69dcSkre 	   { .set_func= sethistsize } },
147c591669fSkre 	{ &vhistfile,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTFILE=",
148c591669fSkre 	   { .set_func= sethistfile } },
149c591669fSkre 	{ &vhistappend,	VSTRFIXED|VTEXTFIXED|VUNSET,	"HISTAPPEND=",
150c591669fSkre 	   { .set_func= sethistappend } },
151e3c63ad9Scgd #endif
152beb57fb3Schristos 	{ &vifs,	VSTRFIXED|VTEXTFIXED,		"IFS= \t\n",
153727a69dcSkre 	   { NULL } },
154beb57fb3Schristos 	{ &vmail,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAIL=",
155727a69dcSkre 	   { NULL } },
156beb57fb3Schristos 	{ &vmpath,	VSTRFIXED|VTEXTFIXED|VUNSET,	"MAILPATH=",
157727a69dcSkre 	   { NULL } },
1589302f8efSchristos 	{ &vvers,	VSTRFIXED|VTEXTFIXED|VNOEXPORT, "NETBSD_SHELL=",
159727a69dcSkre 	   { NULL } },
16084d782a5Scgd 	{ &vpath,	VSTRFIXED|VTEXTFIXED,		"PATH=" _PATH_DEFPATH,
161727a69dcSkre 	   { .set_func= changepath } },
16261f28255Scgd 	/*
16361f28255Scgd 	 * vps1 depends on uid
16461f28255Scgd 	 */
165beb57fb3Schristos 	{ &vps2,	VSTRFIXED|VTEXTFIXED,		"PS2=> ",
166727a69dcSkre 	   { NULL } },
167e314f958Sdsl 	{ &vps4,	VSTRFIXED|VTEXTFIXED,		"PS4=+ ",
168727a69dcSkre 	   { NULL } },
1697efa5addSchristos #ifndef SMALL
170beb57fb3Schristos 	{ &vterm,	VSTRFIXED|VTEXTFIXED|VUNSET,	"TERM=",
171727a69dcSkre 	   { .set_func= setterm } },
1723c2922e7Skre 	{ &editrc, 	VSTRFIXED|VTEXTFIXED|VUNSET,	"EDITRC=",
1733c2922e7Skre 	   { .set_func= set_editrc } },
1744754b1e8Skre 	{ &ps_lit, 	VSTRFIXED|VTEXTFIXED|VUNSET,	"PSlit=",
1754754b1e8Skre 	   { .set_func= set_prompt_lit } },
17661f28255Scgd #endif
177e314f958Sdsl 	{ &voptind,	VSTRFIXED|VTEXTFIXED|VNOFUNC,	"OPTIND=1",
178727a69dcSkre 	   { .set_func= getoptsreset } },
179370f00caSkre 	{ &line_num,	VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL,	"LINENO=1",
180727a69dcSkre 	   { .ref_func= get_lineno } },
181dc833851Skre #ifndef SMALL
182dc833851Skre 	{ &tod,		VSTRFIXED|VTEXTFIXED|VFUNCREF,	"ToD=",
183dc833851Skre 	   { .ref_func= get_tod } },
184dc833851Skre 	{ &host_name,	VSTRFIXED|VTEXTFIXED|VFUNCREF,	"HOSTNAME=",
185dc833851Skre 	   { .ref_func= get_hostname } },
186dc833851Skre 	{ &seconds,	VSTRFIXED|VTEXTFIXED|VFUNCREF,	"SECONDS=",
187dc833851Skre 	   { .ref_func= get_seconds } },
188dc833851Skre 	{ &euname,	VSTRFIXED|VTEXTFIXED|VFUNCREF,	"EUSER=",
189dc833851Skre 	   { .ref_func= get_euser } },
190370f00caSkre 	{ &random_num,	VSTRFIXED|VTEXTFIXED|VFUNCREF|VSPECIAL,	"RANDOM=",
191dc833851Skre 	   { .ref_func= get_random } },
192dc833851Skre #endif
193beb57fb3Schristos 	{ NULL,	0,				NULL,
194727a69dcSkre 	   { NULL } }
19561f28255Scgd };
19661f28255Scgd 
19761f28255Scgd struct var *vartab[VTABSIZE];
19861f28255Scgd 
1995f9b9101Sdsl STATIC int strequal(const char *, const char *);
2005f9b9101Sdsl STATIC struct var *find_var(const char *, struct var ***, int *);
201150d8297Skre STATIC void showvar(struct var *, const char *, const char *, int);
202150d8297Skre static void export_usage(const char *) __dead;
203727a664bSkre STATIC int makespecial(const char *);
20461f28255Scgd 
20561f28255Scgd /*
206f1229b53Sandvar  * Initialize the variable symbol tables and import the environment
20761f28255Scgd  */
20861f28255Scgd 
20961f28255Scgd #ifdef mkinit
2101dd2c2fdSchristos INCLUDE <stdio.h>
2111dd2c2fdSchristos INCLUDE <unistd.h>
212dc833851Skre INCLUDE <time.h>
21361f28255Scgd INCLUDE "var.h"
2149302f8efSchristos INCLUDE "version.h"
21533809804Schristos MKINIT char **environ;
216d73cf48cSkre MKINIT void setvareqsafe(char *, int);
21761f28255Scgd INIT {
21861f28255Scgd 	char **envp;
2191dd2c2fdSchristos 	char buf[64];
22061f28255Scgd 
221dc833851Skre #ifndef SMALL
222dc833851Skre 	sh_start_time = (intmax_t)time((time_t *)0);
223dc833851Skre #endif
224dc833851Skre 	/*
225dc833851Skre 	 * Set up our default variables and their values.
226dc833851Skre 	 */
22761f28255Scgd 	initvar();
228dc833851Skre 
229dc833851Skre 	/*
230dc833851Skre 	 * Import variables from the environment, which will
231d73cf48cSkre 	 * if permitted, override anything initialised just previously.
232dc833851Skre 	 */
23361f28255Scgd 	for (envp = environ ; *envp ; envp++) {
23461f28255Scgd 		if (strchr(*envp, '=')) {
235d73cf48cSkre 			setvareqsafe(*envp, VEXPORT|VTEXTFIXED|VUNSAFE);
23661f28255Scgd 		}
23761f28255Scgd 	}
2381dd2c2fdSchristos 
2391dd2c2fdSchristos 	/*
240f52337daSkre 	 * Set variables which override anything read from environment.
241f52337daSkre 	 *
2421dd2c2fdSchristos 	 * PPID is readonly
243f52337daSkre 	 * Always default IFS
244c6c29888Skre 	 * POSIX: "Whenever the shell is invoked, OPTIND shall
245c6c29888Skre 	 *         be initialized to 1."
246dc833851Skre 	 * PSc indicates the root/non-root status of this shell.
247dc833851Skre 	 * START_TIME belongs only to this shell.
248c6c29888Skre 	 * NETBSD_SHELL is a constant (readonly), and is never exported
249727a69dcSkre 	 * LINENO is simply magic...
2501dd2c2fdSchristos 	 */
2511dd2c2fdSchristos 	snprintf(buf, sizeof(buf), "%d", (int)getppid());
2521dd2c2fdSchristos 	setvar("PPID", buf, VREADONLY);
253ca12a0b8Schristos 	setvar("IFS", ifs_default, VTEXTFIXED);
254c6c29888Skre 	setvar("OPTIND", "1", VTEXTFIXED);
255dc833851Skre 	setvar("PSc", (geteuid() == 0 ? "#" : "$"), VTEXTFIXED);
256dc833851Skre 
257dc833851Skre #ifndef SMALL
258dc833851Skre 	snprintf(buf, sizeof(buf), "%jd", sh_start_time);
259dc833851Skre 	setvar("START_TIME", buf, VTEXTFIXED);
260dc833851Skre #endif
261f52337daSkre 
262f52337daSkre 	setvar("NETBSD_SHELL", NETBSD_SHELL
263f52337daSkre #ifdef BUILD_DATE
264f52337daSkre 		" BUILD:" BUILD_DATE
265f52337daSkre #endif
266f52337daSkre #ifdef DEBUG
267f52337daSkre 		" DEBUG"
268f52337daSkre #endif
269f52337daSkre #if !defined(JOBS) || JOBS == 0
270f52337daSkre 		" -JOBS"
271f52337daSkre #endif
272f52337daSkre #ifndef DO_SHAREDVFORK
273f52337daSkre 		" -VFORK"
274f52337daSkre #endif
275f52337daSkre #ifdef SMALL
276f52337daSkre 		" SMALL"
277f52337daSkre #endif
278f52337daSkre #ifdef TINY
279f52337daSkre 		" TINY"
280f52337daSkre #endif
281f52337daSkre #ifdef OLD_TTY_DRIVER
282f52337daSkre 		" OLD_TTY"
283f52337daSkre #endif
284f52337daSkre #ifdef SYSV
285f52337daSkre 		" SYSV"
286f52337daSkre #endif
287f52337daSkre #ifndef BSD
288f52337daSkre 		" -BSD"
289f52337daSkre #endif
2902d8874d9Skre #ifdef BOGUS_NOT_COMMAND
2912d8874d9Skre 		" BOGUS_NOT"
2922d8874d9Skre #endif
293472691bdSkre #ifdef REJECT_NULS
294472691bdSkre 		" REJECT_NULS"
295472691bdSkre #endif
296472691bdSkre #ifdef RESCUEDIR
297472691bdSkre 		" RESCUE"
298472691bdSkre #endif
299f52337daSkre 		    , VTEXTFIXED|VREADONLY|VNOEXPORT);
300fd38bbe2Skre 
301fd38bbe2Skre 	setvar("LINENO", "1", VTEXTFIXED);
30261f28255Scgd }
30361f28255Scgd #endif
30461f28255Scgd 
30561f28255Scgd 
30661f28255Scgd /*
30761f28255Scgd  * This routine initializes the builtin variables.  It is called when the
30861f28255Scgd  * shell is initialized and again when a shell procedure is spawned.
30961f28255Scgd  */
31061f28255Scgd 
31161f28255Scgd void
312c02b3bbdSchristos initvar(void)
313c02b3bbdSchristos {
31461f28255Scgd 	const struct varinit *ip;
31561f28255Scgd 	struct var *vp;
31661f28255Scgd 	struct var **vpp;
31761f28255Scgd 
31861f28255Scgd 	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
3195f9b9101Sdsl 		if (find_var(ip->text, &vpp, &vp->name_len) != NULL)
3205f9b9101Sdsl 			continue;
32161f28255Scgd 		vp->next = *vpp;
32261f28255Scgd 		*vpp = vp;
3233d424690Schristos 		vp->text = strdup(ip->text);
324dc833851Skre 		vp->flags = (ip->flags & ~VTEXTFIXED) | VSTRFIXED;
325727a69dcSkre 		vp->v_u = ip->v_u;
32661f28255Scgd 	}
32761f28255Scgd 	/*
32861f28255Scgd 	 * PS1 depends on uid
32961f28255Scgd 	 */
3305f9b9101Sdsl 	if (find_var("PS1", &vpp, &vps1.name_len) == NULL) {
33161f28255Scgd 		vps1.next = *vpp;
33261f28255Scgd 		*vpp = &vps1;
333dc833851Skre 		vps1.flags = VSTRFIXED;
334ac33f4eaSchristos 		vps1.text = NULL;
335ac33f4eaSchristos 		choose_ps1();
33661f28255Scgd 	}
33761f28255Scgd }
33861f28255Scgd 
339ac33f4eaSchristos void
340ac33f4eaSchristos choose_ps1(void)
341ac33f4eaSchristos {
342522d1631Skre 	uid_t u = geteuid();
343522d1631Skre 
344dc833851Skre 	if ((vps1.flags & (VTEXTFIXED|VSTACK)) == 0)
345ac33f4eaSchristos 		free(vps1.text);
346522d1631Skre 	vps1.text = strdup(u != 0 ? "PS1=$ " : "PS1=# ");
347dc833851Skre 	vps1.flags &= ~(VTEXTFIXED|VSTACK);
348dc833851Skre 
349dc833851Skre 	/*
350dc833851Skre 	 * Update PSc whenever we feel the need to update PS1
351dc833851Skre 	 */
352522d1631Skre 	setvarsafe("PSc", (u == 0 ? "#" : "$"), 0);
353ac33f4eaSchristos }
354ac33f4eaSchristos 
35561f28255Scgd /*
3564512d568Skre  * Validate a string as a valid variable name
3574512d568Skre  * nb: not parameter - special params and such are "invalid" here.
3584512d568Skre  * Name terminated by either \0 or the term param (usually '=' or '\0').
3594512d568Skre  *
3604512d568Skre  * If not NULL, the length of the (intended) name is returned via len
3614512d568Skre  */
3624512d568Skre 
3634512d568Skre int
3644512d568Skre validname(const char *name, int term, int *len)
3654512d568Skre {
3664512d568Skre 	const char *p = name;
3674512d568Skre 	int ok = 1;
3684512d568Skre 
3694512d568Skre 	if (p == NULL || *p == '\0' || *p == term) {
3704512d568Skre 		if (len != NULL)
3714512d568Skre 			*len = 0;
3724512d568Skre 		return 0;
3734512d568Skre 	}
3744512d568Skre 
3754512d568Skre 	if (!is_name(*p))
3764512d568Skre 		ok = 0;
3774512d568Skre 	p++;
3784512d568Skre 	for (;;) {
3794512d568Skre 		if (*p == '\0' || *p == term)
3804512d568Skre 			break;
3814512d568Skre 		if (!is_in_name(*p))
3824512d568Skre 			ok = 0;
3834512d568Skre 		p++;
3844512d568Skre 	}
3854512d568Skre 	if (len != NULL)
3864512d568Skre 		*len = p - name;
3874512d568Skre 
3884512d568Skre 	return ok;
3894512d568Skre }
3904512d568Skre 
3914512d568Skre /*
392beb57fb3Schristos  * Safe version of setvar, returns 1 on success 0 on failure.
393beb57fb3Schristos  */
394beb57fb3Schristos 
395beb57fb3Schristos int
396c02b3bbdSchristos setvarsafe(const char *name, const char *val, int flags)
397beb57fb3Schristos {
398beb57fb3Schristos 	struct jmploc jmploc;
399354d46cbSkre 	struct jmploc * const savehandler = handler;
400a54fe38aSchristos 	int volatile err = 0;
401beb57fb3Schristos 
402beb57fb3Schristos 	if (setjmp(jmploc.loc))
403beb57fb3Schristos 		err = 1;
404beb57fb3Schristos 	else {
405beb57fb3Schristos 		handler = &jmploc;
406beb57fb3Schristos 		setvar(name, val, flags);
407beb57fb3Schristos 	}
408beb57fb3Schristos 	handler = savehandler;
409beb57fb3Schristos 	return err;
410beb57fb3Schristos }
411beb57fb3Schristos 
412d73cf48cSkre void
413d73cf48cSkre setvareqsafe(char *s, int flags)
414d73cf48cSkre {
415d73cf48cSkre 	struct jmploc jmploc;
416d73cf48cSkre 	struct jmploc * const savehandler = handler;
417d73cf48cSkre 	volatile int e_s = errors_suppressed;
418d73cf48cSkre 
419d73cf48cSkre 	if (!setjmp(jmploc.loc)) {
420d73cf48cSkre 		handler = &jmploc;
421d73cf48cSkre 		errors_suppressed = 1;
422d73cf48cSkre 		setvareq(s, flags);
423d73cf48cSkre 	}
424d73cf48cSkre 	handler = savehandler;
425d73cf48cSkre 	errors_suppressed = e_s;
426d73cf48cSkre }
427d73cf48cSkre 
428beb57fb3Schristos /*
42961f28255Scgd  * Set the value of a variable.  The flags argument is ored with the
43061f28255Scgd  * flags of the variable.  If val is NULL, the variable is unset.
431dc833851Skre  *
432dc833851Skre  * This always copies name and val when setting a variable, so
433dc833851Skre  * the source strings can be from anywhere, and are no longer needed
434dc833851Skre  * after this function returns.  The VTEXTFIXED and VSTACK flags should
435dc833851Skre  * not be used (but just in case they were, clear them.)
43661f28255Scgd  */
43761f28255Scgd 
43861f28255Scgd void
439c02b3bbdSchristos setvar(const char *name, const char *val, int flags)
44061f28255Scgd {
4413d424690Schristos 	const char *p;
4423d424690Schristos 	const char *q;
4433d424690Schristos 	char *d;
44461f28255Scgd 	int len;
44561f28255Scgd 	int namelen;
44661f28255Scgd 	char *nameeq;
44761f28255Scgd 
44861f28255Scgd 	p = name;
4494512d568Skre 
4504512d568Skre 	if (!validname(p, '=', &namelen))
45137ed7877Sjtc 		error("%.*s: bad variable name", namelen, name);
45261f28255Scgd 	len = namelen + 2;		/* 2 is space for '=' and '\0' */
45361f28255Scgd 	if (val == NULL) {
45461f28255Scgd 		flags |= VUNSET;
45561f28255Scgd 	} else {
45661f28255Scgd 		len += strlen(val);
45761f28255Scgd 	}
4583d424690Schristos 	d = nameeq = ckmalloc(len);
45961f28255Scgd 	q = name;
46061f28255Scgd 	while (--namelen >= 0)
4613d424690Schristos 		*d++ = *q++;
4623d424690Schristos 	*d++ = '=';
4633d424690Schristos 	*d = '\0';
46461f28255Scgd 	if (val)
4653d424690Schristos 		scopy(val, d);
466dc833851Skre 	setvareq(nameeq, flags & ~(VTEXTFIXED | VSTACK));
46761f28255Scgd }
46861f28255Scgd 
46961f28255Scgd 
47061f28255Scgd 
47161f28255Scgd /*
47261f28255Scgd  * Same as setvar except that the variable and value are passed in
47361f28255Scgd  * the first argument as name=value.  Since the first argument will
47461f28255Scgd  * be actually stored in the table, it should not be a string that
475dc833851Skre  * will go away.   The flags (VTEXTFIXED or VSTACK) can be used to
476dc833851Skre  * indicate the source of the string (if neither is set, the string will
477dc833851Skre  * eventually be free()d when a replacement value is assigned.)
47861f28255Scgd  */
47961f28255Scgd 
48061f28255Scgd void
481c02b3bbdSchristos setvareq(char *s, int flags)
48261f28255Scgd {
48361f28255Scgd 	struct var *vp, **vpp;
4845f9b9101Sdsl 	int nlen;
48561f28255Scgd 
48659695ae2Skre 	VTRACE(DBG_VARS, ("setvareq([%s],%#x) aflag=%d ", s, flags, aflag));
4879302f8efSchristos 	if (aflag && !(flags & VNOEXPORT))
4886f786375Sbjh21 		flags |= VEXPORT;
4895f9b9101Sdsl 	vp = find_var(s, &vpp, &nlen);
4905f9b9101Sdsl 	if (vp != NULL) {
49159695ae2Skre 		VTRACE(DBG_VARS, ("was [%s] fl:%#x\n", vp->text,
49259695ae2Skre 		    vp->flags));
493ad0b72d9Skre 		if (vp->flags & VREADONLY) {
494ad0b72d9Skre 			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
495ad0b72d9Skre 				ckfree(s);
496ad0b72d9Skre 			if (flags & VNOERROR)
497c02b3bbdSchristos 				return;
498cdfea658Skre 			error("%.*s: is read only", vp->name_len, vp->text);
499ad0b72d9Skre 		}
500ad0b72d9Skre 		if (flags & VNOSET) {
501ad0b72d9Skre 			if ((flags & (VTEXTFIXED|VSTACK)) == 0)
502ad0b72d9Skre 				ckfree(s);
503ad0b72d9Skre 			return;
504ad0b72d9Skre 		}
505727a69dcSkre 
50661f28255Scgd 		INTOFF;
507beb57fb3Schristos 
508727a69dcSkre 		if (vp->func && !(vp->flags & VFUNCREF) && !(flags & VNOFUNC))
509d73cf48cSkre 			(*vp->func)(s + vp->name_len + 1, flags);
510beb57fb3Schristos 
51161f28255Scgd 		if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
51261f28255Scgd 			ckfree(vp->text);
513beb57fb3Schristos 
514370f00caSkre 		/*
515370f00caSkre 		 * if we set a magic var, the magic dissipates,
516370f00caSkre 		 * unless it is very special indeed.
517370f00caSkre 		 */
518370f00caSkre 		if (vp->rfunc && (vp->flags & (VFUNCREF|VSPECIAL)) == VFUNCREF)
519370f00caSkre 			vp->rfunc = NULL;
520370f00caSkre 
521d73cf48cSkre 		vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET|VUNSAFE);
5229302f8efSchristos 		if (flags & VNOEXPORT)
5239302f8efSchristos 			vp->flags &= ~VEXPORT;
524522d1631Skre 		if (flags & VDOEXPORT)
525522d1631Skre 			vp->flags &= ~VNOEXPORT;
5268df083c1Skre 		if (vp->flags & VNOEXPORT)
5278df083c1Skre 			flags &= ~VEXPORT;
528522d1631Skre 		vp->flags |= flags & ~(VNOFUNC | VDOEXPORT);
52961f28255Scgd 		vp->text = s;
530beb57fb3Schristos 
531beb57fb3Schristos 		/*
532beb57fb3Schristos 		 * We could roll this to a function, to handle it as
533beb57fb3Schristos 		 * a regular variable function callback, but why bother?
534beb57fb3Schristos 		 */
53561f28255Scgd 		if (vp == &vmpath || (vp == &vmail && ! mpathset()))
53661f28255Scgd 			chkmail(1);
537727a69dcSkre 
53861f28255Scgd 		INTON;
53961f28255Scgd 		return;
54061f28255Scgd 	}
54161f28255Scgd 	/* not found */
542ad0b72d9Skre 	if (flags & VNOSET) {
543522d1631Skre 		VTRACE(DBG_VARS, ("new noset\n"));
544ad0b72d9Skre 		if ((flags & (VTEXTFIXED|VSTACK)) == 0)
545ad0b72d9Skre 			ckfree(s);
546c02b3bbdSchristos 		return;
547ad0b72d9Skre 	}
54861f28255Scgd 	vp = ckmalloc(sizeof (*vp));
549522d1631Skre 	vp->flags = flags & ~(VNOFUNC|VFUNCREF|VDOEXPORT);
55061f28255Scgd 	vp->text = s;
5515f9b9101Sdsl 	vp->name_len = nlen;
552beb57fb3Schristos 	vp->func = NULL;
553522d1631Skre 	vp->next = *vpp;
55461f28255Scgd 	*vpp = vp;
555522d1631Skre 
556522d1631Skre 	VTRACE(DBG_VARS, ("new [%s] (%d) %#x\n", s, nlen, vp->flags));
55761f28255Scgd }
55861f28255Scgd 
559e7b0505eSkre void
560e7b0505eSkre setvar_invocation(int argc, char **argv)
561e7b0505eSkre {
562e7b0505eSkre 	char value[32];		/* if we ever get 30, HELP */
563e7b0505eSkre 	char *v;
56461f28255Scgd 
565e7b0505eSkre 	/*
566e7b0505eSkre 	 * Keep the following in ascii lexical order ( ie: Z before a )
567e7b0505eSkre 	 */
568e7b0505eSkre 
569e7b0505eSkre 	v = value;
5709eccf618Skre 	*v++ = '!';		/* never empty, and the '-' is not first */
571e7b0505eSkre 
572e7b0505eSkre 	if (argc > 0 && argv[0] != NULL && argv[0][0] == '-')
573e7b0505eSkre 		*v++ = '-';
574e7b0505eSkre 	if (shellparam.nparam == 0)
575e7b0505eSkre 		*v++ = '0';
576e7b0505eSkre 	if (minusc)
577e7b0505eSkre 		*v++ = 'c';
578e7b0505eSkre 	if (commandname)
579e7b0505eSkre 		*v++ = 'f';
580e7b0505eSkre 	if (iflag)
581e7b0505eSkre 		*v++ = 'i';
582e7b0505eSkre 	if (loginsh)
583e7b0505eSkre 		*v++ = 'l';
584e7b0505eSkre 	if (privileged)
585e7b0505eSkre 		*v++ = 'p';
586e7b0505eSkre 	if (sflag)
587e7b0505eSkre 		*v++ = 's';
588e7b0505eSkre 
589e7b0505eSkre 	*v++ = '\0';
590e7b0505eSkre 
591e7b0505eSkre 		/*
592e7b0505eSkre 		 * this cannot fail, the var name is OK,
593e7b0505eSkre 		 * there cannot be any (non special) read only
594e7b0505eSkre 		 * variables at this point, ...
595e7b0505eSkre 		 */
596e7b0505eSkre 	setvar("NBSH_INVOCATION", value, VNOEXPORT);
597e7b0505eSkre }
59861f28255Scgd 
59961f28255Scgd /*
60061f28255Scgd  * Process a linked list of variable assignments.
60161f28255Scgd  */
60261f28255Scgd 
60361f28255Scgd void
604c02b3bbdSchristos listsetvar(struct strlist *list, int flags)
60561f28255Scgd {
60661f28255Scgd 	struct strlist *lp;
60761f28255Scgd 
60861f28255Scgd 	INTOFF;
60961f28255Scgd 	for (lp = list ; lp ; lp = lp->next) {
610c02b3bbdSchristos 		setvareq(savestr(lp->text), flags);
61161f28255Scgd 	}
61261f28255Scgd 	INTON;
61361f28255Scgd }
61461f28255Scgd 
615e314f958Sdsl void
616e314f958Sdsl listmklocal(struct strlist *list, int flags)
617e314f958Sdsl {
618e314f958Sdsl 	struct strlist *lp;
619e314f958Sdsl 
620e314f958Sdsl 	for (lp = list ; lp ; lp = lp->next)
621e314f958Sdsl 		mklocal(lp->text, flags);
622e314f958Sdsl }
62361f28255Scgd 
62461f28255Scgd 
62561f28255Scgd /*
62661f28255Scgd  * Find the value of a variable.  Returns NULL if not set.
62761f28255Scgd  */
62861f28255Scgd 
62961f28255Scgd char *
630c02b3bbdSchristos lookupvar(const char *name)
63161f28255Scgd {
63261f28255Scgd 	struct var *v;
63383735e24Skre 	char *p;
63461f28255Scgd 
6355f9b9101Sdsl 	v = find_var(name, NULL, NULL);
6365f9b9101Sdsl 	if (v == NULL || v->flags & VUNSET)
63761f28255Scgd 		return NULL;
63883735e24Skre 	if (v->rfunc && (v->flags & VFUNCREF) != 0) {
63983735e24Skre 		p = (*v->rfunc)(v);
64083735e24Skre 		if (p == NULL)
64183735e24Skre 			return NULL;
64283735e24Skre 	} else
64383735e24Skre 		p = v->text;
64483735e24Skre 
64583735e24Skre 	return p + v->name_len + 1;
64661f28255Scgd }
64761f28255Scgd 
64861f28255Scgd 
64961f28255Scgd 
65061f28255Scgd /*
65161f28255Scgd  * Search the environment of a builtin command.  If the second argument
65261f28255Scgd  * is nonzero, return the value of a variable even if it hasn't been
65361f28255Scgd  * exported.
65461f28255Scgd  */
65561f28255Scgd 
65661f28255Scgd char *
657c02b3bbdSchristos bltinlookup(const char *name, int doall)
65861f28255Scgd {
65961f28255Scgd 	struct strlist *sp;
66061f28255Scgd 	struct var *v;
66183735e24Skre 	char *p;
66261f28255Scgd 
66361f28255Scgd 	for (sp = cmdenviron ; sp ; sp = sp->next) {
6645f9b9101Sdsl 		if (strequal(sp->text, name))
66561f28255Scgd 			return strchr(sp->text, '=') + 1;
66661f28255Scgd 	}
6675f9b9101Sdsl 
6685f9b9101Sdsl 	v = find_var(name, NULL, NULL);
6695f9b9101Sdsl 
6705f9b9101Sdsl 	if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT)))
67161f28255Scgd 		return NULL;
67283735e24Skre 
67383735e24Skre 	if (v->rfunc && (v->flags & VFUNCREF) != 0) {
67483735e24Skre 		p = (*v->rfunc)(v);
67583735e24Skre 		if (p == NULL)
67683735e24Skre 			return NULL;
67783735e24Skre 	} else
67883735e24Skre 		p = v->text;
67983735e24Skre 
68083735e24Skre 	return p + v->name_len + 1;
68161f28255Scgd }
68261f28255Scgd 
68361f28255Scgd 
68461f28255Scgd 
68561f28255Scgd /*
68661f28255Scgd  * Generate a list of exported variables.  This routine is used to construct
68761f28255Scgd  * the third argument to execve when executing a program.
68861f28255Scgd  */
68961f28255Scgd 
69061f28255Scgd char **
691c02b3bbdSchristos environment(void)
692c02b3bbdSchristos {
69361f28255Scgd 	int nenv;
69461f28255Scgd 	struct var **vpp;
69561f28255Scgd 	struct var *vp;
6963d424690Schristos 	char **env;
6973d424690Schristos 	char **ep;
69861f28255Scgd 
69961f28255Scgd 	nenv = 0;
70061f28255Scgd 	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
70161f28255Scgd 		for (vp = *vpp ; vp ; vp = vp->next)
7023276f23bSkre 			if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
70361f28255Scgd 				nenv++;
70461f28255Scgd 	}
705ce6f66ceSkre 	CTRACE(DBG_VARS, ("environment: %d vars to export\n", nenv));
70661f28255Scgd 	ep = env = stalloc((nenv + 1) * sizeof *env);
70761f28255Scgd 	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
70861f28255Scgd 		for (vp = *vpp ; vp ; vp = vp->next)
709727a69dcSkre 			if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) {
71083735e24Skre 				if (vp->rfunc && (vp->flags & VFUNCREF)) {
71183735e24Skre 					*ep = (*vp->rfunc)(vp);
71283735e24Skre 					if (*ep != NULL)
71383735e24Skre 						ep++;
71483735e24Skre 				} else
71561f28255Scgd 					*ep++ = vp->text;
716ce6f66ceSkre 				VTRACE(DBG_VARS, ("environment: %s\n", ep[-1]));
71761f28255Scgd 			}
718727a69dcSkre 	}
71961f28255Scgd 	*ep = NULL;
72061f28255Scgd 	return env;
72161f28255Scgd }
72261f28255Scgd 
72361f28255Scgd 
72461f28255Scgd /*
72561f28255Scgd  * Called when a shell procedure is invoked to clear out nonexported
72661f28255Scgd  * variables.  It is also necessary to reallocate variables of with
72761f28255Scgd  * VSTACK set since these are currently allocated on the stack.
72861f28255Scgd  */
72961f28255Scgd 
73061f28255Scgd #ifdef mkinit
731c02b3bbdSchristos void shprocvar(void);
73261f28255Scgd 
73361f28255Scgd SHELLPROC {
73461f28255Scgd 	shprocvar();
73561f28255Scgd }
73661f28255Scgd #endif
73761f28255Scgd 
73861f28255Scgd void
739c02b3bbdSchristos shprocvar(void)
740c02b3bbdSchristos {
74161f28255Scgd 	struct var **vpp;
74261f28255Scgd 	struct var *vp, **prev;
74361f28255Scgd 
74461f28255Scgd 	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
74561f28255Scgd 		for (prev = vpp ; (vp = *prev) != NULL ; ) {
74661f28255Scgd 			if ((vp->flags & VEXPORT) == 0) {
74761f28255Scgd 				*prev = vp->next;
74861f28255Scgd 				if ((vp->flags & VTEXTFIXED) == 0)
74961f28255Scgd 					ckfree(vp->text);
75061f28255Scgd 				if ((vp->flags & VSTRFIXED) == 0)
75161f28255Scgd 					ckfree(vp);
75261f28255Scgd 			} else {
75361f28255Scgd 				if (vp->flags & VSTACK) {
75461f28255Scgd 					vp->text = savestr(vp->text);
75561f28255Scgd 					vp->flags &=~ VSTACK;
75661f28255Scgd 				}
75761f28255Scgd 				prev = &vp->next;
75861f28255Scgd 			}
75961f28255Scgd 		}
76061f28255Scgd 	}
76161f28255Scgd 	initvar();
76261f28255Scgd }
76361f28255Scgd 
76461f28255Scgd 
76561f28255Scgd 
76661f28255Scgd /*
76761f28255Scgd  * Command to list all variables which are set.  Currently this command
76861f28255Scgd  * is invoked from the set command when the set command is called without
76961f28255Scgd  * any variables.
77061f28255Scgd  */
77161f28255Scgd 
772c02b3bbdSchristos void
773c02b3bbdSchristos print_quoted(const char *p)
774c02b3bbdSchristos {
775c02b3bbdSchristos 	const char *q;
776c02b3bbdSchristos 
7776f0d4805Skre 	if (p[0] == '\0') {
7786f0d4805Skre 		out1fmt("''");
7796f0d4805Skre 		return;
7806f0d4805Skre 	}
781c02b3bbdSchristos 	if (strcspn(p, "|&;<>()$`\\\"' \t\n*?[]#~=%") == strlen(p)) {
782c02b3bbdSchristos 		out1fmt("%s", p);
783c02b3bbdSchristos 		return;
784c02b3bbdSchristos 	}
785c02b3bbdSchristos 	while (*p) {
786c02b3bbdSchristos 		if (*p == '\'') {
787c02b3bbdSchristos 			out1fmt("\\'");
788c02b3bbdSchristos 			p++;
789c02b3bbdSchristos 			continue;
790c02b3bbdSchristos 		}
7916814c65dSchristos 		q = strchr(p, '\'');
792c02b3bbdSchristos 		if (!q) {
793c02b3bbdSchristos 			out1fmt("'%s'", p );
794c02b3bbdSchristos 			return;
795c02b3bbdSchristos 		}
796ea1d6159Sagc 		out1fmt("'%.*s'", (int)(q - p), p );
797c02b3bbdSchristos 		p = q;
798c02b3bbdSchristos 	}
799c02b3bbdSchristos }
800c02b3bbdSchristos 
801c02b3bbdSchristos static int
802c02b3bbdSchristos sort_var(const void *v_v1, const void *v_v2)
803c02b3bbdSchristos {
804c02b3bbdSchristos 	const struct var * const *v1 = v_v1;
805c02b3bbdSchristos 	const struct var * const *v2 = v_v2;
806be0a98abSkre 	char *t1 = (*v1)->text, *t2 = (*v2)->text;
807c02b3bbdSchristos 
808be0a98abSkre 	if (*t1 == *t2) {
809be0a98abSkre 		char *p, *s;
810be0a98abSkre 
811be0a98abSkre 		STARTSTACKSTR(p);
812be0a98abSkre 
813be0a98abSkre 		/*
814be0a98abSkre 		 * note: if lengths are equal, strings must be different
815be0a98abSkre 		 * so we don't care which string we pick for the \0 in
816be0a98abSkre 		 * that case.
817be0a98abSkre 		 */
818be0a98abSkre 		if ((strchr(t1, '=') - t1) <= (strchr(t2, '=') - t2)) {
819be0a98abSkre 			s = t1;
820be0a98abSkre 			t1 = p;
821be0a98abSkre 		} else {
822be0a98abSkre 			s = t2;
823be0a98abSkre 			t2 = p;
824be0a98abSkre 		}
825be0a98abSkre 
826be0a98abSkre 		while (*s && *s != '=') {
827be0a98abSkre 			STPUTC(*s, p);
828be0a98abSkre 			s++;
829be0a98abSkre 		}
830be0a98abSkre 		STPUTC('\0', p);
831be0a98abSkre 	}
832be0a98abSkre 
833be0a98abSkre 	return strcoll(t1, t2);
834c02b3bbdSchristos }
835c02b3bbdSchristos 
836c02b3bbdSchristos /*
837c02b3bbdSchristos  * POSIX requires that 'set' (but not export or readonly) output the
838c02b3bbdSchristos  * variables in lexicographic order - by the locale's collating order (sigh).
839c02b3bbdSchristos  * Maybe we could keep them in an ordered balanced binary tree
840c02b3bbdSchristos  * instead of hashed lists.
841c02b3bbdSchristos  * For now just roll 'em through qsort for printing...
842c02b3bbdSchristos  */
843c02b3bbdSchristos 
844150d8297Skre STATIC void
845150d8297Skre showvar(struct var *vp, const char *cmd, const char *xtra, int show_value)
846150d8297Skre {
847150d8297Skre 	const char *p;
848150d8297Skre 
84983735e24Skre 	p = vp->text;
85083735e24Skre 	if (vp->rfunc && (vp->flags & VFUNCREF) != 0) {
85183735e24Skre 		p = (*vp->rfunc)(vp);
85283735e24Skre 		if (p == NULL) {
85383735e24Skre 			if (!(show_value & 2))
85483735e24Skre 				return;
85583735e24Skre 			p = vp->text;
85683735e24Skre 			show_value = 0;
85783735e24Skre 		}
85883735e24Skre 	}
859150d8297Skre 	if (cmd)
860150d8297Skre 		out1fmt("%s ", cmd);
861150d8297Skre 	if (xtra)
862150d8297Skre 		out1fmt("%s ", xtra);
863150d8297Skre 	for ( ; *p != '=' ; p++)
864150d8297Skre 		out1c(*p);
865150d8297Skre 	if (!(vp->flags & VUNSET) && show_value) {
866150d8297Skre 		out1fmt("=");
867150d8297Skre 		print_quoted(++p);
868150d8297Skre 	}
869150d8297Skre 	out1c('\n');
870150d8297Skre }
871150d8297Skre 
87261f28255Scgd int
873150d8297Skre showvars(const char *cmd, int flag, int show_value, const char *xtra)
8744ce0d34aScgd {
87561f28255Scgd 	struct var **vpp;
87661f28255Scgd 	struct var *vp;
877c02b3bbdSchristos 
878c02b3bbdSchristos 	static struct var **list;	/* static in case we are interrupted */
879c02b3bbdSchristos 	static int list_len;
880c02b3bbdSchristos 	int count = 0;
881c02b3bbdSchristos 
882c02b3bbdSchristos 	if (!list) {
883c02b3bbdSchristos 		list_len = 32;
884c02b3bbdSchristos 		list = ckmalloc(list_len * sizeof *list);
885c02b3bbdSchristos 	}
88661f28255Scgd 
88761f28255Scgd 	for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
88861f28255Scgd 		for (vp = *vpp ; vp ; vp = vp->next) {
889c02b3bbdSchristos 			if (flag && !(vp->flags & flag))
890c02b3bbdSchristos 				continue;
891c02b3bbdSchristos 			if (vp->flags & VUNSET && !(show_value & 2))
892c02b3bbdSchristos 				continue;
893c02b3bbdSchristos 			if (count >= list_len) {
894c02b3bbdSchristos 				list = ckrealloc(list,
895c02b3bbdSchristos 					(list_len << 1) * sizeof *list);
896c02b3bbdSchristos 				list_len <<= 1;
89761f28255Scgd 			}
898c02b3bbdSchristos 			list[count++] = vp;
899c02b3bbdSchristos 		}
900c02b3bbdSchristos 	}
901c02b3bbdSchristos 
902c02b3bbdSchristos 	qsort(list, count, sizeof *list, sort_var);
903c02b3bbdSchristos 
904150d8297Skre 	for (vpp = list; count--; vpp++)
905150d8297Skre 		showvar(*vpp, cmd, xtra, show_value);
906150d8297Skre 
907150d8297Skre 	/* no free(list), will be used again next time ... */
908150d8297Skre 
90961f28255Scgd 	return 0;
91061f28255Scgd }
91161f28255Scgd 
91261f28255Scgd 
91361f28255Scgd 
91461f28255Scgd /*
91561f28255Scgd  * The export and readonly commands.
91661f28255Scgd  */
91761f28255Scgd 
918150d8297Skre static void __dead
919150d8297Skre export_usage(const char *cmd)
920150d8297Skre {
921150d8297Skre #ifdef SMALL
922150d8297Skre 	if (*cmd == 'r')
923150d8297Skre 	    error("Usage: %s [ -p | var[=val]... ]", cmd);
924150d8297Skre 	else
925150d8297Skre 	    error("Usage: %s [ -p | [-n] var[=val]... ]", cmd);
926150d8297Skre #else
927150d8297Skre 	if (*cmd == 'r')
928150d8297Skre 	    error("Usage: %s [-p [var...] | -q var... | var[=val]... ]", cmd);
929150d8297Skre 	else
930150d8297Skre 	    error(
931150d8297Skre 	     "Usage: %s [ -px [var...] | -q[x] var... | [-n|x] var[=val]... ]",
932150d8297Skre 		cmd);
933150d8297Skre #endif
934150d8297Skre }
935150d8297Skre 
93661f28255Scgd int
937c02b3bbdSchristos exportcmd(int argc, char **argv)
9384ce0d34aScgd {
93961f28255Scgd 	struct var *vp;
94061f28255Scgd 	char *name;
941150d8297Skre 	const char *p = argv[0];
942150d8297Skre 	int flag = p[0] == 'r'? VREADONLY : VEXPORT;
9439302f8efSchristos 	int pflg = 0;
9449302f8efSchristos 	int nflg = 0;
945150d8297Skre #ifndef SMALL
9469302f8efSchristos 	int xflg = 0;
947150d8297Skre 	int qflg = 0;
948150d8297Skre #endif
9499302f8efSchristos 	int res;
9509302f8efSchristos 	int c;
9513276f23bSkre 	int f;
9529302f8efSchristos 
953150d8297Skre #ifdef SMALL
954150d8297Skre #define EXPORT_OPTS "np"
955150d8297Skre #else
956150d8297Skre #define	EXPORT_OPTS "npqx"
957150d8297Skre #endif
958150d8297Skre 
959150d8297Skre 	while ((c = nextopt(EXPORT_OPTS)) != '\0') {
960150d8297Skre 
961150d8297Skre #undef EXPORT_OPTS
962150d8297Skre 
9639302f8efSchristos 		switch (c) {
964150d8297Skre 		case 'n':
965150d8297Skre 			if (pflg || flag == VREADONLY
966150d8297Skre #ifndef SMALL
967150d8297Skre 				|| qflg || xflg
968150d8297Skre #endif
969150d8297Skre 						)
970150d8297Skre 				export_usage(p);
971150d8297Skre 			nflg = 1;
972150d8297Skre 			break;
9739302f8efSchristos 		case 'p':
974150d8297Skre 			if (nflg
975150d8297Skre #ifndef SMALL
976150d8297Skre 				|| qflg
977150d8297Skre #endif
978150d8297Skre 					)
979150d8297Skre 				export_usage(p);
9809302f8efSchristos 			pflg = 3;
9819302f8efSchristos 			break;
982150d8297Skre #ifndef SMALL
983150d8297Skre 		case 'q':
984150d8297Skre 			if (nflg || pflg)
985150d8297Skre 				export_usage(p);
986150d8297Skre 			qflg = 1;
9879302f8efSchristos 			break;
9889302f8efSchristos 		case 'x':
9899302f8efSchristos 			if (nflg || flag == VREADONLY)
990150d8297Skre 				export_usage(p);
9919302f8efSchristos 			flag = VNOEXPORT;
9929302f8efSchristos 			xflg = 1;
9939302f8efSchristos 			break;
994150d8297Skre #endif
9959302f8efSchristos 		}
9969302f8efSchristos 	}
9979302f8efSchristos 
998150d8297Skre 	if ((nflg
999150d8297Skre #ifndef SMALL
1000150d8297Skre 		|| qflg
1001150d8297Skre #endif
1002150d8297Skre 		 ) && *argptr == NULL)
1003150d8297Skre 		export_usage(p);
10049302f8efSchristos 
1005150d8297Skre #ifndef SMALL
1006150d8297Skre 	if (pflg && *argptr != NULL) {
1007150d8297Skre 		while ((name = *argptr++) != NULL) {
1008150d8297Skre 			int len;
1009150d8297Skre 
1010150d8297Skre 			vp = find_var(name, NULL, &len);
1011150d8297Skre 			if (name[len] == '=')
1012150d8297Skre 				export_usage(p);
1013150d8297Skre 			if (!goodname(name))
1014150d8297Skre 				error("%s: bad variable name", name);
1015150d8297Skre 
1016150d8297Skre 			if (vp && vp->flags & flag)
1017150d8297Skre 				showvar(vp, p, xflg ? "-x" : NULL, 1);
1018150d8297Skre 		}
1019c02b3bbdSchristos 		return 0;
1020c02b3bbdSchristos 	}
1021150d8297Skre #endif
1022150d8297Skre 
1023150d8297Skre 	if (pflg || *argptr == NULL)
1024150d8297Skre 		return showvars( pflg ? p : 0, flag, pflg,
1025150d8297Skre #ifndef SMALL
1026150d8297Skre 		    pflg && xflg ? "-x" :
1027150d8297Skre #endif
1028150d8297Skre 					    NULL );
1029c02b3bbdSchristos 
10309302f8efSchristos 	res = 0;
1031150d8297Skre #ifndef SMALL
1032150d8297Skre 	if (qflg) {
1033150d8297Skre 		while ((name = *argptr++) != NULL) {
1034150d8297Skre 			int len;
1035150d8297Skre 
1036150d8297Skre 			vp = find_var(name, NULL, &len);
1037150d8297Skre 			if (name[len] == '=')
1038150d8297Skre 				export_usage(p);
1039150d8297Skre 			if (!goodname(name))
1040150d8297Skre 				error("%s: bad variable name", name);
1041150d8297Skre 
1042150d8297Skre 			if (vp == NULL || !(vp->flags & flag))
1043150d8297Skre 				res = 1;
1044150d8297Skre 		}
1045150d8297Skre 		return res;
1046150d8297Skre 	}
1047150d8297Skre #endif
1048150d8297Skre 
104961f28255Scgd 	while ((name = *argptr++) != NULL) {
1050522d1631Skre 		int len;
1051522d1631Skre 
10523276f23bSkre 		f = flag;
1053522d1631Skre 
1054522d1631Skre 		vp = find_var(name, NULL, &len);
1055522d1631Skre 		p = name + len;
1056522d1631Skre 		if (*p++ != '=')
1057522d1631Skre 			p = NULL;
1058522d1631Skre 
10595f9b9101Sdsl 		if (vp != NULL) {
10609302f8efSchristos 			if (nflg)
10619302f8efSchristos 				vp->flags &= ~flag;
1062522d1631Skre 			else if (flag&VEXPORT && vp->flags&VNOEXPORT) {
106364c9a713Skre 				/* note we go ahead and do any assignment */
1064522d1631Skre 				sh_warnx("%.*s: not available for export",
1065522d1631Skre 				    len, name);
10669302f8efSchristos 				res = 1;
1067522d1631Skre 			} else {
10689302f8efSchristos 				if (flag == VNOEXPORT)
10699302f8efSchristos 					vp->flags &= ~VEXPORT;
107064c9a713Skre 
107164c9a713Skre 				/* if not NULL will be done in setvar below */
107264c9a713Skre 				if (p == NULL)
107364c9a713Skre 					vp->flags |= flag;
10749302f8efSchristos 			}
1075522d1631Skre 			if (p == NULL)
107687aaf3d7Senami 				continue;
1077150d8297Skre 		} else if (nflg && p == NULL && !goodname(name))
1078150d8297Skre 			error("%s: bad variable name", name);
1079522d1631Skre 
1080522d1631Skre 		if (!nflg || p != NULL)
10813276f23bSkre 			setvar(name, p, f);
108261f28255Scgd 	}
10839302f8efSchristos 	return res;
108461f28255Scgd }
108561f28255Scgd 
108661f28255Scgd 
108761f28255Scgd /*
108861f28255Scgd  * The "local" command.
108961f28255Scgd  */
109061f28255Scgd 
10914ce0d34aScgd int
1092c02b3bbdSchristos localcmd(int argc, char **argv)
10934ce0d34aScgd {
109461f28255Scgd 	char *name;
10958df083c1Skre 	int c;
10968df083c1Skre 	int flags = 0;		/*XXX perhaps VUNSET from a -o option value */
109761f28255Scgd 
109861f28255Scgd 	if (! in_function())
109961f28255Scgd 		error("Not in a function");
11008df083c1Skre 
11018df083c1Skre 	/* upper case options, as bash stole all the good ones ... */
11028df083c1Skre 	while ((c = nextopt("INx")) != '\0')
11038df083c1Skre 		switch (c) {
11048df083c1Skre 		case 'I':	flags &= ~VUNSET;	break;
11058df083c1Skre 		case 'N':	flags |= VUNSET;	break;
11068df083c1Skre 		case 'x':	flags |= VEXPORT;	break;
11078df083c1Skre 		}
11088df083c1Skre 
110961f28255Scgd 	while ((name = *argptr++) != NULL) {
11108df083c1Skre 		mklocal(name, flags);
111161f28255Scgd 	}
111261f28255Scgd 	return 0;
111361f28255Scgd }
111461f28255Scgd 
111561f28255Scgd 
111661f28255Scgd /*
1117a640fe8cSsnj  * Make a variable a local variable.  When a variable is made local, its
111861f28255Scgd  * value and flags are saved in a localvar structure.  The saved values
111961f28255Scgd  * will be restored when the shell function returns.  We handle the name
112061f28255Scgd  * "-" as a special case.
112161f28255Scgd  */
112261f28255Scgd 
112361f28255Scgd void
1124e314f958Sdsl mklocal(const char *name, int flags)
112561f28255Scgd {
112661f28255Scgd 	struct localvar *lvp;
112761f28255Scgd 	struct var **vpp;
112861f28255Scgd 	struct var *vp;
112961f28255Scgd 
113061f28255Scgd 	INTOFF;
113161f28255Scgd 	lvp = ckmalloc(sizeof (struct localvar));
113261f28255Scgd 	if (name[0] == '-' && name[1] == '\0') {
11333d424690Schristos 		char *p;
1134c02b3bbdSchristos 		p = ckmalloc(sizeof_optlist);
1135c02b3bbdSchristos 		lvp->text = memcpy(p, optlist, sizeof_optlist);
1136370f00caSkre 		lvp->rfunc = NULL;
113761f28255Scgd 		vp = NULL;
1138ffc64c63Skre 		xtrace_clone(0);
113961f28255Scgd 	} else {
11405f9b9101Sdsl 		vp = find_var(name, &vpp, NULL);
114161f28255Scgd 		if (vp == NULL) {
11428df083c1Skre 			flags &= ~VNOEXPORT;
114361f28255Scgd 			if (strchr(name, '='))
11448df083c1Skre 				setvareq(savestr(name),
11458df083c1Skre 				    VSTRFIXED | (flags & ~VUNSET));
114661f28255Scgd 			else
1147edcb4544Schristos 				setvar(name, NULL, VSTRFIXED|flags);
114861f28255Scgd 			vp = *vpp;	/* the new variable */
114961f28255Scgd 			lvp->text = NULL;
115061f28255Scgd 			lvp->flags = VUNSET;
1151370f00caSkre 			lvp->rfunc = NULL;
115261f28255Scgd 		} else {
115361f28255Scgd 			lvp->text = vp->text;
115461f28255Scgd 			lvp->flags = vp->flags;
1155370f00caSkre 			lvp->v_u = vp->v_u;
115661f28255Scgd 			vp->flags |= VSTRFIXED|VTEXTFIXED;
1157522d1631Skre 			if (flags & (VDOEXPORT | VUNSET))
1158522d1631Skre 				vp->flags &= ~VNOEXPORT;
1159522d1631Skre 			if (vp->flags & VNOEXPORT &&
1160522d1631Skre 			    (flags & (VEXPORT|VDOEXPORT|VUNSET)) == VEXPORT)
11618df083c1Skre 				flags &= ~VEXPORT;
11628df083c1Skre 			if (flags & (VNOEXPORT | VUNSET))
11638df083c1Skre 				vp->flags &= ~VEXPORT;
11648df083c1Skre 			flags &= ~VNOEXPORT;
11655f9b9101Sdsl 			if (name[vp->name_len] == '=')
11668df083c1Skre 				setvareq(savestr(name), flags & ~VUNSET);
11678df083c1Skre 			else if (flags & VUNSET)
11688df083c1Skre 				unsetvar(name, 0);
11698df083c1Skre 			else
11708df083c1Skre 				vp->flags |= flags & (VUNSET|VEXPORT);
1171727a69dcSkre 
1172727a69dcSkre 			if (vp == &line_num) {
1173727a69dcSkre 				if (name[vp->name_len] == '=')
1174727a69dcSkre 					funclinebase = funclineabs -1;
1175727a69dcSkre 				else
1176727a69dcSkre 					funclinebase = 0;
1177727a69dcSkre 			}
117861f28255Scgd 		}
117961f28255Scgd 	}
118061f28255Scgd 	lvp->vp = vp;
118161f28255Scgd 	lvp->next = localvars;
118261f28255Scgd 	localvars = lvp;
118361f28255Scgd 	INTON;
118461f28255Scgd }
118561f28255Scgd 
118661f28255Scgd 
118761f28255Scgd /*
118861f28255Scgd  * Called after a function returns.
118961f28255Scgd  */
119061f28255Scgd 
119161f28255Scgd void
1192c02b3bbdSchristos poplocalvars(void)
1193c02b3bbdSchristos {
119461f28255Scgd 	struct localvar *lvp;
119561f28255Scgd 	struct var *vp;
119661f28255Scgd 
119761f28255Scgd 	while ((lvp = localvars) != NULL) {
119861f28255Scgd 		localvars = lvp->next;
119961f28255Scgd 		vp = lvp->vp;
1200522d1631Skre 		VTRACE(DBG_VARS, ("poplocalvar %s\n", vp ? vp->text : "-"));
120161f28255Scgd 		if (vp == NULL) {	/* $- saved */
1202c02b3bbdSchristos 			memcpy(optlist, lvp->text, sizeof_optlist);
120361f28255Scgd 			ckfree(lvp->text);
1204ffc64c63Skre 			xtrace_pop();
1205727a69dcSkre 			optschanged();
120661f28255Scgd 		} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
1207c02b3bbdSchristos 			(void)unsetvar(vp->text, 0);
120861f28255Scgd 		} else {
1209370f00caSkre 			if (lvp->func && (lvp->flags & (VNOFUNC|VFUNCREF)) == 0)
1210d73cf48cSkre 				(*lvp->func)(lvp->text + vp->name_len + 1,
1211d73cf48cSkre 				    lvp->flags);
121261f28255Scgd 			if ((vp->flags & VTEXTFIXED) == 0)
121361f28255Scgd 				ckfree(vp->text);
121461f28255Scgd 			vp->flags = lvp->flags;
121561f28255Scgd 			vp->text = lvp->text;
1216370f00caSkre 			vp->v_u = lvp->v_u;
121761f28255Scgd 		}
121861f28255Scgd 		ckfree(lvp);
121961f28255Scgd 	}
122061f28255Scgd }
122161f28255Scgd 
122261f28255Scgd 
12234ce0d34aScgd int
1224c02b3bbdSchristos setvarcmd(int argc, char **argv)
12254ce0d34aScgd {
122661f28255Scgd 	if (argc <= 2)
122761f28255Scgd 		return unsetcmd(argc, argv);
122861f28255Scgd 	else if (argc == 3)
122961f28255Scgd 		setvar(argv[1], argv[2], 0);
123061f28255Scgd 	else
123161f28255Scgd 		error("List assignment not implemented");
123261f28255Scgd 	return 0;
123361f28255Scgd }
123461f28255Scgd 
123561f28255Scgd 
123661f28255Scgd /*
123761f28255Scgd  * The unset builtin command.  We unset the function before we unset the
123861f28255Scgd  * variable to allow a function to be unset when there is a readonly variable
123961f28255Scgd  * with the same name.
124061f28255Scgd  */
124161f28255Scgd 
12424ce0d34aScgd int
1243c02b3bbdSchristos unsetcmd(int argc, char **argv)
12444ce0d34aScgd {
124561f28255Scgd 	char **ap;
124637ed7877Sjtc 	int i;
124737ed7877Sjtc 	int flg_func = 0;
124837ed7877Sjtc 	int flg_var = 0;
12498df083c1Skre 	int flg_x = 0;
125037ed7877Sjtc 	int ret = 0;
125161f28255Scgd 
12528df083c1Skre 	while ((i = nextopt("efvx")) != '\0') {
12538df083c1Skre 		switch (i) {
12548df083c1Skre 		case 'f':
125537ed7877Sjtc 			flg_func = 1;
12568df083c1Skre 			break;
12578df083c1Skre 		case 'e':
12588df083c1Skre 		case 'x':
12598df083c1Skre 			flg_x = (2 >> (i == 'e'));
12608df083c1Skre 			/* FALLTHROUGH */
12618df083c1Skre 		case 'v':
12628df083c1Skre 			flg_var = 1;
12638df083c1Skre 			break;
126461f28255Scgd 		}
12658df083c1Skre 	}
12668df083c1Skre 
126737ed7877Sjtc 	if (flg_func == 0 && flg_var == 0)
126837ed7877Sjtc 		flg_var = 1;
126937ed7877Sjtc 
127037ed7877Sjtc 	for (ap = argptr; *ap ; ap++) {
127137ed7877Sjtc 		if (flg_func)
127237ed7877Sjtc 			ret |= unsetfunc(*ap);
127337ed7877Sjtc 		if (flg_var)
12748df083c1Skre 			ret |= unsetvar(*ap, flg_x);
127537ed7877Sjtc 	}
127637ed7877Sjtc 	return ret;
127761f28255Scgd }
127861f28255Scgd 
127961f28255Scgd 
128061f28255Scgd /*
128161f28255Scgd  * Unset the specified variable.
128261f28255Scgd  */
128361f28255Scgd 
1284beb57fb3Schristos int
1285c02b3bbdSchristos unsetvar(const char *s, int unexport)
128661f28255Scgd {
128761f28255Scgd 	struct var **vpp;
128861f28255Scgd 	struct var *vp;
128961f28255Scgd 
12905f9b9101Sdsl 	vp = find_var(s, &vpp, NULL);
12915f9b9101Sdsl 	if (vp == NULL)
12925c83aa64Schristos 		return 0;
12935f9b9101Sdsl 
12948df083c1Skre 	if (vp->flags & VREADONLY && !(unexport & 1))
12955c83aa64Schristos 		return 1;
12965f9b9101Sdsl 
129761f28255Scgd 	INTOFF;
12988df083c1Skre 	if (unexport & 1) {
1299c02b3bbdSchristos 		vp->flags &= ~VEXPORT;
1300c02b3bbdSchristos 	} else {
1301d73cf48cSkre 		if (vp->text[vp->name_len + 1] != '\0' || !(vp->flags & VUNSET))
1302d73cf48cSkre 			setvar(s, nullstr, VUNSET);
13038df083c1Skre 		if (!(unexport & 2))
130461f28255Scgd 			vp->flags &= ~VEXPORT;
130561f28255Scgd 		vp->flags |= VUNSET;
13068df083c1Skre 		if ((vp->flags&(VEXPORT|VSTRFIXED|VREADONLY|VNOEXPORT)) == 0) {
130761f28255Scgd 			if ((vp->flags & VTEXTFIXED) == 0)
130861f28255Scgd 				ckfree(vp->text);
130961f28255Scgd 			*vpp = vp->next;
131061f28255Scgd 			ckfree(vp);
131161f28255Scgd 		}
1312c02b3bbdSchristos 	}
131361f28255Scgd 	INTON;
13145f9b9101Sdsl 	return 0;
131561f28255Scgd }
131661f28255Scgd 
131761f28255Scgd 
131861f28255Scgd /*
13196a1a2ce2Skre  * Returns true if the two strings specify the same variable.  The first
132061f28255Scgd  * variable name is terminated by '='; the second may be terminated by
132161f28255Scgd  * either '=' or '\0'.
132261f28255Scgd  */
132361f28255Scgd 
132461f28255Scgd STATIC int
13255f9b9101Sdsl strequal(const char *p, const char *q)
132661f28255Scgd {
132761f28255Scgd 	while (*p == *q++) {
132861f28255Scgd 		if (*p++ == '=')
132961f28255Scgd 			return 1;
133061f28255Scgd 	}
133161f28255Scgd 	if (*p == '=' && *(q - 1) == '\0')
133261f28255Scgd 		return 1;
133361f28255Scgd 	return 0;
133461f28255Scgd }
13355f9b9101Sdsl 
13365f9b9101Sdsl /*
13375f9b9101Sdsl  * Search for a variable.
13385f9b9101Sdsl  * 'name' may be terminated by '=' or a NUL.
13395f9b9101Sdsl  * vppp is set to the pointer to vp, or the list head if vp isn't found
1340522d1631Skre  * lenp is set to the number of characters in 'name'
13415f9b9101Sdsl  */
13425f9b9101Sdsl 
13435f9b9101Sdsl STATIC struct var *
13445f9b9101Sdsl find_var(const char *name, struct var ***vppp, int *lenp)
13455f9b9101Sdsl {
13465f9b9101Sdsl 	unsigned int hashval;
13475f9b9101Sdsl 	int len;
13485f9b9101Sdsl 	struct var *vp, **vpp;
13495f9b9101Sdsl 	const char *p = name;
13505f9b9101Sdsl 
13515f9b9101Sdsl 	hashval = 0;
13525f9b9101Sdsl 	while (*p && *p != '=')
13535f9b9101Sdsl 		hashval = 2 * hashval + (unsigned char)*p++;
13545f9b9101Sdsl 
1355522d1631Skre 	len = p - name;
13565f9b9101Sdsl 	if (lenp)
13575f9b9101Sdsl 		*lenp = len;
1358522d1631Skre 
1359d9eb0136Skre 	if (len == 0)
1360d9eb0136Skre 		return NULL;
1361d9eb0136Skre 
13625f9b9101Sdsl 	vpp = &vartab[hashval % VTABSIZE];
13635f9b9101Sdsl 	if (vppp)
13645f9b9101Sdsl 		*vppp = vpp;
13655f9b9101Sdsl 
13665f9b9101Sdsl 	for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) {
13675f9b9101Sdsl 		if (vp->name_len != len)
13685f9b9101Sdsl 			continue;
13695f9b9101Sdsl 		if (memcmp(vp->text, name, len) != 0)
13705f9b9101Sdsl 			continue;
13715f9b9101Sdsl 		if (vppp)
13725f9b9101Sdsl 			*vppp = vpp;
13735f9b9101Sdsl 		return vp;
13745f9b9101Sdsl 	}
13755f9b9101Sdsl 	return NULL;
13765f9b9101Sdsl }
1377727a69dcSkre 
1378dc833851Skre /*
1379dc833851Skre  * The following are the functions that create the values for
1380dc833851Skre  * shell variables that are dynamically produced when needed.
1381dc833851Skre  *
1382dc833851Skre  * The output strings cannot be malloc'd as there is nothing to
1383dc833851Skre  * free them - callers assume these are ordinary variables where
1384dc833851Skre  * the value returned is vp->text
1385dc833851Skre  *
1386dc833851Skre  * Each function needs its own storage space, as the results are
1387dc833851Skre  * used to create processes' environment, and (if exported) all
1388dc833851Skre  * the values will (might) be needed simultaneously.
1389dc833851Skre  *
1390dc833851Skre  * It is not a problem if a var is updated while nominally in use
1391dc833851Skre  * somewhere, all these are intended to be dynamic, the value they
1392dc833851Skre  * return is not guaranteed, an updated vaue is just as good.
1393dc833851Skre  *
1394dc833851Skre  * So, malloc a single buffer for the result of each function,
1395dc833851Skre  * grow, and even shrink, it as needed, but once we have one that
1396dc833851Skre  * is a suitable size for the actual usage, simply hold it forever.
1397dc833851Skre  *
1398dc833851Skre  * For a SMALL shell we implement only LINENO, none of the others,
1399dc833851Skre  * and give it just a fixed length static buffer for its result.
1400dc833851Skre  */
1401dc833851Skre 
1402dc833851Skre #ifndef SMALL
1403dc833851Skre 
1404dc833851Skre struct space_reserved {		/* record of space allocated for results */
1405dc833851Skre 	char *b;
1406dc833851Skre 	int len;
1407dc833851Skre };
1408dc833851Skre 
1409dc833851Skre /* rough (over-)estimate of the number of bytes needed to hold a number */
1410dc833851Skre static int
1411dc833851Skre digits_in(intmax_t number)
1412dc833851Skre {
1413dc833851Skre 	int res = 0;
1414dc833851Skre 
1415dc833851Skre 	if (number & ~((1LL << 62) - 1))
1416dc833851Skre 		res = 64;	/* enough for 2^200 and a bit more */
1417dc833851Skre 	else if (number & ~((1LL << 32) - 1))
1418dc833851Skre 		res = 20;	/* enough for 2^64 */
1419dc833851Skre 	else if (number & ~((1 << 23) - 1))
1420dc833851Skre 		res = 10;	/* enough for 2^32 */
1421dc833851Skre 	else
1422dc833851Skre 		res = 8;	/* enough for 2^23 or smaller */
1423dc833851Skre 
1424dc833851Skre 	return res;
1425dc833851Skre }
1426dc833851Skre 
1427dc833851Skre static int
1428dc833851Skre make_space(struct space_reserved *m, int bytes)
1429dc833851Skre {
1430dc833851Skre 	void *p;
1431dc833851Skre 
1432dc833851Skre 	if (m->len >= bytes && m->len <= (bytes<<2))
1433dc833851Skre 		return 1;
1434dc833851Skre 
1435dc833851Skre 	bytes = SHELL_ALIGN(bytes);
1436e8999de4Skre 	INTOFF;
1437dc833851Skre 	/* not ckrealloc() - we want failure, not error() here */
1438dc833851Skre 	p = realloc(m->b, bytes);
1439e8999de4Skre 	if (p != NULL) {
1440dc833851Skre 		m->b = p;
1441dc833851Skre 		m->len = bytes;
1442dc833851Skre 		m->b[bytes - 1] = '\0';
1443e8999de4Skre 	}
1444e8999de4Skre 	INTON;
1445dc833851Skre 
1446e8999de4Skre 	return p != NULL;
1447dc833851Skre }
1448dc833851Skre #endif
1449dc833851Skre 
1450727a69dcSkre char *
1451727a69dcSkre get_lineno(struct var *vp)
1452727a69dcSkre {
1453dc833851Skre #ifdef SMALL
1454dc833851Skre #define length (8 + 10)		/* 10 digits is enough for a 32 bit line num */
1455dc833851Skre 	static char result[length];
1456dc833851Skre #else
1457dc833851Skre 	static struct space_reserved buf;
1458dc833851Skre #define result buf.b
1459dc833851Skre #define length buf.len
1460dc833851Skre #endif
1461727a69dcSkre 	int ln = line_number;
1462727a69dcSkre 
1463727a69dcSkre 	if (vp->flags & VUNSET)
1464727a69dcSkre 		return NULL;
1465727a69dcSkre 
1466727a69dcSkre 	ln -= funclinebase;
1467dc833851Skre 
1468dc833851Skre #ifndef SMALL
1469dc833851Skre 	if (!make_space(&buf, vp->name_len + 2 + digits_in(ln)))
1470dc833851Skre 		return vp->text;
1471dc833851Skre #endif
1472dc833851Skre 
1473dd761e12Skre 	snprintf(result, length, "%.*s=%d", vp->name_len, vp->text, ln);
1474dc833851Skre 	return result;
1475727a69dcSkre }
1476dc833851Skre #undef result
1477dc833851Skre #undef length
1478dc833851Skre 
1479dc833851Skre #ifndef SMALL
1480dc833851Skre 
1481dc833851Skre char *
1482dc833851Skre get_hostname(struct var *vp)
1483dc833851Skre {
1484dc833851Skre 	static struct space_reserved buf;
1485dc833851Skre 
1486dc833851Skre 	if (vp->flags & VUNSET)
1487dc833851Skre 		return NULL;
1488dc833851Skre 
1489dc833851Skre 	if (!make_space(&buf, vp->name_len + 2 + 256))
1490dc833851Skre 		return vp->text;
1491dc833851Skre 
1492dc833851Skre 	memcpy(buf.b, vp->text, vp->name_len + 1);	/* include '=' */
1493dc833851Skre 	(void)gethostname(buf.b + vp->name_len + 1,
1494dc833851Skre 	    buf.len - vp->name_len - 3);
1495dc833851Skre 	return buf.b;
1496dc833851Skre }
1497dc833851Skre 
1498dc833851Skre char *
1499dc833851Skre get_tod(struct var *vp)
1500dc833851Skre {
1501dc833851Skre 	static struct space_reserved buf;	/* space for answers */
1502dc833851Skre 	static struct space_reserved tzs;	/* remember TZ last used */
1503dc833851Skre 	static timezone_t last_zone;		/* timezone data for tzs zone */
1504dc833851Skre 	const char *fmt;
1505dc833851Skre 	char *tz;
1506dc833851Skre 	time_t now;
1507dc833851Skre 	struct tm tm_now, *tmp;
1508dc833851Skre 	timezone_t zone = NULL;
1509dc833851Skre 	static char t_err[] = "time error";
1510dc833851Skre 	int len;
1511dc833851Skre 
1512dc833851Skre 	if (vp->flags & VUNSET)
1513dc833851Skre 		return NULL;
1514dc833851Skre 
1515dc833851Skre 	fmt = lookupvar("ToD_FORMAT");
1516dc833851Skre 	if (fmt == NULL)
1517dc833851Skre 		fmt="%T";
1518dc833851Skre 	tz = lookupvar("TZ");
1519dc833851Skre 	(void)time(&now);
1520dc833851Skre 
1521dc833851Skre 	if (tz != NULL) {
1522dc833851Skre 		if (tzs.b == NULL || strcmp(tzs.b, tz) != 0) {
1523dc833851Skre 			INTOFF;
1524e8999de4Skre 			if (make_space(&tzs, strlen(tz) + 1)) {
1525dc833851Skre 				strcpy(tzs.b, tz);
1526dc833851Skre 				if (last_zone)
1527dc833851Skre 					tzfree(last_zone);
1528dc833851Skre 				last_zone = zone = tzalloc(tz);
1529dc833851Skre 				INTON;
1530dc833851Skre 			} else
1531dc833851Skre 				zone = tzalloc(tz);
1532dc833851Skre 		} else
1533dc833851Skre 			zone = last_zone;
1534dc833851Skre 
1535dc833851Skre 		tmp = localtime_rz(zone, &now, &tm_now);
1536dc833851Skre 	} else
1537dc833851Skre 		tmp = localtime_r(&now, &tm_now);
1538dc833851Skre 
1539dc833851Skre 	len = (strlen(fmt) * 4) + vp->name_len + 2;
1540dc833851Skre 	while (make_space(&buf, len)) {
1541dc833851Skre 		memcpy(buf.b, vp->text, vp->name_len+1);
1542dc833851Skre 		if (tmp == NULL) {
1543dc833851Skre 			if (buf.len >= vp->name_len+2+(int)(sizeof t_err - 1)) {
1544dc833851Skre 				strcpy(buf.b + vp->name_len + 1, t_err);
1545e8999de4Skre 				if (zone && zone != last_zone) {
1546dc833851Skre 					tzfree(zone);
1547e8999de4Skre 					INTON;
1548e8999de4Skre 				}
1549dc833851Skre 				return buf.b;
1550dc833851Skre 			}
1551dc833851Skre 			len = vp->name_len + 4 + sizeof t_err - 1;
1552dc833851Skre 			continue;
1553dc833851Skre 		}
1554*ccb9728fSkre 		if (zone != NULL) {
1555dc833851Skre 			if (strftime_z(zone, buf.b + vp->name_len + 1,
1556dc833851Skre 			     buf.len - vp->name_len - 2, fmt, tmp)) {
1557*ccb9728fSkre 				if (zone != last_zone) {
1558dc833851Skre 					tzfree(zone);
1559e8999de4Skre 					INTON;
1560e8999de4Skre 				}
1561dc833851Skre 				return buf.b;
1562dc833851Skre 			}
1563*ccb9728fSkre 		} else if (strftime(buf.b + vp->name_len + 1,
1564*ccb9728fSkre 			    buf.len - vp->name_len - 2, fmt, tmp))
1565*ccb9728fSkre 				return buf.b;
1566*ccb9728fSkre 
1567dc833851Skre 		if (len >= 4096)	/* Let's be reasonable */
1568dc833851Skre 			break;
1569dc833851Skre 		len <<= 1;
1570dc833851Skre 	}
1571e8999de4Skre 	if (zone && zone != last_zone) {
1572dc833851Skre 		tzfree(zone);
1573e8999de4Skre 		INTON;
1574e8999de4Skre 	}
1575dc833851Skre 	return vp->text;
1576dc833851Skre }
1577dc833851Skre 
1578dc833851Skre char *
1579dc833851Skre get_seconds(struct var *vp)
1580dc833851Skre {
1581dc833851Skre 	static struct space_reserved buf;
1582dc833851Skre 	intmax_t secs;
1583dc833851Skre 
1584dc833851Skre 	if (vp->flags & VUNSET)
1585dc833851Skre 		return NULL;
1586dc833851Skre 
1587dc833851Skre 	secs = (intmax_t)time((time_t *)0) - sh_start_time;
1588dc833851Skre 	if (!make_space(&buf, vp->name_len + 2 + digits_in(secs)))
1589dc833851Skre 		return vp->text;
1590dc833851Skre 
1591dd761e12Skre 	snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text, secs);
1592dc833851Skre 	return buf.b;
1593dc833851Skre }
1594dc833851Skre 
1595dc833851Skre char *
1596dc833851Skre get_euser(struct var *vp)
1597dc833851Skre {
1598dc833851Skre 	static struct space_reserved buf;
1599dc833851Skre 	static uid_t lastuid = 0;
1600dc833851Skre 	uid_t euid;
1601dc833851Skre 	struct passwd *pw;
1602dc833851Skre 
1603dc833851Skre 	if (vp->flags & VUNSET)
1604dc833851Skre 		return NULL;
1605dc833851Skre 
1606dc833851Skre 	euid = geteuid();
1607dc833851Skre 	if (buf.b != NULL && lastuid == euid)
1608dc833851Skre 		return buf.b;
1609dc833851Skre 
1610dc833851Skre 	pw = getpwuid(euid);
1611dc833851Skre 	if (pw == NULL)
1612dc833851Skre 		return vp->text;
1613dc833851Skre 
1614dc833851Skre 	if (make_space(&buf, vp->name_len + 2 + strlen(pw->pw_name))) {
1615e8999de4Skre 		INTOFF;
1616dc833851Skre 		lastuid = euid;
1617dc833851Skre 		snprintf(buf.b, buf.len, "%.*s=%s", vp->name_len, vp->text,
1618dc833851Skre 		    pw->pw_name);
1619e8999de4Skre 		INTON;
1620dc833851Skre 		return buf.b;
1621dc833851Skre 	}
1622dc833851Skre 
1623dc833851Skre 	return vp->text;
1624dc833851Skre }
1625dc833851Skre 
1626dc833851Skre char *
1627dc833851Skre get_random(struct var *vp)
1628dc833851Skre {
1629dc833851Skre 	static struct space_reserved buf;
1630dc833851Skre 	static intmax_t random_val = 0;
1631dc833851Skre 
1632dc833851Skre #ifdef USE_LRAND48
1633dc833851Skre #define random lrand48
1634dc833851Skre #define srandom srand48
1635dc833851Skre #endif
1636dc833851Skre 
1637dc833851Skre 	if (vp->flags & VUNSET)
1638dc833851Skre 		return NULL;
1639dc833851Skre 
1640dc833851Skre 	if (vp->text != buf.b) {
1641dc833851Skre 		/*
1642dc833851Skre 		 * Either initialisation, or a new seed has been set
1643dc833851Skre 		 */
1644dc833851Skre 		if (vp->text[vp->name_len + 1] == '\0') {
1645dc833851Skre 			int fd;
1646dc833851Skre 
1647dc833851Skre 			/*
1648dc833851Skre 			 * initialisation (without pre-seeding),
1649f1229b53Sandvar 			 * or explicitly requesting a truly random seed.
1650dc833851Skre 			 */
1651e8999de4Skre 			INTOFF;
1652dc833851Skre 			fd = open("/dev/urandom", 0);
1653dc833851Skre 			if (fd == -1) {
1654dc833851Skre 				out2str("RANDOM initialisation failed\n");
1655dc833851Skre 				random_val = (getpid()<<3) ^ time((time_t *)0);
1656dc833851Skre 			} else {
1657dc833851Skre 				int n;
1658dc833851Skre 
1659dc833851Skre 				do {
1660dc833851Skre 				    n = read(fd,&random_val,sizeof random_val);
1661dc833851Skre 				} while (n != sizeof random_val);
1662dc833851Skre 				close(fd);
1663dc833851Skre 			}
1664e8999de4Skre 			INTON;
1665dc833851Skre 		} else
1666dc833851Skre 			/* good enough for today */
1667c6c29888Skre 			random_val = strtoimax(vp->text+vp->name_len+1,NULL,0);
1668dc833851Skre 
1669dc833851Skre 		srandom((long)random_val);
1670dc833851Skre 	}
1671dc833851Skre 
1672dc833851Skre #if 0
1673dc833851Skre 	random_val = (random_val + 1) & 0x7FFF;	/* 15 bit "random" numbers */
1674dc833851Skre #else
1675dc833851Skre 	random_val = (random() >> 5) & 0x7FFF;
1676dc833851Skre #endif
1677dc833851Skre 
1678dc833851Skre 	if (!make_space(&buf, vp->name_len + 2 + digits_in(random_val)))
1679dc833851Skre 		return vp->text;
1680dc833851Skre 
1681dd761e12Skre 	snprintf(buf.b, buf.len, "%.*s=%jd", vp->name_len, vp->text,
1682dc833851Skre 	    random_val);
1683dc833851Skre 
1684e8999de4Skre 	INTOFF;
1685dc833851Skre 	if (buf.b != vp->text && (vp->flags & (VTEXTFIXED|VSTACK)) == 0)
1686dc833851Skre 		free(vp->text);
1687dc833851Skre 	vp->flags |= VTEXTFIXED;
1688dc833851Skre 	vp->text = buf.b;
1689e8999de4Skre 	INTON;
1690dc833851Skre 
1691dc833851Skre 	return vp->text;
1692dc833851Skre #undef random
1693dc833851Skre #undef srandom
1694dc833851Skre }
1695dc833851Skre 
1696727a664bSkre STATIC int
1697727a664bSkre makespecial(const char *name)
1698727a664bSkre {
1699727a664bSkre 	const struct varinit *ip;
1700727a664bSkre 	struct var *vp;
1701727a664bSkre 
1702727a664bSkre 	CTRACE(DBG_VARS, ("makespecial('%s') -> ", name));
1703727a664bSkre 	for (ip = varinit ; (vp = ip->var) != NULL ; ip++) {
1704727a664bSkre 		if (strequal(ip->text, name)) {
1705727a664bSkre 			if (!(ip->flags & VFUNCREF)) {
1706727a664bSkre 				CTRACE(DBG_VARS, ("+1\n"));
1707727a664bSkre 				return 1;
1708727a664bSkre 			}
1709727a664bSkre 			INTOFF;
1710727a664bSkre 			vp->flags &= ~VUNSET;
1711727a664bSkre 			vp->v_u = ip->v_u;
1712727a664bSkre 			INTON;
1713727a664bSkre 			CTRACE(DBG_VARS, ("0\n"));
1714727a664bSkre 			return 0;
1715727a664bSkre 		}
1716727a664bSkre 	}
1717727a664bSkre 	CTRACE(DBG_VARS, ("1\n"));
1718727a664bSkre 	return 1;
1719727a664bSkre }
1720727a664bSkre 
1721727a664bSkre int
1722727a664bSkre specialvarcmd(int argc, char **argv)
1723727a664bSkre {
1724727a664bSkre 	int res = 0;
1725727a664bSkre 	char **ap;
1726727a664bSkre 
1727727a664bSkre 	(void) nextopt("");
1728727a664bSkre 
1729727a664bSkre 	if (!*argptr)
1730727a664bSkre 		error("Usage: specialvar var...");
1731727a664bSkre 
1732727a664bSkre 	for (ap = argptr; *ap ; ap++)
1733727a664bSkre 		res |= makespecial(*ap);
1734727a664bSkre 
1735727a664bSkre 	return res;
1736727a664bSkre }
1737727a664bSkre 
1738dc833851Skre #endif /* SMALL */
1739