forked from zeldaret/oot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpreprocess_pragmas.c
154 lines (141 loc) · 5.87 KB
/
preprocess_pragmas.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// 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];
char* const filename = argv[2];
const size_t len_version = strlen(version);
char version_needle[len_version + 2];
memcpy(version_needle, version, len_version);
version_needle[len_version] = ':';
version_needle[len_version + 1] = '\0';
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_needle);
if (version_amount_item != NULL) {
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;
}