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