/*
* Copyright (c) 2003 - 2007, Nils R. Weller
* All rights reserved.
*
* 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 COPYRIGHT HOLDERS AND 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 COPYRIGHT OWNER OR 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.
*
* Functions to execute cpp/NASM/ncc
*/
#include "cc1_main.h"
#include "exectools.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <sys/stat.h>
#include "defs.h"
#include "analyze.h"
#include "error.h"
#include "lex.h"
#include "misc.h"
#include "functions.h"
#include "scope.h"
#include "backend.h"
#include "typemap.h"
#include "debug.h"
#include "config.h"
#include "n_libc.h"
int stackprotectflag;
int ansiflag;
int standard;
int pedanticflag;
int gnuheadersflag = 1;
int gflag;
int Eflag;
int assembler; /* XXX unused by nwcc1, necessary for exectools linking */
int abiflag;
int archflag;
char *input_file;
char *asmflag;
char *gnuc_version;
#define REM_EXIT(n1, n2) do { \
if (is_tmpfile) (void) remove(n1); \
(void) remove(n2); \
return EXIT_FAILURE; \
} while (0)
static char *
do_cpp(char *file, char **args, int cppind) {
/* XXX FILENAME_MAX is broken on HP-UX */
static char output_path[FILENAME_MAX + 1];
char buf[FILENAME_MAX + 1];
char tmpbuf[128] = "/var/tmp/cpp";
char *arch;
char *progname = "gcc -E";
char *p;
char *gnooh = NULL;
int using_nwcpp = 0;
int i;
FILE *fd;
FILE *fd2;
/*
* The GNU cpp on OpenBSD looks pretty broken. This raised the
* point that gcc's builtin cpp is more likely to be correct,
* and that we really want GNU cpp and not a random other cpp.
* So now we prefer ``gcc -E'', and if that fails, ``cpp'',
* and if that fails, nwcpp. Perhaps nwcpp should come before
* cpp.
*/
if (find_cmd("gcc", NULL, 0) != 0) {
if (find_cmd("cpp", NULL, 0) == 0) {
progname = "cpp";
} else {
progname = INSTALLDIR "/nwcc/bin/nwcpp";
using_nwcpp = 1;
}
}
if ((p = getenv("NWCC_CPP")) != NULL) {
progname = p;
if ((p = strrchr(p, '/')) != NULL) {
++p;
} else {
p = progname;
}
if (strcmp(p, "nwcpp") == 0) {
using_nwcpp = 1;
}
}
if (using_nwcpp) {
/*
* If we're cross-compiling, we have to pass on the
* architecture and ABI to nwcpp
*/
if (archflag != 0) {
args[cppind++] = arch_to_option(archflag);
}
if (abiflag != 0) {
args[cppind++] = abi_to_option(abiflag);
}
args[cppind] = NULL;
} else {
int host_arch;
int host_abi;
get_host_arch(&host_arch, &host_abi);
if (host_arch == ARCH_MIPS) {
if (abiflag == ABI_MIPS_N64) {
args[cppind++] = "-mabi=64";
args[cppind] = NULL;
}
} else if (host_arch == ARCH_POWER) {
if (abiflag == ABI_POWER64) {
args[cppind++] = "-maix64";
args[cppind] = NULL;
}
}
}
#if 0
tunit_name = n_xstrdup(file);
if (stat(tunit_name, &sbuf) != -1) {
tunit_size = sbuf.st_size;
} else {
/* !?!? */
tunit_size = rand();
}
#endif
tunit_name = n_xmalloc(strlen(file) + 1);
for (p = file, i = 0; *p != 0; ++p) {
if (isalnum((unsigned char)*p) || *p == '_') {
tunit_name[i++] = *p;
}
}
tunit_name[i] = 0;
if ((p = strrchr(tunit_name, '.')) != NULL) {
*p = 0;
}
if (Eflag) {
fd = stdout;
} else {
fd = get_tmp_file(tmpbuf, output_path, "cpp");
if (fd == NULL) {
return NULL;
}
}
input_file = n_xstrdup(file);
#if 0
#ifdef __i386__
/*arch = "-D__i386__";*/
#elif defined __sgi
/*arch = "-D__sgi";*/
#elif defined __amd64__
/*arch = "-D__amd64__";*/
#elif defined _AIX
/*arch = "-D_AIX";*/
#else
#error "Unsupported host architecture"
#endif
#endif
arch = "";
if (gnuheadersflag && standard != C89) {
#ifdef linux
#ifdef GNUBYDEFAULT
/*
* 07/01/07: Because the major()/minor() macros on my
* old SuSE system do not work with a GNU C version of
* less than 2, now is probably the time we have to
* bump it up. This will probably open a can of worms,
* but has to be done some time
*/
/*
* 07/22/07: That goddamn GNU make tests for version
* 2.5... and if that's not present, it #defines
* __attribute__ to expand to an empty body. That
* breaks stuff like wait() which uses a transparent_
* union attribute. So let's set the rest on fire as
* well by bumping straight to 3
*/
gnooh = " -U__GNUC__ -D__GNUC__=3 "; /* was 1 , then 2 !!! */
#else
gnooh = " -D__GNUC__=3 "; /* was 1, then 2 !!! */
#endif
if (using_nwcpp) gnooh = ""; /* XXX */
fd2 = exec_cmd(progname, " -D__NWCC__=1 %s%s%[] %s",
arch, gnooh, args, file);
#else
fd2 = exec_cmd(progname, " -D__NWCC__=1 %s %[] %s",
arch, args, file);
#endif
#if 0
#endif
} else {
#if defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__
/*
* The FreeBSD headers are worthless without __GNUC__, so
* let's invoke cpp with default settings (enables __GNUC__)
*/
fd2 = exec_cmd(progname, " %s -D__NWCC__=1 %s %s %[] %s",
ansiflag? "-D'__aligned\\(x\\)='": "",
ansiflag? "-U__GNUC__": "",
arch, args, file);
#else
fd2 = exec_cmd(progname, " -D__NWCC__=1 %s -U__GNUC__ %[] %s",
arch, args, file);
#endif
}
if (fd2 == NULL) {
perror("popen");
fclose(fd);
remove(output_path);
return NULL;
}
while (fgets(buf, sizeof buf, fd2)) {
fputs(buf, fd);
}
if (((unsigned)pclose(fd2) >> 8) != 0) {
fprintf(stderr, "*** cpp returned nonzero exit-status.\n");
fclose(fd);
remove(output_path);
return NULL;
}
fclose(fd);
return output_path;
}
static char *garbage;
static void
remove_garbage(void) {
if (errors) {
remove(garbage);
}
}
static int
do_ncc(char *cppfile, char *nccfile, int is_tmpfile) {
static int inits_done;
int fildes;
FILE *input;
FILE *fd;
char *p;
/* Generate in CWD */
if ((p = strrchr(nccfile, '/')) != NULL) {
nccfile = p+1;
}
garbage = nccfile;
atexit(remove_garbage);
if (inits_done == 0) {
init_keylookup();
init_oplookup();
inits_done = 1;
}
toklist = NULL;
funclist = NULL;
input = fopen(cppfile, "r");
if (input == NULL) {
perror(cppfile);
return EXIT_FAILURE;
}
(void) unlink(nccfile); /* trash stale .asm file */
if ((fildes = open(nccfile, O_CREAT | O_EXCL | O_RDWR, S_IRWXU))
== -1) {
perror(nccfile);
if (is_tmpfile) remove(cppfile);
return EXIT_FAILURE;
}
if ((fd = fdopen(fildes, "r+")) == NULL) {
perror(nccfile);
REM_EXIT(cppfile, nccfile);
return EXIT_FAILURE;
}
/*
* It is important to initialize the backend before doing
* lexical analysis because architecture and ABI information
* are needed
*/
if (init_backend(fd, &global_scope) != 0) {
REM_EXIT(cppfile, nccfile);
}
if (lex(input) != 0) {
REM_EXIT(cppfile, nccfile);
}
if (analyze(NULL) != 0) {
REM_EXIT(cppfile, nccfile);
}
if (errors || backend->generate_program() != 0) {
;
}
/* destroy_toklist(&toklist); */
fclose(input);
if (is_tmpfile) remove(cppfile);
(void) fprintf(stderr, "%s - %u error(s), %u warning(s)\n",
cppfile, (unsigned)errors, (unsigned)warnings);
if (errors) {
remove(nccfile);
return EXIT_FAILURE;
}
return 0;
}
static void
segv_handler(int s) {
char *p;
(void) s;
p = "Segmentation fault\n";
write(1, p, strlen(p));
exit(EXIT_FAILURE); /* Dangerous, but necessary for debugging */
}
static void
usage(void) {
/* XXX add useful stuff here */
puts("You invoked nwcc1 incorrectly. Please refer to the README file");
puts("for supported command line arguments");
exit(EXIT_FAILURE);
}
int
main(int argc, char *argv[]) {
int ch;
char *p;
char *nccfile = NULL;
char *tmp;
char *target_str = NULL;
char *abi_str = NULL;
char *cpp_args[128];
int cppind = 0;
int is_tmpfile = 0;
struct ga_option options[] = {
{ 'D', NULL, 1 },
{ 'U', NULL, 1 },
{ 'I', NULL, 1 },
{ 'E', NULL, 0 },
{ 'g', NULL, 0 },
{ 0, "stackprotect", 0 },
{ 0, "nostdinc", 0 },
{ 0, "arch", 1 },
{ 0, "gnuc", 1 },
#ifdef __sun
{ 0, "xarch", 1 },
#endif
{ 0, "mabi", 1 },
{ 0, "abi", 1 },
{ 0, "pedantic", 0 },
{ 0, "asm", 1 },
{ 0, "ansi", 0 }
};
int nopts = N_OPTIONS(options);
int idx;
(void) segv_handler;
#if 0
(void) signal(SIGSEGV, segv_handler);
get_host_arch(&archflag, &abiflag);
#endif
if (argc <= 1) {
usage();
}
while ((ch = nw_get_arg(argc-1, argv+1, options, nopts, &idx)) != -1) {
switch (ch) {
case 'D':
case 'U':
case 'I':
if (cppind == 126) {
(void) fprintf(stderr, "Too many stderr "
"arguments\n");
return EXIT_FAILURE;
}
/* OpenBSD's cpp chokes on -D arg, needs -Darg ... */
cpp_args[cppind] = n_xmalloc(strlen(n_optarg) + 3);
sprintf(cpp_args[cppind++], "-%c%s", ch, n_optarg);
break;
case 'g':
gflag = 1;
/* XXX */gflag = 0;
break;
case 'E':
Eflag = 1;
break;
case '!':
if (nccfile != NULL) {
(void) fprintf(stderr, "Error: More than one "
"input file specified\n");
usage();
}
nccfile = n_xmalloc(strlen(n_optarg) + 16);
strcpy(nccfile, n_optarg);
break;
case '?':
if (idx) {
if (strcmp(options[idx].name, "stackprotect")
== 0) {
stackprotectflag = 1;
} else if (strcmp(options[idx].name, "ansi")
== 0) {
ansiflag = standard = C89;
} else if (strcmp(options[idx].name, "pedantic")
== 0) {
pedanticflag = 1;
} else if (strcmp(options[idx].name, "nostdinc")
== 0) {
cpp_args[cppind++] = "-nostdinc";
} else if (strcmp(options[idx].name, "asm")
== 0) {
asmflag = n_xstrdup(n_optarg);
} else if (strcmp(options[idx].name, "arch")
== 0) {
if (target_str != NULL) {
(void) fprintf(stderr, "-arch "
"used more than once\n");
exit(EXIT_FAILURE);
}
target_str = n_xstrdup(n_optarg);
} else if (strcmp(options[idx].name, "gnuc")
== 0) {
if (gnuc_version != NULL) {
(void) fprintf(stderr, "-gnuc "
"used more than once\n");
exit(EXIT_FAILURE);
}
gnuc_version = n_xstrdup(n_optarg);
} else if (strcmp(options[idx].name, "xarch")
== 0) {
/* SunCC compatibility */
;
} else if (strcmp(options[idx].name, "mabi")
== 0
|| strcmp(options[idx].name, "abi")
== 0) {
if (abi_str != NULL) {
(void) fprintf(stderr, "-%s "
"used more than once\n",
options[idx].name);
exit(EXIT_FAILURE);
}
abi_str = n_xstrdup(n_optarg);
#if 0
abiflag = ascii_abi_to_value(
n_optarg, archflag);
#endif
} else {
usage();
}
} else {
usage();
}
break;
default:
return EXIT_FAILURE;
}
}
if (gnuc_version == NULL) {
gnuc_version = "2";
}
set_target_arch_and_abi(&archflag, &abiflag, target_str, abi_str);
if (abiflag == ABI_POWER64) {
#if 0
cpp_args[cppind++] = "-D__64BIT__=1";
cpp_args[cppind++] = "-U__LONG_MAX__";
#endif
/*
* XXX -maix64 doesn't work with GNU cpp on non-AIX
* platforms...
*/
#ifdef _AIX
cpp_args[cppind++] = "-maix64";
#endif
#if 0
if (sizeof(long) != 8) {
(void) fprintf(stderr, "ERROR: This compiler was not "
"built with 64bit support\n");
exit(EXIT_FAILURE);
}
#endif
} else if (abiflag == ABI_POWER32) {
#if 0
if (sizeof(long) == 8) {
/* Built for 64bit */
abiflag = ABI_POWER64;
}
#endif
} else if (abiflag == ABI_MIPS_N64) {
#ifdef __sgi
cpp_args[cppind++] = "-mabi=n64";
#endif
} else if (abiflag == ABI_SPARC64) {
#if !defined __i386__ && (defined __sun || defined __sparc)
cpp_args[cppind++] = "-m64";
#endif
}
cpp_args[cppind] = NULL;
cross_initialize_type_map(archflag, abiflag);
#if 0
optind = 0;
argv[0] = "new.c";
#endif
if ((p = strrchr(nccfile, '.')) == NULL
|| (strcmp(++p, "c") != 0
&& strcmp(p, "i") != 0)) {
fprintf(stderr, "%s: Invalid input file name\n", nccfile);
return EXIT_FAILURE;
}
if (strcmp(p, "i") == 0) {
/* Already preprocessed */
tmp = n_xstrdup(nccfile);
} else {
is_tmpfile = 1;
if ((tmp = do_cpp(nccfile, cpp_args, cppind)) == NULL) {
return EXIT_FAILURE;
}
}
if (Eflag) {
/* Done! */
return 0;
}
strcpy(p, "asm");
return do_ncc(tmp, nccfile, is_tmpfile);
}
syntax highlighted by Code2HTML, v. 0.9.1