Fix redefinition warning in MinGW64 build.
[bpt/emacs.git] / nt / cmdproxy.c
index 7c68f86..f3433f6 100644 (file)
@@ -1,6 +1,5 @@
 /* Proxy shell designed for use with Emacs on Windows 95 and NT.
-   Copyright (C) 1997, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-     2008, 2009, 2010  Free Software Foundation, Inc.
+   Copyright (C) 1997, 2001-2014 Free Software Foundation, Inc.
 
    Accepts subset of Unix sh(1) command-line options, for compatibility
    with elisp code written for Unix.  When possible, executes external
@@ -34,6 +33,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <malloc.h>  /* alloca */
 #include <stdlib.h>  /* getenv */
 #include <string.h>  /* strlen */
+#include <ctype.h>   /* isspace, isalpha */
 
 /* We don't want to include stdio.h because we are already duplicating
    lots of it here */
@@ -252,7 +252,6 @@ make_absolute (const char *prog)
   char curdir[MAX_PATH];
   char *p, *path;
   const char *fname;
-  int i;
 
   /* At least partial absolute path specified; search there.  */
   if ((isalpha (prog[0]) && prog[1] == ':') ||
@@ -310,6 +309,84 @@ make_absolute (const char *prog)
   return NULL;
 }
 
+/* Try to decode the given command line the way cmd would do it.  On
+   success, return 1 with cmdline dequoted.  Otherwise, when we've
+   found constructs only cmd can properly interpret, return 0 and
+   leave cmdline unchanged.  */
+int
+try_dequote_cmdline (char* cmdline)
+{
+  /* Dequoting can only subtract characters, so the length of the
+     original command line is a bound on the amount of scratch space
+     we need.  This length, in turn, is bounded by the 32k
+     CreateProcess limit.  */
+  char * old_pos = cmdline;
+  char * new_cmdline = alloca (strlen(cmdline));
+  char * new_pos = new_cmdline;
+  char c;
+
+  enum {
+    NORMAL,
+    AFTER_CARET,
+    INSIDE_QUOTE
+  } state = NORMAL;
+
+  while ((c = *old_pos++))
+    {
+      switch (state)
+        {
+        case NORMAL:
+          switch(c)
+            {
+            case '"':
+              *new_pos++ = c;
+              state = INSIDE_QUOTE;
+              break;
+            case '^':
+              state = AFTER_CARET;
+              break;
+            case '<': case '>':
+            case '&': case '|':
+            case '(': case ')':
+            case '%': case '!':
+              /* We saw an unquoted shell metacharacter and we don't
+                 understand it. Bail out.  */
+              return 0;
+            default:
+              *new_pos++ = c;
+              break;
+            }
+          break;
+        case AFTER_CARET:
+          *new_pos++ = c;
+          state = NORMAL;
+          break;
+        case INSIDE_QUOTE:
+          switch (c)
+            {
+            case '"':
+              *new_pos++ = c;
+              state = NORMAL;
+              break;
+            case '%':
+            case '!':
+              /* Variable substitution inside quote.  Bail out.  */
+              return 0;
+            default:
+              *new_pos++ = c;
+              break;
+            }
+          break;
+        }
+    }
+
+  /* We were able to dequote the entire string.  Copy our scratch
+     buffer on top of the original buffer and return success.  */
+  memcpy (cmdline, new_cmdline, new_pos - new_cmdline);
+  cmdline[new_pos - new_cmdline] = '\0';
+  return 1;
+}
+
 /*****************************************************************/
 
 #if 0
@@ -435,7 +512,7 @@ main (int argc, char ** argv)
   char modname[MAX_PATH];
   char path[MAX_PATH];
   char dir[MAX_PATH];
-
+  int status;
 
   interactive = TRUE;
 
@@ -474,20 +551,73 @@ main (int argc, char ** argv)
 
   /* Although Emacs always sets argv[0] to an absolute pathname, we
      might get run in other ways as well, so convert argv[0] to an
-     absolute name before comparing to the module name.  Don't get
-     caught out by mixed short and long names.  */
-  GetShortPathName (modname, modname, sizeof (modname));
+     absolute name before comparing to the module name.  */
   path[0] = '\0';
