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