#include "pex-common.h"
+#include <windows.h>
+
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
# 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. */
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++)
{
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;
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
{
int org_in, org_out, org_errdes;
long pid;
+ const char * const * newargv;
org_in = -1;
org_out = -1;
}
}
+ 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)
{
{
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