/*
 * 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.
 *
 * nwcc compiler driver
 */
#include "cc_main.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "driver.h"
#include "error.h"
#include "defs.h"
#include "debug.h"
#include "backend.h"
#include "misc.h"
#include "cfgfile.h"
#include "n_libc.h"


int		Sflag;
int		sflag;
int		Eflag;
int		cflag;
int		oflag;
int		gflag;
int		assembler;
int		stackprotectflag;
int		gnuheadersflag = 1; /* XXX */
int		pedanticflag;
int		ansiflag;
char		*asmflag;
char		*gnuc_version;

int		archflag;
int		abiflag;
int		abiflag_default;
int		nostdinc_flag;
char		*out_file = "a.out";


static void
usage(void) {
	/* XXX add useful stuff here */
	puts("You invoked nwcc incorrectly. Please refer to the README file");
	puts("for supported command line arguments");
	exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[]) {
	int			ch;
	int			i;
	int			ldind = 0;
	int			idx;
	int			nolibnwccflag = 0;
	size_t			ldpos = 0;
	size_t			ldalloc = 0;
	size_t			cppind = 0;
	char			asm_flags[1024];
	char			*asm_cmdline = NULL;
	char			*ld_flags = NULL;
	char			*ld_args[128];
	char			*cpp_args[128];
	char			*ld_cmdline = NULL;
	char			*target_str = NULL;
	char			*abi_str = NULL;
	struct stat		sbuf;
	struct cfg_option	*cfg_file_options;
	struct ga_option	options[] = {
		{ 'D', NULL, 1 },
		{ 'U', NULL, 1 },
		{ 'I', NULL, 1 },
		{ 'l', NULL, 1 },
		{ 'L', NULL, 1 },
		{ 'o', NULL, 1 },
		{ 'n', NULL, 1 },
		{ 'S', NULL, 0 },
		{ 'E', NULL, 0 },
		{ 'c', NULL, 0 },
		{ 'W', NULL, 0 }, /* ignore */
		{ 'f', NULL, 0 }, /* ignore */
		{ 'g', NULL, 0 }, /* ignore */
		{ 'm', NULL, 0 }, /* ignore */
		{ 'O', NULL, 0 }, /* ignore */
		{ 's', NULL, 0 }, /* strip */
		{ 0, "verboseoffsets", 0 },
		{ 0, "stackprotect", 0 },
		{ 0, "nolibnwcc", 0 },
		{ 0, "pthread", 0 }, /* ignore */
		{ 0, "print-prog-name", 1 },
		{ 0, "nostdinc", 0 },
		{ 0, "rdynamic", 0 }, /* ignore */
		{ 0, "static", 0 }, /* ignore (this hurts, we need it!) */
		{ 0, "export-dynamic", 0 }, /* ignore */
		{ 0, "arch", 1 },
		{ 0, "gnuc", 1 },
#ifdef __sun
		{ 0, "xarch", 1 },
#endif
		{ 0, "O0", 0 }, /* ignore */
		{ 0, "O1", 0 }, /* ignore */
		{ 0, "O2", 0 }, /* ignore */
		{ 0, "O3", 0 }, /* ignore */
		{ 0, "ggdb", 0 }, /* ignore */
		{ 0, "Wall", 0 }, /* ignore */
		{ 0, "pedantic", 0 }, /* ignore */
		{ 0, "version", 0 },
		{ 0, "pg", 0 }, /* ignore */
		{ 0, "pipe", 0 }, /* ignore */
		{ 0, "ansi", 0 },
		{ 0, "std", 0 },
		{ 0, "mabi", 1 },
		{ 0, "maix64", 0 },
		{ 0, "abi", 1 },
		{ 0, "asm", 1 }
	};
	int		nopts = N_OPTIONS(options);

	if ((cfg_file_options = read_config_files()) != NULL) {
		/*
		 * There are config files that need to be integrated into
		 * the command line
		 */
		char	**tmpargv;
		int	tmpargc = argc;
		
		tmpargv = make_new_argv(argv, &tmpargc, cfg_file_options,
				options, nopts);
		if (tmpargv != NULL) {
			argv = tmpargv;
			argc = tmpargc;
		}	
	}

	while ((ch = nw_get_arg(argc-1, argv+1, options, nopts, &idx)) != -1) {
		switch (ch) {
		case 'n':
			asm_cmdline = n_xstrdup(n_optarg);
			break;
		case 'c':
			cflag = 1;
			break;
		case 's':
			sflag = 1;
			break;
		case 'I':
		case 'D':
		case 'U':	
			if (cppind == 126) {
				(void) fprintf(stderr,
					"Too many cpp flags\n");
				return 1;
			}
			if (ch == 'I') {
				cpp_args[cppind++] = "-I";
			} else if (ch == 'D') {
				cpp_args[cppind++] = "-D";
			} else if (ch == 'U') {
				cpp_args[cppind++] = "-U";
			}	
			cpp_args[cppind++] = n_optarg;
			break;
		case 'E':
			Eflag = 1;
			break;
		case 'l':
		case 'L':	
			if (ldind == 126) {
				(void) fprintf(stderr,
					"Too many linker flags\n");
				return 1;
			}
			if (ch == 'l') {
				ld_args[ldind++] = "-l";
			} else if (ch == 'L') {
				ld_args[ldind++] = "-L";
			}
			ld_args[ldind++] = n_optarg;
			break;
		case 'o':
			out_file = n_xstrdup(n_optarg);
			oflag = 1;
			break;
		case 'S':
			Sflag = 1;
			break;
		case 'O':	
		case 'W': /* Ignore -W/-O */
			break;
		case 'g':	
			gflag = 1;
			break;
		case '!':
			break;
		case '?':
			if (idx) {
				if (strcmp(options[idx].name, "stackprotect")
					== 0) {
					stackprotectflag = 1;
				} else if (strcmp(options[idx].name,
					"nolibnwcc") == 0) {
					nolibnwccflag = 1;
				} else if (strcmp(options[idx].name, "Wall")
					== 0) {
					; /* Ignore */
				} else if (options[idx].name[0] == 'O'
					&& isdigit(options[idx].name[1])) {
					; /* Ignore */
				} else if (strcmp(options[idx].name, "ggdb")
					== 0) {
					; /* Ignore */
				} else if (strcmp(options[idx].name, "ansi")
					== 0) {
					ansiflag = 1;
				} else if (strcmp(options[idx].name, "std")
					== 0) {
					;
				} else if (strcmp(options[idx].name, "pg")
					== 0) {
					; /* Ignore */
				} else if (strcmp(options[idx].name, "pedantic")
					== 0) {
					pedanticflag = 1;
				} else if (strcmp(options[idx].name, "pipe")
					== 0) {
					; /* Ignore */
				} else if (strcmp(options[idx].name, "pthread")
					== 0) {
					ld_args[ldind++] =
						"-lpthread";
				} else if (strcmp(options[idx].name, "rdynamic")
					== 0) {
					(void) fprintf(stderr, "Warning: "
						"-rdynamic is ignored\n");
				} else if (strcmp(options[idx].name, "export-dynamic")
					== 0) {
					(void) fprintf(stderr, "Warning: "
						"-export-dynamic is ignored\n");
				} else if (strcmp(options[idx].name,
					"nostdinc") == 0) {
					nostdinc_flag = 1;
				} else if (strcmp(options[idx].name, "asm")
					== 0) {
					if (asmflag) {
						(void) fprintf(stderr, "-asm "
						"used more than once\n");
						exit(EXIT_FAILURE);
					}	
					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, "static")
					== 0) {
					(void) fprintf(stderr, "Warning: "
						"ignoring -static option\n");
				} else if (strcmp(options[idx].name, "gnuc")
					== 0) {
					if (gnuc_version != NULL) {
						(void) fprintf(stderr, "-gnuc "
						"used more than once\n");
						exit(EXIT_FAILURE);
					} else if (strlen(n_optarg) > 10) {
						(void) fprintf(stderr, "Bogus "
							"argument to -gnuc\n");
						exit(EXIT_FAILURE);
					}	
							
					gnuc_version = n_xstrdup(n_optarg);
				} else if (strcmp(options[idx].name, "xarch")
					== 0) {
					;
				} else if (strcmp(options[idx].name,
						"print-prog-name") == 0) {
					char	buf[2048];

					if (find_cmd(n_optarg, buf, sizeof buf)
						== -1) {
						puts(n_optarg);
					} else {
						puts(buf);
					}	
					exit(0);
				} else if (strcmp(options[idx].name, "maix64")
					== 0) {
					abi_str = n_xstrdup("aix64"); /* XXX */
				} else if (strcmp(options[idx].name, "mabi")
					== 0
					|| strcmp(options[idx].name, "abi")
					== 0) {
					if (abi_str != NULL) {
						(void) fprintf(stderr, "-mabi "
							"used more than once");
						exit(EXIT_FAILURE);
					}
					abi_str = n_xstrdup(n_optarg);
				} else if (strcmp(options[idx].name, "version")
					== 0) {
					printf("nwcc %s\n", NWCC_VERSION);
					printf("Copyright (c) 2003 - 2007 "
						"Nils Robert Weller\n");
					exit(0);
				} else {
					usage();
				}	
			} else {
				usage();
			}
			break;
		default:
			usage();
		}
	}
	cpp_args[cppind] = NULL;

	set_target_arch_and_abi(&archflag, &abiflag, target_str, abi_str);
	if (target_str != NULL) {
		int	hostarch;
		int	hostabi;

		get_host_arch(&hostarch, &hostabi);
		if (archflag != hostarch && !Sflag) {
			(void) fprintf(stderr, "Warning: Cross-compilation "
				"implies -S (only generates *.asm file.)\n");
			Sflag = 1;
		}
	}


#if 0
	if (sizeof(long) == 8 && archflag == ARCH_POWER) {
		abiflag = ABI_POWER64;
	}
#endif

	/*
	 * libnwcc is now linked statically because there isn't
	 * a whole lot of code in it anyway
	 */
	if (!nolibnwccflag) {
		int	is_64bit =
			abiflag == ABI_POWER64
			|| abiflag == ABI_MIPS_N64
			|| abiflag == ABI_SPARC64;

		if (is_64bit) {
			if (stat(INSTALLDIR "/lib/libnwcc64.o", &sbuf) != -1) {
				ld_args[ldind++] = INSTALLDIR "/lib/libnwcc64.o";
			} else if (stat("./extlibnwcc64.o", &sbuf) != -1) {
				ld_args[ldind++] = "./extlibnwcc64.o";
			} else if (stat(INSTALLDIR "/lib/libnwcc.o", &sbuf) != -1) {	
				ld_args[ldind++] = INSTALLDIR "/lib/libnwcc.o";
			} else if (stat(INSTALLDIR "./extlibnwcc.o", &sbuf) != -1) {	
				ld_args[ldind++] = INSTALLDIR "./extlibnwcc.o";
			}
		} else {	
			if (stat(INSTALLDIR "/lib/libnwcc32.o", &sbuf) != -1) {
				ld_args[ldind++] = INSTALLDIR "/lib/libnwcc32.o";
			} else if (stat("./extlibnwcc32.o", &sbuf) != -1) {
				ld_args[ldind++] = "./extlibnwcc32.o";
			} else if (stat(INSTALLDIR "/lib/libnwcc.o", &sbuf) != -1) {
				ld_args[ldind++] = INSTALLDIR "/lib/libnwcc.o";
			} else if (stat("./extlibnwcc.o", &sbuf) != -1) {
				ld_args[ldind++] = "./extlibnwcc.o";
			}
		}	
#if 0
		if (is_64bit) {
			if (stat("/usr/local/lib/libnwcc64.o", &sbuf) != -1) {
				ld_args[ldind++] = "/usr/local/lib/libnwcc64.o";
			} else if (stat("./extlibnwcc64.o", &sbuf) != -1) {
				ld_args[ldind++] = "./extlibnwcc64.o";
			}	
		} else {	
			if (stat("/usr/local/lib/libnwcc.o", &sbuf) != -1) {
				ld_args[ldind++] = "/usr/local/lib/libnwcc.o";
			} else if (stat("./extlibnwcc.o", &sbuf) != -1) {
				ld_args[ldind++] = "./extlibnwcc.o";
			}
		}	
#endif
#ifdef __sgi
		/* XXX otherwise symbols of libnwcc are unresolved */
		ld_args[ldind++] = "-lc";
#endif
	}

	if (asmflag == NULL) {
		asmflag = getenv("NWCC_ASM");
	}	
#if 0
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
asmflag = n_xstrdup("gas");	
#endif

#ifdef __i386__

#  if defined __sun
	asmflag = n_xstrdup("gas");
	assembler = ASM_GAS;
	strcpy(asm_flags, "--traditional-format -Qy ");
#  elif defined __FreeBSD__ || defined __OpenBSD__ || defined linux \
	|| defined __DragonFly__
#if 0
	if (asmflag == NULL) {
		asmflag = n_xstrdup("gas");
	}	
#endif
	if (asmflag == NULL) {
		/*
		 * 07/28/07: Finally gas is the default assembler!
		 * This is meant to speed up assembling significantly
		 */
		assembler = ASM_GAS  /*NASM*/;
		asmflag = "gas";
		*asm_flags = 0;
	} else if ( 
		strcmp(asmflag, "nasm") == 0
		|| strcmp(asmflag, "nwasm") == 0
		|| strcmp(asmflag, "yasm") == 0) {
		if (strcmp(asmflag, "nwasm") == 0) {
			assembler = ASM_NWASM;
		} else {
			assembler = *asmflag == 'n'? ASM_NASM: ASM_YASM;
		}
		strcpy(asm_flags, "-f elf ");
		if (assembler == ASM_NWASM) {
			/*
			 * Disable verbose output
			 */
			strcat(asm_flags, "-q ");
		}	
	} else if (strcmp(asmflag, "gas") == 0
		|| strcmp(asmflag, "as") == 0) {
		assembler = ASM_GAS;
		*asm_flags = 0;
	} else {
		(void) fprintf(stderr, "Unknown assembler `%s'\n", asmflag);
	}
