mem1.c revision 1.51 1 /* $NetBSD: mem1.c,v 1.51 2021/08/28 13:29:26 rillig Exp $ */
2
3 /*
4 * Copyright (c) 1994, 1995 Jochen Pohl
5 * All Rights Reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Jochen Pohl for
18 * The NetBSD Project.
19 * 4. The name of the author may not be used to endorse or promote products
20 * derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #if HAVE_NBTOOL_CONFIG_H
35 #include "nbtool_config.h"
36 #endif
37
38 #include <sys/cdefs.h>
39 #if defined(__RCSID) && !defined(lint)
40 __RCSID("$NetBSD: mem1.c,v 1.51 2021/08/28 13:29:26 rillig Exp $");
41 #endif
42
43 #include <sys/types.h>
44 #include <sys/param.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48
49 #include "lint1.h"
50
51 /*
52 * Filenames allocated by record_filename are shared and have unlimited
53 * lifetime.
54 */
55 struct filename {
56 const char *fn_name;
57 size_t fn_len;
58 int fn_id;
59 struct filename *fn_next;
60 };
61
62 static struct filename *filenames; /* null-terminated array */
63
64 /* Find the given filename, or return NULL. */
65 static const struct filename *
66 search_filename(const char *s, size_t len)
67 {
68 const struct filename *fn;
69
70 for (fn = filenames; fn != NULL; fn = fn->fn_next) {
71 if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0)
72 break;
73 }
74 return fn;
75 }
76
77 struct filename_replacement {
78 const char *orig;
79 size_t orig_len;
80 const char *repl;
81 const struct filename_replacement *next;
82 };
83
84 static struct filename_replacement *filename_replacements;
85
86 void
87 add_directory_replacement(char *arg)
88 {
89 struct filename_replacement *r = xmalloc(sizeof(*r));
90
91 char *sep = strchr(arg, '=');
92 if (sep == NULL)
93 err(1, "Bad replacement directory spec `%s'", arg);
94 *sep = '\0';
95
96 r->orig = arg;
97 r->orig_len = sep - arg;
98 r->repl = sep + 1;
99 r->next = filename_replacements;
100 filename_replacements = r;
101 }
102
103 const char *
104 transform_filename(const char *name, size_t len)
105 {
106 static char buf[MAXPATHLEN];
107 const struct filename_replacement *r;
108
109 for (r = filename_replacements; r != NULL; r = r->next)
110 if (r->orig_len < len &&
111 memcmp(name, r->orig, r->orig_len) == 0)
112 break;
113 if (r == NULL)
114 return name;
115 (void)snprintf(buf, sizeof(buf), "%s%s", r->repl, name + r->orig_len);
116 return buf;
117 }
118
119 static int
120 next_filename_id(void)
121 {
122 static int next_id = 0;
123
124 return next_id++;
125 }
126
127 /*
128 * Return a copy of the filename s with unlimited lifetime.
129 * If the filename is new, write it to the output file.
130 */
131 const char *
132 record_filename(const char *s, size_t slen)
133 {
134 const struct filename *existing_fn;
135 struct filename *fn;
136 char *name;
137
138 if (s == NULL)
139 return NULL;
140
141 if ((existing_fn = search_filename(s, slen)) != NULL)
142 return existing_fn->fn_name;
143
144 /* Do not use strdup() because s is not NUL-terminated.*/
145 name = xmalloc(slen + 1);
146 (void)memcpy(name, s, slen);
147 name[slen] = '\0';
148
149 fn = xmalloc(sizeof(*fn));
150 fn->fn_name = name;
151 fn->fn_len = slen;
152 fn->fn_id = next_filename_id();
153 fn->fn_next = filenames;
154 filenames = fn;
155
156 /* Write the ID of this filename to the output file. */
157 outclr();
158 outint(fn->fn_id);
159 outchar('s');
160 outstrg(transform_filename(fn->fn_name, fn->fn_len));
161
162 return fn->fn_name;
163 }
164
165 /* Get the ID of a filename. */
166 int
167 get_filename_id(const char *s)
168 {
169 const struct filename *fn;
170
171 if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL)
172 return -1;
173 return fn->fn_id;
174 }
175
176 /*
177 * Memory for declarations and other things which must be available
178 * until the end of a block (or the end of the translation unit)
179 * is associated with the corresponding mem_block_level, which may be 0.
180 * Because this memory is allocated in large blocks associated with
181 * a given level it can be freed easily at the end of a block.
182 */
183 #define ML_INC ((size_t)32) /* Increment for length of *mblks */
184
185 typedef struct memory_block {
186 void *start; /* beginning of memory block */
187 void *first_free; /* first free byte */
188 size_t nfree; /* # of free bytes */
189 size_t size; /* total size of memory block */
190 struct memory_block *next;
191 } memory_block;
192
193 /*
194 * Array of pointers to lists of memory blocks. mem_block_level is used as
195 * index into this array.
196 */
197 static memory_block **mblks;
198
199 /* number of elements in *mblks */
200 static size_t nmblks;
201
202 /* free list for memory blocks */
203 static memory_block *frmblks;
204
205 /* length of new allocated memory blocks */
206 static size_t mblklen;
207
208
209 static memory_block *
210 xnewblk(void)
211 {
212 memory_block *mb = xmalloc(sizeof(*mb));
213
214 mb->start = xmalloc(mblklen);
215 mb->size = mblklen;
216
217 return mb;
218 }
219
220 /* Allocate new memory, initialized with zero. */
221 static void *
222 xgetblk(memory_block **mbp, size_t s)
223 {
224 memory_block *mb;
225 void *p;
226 size_t t = 0;
227
228 /*
229 * If the first block of the list has not enough free space,
230 * or there is no first block, get a new block. The new block
231 * is taken from the free list or, if there is no block on the
232 * free list, is allocated using xnewblk().
233 *
234 * If a new block is allocated it is initialized with zero.
235 * Blocks taken from the free list are zero'd in xfreeblk().
236 */
237
238 s = WORST_ALIGN(s);
239 if ((mb = *mbp) == NULL || mb->nfree < s) {
240 if ((mb = frmblks) == NULL || mb->size < s) {
241 if (s > mblklen) {
242 t = mblklen;
243 mblklen = s;
244 }
245 mb = xnewblk();
246 #ifndef BLKDEBUG
247 (void)memset(mb->start, 0, mb->size);
248 #endif
249 if (t > 0)
250 mblklen = t;
251 } else {
252 frmblks = mb->next;
253 }
254 mb->first_free = mb->start;
255 mb->nfree = mb->size;
256 mb->next = *mbp;
257 *mbp = mb;
258 }
259 p = mb->first_free;
260 mb->first_free = (char *)mb->first_free + s;
261 mb->nfree -= s;
262 #ifdef BLKDEBUG
263 (void)memset(p, 0, s);
264 #endif
265 return p;
266 }
267
268 /*
269 * Move all blocks from list *fmbp to free list. For each block, set all
270 * used memory to zero.
271 */
272 static void
273 xfreeblk(memory_block **fmbp)
274 {
275 memory_block *mb;
276
277 while ((mb = *fmbp) != NULL) {
278 *fmbp = mb->next;
279 mb->next = frmblks;
280 frmblks = mb;
281 (void)memset(mb->start, ZERO, mb->size - mb->nfree);
282 }
283 }
284
285 void
286 initmem(void)
287 {
288
289 mblklen = MBLKSIZ - MBLKSIZ % (unsigned int)getpagesize();
290 mblks = xcalloc(nmblks = ML_INC, sizeof(*mblks));
291 }
292
293
294 /* Allocate memory associated with level l, initialized with zero. */
295 void *
296 getlblk(size_t l, size_t s)
297 {
298
299 while (l >= nmblks) {
300 mblks = xrealloc(mblks, (nmblks + ML_INC) * sizeof(*mblks));
301 (void)memset(&mblks[nmblks], 0, ML_INC * sizeof(*mblks));
302 nmblks += ML_INC;
303 }
304 return xgetblk(&mblks[l], s);
305 }
306
307 /*
308 * Return allocated memory for the current mem_block_level, initialized with
309 * zero.
310 */
311 void *
312 getblk(size_t s)
313 {
314
315 return getlblk(mem_block_level, s);
316 }
317
318 /* Free all memory associated with level l. */
319 void
320 freelblk(int l)
321 {
322
323 xfreeblk(&mblks[l]);
324 }
325
326 void
327 freeblk(void)
328 {
329
330 freelblk(mem_block_level);
331 }
332
333 static memory_block *tmblk;
334
335 /*
336 * Return zero-initialized memory that is freed at the end of the current
337 * expression.
338 */
339 void *
340 expr_zalloc(size_t s)
341 {
342
343 return xgetblk(&tmblk, s);
344 }
345
346 static bool
347 str_endswith(const char *haystack, const char *needle)
348 {
349 size_t hlen = strlen(haystack);
350 size_t nlen = strlen(needle);
351
352 return nlen <= hlen &&
353 memcmp(haystack + hlen - nlen, needle, nlen) == 0;
354 }
355
356 /*
357 * Return a freshly allocated tree node that is freed at the end of the
358 * current expression.
359 */
360 tnode_t *
361 expr_zalloc_tnode(void)
362 {
363 tnode_t *tn = expr_zalloc(sizeof(*tn));
364 /*
365 * files named *.c that are different from the main translation unit
366 * typically contain generated code that cannot be influenced, such
367 * as a flex lexer or a yacc parser.
368 */
369 tn->tn_relaxed = in_system_header ||
370 (curr_pos.p_file != csrc_pos.p_file &&
371 str_endswith(curr_pos.p_file, ".c"));
372 return tn;
373 }
374
375 /* Free all memory which is allocated by the current expression. */
376 void
377 expr_free_all(void)
378 {
379
380 xfreeblk(&tmblk);
381 }
382
383 /*
384 * Save the memory which is used by the current expression. This memory
385 * is not freed by the next expr_free_all() call. The pointer returned can be
386 * used to restore the memory.
387 */
388 memory_block *
389 expr_save_memory(void)
390 {
391 memory_block *tmem;
392
393 tmem = tmblk;
394 tmblk = NULL;
395 return tmem;
396 }
397
398 /*
399 * Free all memory used for the current expression and restore the memory used
400 * by a previous expression and saved by expr_save_memory(). The next call to
401 * expr_free_all() frees the restored memory.
402 */
403 void
404 expr_restore_memory(memory_block *tmem)
405 {
406
407 expr_free_all();
408 if (tmblk != NULL) {
409 free(tmblk->start);
410 free(tmblk);
411 }
412 tmblk = tmem;
413 }
414