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