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