X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=ftepp.c;h=1d734305406602d4c48e0312554f2b5384e2f70f;hb=b1425dfcf1afa18b81168ed57a4b790ddc0605b5;hp=59ec54504402efdfdbf70c737405e5d4f73f07bd;hpb=79abe3fb6d9cf6c0d5f234d64fd77181b2c2317d;p=xonotic%2Fgmqcc.git diff --git a/ftepp.c b/ftepp.c index 59ec545..1d73430 100644 --- a/ftepp.c +++ b/ftepp.c @@ -62,8 +62,9 @@ typedef struct { ppcondition *conditions; ppmacro **macros; - bool output_string; - char *output; + char *output_string; + + char *itemname; } ftepp_t; #define ftepp_tokval(f) ((f)->lex->tok.value) @@ -152,7 +153,7 @@ static void ppmacro_delete(ppmacro *self) mem_d(self); } -static ftepp_t* ftepp_init() +static ftepp_t* ftepp_new() { ftepp_t *ftepp; @@ -167,11 +168,14 @@ static ftepp_t* ftepp_init() static void ftepp_delete(ftepp_t *self) { size_t i; + if (self->itemname) + mem_d(self->itemname); for (i = 0; i < vec_size(self->macros); ++i) ppmacro_delete(self->macros[i]); vec_free(self->macros); vec_free(self->conditions); - lex_close(self->lex); + if (self->lex) + lex_close(self->lex); mem_d(self); } @@ -181,12 +185,8 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) { size_t len; char *data; - if (!ftepp->output_string) { - printf("%s", str); - return; - } len = strlen(str); - data = vec_add(ftepp->output, len); + data = vec_add(ftepp->output_string, len); memcpy(data, str, len); } } @@ -442,23 +442,78 @@ static bool macro_params_find(ppmacro *macro, const char *name, size_t *idx) return false; } +static void ftepp_stringify_token(ftepp_t *ftepp, pptoken *token) +{ + char chs[2]; + const char *ch; + chs[1] = 0; + switch (token->token) { + case TOKEN_STRINGCONST: + ch = token->value; + while (*ch) { + /* in preprocessor mode strings already are string, + * so we don't get actual newline bytes here. + * Still need to escape backslashes and quotes. + */ + switch (*ch) { + case '\\': ftepp_out(ftepp, "\\\\", false); break; + case '"': ftepp_out(ftepp, "\\\"", false); break; + default: + chs[0] = *ch; + ftepp_out(ftepp, chs, false); + break; + } + ++ch; + } + break; + case TOKEN_WHITE: + ftepp_out(ftepp, " ", false); + break; + case TOKEN_EOL: + ftepp_out(ftepp, "\\n", false); + break; + default: + ftepp_out(ftepp, token->value, false); + break; + } +} + +static void ftepp_stringify(ftepp_t *ftepp, macroparam *param) +{ + size_t i; + ftepp_out(ftepp, "\"", false); + for (i = 0; i < vec_size(param->tokens); ++i) + ftepp_stringify_token(ftepp, param->tokens[i]); + ftepp_out(ftepp, "\"", false); +} + +static void ftepp_recursion_header(ftepp_t *ftepp) +{ + ftepp_out(ftepp, "\n#pragma push(line)\n", false); +} + +static void ftepp_recursion_footer(ftepp_t *ftepp) +{ + ftepp_out(ftepp, "\n#pragma pop(line)\n", false); +} + static bool ftepp_preprocess(ftepp_t *ftepp); static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *params) { - char *old_string = ftepp->output; - bool old_string_flag = ftepp->output_string; + char *old_string = ftepp->output_string; lex_file *old_lexer = ftepp->lex; bool retval = true; size_t o, pi, pv; lex_file *inlex; + int nextok; + /* really ... */ if (!vec_size(macro->output)) return true; - ftepp->output = NULL; - ftepp->output_string = true; + ftepp->output_string = NULL; for (o = 0; o < vec_size(macro->output); ++o) { pptoken *out = macro->output[o]; switch (out->token) { @@ -479,10 +534,22 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param } break; case '#': - if (o + 1 < vec_size(macro->output) && macro->output[o+1]->token == '#') { - /* raw concatenation */ - ++o; - break; + if (o + 1 < vec_size(macro->output)) { + nextok = macro->output[o+1]->token; + if (nextok == '#') { + /* raw concatenation */ + ++o; + break; + } + if ( (nextok == TOKEN_IDENT || + nextok == TOKEN_KEYWORD || + nextok == TOKEN_TYPENAME) && + macro_params_find(macro, macro->output[o+1]->value, &pi)) + { + ++o; + ftepp_stringify(ftepp, ¶ms[pi]); + break; + } } ftepp_out(ftepp, "#", false); break; @@ -494,29 +561,31 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param break; } } - vec_push(ftepp->output, 0); + vec_push(ftepp->output_string, 0); /* Now run the preprocessor recursively on this string buffer */ /* - printf("__________\n%s\n=========\n", ftepp->output); + printf("__________\n%s\n=========\n", ftepp->output_string); */ - inlex = lex_open_string(ftepp->output, vec_size(ftepp->output)-1, ftepp->lex->name); + inlex = lex_open_string(ftepp->output_string, vec_size(ftepp->output_string)-1, ftepp->lex->name); if (!inlex) { ftepp_error(ftepp, "internal error: failed to instantiate lexer"); retval = false; goto cleanup; } - ftepp->output = old_string; - ftepp->output_string = old_string_flag; + ftepp->output_string = old_string; ftepp->lex = inlex; + ftepp_recursion_header(ftepp); if (!ftepp_preprocess(ftepp)) { + lex_close(ftepp->lex); retval = false; goto cleanup; } + ftepp_recursion_footer(ftepp); + old_string = ftepp->output_string; cleanup: ftepp->lex = old_lexer; - ftepp->output = old_string; - ftepp->output_string = old_string_flag; + ftepp->output_string = old_string; return retval; } @@ -799,6 +868,129 @@ static bool ftepp_undef(ftepp_t *ftepp) return true; } +/* Special unescape-string function which skips a leading quote + * and stops at a quote, not just at \0 + */ +static void unescape(const char *str, char *out) { + ++str; + while (*str && *str != '"') { + if (*str == '\\') { + ++str; + switch (*str) { + case '\\': *out++ = *str; break; + case '"': *out++ = *str; break; + case 'a': *out++ = '\a'; break; + case 'b': *out++ = '\b'; break; + case 'r': *out++ = '\r'; break; + case 'n': *out++ = '\n'; break; + case 't': *out++ = '\t'; break; + case 'f': *out++ = '\f'; break; + case 'v': *out++ = '\v'; break; + default: + *out++ = '\\'; + *out++ = *str; + break; + } + ++str; + continue; + } + + *out++ = *str++; + } + *out = 0; +} + +static char *ftepp_include_find(ftepp_t *ftepp, const char *file) +{ + char *filename = NULL; + size_t len; + + if (ftepp->itemname) { + const char *last_slash; + last_slash = strrchr(ftepp->itemname, '/'); + if (last_slash) { + len = last_slash - ftepp->itemname; + memcpy(vec_add(filename, len), ftepp->itemname, len); + vec_push(filename, '/'); + } + else { + len = strlen(ftepp->itemname); + memcpy(vec_add(filename, len), ftepp->itemname, len); + if (vec_last(filename) != '/') + vec_push(filename, '/'); + } + } + len = strlen(file); + memcpy(vec_add(filename, len), file, len); + vec_push(filename, 0); + return filename; +} + +/** + * Include a file. + * FIXME: do we need/want a -I option? + * FIXME: what about when dealing with files in subdirectories coming from a progs.src? + */ +static bool ftepp_include(ftepp_t *ftepp) +{ + lex_file *old_lexer = ftepp->lex; + lex_file *inlex; + lex_ctx ctx; + char lineno[128]; + char *filename; + + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + + if (ftepp->token != TOKEN_STRINGCONST) { + ftepp_error(ftepp, "expected filename to include"); + return false; + } + + ctx = ftepp_ctx(ftepp); + + unescape(ftepp_tokval(ftepp), ftepp_tokval(ftepp)); + + ftepp_out(ftepp, "\n#pragma file(", false); + ftepp_out(ftepp, ftepp_tokval(ftepp), false); + ftepp_out(ftepp, ")\n#pragma line(1)\n", false); + + filename = ftepp_include_find(ftepp, ftepp_tokval(ftepp)); + inlex = lex_open(filename); + if (!inlex) { + ftepp_error(ftepp, "failed to open include file `%s`", filename); + vec_free(filename); + return false; + } + vec_free(filename); + ftepp->lex = inlex; + if (!ftepp_preprocess(ftepp)) { + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + return false; + } + lex_close(ftepp->lex); + ftepp->lex = old_lexer; + + ftepp_out(ftepp, "\n#pragma file(", false); + ftepp_out(ftepp, ctx.file, false); + snprintf(lineno, sizeof(lineno), ")\n#pragma line(%lu)\n", (unsigned long)(ctx.line+1)); + ftepp_out(ftepp, lineno, false); + + /* skip the line */ + (void)ftepp_next(ftepp); + if (!ftepp_skipspace(ftepp)) + return false; + if (ftepp->token != TOKEN_EOL) { + ftepp_error(ftepp, "stray tokens after #include"); + return false; + } + (void)ftepp_next(ftepp); + + return true; +} + /* Basic structure handlers */ static bool ftepp_else_allowed(ftepp_t *ftepp) { @@ -912,6 +1104,13 @@ static bool ftepp_hash(ftepp_t *ftepp) ftepp_update_output_condition(ftepp); break; } + else if (!strcmp(ftepp_tokval(ftepp), "include")) { + return ftepp_include(ftepp); + } + else if (!strcmp(ftepp_tokval(ftepp), "pragma")) { + ftepp_out(ftepp, "#", false); + break; + } else { ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp)); return false; @@ -1005,10 +1204,31 @@ static bool ftepp_preprocess(ftepp_t *ftepp) return newline; } +/* Like in parser.c - files keep the previous state so we have one global + * preprocessor. Except here we will want to warn about dangling #ifs. + */ +static ftepp_t *ftepp; + +static bool ftepp_preprocess_done() +{ + bool retval = true; + lex_close(ftepp->lex); + ftepp->lex = NULL; + if (vec_size(ftepp->conditions)) { + if (ftepp_warn(ftepp, WARN_MULTIFILE_IF, "#if spanning multiple files, is this intended?")) + retval = false; + } + if (ftepp->itemname) { + mem_d(ftepp->itemname); + ftepp->itemname = NULL; + } + return retval; +} + bool ftepp_preprocess_file(const char *filename) { - ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open(filename); + ftepp->itemname = util_strdup(filename); if (!ftepp->lex) { con_out("failed to open file \"%s\"\n", filename); return false; @@ -1017,14 +1237,13 @@ bool ftepp_preprocess_file(const char *filename) ftepp_delete(ftepp); return false; } - ftepp_delete(ftepp); - return true; + return ftepp_preprocess_done(); } bool ftepp_preprocess_string(const char *name, const char *str) { - ftepp_t *ftepp = ftepp_init(); ftepp->lex = lex_open_string(str, strlen(str), name); + ftepp->itemname = util_strdup(name); if (!ftepp->lex) { con_out("failed to create lexer for string \"%s\"\n", name); return false; @@ -1033,6 +1252,29 @@ bool ftepp_preprocess_string(const char *name, const char *str) ftepp_delete(ftepp); return false; } + return ftepp_preprocess_done(); +} + +bool ftepp_init() +{ + ftepp = ftepp_new(); + return !!ftepp; +} + +const char *ftepp_get() +{ + return ftepp->output_string; +} + +void ftepp_flush() +{ + vec_free(ftepp->output_string); +} + +void ftepp_finish() +{ + if (!ftepp) + return; ftepp_delete(ftepp); - return true; + ftepp = NULL; }