/* * Invoke an external C preprocessor * * Copyright (C) 2007 Paul Barker * Copyright (C) 2001-2007 Peter Johnson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include /* TODO: Use autoconf to get the limit on the command line length. */ #define CMDLINE_SIZE 32770 #define BSIZE 512 /* Pre-declare the preprocessor module object. */ yasm_preproc_module yasm_cpp_LTX_preproc; /******************************************************************************* Structures. *******************************************************************************/ /* An entry in a list of arguments to pass to cpp. */ typedef struct cpp_arg_entry { TAILQ_ENTRY(cpp_arg_entry) entry; /* The operator (eg "-I") and the parameter (eg "include/"). op is expected to point to a string literal, whereas param is expected to be a copy of the parameter which is free'd when no-longer needed (in cpp_preproc_destroy()). */ const char *op; char *param; } cpp_arg_entry; typedef struct yasm_preproc_cpp { yasm_preproc_base preproc; /* base structure */ /* List of arguments to pass to cpp. */ TAILQ_HEAD(, cpp_arg_entry) cpp_args; char *filename; FILE *f, *f_deps; yasm_linemap *cur_lm; yasm_errwarns *errwarns; int flags; } yasm_preproc_cpp; /* Flag values for yasm_preproc_cpp->flags. */ #define CPP_HAS_BEEN_INVOKED 0x01 #define CPP_HAS_GENERATED_DEPS 0x02 /******************************************************************************* Internal functions and helpers. *******************************************************************************/ /* Append a string to the command line, ensuring that we don't overflow the buffer. */ #define APPEND(s) do { \ size_t _len = strlen(s); \ if (p + _len >= limit) \ yasm__fatal(N_("command line too long!")); \ strcpy(p, s); \ p += _len; \ } while (0) /* Put all the options together into a command line that can be used to invoke cpp. */ static char * cpp_build_cmdline(yasm_preproc_cpp *pp, const char *extra) { char *cmdline, *p, *limit; cpp_arg_entry *arg; /* Initialize command line. */ cmdline = p = yasm_xmalloc(strlen(CPP_PROG)+CMDLINE_SIZE); limit = p + CMDLINE_SIZE; strcpy(p, CPP_PROG); p += 3; arg = TAILQ_FIRST(&pp->cpp_args); /* Append arguments from the list. */ while ( arg ) { APPEND(" "); APPEND(arg->op); APPEND(" "); APPEND(arg->param); arg = TAILQ_NEXT(arg, entry); } /* Append extra arguments. */ if (extra) { APPEND(" "); APPEND(extra); } /* Append final arguments. */ APPEND(" -x assembler-with-cpp "); APPEND(pp->filename); return cmdline; } /* Invoke the c preprocessor. */ static void cpp_invoke(yasm_preproc_cpp *pp) { char *cmdline; cmdline = cpp_build_cmdline(pp, NULL); #ifdef HAVE_POPEN pp->f = popen(cmdline, "r"); if (!pp->f) yasm__fatal( N_("Failed to execute preprocessor") ); #else yasm__fatal( N_("Cannot execute preprocessor, no popen available") ); #endif yasm_xfree(cmdline); } /* Free memory used by the list of arguments. */ static void cpp_destroy_args(yasm_preproc_cpp *pp) { cpp_arg_entry *arg; while ( (arg = TAILQ_FIRST(&pp->cpp_args)) ) { TAILQ_REMOVE(&pp->cpp_args, arg, entry); yasm_xfree(arg->param); yasm_xfree(arg); } } /* Invoke the c preprocessor to generate dependency info. */ static void cpp_generate_deps(yasm_preproc_cpp *pp) { char *cmdline; cmdline = cpp_build_cmdline(pp, "-M"); #ifdef HAVE_POPEN pp->f_deps = popen(cmdline, "r"); if (!pp->f_deps) yasm__fatal( N_("Failed to execute preprocessor") ); #else yasm__fatal( N_("Cannot execute preprocessor, no popen available") ); #endif yasm_xfree(cmdline); } /******************************************************************************* Interface functions. *******************************************************************************/ static yasm_preproc * cpp_preproc_create(const char *in, yasm_symtab *symtab, yasm_linemap *lm, yasm_errwarns *errwarns) { yasm_preproc_cpp *pp = yasm_xmalloc(sizeof(yasm_preproc_cpp)); void * iter; const char * inc_dir; pp->preproc.module = &yasm_cpp_LTX_preproc; pp->f = pp->f_deps = NULL; pp->cur_lm = lm; pp->errwarns = errwarns; pp->flags = 0; pp->filename = yasm__xstrdup(in); TAILQ_INIT(&pp->cpp_args); /* Iterate through the list of include dirs. */ iter = NULL; while ((inc_dir = yasm_get_include_dir(&iter)) != NULL) { cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry)); arg->op = "-I"; arg->param = yasm__xstrdup(inc_dir); TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry); } return (yasm_preproc *)pp; } static void cpp_preproc_destroy(yasm_preproc *preproc) { yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc; if (pp->f) { #ifdef HAVE_POPEN if (pclose(pp->f) != 0) yasm__fatal( N_("Preprocessor exited with failure") ); #endif } cpp_destroy_args(pp); yasm_xfree(pp->filename); yasm_xfree(pp); } static char * cpp_preproc_get_line(yasm_preproc *preproc) { yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc; int bufsize = BSIZE; char *buf, *p; if (! (pp->flags & CPP_HAS_BEEN_INVOKED) ) { pp->flags |= CPP_HAS_BEEN_INVOKED; cpp_invoke(pp); } /* Once the preprocessor has been run, we're just dealing with a normal file. */ /* Loop to ensure entire line is read (don't want to limit line length). */ buf = yasm_xmalloc((size_t)bufsize); p = buf; for (;;) { if (!fgets(p, bufsize-(p-buf), pp->f)) { if (ferror(pp->f)) { yasm_error_set(YASM_ERROR_IO, N_("error when reading from file")); yasm_errwarn_propagate(pp->errwarns, yasm_linemap_get_current(pp->cur_lm)); } break; } p += strlen(p); if (p > buf && p[-1] == '\n') break; if ((p-buf) >= bufsize) { /* Increase size of buffer */ char *oldbuf = buf; bufsize *= 2; buf = yasm_xrealloc(buf, (size_t)bufsize); p = buf + (p-oldbuf); } } if (p == buf) { /* No data; must be at EOF */ yasm_xfree(buf); return NULL; } /* Strip the line ending */ buf[strcspn(buf, "\r\n")] = '\0'; return buf; } static size_t cpp_preproc_get_included_file(yasm_preproc *preproc, char *buf, size_t max_size) { char *p = buf; int ch = '\0'; size_t n = 0; yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc; if (! (pp->flags & CPP_HAS_GENERATED_DEPS) ) { pp->flags |= CPP_HAS_GENERATED_DEPS; cpp_generate_deps(pp); /* Skip target name and first dependency. */ while (ch != ':') ch = fgetc(pp->f_deps); fgetc(pp->f_deps); /* Discard space after colon. */ while (ch != ' ' && ch != EOF) ch = fgetc(pp->f_deps); if (ch == EOF) return 0; } while (n < max_size) { ch = fgetc(pp->f_deps); if (ch == ' ' || ch == EOF) { *p = '\0'; return n; } /* Eat any silly characters. */ if (ch < ' ') continue; *p++ = ch; n++; } /* Ensure the buffer is null-terminated. */ *(p - 1) = '\0'; return n; } static void cpp_preproc_add_include_file(yasm_preproc *preproc, const char *filename) { yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc; cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry)); arg->op = "-include"; arg->param = yasm__xstrdup(filename); TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry); } static void cpp_preproc_predefine_macro(yasm_preproc *preproc, const char *macronameval) { yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc; cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry)); arg->op = "-D"; arg->param = yasm__xstrdup(macronameval); TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry); } static void cpp_preproc_undefine_macro(yasm_preproc *preproc, const char *macroname) { yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc; cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry)); arg->op = "-U"; arg->param = yasm__xstrdup(macroname); TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry); } static void cpp_preproc_define_builtin(yasm_preproc *preproc, const char *macronameval) { /* Handle a builtin as if it were a predefine. */ cpp_preproc_predefine_macro(preproc, macronameval); } /******************************************************************************* Preprocessor module object. *******************************************************************************/ yasm_preproc_module yasm_cpp_LTX_preproc = { "Run input through external C preprocessor", "cpp", cpp_preproc_create, cpp_preproc_destroy, cpp_preproc_get_line, cpp_preproc_get_included_file, cpp_preproc_add_include_file, cpp_preproc_predefine_macro, cpp_preproc_undefine_macro, cpp_preproc_define_builtin };