/*
* 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