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 "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}