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