/*
 * 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.
 *
 * Driver for cpp, nasm, ncc and ld
 */
#include "driver.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <assert.h>
#include "exectools.h"
#include "backend.h"
#include "misc.h"
#include "debug.h"
#include "cc_main.h"
#include "defs.h"
#include "n_libc.h"

/*
 * Store file path referenced by ``name'' in dynamically allocated variable
 * *output_names, resize it if necessary (*output_index contains the
 * number of files in use, it is incremented at each call. The variable keeping
 * track of allocated members in *output_names is static.)
 */
static int
store_file(char ***output_names, int **output_del, int needdel,
char *name, int *output_index) {
	
	static int	alloc = 0;
	static int	chunk_size =  8;
	int		tmpi = *output_index;

	if (tmpi >= alloc) {
		alloc += chunk_size;

		/* Quadratically increase number of allocated slots */
		chunk_size *= chunk_size;

		*output_names =
			n_xrealloc(*output_names, alloc * sizeof(char *));
		*output_del =
			n_xrealloc(*output_del, alloc * sizeof **output_del);
			
	}
	(*output_names)[tmpi] = n_xstrdup(name);
	(*output_del)[tmpi] = needdel;
	++(*output_index);
	return 0;
}

extern int Sflag; /* XXX */

int
driver(char **cpp_flags, char *asm_flags, char *ld_flags, char **files) {
	int	i;
	int	rc;
	int	out_index = 0;
	int	ld_cmd_len;
	int	has_errors = 0;
	char	*ld_cmd;
	char	**output_names = NULL;
	char	buf[256];
	int	*output_del = NULL;
	pid_t	pid;
	FILE	*fd;
	char	ld_std_flags[512];

	if (archflag == ARCH_X86) {
#if defined __FreeBSD__
		strcpy(ld_std_flags,
			"-dynamic-linker /usr/libexec/ld-elf.so.1 "
			"/usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o " 
			"-L/usr/lib -lc /usr/lib/crtend.o /usr/lib/crtn.o ");
#elif defined __DragonFly__
		/*
		 * Uses same flags as FreeBSD except for ld-elf.so.2 instead
		 * of ld-elf.so.1
		 */
		strcpy(ld_std_flags,
			"-dynamic-linker /usr/libexec/ld-elf.so.2 "
			"/usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o " 
			"-L/usr/lib -lc /usr/lib/crtend.o /usr/lib/crtn.o ");
#elif defined __OpenBSD__		
		strcpy(ld_std_flags,
			"-e __start -Bdynamic -dynamic-linker "
			"/usr/libexec/ld.so /usr/lib/crt0.o /usr/lib/crtbegin.o"
			" -lc /usr/lib/crtend.o ");
#elif defined linux
		strcpy(ld_std_flags,
			"-lc /usr/lib/crt1.o /usr/lib/crti.o "
			"/usr/lib/crtn.o /usr/lib/libc.so "
			"--dynamic-linker /lib/ld-linux.so.2 ");
#elif defined __sun
		strcpy(ld_std_flags,
			"-L/usr/ccs/lib -L/usr/lib -Qy /usr/lib/crt1.o "
			"/usr/lib/crti.o -lc /usr/lib/crtn.o ");
#else
#endif
	} else if (archflag == ARCH_AMD64) {
#if defined linux
		/*
		 * 08/08/07: Explicitly use lib64 instead of lib
		 * dirs, since some distributions seem to put 32bit
		 * files into plain lib dirs
		 */
		strcpy(ld_std_flags, "-lc /usr/lib64/crt1.o /usr/lib64/crti.o "
			"/usr/lib64/crtn.o /usr/lib64/libc.so "
			"--dynamic-linker /lib64/ld-linux-x86-64.so.2 ");
#elif defined __FreeBSD__
		strcpy(ld_std_flags, "/usr/lib/crt1.o /usr/lib/crti.o "
		"/usr/lib/crtbegin.o -L/usr/lib -lc /usr/lib/crtend.o "
		"/usr/lib/crtn.o -dynamic-linker /libexec/ld-elf.so.1 ");
#elif defined __OpenBSD__
		strcpy(ld_std_flags, "/usr/lib/crt0.o -Bdynamic "
			"-dynamic-linker /usr/libexec/ld.so "
			"/usr/lib/crtbegin.o -lc /usr/lib/crtend.o "
			"-L/usr/lib ");
#else
		/* XXX */
		(void) fprintf(stderr, "This operating system isn't supported"
			      " on AMD64\n");
		
#endif
	} else if (archflag == ARCH_MIPS) {
		if (abiflag == ABI_MIPS_N64) {
			strcpy(ld_std_flags,
			"-64 /usr/lib64/mips3/crt1.o -L/usr/lib64/mips3 "
			"-L/usr/lib64 -lc /usr/lib64/mips3/crtn.o ");
		} else {
			strcpy(ld_std_flags,
				"/usr/lib32/crt1.o /usr/lib32/crtn.o "
				"/usr/lib32/bpcrt.o -lc ");
		}
	} else if (archflag == ARCH_POWER) {
		if (abiflag == ABI_POWER64) {
			strcpy(ld_std_flags,
				"-bpT:0x10000000 -bpD:0x20000000 -btextro -b64 "
				"/lib/crt0_64.o -L/usr/lib -lc ");
		} else {
			strcpy(ld_std_flags,
				"-bpT:0x10000000 -bpD:0x20000000 -btextro /lib/crt0.o -L/usr/lib -lc ");
		}
	} else if (archflag == ARCH_SPARC) {
#ifdef linux
		strcpy(ld_std_flags, "-m elf64_sparc -Y P,/usr/lib64 "
			"-dynamic-linker /lib64/ld-linux.so.2 "
			"-L/lib64 -L/usr/lib64 /usr/lib64/crt1.o "
			"/usr/lib64/crti.o /usr/lib64/crtn.o -lc ");
#else /* Solaris */
		/*
		 * Because Solaris/SPARC does not ship with crt1.o, we
		 * use our own
		 */
		struct stat	sbuf;
		char		*crt1 = NULL;

		if (abiflag == ABI_SPARC64) {
			if (stat(INSTALLDIR "/nwcc/lib/crt1-64.o", &sbuf)==0) {
				crt1 = INSTALLDIR "/nwcc/lib/crt1-64.o";
			} else if (stat("./crt1-64.o", &sbuf) == 0) {
				crt1 = "./crt1-64.o";
			}
		} else {
			/* 32bit */
			if (stat(INSTALLDIR "/nwcc/lib/crt1-32.o", &sbuf)==0) {
				crt1 = INSTALLDIR "/nwcc/lib/crt1-324.o";
			} else if (stat("./crt1-32.o", &sbuf) == 0) {
				crt1 = "./crt1-32.o";
			}
		}
		if (crt1 == NULL) {
			(void) fprintf(stderr, "Warning: No crt1 object found "
				"in " INSTALLDIR "/nwcc/lib or .!\n");
			crt1 = "";
		}

		if (abiflag == ABI_SPARC64) {
			/*
			 * /usr/local/lib/gcc/sparc-sun-solaris2.10/3.4.6/crt1.o
			                                                /sparcv9/crt1.o*/
			sprintf(ld_std_flags,
				"/usr/lib/sparcv9/crti.o /usr/lib/sparcv9/crtn.o %s "
				"-Y \"P,/usr/ccs/lib/sparcv9:/lib/sparcv9"
				":/usr/lib/sparcv9\" -lc ",
				crt1);
		} else {
			sprintf(ld_std_flags,
				"/usr/lib/crti.o /usr/lib/crtn.o %s "
				"-Y \"P,/usr/ccs/lib:/lib:/usr/lib\" -lc ",
				crt1);
		}
#endif
	} else {
		unimpl();
	}	

	for (i = 0; files[i] != NULL; ++i) {
		char	*p = strrchr(files[i], '.');
		char	*p2;
		int	needdel = 1;

		/* Ignore files without appropriate extension */
		if (p == NULL) {
			continue;
		}
		++p;

		/*
		 * Determine file type to decide which tools (cpp, nasm, ld)
		 * have to be invoked.
		 */
#if 0
		if (assembler == ASM_GAS
			|| archflag == ARCH_MIPS
			|| archflag == ) {
#endif
			{

			
			/*
			 * gas doesn't do foo.c -> foo.o by default. It calls
			 * the output a.out. *barf*
			 * XXX This is kludged
			 */
			static char	*out_ptr;
			char		*filep;

			if ((filep = strrchr(files[i], '/')) != NULL) {
				++filep;
			} else {
				filep = files[i];
			}	

			if (!cflag || !oflag) {
				if (out_ptr == NULL) {
					out_ptr = strchr(asm_flags, 0);
				}	
				sprintf(out_ptr, " -o %.*s.o",
					(int)(p - filep - 1), filep);
			}
			}
#if 0
		}
#endif

		if (strcmp(p, "c") == 0 || strcmp(p, "i") == 0) {
			p2 = files[i];

#ifdef DEBUG
			printf("Preprocessed successfully as %s\n", p2);
#endif

			/* Compile file ``p2''. */
			if ((pid = fork()) == -1) {
				perror("fork");
				exit(EXIT_FAILURE);
			} else if (pid == 0) {
				char	*nwcc1_args[256]; /* XXX */
				char	*arch = NULL;
				int	j = 0;
				int	k = 0;

				nwcc1_args[j++] = "nwcc1";
				if (stackprotectflag) {
					nwcc1_args[j++] = "-stackprotect";
				}
				if (gnuc_version) {
					static char	gnubuf[128];
					sprintf(gnubuf, "-gnuc=%s",
						gnuc_version);	
					nwcc1_args[j++] = gnubuf;
				}
				if (ansiflag) {
					nwcc1_args[j++] = "-ansi";
				}	
				if (pedanticflag) {
					nwcc1_args[j++] = "-pedantic";
				}	
				if (nostdinc_flag) {
					nwcc1_args[j++] = "-nostdinc";
				}	
				if (gflag) {
					nwcc1_args[j++] = "-g";
				}
				if (Eflag) {
					nwcc1_args[j++] = "-E";
				}
				switch (archflag) {
				case ARCH_X86:	
					arch = "-arch=x86";
					break;
				case ARCH_AMD64:
					arch = "-arch=amd64";
					break;
				case ARCH_POWER:
					arch = "-arch=ppc";
					break;
				case ARCH_MIPS:
					arch = "-arch=mips";
					break;
				case ARCH_SPARC:
					arch = "-arch=sparc";
					break;
				case ARCH_PA:
				case ARCH_ARM:
				case ARCH_SH:
					unimpl();
				}
				nwcc1_args[j++] = arch;
					
				if (abiflag != abiflag_default) {

#if 0
					switch (abiflag) {
					case ABI_POWER32:
						abi = "-mabi=aix32";
						break;
					case ABI_POWER64:
						abi = "-mabi=aix64";
						break;
					case ABI_MIPS_N32:
						abi = "-mabi=n32";
						break;
					case ABI_MIPS_N64:
						abi = "-mabi=n64";
						break;
					case 0:
						break;
					default:
						unimpl();
					}
					if (abi != NULL) {
#endif
					if (abiflag != 0) {
						nwcc1_args[j++] = /*abi*/
							abi_to_option(abiflag);
					}	
				}

				if (asmflag) {
					nwcc1_args[j] =
						n_xmalloc(strlen(asmflag)+16);
					sprintf(nwcc1_args[j++],
						"-asm=%s", asmflag);	
				}
				nwcc1_args[j++] = p2;
				for (; cpp_flags[k] != NULL; ++j, ++k) {
					nwcc1_args[j] = cpp_flags[k];
				}
				nwcc1_args[j] = NULL;
#define DEVEL
#ifdef DEVEL
				execv("./nwcc1", nwcc1_args);
#endif 

				if ((p = getenv("NWCC_CC1")) != NULL) {
					execv(p, nwcc1_args);
					perror(p);
				} else {	
#if 0
					execv("/usr/local/bin/nwcc1",
						nwcc1_args);
					perror("/usr/local/bin/nwcc1");
#endif
					execv(INSTALLDIR "/bin/nwcc1",
						nwcc1_args);
					perror(INSTALLDIR "/bin/nwcc1");
				}
				exit(EXIT_FAILURE);
			} else {
				if (waitpid(pid, &rc, 0) == -1) {
					perror("waitpid");
					exit(EXIT_FAILURE);
				}
				if (rc != 0) {
					/* Try other files anyway */
					has_errors = 1;
					continue;
				}
			}

			/*
			 * nwcc just outputs with the same name as the input,
			 * except that the ending is .asm instead of .cpp
			 */
#ifdef DEBUG
			printf("Compiled successfully as %s\n", p2);
#endif

			if (Sflag || Eflag) {
				continue;
			}

			p = p2;
			p2 = n_xmalloc(strlen(p2) + sizeof ".asm");
			strcpy(p2, p);
			p = strrchr(p2, '.');
			strcpy(++p, "asm");

			/* Hopefully p2 is a valid .asm file now... */
			if ((p = strrchr(p2, '/')) != NULL) {
				char	*saved = p+1;
				p2 = do_asm(p+1, asm_flags, abiflag);
				remove(saved);
			} else {
				char	*saved_p2 = p2;
				p2 = do_asm(p2, asm_flags, abiflag);
				remove(saved_p2);
			}	
			if (p2 == NULL) {
				/* Ignore failure, try other files anyway. */
				continue;
			}

#ifdef DEBUG
			printf("Assembled successfully as %s\n", p2);
#endif
		} else if (strcmp(p, "asm") == 0) {
			p2 = do_asm(files[i], asm_flags, abiflag);
			if (p2 == NULL) {
				/* Ignore failure, try other files anyway. */
				continue;
			}
		} else if (strcmp(p, "o") == 0
			|| strcmp(p, "a") == 0
			|| strcmp(p, "so") == 0) {
			/*
			 * Don't do anything, the name is later just
			 * stored for ld because it is an object,
			 * library archive or shared library file
			 */
			p2 = files[i];
			needdel = 0;
		} else {
			continue;
		}

		/*
		 * File ought to be compiled and assembled fine. Store path for
		 * later ld invocation
		 */
		if (store_file(&output_names, &output_del,
				needdel, p2, &out_index) != 0) {
			fprintf(stderr, "Exiting.\n");
			return 1;
		}
	}

	if (i == 0) {
		fprintf(stderr, "Missing input files.\n");
		return 1;
	}

	if (cflag || Sflag || Eflag) return has_errors;

	if (output_names == NULL) {
		fprintf(stderr, "No valid files to link.\n");
		return 1;
	}

	/* Count length of all files plus white spaces and ld flags */
	ld_cmd_len = 0;
	for (i = 0; i < out_index; ++i) {
		ld_cmd_len += strlen(output_names[i]);
		++ld_cmd_len; /* white space */
	}
	ld_cmd_len += strlen(ld_flags);
	ld_cmd = malloc(sizeof "ld " + ld_cmd_len + 1 + sizeof ld_std_flags);
	if (ld_cmd == NULL) {
		perror("malloc");
		return 1;
	}

	if (archflag == ARCH_SPARC) {
#ifdef linux
		strcpy(ld_cmd, "ld ");
#else
		/* Solaris */
		if (abiflag == ABI_SPARC64) {
			strcpy(ld_cmd, "/usr/ccs/bin/sparcv9/ld ");
		} else {			
			strcpy(ld_cmd, "/usr/ccs/bin/ld ");
		}
#endif
	} else {	
		strcpy(ld_cmd, "ld ");
	}	
	for (i = 0; i < out_index; ++i) {
		strcat(ld_cmd, output_names[i]);
		strcat(ld_cmd, " ");
	}
	strcat(ld_cmd, ld_std_flags);
	strcat(ld_cmd, ld_flags);

#if 0
	printf("Executing %s\n", ld_cmd);
#endif
	fd = popen(ld_cmd, "r");
	if (fd == NULL) {
		perror("popen");
		return 1;
	}
	while (fgets(buf, sizeof buf, fd)) {
		printf("%s", buf);
	}

	for (i = 0; i < out_index; ++i) {
		if (output_del[i]) {
			remove(output_names[i]);
		}	
	}
	rc = pclose(fd);
	if (WIFEXITED(rc)) {
		rc = WEXITSTATUS(rc);
		if (rc != 0) {
			has_errors = 1;
		} else {
			if (sflag) {
				/*
				 * Strip. Note that this is only done here;
				 * i.e. when building an executable file.
				 * When doing ``nwcc -c foo.c -s'', the
				 * flag is silently ignored, just like in
				 * gcc
				 */
				if ((fd = exec_cmd("strip", " %s", out_file)) != NULL) {
					rc = pclose(fd);
					if (!WIFEXITED(rc)
						|| WEXITSTATUS(rc) != 0) {
						has_errors = 1;
					}
				} else {
					has_errors = 1;
				}
			}
		}
	} else {
		has_errors = 1;
	}
	return has_errors /*|| ((unsigned)pclose(fd) >> 8)*/ ; /* XXX !!! */
}



syntax highlighted by Code2HTML, v. 0.9.1