xref: /netbsd-src/bin/sh/memalloc.c (revision 5409234e44ec847f3b3aaa02df39a8b6420ee78c)
1*5409234eSkre /*	$NetBSD: memalloc.c,v 1.39 2023/04/07 10:42: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[] = "@(#)memalloc.c	8.3 (Berkeley) 5/4/95";
3949f0ad86Scgd #else
40*5409234eSkre __RCSID("$NetBSD: memalloc.c,v 1.39 2023/04/07 10:42:28 kre Exp $");
4149f0ad86Scgd #endif
4261f28255Scgd #endif /* not lint */
4361f28255Scgd 
444ca0efa9Skre #include <limits.h>
454ca0efa9Skre #include <stdarg.h>
467faabd58Schristos #include <stdlib.h>
477faabd58Schristos #include <unistd.h>
487faabd58Schristos 
4961f28255Scgd #include "shell.h"
5061f28255Scgd #include "output.h"
5161f28255Scgd #include "memalloc.h"
5261f28255Scgd #include "error.h"
5361f28255Scgd #include "machdep.h"
5461f28255Scgd #include "mystring.h"
5561f28255Scgd 
5661f28255Scgd /*
5761f28255Scgd  * Like malloc, but returns an error when out of space.
5861f28255Scgd  */
5961f28255Scgd 
6061f28255Scgd pointer
ckmalloc(size_t nbytes)614498b1feSmatt ckmalloc(size_t nbytes)
625dad1439Scgd {
6348250187Stls 	pointer p;
6461f28255Scgd 
658f483589Smycroft 	p = malloc(nbytes);
668f483589Smycroft 	if (p == NULL)
6761f28255Scgd 		error("Out of space");
6861f28255Scgd 	return p;
6961f28255Scgd }
7061f28255Scgd 
7161f28255Scgd 
7261f28255Scgd /*
7361f28255Scgd  * Same for realloc.
7461f28255Scgd  */
7561f28255Scgd 
7661f28255Scgd pointer
ckrealloc(pointer p,int nbytes)77c02b3bbdSchristos ckrealloc(pointer p, int nbytes)
7861f28255Scgd {
791ce87c4bSchristos 	p = realloc(p, nbytes);
801ce87c4bSchristos 	if (p == NULL)
8161f28255Scgd 		error("Out of space");
8261f28255Scgd 	return p;
8361f28255Scgd }
8461f28255Scgd 
8561f28255Scgd 
8661f28255Scgd /*
8761f28255Scgd  * Make a copy of a string in safe storage.
8861f28255Scgd  */
8961f28255Scgd 
9061f28255Scgd char *
savestr(const char * s)91e314f958Sdsl savestr(const char *s)
9261f28255Scgd {
9348250187Stls 	char *p;
9461f28255Scgd 
9561f28255Scgd 	p = ckmalloc(strlen(s) + 1);
9661f28255Scgd 	scopy(s, p);
9761f28255Scgd 	return p;
9861f28255Scgd }
9961f28255Scgd 
10061f28255Scgd 
10161f28255Scgd /*
10261f28255Scgd  * Parse trees for commands are allocated in lifo order, so we use a stack
10361f28255Scgd  * to make this more efficient, and also to avoid all sorts of exception
10461f28255Scgd  * handling code to handle interrupts in the middle of a parse.
1051c21834cSmycroft  *
1061c21834cSmycroft  * The size 504 was chosen because the Ultrix malloc handles that size
1071c21834cSmycroft  * well.
10861f28255Scgd  */
10961f28255Scgd 
1101c21834cSmycroft #define MINSIZE 504		/* minimum size of a block */
11161f28255Scgd 
11261f28255Scgd struct stack_block {
11361f28255Scgd 	struct stack_block *prev;
11461f28255Scgd 	char space[MINSIZE];
11561f28255Scgd };
11661f28255Scgd 
11761f28255Scgd struct stack_block stackbase;
11861f28255Scgd struct stack_block *stackp = &stackbase;
1198e2797bcSchristos struct stackmark *markp;
12061f28255Scgd char *stacknxt = stackbase.space;
12161f28255Scgd int stacknleft = MINSIZE;
12261f28255Scgd int sstrnleft;
12361f28255Scgd int herefd = -1;
12461f28255Scgd 
12561f28255Scgd pointer
stalloc(int nbytes)126c02b3bbdSchristos stalloc(int nbytes)
1275dad1439Scgd {
12848250187Stls 	char *p;
12961f28255Scgd 
130b9d3050eSchristos 	nbytes = SHELL_ALIGN(nbytes);
1311c21834cSmycroft 	if (nbytes > stacknleft) {
13261f28255Scgd 		int blocksize;
13361f28255Scgd 		struct stack_block *sp;
13461f28255Scgd 
1351c21834cSmycroft 		blocksize = nbytes;
13661f28255Scgd 		if (blocksize < MINSIZE)
13761f28255Scgd 			blocksize = MINSIZE;
13861f28255Scgd 		INTOFF;
13961f28255Scgd 		sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
14061f28255Scgd 		sp->prev = stackp;
14161f28255Scgd 		stacknxt = sp->space;
14261f28255Scgd 		stacknleft = blocksize;
14361f28255Scgd 		stackp = sp;
14461f28255Scgd 		INTON;
14561f28255Scgd 	}
146e8999de4Skre 	INTOFF;
14761f28255Scgd 	p = stacknxt;
1481c21834cSmycroft 	stacknxt += nbytes;
1491c21834cSmycroft 	stacknleft -= nbytes;
150e8999de4Skre 	INTON;
15161f28255Scgd 	return p;
15261f28255Scgd }
15361f28255Scgd 
15461f28255Scgd 
15561f28255Scgd void
stunalloc(pointer p)156c02b3bbdSchristos stunalloc(pointer p)
15761f28255Scgd {
15861f28255Scgd 	if (p == NULL) {		/*DEBUG */
15961f28255Scgd 		write(2, "stunalloc\n", 10);
16061f28255Scgd 		abort();
16161f28255Scgd 	}
16261f28255Scgd 	stacknleft += stacknxt - (char *)p;
16361f28255Scgd 	stacknxt = p;
16461f28255Scgd }
16561f28255Scgd 
16661f28255Scgd 
16770696c01Skre /* save the current status of the sh stack */
16861f28255Scgd void
setstackmark(struct stackmark * mark)169c02b3bbdSchristos setstackmark(struct stackmark *mark)
17061f28255Scgd {
17161f28255Scgd 	mark->stackp = stackp;
17261f28255Scgd 	mark->stacknxt = stacknxt;
17361f28255Scgd 	mark->stacknleft = stacknleft;
174ee3b307fSkre 	mark->sstrnleft = sstrnleft;
1758e2797bcSchristos 	mark->marknext = markp;
1768e2797bcSchristos 	markp = mark;
17761f28255Scgd }
17861f28255Scgd 
17970696c01Skre /* reset the stack mark, and remove it from the list of marks */
18061f28255Scgd void
popstackmark(struct stackmark * mark)181c02b3bbdSchristos popstackmark(struct stackmark *mark)
18261f28255Scgd {
183e8999de4Skre 	INTOFF;
18470696c01Skre 	markp = mark->marknext;		/* delete mark from the list */
18570696c01Skre 	rststackmark(mark);		/* and reset stack */
186e8999de4Skre 	INTON;
18770696c01Skre }
18870696c01Skre 
18970696c01Skre /* reset the shell stack to its state recorded in the stack mark */
19070696c01Skre void
rststackmark(struct stackmark * mark)19170696c01Skre rststackmark(struct stackmark *mark)
19270696c01Skre {
19361f28255Scgd 	struct stack_block *sp;
19461f28255Scgd 
19561f28255Scgd 	INTOFF;
19661f28255Scgd 	while (stackp != mark->stackp) {
19770696c01Skre 		/* delete any recently allocated mem blocks */
19861f28255Scgd 		sp = stackp;
19961f28255Scgd 		stackp = sp->prev;
20061f28255Scgd 		ckfree(sp);
20161f28255Scgd 	}
20261f28255Scgd 	stacknxt = mark->stacknxt;
20361f28255Scgd 	stacknleft = mark->stacknleft;
204ee3b307fSkre 	sstrnleft = mark->sstrnleft;
20561f28255Scgd 	INTON;
20661f28255Scgd }
20761f28255Scgd 
20861f28255Scgd 
20961f28255Scgd /*
21061f28255Scgd  * When the parser reads in a string, it wants to stick the string on the
21161f28255Scgd  * stack and only adjust the stack pointer when it knows how big the
21261f28255Scgd  * string is.  Stackblock (defined in stack.h) returns a pointer to a block
21361f28255Scgd  * of space on top of the stack and stackblocklen returns the length of
21461f28255Scgd  * this block.  Growstackblock will grow this space by at least one byte,
21561f28255Scgd  * possibly moving it (like realloc).  Grabstackblock actually allocates the
21661f28255Scgd  * part of the block that has been used.
21761f28255Scgd  */
21861f28255Scgd 
21961f28255Scgd void
growstackblock(void)220c02b3bbdSchristos growstackblock(void)
2211ce87c4bSchristos {
222b9d3050eSchristos 	int newlen = SHELL_ALIGN(stacknleft * 2 + 100);
22361f28255Scgd 
224e442a1a0Skre 	INTOFF;
22561f28255Scgd 	if (stacknxt == stackp->space && stackp != &stackbase) {
2261ce87c4bSchristos 		struct stack_block *oldstackp;
2271ce87c4bSchristos 		struct stackmark *xmark;
2281ce87c4bSchristos 		struct stack_block *sp;
2291ce87c4bSchristos 
2308e2797bcSchristos 		oldstackp = stackp;
23161f28255Scgd 		sp = stackp;
23261f28255Scgd 		stackp = sp->prev;
2331ce87c4bSchristos 		sp = ckrealloc((pointer)sp,
2341ce87c4bSchristos 		    sizeof(struct stack_block) - MINSIZE + newlen);
23561f28255Scgd 		sp->prev = stackp;
23661f28255Scgd 		stackp = sp;
23761f28255Scgd 		stacknxt = sp->space;
238e442a1a0Skre 		sstrnleft += newlen - stacknleft;
23961f28255Scgd 		stacknleft = newlen;
2401ce87c4bSchristos 
2411ce87c4bSchristos 		/*
2421ce87c4bSchristos 		 * Stack marks pointing to the start of the old block
2438e2797bcSchristos 		 * must be relocated to point to the new block
2448e2797bcSchristos 		 */
2458e2797bcSchristos 		xmark = markp;
2468e2797bcSchristos 		while (xmark != NULL && xmark->stackp == oldstackp) {
2478e2797bcSchristos 			xmark->stackp = stackp;
2488e2797bcSchristos 			xmark->stacknxt = stacknxt;
249e442a1a0Skre 			xmark->sstrnleft += stacknleft - xmark->stacknleft;
2508e2797bcSchristos 			xmark->stacknleft = stacknleft;
2518e2797bcSchristos 			xmark = xmark->marknext;
2528e2797bcSchristos 		}
25361f28255Scgd 	} else {
2541ce87c4bSchristos 		char *oldspace = stacknxt;
2551ce87c4bSchristos 		int oldlen = stacknleft;
2561ce87c4bSchristos 		char *p = stalloc(newlen);
2571ce87c4bSchristos 
2581ce87c4bSchristos 		(void)memcpy(p, oldspace, oldlen);
25961f28255Scgd 		stacknxt = p;			/* free the space */
260edc0ef34Scgd 		stacknleft += newlen;		/* we just allocated */
26161f28255Scgd 	}
262e442a1a0Skre 	INTON;
26361f28255Scgd }
26461f28255Scgd 
26561f28255Scgd void
grabstackblock(int len)266c02b3bbdSchristos grabstackblock(int len)
2675dad1439Scgd {
268b9d3050eSchristos 	len = SHELL_ALIGN(len);
269e8999de4Skre 	INTOFF;
27061f28255Scgd 	stacknxt += len;
27161f28255Scgd 	stacknleft -= len;
272e8999de4Skre 	INTON;
27361f28255Scgd }
27461f28255Scgd 
27561f28255Scgd /*
276c02b3bbdSchristos  * The following routines are somewhat easier to use than the above.
27761f28255Scgd  * The user declares a variable of type STACKSTR, which may be declared
27861f28255Scgd  * to be a register.  The macro STARTSTACKSTR initializes things.  Then
27961f28255Scgd  * the user uses the macro STPUTC to add characters to the string.  In
28061f28255Scgd  * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
28161f28255Scgd  * grown as necessary.  When the user is done, she can just leave the
28261f28255Scgd  * string there and refer to it using stackblock().  Or she can allocate
28361f28255Scgd  * the space for it using grabstackstr().  If it is necessary to allow
28461f28255Scgd  * someone else to use the stack temporarily and then continue to grow
28561f28255Scgd  * the string, the user should use grabstack to allocate the space, and
28661f28255Scgd  * then call ungrabstr(p) to return to the previous mode of operation.
28761f28255Scgd  *
28861f28255Scgd  * USTPUTC is like STPUTC except that it doesn't check for overflow.
28961f28255Scgd  * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
29061f28255Scgd  * is space for at least one character.
29161f28255Scgd  */
29261f28255Scgd 
29361f28255Scgd char *
growstackstr(void)294c02b3bbdSchristos growstackstr(void)
2951ce87c4bSchristos {
29661f28255Scgd 	int len = stackblocksize();
29761f28255Scgd 	if (herefd >= 0 && len >= 1024) {
29861f28255Scgd 		xwrite(herefd, stackblock(), len);
29961f28255Scgd 		sstrnleft = len - 1;
30061f28255Scgd 		return stackblock();
30161f28255Scgd 	}
30261f28255Scgd 	growstackblock();
30361f28255Scgd 	sstrnleft = stackblocksize() - len - 1;
30461f28255Scgd 	return stackblock() + len;
30561f28255Scgd }
30661f28255Scgd 
30761f28255Scgd /*
30861f28255Scgd  * Called from CHECKSTRSPACE.
30961f28255Scgd  */
31061f28255Scgd 
31161f28255Scgd char *
makestrspace(void)312c02b3bbdSchristos makestrspace(void)
3131ce87c4bSchristos {
31461f28255Scgd 	int len = stackblocksize() - sstrnleft;
31561f28255Scgd 	growstackblock();
31661f28255Scgd 	sstrnleft = stackblocksize() - len;
31761f28255Scgd 	return stackblock() + len;
31861f28255Scgd }
31961f28255Scgd 
320ee3b307fSkre /*
321ee3b307fSkre  * Note that this only works to release stack space for reuse
322ee3b307fSkre  * if nothing else has allocated space on the stack since the grabstackstr()
323ee3b307fSkre  *
324ee3b307fSkre  * "s" is the start of the area to be released, and "p" represents the end
325ee3b307fSkre  * of the string we have stored beyond there and are now releasing.
326ee3b307fSkre  * (ie: "p" should be the same as in the call to grabstackstr()).
327ee3b307fSkre  *
328ff23aff6Sandvar  * stunalloc(s) and ungrabstackstr(s, p) are almost interchangeable after
329ee3b307fSkre  * a grabstackstr(), however the latter also returns string space so we
330ee3b307fSkre  * can just continue with STPUTC() etc without needing a new STARTSTACKSTR(s)
331ee3b307fSkre  */
33261f28255Scgd void
ungrabstackstr(char * s,char * p)333c02b3bbdSchristos ungrabstackstr(char *s, char *p)
33461f28255Scgd {
335ee3b307fSkre #ifdef DEBUG
336ee3b307fSkre 	if (s < stacknxt || stacknxt + stacknleft < s)
337ee3b307fSkre 		abort();
338ee3b307fSkre #endif
33961f28255Scgd 	stacknleft += stacknxt - s;
34061f28255Scgd 	stacknxt = s;
34161f28255Scgd 	sstrnleft = stacknleft - (p - s);
34261f28255Scgd }
3434ca0efa9Skre 
3444ca0efa9Skre /*
3454ca0efa9Skre  * Save the concat of a sequence of strings in stack space
3464ca0efa9Skre  *
3474ca0efa9Skre  * The first arg (if not NULL) is a pointer to where the final string
3484ca0efa9Skre  * length will be returned.
3494ca0efa9Skre  *
3504ca0efa9Skre  * Remaining args are pointers to strings - sufficient space to hold
3514ca0efa9Skre  * the concat of the strings is allocated on the stack, the strings
35275d2abaeSandvar  * are copied into that space, and a pointer to its start is returned.
3534a9058f9Skre  * The arg list is terminated with STSTRC_END.
3544ca0efa9Skre  *
3554ca0efa9Skre  * Use stunalloc(string) (in proper sequence) to release the string
3564ca0efa9Skre  */
3574ca0efa9Skre char *
ststrcat(size_t * lp,...)3584ca0efa9Skre ststrcat(size_t *lp, ...)
3594ca0efa9Skre {
3604ca0efa9Skre 	va_list ap;
3614ca0efa9Skre 	const char *arg;
3624ca0efa9Skre 	size_t len, tlen = 0, alen[8];
3634ca0efa9Skre 	char *str, *nxt;
3644ca0efa9Skre 	unsigned int n;
3654ca0efa9Skre 
3664ca0efa9Skre 	n = 0;
3674ca0efa9Skre 	va_start(ap, lp);
3684ca0efa9Skre 	arg = va_arg(ap, const char *);
3694a9058f9Skre 	while (arg != STSTRC_END) {
3704ca0efa9Skre 		len = strlen(arg);
3714ca0efa9Skre 		if (n < sizeof(alen)/sizeof(alen[0]))
3724ca0efa9Skre 			alen[n++] = len;
3734ca0efa9Skre 		tlen += len;
3744ca0efa9Skre 		arg = va_arg(ap, const char *);
3754ca0efa9Skre 	}
3764ca0efa9Skre 	va_end(ap);
3774ca0efa9Skre 
3784ca0efa9Skre 	if (lp != NULL)
3794ca0efa9Skre 		*lp = tlen;
3804ca0efa9Skre 
3814ca0efa9Skre 	if (tlen >= INT_MAX)
3824ca0efa9Skre 		error("ststrcat() over length botch");
3834ca0efa9Skre 	str = (char *)stalloc((int)tlen + 1);	/* 1 for \0 */
3844a9058f9Skre 	str[tlen] = '\0';	/* in case of no args  */
3854ca0efa9Skre 
3864ca0efa9Skre 	n = 0;
3874ca0efa9Skre 	nxt = str;
3884ca0efa9Skre 	va_start(ap, lp);
3894ca0efa9Skre 	arg = va_arg(ap, const char *);
3904a9058f9Skre 	while (arg != STSTRC_END) {
3914ca0efa9Skre 		if (n < sizeof(alen)/sizeof(alen[0]))
3924ca0efa9Skre 			len = alen[n++];
3934ca0efa9Skre 		else
3944ca0efa9Skre 			len = strlen(arg);
3954ca0efa9Skre 
3964ca0efa9Skre 		scopy(arg, nxt);
3974ca0efa9Skre 		nxt += len;
3984ca0efa9Skre 
3994ca0efa9Skre 		arg = va_arg(ap, const char *);
4004ca0efa9Skre 	}
4014ca0efa9Skre 	va_end(ap);
4024ca0efa9Skre 
4034ca0efa9Skre 	return str;
4044ca0efa9Skre }
405