/*
 * Copyright (c) 2003 - 2006, 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.
 *
 * Various utility functions
 */
#include "misc.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "token.h"
/*#include "debug.h"*/
#include "n_libc.h"
#include "archdefs.h"

static int 
do_string(char *buf, char *str, size_t size, size_t *len, int dospace) {
	size_t	slen = strlen(str) * 2; /* XXX */
	char	*p;
	char	*bufp = buf + strlen(buf);

	if (slen > (size - *len - (dospace != 0))) {
		(void) fprintf(stderr, "Arguments too long\n");
		return -1;
	}	
#if 0
	strcat(buf, str);
	*len += slen;
#endif
	/*
	 * XXX workaround because of popen() trashing " chars. This is
	 * broken too because it will also trash some command lines,
	 * but those are less likely to occur. Fix exec_cmd()!!!!
	 */
	for (p = str; *p != 0; ++p) {
		if (*p == '"') {
			*len += 2;
			*bufp++ = '\\';
			*bufp++ = '"';
		} else {
			*len += 1;
			*bufp++ = *p;
		}
	}
	/*--*len*/
	*bufp = 0;

	if (dospace) {
		strcat(buf, " ");
		++*len;
	}	
	return 0;
}


/*
 * XXX this SUCKS! popen() should not be used at all because its shell
 * processing will trash command line arguments :-(
 * ./nwcc -DFOO='"bar"' file.c
 * ... the "" quotes are trashed here, thus cpp receives -DFOO=bar
 * Use fork()/pipe()/exec*() manually
 */
FILE *
exec_cmd(char *prog, char *format, ...) {
	va_list		v;
	char		buf[4096] = { 0 };
	char		*p;
	int		err = 0;
	size_t		len = 0;
	
	va_start(v, format);

	strncpy(buf, prog, sizeof buf);
	buf[sizeof(buf) - 1] = 0;
	len = strlen(buf);
	while (*format) {
		switch (*format) {
		case '%':
			switch (*++format) {
			case 's':
				p = va_arg(v, char *);
				if (do_string(buf, p, sizeof buf, &len, 0)
					!= 0) {
					err = 1;
				}	
				break;
			case '[':
				if (*++format == ']') {
					char	**sv;
					int	i;

					sv = va_arg(v, char **);
					for (i = 0; sv[i] != NULL; ++i) {
						if (do_string(buf, sv[i],
						sizeof buf, &len, 1) != 0) {
							err = 1;
							break;
						}	
					}	
					break;
				}
				/* FALLTHRU */
			default:
				(void) fprintf(stderr,
				"Unknown escape sequence - ignoring.\n");
				if (*format == 0) {
					err = 1;
				}
			}
			break;
		default:
			if (len < 4095) {
				buf[len] = *format;
				++len;
				buf[len] = 0;
			} else {
				fprintf(stderr, "Arguments too long.\n");
				err = 1;
				break;
			}
		}
		if (err == 1) {
			return NULL;
		}
		if (*++format == 0) {
			break;
		}
	}
	va_end(v);
	buf[len] = 0;
#if 0
	printf("Executing ``%s''\n", buf);
#endif
	return popen(buf, "r");
}

/*
 * Get default ABI for systems on where there are multiple supported
 * ABIs (PPC/MIPS currently.)
 */
void
get_default_abi(int *abi, int arch) {
	switch (arch) {
	case ARCH_MIPS:	
		*abi = ABI_MIPS_N32;
		break;
	case ARCH_POWER:
#ifdef _AIX
		/* host equals target */
		if (sizeof(long) == 8) {
			*abi = ABI_POWER64;
		} else
#endif
		{	
			*abi = ABI_POWER32;
		}	
		break;
	default:	
		*abi = 0;
	}
}

/*
 * Get host architecture and ABI. These are then used as
 * target arch/ABI if no other target has been explicitly
 * requested
 */
void
get_host_arch(int *arch, int *abi) {
	*abi = 0;
#ifdef __i386__
	*arch = ARCH_X86;
#elif defined __sgi
	*arch = ARCH_MIPS;
#elif defined _AIX
	*arch = ARCH_POWER;
#elif defined __amd64__
	*arch = ARCH_AMD64;
#else
	(void) fprintf(stderr, "Unknown host architecture!\n");
	exit(EXIT_FAILURE);
#endif
	get_default_abi(abi, *arch);
}

