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