memalloc.c revision 1.41 1 /* $NetBSD: memalloc.c,v 1.41 2025/05/07 14:01:01 kre Exp $ */
2
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/cdefs.h>
36 #ifndef lint
37 #if 0
38 static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95";
39 #else
40 __RCSID("$NetBSD: memalloc.c,v 1.41 2025/05/07 14:01:01 kre Exp $");
41 #endif
42 #endif /* not lint */
43
44 #include <limits.h>
45 #include <stdarg.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48
49 #include "shell.h"
50 #include "output.h"
51 #include "memalloc.h"
52 #include "error.h"
53 #include "machdep.h"
54 #include "mystring.h"
55
56 /*
57 * Like malloc, but returns an error when out of space.
58 */
59
60 pointer
61 ckmalloc(size_t nbytes)
62 {
63 pointer p;
64
65 p = malloc(nbytes);
66 if (p == NULL)
67 error("Out of space");
68 return p;
69 }
70
71
72 /*
73 * Same for realloc.
74 */
75
76 pointer
77 ckrealloc(pointer p, int nbytes)
78 {
79 p = realloc(p, nbytes);
80 if (p == NULL)
81 error("Out of space");
82 return p;
83 }
84
85
86 /*
87 * Make a copy of a string in safe storage.
88 */
89
90 char *
91 savestr(const char *s)
92 {
93 char *p;
94
95 p = ckmalloc(strlen(s) + 1);
96 scopy(s, p);
97 return p;
98 }
99
100
101 /*
102 * Parse trees for commands are allocated in lifo order, so we use a stack
103 * to make this more efficient, and also to avoid all sorts of exception
104 * handling code to handle interrupts in the middle of a parse.
105 *
106 * The size 504 was chosen because the Ultrix malloc handles that size
107 * well.
108 */
109
110 #define MINSIZE 504 /* minimum size of a block */
111
112 struct stack_block {
113 struct stack_block *prev;
114 char space[MINSIZE];
115 };
116
117 struct stack_block stackbase;
118 struct stack_block *stackp = &stackbase;
119 struct stackmark *markp;
120 char *stacknxt = stackbase.space;
121 int stacknleft = MINSIZE;
122 int sstrnleft;
123
124 pointer
125 stalloc(int nbytes)
126 {
127 char *p;
128
129 nbytes = SHELL_ALIGN(nbytes);
130 if (nbytes > stacknleft) {
131 int blocksize;
132 struct stack_block *sp;
133
134 blocksize = nbytes;
135 if (blocksize < MINSIZE)
136 blocksize = MINSIZE;
137 INTOFF;
138 sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize);
139 sp->prev = stackp;
140 stacknxt = sp->space;
141 stacknleft = blocksize;
142 stackp = sp;
143 INTON;
144 }
145 INTOFF;
146 p = stacknxt;
147 stacknxt += nbytes;
148 stacknleft -= nbytes;
149 INTON;
150 return p;
151 }
152
153
154 void
155 stunalloc(pointer p)
156 {
157 if (p == NULL) { /*DEBUG */
158 write(2, "stunalloc\n", 10);
159 abort();
160 }
161 stacknleft += stacknxt - (char *)p;
162 stacknxt = p;
163 }
164
165
166 /* save the current status of the sh stack */
167 void
168 setstackmark(struct stackmark *mark)
169 {
170 mark->stackp = stackp;
171 mark->stacknxt = stacknxt;
172 mark->stacknleft = stacknleft;
173 mark->sstrnleft = sstrnleft;
174 mark->marknext = markp;
175 markp = mark;
176 }
177
178 /* reset the stack mark, and remove it from the list of marks */
179 void
180 popstackmark(struct stackmark *mark)
181 {
182 INTOFF;
183 markp = mark->marknext; /* delete mark from the list */
184 rststackmark(mark); /* and reset stack */
185 INTON;
186 }
187
188 /* reset the shell stack to its state recorded in the stack mark */
189 void
190 rststackmark(struct stackmark *mark)
191 {
192 struct stack_block *sp;
193
194 INTOFF;
195 while (stackp != mark->stackp) {
196 /* delete any recently allocated mem blocks */
197 sp = stackp;
198 stackp = sp->prev;
199 ckfree(sp);
200 }
201 stacknxt = mark->stacknxt;
202 stacknleft = mark->stacknleft;
203 sstrnleft = mark->sstrnleft;
204 INTON;
205 }
206
207
208 /*
209 * When the parser reads in a string, it wants to stick the string on the
210 * stack and only adjust the stack pointer when it knows how big the
211 * string is. Stackblock (defined in stack.h) returns a pointer to a block
212 * of space on top of the stack and stackblocklen returns the length of
213 * this block. Growstackblock will grow this space by at least one byte,
214 * possibly moving it (like realloc). Grabstackblock actually allocates the
215 * part of the block that has been used.
216 */
217
218 void
219 growstackblock(void)
220 {
221 int newlen = SHELL_ALIGN(stacknleft * 2 + 100);
222
223 INTOFF;
224 if (stacknxt == stackp->space && stackp != &stackbase) {
225 struct stack_block *oldstackp;
226 struct stackmark *xmark;
227 struct stack_block *sp;
228
229 oldstackp = stackp;
230 sp = stackp;
231 stackp = sp->prev;
232 sp = ckrealloc((pointer)sp,
233 sizeof(struct stack_block) - MINSIZE + newlen);
234 sp->prev = stackp;
235 stackp = sp;
236 stacknxt = sp->space;
237 sstrnleft += newlen - stacknleft;
238 stacknleft = newlen;
239
240 /*
241 * Stack marks pointing to the start of the old block
242 * must be relocated to point to the new block
243 */
244 xmark = markp;
245 while (xmark != NULL && xmark->stackp == oldstackp) {
246 xmark->stackp = stackp;
247 xmark->stacknxt = stacknxt;
248 xmark->sstrnleft += stacknleft - xmark->stacknleft;
249 xmark->stacknleft = stacknleft;
250 xmark = xmark->marknext;
251 }
252 } else {
253 char *oldspace = stacknxt;
254 int oldlen = stacknleft;
255 char *p = stalloc(newlen);
256
257 (void)memcpy(p, oldspace, oldlen);
258 stacknxt = p; /* free the space */
259 stacknleft += newlen; /* we just allocated */
260 }
261 INTON;
262 }
263
264 void
265 grabstackblock(int len)
266 {
267 len = SHELL_ALIGN(len);
268 INTOFF;
269 stacknxt += len;
270 stacknleft -= len;
271 INTON;
272 }
273
274 /*
275 * The following routines are somewhat easier to use than the above.
276 * The user declares a variable of type STACKSTR, which may be declared
277 * to be a register. The macro STARTSTACKSTR initializes things. Then
278 * the user uses the macro STPUTC to add characters to the string. In
279 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
280 * grown as necessary. When the user is done, she can just leave the
281 * string there and refer to it using stackblock(). Or she can allocate
282 * the space for it using grabstackstr(). If it is necessary to allow
283 * someone else to use the stack temporarily and then continue to grow
284 * the string, the user should use grabstack to allocate the space, and
285 * then call ungrabstr(p) to return to the previous mode of operation.
286 *
287 * USTPUTC is like STPUTC except that it doesn't check for overflow.
288 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
289 * is space for at least one character.
290 */
291
292 char *
293 growstackstr(void)
294 {
295 int len = stackblocksize();
296
297 growstackblock();
298 sstrnleft = stackblocksize() - len - 1;
299 return stackblock() + len;
300 }
301
302 /*
303 * Called from CHECKSTRSPACE.
304 */
305
306 char *
307 makestrspace(void)
308 {
309 int len = stackblocksize() - sstrnleft;
310 growstackblock();
311 sstrnleft = stackblocksize() - len;
312 return stackblock() + len;
313 }
314
315 /*
316 * Note that this only works to release stack space for reuse
317 * if nothing else has allocated space on the stack since the grabstackstr()
318 *
319 * "s" is the start of the area to be released, and "p" represents the end
320 * of the string we have stored beyond there and are now releasing.
321 * (ie: "p" should be the same as in the call to grabstackstr()).
322 *
323 * stunalloc(s) and ungrabstackstr(s, p) are almost interchangeable after
324 * a grabstackstr(), however the latter also returns string space so we
325 * can just continue with STPUTC() etc without needing a new STARTSTACKSTR(s)
326 */
327 void
328 ungrabstackstr(char *s, char *p)
329 {
330 #ifdef DEBUG
331 if (s < stacknxt || stacknxt + stacknleft < s)
332 abort();
333 #endif
334 stacknleft += stacknxt - s;
335 stacknxt = s;
336 sstrnleft = stacknleft - (p - s);
337 }
338
339 /*
340 * Save the concat of a sequence of strings in stack space
341 *
342 * The first arg (if not NULL) is a pointer to where the final string
343 * length will be returned.
344 *
345 * Remaining args are pointers to strings - sufficient space to hold
346 * the concat of the strings is allocated on the stack, the strings
347 * are copied into that space, and a pointer to its start is returned.
348 * The arg list is terminated with STSTRC_END.
349 *
350 * Use stunalloc(string) (in proper sequence) to release the string
351 */
352 char *
353 ststrcat(size_t *lp, ...)
354 {
355 va_list ap;
356 const char *arg;
357 size_t len, tlen = 0, alen[8];
358 char *str, *nxt;
359 unsigned int n;
360
361 n = 0;
362 va_start(ap, lp);
363 arg = va_arg(ap, const char *);
364 while (arg != STSTRC_END) {
365 len = strlen(arg);
366 if (n < sizeof(alen)/sizeof(alen[0]))
367 alen[n++] = len;
368 tlen += len;
369 arg = va_arg(ap, const char *);
370 }
371 va_end(ap);
372
373 if (lp != NULL)
374 *lp = tlen;
375
376 if (tlen >= INT_MAX)
377 error("ststrcat() over length botch");
378 str = (char *)stalloc((int)tlen + 1); /* 1 for \0 */
379 str[tlen] = '\0'; /* in case of no args */
380
381 n = 0;
382 nxt = str;
383 va_start(ap, lp);
384 arg = va_arg(ap, const char *);
385 while (arg != STSTRC_END) {
386 if (n < sizeof(alen)/sizeof(alen[0]))
387 len = alen[n++];
388 else
389 len = strlen(arg);
390
391 scopy(arg, nxt);
392 nxt += len;
393
394 arg = va_arg(ap, const char *);
395 }
396 va_end(ap);
397
398 return str;
399 }
400