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