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