void
get_target_arch_by_name(int *arch, const char *name) {
	static struct {
		int		val;
		const char	*name;
	} archs[] = {
		{ ARCH_X86, "x86" },
		{ ARCH_AMD64, "amd64" },
		{ ARCH_POWER, "ppc" },
		{ ARCH_MIPS, "mips" },
		{ 0, NULL }
	};
	int	i;

	for (i = 0; archs[i].name != NULL; ++i) {
		if (strcasecmp(archs[i].name, name) == 0) {
			*arch = archs[i].val;
			return;
		}
	}
	(void) fprintf(stderr, "Unknown target architecture `%s'!\n",
		name);
	(void) fprintf(stderr, "Supported values for -arch are:\n");
	for (i = 0; archs[i].name != NULL; ++i) {
		(void) fprintf(stderr, "\t%s\n", archs[i].name);
	}	
	exit(EXIT_FAILURE);
}

/*
 * This is placed in misc.c because backend.c drags in other files and
 * should really only be linked with nwcc1
 */
int
ascii_abi_to_value(const char *name, int arch) {
	static const struct {
		char	*name;
		int	value[8];
		int	arch[8];
#define ONEAR(x) {x,-1,-1,-1,-1,-1,-1,-1}
	} supported[] = {
		{ "n32", ONEAR(ABI_MIPS_N32), ONEAR(ARCH_MIPS) },
		{ "n64", ONEAR(ABI_MIPS_N64), ONEAR(ARCH_MIPS) },
		{ "64", ONEAR(ABI_MIPS_N64), ONEAR(ARCH_MIPS) },
		{ "aix32", ONEAR(ABI_POWER32), ONEAR(ARCH_POWER) },
		{ "aix64", ONEAR(ABI_POWER64), ONEAR(ARCH_POWER) },
		{ NULL, ONEAR(0), ONEAR(0) } 
	};
	int	i;

	for (i = 0; supported[i].name != NULL; ++i) {
		if (strcmp(supported[i].name, name) == 0) {
			int	j;

			for (j = 0; supported[i].arch[j] != -1; ++j) {
/*if (arch == ARCH_X86) break;*/
				if (supported[i].arch[j] == arch) {
					break;
				}	
			}
			if (supported[i].arch[j] == -1
				/*&& supported[i].value[0] != ABI_POWER64*/) {
				(void) fprintf(stderr,
				"Option %s is not available for this target "
				"architecture\n", name);
				return -1;
			} else {
				return supported[i].value[j];
			}
		}	
	}
	(void) fprintf(stderr, "Unknown ABI option `%s'\n", name);
	abort();
	return -1;
}

/*
 * Set target architecture and ABI. If ``archflag'' and/or ``abiflag''
 * are non-null, those will be used for them. Otherwise, the host
 * architecture is also used as target architecture, and/or the ABI is
 * set to the corresponding default (e.g. n32 on MIPS.)
 */
void
set_target_arch_and_abi(int *archflag, int *abiflag,
	const char *target_str, const char *abi_str) {

	int	abiflag_default = 0;

	if (target_str != NULL) {
		/*
		 * Command line contained an -arch option, i.e. don't use
		 * default (host) architecture (probably cross-compilation
		 * requested.)
		 */
		get_target_arch_by_name(archflag, target_str);
	} else {
		/* Use default architecture/ABI */
		get_host_arch(archflag, &abiflag_default);
	}


	if (abi_str != NULL) {
		/*
		 * Command line contained an -abi/-mabi option, i.e. don't
		 * use default ABI for target architecture
		 */
		*abiflag = ascii_abi_to_value(abi_str,
			*archflag);
		if (*abiflag == -1) {
			exit(EXIT_FAILURE);
		}
	} else {
		/* Use default ABI */
		if (abiflag_default != 0) {
			*abiflag = abiflag_default;
		} else {	
			get_default_abi(abiflag, *archflag); 
		}
	}
}

char *
arch_to_ascii(int val) {
	if (val == -1) {
		/* Get host architecture */
#ifdef __i386__
		val = ARCH_X86;
#elif defined __amd64__
		val = ARCH_AMD64;
#elif defined __sgi
		val = ARCH_MIPS;
#elif defined _AIX
		val = ARCH_POWER;
#endif
	}
	switch (val) {
	case ARCH_X86:	
		return "x86";
	case ARCH_AMD64:
		return "amd64";
	case ARCH_MIPS:
		return "mips";
	case ARCH_POWER:
		return "ppc";
	default:
		return "unknown";
	}	
}



syntax highlighted by Code2HTML, v. 0.9.1