1 /* $NetBSD: mem1.c,v 1.65 2023/04/11 19:02:19 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.65 2023/04/11 19:02:19 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 126 const struct filename *existing_fn = search_filename(s, slen); 127 if (existing_fn != NULL) 128 return existing_fn->fn_name; 129 130 char *name = xmalloc(slen + 1); 131 (void)memcpy(name, s, slen); 132 name[slen] = '\0'; 133 134 struct filename *fn = xmalloc(sizeof(*fn)); 135 fn->fn_name = name; 136 fn->fn_len = slen; 137 fn->fn_id = next_filename_id++; 138 fn->fn_next = filenames; 139 filenames = fn; 140 141 /* Write the ID of this filename to the output file. */ 142 outclr(); 143 outint(fn->fn_id); 144 outchar('s'); 145 outstrg(transform_filename(fn->fn_name, fn->fn_len)); 146 147 return fn->fn_name; 148 } 149 150 /* Get the ID of a filename. */ 151 int 152 get_filename_id(const char *s) 153 { 154 const struct filename *fn; 155 156 if (s == NULL || (fn = search_filename(s, strlen(s))) == NULL) 157 return -1; 158 return fn->fn_id; 159 } 160 161 typedef struct memory_pools { 162 struct memory_pool *pools; 163 size_t cap; 164 } memory_pools; 165 166 /* Array of memory pools, indexed by mem_block_level. */ 167 static memory_pools mpools; 168 169 /* The pool for the current expression is independent of any block level. */ 170 static memory_pool expr_pool; 171 172 static void 173 mpool_add(memory_pool *pool, void *item) 174 { 175 176 if (pool->len >= pool->cap) { 177 pool->cap = 2 * pool->len + 16; 178 pool->items = xrealloc(pool->items, 179 sizeof(*pool->items) * pool->cap); 180 } 181 pool->items[pool->len++] = item; 182 } 183 184 static void 185 mpool_free(memory_pool *pool) 186 { 187 188 for (; pool->len > 0; pool->len--) 189 free(pool->items[pool->len - 1]); 190 } 191 192 static void * 193 mpool_zero_alloc(memory_pool *pool, size_t size) 194 { 195 196 void *mem = xmalloc(size); 197 memset(mem, 0, size); 198 mpool_add(pool, mem); 199 return mem; 200 } 201 202 static memory_pool * 203 mpool_at(size_t level) 204 { 205 206 if (level >= mpools.cap) { 207 size_t prev_cap = mpools.cap; 208 mpools.cap = level + 16; 209 mpools.pools = xrealloc(mpools.pools, 210 sizeof(*mpools.pools) * mpools.cap); 211 for (size_t i = prev_cap; i < mpools.cap; i++) 212 mpools.pools[i] = (memory_pool){ NULL, 0, 0 }; 213 } 214 return mpools.pools + level; 215 } 216 217 218 /* Allocate memory associated with level l, initialized with zero. */ 219 void * 220 level_zero_alloc(size_t l, size_t s) 221 { 222 223 return mpool_zero_alloc(mpool_at(l), s); 224 } 225 226 /* Allocate memory that is freed at the end of the current block. */ 227 void * 228 block_zero_alloc(size_t s) 229 { 230 231 return level_zero_alloc(mem_block_level, s); 232 } 233 234 void 235 level_free_all(size_t level) 236 { 237 238 mpool_free(mpool_at(level)); 239 } 240 241 /* Allocate memory that is freed at the end of the current expression. */ 242 void * 243 expr_zero_alloc(size_t s) 244 { 245 246 return mpool_zero_alloc(&expr_pool, s); 247 } 248 249 static bool 250 str_endswith(const char *haystack, const char *needle) 251 { 252 size_t hlen = strlen(haystack); 253 size_t nlen = strlen(needle); 254 255 return nlen <= hlen && 256 memcmp(haystack + hlen - nlen, needle, nlen) == 0; 257 } 258 259 /* 260 * Return a freshly allocated tree node that is freed at the end of the 261 * current expression. 262 * 263 * The node records whether it comes from a system file, which makes strict 264 * bool mode less restrictive. 265 */ 266 tnode_t * 267 expr_alloc_tnode(void) 268 { 269 tnode_t *tn = expr_zero_alloc(sizeof(*tn)); 270 /* 271 * files named *.c that are different from the main translation unit 272 * typically contain generated code that cannot be influenced, such 273 * as a flex lexer or a yacc parser. 274 */ 275 tn->tn_sys = in_system_header || 276 (curr_pos.p_file != csrc_pos.p_file && 277 str_endswith(curr_pos.p_file, ".c")); 278 return tn; 279 } 280 281 /* Free all memory which is allocated by the current expression. */ 282 void 283 expr_free_all(void) 284 { 285 286 mpool_free(&expr_pool); 287 } 288 289 /* 290 * Save the memory which is used by the current expression. This memory 291 * is not freed by the next expr_free_all() call. The returned value can be 292 * used to restore the memory. 293 */ 294 memory_pool 295 expr_save_memory(void) 296 { 297 298 memory_pool saved_pool = expr_pool; 299 expr_pool = (memory_pool){ NULL, 0, 0 }; 300 return saved_pool; 301 } 302 303 /* 304 * Free all memory used for the current expression and restore the memory used 305 * by a previous expression and saved by expr_save_memory(). The next call to 306 * expr_free_all() frees the restored memory. 307 */ 308 void 309 expr_restore_memory(memory_pool saved_pool) 310 { 311 312 expr_free_all(); 313 free(expr_pool.items); 314 expr_pool = saved_pool; 315 } 316