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