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