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