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