#include "n_libc.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#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 = "<stdin>";
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;
}
syntax highlighted by Code2HTML, v. 0.9.1