X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/2b0c7330457b8ca42375c92ada7dc7cefb0fa9fb..b6e9e0ffacf249934e92448e85e965e90aa3cf4b:/nt/cmdproxy.c
diff --git a/nt/cmdproxy.c b/nt/cmdproxy.c
index 9a98e1e503..7c52244007 100644
--- a/nt/cmdproxy.c
+++ b/nt/cmdproxy.c
@@ -1,5 +1,5 @@
/* Proxy shell designed for use with Emacs on Windows 95 and NT.
- Copyright (C) 1997, 2001-2011 Free Software Foundation, Inc.
+ Copyright (C) 1997, 2001-2012 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
@@ -33,6 +33,7 @@ along with GNU Emacs. If not, see . */
#include /* alloca */
#include /* getenv */
#include /* strlen */
+#include /* isspace, isalpha */
/* We don't want to include stdio.h because we are already duplicating
lots of it here */
@@ -251,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] == ':') ||
@@ -309,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
+ CreateProces 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
@@ -434,7 +512,7 @@ main (int argc, char ** argv)
char modname[MAX_PATH];
char path[MAX_PATH];
char dir[MAX_PATH];
-
+ int status;
interactive = TRUE;
@@ -473,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
@@ -574,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: