|
10 | 10 | #include <vector> |
11 | 11 | #include <cstring> |
12 | 12 | #include <cstdlib> |
| 13 | +#include <cstdint> |
13 | 14 |
|
14 | 15 | extern "C" { |
15 | 16 |
|
16 | | -char* cbm_preprocess( |
17 | | - const char* source, int source_len, |
18 | | - const char* filename, |
19 | | - const char** extra_defines, |
20 | | - const char** include_paths, |
21 | | - int cpp_mode |
22 | | -) { |
23 | | - if (!source || source_len <= 0) return NULL; |
24 | | - |
25 | | - // Fast-path: skip if no preprocessor directives worth expanding. |
26 | | - bool has_macros = false; |
| 17 | +static bool has_preprocessor_work(const char *source, int source_len) { |
| 18 | + if (!source || source_len <= 0) { |
| 19 | + return false; |
| 20 | + } |
27 | 21 | for (int i = 0; i < source_len - 1; i++) { |
28 | 22 | if (source[i] == '#') { |
29 | 23 | // Skip whitespace after # |
30 | 24 | int j = i + 1; |
31 | | - while (j < source_len && (source[j] == ' ' || source[j] == '\t')) j++; |
| 25 | + while (j < source_len && (source[j] == ' ' || source[j] == '\t')) |
| 26 | + j++; |
32 | 27 | int remaining = source_len - j; |
33 | 28 | if (remaining >= 6 && strncmp(source + j, "define", 6) == 0) { |
34 | | - has_macros = true; |
35 | | - break; |
| 29 | + return true; |
36 | 30 | } |
37 | 31 | if (remaining >= 5 && strncmp(source + j, "ifdef", 5) == 0) { |
38 | | - has_macros = true; |
39 | | - break; |
| 32 | + return true; |
40 | 33 | } |
41 | 34 | if (remaining >= 6 && strncmp(source + j, "ifndef", 6) == 0) { |
42 | | - has_macros = true; |
43 | | - break; |
| 35 | + return true; |
44 | 36 | } |
45 | 37 | if (remaining >= 3 && strncmp(source + j, "if ", 3) == 0) { |
46 | | - has_macros = true; |
47 | | - break; |
| 38 | + return true; |
48 | 39 | } |
49 | 40 | } |
50 | 41 | } |
51 | | - if (!has_macros) return NULL; // NULL = no expansion needed, use original |
| 42 | + return false; |
| 43 | +} |
| 44 | + |
| 45 | +static int count_expanded_lines(const std::string &text) { |
| 46 | + int count = 1; |
| 47 | + for (char c : text) { |
| 48 | + if (c == '\n') { |
| 49 | + count++; |
| 50 | + } |
| 51 | + } |
| 52 | + return count; |
| 53 | +} |
| 54 | + |
| 55 | +static bool parse_line_directive(const char *line, size_t len, uint32_t *out_line, |
| 56 | + std::string *out_file) { |
| 57 | + size_t i = 0; |
| 58 | + while (i < len && (line[i] == ' ' || line[i] == '\t')) { |
| 59 | + i++; |
| 60 | + } |
| 61 | + if (i >= len || line[i++] != '#') { |
| 62 | + return false; |
| 63 | + } |
| 64 | + while (i < len && (line[i] == ' ' || line[i] == '\t')) { |
| 65 | + i++; |
| 66 | + } |
| 67 | + static const char prefix[] = "line"; |
| 68 | + if (i + sizeof(prefix) - 1 > len || strncmp(line + i, prefix, sizeof(prefix) - 1) != 0) { |
| 69 | + return false; |
| 70 | + } |
| 71 | + i += sizeof(prefix) - 1; |
| 72 | + if (i >= len || (line[i] != ' ' && line[i] != '\t')) { |
| 73 | + return false; |
| 74 | + } |
| 75 | + while (i < len && (line[i] == ' ' || line[i] == '\t')) { |
| 76 | + i++; |
| 77 | + } |
| 78 | + if (i >= len || line[i] < '0' || line[i] > '9') { |
| 79 | + return false; |
| 80 | + } |
| 81 | + uint64_t parsed_line = 0; |
| 82 | + while (i < len && line[i] >= '0' && line[i] <= '9') { |
| 83 | + parsed_line = parsed_line * 10u + (uint64_t)(line[i] - '0'); |
| 84 | + if (parsed_line > UINT32_MAX) { |
| 85 | + return false; |
| 86 | + } |
| 87 | + i++; |
| 88 | + } |
| 89 | + while (i < len && (line[i] == ' ' || line[i] == '\t')) { |
| 90 | + i++; |
| 91 | + } |
| 92 | + if (i >= len || line[i++] != '"') { |
| 93 | + return false; |
| 94 | + } |
| 95 | + size_t file_start = i; |
| 96 | + while (i < len && line[i] != '"') { |
| 97 | + i++; |
| 98 | + } |
| 99 | + if (i >= len) { |
| 100 | + return false; |
| 101 | + } |
| 102 | + *out_line = (uint32_t)parsed_line; |
| 103 | + *out_file = std::string(line + file_start, i - file_start); |
| 104 | + return true; |
| 105 | +} |
| 106 | + |
| 107 | +static bool build_line_map(const std::string &expanded, const std::string &main_file, |
| 108 | + uint32_t *original_line_by_expanded_line, |
| 109 | + uint8_t *belongs_to_main_file) { |
| 110 | + std::string current_file = main_file; |
| 111 | + uint32_t current_line = 1; |
| 112 | + int expanded_line = 1; |
| 113 | + size_t line_start = 0; |
| 114 | + |
| 115 | + while (line_start <= expanded.size()) { |
| 116 | + size_t line_end = expanded.find('\n', line_start); |
| 117 | + if (line_end == std::string::npos) { |
| 118 | + line_end = expanded.size(); |
| 119 | + } |
| 120 | + |
| 121 | + uint32_t directive_line = 0; |
| 122 | + std::string directive_file; |
| 123 | + if (parse_line_directive(expanded.c_str() + line_start, line_end - line_start, |
| 124 | + &directive_line, &directive_file)) { |
| 125 | + current_file = directive_file; |
| 126 | + current_line = directive_line; |
| 127 | + original_line_by_expanded_line[expanded_line] = 0; |
| 128 | + belongs_to_main_file[expanded_line] = 0; |
| 129 | + } else { |
| 130 | + original_line_by_expanded_line[expanded_line] = current_line; |
| 131 | + belongs_to_main_file[expanded_line] = current_file == main_file ? 1 : 0; |
| 132 | + if (current_line < UINT32_MAX) { |
| 133 | + current_line++; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + if (line_end == expanded.size()) { |
| 138 | + break; |
| 139 | + } |
| 140 | + line_start = line_end + 1; |
| 141 | + expanded_line++; |
| 142 | + } |
| 143 | + return true; |
| 144 | +} |
| 145 | + |
| 146 | +CBMPreprocessedSource *cbm_preprocess_with_map(const char *source, int source_len, |
| 147 | + const char *filename, const char **extra_defines, |
| 148 | + const char **include_paths, int cpp_mode) { |
| 149 | + if (!has_preprocessor_work(source, source_len)) { |
| 150 | + return NULL; // NULL = no expansion needed, use original |
| 151 | + } |
52 | 152 |
|
53 | 153 | try { |
54 | 154 | simplecpp::DUI dui; |
@@ -78,18 +178,58 @@ char* cbm_preprocess( |
78 | 178 | // Clean up loaded file data |
79 | 179 | simplecpp::cleanup(filedata); |
80 | 180 |
|
81 | | - char* out = (char*)malloc(result.size() + 1); |
82 | | - if (!out) return NULL; |
83 | | - memcpy(out, result.c_str(), result.size() + 1); |
84 | | - return out; |
| 181 | + CBMPreprocessedSource *pp = (CBMPreprocessedSource *)calloc(1, sizeof(*pp)); |
| 182 | + if (!pp) { |
| 183 | + return NULL; |
| 184 | + } |
| 185 | + int line_count = count_expanded_lines(result); |
| 186 | + pp->source = (char *)malloc(result.size() + 1); |
| 187 | + pp->original_line_by_expanded_line = |
| 188 | + (uint32_t *)calloc((size_t)line_count + 1u, sizeof(uint32_t)); |
| 189 | + pp->belongs_to_main_file = (uint8_t *)calloc((size_t)line_count + 1u, sizeof(uint8_t)); |
| 190 | + pp->expanded_line_count = line_count; |
| 191 | + if (!pp->source || !pp->original_line_by_expanded_line || !pp->belongs_to_main_file) { |
| 192 | + cbm_preprocessed_source_free(pp); |
| 193 | + return NULL; |
| 194 | + } |
| 195 | + memcpy(pp->source, result.c_str(), result.size() + 1); |
| 196 | + if (!build_line_map(result, files[0], pp->original_line_by_expanded_line, |
| 197 | + pp->belongs_to_main_file)) { |
| 198 | + cbm_preprocessed_source_free(pp); |
| 199 | + return NULL; |
| 200 | + } |
| 201 | + return pp; |
85 | 202 | } catch (...) { |
86 | 203 | // Graceful fallback: return NULL = use original source |
87 | 204 | return NULL; |
88 | 205 | } |
89 | 206 | } |
90 | 207 |
|
91 | | -void cbm_preprocess_free(char* expanded) { |
| 208 | +char *cbm_preprocess(const char *source, int source_len, const char *filename, |
| 209 | + const char **extra_defines, const char **include_paths, int cpp_mode) { |
| 210 | + CBMPreprocessedSource *pp = cbm_preprocess_with_map(source, source_len, filename, extra_defines, |
| 211 | + include_paths, cpp_mode); |
| 212 | + if (!pp) { |
| 213 | + return NULL; |
| 214 | + } |
| 215 | + char *out = pp->source; |
| 216 | + pp->source = NULL; |
| 217 | + cbm_preprocessed_source_free(pp); |
| 218 | + return out; |
| 219 | +} |
| 220 | + |
| 221 | +void cbm_preprocess_free(char *expanded) { |
92 | 222 | free(expanded); |
93 | 223 | } |
94 | 224 |
|
| 225 | +void cbm_preprocessed_source_free(CBMPreprocessedSource *pp) { |
| 226 | + if (!pp) { |
| 227 | + return; |
| 228 | + } |
| 229 | + free(pp->source); |
| 230 | + free(pp->original_line_by_expanded_line); |
| 231 | + free(pp->belongs_to_main_file); |
| 232 | + free(pp); |
| 233 | +} |
| 234 | + |
95 | 235 | } // extern "C" |
0 commit comments