common.c (14093B)
1 /* Pure libc bits with no OS-specific behavior: the heap vtable, the 2 * stderr diag sink, stdout/fd writers, and the small printf/errf/alloc 3 * helpers that route through stdio + malloc. Compiled on every host. */ 4 5 #include <stdarg.h> 6 #include <stdint.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include "env_internal.h" 12 13 /* ---------------- heap (libc-backed) ---------------- */ 14 15 static void* heap_libc_alloc(KitHeap* h, size_t size, size_t align) { 16 (void)h; 17 (void)align; /* malloc satisfies all max_align_t alignments */ 18 return size ? malloc(size) : NULL; 19 } 20 21 static void* heap_libc_realloc(KitHeap* h, void* p, size_t old_size, 22 size_t new_size, size_t align) { 23 (void)h; 24 (void)old_size; 25 (void)align; 26 return realloc(p, new_size); 27 } 28 29 static void heap_libc_free(KitHeap* h, void* p, size_t size) { 30 (void)h; 31 (void)size; 32 free(p); 33 } 34 35 KitHeap g_heap_libc = { 36 heap_libc_alloc, 37 heap_libc_realloc, 38 heap_libc_free, 39 NULL, 40 }; 41 42 /* ---------------- diag sink (stderr) ---------------- */ 43 44 static const char* diag_label(KitDiagKind k) { 45 switch (k) { 46 case KIT_DIAG_NOTE: 47 return "note"; 48 case KIT_DIAG_WARN: 49 return "warning"; 50 case KIT_DIAG_ERROR: 51 return "error"; 52 case KIT_DIAG_FATAL: 53 return "fatal"; 54 } 55 return "diag"; 56 } 57 58 /* Tracks the compiler currently driving libkit calls so the stderr 59 * diag sink can resolve loc.file_id to the source's spelling (path or 60 * memory-input label). NULL falls back to the numeric `<file:%u>` form. */ 61 static KitCompiler* g_diag_active_compiler; 62 63 void driver_diag_set_compiler(KitCompiler* c) { g_diag_active_compiler = c; } 64 65 KitStatus driver_compiler_new(const KitTarget* t, const KitContext* ctx, 66 KitCompiler** out) { 67 KitCompiler* c = NULL; 68 KitStatus st = kit_compiler_new(t, ctx, &c); 69 if (st != KIT_OK) { 70 if (out) *out = NULL; 71 return st; 72 } 73 driver_diag_set_compiler(c); 74 if (out) *out = c; 75 return KIT_OK; 76 } 77 78 void driver_compiler_free(KitCompiler* c) { 79 if (!c) return; 80 if (g_diag_active_compiler == c) driver_diag_set_compiler(NULL); 81 kit_compiler_free(c); 82 } 83 84 static void diag_stderr_emit(KitDiagSink* s, KitDiagKind k, KitSrcLoc loc, 85 const char* fmt, va_list ap) { 86 (void)s; 87 if (loc.file_id || loc.line) { 88 KitSlice name = kit_compiler_file_name(g_diag_active_compiler, loc.file_id); 89 if (name.len) { 90 fprintf(stderr, "%.*s:%u:%u: %.*s: ", KIT_SLICE_ARG(name), loc.line, 91 loc.col, KIT_SLICE_ARG(kit_slice_cstr(diag_label(k)))); 92 } else { 93 fprintf(stderr, "<file:%u>:%u:%u: %.*s: ", loc.file_id, loc.line, loc.col, 94 KIT_SLICE_ARG(kit_slice_cstr(diag_label(k)))); 95 } 96 } else { 97 fprintf(stderr, "%.*s: ", KIT_SLICE_ARG(kit_slice_cstr(diag_label(k)))); 98 } 99 vfprintf(stderr, fmt, ap); 100 fputc('\n', stderr); 101 } 102 103 KitDiagSink g_diag_stderr = { 104 diag_stderr_emit, 105 NULL, 106 0, 107 0, 108 }; 109 110 /* ---------------- alloc helpers ---------------- */ 111 112 void* driver_alloc(DriverEnv* e, size_t n) { 113 return e->heap->alloc(e->heap, n, _Alignof(max_align_t)); 114 } 115 116 void* driver_alloc_zeroed(DriverEnv* e, size_t n) { 117 void* p = driver_alloc(e, n); 118 if (p) memset(p, 0, n); 119 return p; 120 } 121 122 void driver_free(DriverEnv* e, void* p, size_t n) { 123 if (p) e->heap->free(e->heap, p, n); 124 } 125 126 void driver_memcpy(void* dst, const void* src, size_t n) { 127 memcpy(dst, src, n); 128 } 129 130 /* ---------------- hosted dir lists ---------------- */ 131 132 /* Mechanical only: join `a` (+ optional `/sub`), dup into the list. No per-OS 133 * policy lives here -- producers (driver/lib/hosted.c expansion and the 134 * per-host driver_default_hosted_dirs probes) supply the paths. */ 135 static int hd_store(DriverEnv* env, char** arr, size_t* sizes, uint32_t* n, 136 uint32_t cap, const char* a, const char* b) { 137 size_t alen, blen, slash, bytes, off; 138 char* out; 139 if (!a || !a[0]) return 0; /* empty base -> successful no-op */ 140 if (*n >= cap) return 1; /* loud overflow -- never a silent drop */ 141 alen = driver_strlen(a); 142 blen = b ? driver_strlen(b) : 0; 143 slash = (blen && a[alen - 1] != '/') ? 1u : 0u; 144 bytes = alen + slash + blen + 1u; 145 out = driver_alloc(env, bytes); 146 if (!out) return 1; 147 driver_memcpy(out, a, alen); 148 off = alen; 149 if (slash) out[off++] = '/'; 150 if (blen) { 151 driver_memcpy(out + off, b, blen); 152 off += blen; 153 } 154 out[off] = '\0'; 155 arr[*n] = out; 156 sizes[*n] = bytes; 157 (*n)++; 158 return 0; 159 } 160 161 int driver_hosted_dirs_add_inc(DriverHostedDirs* d, const char* dir) { 162 return hd_store(d->env, d->incdirs, d->incdir_sizes, &d->nincdirs, 163 DRIVER_HOSTED_MAX_INCDIRS, dir, NULL); 164 } 165 166 int driver_hosted_dirs_add_lib(DriverHostedDirs* d, const char* dir) { 167 return hd_store(d->env, d->libdirs, d->libdir_sizes, &d->nlibdirs, 168 DRIVER_HOSTED_MAX_LIBDIRS, dir, NULL); 169 } 170 171 int driver_hosted_dirs_add_inc_join(DriverHostedDirs* d, const char* base, 172 const char* sub) { 173 return hd_store(d->env, d->incdirs, d->incdir_sizes, &d->nincdirs, 174 DRIVER_HOSTED_MAX_INCDIRS, base, sub); 175 } 176 177 int driver_hosted_dirs_add_lib_join(DriverHostedDirs* d, const char* base, 178 const char* sub) { 179 return hd_store(d->env, d->libdirs, d->libdir_sizes, &d->nlibdirs, 180 DRIVER_HOSTED_MAX_LIBDIRS, base, sub); 181 } 182 183 void driver_hosted_dirs_fini(DriverHostedDirs* d) { 184 uint32_t i; 185 if (!d || !d->env) return; 186 for (i = 0; i < d->nincdirs; ++i) 187 driver_free(d->env, d->incdirs[i], d->incdir_sizes[i]); 188 for (i = 0; i < d->nlibdirs; ++i) 189 driver_free(d->env, d->libdirs[i], d->libdir_sizes[i]); 190 d->nincdirs = 0; 191 d->nlibdirs = 0; 192 } 193 194 /* ---------------- string predicates ---------------- */ 195 196 /* The driver's only NUL-terminated-string handling: thin boundary shims 197 * that route every length scan through kit_slice_cstr and otherwise use 198 * the length-based mem* primitives. No libc str* is used. */ 199 200 int driver_streq(const char* a, const char* b) { 201 return kit_slice_eq(kit_slice_cstr(a), kit_slice_cstr(b)); 202 } 203 204 int driver_strneq(const char* a, const char* b, size_t n) { 205 size_t i; 206 for (i = 0; i < n; ++i) { 207 unsigned char ca = (unsigned char)a[i], cb = (unsigned char)b[i]; 208 if (ca != cb) return 0; 209 if (ca == '\0') return 1; 210 } 211 return 1; 212 } 213 214 size_t driver_strlen(const char* s) { return kit_slice_cstr(s).len; } 215 216 const char* driver_strchr(const char* s, int c) { 217 /* search includes the terminator so driver_strchr(s, 0) works like strchr */ 218 return (const char*)memchr(s, c, kit_slice_cstr(s).len + 1u); 219 } 220 221 const char* driver_basename(const char* path) { 222 size_t i = kit_slice_cstr(path).len; 223 while (i > 0) { 224 if (path[i - 1] == '/') return path + i; 225 --i; 226 } 227 return path; 228 } 229 230 int driver_has_suffix(const char* s, const char* suffix) { 231 size_t ls = kit_slice_cstr(s).len; 232 size_t lf = kit_slice_cstr(suffix).len; 233 return ls >= lf && memcmp(s + ls - lf, suffix, lf) == 0; 234 } 235 236 /* ---------------- printf/errf/logf ---------------- */ 237 238 void driver_errf(const char* tool, const char* fmt, ...) { 239 va_list ap; 240 fprintf(stderr, "%.*s: ", KIT_SLICE_ARG(kit_slice_cstr(tool))); 241 va_start(ap, fmt); 242 vfprintf(stderr, fmt, ap); 243 va_end(ap); 244 fputc('\n', stderr); 245 } 246 247 void driver_logf(const char* fmt, ...) { 248 va_list ap; 249 va_start(ap, fmt); 250 vfprintf(stderr, fmt, ap); 251 va_end(ap); 252 fputc('\n', stderr); 253 } 254 255 void kit_debug_printf(const char* fmt, ...) { 256 va_list ap; 257 va_start(ap, fmt); 258 vfprintf(stderr, fmt, ap); 259 va_end(ap); 260 } 261 262 const char* kit_debug_getenv(const char* name) { return getenv(name); } 263 264 /* ---------------- tracing (KIT_TRACE) ---------------- */ 265 266 /* Hosted side of the kit/trace.h seam: parse KIT_TRACE once, cache it, and 267 * answer kit_trace_enabled / format kit_trace_emit. The config is immutable 268 * after the first read, so the lazy init is intentionally lock-free -- 269 * concurrent first-callers re-parse identical data into the same cache, which 270 * is idempotent. Tracing is diagnostic-only and never affects compiler 271 * output, so this process-scoped state is the one place trace state lives. */ 272 273 #define TRACE_MAX_SPECS 32 274 #define TRACE_BUF_CAP 512 275 276 typedef struct TraceSpec { 277 const char* module; /* substring matched against the trace point's module */ 278 int level; /* threshold 1..5 (KitTraceLevel) */ 279 } TraceSpec; 280 281 static struct { 282 int parsed; 283 int global_level; /* threshold for modules with no matching spec; 0 = off */ 284 int nspecs; 285 TraceSpec specs[TRACE_MAX_SPECS]; 286 char buf[TRACE_BUF_CAP]; /* owns the tokenized copy the specs point into */ 287 } g_trace; 288 289 static int trace_level_from_name(const char* s, size_t n) { 290 if (n == 5 && memcmp(s, "error", 5) == 0) return KIT_TRACE_ERROR; 291 if (n == 4 && memcmp(s, "warn", 4) == 0) return KIT_TRACE_WARN; 292 if (n == 7 && memcmp(s, "warning", 7) == 0) return KIT_TRACE_WARN; 293 if (n == 4 && memcmp(s, "info", 4) == 0) return KIT_TRACE_INFO; 294 if (n == 5 && memcmp(s, "debug", 5) == 0) return KIT_TRACE_DEBUG; 295 if (n == 5 && memcmp(s, "trace", 5) == 0) return KIT_TRACE_TRACE; 296 return 0; 297 } 298 299 /* "1" / "*" / "all" are spellings of "everything, max verbosity". */ 300 static int trace_is_all(const char* s, size_t n) { 301 return (n == 1 && (s[0] == '1' || s[0] == '*')) || 302 (n == 3 && memcmp(s, "all", 3) == 0); 303 } 304 305 static void trace_raise_global(int lvl) { 306 if (lvl > g_trace.global_level) g_trace.global_level = lvl; 307 } 308 309 static void trace_add_module(const char* module, int lvl) { 310 if (g_trace.nspecs >= TRACE_MAX_SPECS) return; 311 g_trace.specs[g_trace.nspecs].module = module; 312 g_trace.specs[g_trace.nspecs].level = lvl; 313 g_trace.nspecs++; 314 } 315 316 static void trace_parse(void) { 317 const char* env = getenv("KIT_TRACE"); 318 size_t len, i, start; 319 320 g_trace.parsed = 1; 321 g_trace.global_level = 0; 322 g_trace.nspecs = 0; 323 if (!env || !env[0]) return; 324 325 len = strlen(env); 326 if (len >= TRACE_BUF_CAP) len = TRACE_BUF_CAP - 1; 327 memcpy(g_trace.buf, env, len); 328 g_trace.buf[len] = '\0'; 329 330 /* Split on ',' in place; each token is "[module=]level" or a bare token. */ 331 start = 0; 332 for (i = 0; i <= len; ++i) { 333 char* tok; 334 size_t tlen; 335 char* eq; 336 if (i != len && g_trace.buf[i] != ',') continue; 337 g_trace.buf[i] = '\0'; 338 tok = &g_trace.buf[start]; 339 tlen = i - start; 340 start = i + 1; 341 if (tlen == 0) continue; 342 343 eq = memchr(tok, '=', tlen); 344 if (eq) { 345 size_t mlen = (size_t)(eq - tok); 346 const char* lvls = eq + 1; 347 size_t llen = tlen - mlen - 1; 348 int lvl = trace_level_from_name(lvls, llen); 349 if (lvl == 0 && trace_is_all(lvls, llen)) lvl = KIT_TRACE_TRACE; 350 if (lvl == 0) continue; /* unknown level name: ignore the spec */ 351 *eq = '\0'; /* terminate the module substring in place */ 352 if (mlen == 0) 353 trace_raise_global(lvl); 354 else 355 trace_add_module(tok, lvl); 356 } else { 357 int lvl = trace_level_from_name(tok, tlen); 358 if (trace_is_all(tok, tlen)) lvl = KIT_TRACE_TRACE; 359 if (lvl != 0) 360 trace_raise_global(lvl); /* bare level => default for all modules */ 361 else 362 trace_add_module(tok, KIT_TRACE_TRACE); /* bare module => at TRACE */ 363 } 364 } 365 } 366 367 int kit_trace_enabled(const char* module, int level) { 368 int threshold, i; 369 if (!g_trace.parsed) trace_parse(); 370 threshold = g_trace.global_level; 371 if (module) { 372 /* Most-verbose matching module spec wins, falling back to the global. */ 373 for (i = 0; i < g_trace.nspecs; ++i) 374 if (g_trace.specs[i].level > threshold && 375 strstr(module, g_trace.specs[i].module) != NULL) 376 threshold = g_trace.specs[i].level; 377 } 378 return threshold > 0 && level <= threshold; 379 } 380 381 static char trace_level_char(int level) { 382 switch (level) { 383 case KIT_TRACE_ERROR: 384 return 'E'; 385 case KIT_TRACE_WARN: 386 return 'W'; 387 case KIT_TRACE_INFO: 388 return 'I'; 389 case KIT_TRACE_DEBUG: 390 return 'D'; 391 case KIT_TRACE_TRACE: 392 return 'T'; 393 default: 394 return '?'; 395 } 396 } 397 398 void kit_trace_emit(const char* module, int level, const char* file, int line, 399 const char* fmt, ...) { 400 va_list ap; 401 char lc = trace_level_char(level); 402 if (!file) file = "?"; 403 /* Show the module tag unless it is just the source path (the default). */ 404 if (module && strcmp(module, file) != 0) 405 fprintf(stderr, "[%c][%s] %s:%d: ", lc, module, file, line); 406 else 407 fprintf(stderr, "[%c] %s:%d: ", lc, file, line); 408 va_start(ap, fmt); 409 vfprintf(stderr, fmt, ap); 410 va_end(ap); 411 fputc('\n', stderr); 412 } 413 414 void driver_printf(const char* fmt, ...) { 415 va_list ap; 416 va_start(ap, fmt); 417 vprintf(fmt, ap); 418 va_end(ap); 419 } 420 421 void driver_flush_stdout(void) { fflush(stdout); } 422 423 const char* driver_getenv(const char* name) { return getenv(name); } 424 425 int driver_line_completion_add(DriverLineCompletionList* l, const char* text, 426 size_t len) { 427 DriverLineCompletion* ni; 428 char* copy; 429 uint32_t nc; 430 size_t old_size; 431 size_t new_size; 432 if (!l || !l->env || !text) return 1; 433 if (l->count == l->cap) { 434 nc = l->cap ? l->cap * 2u : 16u; 435 old_size = (size_t)l->cap * sizeof(*l->items); 436 new_size = (size_t)nc * sizeof(*l->items); 437 ni = (DriverLineCompletion*)l->env->heap->realloc( 438 l->env->heap, l->items, old_size, new_size, 439 _Alignof(DriverLineCompletion)); 440 if (!ni) return 1; 441 l->items = ni; 442 l->cap = nc; 443 } 444 copy = (char*)driver_alloc(l->env, len + 1u); 445 if (!copy) return 1; 446 driver_memcpy(copy, text, len); 447 copy[len] = '\0'; 448 l->items[l->count].text = copy; 449 l->items[l->count].size = len + 1u; 450 l->count++; 451 return 0; 452 } 453 454 void driver_line_history_fini(DriverEnv* env, DriverLineHistory* h) { 455 uint32_t i; 456 if (!env || !h) return; 457 for (i = 0; i < h->count; ++i) { 458 if (h->items && h->items[i]) driver_free(env, h->items[i], h->sizes[i]); 459 } 460 if (h->items) driver_free(env, h->items, (size_t)h->cap * sizeof(*h->items)); 461 if (h->sizes) driver_free(env, h->sizes, (size_t)h->cap * sizeof(*h->sizes)); 462 { 463 DriverLineHistory z = {0}; 464 *h = z; 465 } 466 }