#include "n_libc.h" #include #include #include #include #include #include #include "misc.h" #include "error.h" #include "defs.h" #include "archdefs.h" #include "preprocess.h" static struct include_dir *includes_tail = NULL; int archflag; int abiflag; int nostdincflag; static struct include_dir * make_inc_dir(const char *path) { struct include_dir *ret; static struct include_dir nullinc; ret = n_xmalloc(sizeof *ret); *ret = nullinc; ret->path = (char *)path; return ret; } static struct input_file * tmp_in_file(FILE *fd, char *name) { static struct input_file in; in.fd = fd; in.path = name; in.is_cmdline = 1; return ∈ } static struct predef_macro * tmp_pre_macro(const char *name, const char *text) { static struct predef_macro pm; struct predef_macro *ret = n_xmalloc(sizeof *ret); *ret = pm; ret->name = n_xstrdup(name); ret->text = n_xstrdup(text); return ret; } static void usage(void) { puts("You invoked nwcpp incorrectly. Please refer to the README file"); puts("for supported command line arguments"); exit(EXIT_FAILURE); } /* * XXX The macros passed to the program are totally incomplete. In particular * there's usually only one operating system/architecture macro, such that * e.g. __amd64__ is defined but __amd64 isn't. This SUCKS!! */ int main(int argc, char **argv) { struct ga_option options[] = { { 'D', NULL, 1 }, { 'I', NULL, 1 }, #ifdef _AIX { 0, "maix64", 0 }, #endif { 0, "arch", 1 }, { 0, "abi", 1 }, { 0, "mabi", 1 }, { 0, "nostdinc", 0 }, }; int n_opts = sizeof options / sizeof options[0]; int n_predef = 0; int ch; int idx; int rc = 0; int have_gnu = 0; char buf[1024]; char *arch_str = NULL; char *abi_str = NULL; struct stat sbuf; struct include_dir *tmp_inc; struct predef_macro *predef = NULL; struct predef_macro *predef_tail = NULL; struct predef_macro *tmp_pre; struct input_file *files = NULL; struct input_file *files_tail = NULL; struct input_file *tmp_fil; static struct predef_macro nullpre; static struct input_file nullinput; char *p; FILE *predef_file = NULL; while ((ch = nw_get_arg(argc, argv, options, n_opts, &idx)) != -1) { switch (ch) { case 'D': tmp_pre = n_xmalloc(sizeof *tmp_pre); *tmp_pre = nullpre; tmp_pre->name = n_xstrdup(n_optarg); if (strcmp(n_optarg, "__GNUC__") == 0) { have_gnu = 1; } if ((p = strchr(tmp_pre->name, '=')) != NULL) { *p = 0; tmp_pre->text = ++p; } APPEND_LIST(predef, predef_tail, tmp_pre); break; case 'I': tmp_inc = make_inc_dir(n_xstrdup(n_optarg)); APPEND_LIST(include_dirs, includes_tail, tmp_inc); break; case '!': tmp_fil = n_xmalloc(sizeof *tmp_fil); *tmp_fil = nullinput; tmp_fil->path = n_xstrdup(n_optarg); if ((tmp_fil->fd = fopen(n_optarg, "r")) == NULL) { perror(n_optarg); return EXIT_FAILURE; } APPEND_LIST(files, files_tail, tmp_fil); break; case '?': if (idx) { if (strcmp(options[idx].name, "arch") == 0) { arch_str = n_xstrdup(n_optarg); } else if (strcmp(options[idx].name, "abi") == 0 || strcmp(options[idx].name, "mabi") == 0) { abi_str = n_xstrdup(n_optarg); } else if (strcmp(options[idx].name, "nostdinc") == 0) { nostdincflag = 1; } else if (strcmp(options[idx].name, "maix64") == 0) { abi_str = "aix64"; } else { usage(); } } else { usage(); } break; default: usage(); } } tmp_pre = tmp_pre_macro("__STDC__", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); if (!have_gnu) { /* * __GNUC__=2 or higher requires us to define things like * limits.h constants ourselves on Linux, which is * undesirable at this point. Unfortunately the defective * FreeBSD garbage headers cannot deal with older versions, * so we set the version to 2.95 if we're not on Linux */ #ifdef linux /* XXX hmm set __GNUC_MINOR__ to what!? */ tmp_pre = tmp_pre_macro("__GNUC__", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); #else tmp_pre = tmp_pre_macro("__GNUC__", "2"); APPEND_LIST(predef, predef_tail, tmp_pre); tmp_pre = tmp_pre_macro("__GNUC_MINOR__", "95"); APPEND_LIST(predef, predef_tail, tmp_pre); #endif } set_target_arch_and_abi(&archflag, &abiflag, arch_str, abi_str); if (arch_str != NULL) { /* Are we generating for the host? */ int hostarch; int hostabi; get_host_arch(&hostarch, &hostabi); if (archflag == hostarch) { arch_str = NULL; } } /* * If no target architecture has been specified, we are generating * for the host, and also assume the same target operating system: * We then propagate some system-specific macros. Note that if you * use -arch, but specify as target the same architecture as the * host, these macros will not be propagated. This is obscure and * probably bad. XXX figure out a better way to handle this! */ if (arch_str == NULL) { /* Propagate some system macros */ #ifdef __FreeBSD__ sprintf(buf, "%d", __FreeBSD__); tmp_pre = tmp_pre_macro("__FreeBSD__", buf); APPEND_LIST(predef, predef_tail, tmp_pre); #elif defined __OpenBSD__ sprintf(buf, "%d", __OpenBSD__); tmp_pre = tmp_pre_macro("__OpenBSD__", buf); APPEND_LIST(predef, predef_tail, tmp_pre); #elif defined linux sprintf(buf, "%d", linux); tmp_pre = tmp_pre_macro("linux", buf); APPEND_LIST(predef, predef_tail, tmp_pre); #elif defined __sgi tmp_pre = tmp_pre_macro("__sgi", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); /* * IRIX seems to need extremely freaking much information * by the preprocessor. I started out by doing * * echo 'main() {}' | gcc -E -dM - >foo.c * cat icode.c >>foo.c * * ... and then removed lots and lots of macros defined by * GNU cpp. The result is the list of macros below. Some of * the stuff I removed seems to be rather important - e.g. * SVR4 and MIPS version definitions and such - but there's * no way I'm going to define dozens and dozens of macros * to make stuff work in a voodoo black magic kind of way. * * In other words, this is a preliminary list that I will * change as I see fit * * (Stuff like _MIPS_SZPTR/INT/LONG, _ABI*... should * probably be passed by the compiler once we have support * for more ABIs.) */ { int i; static const struct { char *name; char *text; } sgi_voodoo[] = { { "_LANGUAGE_C", "1" }, { "__EXTENSIONS__", "1" }, { "_COMPILER_VERSION", "601" }, { "__LANGUAGE_C", "1" }, { "__mips64", "1" }, { "__LANGUAGE_C__", "1" }, { "_MIPS_SZPTR", "32" }, { "__STDC_HOSTED__", "1" }, { "_MIPS_SZINT", "32" }, { "_LONGLONG", "1" }, { "_MIPS_SIM", "_ABIN32" }, { "_SGI_SOURCE", "1" }, { "_ABIN32", "2" }, { "LANGUAGE_C", "1" }, { "_LANGUAGE_C", "1" }, { "__MIPSEB__", "1" }, { "_MIPSEB", "1" }, { "__mips", "3" }, #if 0 { "_MIPS_SZLONG", "32" }, #endif { "__R4000__", "1" }, { NULL, NULL } }; for (i = 0; sgi_voodoo[i].name != NULL; ++i) { tmp_pre = tmp_pre_macro(sgi_voodoo[i].name, sgi_voodoo[i].text); APPEND_LIST(predef, predef_tail, tmp_pre); } tmp_pre = tmp_pre_macro("_MIPS_SZLONG", abiflag == ABI_MIPS_N32? "32": "64"); APPEND_LIST(predef, predef_tail, tmp_pre); } #elif defined _AIX tmp_pre = tmp_pre_macro("_AIX", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); /* Announce ``long long'' support to headers */ tmp_pre = tmp_pre_macro("_LONG_LONG", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); /* * Apparently leaving __STDC_VERSION__ unset, or * defining _LONG_LONG, or somesuch - causes the * AIX headers to assume C99 support, which does * not quite work with nwcc yet */ tmp_pre = tmp_pre_macro("__STDC_VERSION__", "199409L"); APPEND_LIST(predef, predef_tail, tmp_pre); #endif #ifdef __i386__ tmp_pre = tmp_pre_macro("__i386__", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); #elif defined __amd64__ tmp_pre = tmp_pre_macro("__amd64__", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); #endif } else { /* * Host and target differ - only define architecture- * specific macros (no OS stuff!) */ char *arch_name = NULL; switch (archflag) { case ARCH_X86: arch_name = "__i386__"; break; case ARCH_AMD64: arch_name = "__amd64__"; break; case ARCH_POWER: arch_name = "_AIX"; /* XXX :-( */ break; case ARCH_MIPS: arch_name = "__sgi"; /* XXX :-/ */ break; default: unimpl(); } tmp_pre = tmp_pre_macro(arch_name, "1"); APPEND_LIST(predef, predef_tail, tmp_pre); } if (abiflag == ABI_POWER64) { tmp_pre = tmp_pre_macro("__64BIT__", "1"); APPEND_LIST(predef, predef_tail, tmp_pre); } if (stat("/usr/local/nwcc/include", &sbuf) == 0) { p = "/usr/local/nwcc/include"; } else { static char cwdbuf[2048]; if (getcwd(cwdbuf, sizeof cwdbuf - sizeof "/include") == NULL) { perror("getcwd"); return EXIT_FAILURE; } strcat(cwdbuf, "/include"); if (stat(cwdbuf, &sbuf) == 0) { p = cwdbuf; } else if ((p = getenv("NWCC_CPP")) != NULL) { char *p2; if ((p2 = strrchr(p, '/')) != NULL) { *cwdbuf = 0; #define MIN__VALUE(x, y) \ ((x) > (y)? (y): (x)) strncat(cwdbuf, p, MIN__VALUE(p2 - p, sizeof cwdbuf - sizeof "/include")); strcat(cwdbuf, "/include"); p = cwdbuf; } else { p = NULL; } } } if (p == NULL) { (void) fprintf(stderr, "Warning: Cannot determine private " "include file directory"); } else { tmp_inc = make_inc_dir(n_xstrdup(p)); APPEND_LIST(include_dirs, includes_tail, tmp_inc); } if (!nostdincflag) { /* Set default include directory (comes last, as it should!) */ tmp_inc = make_inc_dir(n_xstrdup("/usr/include")); APPEND_LIST(include_dirs, includes_tail, tmp_inc); } if (predef != NULL) { /* * It is possible - though probably rare - to have complex * definitions on the command line, such as * -Dfoo='do { puts("lol"); } while (0)' * ... so the predefined macros are supplied in a file that * can be processed just like normal macro defintions */ if ((predef_file = get_tmp_file("", buf, "def")) == NULL) { return EXIT_FAILURE; } while (predef != NULL) { struct predef_macro *pm; if (fprintf(predef_file, predef->text? "#define %s %s\n" : "#define %s\n", predef->name, predef->text) < 0) { perror("fprintf"); return EXIT_FAILURE; } pm = predef; predef = predef->next; free(pm->name); free(pm); } (void) fclose(predef_file); if ((predef_file = fopen(buf, "r")) == NULL) { perror(buf); return EXIT_FAILURE; } (void) unlink(buf); } init_oplookup(); cross_initialize_type_map(archflag, abiflag); if (files == NULL) { static struct input_file in; int ch; in.path = ""; in.fd = get_tmp_file("stdin", buf, "c"); if (in.fd == NULL) { return EXIT_FAILURE; } while ((ch = getchar()) != EOF) { if (fputc(ch, in.fd) < 0) { perror("fputc"); (void) unlink(buf); return EXIT_FAILURE; } } if (fclose(in.fd) == EOF) { perror("fclose"); (void) unlink(buf); return EXIT_FAILURE; } if ((in.fd = fopen(buf, "r")) == NULL) { perror(buf); return EXIT_FAILURE; } (void) unlink(buf); if (predef_file) { rc |= preprocess( tmp_in_file(predef_file, "predef"), stdout); } rc |= preprocess(&in, stdout); return errors != 0; /* XXX - rc ? */ } else { for (; files != NULL; files = files->next) { if (predef_file) { (void) rewind(predef_file); rc |= preprocess( tmp_in_file(predef_file, "predef"), stdout); } rc |= preprocess(files, stdout); } } return errors != 0? EXIT_FAILURE: 0; }