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