mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-15 06:06:04 +00:00
155 lines
6 KiB
C
155 lines
6 KiB
C
|
|
||
|
// SPDX-FileCopyrightText: © 2024 ZeldaRET
|
||
|
// SPDX-License-Identifier: CC0-1.0
|
||
|
|
||
|
// Usage: preprocess_pragmas OOT_VERSION filename < source.c
|
||
|
// The filename argument is only used for linemarkers.
|
||
|
// Preprocess C source on stdin, writes to stdout
|
||
|
// Replace `#pragma increment_block_number` with fake structs for controlling BSS ordering.
|
||
|
// The names of these fake structs are expected to be increment_block_number_%d_%d with the first number indicating
|
||
|
// the line number of the #pragma in the original source file. (this is for use by fix_bss.py)
|
||
|
|
||
|
#include <assert.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stddef.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
const char str_pragma_increment_block_number[] = "#pragma increment_block_number";
|
||
|
|
||
|
int main(int argc, char** argv) {
|
||
|
if (argc != 3) {
|
||
|
fprintf(stderr, "Usage: preprocess_pragmas OOT_VERSION filename < source.c\n");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
char* const version = argv[1];
|
||
|
const int len_version = strlen(version);
|
||
|
char* const filename = argv[2];
|
||
|
|
||
|
char buf[32 * 1024];
|
||
|
char* const bufend = buf + sizeof(buf);
|
||
|
char* bufp = buf;
|
||
|
bool cont = true;
|
||
|
int line_num = 1;
|
||
|
// whether the current line follows a #pragma increment_block_number,
|
||
|
// including continuation lines (lines after a \-ending line)
|
||
|
bool is_in_pragma = false;
|
||
|
// the line where the #pragma increment_block_number is
|
||
|
int pragma_line_number;
|
||
|
// how many fake structs to write to replace the current pragma
|
||
|
int n_fake_structs;
|
||
|
|
||
|
while (cont) {
|
||
|
size_t nread = fread(bufp, 1, bufend - bufp, stdin);
|
||
|
bufp += nread;
|
||
|
if (nread == 0) {
|
||
|
if (!feof(stdin)) {
|
||
|
perror("fread");
|
||
|
fprintf(stderr, "Failed to read from stdin\n");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
cont = false;
|
||
|
if (bufp == buf) {
|
||
|
// All lines processed
|
||
|
break;
|
||
|
} else {
|
||
|
// The buffer contains the last line and that line isn't terminated with a newline.
|
||
|
// Add a final newline and do one last iteration.
|
||
|
assert(bufp < bufend);
|
||
|
*bufp = '\n';
|
||
|
bufp++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
char* last_newline = NULL;
|
||
|
for (char* p = bufp - 1; p >= buf; p--) {
|
||
|
if (*p == '\n') {
|
||
|
last_newline = p;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (last_newline == NULL) {
|
||
|
// No newline, read more data.
|
||
|
// Assert there is space for it (there should be no line long enough to not fit in buf).
|
||
|
assert(bufp < bufend);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
char* line = buf;
|
||
|
while (true) {
|
||
|
char* line_end = line;
|
||
|
while (*line_end != '\n') {
|
||
|
line_end++;
|
||
|
assert(line_end <= last_newline);
|
||
|
}
|
||
|
if (!strncmp(line, str_pragma_increment_block_number, strlen(str_pragma_increment_block_number))) {
|
||
|
is_in_pragma = true;
|
||
|
pragma_line_number = line_num;
|
||
|
n_fake_structs = 0;
|
||
|
}
|
||
|
if (is_in_pragma) {
|
||
|
*line_end = '\0';
|
||
|
char* version_amount_item = strstr(line, version);
|
||
|
if (version_amount_item != NULL) {
|
||
|
if (version_amount_item[len_version] != ':') {
|
||
|
fprintf(stderr, "Found version %s in pragma line but no :amount attached\n", version);
|
||
|
fprintf(stderr, "%s\n", line);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
char* version_amount_str_start = &version_amount_item[len_version + 1];
|
||
|
char* version_amount_str_end;
|
||
|
long amount = strtol(version_amount_str_start, &version_amount_str_end, 10);
|
||
|
if (version_amount_str_start == version_amount_str_end) {
|
||
|
fprintf(stderr, "Found version %s in pragma line but no amount integer\n", version);
|
||
|
fprintf(stderr, "%s\n", line);
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
n_fake_structs = (int)amount;
|
||
|
}
|
||
|
} else {
|
||
|
char* p = line;
|
||
|
size_t sz = line_end + 1 - line;
|
||
|
while (sz != 0) {
|
||
|
size_t nwritten = fwrite(p, 1, sz, stdout);
|
||
|
if (nwritten == 0) {
|
||
|
fprintf(stderr, "Failed to write to stdout\n");
|
||
|
return EXIT_FAILURE;
|
||
|
}
|
||
|
p += nwritten;
|
||
|
sz -= nwritten;
|
||
|
}
|
||
|
}
|
||
|
if (is_in_pragma && line_end[-1] != '\\') {
|
||
|
is_in_pragma = false;
|
||
|
|
||
|
// Always generate at least one struct,
|
||
|
// so that fix_bss.py can know where the increment_block_number pragmas are
|
||
|
if (n_fake_structs == 0) {
|
||
|
n_fake_structs = 256;
|
||
|
}
|
||
|
|
||
|
// Write fake structs for BSS ordering
|
||
|
// pragma_line_number is used for symbol uniqueness,
|
||
|
// and also by fix_bss.py to locate the pragma these symbols originate from.
|
||
|
for (int i = 0; i < n_fake_structs; i++)
|
||
|
fprintf(stdout, "struct increment_block_number_%05d_%03d;\n", pragma_line_number, i);
|
||
|
fprintf(stdout, "#line %d \"%s\"\n", line_num + 1, filename);
|
||
|
}
|
||
|
line_num++;
|
||
|
if (line_end == last_newline)
|
||
|
break;
|
||
|
line = line_end + 1;
|
||
|
}
|
||
|
assert(bufp <= bufend);
|
||
|
assert(bufp > last_newline);
|
||
|
char* next_incomplete_line_start = last_newline + 1;
|
||
|
ptrdiff_t next_incomplete_line_sz = bufp - next_incomplete_line_start;
|
||
|
assert(next_incomplete_line_sz >= 0);
|
||
|
memmove(buf, next_incomplete_line_start, next_incomplete_line_sz);
|
||
|
bufp = buf + next_incomplete_line_sz;
|
||
|
}
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
}
|