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