/* * 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 #include #include #include #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"; } }