//============================================================
//
// vconv.c - VC++ parameter conversion code
//
//============================================================
//
// Copyright Aaron Giles
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or
// without modification, are permitted provided that the
// following conditions are met:
//
// * Redistributions of source code must retain the above
// copyright notice, this list of conditions and the
// following disclaimer.
// * 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.
// * Neither the name 'MAME' nor the names of its
// contributors may be used to endorse or promote
// products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY AARON GILES ''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 AARON GILES BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGE (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.
//
//============================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
//============================================================
// CONSTANTS
//============================================================
#define PRINT_COMMAND_LINE 0
#define VS6 0x00060000
#define VS7 0x00070000
#define VS71 0x0007000a
#define VS2005 0x00080000
#define VS2008 0x00090000
//============================================================
// TYPE DEFINITIONS
//============================================================
typedef struct
{
DWORD vc_version;
const char *gcc_option;
const char *vc_option;
} translation_info;
//============================================================
// TRANSLATION TABLES
//============================================================
static const translation_info gcc_translate[] =
{
{ 0, "-D*", "/D*" },
{ 0, "-U*", "/U*" },
{ 0, "-I*", "/I*" },
{ 0, "-o*", "~*" },
{ 0, "-include*", "/FI*" },
{ 0, "-c", "/c~/Fo" },
{ 0, "-E", "/c~/E >" },
{ 0, "-S", "/c~/Fa" },
{ VS7, "-O0", "/Od /GS /Oi" },
{ 0, "-O0", "/Od" },
{ 0, "-O1", "/O2" },
{ 0, "-O2", "/O2" },
{ 0, "-O3", "/O2" },
{ 0, "-Os", "/O1" },
{ 0, "-g*", "/Zi" },
{ VS2005, "-fno-strict-aliasing", "" }, // deprecated in VS2005
{ 0, "-fno-strict-aliasing", "/Oa" },
{ 0, "-fno-omit-frame-pointer", "" },
{ 0, "-fomit-frame-pointer", "" },
{ 0, "-Werror", "/WX" },
//{ VS7, "-Wall", "/Wall /W3 /wd4003 /wd4018 /wd4146 /wd4242 /wd4244 /wd4619 /wd4702 /wd4706 /wd4710 /wd4711 /wd4738 /wd4826" },
{ VS7, "-Wall", "/Wall /W4 /wd4003 /wd4018 /wd4146 /wd4242 /wd4244 /wd4619 /wd4702 /wd4706 /wd4710 /wd4711 /wd4738 /wd4826 /wd4820 /wd4514 /wd4668 /wd4127 /wd4625 /wd4626 /wd4512 /wd4100 /wd4310 /wd4571 /wd4061 /wd4131 /wd4255 /wd4510 /wd4610 /wd4505 /wd4324 /wd4611 /wd4201 /wd4189 /wd4296 /wd4986 /wd4347" },
{ 0, "-Wall", "/W0" },
{ VS7, "-Wno-unused", "/wd4100 /wd4101 /wd4102 /wd4505" },
{ 0, "-Wno-sign-compare", "/wd4365 /wd4389 /wd4245" },
{ 0, "-W*", "" },
{ VS2005, "-march=*", "" }, // deprecated in VS2005
{ 0, "-march=pentium", "/G5" },
{ 0, "-march=pentiumpro", "/G6" },
{ 0, "-march=pentium3", "/G6" },
{ 0, "-march=pentium-m", "/G6" },
{ 0, "-march=athlon", "/G7" },
{ 0, "-march=pentium4", "/G7" },
{ 0, "-march=athlon64", "/G7" },
{ VS71, "-msse2", "/arch:SSE2" },
{ 0, "-msse2", "" },
{ 0, "-msse3", "" },
{ 0, "-mwindows", "" },
{ 0, "-mno-cygwin", "" },
{ 0, "-std=gnu89", "" },
{ 0, "-std=gnu++98", "/TP" },
{ 0, "-pipe", "" },
{ 0, "-x", "" },
{ 0, "c++", "" },
{ 0 }
};
static const translation_info ld_translate[] =
{
{ VS2008, "-lbufferoverflowu", "" },
{ 0, "-l*", "*.lib" },
{ 0, "-o*", "/out:*" },
{ 0, "-Wl,-Map,*", "/map:*" },
{ 0, "-Wl,--allow-multiple-definition", "/force:multiple" },
{ 0, "-Wl,--warn-common", "" },
{ 0, "-mno-cygwin", "" },
{ 0, "-s", "" },
{ 0, "-WO", "" },
{ 0, "-mconsole", "/subsystem:console" },
{ 0, "-mwindows", "/subsystem:windows" },
{ 0, "-municode", "" },
{ 0, "-static-libgcc", "" },
{ 0, "-static-libstdc++", "" },
{ 0, "-shared", "/dll" },
{ 0 }
};
static const translation_info ar_translate[] =
{
{ 0, "-cr", "" },
{ 0, "/LTCG", "/LTCG" },
{ 0 }
};
static const translation_info windres_translate[] =
{
{ 0, "-D*", "/D*" },
{ 0, "-U*", "/U*" },
{ 0, "-I*", "/I*" },
{ 0, "--include-dir*", "/I*" },
{ 0, "-o*", "/fo*" },
{ 0, "-O*", "" },
{ 0, "-i*", "*" },
{ 0 }
};
//============================================================
// GLOBALS
//============================================================
static char command_line[32768];
//============================================================
// get_exe_version
//============================================================
static DWORD get_exe_version(const char *executable)
{
char path[MAX_PATH];
void *version_info;
DWORD version_info_size, dummy;
void *sub_buffer;
UINT sub_buffer_size;
VS_FIXEDFILEINFO *info;
DWORD product_version;
char sub_block[2] = { '\\', '\0' };
// try to locate the executable
if (!SearchPath(NULL, executable, NULL, sizeof(path) / sizeof(path[0]), path, NULL))
{
fprintf(stderr, "Cannot find %s\n", executable);
exit(-100);
}
// determine the size of the version info
version_info_size = GetFileVersionInfoSize(path, &dummy);
if (version_info_size == 0)
{
switch(GetLastError())
{
case ERROR_RESOURCE_TYPE_NOT_FOUND:
fprintf(stderr, "\"%s\" does not contain version info; this is probably not a MSVC executable\n", path);
break;
default:
fprintf(stderr, "GetFileVersionInfoSize() failed\n");
break;
}
exit(-100);
}
// allocate the memory; using GlobalAlloc() so that we do not
// unintentionally uses malloc() overrides
version_info = GlobalAlloc(GMEM_FIXED, version_info_size);
if (!version_info)
{
fprintf(stderr, "Out of memory\n");
exit(-100);
}
// retrieve the version info
if (!GetFileVersionInfo(path, 0, version_info_size, version_info))
{
fprintf(stderr, "GetFileVersionInfo() failed\n");
exit(-100);
}
// extract the VS_FIXEDFILEINFO from the version info
if (!VerQueryValue(version_info, sub_block, &sub_buffer, &sub_buffer_size))
{
fprintf(stderr, "VerQueryValue() failed\n");
exit(-100);
}
info = (VS_FIXEDFILEINFO *) sub_buffer;
product_version = info->dwProductVersionMS;
GlobalFree(version_info);
return product_version;
}
//============================================================
// build_command_line
//============================================================
static void build_command_line(int argc, char *argv[])
{
const translation_info *transtable;
const char *executable;
const char *outstring = "";
char *dst = command_line;
int output_is_first = 0;
int param;
DWORD exe_version;
// if no parameters, show usage
if (argc < 2)
{
fprintf(stderr, "Usage:\n vconv {gcc|ar|ld} [param [...]]\n");
exit(0);
}
// first parameter determines the type
if (!strcmp(argv[1], "gcc"))
{
transtable = gcc_translate;
executable = "cl.exe";
dst += sprintf(dst, "cl /nologo ");
}
else if (!strcmp(argv[1], "windres"))
{
transtable = windres_translate;
executable = "rc.exe";
dst += sprintf(dst, "rc ");
}
else if (!strcmp(argv[1], "ld"))
{
transtable = ld_translate;
executable = "link.exe";
dst += sprintf(dst, "link /nologo /debug ");
}
else if (!strcmp(argv[1], "ar"))
{
transtable = ar_translate;
executable = "link.exe";
dst += sprintf(dst, "link /lib /nologo ");
outstring = "/out:";
output_is_first = 1;
}
else
{
fprintf(stderr, "Error: unknown translation type '%s'\n", argv[1]);
exit(-100);
}
// identify the version number of the EXE
exe_version = get_exe_version(executable);
// special case
if (!strcmp(executable, "cl.exe") && (exe_version >= 0x00070000))
dst += sprintf(dst, "/wd4025 ");
// iterate over parameters
for (param = 2; param < argc; param++)
{
const char *src = argv[param];
int firstchar = src[0];
int srclen = strlen(src);
int matched = FALSE;
int i;
// find a match
for (i = 0; !matched && transtable[i].gcc_option != NULL; i++)
{
const char *compare = transtable[i].gcc_option;
const char *replace;
int j;
// check version number
if (exe_version < transtable[i].vc_version)
continue;
// find a match
for (j = 0; j < srclen; j++)
if (src[j] != compare[j])
break;
// if we hit an asterisk, we're ok
if (compare[j] == '*')
{
// if this is the end of the parameter, use the next one
if (src[j] == 0)
src = argv[++param];
else
src += j;
// copy the replacement up to the asterisk
replace = transtable[i].vc_option;
while (*replace && *replace != '*')
{
if (*replace == '~')
{
dst += sprintf(dst, "%s", outstring);
replace++;
}
else
*dst++ = *replace++;
}
// if we have an asterisk in the replacement, copy the rest of the source
if (*replace == '*')
{
int addquote = (strchr(src, ' ') != NULL);
if (addquote)
*dst++ = '"';
while (*src)
{
*dst++ = (*src == '/') ? '\\' : *src;
src++;
}
if (addquote)
*dst++ = '"';
// if there's stuff after the asterisk, copy that
replace++;
while (*replace)
*dst++ = *replace++;
}
// append a final space
*dst++ = ' ';
matched = TRUE;
}
// if we hit the end, we're also ok
else if (compare[j] == 0 && j == srclen)
{
// copy the replacement up to the tilde
replace = transtable[i].vc_option;
while (*replace && *replace != '~')
*dst++ = *replace++;
// if we hit a tilde, set the new output
if (*replace == '~')
outstring = replace + 1;
// append a final space
*dst++ = ' ';
matched = TRUE;
}
// else keep looking
}
// if we didn't match, process
if (!matched)
{
// warn if we missed a parameter
if (transtable[i].gcc_option == NULL && firstchar == '-')
fprintf(stderr, "Unable to match parameter '%s'\n", src);
// otherwise, assume it's a filename and copy translating slashes
// it can also be a Windows-specific option which is passed through unscathed
else if (firstchar != '-')
{
int dotrans = (*src != '/');
// if the output filename is implicitly first, append the out parameter
if (output_is_first)
{
dst += sprintf(dst, "%s", outstring);
output_is_first = 0;
}
// now copy the rest of the string
while (*src)
{
*dst++ = (dotrans && *src == '/') ? '\\' : *src;
src++;
}
*dst++ = ' ';
}
}
}
// trim remaining spaces and NULL terminate
while (dst > command_line && dst[-1] == ' ')
dst--;
*dst = 0;
}
//============================================================
// main
//============================================================
int main(int argc, char *argv[])
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
DWORD exitcode;
int uses_redirection, in_quotes, i;
static const char cmd_prefix[] = "cmd.exe /c ";
// build the new command line
build_command_line(argc, argv);
// do we use redirection? if so, use cmd.exe
uses_redirection = FALSE;
in_quotes = FALSE;
for (i = 0; command_line[i]; i++)
{
if (command_line[i] == '\"')
in_quotes = !in_quotes;
if (!in_quotes && strchr("|<>", command_line[i]))
uses_redirection = TRUE;
}
if (uses_redirection)
{
memmove(command_line + strlen(cmd_prefix), command_line, strlen(command_line) + 1);
memcpy(command_line, cmd_prefix, strlen(cmd_prefix));
}
if (PRINT_COMMAND_LINE)
printf("%s\n", command_line);
// create the process information structures
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
memset(&pi, 0, sizeof(pi));
// create and execute the process
if (!CreateProcess(NULL, command_line, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) ||
pi.hProcess == INVALID_HANDLE_VALUE)
return -101;
// block until done and fetch the error code
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, &exitcode);
// clean up the handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
// return the child's error code
return exitcode;
}