master xplshn/aruu / cmd / posix / sh / memalloc.c
  1/*-
  2 * SPDX-License-Identifier: BSD-3-Clause
  3 *
  4 * Copyright (c) 1991, 1993
  5 *	The Regents of the University of California.  All rights reserved.
  6 *
  7 * This code is derived from software contributed to Berkeley by
  8 * Kenneth Almquist.
  9 *
 10 * Redistribution and use in source and binary forms, with or without
 11 * modification, are permitted provided that the following conditions
 12 * are met:
 13 * 1. Redistributions of source code must retain the above copyright
 14 *    notice, this list of conditions and the following disclaimer.
 15 * 2. Redistributions in binary form must reproduce the above copyright
 16 *    notice, this list of conditions and the following disclaimer in the
 17 *    documentation and/or other materials provided with the distribution.
 18 * 3. Neither the name of the University nor the names of its contributors
 19 *    may be used to endorse or promote products derived from this software
 20 *    without specific prior written permission.
 21 *
 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 32 * SUCH DAMAGE.
 33 */
 34
 35#include <sys/param.h>
 36#include "shell.h"
 37#include "output.h"
 38#include "memalloc.h"
 39#include "error.h"
 40#include "mystring.h"
 41#include "expand.h"
 42#include <stdlib.h>
 43#include <unistd.h>
 44
 45static void
 46badalloc(const char *message)
 47{
 48	write(2, message, strlen(message));
 49	abort();
 50}
 51
 52/*
 53 * Like malloc, but returns an error when out of space.
 54 */
 55
 56pointer
 57ckmalloc(size_t nbytes)
 58{
 59	pointer p;
 60
 61	if (!is_int_on())
 62		badalloc("Unsafe ckmalloc() call\n");
 63	p = malloc(nbytes);
 64	if (p == NULL)
 65		error("Out of space");
 66	return p;
 67}
 68
 69
 70/*
 71 * Same for realloc.
 72 */
 73
 74pointer
 75ckrealloc(pointer p, int nbytes)
 76{
 77	if (!is_int_on())
 78		badalloc("Unsafe ckrealloc() call\n");
 79	p = realloc(p, nbytes);
 80	if (p == NULL)
 81		error("Out of space");
 82	return p;
 83}
 84
 85void
 86ckfree(pointer p)
 87{
 88	if (!is_int_on())
 89		badalloc("Unsafe ckfree() call\n");
 90	free(p);
 91}
 92
 93
 94/*
 95 * Make a copy of a string in safe storage.
 96 */
 97
 98char *
 99savestr(const char *s)
