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