pex-win32.c: Include "windows.h".

* pex-win32.c: Include "windows.h".
(backslashify): New function.
(fix_argv): Use backslashify to convert path to windows format.  Allocate one
more place in new argv for potential executable from '#!' parsing.
(tack_on_executable): New function.  Conditional on USE_MINGW_MSYS
(openkey): Ditto.
(mingw_rootify): Ditto.
(msys_rootify): Ditto.
(spawn_script): New function.
(pex_win32_exec_child): Save translated argv in newargv.  Pass to spawn_script
if spawnv* fails.
(main): New function.  Conditional on MAIN.  Useful for testing.

From-SVN: r104292
This commit is contained in:
Christopher Faylor 2005-09-15 00:46:20 +00:00 committed by Christopher Faylor
parent 62ff44d425
commit 0d676b85ae
2 changed files with 307 additions and 14 deletions

View File

@ -1,3 +1,19 @@
2005-09-14 Christopher Faylor <cgf@timesys.com>
* pex-win32.c: Include "windows.h".
(backslashify): New function.
(fix_argv): Use backslashify to convert path to windows format.
Allocate one more place in new argv for potential executable from '#!'
parsing.
(tack_on_executable): New function. Conditional on USE_MINGW_MSYS
(openkey): Ditto.
(mingw_rootify): Ditto.
(msys_rootify): Ditto.
(spawn_script): New function.
(pex_win32_exec_child): Save translated argv in newargv. Pass to
spawn_script if spawnv* fails.
(main): New function. Conditional on MAIN. Useful for testing.
2005-08-17 Mark Kettenis <kettenis@gnu.org>
* floatformat.c (floatformat_always_valid): Change type of last

View File

@ -21,6 +21,8 @@ Boston, MA 02110-1301, USA. */
#include "pex-common.h"
#include <windows.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
@ -53,6 +55,23 @@ Boston, MA 02110-1301, USA. */
# define WAIT_GRANDCHILD 1
#endif
#define MINGW_NAME "Minimalist GNU for Windows"
#define MINGW_NAME_LEN (sizeof(MINGW_NAME) - 1)
/* Ensure that the executable pathname uses Win32 backslashes. This
is not necessary on NT, but on W9x, forward slashes causes
failure of spawn* and exec* functions (and probably any function
that calls CreateProcess) *iff* the executable pathname (argv[0])
is a quoted string. And quoting is necessary in case a pathname
contains embedded white space. You can't win. */
static void
backslashify (char *s)
{
while ((s = strchr (s, '/')) != NULL)
*s = '\\';
return;
}
/* This is a kludge to get around the Microsoft C spawn functions' propensity
to remove the outermost set of double quotes from all arguments. */
@ -79,20 +98,16 @@ fix_argv (char * const *argvec)
for (i = 0; argvec[i] != NULL; i++)
;
argv = XNEWVEC (char *, i + 1);
argv = XNEWVEC (char *, i + 2);
argv++; /* Leave space at the beginning of argv
for potential #! handling */
for (i = 0; argvec[i] != NULL; i++)
argv[i] = xstrdup (argvec[i]);
argv[i] = NULL;
/* Ensure that the executable pathname uses Win32 backslashes. This
is not necessary on NT, but on W9x, forward slashes causes
failure of spawn* and exec* functions (and probably any function
that calls CreateProcess) *iff* the executable pathname (argv[0])
is a quoted string. And quoting is necessary in case a pathname
contains embedded white space. You can't win. */
for (command0 = argv[0]; *command0 != '\0'; command0++)
if (*command0 == '/')
*command0 = '\\';
backslashify (argv[0]);
for (i = 1; argv[i] != 0; i++)
{
@ -137,11 +152,11 @@ fix_argv (char * const *argvec)
space ends in a backslash (such as in the case of -iprefix arg
passed to cpp). The resulting quoted strings gets misinterpreted
by the command interpreter -- it thinks that the ending quote
is escaped by the trailing backslash and things get confused.
is escaped by the trailing backslash and things get confused.
We handle this case by escaping the trailing backslash, provided
it was not escaped in the first place. */
if (len > 1
&& argv[i][len-1] == '\\'
if (len > 1
&& argv[i][len-1] == '\\'
&& argv[i][len-2] != '\\')
{
trailing_backslash = 1;
@ -230,6 +245,249 @@ pex_win32_close (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd)
return _close (fd);
}
#ifdef USE_MINGW_MSYS
static const char *mingw_keys[] = {"SOFTWARE", "Microsoft", "Windows", "CurrentVersion", "Uninstall", NULL};
/* Tack the executable on the end of a (possibly slash terminated) buffer
and convert everything to \. */
static const char *
tack_on_executable (char *buf, const char *executable)
{
char *p = strchr (buf, '\0');
if (p > buf && (p[-1] == '\\' || p[-1] == '/'))
p[-1] = '\0';
backslashify (strcat (buf, executable));
return buf;
}
/* Walk down a registry hierarchy until the end. Return the key. */
static HKEY
openkey (HKEY hStart, const char *keys[])
{
HKEY hKey, hTmp;
for (hKey = hStart; *keys; keys++)
{
LONG res;
hTmp = hKey;
res = RegOpenKey (hTmp, *keys, &hKey);
if (hTmp != HKEY_LOCAL_MACHINE)
RegCloseKey (hTmp);
if (res != ERROR_SUCCESS)
return NULL;
}
return hKey;
}
/* Return the "mingw root" as derived from the mingw uninstall information. */
static const char *
mingw_rootify (const char *executable)
{
HKEY hKey, hTmp;
DWORD maxlen;
char *namebuf, *foundbuf;
DWORD i;
LONG res;
/* Open the uninstall "directory". */
hKey = openkey (HKEY_LOCAL_MACHINE, mingw_keys);
/* Not found. */
if (!hKey)
return executable;
/* Need to enumerate all of the keys here looking for one the most recent
one for MinGW. */
if (RegQueryInfoKey (hKey, NULL, NULL, NULL, NULL, &maxlen, NULL, NULL,
NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
{
RegCloseKey (hKey);
return executable;
}
namebuf = XNEWVEC (char, ++maxlen);
foundbuf = XNEWVEC (char, maxlen);
foundbuf[0] = '\0';
if (!namebuf || !foundbuf)
{
RegCloseKey (hKey);
if (namebuf)
free (namebuf);
if (foundbuf)
free (foundbuf);
return executable;
}
/* Look through all of the keys for one that begins with Minimal GNU...
Try to get the latest version by doing a string compare although that
string never really works with version number sorting. */
for (i = 0; RegEnumKey (hKey, i, namebuf, maxlen) == ERROR_SUCCESS; i++)
{
int match = strcasecmp (namebuf, MINGW_NAME);
if (match < 0)
continue;
if (match > 0 && strncasecmp (namebuf, MINGW_NAME, MINGW_NAME_LEN) > 0)
continue;
if (strcasecmp (namebuf, foundbuf) > 0)
strcpy (foundbuf, namebuf);
}
free (namebuf);
/* If foundbuf is empty, we didn't find anything. Punt. */
if (!foundbuf[0])
{
free (foundbuf);
RegCloseKey (hKey);
return executable;
}
/* Open the key that we wanted */
res = RegOpenKey (hKey, foundbuf, &hTmp);
RegCloseKey (hKey);
free (foundbuf);
/* Don't know why this would fail, but you gotta check */
if (res != ERROR_SUCCESS)
return executable;
maxlen = 0;
/* Get the length of the value pointed to by InstallLocation */
if (RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, NULL,
&maxlen) != ERROR_SUCCESS || maxlen == 0)
{
RegCloseKey (hTmp);
return executable;
}
/* Allocate space for the install location */
foundbuf = XNEWVEC (char, maxlen + strlen (executable));
if (!foundbuf)
{
free (foundbuf);
RegCloseKey (hTmp);
}
/* Read the install location into the buffer */
res = RegQueryValueEx (hTmp, "InstallLocation", 0, NULL, (LPBYTE) foundbuf,
&maxlen);
RegCloseKey (hTmp);
if (res != ERROR_SUCCESS)
{
free (foundbuf);
return executable;
}
/* Concatenate the install location and the executable, turn all slashes
to backslashes, and return that. */
return tack_on_executable (foundbuf, executable);
}
/* Read the install location of msys from it's installation file and
rootify the executable based on that. */
static const char *
msys_rootify (const char *executable)
{
size_t bufsize = 64;
size_t execlen = strlen (executable) + 1;
char *buf;
DWORD res = 0;
for (;;)
{
buf = XNEWVEC (char, bufsize + execlen);
if (!buf)
break;
res = GetPrivateProfileString ("InstallSettings", "InstallPath", NULL,
buf, bufsize, "msys.ini");
if (!res)
break;
if (strlen (buf) < bufsize)
break;
res = 0;
free (buf);
bufsize *= 2;
if (bufsize > 65536)
{
buf = NULL;
break;
}
}
if (res)
return tack_on_executable (buf, executable);
/* failed */
if (buf)
free (buf);
return executable;
}
#endif
static long
spawn_script (const char *executable, const char * const * argv)
{
int pid = -1;
int save_errno = errno;
int fd = _open (executable, _O_RDONLY);
if (fd >= 0)
{
char buf[MAX_PATH + 5];
int len = _read (fd, buf, sizeof (buf) - 1);
_close (fd);
if (len > 3)
{
char *eol;
buf[len] = '\0';
eol = strchr (buf, '\n');
if (eol && strncmp (buf, "#!", 2) == 0)
{
char *executable1;
const char ** avhere = (const char **) --argv;
do
*eol = '\0';
while (*--eol == '\r' || *eol == ' ' || *eol == '\t');
for (executable1 = buf + 2; *executable1 == ' ' || *executable1 == '\t'; executable1++)
continue;
backslashify (executable1);
*avhere = executable1;
#ifndef USE_MINGW_MSYS
executable = strrchr (executable1, '\\') + 1;
if (!executable)
executable = executable1;
pid = _spawnvp (_P_NOWAIT, executable, argv);
#else
if (strchr (executable1, '\\') == NULL)
pid = _spawnvp (_P_NOWAIT, executable1, argv);
else if (executable1[0] != '\\')
pid = _spawnv (_P_NOWAIT, executable1, argv);
else
{
const char *newex = mingw_rootify (executable1);
*avhere = newex;
pid = _spawnv (_P_NOWAIT, newex, argv);
if (executable1 != newex)
free ((char *) newex);
if (pid < 0)
{
newex = msys_rootify (executable1);
if (newex != executable1)
{
*avhere = newex;
pid = _spawnv (_P_NOWAIT, newex, argv);
free ((char *) newex);
}
}
}
#endif
}
}
}
if (pid < 0)
errno = save_errno;
return pid;
}
/* Execute a child. */
static long
@ -240,6 +498,7 @@ pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
{
int org_in, org_out, org_errdes;
long pid;
const char * const * newargv;
org_in = -1;
org_out = -1;
@ -319,8 +578,12 @@ pex_win32_exec_child (struct pex_obj *obj ATTRIBUTE_UNUSED, int flags,
}
}
newargv = fix_argv (argv);
pid = (((flags & PEX_SEARCH) != 0 ? _spawnvp : _spawnv)
(_P_NOWAIT, executable, fix_argv (argv)));
(_P_NOWAIT, executable, newargv));
if (pid == -1)
pid = spawn_script (executable, newargv);
if (pid == -1)
{
@ -438,3 +701,17 @@ pex_win32_fdopenr (struct pex_obj *obj ATTRIBUTE_UNUSED, int fd,
{
return fdopen (fd, binary ? "rb" : "r");
}
#ifdef MAIN
#include <stdio.h>
int
main (int argc ATTRIBUTE_UNUSED, char **argv)
{
char const *errmsg;
int err;
argv++;
printf ("%ld\n", pex_win32_exec_child (NULL, PEX_SEARCH, argv[0], argv, 0, 1, 2, &errmsg, &err));
exit (0);
}
#endif