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