kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit c9baaf8f7d59e4bed9f7dc7d280dc692945c5d86
parent 89191859b440d20f149e51762e4f888b69116955
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Mon, 11 May 2026 10:59:19 -0700

pp: search includer's directory first for quoted #include

Per C §6.10.2, `#include "x.h"` resolves relative to the directory of
the file containing the directive. Previously the quoted form tried the
path verbatim (CWD-relative) and then walked -I dirs, which only worked
when the driver happened to be invoked from the source's directory.

find_and_open_include now derives the includer's dir from loc.file_id
(falling back to CWD for synthetic sources like <command-line>) and
tries it ahead of the -I search. Absolute paths bypass both and open
verbatim. Bracket form is unchanged.

Diffstat:
Msrc/pp/pp.c | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 68 insertions(+), 9 deletions(-)

diff --git a/src/pp/pp.c b/src/pp/pp.c @@ -1241,23 +1241,82 @@ static int try_open_include(Pp* pp, const char* path, const u8** data_out, return 1; } -/* Search for a header. Quoted form ("...") tries the path verbatim first - * (covering relative-to-CWD which is dir-of-current for the tests), - * then walks user and system include dirs. Bracket form (<...>) skips the - * verbatim attempt. */ +/* Return the includer's directory for resolving a quoted include, or "." + * for in-memory/builtin sources (where CWD is the natural fallback, like + * gcc treats stdin). `dir_out` must point to a buffer of size >= cap. */ +static int includer_dir(Pp* pp, SrcLoc loc, char* dir_out, size_t cap) { + const SourceFile* sf = source_file(pp->c->sources, loc.file_id); + const char* p = NULL; + size_t plen = 0; + const char* slash; + size_t dlen; + if (sf && sf->name) p = pool_str(pp->c->global, sf->name, &plen); + if (!p || plen == 0 || p[0] == '<') { + if (cap < 2) return 0; + dir_out[0] = '.'; + dir_out[1] = 0; + return 1; + } + slash = NULL; + { + size_t i; + for (i = plen; i > 0; --i) { + if (p[i - 1] == '/') { + slash = p + i - 1; + break; + } + } + } + if (!slash) { + if (cap < 2) return 0; + dir_out[0] = '.'; + dir_out[1] = 0; + return 1; + } + dlen = (size_t)(slash - p); + if (dlen == 0) dlen = 1; /* path was "/x" — dir is "/" */ + if (dlen + 1 > cap) return 0; + memcpy(dir_out, p, dlen); + dir_out[dlen] = 0; + return 1; +} + +/* Search for a header. Absolute paths are opened verbatim. Quoted form + * ("...") additionally searches the directory of the file containing the + * #include first (per C §6.10.2); bracket form (<...>) skips that step. + * Both forms then walk the configured -I / -isystem dirs in order. */ static int find_and_open_include(Pp* pp, const char* path, int system, - const u8** data, size_t* size, char* resolved, - size_t resolved_cap) { + SrcLoc loc, const u8** data, size_t* size, + char* resolved, size_t resolved_cap) { char buf[4096]; u32 i; size_t plen = strlen(path); - if (!system) { + if (plen > 0 && path[0] == '/') { if (try_open_include(pp, path, data, size)) { if (plen + 1 > resolved_cap) return 0; memcpy(resolved, path, plen + 1); return 1; } + return 0; + } + + if (!system) { + char dir[4096]; + if (includer_dir(pp, loc, dir, sizeof(dir))) { + size_t dlen = strlen(dir); + if (dlen + 1 + plen + 1 <= sizeof(buf)) { + memcpy(buf, dir, dlen); + buf[dlen] = '/'; + memcpy(buf + dlen + 1, path, plen); + buf[dlen + 1 + plen] = 0; + if (try_open_include(pp, buf, data, size)) { + if (dlen + 1 + plen + 1 > resolved_cap) return 0; + memcpy(resolved, buf, dlen + 1 + plen + 1); + return 1; + } + } + } } for (i = 0; i < pp->ninc_dirs; ++i) { const char* d = pp->inc_dirs[i].path; @@ -1365,7 +1424,7 @@ static void do_include(Pp* pp, const Tok* line, u32 n, SrcLoc loc) { parse_include_path(pp, line, n, loc, path, sizeof(path), &system_form); - if (!find_and_open_include(pp, path, system_form, &data, &size, resolved, + if (!find_and_open_include(pp, path, system_form, loc, &data, &size, resolved, sizeof(resolved))) { compiler_panic(pp->c, loc, "#include: file not found: %s", path); } @@ -1709,7 +1768,7 @@ static void do_embed(Pp* pp, const Tok* line, u32 n, SrcLoc loc) { compiler_panic(pp->c, loc, "#embed: unexpected token in parameter list"); } - if (!find_and_open_include(pp, path, system_form, &data, &size, resolved, + if (!find_and_open_include(pp, path, system_form, loc, &data, &size, resolved, sizeof(resolved))) { compiler_panic(pp->c, loc, "#embed: file not found: %s", path); }