aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-01-14 23:12:49 +0900
committernsfisis <nsfisis@gmail.com>2026-01-17 00:27:28 +0900
commitf0351da192b8f32bdf9323b3796521cbc390c749 (patch)
tree789fbf7d954a2dd43bf01c80d1ae959a2a807729
parent82df1b08c42d40c79b0dbd1634e18786cad860da (diff)
downloadducc-f0351da192b8f32bdf9323b3796521cbc390c749.tar.gz
ducc-f0351da192b8f32bdf9323b3796521cbc390c749.tar.zst
ducc-f0351da192b8f32bdf9323b3796521cbc390c749.zip
feat: support #include_next directive (GNU extension)
-rw-r--r--src/preprocess.c51
-rw-r--r--src/token.c7
-rw-r--r--src/token.h1
-rw-r--r--src/tokenize.c5
4 files changed, 60 insertions, 4 deletions
diff --git a/src/preprocess.c b/src/preprocess.c
index 54e3648..869b069 100644
--- a/src/preprocess.c
+++ b/src/preprocess.c
@@ -330,7 +330,7 @@ static void make_tokens_removed(Preprocessor* pp, int start, int end) {
static Token* read_include_header_name(Preprocessor* pp) {
Token* tok = next_pp_token(pp);
if (tok->kind != TokenKind_header_name) {
- fatal_error("%s:%d: invalid #include", tok->loc.filename, tok->loc.line);
+ fatal_error("%s:%d: invalid #include, %s", tok->loc.filename, tok->loc.line, token_stringify(tok));
}
return tok;
}
@@ -355,6 +355,23 @@ static const char* resolve_include_name(Preprocessor* pp, const Token* include_n
}
}
+static const char* resolve_next_include_name(Preprocessor* pp, const Token* include_name_token) {
+ const char* include_name = include_name_token->value.string;
+ char* current_filename = strdup(include_name_token->loc.filename);
+ for (size_t i = 0; i < pp->include_paths.len; ++i) {
+ char* buf = calloc(strlen(include_name) - 2 + 1 + strlen(pp->include_paths.data[i]) + 1, sizeof(char));
+ sprintf(buf, "%s/%.*s", pp->include_paths.data[i], (int)(strlen(include_name) - 2), include_name + 1);
+ if (strcmp(buf, current_filename) == 0) {
+ // #include_next skips the same file.
+ continue;
+ }
+ if (access(buf, F_OK | R_OK) == 0) {
+ return buf;
+ }
+ }
+ return NULL;
+}
+
static int replace_pp_tokens(Preprocessor* pp, int dest_start, int dest_end, TokenArray* source_tokens) {
size_t n_tokens_to_remove = dest_end - dest_start;
size_t n_tokens_after_dest = pp->pp_tokens->len - dest_end;
@@ -1061,6 +1078,36 @@ static void preprocess_include_directive(Preprocessor* pp) {
expand_include_directive(pp, include_name_resolved, include_name);
}
+// #include_next is a part of GNU extension.
+// https://gcc.gnu.org/onlinedocs/cpp/Wrapper-Headers.html
+static void preprocess_include_next_directive(Preprocessor* pp) {
+ skip_pp_token(pp, TokenKind_pp_directive_include_next);
+ skip_whitespaces(pp);
+ Token* include_name = read_include_header_name(pp);
+ const char* include_name_resolved = resolve_next_include_name(pp, include_name);
+ if (include_name_resolved == NULL) {
+ fatal_error("%s:%d: cannot resolve include file name: %s", include_name->loc.filename, include_name->loc.line,
+ token_stringify(include_name));
+ }
+
+ if (include_name->value.string[0] == '"') {
+ bool already_included = false;
+ for (size_t i = 0; i < pp->included_files->len; ++i) {
+ if (strcmp(pp->included_files->data[i], include_name_resolved) == 0) {
+ already_included = true;
+ break;
+ }
+ }
+ if (!already_included) {
+ strings_push(pp->included_files, include_name_resolved);
+ }
+ }
+
+ skip_whitespaces(pp);
+ expect_pp_token(pp, TokenKind_newline);
+ expand_include_directive(pp, include_name_resolved, include_name);
+}
+
static void preprocess_embed_directive(Preprocessor*) {
unimplemented();
}
@@ -1222,6 +1269,8 @@ static void preprocess_group_part(Preprocessor* pp) {
token_kind_stringify(tok->kind));
} else if (tok->kind == TokenKind_pp_directive_include) {
preprocess_include_directive(pp);
+ } else if (tok->kind == TokenKind_pp_directive_include_next) {
+ preprocess_include_next_directive(pp);
} else if (tok->kind == TokenKind_pp_directive_embed) {
preprocess_embed_directive(pp);
} else if (tok->kind == TokenKind_pp_directive_define) {
diff --git a/src/token.c b/src/token.c
index ebb803e..a7b2afd 100644
--- a/src/token.c
+++ b/src/token.c
@@ -47,6 +47,8 @@ const char* token_kind_stringify(TokenKind k) {
return "#ifndef";
else if (k == TokenKind_pp_directive_include)
return "#include";
+ else if (k == TokenKind_pp_directive_include_next)
+ return "#include_next";
else if (k == TokenKind_pp_directive_line)
return "#line";
else if (k == TokenKind_pp_directive_non_directive)
@@ -283,8 +285,9 @@ bool is_pp_directive(TokenKind k) {
k == TokenKind_pp_directive_else || k == TokenKind_pp_directive_embed || k == TokenKind_pp_directive_endif ||
k == TokenKind_pp_directive_error || k == TokenKind_pp_directive_if || k == TokenKind_pp_directive_ifdef ||
k == TokenKind_pp_directive_ifndef || k == TokenKind_pp_directive_include ||
- k == TokenKind_pp_directive_line || k == TokenKind_pp_directive_non_directive ||
- k == TokenKind_pp_directive_nop || k == TokenKind_pp_directive_pragma || k == TokenKind_pp_directive_undef ||
+ k == TokenKind_pp_directive_include_next || k == TokenKind_pp_directive_line ||
+ k == TokenKind_pp_directive_non_directive || k == TokenKind_pp_directive_nop ||
+ k == TokenKind_pp_directive_pragma || k == TokenKind_pp_directive_undef ||
k == TokenKind_pp_directive_warning;
}
diff --git a/src/token.h b/src/token.h
index e093a8c..b81c10a 100644
--- a/src/token.h
+++ b/src/token.h
@@ -29,6 +29,7 @@ typedef enum {
TokenKind_pp_directive_ifdef,
TokenKind_pp_directive_ifndef,
TokenKind_pp_directive_include,
+ TokenKind_pp_directive_include_next, // GNU extension
TokenKind_pp_directive_line,
TokenKind_pp_directive_non_directive,
TokenKind_pp_directive_nop,
diff --git a/src/tokenize.c b/src/tokenize.c
index 26c6e13..ceba12d 100644
--- a/src/tokenize.c
+++ b/src/tokenize.c
@@ -37,7 +37,7 @@ static void pplexer_tokenize_pp_directive(Lexer* l, Token* tok) {
StrBuilder builder;
strbuilder_init(&builder);
- while (isalnum(infile_peek_char(l->src))) {
+ while (isalnum(infile_peek_char(l->src)) || infile_peek_char(l->src) == '_') {
strbuilder_append_char(&builder, infile_peek_char(l->src));
infile_next_char(l->src);
}
@@ -70,6 +70,9 @@ static void pplexer_tokenize_pp_directive(Lexer* l, Token* tok) {
} else if (strcmp(pp_directive_name, "include") == 0) {
l->expect_header_name = true;
tok->kind = TokenKind_pp_directive_include;
+ } else if (strcmp(pp_directive_name, "include_next") == 0) {
+ l->expect_header_name = true;
+ tok->kind = TokenKind_pp_directive_include_next;
} else if (strcmp(pp_directive_name, "line") == 0) {
tok->kind = TokenKind_pp_directive_line;
} else if (strcmp(pp_directive_name, "pragma") == 0) {