100{
101	char *p;
102	size_t len;
103
104	len = strlen(s);
105	p = ckmalloc(len + 1);
106	memcpy(p, s, len + 1);
107	return p;
108}
109
110
111/*
112 * Parse trees for commands are allocated in lifo order, so we use a stack
113 * to make this more efficient, and also to avoid all sorts of exception
114 * handling code to handle interrupts in the middle of a parse.
115 *
116 * The size 496 was chosen because with 16-byte alignment the total size
117 * for the allocated block is 512.
118 */
119
120#define MINSIZE 496		/* minimum size of a block. */
121
122
123struct stack_block {
124	struct stack_block *prev;
125	/* Data follows */
126};
127#define SPACE(sp)	((char*)(sp) + ALIGN(sizeof(struct stack_block)))
128
129static struct stack_block *stackp;
130char *stacknxt;
131int stacknleft;
132char *sstrend;
133
134
135static void
136stnewblock(int nbytes)
137{
138	struct stack_block *sp;
139	int allocsize;
140
141	if (nbytes < MINSIZE)
142		nbytes = MINSIZE;
143
144	allocsize = ALIGN(sizeof(struct stack_block)) + ALIGN(nbytes);
145
146	INTOFF;
147	sp = ckmalloc(allocsize);
148	sp->prev = stackp;
149	stacknxt = SPACE(sp);
150	stacknleft = allocsize - (stacknxt - (char*)sp);
151	sstrend = stacknxt + stacknleft;
152	stackp = sp;
153	INTON;
154}
155
156
157pointer
158stalloc(int nbytes)
159{
160	char *p;
161
162	nbytes = ALIGN(nbytes);
163	if (nbytes > stacknleft)
164		stnewblock(nbytes);
165	p = stacknxt;
166	stacknxt += nbytes;
167	stacknleft -= nbytes;
168	return p;
169}
170
171
172void
173stunalloc(pointer p)
174{
175	if (p == NULL) {		/*DEBUG */
176		write(STDERR_FILENO, "stunalloc\n", 10);
177		abort();
178	}
179	stacknleft += stacknxt - (char *)p;
180	stacknxt = p;
181}
182
183
184char *
185stsavestr(const char *s)
186{
187	char *p;
188	size_t len;
189
190	len = strlen(s);
191	p = stalloc(len + 1);
192	memcpy(p, s, len + 1);
193	return p;
194}
195
196
197void
198setstackmark(struct stackmark *mark)
199{
200	mark->stackp = stackp;
201	mark->stacknxt = stacknxt;
202	mark->stacknleft = stacknleft;
203	/* Ensure this block stays in place. */
204	if (stackp != NULL && stacknxt == SPACE(stackp))
205		stalloc(1);
206}
207
208
209void
210popstackmark(struct stackmark *mark)
211{
212	struct stack_block *sp;
213
214	INTOFF;
215	while (stackp != mark->stackp) {
216		sp = stackp;
217		stackp = sp->prev;
218		ckfree(sp);
219	}
220	stacknxt = mark->stacknxt;
221	stacknleft = mark->stacknleft;
222	if (stacknleft != 0)
223		sstrend = stacknxt + stacknleft;
224	else
225		sstrend = stacknxt;
226	INTON;
227}
228
229
230/*
231 * When the parser reads in a string, it wants to stick the string on the
232 * stack and only adjust the stack pointer when it knows how big the
233 * string is.  Stackblock (defined in stack.h) returns a pointer to a block
234 * of space on top of the stack and stackblocklen returns the length of
235 * this block.  Growstackblock will grow this space by at least one byte,
236 * possibly moving it (like realloc).  Grabstackblock actually allocates the
237 * part of the block that has been used.
238 */
239
240static void
241growstackblock(int min)
242{
243	char *p;
244	int newlen;
245	char *oldspace;
246	int oldlen;
247	struct stack_block *sp;
248	struct stack_block *oldstackp;
249
250	if (min < stacknleft)
251		min = stacknleft;
252	if ((unsigned int)min >=
253	    INT_MAX / 2 - ALIGN(sizeof(struct stack_block)))
254		error("Out of space");
255	min += stacknleft;
256	min += ALIGN(sizeof(struct stack_block));
257	newlen = 512;
258	while (newlen < min)
259		newlen <<= 1;
260	oldspace = stacknxt;
261	oldlen = stacknleft;
262
263	if (stackp != NULL && stacknxt == SPACE(stackp)) {
264		INTOFF;
265		oldstackp = stackp;
266		stackp = oldstackp->prev;
267		sp = ckrealloc((pointer)oldstackp, newlen);
268		sp->prev = stackp;
269		stackp = sp;
270		stacknxt = SPACE(sp);
271		stacknleft = newlen - (stacknxt - (char*)sp);
272		sstrend = stacknxt + stacknleft;
273		INTON;
274	} else {
275		newlen -= ALIGN(sizeof(struct stack_block));
276		p = stalloc(newlen);
277		if (oldlen != 0)
278			memcpy(p, oldspace, oldlen);
279		stunalloc(p);
280	}
281}
282
283
284/*
285 * The following routines are somewhat easier to use than the above.
286 * The user declares a variable of type STACKSTR, which may be declared
287 * to be a register.  The macro STARTSTACKSTR initializes things.  Then
288 * the user uses the macro STPUTC to add characters to the string.  In
289 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
290 * grown as necessary.  When the user is done, she can just leave the
291 * string there and refer to it using stackblock().  Or she can allocate
292 * the space for it using grabstackstr().  If it is necessary to allow
293 * someone else to use the stack temporarily and then continue to grow
294 * the string, the user should use grabstack to allocate the space, and
295 * then call ungrabstr(p) to return to the previous mode of operation.
296 *
297 * USTPUTC is like STPUTC except that it doesn't check for overflow.
298 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
299 * is space for at least one character.
300 */
301
302static char *
303growstrstackblock(int n, int min)
304{
305	growstackblock(min);
306	return stackblock() + n;
307}
308
309char *
310growstackstr(void)
311{
312	int len;
313
314	len = stackblocksize();
315	return (growstrstackblock(len, 0));
316}
317
318
319/*
320 * Called from CHECKSTRSPACE.
321 */
322
323char *
324makestrspace(int min, char *p)
325{
326	int len;
327
328	len = p - stackblock();
329	return (growstrstackblock(len, min));
330}
331
332
333char *
334stputbin(const char *data, size_t len, char *p)
335{
336	CHECKSTRSPACE(len, p);
337	memcpy(p, data, len);
338	return (p + len);
339}
340
341char *
342stputs(const char *data, char *p)
343{
344	return (stputbin(data, strlen(data), p));
345}