merge from gcc
authorDJ Delorie <dj@redhat.com>
Thu, 15 Sep 2005 01:04:05 +0000 (01:04 +0000)
committerDJ Delorie <dj@redhat.com>
Thu, 15 Sep 2005 01:04:05 +0000 (01:04 +0000)
libiberty/ChangeLog
libiberty/pex-win32.c

index 3e2e92e6068486c6075165c0f6c98c0eb1ad3904..4cf1a404b117c4df668a3097c9f674a60a20dd58 100644 (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
index 10262fbfeeddd66d3b9c393552967664437fda47..ed45e5b8bb83a2aa1223873b250c6782498c2bea 100644 (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