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