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