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 "memalloc.h"
36#include "error.h"
37#include "expand.h"
38#include "mystring.h"
39#include "output.h"
40#include "shell.h"
41#include <stdlib.h>
42#include <sys/param.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 * Same for realloc.
71 */
72
73pointer
74ckrealloc(pointer p, int nbytes)
75{
76 if (!is_int_on())
77 badalloc("Unsafe ckrealloc() call\n");
78 p = realloc(p, nbytes);
79 if (p == NULL)
80 error("Out of space");
81 return p;
82}
83
84void
85ckfree(pointer p)
86{
87 if (!is_int_on())
88 badalloc("Unsafe ckfree() call\n");
89 free(p);
90}
91
92/*
93 * Make a copy of a string in safe storage.
94 */
95
96char *
97savestr(const char *s)
98{
99 char *p;
100 size_t len;
101
102 len = strlen(s);
103 p = ckmalloc(len + 1);
104 memcpy(p, s, len + 1);
105 return p;
106}
107
108/*
109 * Parse trees for commands are allocated in lifo order, so we use a stack
110 * to make this more efficient, and also to avoid all sorts of exception
111 * handling code to handle interrupts in the middle of a parse.
112 *
113 * The size 496 was chosen because with 16-byte alignment the total size
114 * for the allocated block is 512.
115 */
116
117#define MINSIZE 496 /* minimum size of a block. */
118
119struct stack_block {
120 struct stack_block *prev;
121 /* Data follows */
122};
123#define SPACE(sp) ((char *)(sp) + ALIGN(sizeof(struct stack_block)))
124
125static struct stack_block *stackp;
126char *stacknxt;
127int stacknleft;
128char *sstrend;
129
130static void
131stnewblock(int nbytes)
132{
133 struct stack_block *sp;
134 int allocsize;
135
136 if (nbytes < MINSIZE)
137 nbytes = MINSIZE;
138
139 allocsize = ALIGN(sizeof(struct stack_block)) + ALIGN(nbytes);
140
141 INTOFF;
142 sp = ckmalloc(allocsize);
143 sp->prev = stackp;
144 stacknxt = SPACE(sp);
145 stacknleft = allocsize - (stacknxt - (char *)sp);
146 sstrend = stacknxt + stacknleft;
147 stackp = sp;
148 INTON;
149}
150
151pointer
152stalloc(int nbytes)
153{
154 char *p;
155
156 nbytes = ALIGN(nbytes);
157 if (nbytes > stacknleft)
158 stnewblock(nbytes);
159 p = stacknxt;
160 stacknxt += nbytes;
161 stacknleft -= nbytes;
162 return p;
163}
164
165void
166stunalloc(pointer p)
167{
168 if (p == NULL) { /*DEBUG */
169 write(STDERR_FILENO, "stunalloc\n", 10);
170 abort();
171 }
172 stacknleft += stacknxt - (char *)p;
173 stacknxt = p;
174}
175
176char *
177stsavestr(const char *s)
178{
179 char *p;
180 size_t len;
181
182 len = strlen(s);
183 p = stalloc(len + 1);
184 memcpy(p, s, len + 1);
185 return p;
186}
187
188void
189setstackmark(struct stackmark *mark)
190{
191 mark->stackp = stackp;
192 mark->stacknxt = stacknxt;
193 mark->stacknleft = stacknleft;
194 /* Ensure this block stays in place. */
195 if (stackp != NULL && stacknxt == SPACE(stackp))
196 stalloc(1);
197}
198
199void
200popstackmark(struct stackmark *mark)
201{
202 struct stack_block *sp;
203
204 INTOFF;
205 while (stackp != mark->stackp) {
206 sp = stackp;
207 stackp = sp->prev;
208 ckfree(sp);
209 }
210 stacknxt = mark->stacknxt;
211 stacknleft = mark->stacknleft;
212 if (stacknleft != 0)
213 sstrend = stacknxt + stacknleft;
214 else
215 sstrend = stacknxt;
216 INTON;
217}
218
219/*
220 * When the parser reads in a string, it wants to stick the string on the
221 * stack and only adjust the stack pointer when it knows how big the
222 * string is. Stackblock (defined in stack.h) returns a pointer to a block
223 * of space on top of the stack and stackblocklen returns the length of
224 * this block. Growstackblock will grow this space by at least one byte,
225 * possibly moving it (like realloc). Grabstackblock actually allocates the
226 * part of the block that has been used.
227 */
228
229static void
230growstackblock(int min)
231{
232 char *p;
233 int newlen;
234 char *oldspace;
235 int oldlen;
236 struct stack_block *sp;
237 struct stack_block *oldstackp;
238
239 if (min < stacknleft)
240 min = stacknleft;
241 if ((unsigned int)min >= INT_MAX / 2 - ALIGN(sizeof(struct stack_block)))
242 error("Out of space");
243 min += stacknleft;
244 min += ALIGN(sizeof(struct stack_block));
245 newlen = 512;
246 while (newlen < min)
247 newlen <<= 1;
248 oldspace = stacknxt;
249 oldlen = stacknleft;
250
251 if (stackp != NULL && stacknxt == SPACE(stackp)) {
252 INTOFF;
253 oldstackp = stackp;
254 stackp = oldstackp->prev;
255 sp = ckrealloc((pointer)oldstackp, newlen);
256 sp->prev = stackp;
257 stackp = sp;
258 stacknxt = SPACE(sp);
259 stacknleft = newlen - (stacknxt - (char *)sp);
260 sstrend = stacknxt + stacknleft;
261 INTON;
262 } else {
263 newlen -= ALIGN(sizeof(struct stack_block));
264 p = stalloc(newlen);
265 if (oldlen != 0)
266 memcpy(p, oldspace, oldlen);
267 stunalloc(p);
268 }
269}
270
271/*
272 * The following routines are somewhat easier to use than the above.
273 * The user declares a variable of type STACKSTR, which may be declared
274 * to be a register. The macro STARTSTACKSTR initializes things. Then
275 * the user uses the macro STPUTC to add characters to the string. In
276 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
277 * grown as necessary. When the user is done, she can just leave the
278 * string there and refer to it using stackblock(). Or she can allocate
279 * the space for it using grabstackstr(). If it is necessary to allow
280 * someone else to use the stack temporarily and then continue to grow
281 * the string, the user should use grabstack to allocate the space, and
282 * then call ungrabstr(p) to return to the previous mode of operation.
283 *
284 * USTPUTC is like STPUTC except that it doesn't check for overflow.
285 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
286 * is space for at least one character.
287 */
288
289static char *
290growstrstackblock(int n, int min)
291{
292 growstackblock(min);
293 return stackblock() + n;
294}
295
296char *
297growstackstr(void)
298{
299 int len;
300
301 len = stackblocksize();
302 return (growstrstackblock(len, 0));
303}
304
305/*
306 * Called from CHECKSTRSPACE.
307 */
308
309char *
310makestrspace(int min, char *p)
311{
312 int len;
313
314 len = p - stackblock();
315 return (growstrstackblock(len, min));
316}
317
318char *
319stputbin(const char *data, size_t len, char *p)
320{
321 CHECKSTRSPACE(len, p);
322 memcpy(p, data, len);
323 return (p + len);
324}
325
326char *
327stputs(const char *data, char *p)
328{
329 return (stputbin(data, strlen(data), p));
330}