1 #include <string.h>
2 #include <stdlib.h>
3 #include <sys/stat.h>
4
5 #include "gmqcc.h"
6 #include "lexer.h"
7
8 #define HT_MACROS 1024
9
10 struct ppcondition {
11 bool on;
12 bool was_on;
13 bool had_else;
14 };
15
16 struct pptoken {
17 int token;
18 char *value;
19 /* a copy from the lexer */
20 union {
21 vec3_t v;
22 int i;
23 double f;
24 int t; /* type */
25 } constval;
26 };
27
28 struct ppmacro {
29 lex_ctx_t ctx;
30 char *name;
31 char **params;
32 /* yes we need an extra flag since `#define FOO x` is not the same as `#define FOO() x` */
33 bool has_params;
34 bool variadic;
35 pptoken **output;
36 };
37
38 struct ftepp_t {
39 lex_file *lex;
40 int token;
41 unsigned int errors;
42 bool output_on;
43 ppcondition *conditions;
44 ht macros; /* hashtable<string, ppmacro*> */
45 char *output_string;
46 char *itemname;
47 char *includename;
48 bool in_macro;
49 uint32_t predef_countval;
50 uint32_t predef_randval;
51 };
52
53 /* __DATE__ */
ftepp_predef_date(ftepp_t * context)54 static char *ftepp_predef_date(ftepp_t *context) {
55 const struct tm *itime = nullptr;
56 char *value = (char*)mem_a(82);
57 time_t rtime;
58
59 (void)context;
60
61 time (&rtime);
62 itime = util_localtime(&rtime);
63 strftime(value, 82, "\"%b %d %Y\"", itime);
64
65 return value;
66 }
67
68 /* __TIME__ */
ftepp_predef_time(ftepp_t * context)69 static char *ftepp_predef_time(ftepp_t *context) {
70 const struct tm *itime = nullptr;
71 char *value = (char*)mem_a(82);
72 time_t rtime;
73
74 (void)context;
75
76 time (&rtime);
77 itime = util_localtime(&rtime);
78 strftime(value, 82, "\"%X\"", itime);
79
80 return value;
81 }
82
83 /* __LINE__ */
ftepp_predef_line(ftepp_t * context)84 static char *ftepp_predef_line(ftepp_t *context) {
85 char *value;
86
87 util_asprintf(&value, "%d", (int)context->lex->line);
88 return value;
89 }
90 /* __FILE__ */
ftepp_predef_file(ftepp_t * context)91 static char *ftepp_predef_file(ftepp_t *context) {
92 size_t length = strlen(context->lex->name) + 3; /* two quotes and a terminator */
93 char *value = (char*)mem_a(length);
94
95 util_snprintf(value, length, "\"%s\"", context->lex->name);
96 return value;
97 }
98 /* __COUNTER_LAST__ */
ftepp_predef_counterlast(ftepp_t * context)99 static char *ftepp_predef_counterlast(ftepp_t *context) {
100 char *value;
101 util_asprintf(&value, "%u", context->predef_countval);
102 return value;
103 }
104 /* __COUNTER__ */
ftepp_predef_counter(ftepp_t * context)105 static char *ftepp_predef_counter(ftepp_t *context) {
106 char *value;
107
108 context->predef_countval ++;
109 util_asprintf(&value, "%u", context->predef_countval);
110
111 return value;
112 }
113 /* __RANDOM__ */
ftepp_predef_random(ftepp_t * context)114 static char *ftepp_predef_random(ftepp_t *context) {
115 char *value;
116
117 context->predef_randval = (util_rand() % 0xFF) + 1;
118 util_asprintf(&value, "%u", context->predef_randval);
119 return value;
120 }
121 /* __RANDOM_LAST__ */
ftepp_predef_randomlast(ftepp_t * context)122 static char *ftepp_predef_randomlast(ftepp_t *context) {
123 char *value;
124
125 util_asprintf(&value, "%u", context->predef_randval);
126 return value;
127 }
128 /* __TIMESTAMP__ */
ftepp_predef_timestamp(ftepp_t * context)129 static char *ftepp_predef_timestamp(ftepp_t *context) {
130 struct stat finfo;
131 const char *find;
132 char *value;
133 size_t size;
134
135 if (stat(context->lex->name, &finfo))
136 return util_strdup("\"<failed to determine timestamp>\"");
137
138 find = util_ctime(&finfo.st_mtime);
139 value = (char*)mem_a(strlen(find) + 1);
140 memcpy(&value[1], find, (size = strlen(find)) - 1);
141
142 value[0] = '"';
143 value[size] = '"';
144
145 return value;
146 }
147
148 struct ftepp_predef_t {
149 const char *name;
150 char *(*func)(ftepp_t *);
151 };
152
153 static const ftepp_predef_t ftepp_predefs[] = {
154 { "__LINE__", &ftepp_predef_line },
155 { "__FILE__", &ftepp_predef_file },
156 { "__COUNTER__", &ftepp_predef_counter },
157 { "__COUNTER_LAST__", &ftepp_predef_counterlast },
158 { "__RANDOM__", &ftepp_predef_random },
159 { "__RANDOM_LAST__", &ftepp_predef_randomlast },
160 { "__DATE__", &ftepp_predef_date },
161 { "__TIME__", &ftepp_predef_time },
162 { "__TIME_STAMP__", &ftepp_predef_timestamp }
163 };
164
ftepp_predef_index(const char * name)165 static GMQCC_INLINE size_t ftepp_predef_index(const char *name) {
166 /* no hashtable here, we simply check for one to exist the naive way */
167 size_t i;
168 for(i = 1; i < GMQCC_ARRAY_COUNT(ftepp_predefs) + 1; i++)
169 if (!strcmp(ftepp_predefs[i-1].name, name))
170 return i;
171 return 0;
172 }
173
174 bool ftepp_predef_exists(const char *name);
ftepp_predef_exists(const char * name)175 bool ftepp_predef_exists(const char *name) {
176 return ftepp_predef_index(name) != 0;
177 }
178
179 /* singleton because we're allowed */
ftepp_predef(const char * name)180 static GMQCC_INLINE char *(*ftepp_predef(const char *name))(ftepp_t *context) {
181 size_t i = ftepp_predef_index(name);
182 return (i != 0) ? ftepp_predefs[i-1].func : nullptr;
183 }
184
185 #define ftepp_tokval(f) ((f)->lex->tok.value)
186 #define ftepp_ctx(f) ((f)->lex->tok.ctx)
187
ftepp_errorat(ftepp_t * ftepp,lex_ctx_t ctx,const char * fmt,...)188 static void ftepp_errorat(ftepp_t *ftepp, lex_ctx_t ctx, const char *fmt, ...)
189 {
190 va_list ap;
191
192 ftepp->errors++;
193
194 va_start(ap, fmt);
195 con_cvprintmsg(ctx, LVL_ERROR, "error", fmt, ap);
196 va_end(ap);
197 }
198
ftepp_error(ftepp_t * ftepp,const char * fmt,...)199 static void ftepp_error(ftepp_t *ftepp, const char *fmt, ...)
200 {
201 va_list ap;
202
203 ftepp->errors++;
204
205 va_start(ap, fmt);
206 con_cvprintmsg(ftepp->lex->tok.ctx, LVL_ERROR, "error", fmt, ap);
207 va_end(ap);
208 }
209
ftepp_warn(ftepp_t * ftepp,int warntype,const char * fmt,...)210 static bool GMQCC_WARN ftepp_warn(ftepp_t *ftepp, int warntype, const char *fmt, ...)
211 {
212 bool r;
213 va_list ap;
214
215 va_start(ap, fmt);
216 r = vcompile_warning(ftepp->lex->tok.ctx, warntype, fmt, ap);
217 va_end(ap);
218 return r;
219 }
220
pptoken_make(ftepp_t * ftepp)221 static pptoken *pptoken_make(ftepp_t *ftepp)
222 {
223 pptoken *token = (pptoken*)mem_a(sizeof(pptoken));
224 token->token = ftepp->token;
225 token->value = util_strdup(ftepp_tokval(ftepp));
226 memcpy(&token->constval, &ftepp->lex->tok.constval, sizeof(token->constval));
227 return token;
228 }
229
pptoken_delete(pptoken * self)230 static GMQCC_INLINE void pptoken_delete(pptoken *self)
231 {
232 mem_d(self->value);
233 mem_d(self);
234 }
235
ppmacro_new(lex_ctx_t ctx,const char * name)236 static ppmacro *ppmacro_new(lex_ctx_t ctx, const char *name)
237 {
238 ppmacro *macro = (ppmacro*)mem_a(sizeof(ppmacro));
239
240 (void)ctx;
241 memset(macro, 0, sizeof(*macro));
242 macro->name = util_strdup(name);
243 return macro;
244 }
245
ppmacro_delete(ppmacro * self)246 static void ppmacro_delete(ppmacro *self)
247 {
248 size_t i;
249 for (i = 0; i < vec_size(self->params); ++i)
250 mem_d(self->params[i]);
251 vec_free(self->params);
252 for (i = 0; i < vec_size(self->output); ++i)
253 pptoken_delete(self->output[i]);
254 vec_free(self->output);
255 mem_d(self->name);
256 mem_d(self);
257 }
258
ftepp_new(void)259 static ftepp_t* ftepp_new(void)
260 {
261 ftepp_t *ftepp;
262
263 ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
264 memset(ftepp, 0, sizeof(*ftepp));
265
266 ftepp->macros = util_htnew(HT_MACROS);
267 ftepp->output_on = true;
268 ftepp->predef_countval = 0;
269 ftepp->predef_randval = 0;
270
271 return ftepp;
272 }
273
ftepp_flush_do(ftepp_t * self)274 static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
275 {
276 vec_free(self->output_string);
277 }
278
ftepp_delete(ftepp_t * self)279 static void ftepp_delete(ftepp_t *self)
280 {
281 ftepp_flush_do(self);
282 if (self->itemname)
283 mem_d(self->itemname);
284 if (self->includename)
285 vec_free(self->includename);
286
287 util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
288
289 vec_free(self->conditions);
290 if (self->lex)
291 lex_close(self->lex);
292 mem_d(self);
293 }
294
ftepp_out(ftepp_t * ftepp,const char * str,bool ignore_cond)295 static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
296 {
297 if (ignore_cond || ftepp->output_on)
298 {
299 size_t len;
300 char *data;
301 len = strlen(str);
302 data = vec_add(ftepp->output_string, len);
303 memcpy(data, str, len);
304 }
305 }
306
ftepp_update_output_condition(ftepp_t * ftepp)307 static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
308 {
309 size_t i;
310 ftepp->output_on = true;
311 for (i = 0; i < vec_size(ftepp->conditions); ++i)
312 ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
313 }
314
ftepp_macro_find(ftepp_t * ftepp,const char * name)315 static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
316 {
317 return (ppmacro*)util_htget(ftepp->macros, name);
318 }
319
ftepp_macro_delete(ftepp_t * ftepp,const char * name)320 static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
321 {
322 util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
323 }
324
ftepp_next(ftepp_t * ftepp)325 static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
326 {
327 return (ftepp->token = lex_do(ftepp->lex));
328 }
329
330 /* Important: this does not skip newlines! */
ftepp_skipspace(ftepp_t * ftepp)331 static bool ftepp_skipspace(ftepp_t *ftepp)
332 {
333 if (ftepp->token != TOKEN_WHITE)
334 return true;
335 while (ftepp_next(ftepp) == TOKEN_WHITE) {}
336 if (ftepp->token >= TOKEN_EOF) {
337 ftepp_error(ftepp, "unexpected end of preprocessor directive");
338 return false;
339 }
340 return true;
341 }
342
343 /* this one skips EOLs as well */
ftepp_skipallwhite(ftepp_t * ftepp)344 static bool ftepp_skipallwhite(ftepp_t *ftepp)
345 {
346 if (ftepp->token != TOKEN_WHITE && ftepp->token != TOKEN_EOL)
347 return true;
348 do {
349 ftepp_next(ftepp);
350 } while (ftepp->token == TOKEN_WHITE || ftepp->token == TOKEN_EOL);
351 if (ftepp->token >= TOKEN_EOF) {
352 ftepp_error(ftepp, "unexpected end of preprocessor directive");
353 return false;
354 }
355 return true;
356 }
357
358 /**
359 * The huge macro parsing code...
360 */
ftepp_define_params(ftepp_t * ftepp,ppmacro * macro)361 static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
362 {
363 do {
364 ftepp_next(ftepp);
365 if (!ftepp_skipspace(ftepp))
366 return false;
367 if (ftepp->token == ')')
368 break;
369 switch (ftepp->token) {
370 case TOKEN_IDENT:
371 case TOKEN_TYPENAME:
372 case TOKEN_KEYWORD:
373 vec_push(macro->params, util_strdup(ftepp_tokval(ftepp)));
374 break;
375 case TOKEN_DOTS:
376 macro->variadic = true;
377 break;
378 default:
379 ftepp_error(ftepp, "unexpected token in parameter list");
380 return false;
381 }
382 ftepp_next(ftepp);
383 if (!ftepp_skipspace(ftepp))
384 return false;
385 if (macro->variadic && ftepp->token != ')') {
386 ftepp_error(ftepp, "cannot have parameters after the variadic parameters");
387 return false;
388 }
389 } while (ftepp->token == ',');
390
391 if (ftepp->token != ')') {
392 ftepp_error(ftepp, "expected closing paren after macro parameter list");
393 return false;
394 }
395 ftepp_next(ftepp);
396 /* skipspace happens in ftepp_define */
397 return true;
398 }
399
ftepp_define_body(ftepp_t * ftepp,ppmacro * macro)400 static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
401 {
402 pptoken *ptok;
403 while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
404 bool subscript = false;
405 size_t index = 0;
406 if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
407 subscript = !!(ftepp_next(ftepp) == '#');
408
409 if (subscript && ftepp_next(ftepp) != '#') {
410 ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
411 return false;
412 } else if (subscript) {
413 if (ftepp_next(ftepp) == '[') {
414 if (ftepp_next(ftepp) != TOKEN_INTCONST) {
415 ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
416 return false;
417 }
418
419 index = (int)strtol(ftepp_tokval(ftepp), nullptr, 10);
420
421 if (ftepp_next(ftepp) != ']') {
422 ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
423 return false;
424 }
425
426 /*
427 * mark it as an array to be handled later as such and not
428 * as traditional __VA_ARGS__
429 */
430 ftepp->token = TOKEN_VA_ARGS_ARRAY;
431 ptok = pptoken_make(ftepp);
432 ptok->constval.i = index;
433 vec_push(macro->output, ptok);
434 ftepp_next(ftepp);
435 } else {
436 ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
437 return false;
438 }
439 } else {
440 int old = ftepp->token;
441 ftepp->token = TOKEN_VA_ARGS;
442 ptok = pptoken_make(ftepp);
443 vec_push(macro->output, ptok);
444 ftepp->token = old;
445 }
446 }
447 else if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_COUNT__")) {
448 ftepp->token = TOKEN_VA_COUNT;
449 ptok = pptoken_make(ftepp);
450 vec_push(macro->output, ptok);
451 ftepp_next(ftepp);
452 } else {
453 ptok = pptoken_make(ftepp);
454 vec_push(macro->output, ptok);
455 ftepp_next(ftepp);
456 }
457 }
458 /* recursive expansion can cause EOFs here */
459 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
460 ftepp_error(ftepp, "unexpected junk after macro or unexpected end of file");
461 return false;
462 }
463 return true;
464 }
465
466 static const char *ftepp_math_constants[][2] = {
467 { "M_E", "2.7182818284590452354" }, /* e */
468 { "M_LOG2E", "1.4426950408889634074" }, /* log_2 e */
469 { "M_LOG10E", "0.43429448190325182765" }, /* log_10 e */
470 { "M_LN2", "0.69314718055994530942" }, /* log_e 2 */
471 { "M_LN10", "2.30258509299404568402" }, /* log_e 10 */
472 { "M_PI", "3.14159265358979323846" }, /* pi */
473 { "M_PI_2", "1.57079632679489661923" }, /* pi/2 */
474 { "M_PI_4", "0.78539816339744830962" }, /* pi/4 */
475 { "M_1_PI", "0.31830988618379067154" }, /* 1/pi */
476 { "M_2_PI", "0.63661977236758134308" }, /* 2/pi */
477 { "M_2_SQRTPI", "1.12837916709551257390" }, /* 2/sqrt(pi) */
478 { "M_SQRT2", "1.41421356237309504880" }, /* sqrt(2) */
479 { "M_SQRT1_2", "0.70710678118654752440" }, /* 1/sqrt(2) */
480 { "M_TAU", "6.28318530717958647692" } /* pi*2 */
481 };
482
ftepp_define(ftepp_t * ftepp)483 static bool ftepp_define(ftepp_t *ftepp)
484 {
485 ppmacro *macro = nullptr;
486 size_t l = ftepp_ctx(ftepp).line;
487 size_t i;
488 bool mathconstant = false;
489
490 (void)ftepp_next(ftepp);
491 if (!ftepp_skipspace(ftepp))
492 return false;
493
494 switch (ftepp->token) {
495 case TOKEN_IDENT:
496 case TOKEN_TYPENAME:
497 case TOKEN_KEYWORD:
498 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
499 for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++) {
500 if (!strcmp(ftepp_math_constants[i][0], ftepp_tokval(ftepp))) {
501 mathconstant = true;
502 break;
503 }
504 }
505 }
506
507 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
508
509 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
510 /* user defined ones take precedence */
511 if (macro && mathconstant) {
512 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
513 macro = nullptr;
514 }
515 }
516
517 if (macro && ftepp->output_on) {
518 if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
519 return false;
520 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
521 }
522 macro = ppmacro_new(ftepp_ctx(ftepp), ftepp_tokval(ftepp));
523 break;
524 default:
525 ftepp_error(ftepp, "expected macro name");
526 return false;
527 }
528
529 (void)ftepp_next(ftepp);
530
531 if (ftepp->token == '(') {
532 macro->has_params = true;
533 if (!ftepp_define_params(ftepp, macro)) {
534 ppmacro_delete(macro);
535 return false;
536 }
537 }
538
539 if (!ftepp_skipspace(ftepp)) {
540 ppmacro_delete(macro);
541 return false;
542 }
543
544 if (!ftepp_define_body(ftepp, macro)) {
545 ppmacro_delete(macro);
546 return false;
547 }
548
549 if (ftepp->output_on)
550 util_htset(ftepp->macros, macro->name, (void*)macro);
551 else {
552 ppmacro_delete(macro);
553 }
554
555 for (; l < ftepp_ctx(ftepp).line; ++l)
556 ftepp_out(ftepp, "\n", true);
557 return true;
558 }
559
560 /**
561 * When a macro is used we have to handle parameters as well
562 * as special-concatenation via ## or stringification via #
563 *
564 * Note: parenthesis can nest, so FOO((a),b) is valid, but only
565 * this kind of parens. Curly braces or [] don't count towards the
566 * paren-level.
567 */
568 struct macroparam {
569 pptoken **tokens;
570 };
571
macroparam_clean(macroparam * self)572 static void macroparam_clean(macroparam *self)
573 {
574 size_t i;
575 for (i = 0; i < vec_size(self->tokens); ++i)
576 pptoken_delete(self->tokens[i]);
577 vec_free(self->tokens);
578 }
579
580 /* need to leave the last token up */
ftepp_macro_call_params(ftepp_t * ftepp,macroparam ** out_params)581 static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
582 {
583 macroparam *params = nullptr;
584 pptoken *ptok;
585 macroparam mp;
586 size_t parens = 0;
587 size_t i;
588
589 if (!ftepp_skipallwhite(ftepp))
590 return false;
591 while (ftepp->token != ')') {
592 mp.tokens = nullptr;
593 if (!ftepp_skipallwhite(ftepp))
594 return false;
595 while (parens || ftepp->token != ',') {
596 if (ftepp->token == '(')
597 ++parens;
598 else if (ftepp->token == ')') {
599 if (!parens)
600 break;
601 --parens;
602 }
603 ptok = pptoken_make(ftepp);
604 vec_push(mp.tokens, ptok);
605 if (ftepp_next(ftepp) >= TOKEN_EOF) {
606 ftepp_error(ftepp, "unexpected end of file in macro call");
607 goto on_error;
608 }
609 }
610 vec_push(params, mp);
611 mp.tokens = nullptr;
612 if (ftepp->token == ')')
613 break;
614 if (ftepp->token != ',') {
615 ftepp_error(ftepp, "expected closing paren or comma in macro call");
616 goto on_error;
617 }
618 if (ftepp_next(ftepp) >= TOKEN_EOF) {
619 ftepp_error(ftepp, "unexpected end of file in macro call");
620 goto on_error;
621 }
622 }
623 *out_params = params;
624 return true;
625
626 on_error:
627 if (mp.tokens)
628 macroparam_clean(&mp);
629 for (i = 0; i < vec_size(params); ++i)
630 macroparam_clean(¶ms[i]);
631 vec_free(params);
632 return false;
633 }
634
macro_params_find(ppmacro * macro,const char * name,size_t * idx)635 static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
636 {
637 size_t i;
638 for (i = 0; i < vec_size(macro->params); ++i) {
639 if (!strcmp(macro->params[i], name)) {
640 *idx = i;
641 return true;
642 }
643 }
644 return false;
645 }
646
ftepp_stringify_token(ftepp_t * ftepp,pptoken * token)647 static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
648 {
649 char chs[2];
650 const char *ch;
651 chs[1] = 0;
652 switch (token->token) {
653 case TOKEN_STRINGCONST:
654 ch = token->value;
655 while (*ch) {
656 /* in preprocessor mode strings already are string,
657 * so we don't get actual newline bytes here.
658 * Still need to escape backslashes and quotes.
659 */
660 switch (*ch) {
661 case '\\': ftepp_out(ftepp, "\\\\", false); break;
662 case '"': ftepp_out(ftepp, "\\\"", false); break;
663 default:
664 chs[0] = *ch;
665 ftepp_out(ftepp, chs, false);
666 break;
667 }
668 ++ch;
669 }
670 break;
671 /*case TOKEN_WHITE:
672 ftepp_out(ftepp, " ", false);
673 break;*/
674 case TOKEN_EOL:
675 ftepp_out(ftepp, "\\n", false);
676 break;
677 default:
678 ftepp_out(ftepp, token->value, false);
679 break;
680 }
681 }
682
ftepp_stringify(ftepp_t * ftepp,macroparam * param)683 static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
684 {
685 size_t i;
686 ftepp_out(ftepp, "\"", false);
687 for (i = 0; i < vec_size(param->tokens); ++i)
688 ftepp_stringify_token(ftepp, param->tokens[i]);
689 ftepp_out(ftepp, "\"", false);
690 }
691
ftepp_recursion_header(ftepp_t * ftepp)692 static void ftepp_recursion_header(ftepp_t *ftepp)
693 {
694 ftepp_out(ftepp, "\n#pragma push(line)\n", false);
695 }
696
ftepp_recursion_footer(ftepp_t * ftepp)697 static void ftepp_recursion_footer(ftepp_t *ftepp)
698 {
699 ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
700 }
701
702 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline);
ftepp_param_out(ftepp_t * ftepp,macroparam * param)703 static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
704 {
705 size_t i;
706 pptoken *out;
707 for (i = 0; i < vec_size(param->tokens); ++i) {
708 out = param->tokens[i];
709 if (out->token == TOKEN_EOL)
710 ftepp_out(ftepp, "\n", false);
711 else {
712 ppmacro *find = ftepp_macro_find(ftepp, out->value);
713 if (OPTS_FLAG(FTEPP_INDIRECT_EXPANSION) && find && !find->has_params)
714 ftepp_macro_expand(ftepp, find, nullptr, false);
715 else
716 ftepp_out(ftepp, out->value, false);
717 }
718 }
719 }
720
721 static bool ftepp_preprocess(ftepp_t *ftepp);
ftepp_macro_expand(ftepp_t * ftepp,ppmacro * macro,macroparam * params,bool resetline)722 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
723 {
724 char *buffer = nullptr;
725 char *old_string = ftepp->output_string;
726 char *inner_string;
727 lex_file *old_lexer = ftepp->lex;
728 size_t vararg_start = vec_size(macro->params);
729 bool retval = true;
730 bool has_newlines;
731 size_t varargs;
732
733 size_t o, pi;
734 lex_file *inlex;
735
736 bool old_inmacro;
737 bool strip = false;
738
739 int nextok;
740
741 if (vararg_start < vec_size(params))
742 varargs = vec_size(params) - vararg_start;
743 else
744 varargs = 0;
745
746 /* really ... */
747 if (!vec_size(macro->output))
748 return true;
749
750 ftepp->output_string = nullptr;
751 for (o = 0; o < vec_size(macro->output); ++o) {
752 pptoken *out = macro->output[o];
753 switch (out->token) {
754 case TOKEN_VA_ARGS:
755 if (!macro->variadic) {
756 ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
757 vec_free(old_string);
758 return false;
759 }
760 if (!varargs)
761 break;
762
763 pi = 0;
764 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
765 for (++pi; pi < varargs; ++pi) {
766 ftepp_out(ftepp, ", ", false);
767 ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
768 }
769 break;
770
771 case TOKEN_VA_ARGS_ARRAY:
772 if ((size_t)out->constval.i >= varargs) {
773 ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
774 vec_free(old_string);
775 return false;
776 }
777
778 ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]);
779 break;
780
781 case TOKEN_VA_COUNT:
782 util_asprintf(&buffer, "%d", varargs);
783 ftepp_out(ftepp, buffer, false);
784 mem_d(buffer);
785 break;
786
787 case TOKEN_IDENT:
788 case TOKEN_TYPENAME:
789 case TOKEN_KEYWORD:
790 if (!macro_params_find(macro, out->value, &pi)) {
791 ftepp_out(ftepp, out->value, false);
792 break;
793 } else
794 ftepp_param_out(ftepp, ¶ms[pi]);
795 break;
796 case '#':
797 if (o + 1 < vec_size(macro->output)) {
798 nextok = macro->output[o+1]->token;
799 if (nextok == '#') {
800 /* raw concatenation */
801 ++o;
802 strip = true;
803 break;
804 }
805 if ( (nextok == TOKEN_IDENT ||
806 nextok == TOKEN_KEYWORD ||
807 nextok == TOKEN_TYPENAME) &&
808 macro_params_find(macro, macro->output[o+1]->value, &pi))
809 {
810 ++o;
811
812 ftepp_stringify(ftepp, ¶ms[pi]);
813 break;
814 }
815 }
816 ftepp_out(ftepp, "#", false);
817 break;
818 case TOKEN_EOL:
819 ftepp_out(ftepp, "\n", false);
820 break;
821 default:
822 buffer = out->value;
823 #define buffer_stripable(X) ((X) == ' ' || (X) == '\t')
824 if (vec_size(macro->output) > o + 1 && macro->output[o+1]->token == '#' && buffer_stripable(*buffer))
825 buffer++;
826 if (strip) {
827 while (buffer_stripable(*buffer)) buffer++;
828 strip = false;
829 }
830 ftepp_out(ftepp, buffer, false);
831 break;
832 }
833 }
834 vec_push(ftepp->output_string, 0);
835 /* Now run the preprocessor recursively on this string buffer */
836 /*
837 printf("__________\n%s\n=========\n", ftepp->output_string);
838 */
839 inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
840 if (!inlex) {
841 ftepp_error(ftepp, "internal error: failed to instantiate lexer");
842 retval = false;
843 goto cleanup;
844 }
845
846 inlex->line = ftepp->lex->line;
847 inlex->sline = ftepp->lex->sline;
848 ftepp->lex = inlex;
849
850 old_inmacro = ftepp->in_macro;
851 ftepp->in_macro = true;
852 ftepp->output_string = nullptr;
853 if (!ftepp_preprocess(ftepp)) {
854 ftepp->in_macro = old_inmacro;
855 vec_free(ftepp->lex->open_string);
856 vec_free(ftepp->output_string);
857 lex_close(ftepp->lex);
858 retval = false;
859 goto cleanup;
860 }
861 ftepp->in_macro = old_inmacro;
862 vec_free(ftepp->lex->open_string);
863 lex_close(ftepp->lex);
864
865 inner_string = ftepp->output_string;
866 ftepp->output_string = old_string;
867
868 has_newlines = (strchr(inner_string, '\n') != nullptr);
869
870 if (has_newlines && !old_inmacro)
871 ftepp_recursion_header(ftepp);
872
873 vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
874 vec_free(inner_string);
875
876 if (has_newlines && !old_inmacro)
877 ftepp_recursion_footer(ftepp);
878
879 if (resetline && !ftepp->in_macro) {
880 char lineno[128];
881 util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
882 ftepp_out(ftepp, lineno, false);
883 }
884
885 old_string = ftepp->output_string;
886 cleanup:
887 ftepp->lex = old_lexer;
888 ftepp->output_string = old_string;
889 return retval;
890 }
891
ftepp_macro_call(ftepp_t * ftepp,ppmacro * macro)892 static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
893 {
894 size_t o;
895 macroparam *params = nullptr;
896 bool retval = true;
897 size_t paramline;
898
899 if (!macro->has_params) {
900 if (!ftepp_macro_expand(ftepp, macro, nullptr, false))
901 return false;
902 ftepp_next(ftepp);
903 return true;
904 }
905 ftepp_next(ftepp);
906
907 if (!ftepp_skipallwhite(ftepp))
908 return false;
909
910 if (ftepp->token != '(') {
911 ftepp_error(ftepp, "expected macro parameters in parenthesis");
912 return false;
913 }
914
915 ftepp_next(ftepp);
916 paramline = ftepp->lex->sline;
917 if (!ftepp_macro_call_params(ftepp, ¶ms))
918 return false;
919
920 if ( vec_size(params) < vec_size(macro->params) ||
921 (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
922 {
923 ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
924 (macro->variadic ? " at least" : ""),
925 (unsigned int)vec_size(macro->params),
926 (unsigned int)vec_size(params));
927 retval = false;
928 goto cleanup;
929 }
930
931 if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
932 retval = false;
933 ftepp_next(ftepp);
934
935 cleanup:
936 for (o = 0; o < vec_size(params); ++o)
937 macroparam_clean(¶ms[o]);
938 vec_free(params);
939 return retval;
940 }
941
942 /**
943 * #if - the FTEQCC way:
944 * defined(FOO) => true if FOO was #defined regardless of parameters or contents
945 * <numbers> => True if the number is not 0
946 * !<factor> => True if the factor yields false
947 * !!<factor> => ERROR on 2 or more unary nots
948 * <macro> => becomes the macro's FIRST token regardless of parameters
949 * <e> && <e> => True if both expressions are true
950 * <e> || <e> => True if either expression is true
951 * <string> => False
952 * <ident> => False (remember for macros the <macro> rule applies instead)
953 * Unary + and - are weird and wrong in fteqcc so we don't allow them
954 * parenthesis in expressions are allowed
955 * parameter lists on macros are errors
956 * No mathematical calculations are executed
957 */
958 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out);
ftepp_if_op(ftepp_t * ftepp)959 static bool ftepp_if_op(ftepp_t *ftepp)
960 {
961 ftepp->lex->flags.noops = false;
962 ftepp_next(ftepp);
963 if (!ftepp_skipspace(ftepp))
964 return false;
965 ftepp->lex->flags.noops = true;
966 return true;
967 }
ftepp_if_value(ftepp_t * ftepp,bool * out,double * value_out)968 static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
969 {
970 ppmacro *macro;
971 bool wasnot = false;
972 bool wasneg = false;
973
974 if (!ftepp_skipspace(ftepp))
975 return false;
976
977 while (ftepp->token == '!') {
978 wasnot = true;
979 ftepp_next(ftepp);
980 if (!ftepp_skipspace(ftepp))
981 return false;
982 }
983
984 if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-"))
985 {
986 wasneg = true;
987 ftepp_next(ftepp);
988 if (!ftepp_skipspace(ftepp))
989 return false;
990 }
991
992 switch (ftepp->token) {
993 case TOKEN_IDENT:
994 case TOKEN_TYPENAME:
995 case TOKEN_KEYWORD:
996 if (!strcmp(ftepp_tokval(ftepp), "defined")) {
997 ftepp_next(ftepp);
998 if (!ftepp_skipspace(ftepp))
999 return false;
1000 if (ftepp->token != '(') {
1001 ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
1002 return false;
1003 }
1004 ftepp_next(ftepp);
1005 if (!ftepp_skipspace(ftepp))
1006 return false;
1007 if (ftepp->token != TOKEN_IDENT &&
1008 ftepp->token != TOKEN_TYPENAME &&
1009 ftepp->token != TOKEN_KEYWORD)
1010 {
1011 ftepp_error(ftepp, "defined() used on an unexpected token type");
1012 return false;
1013 }
1014 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1015 *out = !!macro;
1016 ftepp_next(ftepp);
1017 if (!ftepp_skipspace(ftepp))
1018 return false;
1019 if (ftepp->token != ')') {
1020 ftepp_error(ftepp, "expected closing paren");
1021 return false;
1022 }
1023 break;
1024 }
1025
1026 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1027 if (!macro || !vec_size(macro->output)) {
1028 *out = false;
1029 *value_out = 0;
1030 } else {
1031 /* This does not expand recursively! */
1032 switch (macro->output[0]->token) {
1033 case TOKEN_INTCONST:
1034 *value_out = macro->output[0]->constval.i;
1035 *out = !!(macro->output[0]->constval.i);
1036 break;
1037 case TOKEN_FLOATCONST:
1038 *value_out = macro->output[0]->constval.f;
1039 *out = !!(macro->output[0]->constval.f);
1040 break;
1041 default:
1042 *out = false;
1043 break;
1044 }
1045 }
1046 break;
1047 case TOKEN_STRINGCONST:
1048 *value_out = 0;
1049 *out = false;
1050 break;
1051 case TOKEN_INTCONST:
1052 *value_out = ftepp->lex->tok.constval.i;
1053 *out = !!(ftepp->lex->tok.constval.i);
1054 break;
1055 case TOKEN_FLOATCONST:
1056 *value_out = ftepp->lex->tok.constval.f;
1057 *out = !!(ftepp->lex->tok.constval.f);
1058 break;
1059
1060 case '(':
1061 ftepp_next(ftepp);
1062 if (!ftepp_if_expr(ftepp, out, value_out))
1063 return false;
1064 if (ftepp->token != ')') {
1065 ftepp_error(ftepp, "expected closing paren in #if expression");
1066 return false;
1067 }
1068 break;
1069
1070 default:
1071 ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
1072 if (OPTS_OPTION_BOOL(OPTION_DEBUG))
1073 ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
1074 return false;
1075 }
1076 if (wasneg)
1077 *value_out = -*value_out;
1078 if (wasnot) {
1079 *out = !*out;
1080 *value_out = (*out ? 1 : 0);
1081 }
1082 return true;
1083 }
1084
1085 /*
1086 static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
1087 {
1088 if (!ftepp_next(ftepp))
1089 return false;
1090 return ftepp_if_value(ftepp, out, value_out);
1091 }
1092 */
1093
ftepp_if_expr(ftepp_t * ftepp,bool * out,double * value_out)1094 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
1095 {
1096 if (!ftepp_if_value(ftepp, out, value_out))
1097 return false;
1098
1099 if (!ftepp_if_op(ftepp))
1100 return false;
1101
1102 if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
1103 return true;
1104
1105 /* FTEQCC is all right-associative and no precedence here */
1106 if (!strcmp(ftepp_tokval(ftepp), "&&") ||
1107 !strcmp(ftepp_tokval(ftepp), "||"))
1108 {
1109 bool next = false;
1110 char opc = ftepp_tokval(ftepp)[0];
1111 double nextvalue;
1112
1113 (void)nextvalue;
1114 if (!ftepp_next(ftepp))
1115 return false;
1116 if (!ftepp_if_expr(ftepp, &next, &nextvalue))
1117 return false;
1118
1119 if (opc == '&')
1120 *out = *out && next;
1121 else
1122 *out = *out || next;
1123
1124 *value_out = (*out ? 1 : 0);
1125 return true;
1126 }
1127 else if (!strcmp(ftepp_tokval(ftepp), "==") ||
1128 !strcmp(ftepp_tokval(ftepp), "!=") ||
1129 !strcmp(ftepp_tokval(ftepp), ">=") ||
1130 !strcmp(ftepp_tokval(ftepp), "<=") ||
1131 !strcmp(ftepp_tokval(ftepp), ">") ||
1132 !strcmp(ftepp_tokval(ftepp), "<"))
1133 {
1134 bool next = false;
1135 const char opc0 = ftepp_tokval(ftepp)[0];
1136 const char opc1 = ftepp_tokval(ftepp)[1];
1137 double other;
1138
1139 if (!ftepp_next(ftepp))
1140 return false;
1141 if (!ftepp_if_expr(ftepp, &next, &other))
1142 return false;
1143
1144 if (opc0 == '=')
1145 *out = (*value_out == other);
1146 else if (opc0 == '!')
1147 *out = (*value_out != other);
1148 else if (opc0 == '>') {
1149 if (opc1 == '=') *out = (*value_out >= other);
1150 else *out = (*value_out > other);
1151 }
1152 else if (opc0 == '<') {
1153 if (opc1 == '=') *out = (*value_out <= other);
1154 else *out = (*value_out < other);
1155 }
1156 *value_out = (*out ? 1 : 0);
1157
1158 return true;
1159 }
1160 else {
1161 ftepp_error(ftepp, "junk after #if");
1162 return false;
1163 }
1164 }
1165
ftepp_if(ftepp_t * ftepp,ppcondition * cond)1166 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
1167 {
1168 bool result = false;
1169 double dummy = 0;
1170
1171 memset(cond, 0, sizeof(*cond));
1172 (void)ftepp_next(ftepp);
1173
1174 if (!ftepp_skipspace(ftepp))
1175 return false;
1176 if (ftepp->token == TOKEN_EOL) {
1177 ftepp_error(ftepp, "expected expression for #if-directive");
1178 return false;
1179 }
1180
1181 if (!ftepp_if_expr(ftepp, &result, &dummy))
1182 return false;
1183
1184 cond->on = result;
1185 return true;
1186 }
1187
1188 /**
1189 * ifdef is rather simple
1190 */
ftepp_ifdef(ftepp_t * ftepp,ppcondition * cond)1191 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
1192 {
1193 ppmacro *macro;
1194 memset(cond, 0, sizeof(*cond));
1195 (void)ftepp_next(ftepp);
1196 if (!ftepp_skipspace(ftepp))
1197 return false;
1198
1199 switch (ftepp->token) {
1200 case TOKEN_IDENT:
1201 case TOKEN_TYPENAME:
1202 case TOKEN_KEYWORD:
1203 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1204 break;
1205 default:
1206 ftepp_error(ftepp, "expected macro name");
1207 return false;
1208 }
1209
1210 (void)ftepp_next(ftepp);
1211 if (!ftepp_skipspace(ftepp))
1212 return false;
1213 /* relaxing this condition
1214 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1215 ftepp_error(ftepp, "stray tokens after #ifdef");
1216 return false;
1217 }
1218 */
1219 cond->on = !!macro;
1220 return true;
1221 }
1222
1223 /**
1224 * undef is also simple
1225 */
ftepp_undef(ftepp_t * ftepp)1226 static bool ftepp_undef(ftepp_t *ftepp)
1227 {
1228 (void)ftepp_next(ftepp);
1229 if (!ftepp_skipspace(ftepp))
1230 return false;
1231
1232 if (ftepp->output_on) {
1233 switch (ftepp->token) {
1234 case TOKEN_IDENT:
1235 case TOKEN_TYPENAME:
1236 case TOKEN_KEYWORD:
1237 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
1238 break;
1239 default:
1240 ftepp_error(ftepp, "expected macro name");
1241 return false;
1242 }
1243 }
1244
1245 (void)ftepp_next(ftepp);
1246 if (!ftepp_skipspace(ftepp))
1247 return false;
1248 /* relaxing this condition
1249 if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1250 ftepp_error(ftepp, "stray tokens after #ifdef");
1251 return false;
1252 }
1253 */
1254 return true;
1255 }
1256
1257 /* Special unescape-string function which skips a leading quote
1258 * and stops at a quote, not just at \0
1259 */
unescape(const char * str,char * out)1260 static void unescape(const char *str, char *out) {
1261 ++str;
1262 while (*str && *str != '"') {
1263 if (*str == '\\') {
1264 ++str;
1265 switch (*str) {
1266 case '\\': *out++ = *str; break;
1267 case '"': *out++ = *str; break;
1268 case 'a': *out++ = '\a'; break;
1269 case 'b': *out++ = '\b'; break;
1270 case 'r': *out++ = '\r'; break;
1271 case 'n': *out++ = '\n'; break;
1272 case 't': *out++ = '\t'; break;
1273 case 'f': *out++ = '\f'; break;
1274 case 'v': *out++ = '\v'; break;
1275 default:
1276 *out++ = '\\';
1277 *out++ = *str;
1278 break;
1279 }
1280 ++str;
1281 continue;
1282 }
1283
1284 *out++ = *str++;
1285 }
1286 *out = 0;
1287 }
1288
ftepp_include_find_path(const char * file,const char * pathfile)1289 static char *ftepp_include_find_path(const char *file, const char *pathfile)
1290 {
1291 FILE *fp;
1292 char *filename = nullptr;
1293 const char *last_slash;
1294 size_t len;
1295
1296 if (!pathfile)
1297 return nullptr;
1298
1299 last_slash = strrchr(pathfile, '/');
1300
1301 if (last_slash) {
1302 len = last_slash - pathfile;
1303 memcpy(vec_add(filename, len), pathfile, len);
1304 vec_push(filename, '/');
1305 }
1306
1307 len = strlen(file);
1308 memcpy(vec_add(filename, len+1), file, len);
1309 vec_last(filename) = 0;
1310
1311 fp = fopen(filename, "rb");
1312 if (fp) {
1313 fclose(fp);
1314 return filename;
1315 }
1316 vec_free(filename);
1317 return nullptr;
1318 }
1319
ftepp_include_find(ftepp_t * ftepp,const char * file)1320 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
1321 {
1322 char *filename = nullptr;
1323
1324 filename = ftepp_include_find_path(file, ftepp->includename);
1325 if (!filename)
1326 filename = ftepp_include_find_path(file, ftepp->itemname);
1327 return filename;
1328 }
1329
ftepp_directive_warning(ftepp_t * ftepp)1330 static bool ftepp_directive_warning(ftepp_t *ftepp) {
1331 char *message = nullptr;
1332
1333 if (!ftepp_skipspace(ftepp))
1334 return false;
1335
1336 /* handle the odd non string constant case so it works like C */
1337 if (ftepp->token != TOKEN_STRINGCONST) {
1338 bool store = false;
1339 vec_append(message, 8, "#warning");
1340 ftepp_next(ftepp);
1341 while (ftepp->token != TOKEN_EOL) {
1342 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1343 ftepp_next(ftepp);
1344 }
1345 vec_push(message, '\0');
1346 if (ftepp->output_on)
1347 store = ftepp_warn(ftepp, WARN_CPP, message);
1348 else
1349 store = false;
1350 vec_free(message);
1351 return store;
1352 }
1353
1354 if (!ftepp->output_on)
1355 return false;
1356
1357 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1358 return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
1359 }
1360
ftepp_directive_error(ftepp_t * ftepp)1361 static void ftepp_directive_error(ftepp_t *ftepp) {
1362 char *message = nullptr;
1363
1364 if (!ftepp_skipspace(ftepp))
1365 return;
1366
1367 /* handle the odd non string constant case so it works like C */
1368 if (ftepp->token != TOKEN_STRINGCONST) {
1369 vec_append(message, 6, "#error");
1370 ftepp_next(ftepp);
1371 while (ftepp->token != TOKEN_EOL) {
1372 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1373 ftepp_next(ftepp);
1374 }
1375 vec_push(message, '\0');
1376 if (ftepp->output_on)
1377 ftepp_error(ftepp, message);
1378 vec_free(message);
1379 return;
1380 }
1381
1382 if (!ftepp->output_on)
1383 return;
1384
1385 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1386 ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
1387 }
1388
ftepp_directive_message(ftepp_t * ftepp)1389 static void ftepp_directive_message(ftepp_t *ftepp) {
1390 char *message = nullptr;
1391
1392 if (!ftepp_skipspace(ftepp))
1393 return;
1394
1395 /* handle the odd non string constant case so it works like C */
1396 if (ftepp->token != TOKEN_STRINGCONST) {
1397 vec_append(message, 8, "#message");
1398 ftepp_next(ftepp);
1399 while (ftepp->token != TOKEN_EOL) {
1400 vec_append(message, strlen(ftepp_tokval(ftepp)), ftepp_tokval(ftepp));
1401 ftepp_next(ftepp);
1402 }
1403 vec_push(message, '\0');
1404 if (ftepp->output_on)
1405 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", message);
1406 vec_free(message);
1407 return;
1408 }
1409
1410 if (!ftepp->output_on)
1411 return;
1412
1413 unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1414 con_cprintmsg(ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp));
1415 }
1416
1417 /**
1418 * Include a file.
1419 * FIXME: do we need/want a -I option?
1420 * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1421 */
ftepp_include(ftepp_t * ftepp)1422 static bool ftepp_include(ftepp_t *ftepp)
1423 {
1424 lex_file *old_lexer = ftepp->lex;
1425 lex_file *inlex;
1426 lex_ctx_t ctx;
1427 char lineno[128];
1428 char *filename;
1429 char *parsename = nullptr;
1430 char *old_includename;
1431
1432 (void)ftepp_next(ftepp);
1433 if (!ftepp_skipspace(ftepp))
1434 return false;
1435
1436 if (ftepp->token != TOKEN_STRINGCONST) {
1437 ppmacro *macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1438 if (macro) {
1439 char *backup = ftepp->output_string;
1440 ftepp->output_string = nullptr;
1441 if (ftepp_macro_expand(ftepp, macro, nullptr, true)) {
1442 parsename = util_strdup(ftepp->output_string);
1443 vec_free(ftepp->output_string);
1444 ftepp->output_string = backup;
1445 } else {
1446 ftepp->output_string = backup;
1447 ftepp_error(ftepp, "expected filename to include");
1448 return false;
1449 }
1450 } else if (OPTS_FLAG(FTEPP_PREDEFS)) {
1451 /* Well it could be a predefine like __LINE__ */
1452 char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
1453 if (predef) {
1454 parsename = predef(ftepp);
1455 } else {
1456 ftepp_error(ftepp, "expected filename to include");
1457 return false;
1458 }
1459 }
1460 }
1461
1462 if (!ftepp->output_on) {
1463 (void)ftepp_next(ftepp);
1464 return true;
1465 }
1466
1467 if (parsename)
1468 unescape(parsename, parsename);
1469 else {
1470 char *tokval = ftepp_tokval(ftepp);
1471 unescape(tokval, tokval);
1472 parsename = util_strdup(tokval);
1473 }
1474
1475 ctx = ftepp_ctx(ftepp);
1476 ftepp_out(ftepp, "\n#pragma file(", false);
1477 ftepp_out(ftepp, parsename, false);
1478 ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1479
1480 filename = ftepp_include_find(ftepp, parsename);
1481 if (!filename) {
1482 ftepp_error(ftepp, "failed to open include file `%s`", parsename);
1483 mem_d(parsename);
1484 return false;
1485 }
1486 mem_d(parsename);
1487 inlex = lex_open(filename);
1488 if (!inlex) {
1489 ftepp_error(ftepp, "open failed on include file `%s`", filename);
1490 vec_free(filename);
1491 return false;
1492 }
1493 ftepp->lex = inlex;
1494 old_includename = ftepp->includename;
1495 ftepp->includename = filename;
1496 if (!ftepp_preprocess(ftepp)) {
1497 vec_free(ftepp->includename);
1498 ftepp->includename = old_includename;
1499 lex_close(ftepp->lex);
1500 ftepp->lex = old_lexer;
1501 return false;
1502 }
1503 vec_free(ftepp->includename);
1504 ftepp->includename = old_includename;
1505 lex_close(ftepp->lex);
1506 ftepp->lex = old_lexer;
1507
1508 ftepp_out(ftepp, "\n#pragma file(", false);
1509 ftepp_out(ftepp, ctx.file, false);
1510 util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1511 ftepp_out(ftepp, lineno, false);
1512
1513 /* skip the line */
1514 (void)ftepp_next(ftepp);
1515 if (!ftepp_skipspace(ftepp))
1516 return false;
1517 if (ftepp->token != TOKEN_EOL) {
1518 ftepp_error(ftepp, "stray tokens after #include");
1519 return false;
1520 }
1521 (void)ftepp_next(ftepp);
1522
1523 return true;
1524 }
1525
1526 /* Basic structure handlers */
ftepp_else_allowed(ftepp_t * ftepp)1527 static bool ftepp_else_allowed(ftepp_t *ftepp)
1528 {
1529 if (!vec_size(ftepp->conditions)) {
1530 ftepp_error(ftepp, "#else without #if");
1531 return false;
1532 }
1533 if (vec_last(ftepp->conditions).had_else) {
1534 ftepp_error(ftepp, "multiple #else for a single #if");
1535 return false;
1536 }
1537 return true;
1538 }
1539
ftepp_inmacro(ftepp_t * ftepp,const char * hash)1540 static GMQCC_INLINE void ftepp_inmacro(ftepp_t *ftepp, const char *hash) {
1541 if (ftepp->in_macro)
1542 (void)!ftepp_warn(ftepp, WARN_DIRECTIVE_INMACRO, "`#%s` directive in macro", hash);
1543 }
1544
ftepp_hash(ftepp_t * ftepp)1545 static bool ftepp_hash(ftepp_t *ftepp)
1546 {
1547 ppcondition cond;
1548 ppcondition *pc;
1549
1550 lex_ctx_t ctx = ftepp_ctx(ftepp);
1551
1552 if (!ftepp_skipspace(ftepp))
1553 return false;
1554
1555 switch (ftepp->token) {
1556 case TOKEN_KEYWORD:
1557 case TOKEN_IDENT:
1558 case TOKEN_TYPENAME:
1559 if (!strcmp(ftepp_tokval(ftepp), "define")) {
1560 ftepp_inmacro(ftepp, "define");
1561 return ftepp_define(ftepp);
1562 }
1563 else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1564 ftepp_inmacro(ftepp, "undef");
1565 return ftepp_undef(ftepp);
1566 }
1567 else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1568 ftepp_inmacro(ftepp, "ifdef");
1569 if (!ftepp_ifdef(ftepp, &cond))
1570 return false;
1571 cond.was_on = cond.on;
1572 vec_push(ftepp->conditions, cond);
1573 ftepp->output_on = ftepp->output_on && cond.on;
1574 break;
1575 }
1576 else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1577 ftepp_inmacro(ftepp, "ifndef");
1578 if (!ftepp_ifdef(ftepp, &cond))
1579 return false;
1580 cond.on = !cond.on;
1581 cond.was_on = cond.on;
1582 vec_push(ftepp->conditions, cond);
1583 ftepp->output_on = ftepp->output_on && cond.on;
1584 break;
1585 }
1586 else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1587 ftepp_inmacro(ftepp, "elifdef");
1588 if (!ftepp_else_allowed(ftepp))
1589 return false;
1590 if (!ftepp_ifdef(ftepp, &cond))
1591 return false;
1592 pc = &vec_last(ftepp->conditions);
1593 pc->on = !pc->was_on && cond.on;
1594 pc->was_on = pc->was_on || pc->on;
1595 ftepp_update_output_condition(ftepp);
1596 break;
1597 }
1598 else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1599 ftepp_inmacro(ftepp, "elifndef");
1600 if (!ftepp_else_allowed(ftepp))
1601 return false;
1602 if (!ftepp_ifdef(ftepp, &cond))
1603 return false;
1604 cond.on = !cond.on;
1605 pc = &vec_last(ftepp->conditions);
1606 pc->on = !pc->was_on && cond.on;
1607 pc->was_on = pc->was_on || pc->on;
1608 ftepp_update_output_condition(ftepp);
1609 break;
1610 }
1611 else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1612 ftepp_inmacro(ftepp, "elif");
1613 if (!ftepp_else_allowed(ftepp))
1614 return false;
1615 if (!ftepp_if(ftepp, &cond))
1616 return false;
1617 pc = &vec_last(ftepp->conditions);
1618 pc->on = !pc->was_on && cond.on;
1619 pc->was_on = pc->was_on || pc->on;
1620 ftepp_update_output_condition(ftepp);
1621 break;
1622 }
1623 else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1624 ftepp_inmacro(ftepp, "if");
1625 if (!ftepp_if(ftepp, &cond))
1626 return false;
1627 cond.was_on = cond.on;
1628 vec_push(ftepp->conditions, cond);
1629 ftepp->output_on = ftepp->output_on && cond.on;
1630 break;
1631 }
1632 else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1633 ftepp_inmacro(ftepp, "else");
1634 if (!ftepp_else_allowed(ftepp))
1635 return false;
1636 pc = &vec_last(ftepp->conditions);
1637 pc->on = !pc->was_on;
1638 pc->had_else = true;
1639 ftepp_next(ftepp);
1640 ftepp_update_output_condition(ftepp);
1641 break;
1642 }
1643 else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1644 ftepp_inmacro(ftepp, "endif");
1645 if (!vec_size(ftepp->conditions)) {
1646 ftepp_error(ftepp, "#endif without #if");
1647 return false;
1648 }
1649 vec_pop(ftepp->conditions);
1650 ftepp_next(ftepp);
1651 ftepp_update_output_condition(ftepp);
1652 break;
1653 }
1654 else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1655 ftepp_inmacro(ftepp, "include");
1656 return ftepp_include(ftepp);
1657 }
1658 else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1659 ftepp_out(ftepp, "#", false);
1660 break;
1661 }
1662 else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
1663 ftepp_directive_warning(ftepp);
1664 break;
1665 }
1666 else if (!strcmp(ftepp_tokval(ftepp), "error")) {
1667 ftepp_directive_error(ftepp);
1668 break;
1669 }
1670 else if (!strcmp(ftepp_tokval(ftepp), "message")) {
1671 ftepp_directive_message(ftepp);
1672 break;
1673 }
1674 else {
1675 if (ftepp->output_on) {
1676 ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1677 return false;
1678 } else {
1679 ftepp_next(ftepp);
1680 break;
1681 }
1682 }
1683 /* break; never reached */
1684 default:
1685 ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1686 return false;
1687 case TOKEN_EOL:
1688 ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1689 return false;
1690 case TOKEN_EOF:
1691 ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1692 return false;
1693
1694 /* Builtins! Don't forget the builtins! */
1695 case TOKEN_INTCONST:
1696 case TOKEN_FLOATCONST:
1697 ftepp_out(ftepp, "#", false);
1698 return true;
1699 }
1700 if (!ftepp_skipspace(ftepp))
1701 return false;
1702 return true;
1703 }
1704
ftepp_preprocess(ftepp_t * ftepp)1705 static bool ftepp_preprocess(ftepp_t *ftepp)
1706 {
1707 ppmacro *macro;
1708 bool newline = true;
1709
1710 /* predef stuff */
1711 char *expand = nullptr;
1712
1713 ftepp->lex->flags.preprocessing = true;
1714 ftepp->lex->flags.mergelines = false;
1715 ftepp->lex->flags.noops = true;
1716
1717 ftepp_next(ftepp);
1718 do
1719 {
1720 if (ftepp->token >= TOKEN_EOF)
1721 break;
1722 switch (ftepp->token) {
1723 case TOKEN_KEYWORD:
1724 case TOKEN_IDENT:
1725 case TOKEN_TYPENAME:
1726 /* is it a predef? */
1727 if (OPTS_FLAG(FTEPP_PREDEFS)) {
1728 char *(*predef)(ftepp_t*) = ftepp_predef(ftepp_tokval(ftepp));
1729 if (predef) {
1730 expand = predef(ftepp);
1731 ftepp_out (ftepp, expand, false);
1732 ftepp_next(ftepp);
1733
1734 mem_d(expand);
1735 break;
1736 }
1737 }
1738
1739 if (ftepp->output_on)
1740 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1741 else
1742 macro = nullptr;
1743
1744 if (!macro) {
1745 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1746 ftepp_next(ftepp);
1747 break;
1748 }
1749 if (!ftepp_macro_call(ftepp, macro))
1750 ftepp->token = TOKEN_ERROR;
1751 break;
1752 case '#':
1753 if (!newline) {
1754 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1755 ftepp_next(ftepp);
1756 break;
1757 }
1758 ftepp->lex->flags.mergelines = true;
1759 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1760 ftepp_error(ftepp, "error in preprocessor directive");
1761 ftepp->token = TOKEN_ERROR;
1762 break;
1763 }
1764 if (!ftepp_hash(ftepp))
1765 ftepp->token = TOKEN_ERROR;
1766 ftepp->lex->flags.mergelines = false;
1767 break;
1768 case TOKEN_EOL:
1769 newline = true;
1770 ftepp_out(ftepp, "\n", true);
1771 ftepp_next(ftepp);
1772 break;
1773 case TOKEN_WHITE:
1774 /* same as default but don't set newline=false */
1775 ftepp_out(ftepp, ftepp_tokval(ftepp), true);
1776 ftepp_next(ftepp);
1777 break;
1778 default:
1779 newline = false;
1780 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1781 ftepp_next(ftepp);
1782 break;
1783 }
1784 } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1785
1786 /* force a 0 at the end but don't count it as added to the output */
1787 vec_push(ftepp->output_string, 0);
1788 vec_shrinkby(ftepp->output_string, 1);
1789
1790 return (ftepp->token == TOKEN_EOF);
1791 }
1792
1793 /* Like in parser.c - files keep the previous state so we have one global
1794 * preprocessor. Except here we will want to warn about dangling #ifs.
1795 */
ftepp_preprocess_done(ftepp_t * ftepp)1796 static bool ftepp_preprocess_done(ftepp_t *ftepp)
1797 {
1798 bool retval = true;
1799 if (vec_size(ftepp->conditions)) {
1800 if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1801 retval = false;
1802 }
1803 lex_close(ftepp->lex);
1804 ftepp->lex = nullptr;
1805 if (ftepp->itemname) {
1806 mem_d(ftepp->itemname);
1807 ftepp->itemname = nullptr;
1808 }
1809 return retval;
1810 }
1811
ftepp_preprocess_file(ftepp_t * ftepp,const char * filename)1812 bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
1813 {
1814 ftepp->lex = lex_open(filename);
1815 ftepp->itemname = util_strdup(filename);
1816 if (!ftepp->lex) {
1817 con_out("failed to open file \"%s\"\n", filename);
1818 return false;
1819 }
1820 if (!ftepp_preprocess(ftepp))
1821 return false;
1822 return ftepp_preprocess_done(ftepp);
1823 }
1824
ftepp_preprocess_string(ftepp_t * ftepp,const char * name,const char * str)1825 bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
1826 {
1827 ftepp->lex = lex_open_string(str, strlen(str), name);
1828 ftepp->itemname = util_strdup(name);
1829 if (!ftepp->lex) {
1830 con_out("failed to create lexer for string \"%s\"\n", name);
1831 return false;
1832 }
1833 if (!ftepp_preprocess(ftepp))
1834 return false;
1835 return ftepp_preprocess_done(ftepp);
1836 }
1837
1838
ftepp_add_macro(ftepp_t * ftepp,const char * name,const char * value)1839 void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
1840 char *create = nullptr;
1841
1842 /* use saner path for empty macros */
1843 if (!value) {
1844 ftepp_add_define(ftepp, "__builtin__", name);
1845 return;
1846 }
1847
1848 vec_append(create, 8, "#define ");
1849 vec_append(create, strlen(name), name);
1850 vec_push (create, ' ');
1851 vec_append(create, strlen(value), value);
1852 vec_push (create, 0);
1853
1854 ftepp_preprocess_string(ftepp, "__builtin__", create);
1855 vec_free (create);
1856 }
1857
ftepp_create()1858 ftepp_t *ftepp_create()
1859 {
1860 ftepp_t *ftepp;
1861 char minor[32];
1862 char major[32];
1863 size_t i;
1864
1865 ftepp = ftepp_new();
1866 if (!ftepp)
1867 return nullptr;
1868
1869 memset(minor, 0, sizeof(minor));
1870 memset(major, 0, sizeof(major));
1871
1872 /* set the right macro based on the selected standard */
1873 ftepp_add_define(ftepp, nullptr, "GMQCC");
1874 if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
1875 ftepp_add_define(ftepp, nullptr, "__STD_FTEQCC__");
1876 /* 1.00 */
1877 major[0] = '"';
1878 major[1] = '1';
1879 major[2] = '"';
1880
1881 minor[0] = '"';
1882 minor[1] = '0';
1883 minor[2] = '"';
1884 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
1885 ftepp_add_define(ftepp, nullptr, "__STD_GMQCC__");
1886 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1887 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1888 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
1889 ftepp_add_define(ftepp, nullptr, "__STD_QCCX__");
1890 util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1891 util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1892 } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
1893 ftepp_add_define(ftepp, nullptr, "__STD_QCC__");
1894 /* 1.0 */
1895 major[0] = '"';
1896 major[1] = '1';
1897 major[2] = '"';
1898
1899 minor[0] = '"';
1900 minor[1] = '0';
1901 minor[2] = '"';
1902 }
1903
1904 ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
1905 ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
1906
1907 /*
1908 * We're going to just make __NULL__ nil, which works for 60% of the
1909 * cases of __NULL_ for fteqcc.
1910 */
1911 ftepp_add_macro(ftepp, "__NULL__", "nil");
1912
1913 /* add all the math constants if they can be */
1914 if (OPTS_FLAG(FTEPP_MATHDEFS)) {
1915 for (i = 0; i < GMQCC_ARRAY_COUNT(ftepp_math_constants); i++)
1916 if (!ftepp_macro_find(ftepp, ftepp_math_constants[i][0]))
1917 ftepp_add_macro(ftepp, ftepp_math_constants[i][0], ftepp_math_constants[i][1]);
1918 }
1919
1920 return ftepp;
1921 }
1922
ftepp_add_define(ftepp_t * ftepp,const char * source,const char * name)1923 void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
1924 {
1925 ppmacro *macro;
1926 lex_ctx_t ctx = { "__builtin__", 0, 0 };
1927 ctx.file = source;
1928 macro = ppmacro_new(ctx, name);
1929 util_htset(ftepp->macros, name, macro);
1930 }
1931
ftepp_get(ftepp_t * ftepp)1932 const char *ftepp_get(ftepp_t *ftepp)
1933 {
1934 return ftepp->output_string;
1935 }
1936
ftepp_flush(ftepp_t * ftepp)1937 void ftepp_flush(ftepp_t *ftepp)
1938 {
1939 ftepp_flush_do(ftepp);
1940 }
1941
ftepp_finish(ftepp_t * ftepp)1942 void ftepp_finish(ftepp_t *ftepp)
1943 {
1944 if (!ftepp)
1945 return;
1946 ftepp_delete(ftepp);
1947 }
1948