X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/cce7d53002e8abc346b67ea4100507b0e7c4d68e..0b32070bbd13c8f19be93185172e985213972d7b:/nt/cmdproxy.c diff --git a/nt/cmdproxy.c b/nt/cmdproxy.c index 9a98e1e503..7d810e3365 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-2013 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: