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