1 /* $NetBSD: mem1.c,v 1.64 2023/01/13 19:41:50 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) 40 __RCSID("$NetBSD: mem1.c,v 1.64 2023/01/13 19:41:50 rillig Exp $"); 41 #endif 42 43 #include <sys/param.h> 44 #include <stdlib.h> 45 #include <string.h> 46 47 #include "lint1.h" 48 49 /* 50 * Filenames allocated by record_filename are shared and have unlimited 51 * lifetime. 52 */ 53 struct filename { 54 const char *fn_name; 55 size_t fn_len; 56 int fn_id; 57 struct filename *fn_next; 58 }; 59 60 static struct filename *filenames; /* null-terminated array */ 61 static int next_filename_id; 62 63 /* Find the given filename, or return NULL. */ 64 static const struct filename * 65 search_filename(const char *s, size_t len) 66 { 67 const struct filename *fn; 68 69 for (fn = filenames; fn != NULL; fn = fn->fn_next) { 70 if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0) 71 break; 72 } 73 return fn; 74 } 75 76 struct filename_replacement { 77 const char *orig; 78 size_t orig_len; 79 const char *repl; 80 const struct filename_replacement *next; 81 }; 82 83 static struct filename_replacement *filename_replacements; 84 85 void 86 add_directory_replacement(char *arg) 87 { 88 struct filename_replacement *r = xmalloc(sizeof(*r)); 89 90 char *sep = strchr(arg, '='); 91 if (sep == NULL) 92 err(1, "Bad replacement directory spec `%s'", arg); 93 *sep = '\0'; 94 95 r->orig = arg; 96 r->orig_len = sep - arg; 97 r->repl = sep + 1; 98 r->next = filename_replacements; 99 filename_replacements = r; 100 } 101 102 const char * 103 transform_filename(const char *name, size_t len) 104 { 105 static char buf[MAXPATHLEN]; 106 const struct filename_replacement *r; 107 108 for (r = filename_replacements; r != NULL; r = r->next) 109 if (r->orig_len < len && 110 memcmp(name, r->orig, r->orig_len) == 0) 111 break; 112 if (r == NULL) 113 return name; 114 (void)snprintf(buf, sizeof(buf), "%s%s", r->repl, name + r->orig_len); 115 return buf; 116 } 117 118 /* 119 * Return a copy of the filename s with unlimited lifetime. 120 * If the filename is new, write it to the output file. 121 */ 122 const char * 123 record_filename(const char *s, size_t slen) 124 { 125 const struct filename *existing_fn; 126 struct filename *fn; 127 char *name; 128 129 if ((existing_fn = search_filename(s, slen)) != NULL) 130 return existing_fn->fn_name; 131 132 name = xmalloc(slen + 1); 133 (void)memcpy(name, s, slen); 134 name[slen] = '\0'; 135 136 fn = xmalloc(sizeof(*fn)); 137 fn->fn_name = name; 138 fn->fn_len = slen; 139 fn->fn_id = next_filename_id++; 140 fn->fn_next = filenames; 141 filenames = fn; 142 143 /* Write the ID of this filename to the output file. */ 144 outclr(); 145 outint(fn->fn_id); 146 outchar('s'); 147 outstrg(transform_filename(fn->fn_name, fn->fn_len)); 148 149 return fn->fn_name; 150 } 151 152 /* Get the ID of a filename. */ 153 int 154 get_filename_id(const char *s) 155 { 156 const struct filename *fn; 157 158 if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL) 159 return -1; 160 return fn->fn_id; 161 } 162 163 /* Array of memory pools, indexed by mem_block_level. */ 164 typedef struct memory_pools { 165 struct memory_pool *pools; 166 size_t cap; 167 } memory_pools; 168 169 static memory_pools mpools; 170 171 /* The pool for the current expression is independent of any block level. */ 172 static memory_pool expr_pool; 173 174 static void 175 mpool_add(memory_pool *pool, void *item) 176 { 177 178 if (pool->len >= pool->cap) { 179 pool->cap = 2 * pool->len + 16; 180 pool->items = xrealloc(pool->items, 181 sizeof(*pool->items) * pool->cap); 182 } 183 pool->items[pool->len++] = item; 184 } 185 186 static void 187 mpool_free(memory_pool *pool) 188 { 189 190 for (; pool->len > 0; pool->len--) 191 free(pool->items[pool->len - 1]); 192 } 193 194 static void * 195 mpool_zero_alloc(memory_pool *pool, size_t size) 196 { 197 198 void *mem = xmalloc(size); 199 memset(mem, 0, size); 200 mpool_add(pool, mem); 201 return mem; 202 } 203 204 static memory_pool * 205 mpool_at(size_t level) 206 { 207 208 if (level >= mpools.cap) { 209 size_t prev_cap = mpools.cap; 210 mpools.cap = level + 16; 211 mpools.pools = xrealloc(mpools.pools, 212 sizeof(*mpools.pools) * mpools.cap); 213 for (size_t i = prev_cap; i < mpools.cap; i++) 214 mpools.pools[i] = (memory_pool){ NULL, 0, 0 }; 215 } 216 return mpools.pools + level; 217 } 218 219 220 /* Allocate memory associated with level l, initialized with zero. */ 221 void * 222 level_zero_alloc(size_t l, size_t s) 223 { 224 225 return mpool_zero_alloc(mpool_at(l), s); 226 } 227 228 /* Allocate memory that is freed at the end of the current block. */ 229 void * 230 block_zero_alloc(size_t s) 231 { 232 233 return level_zero_alloc(mem_block_level, s); 234 } 235 236 void 237 level_free_all(size_t level) 238 { 239 240 mpool_free(mpool_at(level)); 241 } 242 243 /* Allocate memory that is freed at the end of the current expression. */ 244 void * 245 expr_zero_alloc(size_t s) 246 { 247 248 return mpool_zero_alloc(&expr_pool, s); 249 } 250 251 static bool 252 str_endswith(const char *haystack, const char *needle) 253 { 254 size_t hlen = strlen(haystack); 255 size_t nlen = strlen(needle); 256 257 return nlen <= hlen && 258 memcmp(haystack + hlen - nlen, needle, nlen) == 0; 259 } 260 261 /* 262 * Return a freshly allocated tree node that is freed at the end of the 263 * current expression. 264 * 265 * The node records whether it comes from a system file, which makes strict 266 * bool mode less restrictive. 267 */ 268 tnode_t * 269 expr_alloc_tnode(void) 270 { 271 tnode_t *tn = expr_zero_alloc(sizeof(*tn)); 272 /* 273 * files named *.c that are different from the main translation unit 274 * typically contain generated code that cannot be influenced, such 275 * as a flex lexer or a yacc parser. 276 */ 277 tn->tn_sys = in_system_header || 278 (curr_pos.p_file != csrc_pos.p_file && 279 str_endswith(curr_pos.p_file, ".c")); 280 return tn; 281 } 282 283 /* Free all memory which is allocated by the current expression. */ 284 void 285 expr_free_all(void) 286 { 287 288 mpool_free(&expr_pool); 289 } 290 291 /* 292 * Save the memory which is used by the current expression. This memory 293 * is not freed by the next expr_free_all() call. The returned value can be 294 * used to restore the memory. 295 */ 296 memory_pool 297 expr_save_memory(void) 298 { 299 300 memory_pool saved_pool = expr_pool; 301 expr_pool = (memory_pool){ NULL, 0, 0 }; 302 return saved_pool; 303 } 304 305 /* 306 * Free all memory used for the current expression and restore the memory used 307 * by a previous expression and saved by expr_save_memory(). The next call to 308 * expr_free_all() frees the restored memory. 309 */ 310 void 311 expr_restore_memory(memory_pool saved_pool) 312 { 313 314 expr_free_all(); 315 free(expr_pool.items); 316 expr_pool = saved_pool; 317 } 318