1 /* Custom preprocessor for LUA pkg files by C. Blue
2 * Use:
3 * Enable usage of #if..#else..#endif control structures in LUA pkg files
4 * by making use of the C preprocessor.
5 * Note:
6 * The LUA file (.pre) will need a #parse statement that includes a header
7 * file containing all #define directives that ought to be used for
8 * evaluating control structures (#if..) within the .pre file.
9 *
10 * Local #define directives within the LUA (.pre) file itself will be ignored
11 * for evaluating local control structures!
12 * The reason for this is that 'tolua' actually can and does process
13 * local #define statements already, so they must not be eliminated by
14 * the C preprocessor.
15 *
16 * Local #include directives will be processed normally and insert the
17 * according files into the LUA file (.pre) source code.
18 *
19 * Local define- and include-statements prefixed with a '$' will not be
20 * evaluated by either the preprocessor or tolua and will instead be passed
21 * down to become actual define- and include-statements in the resulting
22 * w_xxxx.c file!
23 */
24
25
26 /* debug mode: Don't remove temporary files */
27 //#define DEBUG
28
29 /* Use custom #parse and #include directives:
30 #parse does what #include actually did, while #include is now a statement
31 to pseudo-include further lua sources, without actually parsing them with
32 the preprocessor - ie it's just for basic text inclusion.
33 This allows to include #define lists such as skills required for spells. */
34 #define CUSTOM_INCLUDE
35
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
main(int argc,char * argv[])41 int main(int argc, char *argv[]) {
42 FILE *f_in, *f_out;
43 char tmp_file[160 + 3], line[320 + 1], *line_ptr, line_mod[320 + 1 + 6];
44 /* 320 + 1 + 6 -> allow +6 extra marker chars (originally '//', but interferes
45 with additionally included header files. So now a 'non-C' sequence instead,
46 ie a character sequence that doesn't occur in valid C code at line beginning.
47 It must also end on a C comment tag if we want the preprocessor to not remove
48 'obsolete' whitespace. This isn't required, but allows for better comparability
49 if the files are viewed by a human. Current sequence therefore: */
50 #ifdef CUSTOM_INCLUDE
51 int included;//bool
52 FILE *f_included;
53 char included_file[160 + 3], included_line[320 + 1], *included_file_ptr, *included_line_ptr;
54 #endif
55 char seq[7] = {'1', '=', '/', '0', '/', '/', 0};
56 int cpp_opts;
57
58 char *cptr, *in_comment = NULL, *out_comment = NULL, *prev_in_comment;
59 int in_comment_block = 0;
60
61
62 /* check command-line arguments */
63
64 if (argc < 4) {
65 printf("Usage: preproc <input file> <output file> <name of C preprocessor> [<C preprocessor options>..]\n");
66 printf(" C preprocessor options should usually (cpp) be: -C -P\n");
67 return -1;
68 }
69 if (strlen(argv[1]) == 0) { /* paranoia */
70 printf("Error: No input filename specified.\n");
71 return -2;
72 }
73 if (strlen(argv[1]) > 160) {
74 printf("Error: Input filename must not be longer than 160 characters.\n");
75 return -3;
76 }
77 if (strlen(argv[2]) == 0) { /* paranoia */
78 printf("Error: No output filename specified.\n");
79 return -4;
80 }
81
82
83 /* open file */
84 f_in = fopen(argv[1], "r");
85 if (f_in == NULL) {
86 printf("Error: Couldn't open input file.\n");
87 return -5;
88 }
89
90 sprintf(tmp_file, "%s_", argv[1]);
91 f_out = fopen(tmp_file, "w");
92 if (f_out == NULL) {
93 printf("Error: Couldn't create temporary file.\n");
94 return -6;
95 }
96
97 /* create temporary working copy */
98 while (fgets(line, 320, f_in)) {
99 /* skip whitespaces at the beginning */
100 line_ptr = line;
101 while (*line_ptr == ' ' || *line_ptr == '\t') line_ptr++;
102 /* copy it */
103 fputs(line_ptr, f_out);
104 }
105 fclose(f_in);
106 fclose(f_out);
107
108
109 #ifdef CUSTOM_INCLUDE
110 do {
111 included = 0;
112
113 /* pseudo-#include other source files recursively until no more #includes are found */
114 sprintf(tmp_file, "%s_", argv[1]);
115 f_in = fopen(tmp_file, "r");
116 if (f_in == NULL) {
117 printf("Error: Couldn't open custom-including input file.\n");
118 return -11;
119 }
120
121 sprintf(tmp_file, "%s__", argv[1]);
122 f_out = fopen(tmp_file, "w");
123 if (f_out == NULL) {
124 printf("Error: Couldn't create custom-including temporary file.\n");
125 return -12;
126 }
127
128 /* read a line */
129 while (fgets(line, 320, f_in)) {
130 line_ptr = line;
131
132 /* actually process #include directive in our own, special way, same as all existing #defines:
133 we just add/insert them to the LUA (.pre) source! */
134 if (strstr(line_ptr, "#include") != line_ptr) {
135 /* copy normal lines without any change */
136 fputs(line, f_out);
137 continue;
138 }
139
140 /* discard line, since WE do the work here, cpp won't get to see this #include at all */
141 //hardcore!
142 included = 1;
143
144 /* extract file name from #include directive, doesn't matter if in quotations or <..> */
145 strcpy(included_file, line_ptr + 8);
146 included_file_ptr = included_file;
147 /* strip trailing spaces and tabs and newline */
148 while (included_file[strlen(included_file) - 1] == ' ' ||
149 included_file[strlen(included_file) - 1] == '\t' ||
150 included_file[strlen(included_file) - 1] == '\n')
151 included_file[strlen(included_file) - 1] = '\0';
152 /* strip final " or > */
153 if (included_file[strlen(included_file) - 1] == '"' ||
154 included_file[strlen(included_file) - 1] == '>')
155 included_file[strlen(included_file) - 1] = '\0';
156 /* strip leading tabs and spaces */
157 while (*included_file_ptr == ' ' ||
158 *included_file_ptr == '\t')
159 included_file_ptr++;
160 /* strip first " or < */
161 if (*included_file_ptr == '"' ||
162 *included_file_ptr == '<')
163 included_file_ptr++;
164
165 f_included = fopen(included_file_ptr, "r");
166 if (f_included == NULL) {
167 printf("Error: Couldn't open included file: %s\n", included_file_ptr);
168 return -10;
169 }
170
171 fputs("\n", f_out);
172 sprintf(tmp_file, "/* ---------------- #include '%s' ---------------- */\n", included_file_ptr);
173 fputs(tmp_file, f_out);
174
175 /* copy included file line by line */
176 while (fgets(included_line, 320, f_included)) {
177 /* skip whitespaces at the beginning */
178 included_line_ptr = included_line;
179 while (*included_line_ptr == ' ' || *included_line_ptr == '\t') included_line_ptr++;
180 /* copy it */
181 fputs(included_line_ptr, f_out);
182 }
183
184 sprintf(tmp_file, "/* ------------ end of #include '%s'. ------------ */\n", included_file);
185 fputs(tmp_file, f_out);
186 fputs("\n", f_out);
187 }
188
189 fclose(f_in);
190 fclose(f_out);
191
192 /* the dest file becomes the src file, for next pass (recursion) */
193 sprintf(line, "%s_", argv[1]);
194 remove(line);
195 sprintf(tmp_file, "%s__", argv[1]);
196 rename(tmp_file, line);
197 } while (included);
198 #endif
199
200 /* create temporary file that can be preprocessed */
201
202 /* open file */
203 sprintf(tmp_file, "%s_", argv[1]);
204 f_in = fopen(tmp_file, "r");
205 if (f_in == NULL) {
206 printf("Error: Couldn't open input file.\n");
207 return -5;
208 }
209
210 sprintf(tmp_file, "%s__", argv[1]);
211 f_out = fopen(tmp_file, "w");
212 if (f_out == NULL) {
213 printf("Error: Couldn't create temporary file.\n");
214 return -6;
215 }
216
217 /* read a line */
218 while (fgets(line, 320, f_in)) {
219 /* trim newline (at the end) */
220 line_ptr = strchr(line, '\n');
221 if (line_ptr) *line_ptr = '\0';
222
223 /* skip whitespaces at the beginning (redundant, done in 'working copy') */
224 line_ptr = line;
225 while (*line_ptr == ' ' || *line_ptr == '\t') line_ptr++;
226
227 #ifndef CUSTOM_INCLUDE
228 /* forward #parse directives as #include directives to the preprocessor
229 so it can process #if/#else/#endif structures accordingly */
230 if (strstr(line_ptr, "#include") == line_ptr) {
231 /* keep line as it is, so it will be processed by the C preprocessor */
232 sprintf(line_mod, "%s\n", line);
233 #else
234 /* forward #parse directives as #include directives to the preprocessor
235 so it can process #if/#else/#endif structures accordingly */
236 if (strstr(line_ptr, "#parse") == line_ptr) {
237 /* replace "#parse" by "#include" */
238 sprintf(line_mod, "%s\n", line);
239 cptr = strstr(line_mod, "#parse");
240 strcpy(cptr, "#include");
241 strcat(cptr, line_ptr + 6);
242 strcat(cptr, "\n");
243 #endif
244 #if 0 /* don't include these, way too messy/can't work */
245 } else if (strstr(line_ptr, "$#include") == line_ptr) {
246 /* turn it into a normal #include so it will be processed by the C preprocessor */
247 sprintf(line_mod, "%s\n", line_ptr + 1);
248 #endif
249
250 /* test for #if / #else / #endif at the beginning
251 of the line (after whitespaces have been trimmed).
252 Any other preprocessor directives could be added. */
253 } else if (strstr(line_ptr, "#if") == line_ptr ||
254 strstr(line_ptr, "#else") == line_ptr ||
255 strstr(line_ptr, "#endif") == line_ptr) {
256 /* keep line as it is, so it will get treated by the C preprocessor */
257 sprintf(line_mod, "%s\n", line);
258
259 /* normal line - protect it from being changed by the C preprocessor */
260 } else {
261 /* add the marker that indicates a line that is not to be touched */
262 sprintf(line_mod, "%s%s\n", seq, line);
263 }
264
265 /* write modified line to temporary file */
266 fputs(line_mod, f_out);
267 }
268
269 fclose(f_out);
270 fclose(f_in);
271
272
273 /* call C preprocessor on temporary file */
274
275 sprintf(line, "%s ", argv[3]);
276 for (cpp_opts = 5; cpp_opts <= argc; cpp_opts++) {
277 strcat(line, argv[cpp_opts - 1]);
278 strcat(line, " ");
279 }
280 strcat(line, tmp_file);
281 strcat(line, " ");
282 #if 0 /* This works if we just invoke 'cpp', but it doesn't if the system doesn't \
283 have a 'cpp' command and we need to fallback to 'gcc', because gcc \
284 doesn't take another file name parm in the same syntax as cpp. */
285 strcat(line, tmp_file);
286 strcat(line, "_");
287 #else /* So instead we'll just grab the stdout output, which works fine with both */
288 strcat(line, "> ");
289 strcat(line, tmp_file);
290 strcat(line, "_");
291 #endif
292
293 if (system(line) == -1) {
294 printf("Error: Couldn't execute C preprocessor.\n");
295 return -7;
296 }
297
298 #ifndef DEBUG
299 remove(tmp_file);
300 #endif
301
302
303 /* clean up preprocessed file to generate output file */
304
305 sprintf(tmp_file, "%s___", argv[1]);
306
307 f_in = fopen(tmp_file, "r");
308 if (f_in == NULL) {
309 printf("Error: Couldn't open preprocessed temporary file.\n");
310 return -8;
311 }
312
313 f_out = fopen(argv[2], "w");
314 if (f_out == NULL) {
315 printf("Error: Couldn't create output file.\n");
316 return -9;
317 }
318
319 /* read a line */
320 while (fgets(line_mod, 320 + 2, f_in)) {
321 /* preprocessor directives that were hidden _inside_ comments
322 because silly tolua will choke on them */
323 prev_in_comment = line_mod;
324 do {
325 if (out_comment) in_comment = out_comment = NULL;
326
327 if (!in_comment) {
328 if (in_comment_block) in_comment = line_mod;
329 else {
330 in_comment = strstr(prev_in_comment, "/*");
331 if (in_comment) prev_in_comment = in_comment + 2;
332 }
333 }
334 if (in_comment) {
335 if (in_comment_block) out_comment = strstr(line_mod, "*/");
336 else out_comment = strstr(in_comment, "*/");
337
338 while ((cptr = strstr(in_comment, "#"))) {
339 if (!out_comment || cptr < out_comment) {
340 *cptr = ' ';
341 } else break;
342 }
343
344 if (out_comment) in_comment_block = 0;
345 }
346 } while (in_comment && out_comment);
347 if (in_comment) in_comment_block = 1;
348
349 /* aaand also strip comments that are adjacent, silyl tolua */
350 if ((cptr = strstr(line_mod, "*//*"))) {
351 cptr[0] = ' ';
352 cptr[1] = ' ';
353 cptr[2] = ' ';
354 cptr[3] = ' ';
355 }
356
357 /* gcc 4.8.0 now puts an URL in the top comment, on which tolua
358 chokes, sigh. */
359 if ((cptr = strstr(line_mod, "http://www.gnu.org"))) cptr[5] = ':';
360
361 /* on to the actual work.. */
362
363
364 /* strip prefixed marker sequence again and write line to output file */
365 if (line_mod[0] == seq[0] &&
366 line_mod[1] == seq[1] &&
367 line_mod[2] == seq[2] &&
368 line_mod[3] == seq[3] &&
369 line_mod[4] == seq[4] &&
370 line_mod[5] == seq[5])
371 fputs(line_mod + 6, f_out);
372 /* lines that were eliminated by the preprocessor don't need any treatment */
373 else fputs(line_mod, f_out);
374 }
375
376 fclose(f_out);
377 fclose(f_in);
378
379 #ifndef DEBUG
380 remove(tmp_file);
381 #endif
382
383
384 /* all done */
385
386 return 0;
387 }
388
389 #ifdef DUMB_WIN
390 int FAR PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
391 LPSTR lpCmdLine, int nCmdShow) {
392 MSG msg;
393
394 /* Process messages forever */
395 while (GetMessage(&msg, NULL, 0, 0)) {
396 TranslateMessage(&msg);
397 DispatchMessage(&msg);
398 }
399
400 main(0, NULL);
401 return (0);
402 }
403 #endif
404