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