Last active
September 8, 2024 12:44
-
-
Save silverweed/0d1a029a21eb364f55e1a3c61772a4d1 to your computer and use it in GitHub Desktop.
resolve_includes.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given the string `input`, replaces every instance of `#include "file"` with the contents | |
// of `file` (relative to `include_dir`). The returned string aliases `input` if no includes were found, | |
// otherwise it's a new copy with the same lifetime as `arena`. | |
String8 file_resolve_includes(Arena *arena, String8 input, String8 include_dir) | |
{ | |
String8 output = {}; | |
String8 include_str = str8("#include"); | |
Temp scratch = scratch_begin(&arena, 1); | |
// locate all include directives | |
struct Include { | |
usz pos; | |
usz len; // length of the include directive, up to the last '"' | |
String8 file; | |
}; | |
struct Include includes[256] = {}; | |
u64 n_includes = 0; | |
u64 pos = 0; | |
while (pos < input.size - include_str.size) { | |
const char *include_pos = strstr(cstr(input) + pos, cstr(include_str)); | |
if (!include_pos) | |
break; | |
if (n_includes == countof(includes)) { | |
ERR_TAG("Files", "Too many #include directives."); | |
return output; | |
} | |
struct Include inc; | |
inc.pos = (u8 *)include_pos - input.str; | |
// parse file name | |
pos = inc.pos + include_str.size; | |
u8 c; | |
do { | |
c = input.str[pos++]; | |
} while (pos < input.size && (c == ' ' || c == '\t')); | |
if (c != '"') { | |
ERR_TAG("Files", "Error resolving includes: expected '\"' after #include, found %c (at pos %zu).\n", c, pos - 1); | |
u8 code[50] = {}; | |
memcpy(code, input.str + pos - 20, countof(code)); | |
ERR_TAG("Files", "Code around pos:\n%s\n", code); | |
assert(false); | |
return output; | |
} | |
u8 *included_file_start = input.str + pos; | |
usz included_file_size = 0; | |
b8 found_closing_quote = false; | |
while (pos < input.size) { | |
c = input.str[pos++]; | |
if (c == '"') { | |
found_closing_quote = true; | |
break; | |
} | |
++included_file_size; | |
} | |
if (!found_closing_quote) { | |
ERR_TAG("Files", "Error resolving includes: unterminated string after #include directive.\n"); | |
assert(false); | |
return output; | |
} | |
if (included_file_size == 0) { | |
ERR_TAG("Files", "Error resolving includes: empty string in #include directive.\n"); | |
assert(false); | |
return output; | |
} | |
String8 included_file = str8_from_buf(scratch.arena, included_file_start, included_file_size); | |
inc.file = push_str8f(scratch.arena, "%s/%s", cstr(include_dir), cstr(included_file)); | |
inc.len = pos - inc.pos; | |
includes[n_includes++] = inc; | |
} | |
// Copy content into output | |
if (n_includes == 0) { | |
output = input; | |
return output; | |
} | |
u64 output_tot_bytes = input.size; | |
for (u64 i = 0; i < n_includes; ++i) { | |
FILE *f = fopen(cstr(includes[i].file), "r"); | |
if (!f) { | |
ERR_TAG("Files", "Failed to open included file %s", cstr(includes[i].file)); | |
return output; | |
} | |
output_tot_bytes += file_size(f); | |
output_tot_bytes -= includes[i].len; | |
fclose(f); | |
} | |
output.size = output_tot_bytes; | |
output.str = arena_push_array_nozero(u8, arena, output.size + 1); | |
output.str[output.size] = 0; | |
usz input_pos = 0; | |
usz output_pos = 0; | |
for (u64 inc_idx = 0; inc_idx < n_includes; ++inc_idx) { | |
struct Include *inc = &includes[inc_idx]; | |
assert(inc->pos >= input_pos); | |
usz input_len = inc->pos - input_pos; | |
if (input_len > 0) { | |
memcpy(output.str + output_pos, input.str + input_pos, input_len); | |
input_pos += input_len; | |
output_pos += input_len; | |
} | |
u64 arena_pre_pos = arena_pos(scratch.arena); | |
String8 inc_file_content = file_read_to_string(scratch.arena, inc->file); | |
if (inc_file_content.size > 0) { | |
memcpy(output.str + output_pos, inc_file_content.str, inc_file_content.size); | |
input_pos += inc->len; | |
output_pos += inc_file_content.size; | |
} | |
arena_pop_to(scratch.arena, arena_pre_pos); | |
assert(output_pos < output.size); | |
} | |
usz rem = input.size - input_pos; | |
assert(rem == output.size - output_pos); | |
if (rem > 0) | |
memcpy(output.str + output_pos, input.str + input_pos, rem); | |
return output; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment