Home | History | Annotate | Download | only in cpp
      1 /*
      2  * Invoke an external C preprocessor
      3  *
      4  *  Copyright (C) 2007       Paul Barker
      5  *  Copyright (C) 2001-2007  Peter Johnson
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS''
     17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE
     20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <util.h>
     30 #include <libyasm.h>
     31 
     32 /* TODO: Use autoconf to get the limit on the command line length. */
     33 #define CMDLINE_SIZE 32770
     34 
     35 #define BSIZE 512
     36 
     37 /* Pre-declare the preprocessor module object. */
     38 yasm_preproc_module yasm_cpp_LTX_preproc;
     39 
     40 /*******************************************************************************
     41     Structures.
     42 *******************************************************************************/
     43 
     44 /* An entry in a list of arguments to pass to cpp. */
     45 typedef struct cpp_arg_entry {
     46     TAILQ_ENTRY(cpp_arg_entry) entry;
     47 
     48     /*
     49         The operator (eg "-I") and the parameter (eg "include/"). op is expected
     50         to point to a string literal, whereas param is expected to be a copy of
     51         the parameter which is free'd when no-longer needed (in
     52         cpp_preproc_destroy()).
     53     */
     54     const char *op;
     55     char *param;
     56 } cpp_arg_entry;
     57 
     58 typedef struct yasm_preproc_cpp {
     59     yasm_preproc_base preproc;   /* base structure */
     60 
     61     /* List of arguments to pass to cpp. */
     62     TAILQ_HEAD(cpp_arg_head, cpp_arg_entry) cpp_args;
     63 
     64     char *filename;
     65     FILE *f, *f_deps;
     66     yasm_linemap *cur_lm;
     67     yasm_errwarns *errwarns;
     68 
     69     int flags;
     70 } yasm_preproc_cpp;
     71 
     72 /* Flag values for yasm_preproc_cpp->flags. */
     73 #define CPP_HAS_BEEN_INVOKED        0x01
     74 #define CPP_HAS_GENERATED_DEPS      0x02
     75 
     76 /*******************************************************************************
     77     Internal functions and helpers.
     78 *******************************************************************************/
     79 
     80 /*
     81     Append a string to the command line, ensuring that we don't overflow the
     82     buffer.
     83 */
     84 #define APPEND(s) do {                              \
     85     size_t _len = strlen(s);                        \
     86     if (p + _len >= limit)                          \
     87         yasm__fatal(N_("command line too long!"));  \
     88     strcpy(p, s);                                   \
     89     p += _len;                                      \
     90 } while (0)
     91 
     92 /*
     93     Put all the options together into a command line that can be used to invoke
     94     cpp.
     95 */
     96 static char *
     97 cpp_build_cmdline(yasm_preproc_cpp *pp, const char *extra)
     98 {
     99     char *cmdline, *p, *limit;
    100     cpp_arg_entry *arg;
    101 
    102     /* Initialize command line. */
    103     cmdline = p = yasm_xmalloc(strlen(CPP_PROG)+CMDLINE_SIZE);
    104     limit = p + CMDLINE_SIZE;
    105     strcpy(p, CPP_PROG);
    106     p += strlen(CPP_PROG);
    107 
    108     arg = TAILQ_FIRST(&pp->cpp_args);
    109 
    110     /* Append arguments from the list. */
    111     while ( arg ) {
    112         APPEND(" ");
    113         APPEND(arg->op);
    114         APPEND(" ");
    115         APPEND(arg->param);
    116 
    117         arg = TAILQ_NEXT(arg, entry);
    118     }
    119 
    120     /* Append extra arguments. */
    121     if (extra) {
    122         APPEND(" ");
    123         APPEND(extra);
    124     }
    125     /* Append final arguments. */
    126     APPEND(" -x assembler-with-cpp ");
    127     APPEND(pp->filename);
    128 
    129     return cmdline;
    130 }
    131 
    132 /* Invoke the c preprocessor. */
    133 static void
    134 cpp_invoke(yasm_preproc_cpp *pp)
    135 {
    136     char *cmdline;
    137 
    138     cmdline = cpp_build_cmdline(pp, NULL);
    139 
    140 #ifdef HAVE_POPEN
    141     pp->f = popen(cmdline, "r");
    142     if (!pp->f)
    143         yasm__fatal( N_("Failed to execute preprocessor") );
    144 #else
    145     yasm__fatal( N_("Cannot execute preprocessor, no popen available") );
    146 #endif
    147 
    148     yasm_xfree(cmdline);
    149 }
    150 
    151 /* Free memory used by the list of arguments. */
    152 static void
    153 cpp_destroy_args(yasm_preproc_cpp *pp)
    154 {
    155     cpp_arg_entry *arg;
    156 
    157     while ( (arg = TAILQ_FIRST(&pp->cpp_args)) ) {
    158         TAILQ_REMOVE(&pp->cpp_args, arg, entry);
    159         yasm_xfree(arg->param);
    160         yasm_xfree(arg);
    161     }
    162 }
    163 
    164 /* Invoke the c preprocessor to generate dependency info. */
    165 static void
    166 cpp_generate_deps(yasm_preproc_cpp *pp)
    167 {
    168     char *cmdline;
    169 
    170     cmdline = cpp_build_cmdline(pp, "-M");
    171 
    172 #ifdef HAVE_POPEN
    173     pp->f_deps = popen(cmdline, "r");
    174     if (!pp->f_deps)
    175         yasm__fatal( N_("Failed to execute preprocessor") );
    176 #else
    177     yasm__fatal( N_("Cannot execute preprocessor, no popen available") );
    178 #endif
    179 
    180     yasm_xfree(cmdline);
    181 }
    182 
    183 /*******************************************************************************
    184     Interface functions.
    185 *******************************************************************************/
    186 static yasm_preproc *
    187 cpp_preproc_create(const char *in, yasm_symtab *symtab, yasm_linemap *lm,
    188                    yasm_errwarns *errwarns)
    189 {
    190     yasm_preproc_cpp *pp = yasm_xmalloc(sizeof(yasm_preproc_cpp));
    191     void * iter;
    192     const char * inc_dir;
    193 
    194     pp->preproc.module = &yasm_cpp_LTX_preproc;
    195     pp->f = pp->f_deps = NULL;
    196     pp->cur_lm = lm;
    197     pp->errwarns = errwarns;
    198     pp->flags = 0;
    199     pp->filename = yasm__xstrdup(in);
    200 
    201     TAILQ_INIT(&pp->cpp_args);
    202 
    203     /* Iterate through the list of include dirs. */
    204     iter = NULL;
    205     while ((inc_dir = yasm_get_include_dir(&iter)) != NULL) {
    206         cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry));
    207         arg->op = "-I";
    208         arg->param = yasm__xstrdup(inc_dir);
    209 
    210         TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry);
    211     }
    212 
    213     return (yasm_preproc *)pp;
    214 }
    215 
    216 static void
    217 cpp_preproc_destroy(yasm_preproc *preproc)
    218 {
    219     yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc;
    220 
    221     if (pp->f) {
    222 #ifdef HAVE_POPEN
    223         if (pclose(pp->f) != 0)
    224             yasm__fatal( N_("Preprocessor exited with failure") );
    225 #endif
    226     }
    227 
    228     cpp_destroy_args(pp);
    229 
    230     yasm_xfree(pp->filename);
    231     yasm_xfree(pp);
    232 }
    233 
    234 static char *
    235 cpp_preproc_get_line(yasm_preproc *preproc)
    236 {
    237     yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc;
    238     int bufsize = BSIZE;
    239     char *buf, *p;
    240 
    241     if (! (pp->flags & CPP_HAS_BEEN_INVOKED) ) {
    242         pp->flags |= CPP_HAS_BEEN_INVOKED;
    243 
    244         cpp_invoke(pp);
    245     }
    246 
    247     /*
    248         Once the preprocessor has been run, we're just dealing with a normal
    249         file.
    250     */
    251 
    252     /* Loop to ensure entire line is read (don't want to limit line length). */
    253     buf = yasm_xmalloc((size_t)bufsize);
    254     p = buf;
    255     for (;;) {
    256         if (!fgets(p, bufsize-(p-buf), pp->f)) {
    257             if (ferror(pp->f)) {
    258                 yasm_error_set(YASM_ERROR_IO,
    259                                N_("error when reading from file"));
    260                 yasm_errwarn_propagate(pp->errwarns,
    261                     yasm_linemap_get_current(pp->cur_lm));
    262             }
    263             break;
    264         }
    265         p += strlen(p);
    266         if (p > buf && p[-1] == '\n')
    267             break;
    268         if ((p-buf) >= bufsize) {
    269             /* Increase size of buffer */
    270             char *oldbuf = buf;
    271             bufsize *= 2;
    272             buf = yasm_xrealloc(buf, (size_t)bufsize);
    273             p = buf + (p-oldbuf);
    274         }
    275     }
    276 
    277     if (p == buf) {
    278         /* No data; must be at EOF */
    279         yasm_xfree(buf);
    280         return NULL;
    281     }
    282 
    283     /* Strip the line ending */
    284     buf[strcspn(buf, "\r\n")] = '\0';
    285 
    286     return buf;
    287 }
    288 
    289 static size_t
    290 cpp_preproc_get_included_file(yasm_preproc *preproc, char *buf,
    291                               size_t max_size)
    292 {
    293     char *p = buf;
    294     int ch = '\0';
    295     size_t n = 0;
    296     yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc;
    297 
    298     if (! (pp->flags & CPP_HAS_GENERATED_DEPS) ) {
    299         pp->flags |= CPP_HAS_GENERATED_DEPS;
    300 
    301         cpp_generate_deps(pp);
    302 
    303         /* Skip target name and first dependency. */
    304         while (ch != ':')
    305             ch = fgetc(pp->f_deps);
    306 
    307         fgetc(pp->f_deps);      /* Discard space after colon. */
    308 
    309         while (ch != ' ' && ch != EOF)
    310             ch = fgetc(pp->f_deps);
    311 
    312         if (ch == EOF)
    313             return 0;
    314     }
    315 
    316     while (n < max_size) {
    317         ch = fgetc(pp->f_deps);
    318 
    319         if (ch == ' ' || ch == EOF) {
    320             *p = '\0';
    321             return n;
    322         }
    323 
    324         /* Eat any silly characters. */
    325         if (ch < ' ')
    326             continue;
    327 
    328         *p++ = ch;
    329         n++;
    330     }
    331 
    332     /* Ensure the buffer is null-terminated. */
    333     *(p - 1) = '\0';
    334     return n;
    335 }
    336 
    337 static void
    338 cpp_preproc_add_include_file(yasm_preproc *preproc, const char *filename)
    339 {
    340     yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc;
    341 
    342     cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry));
    343     arg->op = "-include";
    344     arg->param = yasm__xstrdup(filename);
    345 
    346     TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry);
    347 }
    348 
    349 static void
    350 cpp_preproc_predefine_macro(yasm_preproc *preproc, const char *macronameval)
    351 {
    352     yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc;
    353 
    354     cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry));
    355     arg->op = "-D";
    356     arg->param = yasm__xstrdup(macronameval);
    357 
    358     TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry);
    359 }
    360 
    361 static void
    362 cpp_preproc_undefine_macro(yasm_preproc *preproc, const char *macroname)
    363 {
    364     yasm_preproc_cpp *pp = (yasm_preproc_cpp *)preproc;
    365 
    366     cpp_arg_entry *arg = yasm_xmalloc(sizeof(cpp_arg_entry));
    367     arg->op = "-U";
    368     arg->param = yasm__xstrdup(macroname);
    369 
    370     TAILQ_INSERT_TAIL(&pp->cpp_args, arg, entry);
    371 }
    372 
    373 static void
    374 cpp_preproc_define_builtin(yasm_preproc *preproc, const char *macronameval)
    375 {
    376     /* Handle a builtin as if it were a predefine. */
    377     cpp_preproc_predefine_macro(preproc, macronameval);
    378 }
    379 
    380 static void
    381 cpp_preproc_add_standard(yasm_preproc *preproc, const char **macros)
    382 {
    383     /* TODO */
    384 }
    385 
    386 /*******************************************************************************
    387     Preprocessor module object.
    388 *******************************************************************************/
    389 
    390 yasm_preproc_module yasm_cpp_LTX_preproc = {
    391     "Run input through external C preprocessor",
    392     "cpp",
    393     cpp_preproc_create,
    394     cpp_preproc_destroy,
    395     cpp_preproc_get_line,
    396     cpp_preproc_get_included_file,
    397     cpp_preproc_add_include_file,
    398     cpp_preproc_predefine_macro,
    399     cpp_preproc_undefine_macro,
    400     cpp_preproc_define_builtin,
    401     cpp_preproc_add_standard
    402 };
    403