-  if (!SearchPath (NULL, argv[0], ".exe", sizeof (path), path, &progname)
-      || !GetShortPathName (path, path, sizeof (path))
-      || stricmp (modname, path) != 0)
+  /* The call to SearchPath will find argv[0] in the current
+     directory, append ".exe" to it if needed, and also canonicalize
+     it, to resolve references to ".", "..", etc.  */
+  status = SearchPath (NULL, argv[0], ".exe", sizeof (path), path,
+                                 &progname);
+  if (!(status > 0 && stricmp (modname, path) == 0))
     {
-      /* We are being used as a helper to run a DOS app; just pass
-        command line to DOS app without change.  */
-      /* TODO: fill in progname.  */
-      if (spawn (NULL, GetCommandLine (), dir, &rc))
-       return rc;
-      fail ("Could not run %s\n", GetCommandLine ());
+      if (status <= 0)
+       {
+         char *s;
+
+         /* Make sure we have argv[0] in path[], as the failed
+            SearchPath might not have copied it there.  */
+         strcpy (path, argv[0]);
+         /* argv[0] could include forward slashes; convert them all
+            to backslashes, for strrchr calls below to DTRT.  */
+         for (s = path; *s; s++)
+           if (*s == '/')
+             *s = '\\';
+       }
+      /* Perhaps MODNAME and PATH use mixed short and long file names.  */
+      if (!(GetShortPathName (modname, modname, sizeof (modname))
+           && GetShortPathName (path, path, sizeof (path))
+           && stricmp (modname, path) == 0))
+       {
+         /* Sometimes GetShortPathName fails because one or more
+            directories leading to argv[0] have issues with access
+            rights.  In that case, at least we can compare the
+            basenames.  Note: this disregards the improbable case of
+            invoking a program of the same name from another
+            directory, since the chances of that other executable to
+            be both our namesake and a 16-bit DOS application are nil.  */
+         char *p = strrchr (path, '\\');
+         char *q = strrchr (modname, '\\');
+         char *pdot, *qdot;
+
+         if (!p)
+           p = strchr (path, ':');
+         if (!p)
+           p = path;
+         else
+           p++;
+         if (!q)
+           q = strchr (modname, ':');
+         if (!q)
+           q = modname;
+         else
+           q++;
+
+         pdot = strrchr (p, '.');
+         if (!pdot || stricmp (pdot, ".exe") != 0)
+           pdot = p + strlen (p);
+         qdot = strrchr (q, '.');
+         if (!qdot || stricmp (qdot, ".exe") != 0)
+           qdot = q + strlen (q);
+         if (pdot - p != qdot - q || strnicmp (p, q, pdot - p) != 0)
+           {
+             /* We are being used as a helper to run a DOS app; just
+                pass command line to DOS app without change.  */
+             /* TODO: fill in progname.  */
+             if (spawn (NULL, GetCommandLine (), dir, &rc))
+               return rc;
+             fail ("Could not run %s\n", GetCommandLine ());
+           }
+       }
     }
 
   /* Process command line.  If running interactively (-c or /c not
@@ -575,30 +705,26 @@ main (int argc, char ** argv)
      execute the command directly ourself.  */
   if (cmdline)
     {
-      /* If no redirection or piping, and if program can be found, then
-        run program directly.  Otherwise invoke a real shell. */
-
-      static char copout_chars[] = "|<>&";
-
-      if (strpbrk (cmdline, copout_chars) == NULL)
-       {
-         const char *args;
-
-         /* The program name is the first token of cmdline.  Since
-            filenames cannot legally contain embedded quotes, the value
-            of escape_char doesn't matter.  */
-         args = cmdline;
-         if (!get_next_token (path, &args))
-           fail ("error: no program name specified.\n");
-
-         canon_filename (path);
-         progname = make_absolute (path);
-
-         /* If we found the program, run it directly (if not found it
-             might be an internal shell command, so don't fail).  */
-         if (progname != NULL)
-           need_shell = FALSE;
-       }
+      const char *args;
+
+      /* The program name is the first token of cmdline.  Since
+         filenames cannot legally contain embedded quotes, the value
+         of escape_char doesn't matter.  */
+      args = cmdline;
+      if (!get_next_token (path, &args))
+        fail ("error: no program name specified.\n");
+
+      canon_filename (path);
+      progname = make_absolute (path);
+
+      /* If we found the program and the rest of the command line does
+         not contain unquoted shell metacharacters, run the program
+         directly (if not found it might be an internal shell command,
+         so don't fail).  */
+      if (progname != NULL && try_dequote_cmdline (cmdline))
+        need_shell = FALSE;
+      else
+        progname = NULL;
     }
 
  pass_to_shell:
@@ -717,6 +843,3 @@ main (int argc, char ** argv)
 
   return 0;
 }
-
-/* arch-tag: 88678d93-07ac-4e2f-ad63-d4a740ca69ac
-   (do not change this comment) */