#elif defined __sun

#  else
#     error "Unsupported system (currently only Linux/FreeBSD/OpenBSD on x86 \
are supported)"
#  endif
#elif defined __amd64__ || defined __x86_64__

	if (asmflag == NULL) {
		/*
		 * 07/28/07: Finally gas is the default assembler!
		 * This is meant to speed up assembling significantly
		 */
		asmflag = "gas";
		assembler = ASM_GAS  /*NASM*/;
		*asm_flags = 0;
	} else if ( 
		strcmp(asmflag, "yasm") == 0) {	
		assembler = ASM_YASM; /* XXX */
		strcpy(asm_flags, "-f elf -m amd64");
	} else if (strcmp(asmflag, "gas") == 0
		|| strcmp(asmflag, "as") == 0)	{
		assembler = ASM_GAS;
		*asm_flags = 0;
	} else {
		(void) fprintf(stderr, "Unknown assembler `%s'\n", asmflag);
	}	
#elif defined __sgi
	assembler = ASM_SGI_AS; /* XXX */
	if (abiflag == ABI_MIPS_N64) {
		strcpy(asm_flags, "-pic2 -elf -EB -O0 -g0 -G0 -w -mips3 "
			"-64 -t5_ll_sc_bug ");	
	} else {
		strcpy(asm_flags, "-pic2 -elf -EB -O0 -g0 -G0 -w -mips3 "
			"-n32 -t5_ll_sc_bug ");	
	}	
#elif defined __sun || defined __sparc
	if (abiflag == ABI_SPARC64) {
#ifndef linux
		strcpy(asm_flags, "-xarch=v9 ");
#else
		strcpy(asm_flags, "--64 -Av9a -Qy ");
#endif

	}
#elif defined _AIX
#if 0
	strcpy(asm_flags, "-u -mcom ");
#endif
	if (abiflag == ABI_POWER64) {
		strcpy(asm_flags, "-u -many -a64 -mppc64");
	} else {
		strcpy(asm_flags, "-u -many");
	}
#else
#error "Unsupported host architecture"
#endif	
	if (asm_cmdline != NULL) {
		strncat(asm_flags, asm_cmdline,
			sizeof asm_flags-strlen(asm_flags));
	}

	if (cflag && oflag) {
		/* nwcc -c foo.c -o foo.o */
		/* XXX */
		sprintf(asm_flags+strlen(asm_flags), " -o %s", out_file);
	}

	make_room(&ld_flags, &ldalloc, sizeof "-o" + strlen(out_file));
	ldpos += sprintf(ld_flags, "-o %s", out_file);
	for (i = 0; i < ldind; ++i) {
		make_room(&ld_flags, &ldalloc,
				ldpos + strlen(ld_args[i]) + 2);
		ldpos += sprintf(ld_flags+ldpos, " %s", ld_args[i]);
	}
	
	if (asm_cmdline) {
		free(asm_cmdline);
	}
	if (ld_cmdline) {
		free(ld_cmdline);
	}

	return driver(cpp_args, asm_flags, ld_flags, argv+1);


#if 0
	- finish n64 on mips (self-compilation?)
todo:
	- -ansi/-pedantic (warn about gnu features)
	- for () variarble declarations
	- stupid "c++ class style" scope bug
	- check the "const int" snippet in log, and the tinycc
	  bugs before that
	- check const correctnes.s.. char ** vs const char **  
        - fix initializer array/struct type problems once and for all!!!
	  (see mips initializer output o rwhatever that was)
	- take a peek at gxemul (groan)
	- x86 stack allocation bug (update sp before use)
	- bitfields
	- diagnostic void<->funcptr
	- clean up builtins..mips should use x86? that amd64 sysv style
	  stuff should be reused for e.g. sysv on ppc
	- floating point values in variadic funcs on amd64  
	
#endif
}



syntax highlighted by Code2HTML, v. 0.9.1