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