]> git.xonotic.org Git - xonotic/gmqcc.git/blob - ftepp.c
Merge branch 'master' into cooking
[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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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 static 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, (void (*)(void*))&ppmacro_delete);
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 (ftepp->output_on)
568         util_htset(ftepp->macros, macro->name, (void*)macro);
569     else {
570         ppmacro_delete(macro);
571     }
572
573     for (; l < ftepp_ctx(ftepp).line; ++l)
574         ftepp_out(ftepp, "\n", true);
575     return true;
576 }
577
578 /**
579  * When a macro is used we have to handle parameters as well
580  * as special-concatenation via ## or stringification via #
581  *
582  * Note: parenthesis can nest, so FOO((a),b) is valid, but only
583  * this kind of parens. Curly braces or [] don't count towards the
584  * paren-level.
585  */
586 typedef struct {
587     pptoken **tokens;
588 } macroparam;
589
590 static void macroparam_clean(macroparam *self)
591 {
592     size_t i;
593     for (i = 0; i < vec_size(self->tokens); ++i)
594         pptoken_delete(self->tokens[i]);
595     vec_free(self->tokens);
596 }
597
598 /* need to leave the last token up */
599 static bool ftepp_macro_call_params(ftepp_t *ftepp, macroparam **out_params)
600 {
601     macroparam *params = NULL;
602     pptoken    *ptok;
603     macroparam  mp;
604     size_t      parens = 0;
605     size_t      i;
606
607     if (!ftepp_skipallwhite(ftepp))
608         return false;
609     while (ftepp->token != ')') {
610         mp.tokens = NULL;
611         if (!ftepp_skipallwhite(ftepp))
612             return false;
613         while (parens || ftepp->token != ',') {
614             if (ftepp->token == '(')
615                 ++parens;
616             else if (ftepp->token == ')') {
617                 if (!parens)
618                     break;
619                 --parens;
620             }
621             ptok = pptoken_make(ftepp);
622             vec_push(mp.tokens, ptok);
623             if (ftepp_next(ftepp) >= TOKEN_EOF) {
624                 ftepp_error(ftepp, "unexpected EOF in macro call");
625                 goto on_error;
626             }
627         }
628         vec_push(params, mp);
629         mp.tokens = NULL;
630         if (ftepp->token == ')')
631             break;
632         if (ftepp->token != ',') {
633             ftepp_error(ftepp, "expected closing paren or comma in macro call");
634             goto on_error;
635         }
636         if (ftepp_next(ftepp) >= TOKEN_EOF) {
637             ftepp_error(ftepp, "unexpected EOF in macro call");
638             goto on_error;
639         }
640     }
641     /* need to leave that up
642     if (ftepp_next(ftepp) >= TOKEN_EOF) {
643         ftepp_error(ftepp, "unexpected EOF in macro call");
644         goto on_error;
645     }
646     */
647     *out_params = params;
648     return true;
649
650 on_error:
651     if (mp.tokens)
652         macroparam_clean(&mp);
653     for (i = 0; i < vec_size(params); ++i)
654         macroparam_clean(&params[i]);
655     vec_free(params);
656     return false;
657 }
658
659 static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx)
660 {
661     size_t i;
662     for (i = 0; i < vec_size(macro->params); ++i) {
663         if (!strcmp(macro->params[i], name)) {
664             *idx = i;
665             return true;
666         }
667     }
668     return false;
669 }
670
671 static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token)
672 {
673     char        chs[2];
674     const char *ch;
675     chs[1] = 0;
676     switch (token->token) {
677         case TOKEN_STRINGCONST:
678             ch = token->value;
679             while (*ch) {
680                 /* in preprocessor mode strings already are string,
681                  * so we don't get actual newline bytes here.
682                  * Still need to escape backslashes and quotes.
683                  */
684                 switch (*ch) {
685                     case '\\': ftepp_out(ftepp, "\\\\", false); break;
686                     case '"':  ftepp_out(ftepp, "\\\"", false); break;
687                     default:
688                         chs[0] = *ch;
689                         ftepp_out(ftepp, chs, false);
690                         break;
691                 }
692                 ++ch;
693             }
694             break;
695         case TOKEN_WHITE:
696             ftepp_out(ftepp, " ", false);
697             break;
698         case TOKEN_EOL:
699             ftepp_out(ftepp, "\\n", false);
700             break;
701         default:
702             ftepp_out(ftepp, token->value, false);
703             break;
704     }
705 }
706
707 static void ftepp_stringify(ftepp_t *ftepp, macroparam *param)
708 {
709     size_t i;
710     ftepp_out(ftepp, "\"", false);
711     for (i = 0; i < vec_size(param->tokens); ++i)
712         ftepp_stringify_token(ftepp, param->tokens[i]);
713     ftepp_out(ftepp, "\"", false);
714 }
715
716 static void ftepp_recursion_header(ftepp_t *ftepp)
717 {
718     ftepp_out(ftepp, "\n#pragma push(line)\n", false);
719 }
720
721 static void ftepp_recursion_footer(ftepp_t *ftepp)
722 {
723     ftepp_out(ftepp, "\n#pragma pop(line)\n", false);
724 }
725
726 static void ftepp_param_out(ftepp_t *ftepp, macroparam *param)
727 {
728     size_t   i;
729     pptoken *out;
730     for (i = 0; i < vec_size(param->tokens); ++i) {
731         out = param->tokens[i];
732         if (out->token == TOKEN_EOL)
733             ftepp_out(ftepp, "\n", false);
734         else
735             ftepp_out(ftepp, out->value, false);
736     }
737 }
738
739 static bool ftepp_preprocess(ftepp_t *ftepp);
740 static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params, bool resetline)
741 {
742     char     *buffer       = NULL;
743     char     *old_string   = ftepp->output_string;
744     char     *inner_string;
745     lex_file *old_lexer    = ftepp->lex;
746     size_t    vararg_start = vec_size(macro->params);
747     bool      retval       = true;
748     bool      has_newlines;
749     size_t    varargs;
750
751     size_t    o, pi;
752     lex_file *inlex;
753
754     bool      old_inmacro;
755
756     int nextok;
757
758     if (vararg_start < vec_size(params))
759         varargs = vec_size(params) - vararg_start;
760     else
761         varargs = 0;
762
763     /* really ... */
764     if (!vec_size(macro->output))
765         return true;
766
767     ftepp->output_string = NULL;
768     for (o = 0; o < vec_size(macro->output); ++o) {
769         pptoken *out = macro->output[o];
770         switch (out->token) {
771             case TOKEN_VA_ARGS:
772                 if (!macro->variadic) {
773                     ftepp_error(ftepp, "internal preprocessor error: TOKEN_VA_ARGS in non-variadic macro");
774                     vec_free(old_string);
775                     return false;
776                 }
777                 if (!varargs)
778                     break;
779
780                 pi = 0;
781                 ftepp_param_out(ftepp, &params[pi + vararg_start]);
782                 for (++pi; pi < varargs; ++pi) {
783                     ftepp_out(ftepp, ", ", false);
784                     ftepp_param_out(ftepp, &params[pi + vararg_start]);
785                 }
786                 break;
787
788             case TOKEN_VA_ARGS_ARRAY:
789                 if ((size_t)out->constval.i >= varargs) {
790                     ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
791                     vec_free(old_string);
792                     return false;
793                 }
794
795                 ftepp_param_out(ftepp, &params[out->constval.i + vararg_start]);
796                 break;
797
798             case TOKEN_VA_COUNT:
799                 util_asprintf(&buffer, "%d", varargs);
800                 ftepp_out(ftepp, buffer, false);
801                 mem_d(buffer);
802                 break;
803
804             case TOKEN_IDENT:
805             case TOKEN_TYPENAME:
806             case TOKEN_KEYWORD:
807                 if (!macro_params_find(macro, out->value, &pi)) {
808                     ftepp_out(ftepp, out->value, false);
809                     break;
810                 } else
811                     ftepp_param_out(ftepp, &params[pi]);
812                 break;
813             case '#':
814                 if (o + 1 < vec_size(macro->output)) {
815                     nextok = macro->output[o+1]->token;
816                     if (nextok == '#') {
817                         /* raw concatenation */
818                         ++o;
819                         break;
820                     }
821                     if ( (nextok == TOKEN_IDENT    ||
822                           nextok == TOKEN_KEYWORD  ||
823                           nextok == TOKEN_TYPENAME) &&
824                         macro_params_find(macro, macro->output[o+1]->value, &pi))
825                     {
826                         ++o;
827                         ftepp_stringify(ftepp, &params[pi]);
828                         break;
829                     }
830                 }
831                 ftepp_out(ftepp, "#", false);
832                 break;
833             case TOKEN_EOL:
834                 ftepp_out(ftepp, "\n", false);
835                 break;
836             default:
837                 ftepp_out(ftepp, out->value, false);
838                 break;
839         }
840     }
841     vec_push(ftepp->output_string, 0);
842     /* Now run the preprocessor recursively on this string buffer */
843     /*
844     printf("__________\n%s\n=========\n", ftepp->output_string);
845     */
846     inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name);
847     if (!inlex) {
848         ftepp_error(ftepp, "internal error: failed to instantiate lexer");
849         retval = false;
850         goto cleanup;
851     }
852
853     inlex->line  = ftepp->lex->line;
854     inlex->sline = ftepp->lex->sline;
855     ftepp->lex   = inlex;
856
857     old_inmacro     = ftepp->in_macro;
858     ftepp->in_macro = true;
859     ftepp->output_string = NULL;
860     if (!ftepp_preprocess(ftepp)) {
861         ftepp->in_macro = old_inmacro;
862         vec_free(ftepp->lex->open_string);
863         vec_free(ftepp->output_string);
864         lex_close(ftepp->lex);
865         retval = false;
866         goto cleanup;
867     }
868     ftepp->in_macro = old_inmacro;
869     vec_free(ftepp->lex->open_string);
870     lex_close(ftepp->lex);
871
872     inner_string = ftepp->output_string;
873     ftepp->output_string = old_string;
874
875     has_newlines = (strchr(inner_string, '\n') != NULL);
876
877     if (has_newlines && !old_inmacro)
878         ftepp_recursion_header(ftepp);
879
880     vec_append(ftepp->output_string, vec_size(inner_string), inner_string);
881     vec_free(inner_string);
882
883     if (has_newlines && !old_inmacro)
884         ftepp_recursion_footer(ftepp);
885
886     if (resetline && !ftepp->in_macro) {
887         char lineno[128];
888         util_snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
889         ftepp_out(ftepp, lineno, false);
890     }
891
892     old_string = ftepp->output_string;
893 cleanup:
894     ftepp->lex           = old_lexer;
895     ftepp->output_string = old_string;
896     return retval;
897 }
898
899 static bool ftepp_macro_call(ftepp_t *ftepp, ppmacro *macro)
900 {
901     size_t     o;
902     macroparam *params = NULL;
903     bool        retval = true;
904     size_t      paramline;
905
906     if (!macro->has_params) {
907         if (!ftepp_macro_expand(ftepp, macro, NULL, false))
908             return false;
909         ftepp_next(ftepp);
910         return true;
911     }
912     ftepp_next(ftepp);
913
914     if (!ftepp_skipallwhite(ftepp))
915         return false;
916
917     if (ftepp->token != '(') {
918         ftepp_error(ftepp, "expected macro parameters in parenthesis");
919         return false;
920     }
921
922     ftepp_next(ftepp);
923     paramline = ftepp->lex->sline;
924     if (!ftepp_macro_call_params(ftepp, &params))
925         return false;
926
927     if ( vec_size(params) < vec_size(macro->params) ||
928         (vec_size(params) > vec_size(macro->params) && !macro->variadic) )
929     {
930         ftepp_error(ftepp, "macro %s expects%s %u paramteters, %u provided", macro->name,
931                     (macro->variadic ? " at least" : ""),
932                     (unsigned int)vec_size(macro->params),
933                     (unsigned int)vec_size(params));
934         retval = false;
935         goto cleanup;
936     }
937
938     if (!ftepp_macro_expand(ftepp, macro, params, (paramline != ftepp->lex->sline)))
939         retval = false;
940     ftepp_next(ftepp);
941
942 cleanup:
943     for (o = 0; o < vec_size(params); ++o)
944         macroparam_clean(&params[o]);
945     vec_free(params);
946     return retval;
947 }
948
949 /**
950  * #if - the FTEQCC way:
951  *    defined(FOO) => true if FOO was #defined regardless of parameters or contents
952  *    <numbers>    => True if the number is not 0
953  *    !<factor>    => True if the factor yields false
954  *    !!<factor>   => ERROR on 2 or more unary nots
955  *    <macro>      => becomes the macro's FIRST token regardless of parameters
956  *    <e> && <e>   => True if both expressions are true
957  *    <e> || <e>   => True if either expression is true
958  *    <string>     => False
959  *    <ident>      => False (remember for macros the <macro> rule applies instead)
960  * Unary + and - are weird and wrong in fteqcc so we don't allow them
961  * parenthesis in expressions are allowed
962  * parameter lists on macros are errors
963  * No mathematical calculations are executed
964  */
965 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out);
966 static bool ftepp_if_op(ftepp_t *ftepp)
967 {
968     ftepp->lex->flags.noops = false;
969     ftepp_next(ftepp);
970     if (!ftepp_skipspace(ftepp))
971         return false;
972     ftepp->lex->flags.noops = true;
973     return true;
974 }
975 static bool ftepp_if_value(ftepp_t *ftepp, bool *out, double *value_out)
976 {
977     ppmacro *macro;
978     bool     wasnot = false;
979     bool     wasneg = false;
980
981     if (!ftepp_skipspace(ftepp))
982         return false;
983
984     while (ftepp->token == '!') {
985         wasnot = true;
986         ftepp_next(ftepp);
987         if (!ftepp_skipspace(ftepp))
988             return false;
989     }
990
991     if (ftepp->token == TOKEN_OPERATOR && !strcmp(ftepp_tokval(ftepp), "-"))
992     {
993         wasneg = true;
994         ftepp_next(ftepp);
995         if (!ftepp_skipspace(ftepp))
996             return false;
997     }
998
999     switch (ftepp->token) {
1000         case TOKEN_IDENT:
1001         case TOKEN_TYPENAME:
1002         case TOKEN_KEYWORD:
1003             if (!strcmp(ftepp_tokval(ftepp), "defined")) {
1004                 ftepp_next(ftepp);
1005                 if (!ftepp_skipspace(ftepp))
1006                     return false;
1007                 if (ftepp->token != '(') {
1008                     ftepp_error(ftepp, "`defined` keyword in #if requires a macro name in parenthesis");
1009                     return false;
1010                 }
1011                 ftepp_next(ftepp);
1012                 if (!ftepp_skipspace(ftepp))
1013                     return false;
1014                 if (ftepp->token != TOKEN_IDENT &&
1015                     ftepp->token != TOKEN_TYPENAME &&
1016                     ftepp->token != TOKEN_KEYWORD)
1017                 {
1018                     ftepp_error(ftepp, "defined() used on an unexpected token type");
1019                     return false;
1020                 }
1021                 macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1022                 *out = !!macro;
1023                 ftepp_next(ftepp);
1024                 if (!ftepp_skipspace(ftepp))
1025                     return false;
1026                 if (ftepp->token != ')') {
1027                     ftepp_error(ftepp, "expected closing paren");
1028                     return false;
1029                 }
1030                 break;
1031             }
1032
1033             macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1034             if (!macro || !vec_size(macro->output)) {
1035                 *out = false;
1036                 *value_out = 0;
1037             } else {
1038                 /* This does not expand recursively! */
1039                 switch (macro->output[0]->token) {
1040                     case TOKEN_INTCONST:
1041                         *value_out = macro->output[0]->constval.i;
1042                         *out = !!(macro->output[0]->constval.i);
1043                         break;
1044                     case TOKEN_FLOATCONST:
1045                         *value_out = macro->output[0]->constval.f;
1046                         *out = !!(macro->output[0]->constval.f);
1047                         break;
1048                     default:
1049                         *out = false;
1050                         break;
1051                 }
1052             }
1053             break;
1054         case TOKEN_STRINGCONST:
1055             *value_out = 0;
1056             *out = false;
1057             break;
1058         case TOKEN_INTCONST:
1059             *value_out = ftepp->lex->tok.constval.i;
1060             *out = !!(ftepp->lex->tok.constval.i);
1061             break;
1062         case TOKEN_FLOATCONST:
1063             *value_out = ftepp->lex->tok.constval.f;
1064             *out = !!(ftepp->lex->tok.constval.f);
1065             break;
1066
1067         case '(':
1068             ftepp_next(ftepp);
1069             if (!ftepp_if_expr(ftepp, out, value_out))
1070                 return false;
1071             if (ftepp->token != ')') {
1072                 ftepp_error(ftepp, "expected closing paren in #if expression");
1073                 return false;
1074             }
1075             break;
1076
1077         default:
1078             ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
1079             if (OPTS_OPTION_BOOL(OPTION_DEBUG))
1080                 ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
1081             return false;
1082     }
1083     if (wasneg)
1084         *value_out = -*value_out;
1085     if (wasnot) {
1086         *out = !*out;
1087         *value_out = (*out ? 1 : 0);
1088     }
1089     return true;
1090 }
1091
1092 /*
1093 static bool ftepp_if_nextvalue(ftepp_t *ftepp, bool *out, double *value_out)
1094 {
1095     if (!ftepp_next(ftepp))
1096         return false;
1097     return ftepp_if_value(ftepp, out, value_out);
1098 }
1099 */
1100
1101 static bool ftepp_if_expr(ftepp_t *ftepp, bool *out, double *value_out)
1102 {
1103     if (!ftepp_if_value(ftepp, out, value_out))
1104         return false;
1105
1106     if (!ftepp_if_op(ftepp))
1107         return false;
1108
1109     if (ftepp->token == ')' || ftepp->token != TOKEN_OPERATOR)
1110         return true;
1111
1112     /* FTEQCC is all right-associative and no precedence here */
1113     if (!strcmp(ftepp_tokval(ftepp), "&&") ||
1114         !strcmp(ftepp_tokval(ftepp), "||"))
1115     {
1116         bool next = false;
1117         char opc  = ftepp_tokval(ftepp)[0];
1118         double nextvalue;
1119
1120         (void)nextvalue;
1121         if (!ftepp_next(ftepp))
1122             return false;
1123         if (!ftepp_if_expr(ftepp, &next, &nextvalue))
1124             return false;
1125
1126         if (opc == '&')
1127             *out = *out && next;
1128         else
1129             *out = *out || next;
1130
1131         *value_out = (*out ? 1 : 0);
1132         return true;
1133     }
1134     else if (!strcmp(ftepp_tokval(ftepp), "==") ||
1135              !strcmp(ftepp_tokval(ftepp), "!=") ||
1136              !strcmp(ftepp_tokval(ftepp), ">=") ||
1137              !strcmp(ftepp_tokval(ftepp), "<=") ||
1138              !strcmp(ftepp_tokval(ftepp), ">") ||
1139              !strcmp(ftepp_tokval(ftepp), "<"))
1140     {
1141         bool next = false;
1142         const char opc0 = ftepp_tokval(ftepp)[0];
1143         const char opc1 = ftepp_tokval(ftepp)[1];
1144         double other;
1145
1146         if (!ftepp_next(ftepp))
1147             return false;
1148         if (!ftepp_if_expr(ftepp, &next, &other))
1149             return false;
1150
1151         if (opc0 == '=')
1152             *out = (*value_out == other);
1153         else if (opc0 == '!')
1154             *out = (*value_out != other);
1155         else if (opc0 == '>') {
1156             if (opc1 == '=') *out = (*value_out >= other);
1157             else             *out = (*value_out > other);
1158         }
1159         else if (opc0 == '<') {
1160             if (opc1 == '=') *out = (*value_out <= other);
1161             else             *out = (*value_out < other);
1162         }
1163         *value_out = (*out ? 1 : 0);
1164
1165         return true;
1166     }
1167     else {
1168         ftepp_error(ftepp, "junk after #if");
1169         return false;
1170     }
1171 }
1172
1173 static bool ftepp_if(ftepp_t *ftepp, ppcondition *cond)
1174 {
1175     bool result = false;
1176     double dummy = 0;
1177
1178     memset(cond, 0, sizeof(*cond));
1179     (void)ftepp_next(ftepp);
1180
1181     if (!ftepp_skipspace(ftepp))
1182         return false;
1183     if (ftepp->token == TOKEN_EOL) {
1184         ftepp_error(ftepp, "expected expression for #if-directive");
1185         return false;
1186     }
1187
1188     if (!ftepp_if_expr(ftepp, &result, &dummy))
1189         return false;
1190
1191     cond->on = result;
1192     return true;
1193 }
1194
1195 /**
1196  * ifdef is rather simple
1197  */
1198 static bool ftepp_ifdef(ftepp_t *ftepp, ppcondition *cond)
1199 {
1200     ppmacro *macro;
1201     memset(cond, 0, sizeof(*cond));
1202     (void)ftepp_next(ftepp);
1203     if (!ftepp_skipspace(ftepp))
1204         return false;
1205
1206     switch (ftepp->token) {
1207         case TOKEN_IDENT:
1208         case TOKEN_TYPENAME:
1209         case TOKEN_KEYWORD:
1210             macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1211             break;
1212         default:
1213             ftepp_error(ftepp, "expected macro name");
1214             return false;
1215     }
1216
1217     (void)ftepp_next(ftepp);
1218     if (!ftepp_skipspace(ftepp))
1219         return false;
1220     /* relaxing this condition
1221     if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1222         ftepp_error(ftepp, "stray tokens after #ifdef");
1223         return false;
1224     }
1225     */
1226     cond->on = !!macro;
1227     return true;
1228 }
1229
1230 /**
1231  * undef is also simple
1232  */
1233 static bool ftepp_undef(ftepp_t *ftepp)
1234 {
1235     (void)ftepp_next(ftepp);
1236     if (!ftepp_skipspace(ftepp))
1237         return false;
1238
1239     if (ftepp->output_on) {
1240         switch (ftepp->token) {
1241             case TOKEN_IDENT:
1242             case TOKEN_TYPENAME:
1243             case TOKEN_KEYWORD:
1244                 ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
1245                 break;
1246             default:
1247                 ftepp_error(ftepp, "expected macro name");
1248                 return false;
1249         }
1250     }
1251
1252     (void)ftepp_next(ftepp);
1253     if (!ftepp_skipspace(ftepp))
1254         return false;
1255     /* relaxing this condition
1256     if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
1257         ftepp_error(ftepp, "stray tokens after #ifdef");
1258         return false;
1259     }
1260     */
1261     return true;
1262 }
1263
1264 /* Special unescape-string function which skips a leading quote
1265  * and stops at a quote, not just at \0
1266  */
1267 static void unescape(const char *str, char *out) {
1268     ++str;
1269     while (*str && *str != '"') {
1270         if (*str == '\\') {
1271             ++str;
1272             switch (*str) {
1273                 case '\\': *out++ = *str; break;
1274                 case '"':  *out++ = *str; break;
1275                 case 'a':  *out++ = '\a'; break;
1276                 case 'b':  *out++ = '\b'; break;
1277                 case 'r':  *out++ = '\r'; break;
1278                 case 'n':  *out++ = '\n'; break;
1279                 case 't':  *out++ = '\t'; break;
1280                 case 'f':  *out++ = '\f'; break;
1281                 case 'v':  *out++ = '\v'; break;
1282                 default:
1283                     *out++ = '\\';
1284                     *out++ = *str;
1285                     break;
1286             }
1287             ++str;
1288             continue;
1289         }
1290
1291         *out++ = *str++;
1292     }
1293     *out = 0;
1294 }
1295
1296 static char *ftepp_include_find_path(const char *file, const char *pathfile)
1297 {
1298     FILE       *fp;
1299     char       *filename = NULL;
1300     const char *last_slash;
1301     size_t      len;
1302
1303     if (!pathfile)
1304         return NULL;
1305
1306     last_slash = strrchr(pathfile, '/');
1307
1308     if (last_slash) {
1309         len = last_slash - pathfile;
1310         memcpy(vec_add(filename, len), pathfile, len);
1311         vec_push(filename, '/');
1312     }
1313
1314     len = strlen(file);
1315     memcpy(vec_add(filename, len+1), file, len);
1316     vec_last(filename) = 0;
1317
1318     fp = fs_file_open(filename, "rb");
1319     if (fp) {
1320         fs_file_close(fp);
1321         return filename;
1322     }
1323     vec_free(filename);
1324     return NULL;
1325 }
1326
1327 static char *ftepp_include_find(ftepp_t *ftepp, const char *file)
1328 {
1329     char *filename = NULL;
1330
1331     filename = ftepp_include_find_path(file, ftepp->includename);
1332     if (!filename)
1333         filename = ftepp_include_find_path(file, ftepp->itemname);
1334     return filename;
1335 }
1336
1337 static bool ftepp_directive_warning(ftepp_t *ftepp) {
1338     char *message = NULL;
1339
1340     if (!ftepp_skipspace(ftepp))
1341         return false;
1342
1343     /* handle the odd non string constant case so it works like C */
1344     if (ftepp->token != TOKEN_STRINGCONST) {
1345         bool  store   = false;
1346         vec_upload(message, "#warning", 8);
1347         ftepp_next(ftepp);
1348         while (ftepp->token != TOKEN_EOL) {
1349             vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
1350             ftepp_next(ftepp);
1351         }
1352         vec_push(message, '\0');
1353         if (ftepp->output_on)
1354             store = ftepp_warn(ftepp, WARN_CPP, message);
1355         else
1356             store = false;
1357         vec_free(message);
1358         return store;
1359     }
1360
1361     if (!ftepp->output_on)
1362         return false;
1363
1364     unescape  (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1365     return ftepp_warn(ftepp, WARN_CPP, "#warning %s", ftepp_tokval(ftepp));
1366 }
1367
1368 static void ftepp_directive_error(ftepp_t *ftepp) {
1369     char *message = NULL;
1370
1371     if (!ftepp_skipspace(ftepp))
1372         return;
1373
1374     /* handle the odd non string constant case so it works like C */
1375     if (ftepp->token != TOKEN_STRINGCONST) {
1376         vec_upload(message, "#error", 6);
1377         ftepp_next(ftepp);
1378         while (ftepp->token != TOKEN_EOL) {
1379             vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
1380             ftepp_next(ftepp);
1381         }
1382         vec_push(message, '\0');
1383         if (ftepp->output_on)
1384             ftepp_error(ftepp, message);
1385         vec_free(message);
1386         return;
1387     }
1388
1389     if (!ftepp->output_on)
1390         return;
1391
1392     unescape  (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1393     ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
1394 }
1395
1396 static void ftepp_directive_message(ftepp_t *ftepp) {
1397     char *message = NULL;
1398
1399     if (!ftepp_skipspace(ftepp))
1400         return;
1401
1402     /* handle the odd non string constant case so it works like C */
1403     if (ftepp->token != TOKEN_STRINGCONST) {
1404         vec_upload(message, "#message", 8);
1405         ftepp_next(ftepp);
1406         while (ftepp->token != TOKEN_EOL) {
1407             vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
1408             ftepp_next(ftepp);
1409         }
1410         vec_push(message, '\0');
1411         if (ftepp->output_on)
1412             con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", message);
1413         vec_free(message);
1414         return;
1415     }
1416
1417     if (!ftepp->output_on)
1418         return;
1419
1420     unescape     (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1421     con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message",  ftepp_tokval(ftepp));
1422 }
1423
1424 /**
1425  * Include a file.
1426  * FIXME: do we need/want a -I option?
1427  * FIXME: what about when dealing with files in subdirectories coming from a progs.src?
1428  */
1429 static bool ftepp_include(ftepp_t *ftepp)
1430 {
1431     lex_file *old_lexer = ftepp->lex;
1432     lex_file *inlex;
1433     lex_ctx  ctx;
1434     char     lineno[128];
1435     char     *filename;
1436     char     *old_includename;
1437
1438     (void)ftepp_next(ftepp);
1439     if (!ftepp_skipspace(ftepp))
1440         return false;
1441
1442     if (ftepp->token != TOKEN_STRINGCONST) {
1443         ftepp_error(ftepp, "expected filename to include");
1444         return false;
1445     }
1446
1447     if (!ftepp->output_on) {
1448         ftepp_next(ftepp);
1449         return true;
1450     }
1451
1452     ctx = ftepp_ctx(ftepp);
1453
1454     unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp));
1455
1456     ftepp_out(ftepp, "\n#pragma file(", false);
1457     ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1458     ftepp_out(ftepp, ")\n#pragma line(1)\n", false);
1459
1460     filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp));
1461     if (!filename) {
1462         ftepp_error(ftepp, "failed to open include file `%s`", ftepp_tokval(ftepp));
1463         return false;
1464     }
1465     inlex = lex_open(filename);
1466     if (!inlex) {
1467         ftepp_error(ftepp, "open failed on include file `%s`", filename);
1468         vec_free(filename);
1469         return false;
1470     }
1471     ftepp->lex = inlex;
1472     old_includename = ftepp->includename;
1473     ftepp->includename = filename;
1474     if (!ftepp_preprocess(ftepp)) {
1475         vec_free(ftepp->includename);
1476         ftepp->includename = old_includename;
1477         lex_close(ftepp->lex);
1478         ftepp->lex = old_lexer;
1479         return false;
1480     }
1481     vec_free(ftepp->includename);
1482     ftepp->includename = old_includename;
1483     lex_close(ftepp->lex);
1484     ftepp->lex = old_lexer;
1485
1486     ftepp_out(ftepp, "\n#pragma file(", false);
1487     ftepp_out(ftepp, ctx.file, false);
1488     util_snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1));
1489     ftepp_out(ftepp, lineno, false);
1490
1491     /* skip the line */
1492     (void)ftepp_next(ftepp);
1493     if (!ftepp_skipspace(ftepp))
1494         return false;
1495     if (ftepp->token != TOKEN_EOL) {
1496         ftepp_error(ftepp, "stray tokens after #include");
1497         return false;
1498     }
1499     (void)ftepp_next(ftepp);
1500
1501     return true;
1502 }
1503
1504 /* Basic structure handlers */
1505 static bool ftepp_else_allowed(ftepp_t *ftepp)
1506 {
1507     if (!vec_size(ftepp->conditions)) {
1508         ftepp_error(ftepp, "#else without #if");
1509         return false;
1510     }
1511     if (vec_last(ftepp->conditions).had_else) {
1512         ftepp_error(ftepp, "multiple #else for a single #if");
1513         return false;
1514     }
1515     return true;
1516 }
1517
1518 static bool ftepp_hash(ftepp_t *ftepp)
1519 {
1520     ppcondition cond;
1521     ppcondition *pc;
1522
1523     lex_ctx ctx = ftepp_ctx(ftepp);
1524
1525     if (!ftepp_skipspace(ftepp))
1526         return false;
1527
1528     switch (ftepp->token) {
1529         case TOKEN_KEYWORD:
1530         case TOKEN_IDENT:
1531         case TOKEN_TYPENAME:
1532             if (!strcmp(ftepp_tokval(ftepp), "define")) {
1533                 return ftepp_define(ftepp);
1534             }
1535             else if (!strcmp(ftepp_tokval(ftepp), "undef")) {
1536                 return ftepp_undef(ftepp);
1537             }
1538             else if (!strcmp(ftepp_tokval(ftepp), "ifdef")) {
1539                 if (!ftepp_ifdef(ftepp, &cond))
1540                     return false;
1541                 cond.was_on = cond.on;
1542                 vec_push(ftepp->conditions, cond);
1543                 ftepp->output_on = ftepp->output_on && cond.on;
1544                 break;
1545             }
1546             else if (!strcmp(ftepp_tokval(ftepp), "ifndef")) {
1547                 if (!ftepp_ifdef(ftepp, &cond))
1548                     return false;
1549                 cond.on = !cond.on;
1550                 cond.was_on = cond.on;
1551                 vec_push(ftepp->conditions, cond);
1552                 ftepp->output_on = ftepp->output_on && cond.on;
1553                 break;
1554             }
1555             else if (!strcmp(ftepp_tokval(ftepp), "elifdef")) {
1556                 if (!ftepp_else_allowed(ftepp))
1557                     return false;
1558                 if (!ftepp_ifdef(ftepp, &cond))
1559                     return false;
1560                 pc = &vec_last(ftepp->conditions);
1561                 pc->on     = !pc->was_on && cond.on;
1562                 pc->was_on = pc->was_on || pc->on;
1563                 ftepp_update_output_condition(ftepp);
1564                 break;
1565             }
1566             else if (!strcmp(ftepp_tokval(ftepp), "elifndef")) {
1567                 if (!ftepp_else_allowed(ftepp))
1568                     return false;
1569                 if (!ftepp_ifdef(ftepp, &cond))
1570                     return false;
1571                 cond.on = !cond.on;
1572                 pc = &vec_last(ftepp->conditions);
1573                 pc->on     = !pc->was_on && cond.on;
1574                 pc->was_on = pc->was_on || pc->on;
1575                 ftepp_update_output_condition(ftepp);
1576                 break;
1577             }
1578             else if (!strcmp(ftepp_tokval(ftepp), "elif")) {
1579                 if (!ftepp_else_allowed(ftepp))
1580                     return false;
1581                 if (!ftepp_if(ftepp, &cond))
1582                     return false;
1583                 pc = &vec_last(ftepp->conditions);
1584                 pc->on     = !pc->was_on && cond.on;
1585                 pc->was_on = pc->was_on  || pc->on;
1586                 ftepp_update_output_condition(ftepp);
1587                 break;
1588             }
1589             else if (!strcmp(ftepp_tokval(ftepp), "if")) {
1590                 if (!ftepp_if(ftepp, &cond))
1591                     return false;
1592                 cond.was_on = cond.on;
1593                 vec_push(ftepp->conditions, cond);
1594                 ftepp->output_on = ftepp->output_on && cond.on;
1595                 break;
1596             }
1597             else if (!strcmp(ftepp_tokval(ftepp), "else")) {
1598                 if (!ftepp_else_allowed(ftepp))
1599                     return false;
1600                 pc = &vec_last(ftepp->conditions);
1601                 pc->on = !pc->was_on;
1602                 pc->had_else = true;
1603                 ftepp_next(ftepp);
1604                 ftepp_update_output_condition(ftepp);
1605                 break;
1606             }
1607             else if (!strcmp(ftepp_tokval(ftepp), "endif")) {
1608                 if (!vec_size(ftepp->conditions)) {
1609                     ftepp_error(ftepp, "#endif without #if");
1610                     return false;
1611                 }
1612                 vec_pop(ftepp->conditions);
1613                 ftepp_next(ftepp);
1614                 ftepp_update_output_condition(ftepp);
1615                 break;
1616             }
1617             else if (!strcmp(ftepp_tokval(ftepp), "include")) {
1618                 return ftepp_include(ftepp);
1619             }
1620             else if (!strcmp(ftepp_tokval(ftepp), "pragma")) {
1621                 ftepp_out(ftepp, "#", false);
1622                 break;
1623             }
1624             else if (!strcmp(ftepp_tokval(ftepp), "warning")) {
1625                 ftepp_directive_warning(ftepp);
1626                 break;
1627             }
1628             else if (!strcmp(ftepp_tokval(ftepp), "error")) {
1629                 ftepp_directive_error(ftepp);
1630                 break;
1631             }
1632             else if (!strcmp(ftepp_tokval(ftepp), "message")) {
1633                 ftepp_directive_message(ftepp);
1634                 break;
1635             }
1636             else {
1637                 if (ftepp->output_on) {
1638                     ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
1639                     return false;
1640                 } else {
1641                     ftepp_next(ftepp);
1642                     break;
1643                 }
1644             }
1645             /* break; never reached */
1646         default:
1647             ftepp_error(ftepp, "unexpected preprocessor token: `%s`", ftepp_tokval(ftepp));
1648             return false;
1649         case TOKEN_EOL:
1650             ftepp_errorat(ftepp, ctx, "empty preprocessor directive");
1651             return false;
1652         case TOKEN_EOF:
1653             ftepp_error(ftepp, "missing newline at end of file", ftepp_tokval(ftepp));
1654             return false;
1655
1656         /* Builtins! Don't forget the builtins! */
1657         case TOKEN_INTCONST:
1658         case TOKEN_FLOATCONST:
1659             ftepp_out(ftepp, "#", false);
1660             return true;
1661     }
1662     if (!ftepp_skipspace(ftepp))
1663         return false;
1664     return true;
1665 }
1666
1667 static bool ftepp_preprocess(ftepp_t *ftepp)
1668 {
1669     ppmacro *macro;
1670     bool     newline = true;
1671
1672     /* predef stuff */
1673     char    *expand  = NULL;
1674
1675     ftepp->lex->flags.preprocessing = true;
1676     ftepp->lex->flags.mergelines    = false;
1677     ftepp->lex->flags.noops         = true;
1678
1679     ftepp_next(ftepp);
1680     do
1681     {
1682         if (ftepp->token >= TOKEN_EOF)
1683             break;
1684 #if 0
1685         newline = true;
1686 #endif
1687
1688         switch (ftepp->token) {
1689             case TOKEN_KEYWORD:
1690             case TOKEN_IDENT:
1691             case TOKEN_TYPENAME:
1692                 /* is it a predef? */
1693                 if (OPTS_FLAG(FTEPP_PREDEFS)) {
1694                     char *(*predef)(lex_file*) = ftepp_predef(ftepp_tokval(ftepp));
1695                     if (predef) {
1696                         expand = predef(ftepp->lex);
1697                         ftepp_out (ftepp, expand, false);
1698                         ftepp_next(ftepp);
1699
1700                         mem_d(expand);
1701                         break;
1702                     }
1703                 }
1704
1705                 if (ftepp->output_on)
1706                     macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
1707                 else
1708                     macro = NULL;
1709
1710                 if (!macro) {
1711                     ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1712                     ftepp_next(ftepp);
1713                     break;
1714                 }
1715                 if (!ftepp_macro_call(ftepp, macro))
1716                     ftepp->token = TOKEN_ERROR;
1717                 break;
1718             case '#':
1719                 if (!newline) {
1720                     ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1721                     ftepp_next(ftepp);
1722                     break;
1723                 }
1724                 ftepp->lex->flags.mergelines = true;
1725                 if (ftepp_next(ftepp) >= TOKEN_EOF) {
1726                     ftepp_error(ftepp, "error in preprocessor directive");
1727                     ftepp->token = TOKEN_ERROR;
1728                     break;
1729                 }
1730                 if (!ftepp_hash(ftepp))
1731                     ftepp->token = TOKEN_ERROR;
1732                 ftepp->lex->flags.mergelines = false;
1733                 break;
1734             case TOKEN_EOL:
1735                 newline = true;
1736                 ftepp_out(ftepp, "\n", true);
1737                 ftepp_next(ftepp);
1738                 break;
1739             case TOKEN_WHITE:
1740                 /* same as default but don't set newline=false */
1741                 ftepp_out(ftepp, ftepp_tokval(ftepp), true);
1742                 ftepp_next(ftepp);
1743                 break;
1744             default:
1745                 newline = false;
1746                 ftepp_out(ftepp, ftepp_tokval(ftepp), false);
1747                 ftepp_next(ftepp);
1748                 break;
1749         }
1750     } while (!ftepp->errors && ftepp->token < TOKEN_EOF);
1751
1752     /* force a 0 at the end but don't count it as added to the output */
1753     vec_push(ftepp->output_string, 0);
1754     vec_shrinkby(ftepp->output_string, 1);
1755
1756     return (ftepp->token == TOKEN_EOF);
1757 }
1758
1759 /* Like in parser.c - files keep the previous state so we have one global
1760  * preprocessor. Except here we will want to warn about dangling #ifs.
1761  */
1762 static bool ftepp_preprocess_done(ftepp_t *ftepp)
1763 {
1764     bool retval = true;
1765     if (vec_size(ftepp->conditions)) {
1766         if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?"))
1767             retval = false;
1768     }
1769     lex_close(ftepp->lex);
1770     ftepp->lex = NULL;
1771     if (ftepp->itemname) {
1772         mem_d(ftepp->itemname);
1773         ftepp->itemname = NULL;
1774     }
1775     return retval;
1776 }
1777
1778 bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
1779 {
1780     ftepp->lex = lex_open(filename);
1781     ftepp->itemname = util_strdup(filename);
1782     if (!ftepp->lex) {
1783         con_out("failed to open file \"%s\"\n", filename);
1784         return false;
1785     }
1786     if (!ftepp_preprocess(ftepp))
1787         return false;
1788     return ftepp_preprocess_done(ftepp);
1789 }
1790
1791 bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
1792 {
1793     ftepp->lex = lex_open_string(str, strlen(str), name);
1794     ftepp->itemname = util_strdup(name);
1795     if (!ftepp->lex) {
1796         con_out("failed to create lexer for string \"%s\"\n", name);
1797         return false;
1798     }
1799     if (!ftepp_preprocess(ftepp))
1800         return false;
1801     return ftepp_preprocess_done(ftepp);
1802 }
1803
1804
1805 void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
1806     char *create = NULL;
1807
1808     /* use saner path for empty macros */
1809     if (!value) {
1810         ftepp_add_define(ftepp, "__builtin__", name);
1811         return;
1812     }
1813
1814     vec_upload(create, "#define ", 8);
1815     vec_upload(create, name,  strlen(name));
1816     vec_push  (create, ' ');
1817     vec_upload(create, value, strlen(value));
1818     vec_push  (create, 0);
1819
1820     ftepp_preprocess_string(ftepp, "__builtin__", create);
1821     vec_free  (create);
1822 }
1823
1824 ftepp_t *ftepp_create()
1825 {
1826     ftepp_t *ftepp;
1827     char minor[32];
1828     char major[32];
1829
1830     ftepp = ftepp_new();
1831     if (!ftepp)
1832         return NULL;
1833
1834     memset(minor, 0, sizeof(minor));
1835     memset(major, 0, sizeof(major));
1836
1837     /* set the right macro based on the selected standard */
1838     ftepp_add_define(ftepp, NULL, "GMQCC");
1839     if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
1840         ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
1841         /* 1.00 */
1842         major[0] = '"';
1843         major[1] = '1';
1844         major[2] = '"';
1845
1846         minor[0] = '"';
1847         minor[1] = '0';
1848         minor[2] = '"';
1849     } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
1850         ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
1851         util_snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
1852         util_snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
1853     } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
1854         ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
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_QCC) {
1858         ftepp_add_define(ftepp, NULL, "__STD_QCC__");
1859         /* 1.0 */
1860         major[0] = '"';
1861         major[1] = '1';
1862         major[2] = '"';
1863
1864         minor[0] = '"';
1865         minor[1] = '0';
1866         minor[2] = '"';
1867     }
1868
1869     ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
1870     ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
1871
1872     /*
1873      * We're going to just make __NULL__ nil, which works for 60% of the
1874      * cases of __NULL_ for fteqcc.
1875      */
1876     ftepp_add_macro(ftepp, "__NULL__", "nil");
1877
1878     return ftepp;
1879 }
1880
1881 void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
1882 {
1883     ppmacro *macro;
1884     lex_ctx ctx = { "__builtin__", 0, 0 };
1885     ctx.file = source;
1886     macro = ppmacro_new(ctx, name);
1887     /*vec_push(ftepp->macros, macro);*/
1888     util_htset(ftepp->macros, name, macro);
1889 }
1890
1891 const char *ftepp_get(ftepp_t *ftepp)
1892 {
1893     return ftepp->output_string;
1894 }
1895
1896 void ftepp_flush(ftepp_t *ftepp)
1897 {
1898     ftepp_flush_do(ftepp);
1899 }
1900
1901 void ftepp_finish(ftepp_t *ftepp)
1902 {
1903     if (!ftepp)
1904         return;
1905     ftepp_delete(ftepp);
1906 }