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