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