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