diff options
Diffstat (limited to 'win32')
-rw-r--r-- | win32/win32.c | 134 |
1 files changed, 97 insertions, 37 deletions
diff --git a/win32/win32.c b/win32/win32.c index f50436e6bf..b23ce65c36 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -586,6 +586,30 @@ do_aspawn(void *vreally, void **vmark, void **vsp) return (status); } +/* returns pointer to the next unquoted space or the end of the string */ +static char* +find_next_space(const char *s) +{ + bool in_quotes = FALSE; + while (*s) { + /* ignore doubled backslashes, or backslash+quote */ + if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) { + s += 2; + } + /* keep track of when we're within quotes */ + else if (*s == '"') { + s++; + in_quotes = !in_quotes; + } + /* break it up only at spaces that aren't in quotes */ + else if (!in_quotes && isSPACE(*s)) + return (char*)s; + else + s++; + } + return (char*)s; +} + int do_spawn2(char *cmd, int exectype) { @@ -605,27 +629,11 @@ do_spawn2(char *cmd, int exectype) strcpy(cmd2, cmd); a = argv; for (s = cmd2; *s;) { - bool in_quotes = FALSE; while (*s && isSPACE(*s)) s++; if (*s) *(a++) = s; - while (*s) { - /* ignore doubled backslashes, or backslash+quote */ - if (*s == '\\' && (s[1] == '\\' || s[1] == '"')) { - s += 2; - } - /* keep track of when we're within quotes */ - else if (*s == '"') { - s++; - in_quotes = !in_quotes; - } - /* break it up only at spaces that aren't in quotes */ - else if (!in_quotes && isSPACE(*s)) - break; - else - s++; - } + s = find_next_space(s); if (*s) *s++ = '\0'; } @@ -3092,7 +3100,7 @@ win32_chmod(const char *path, int mode) static char * -create_command_line(const char *cmdname, const char * const *args) +create_command_line(char *cname, STRLEN clen, const char * const *args) { dTHX; int index, argc; @@ -3102,7 +3110,7 @@ create_command_line(const char *cmdname, const char * const *args) bool bat_file = FALSE; bool cmd_shell = FALSE; bool extra_quotes = FALSE; - char *cname = (char*)cmdname; + bool quote_next = FALSE; if (!cname) cname = (char*)args[0]; @@ -3119,9 +3127,13 @@ create_command_line(const char *cmdname, const char * const *args) * to the string, if the first argument is either "cmd.exe" or "cmd", * and there were at least two or more arguments passed to cmd.exe * (not including switches). + * XXX the above rules (from "cmd /?") don't seem to be applied + * always, making for the convolutions below :-( */ if (cname) { - STRLEN clen = strlen(cname); + if (!clen) + clen = strlen(cname); + if (clen > 4 && (stricmp(&cname[clen-4], ".bat") == 0 || (IsWinNT() && stricmp(&cname[clen-4], ".cmd") == 0))) @@ -3129,11 +3141,19 @@ create_command_line(const char *cmdname, const char * const *args) bat_file = TRUE; len += 3; } - else if (stricmp(cname, "cmd.exe") == 0 - || stricmp(cname, "cmd") == 0) - { - cmd_shell = TRUE; - len += 3; + else { + char *exe = strrchr(cname, '/'); + char *exe2 = strrchr(cname, '\\'); + if (exe2 > exe) + exe = exe2; + if (exe) + ++exe; + else + exe = cname; + if (stricmp(exe, "cmd.exe") == 0 || stricmp(exe, "cmd") == 0) { + cmd_shell = TRUE; + len += 3; + } } } @@ -3175,6 +3195,13 @@ create_command_line(const char *cmdname, const char * const *args) i++; } } + else if (quote_next) { + /* ok, we know the argument already has quotes; see if it + * really is multiple arguments pretending to be one and + * force a set of quotes around it */ + if (*find_next_space(arg)) + do_quote = 1; + } if (do_quote) *ptr++ = '"'; @@ -3190,11 +3217,20 @@ create_command_line(const char *cmdname, const char * const *args) if (!extra_quotes && cmd_shell - && (stricmp(arg, "/x/c") == 0 || stricmp(arg, "/c") == 0) - && (argc-1 > index+1)) /* two or more arguments to cmd.exe? */ + && (stricmp(arg, "/x/c") == 0 || stricmp(arg, "/c") == 0)) { - *ptr++ = '"'; - extra_quotes = TRUE; + /* is there a next argument? */ + if (args[index+1]) { + /* are there two or more next arguments? */ + if (args[index+2]) { + *ptr++ = '"'; + extra_quotes = TRUE; + } + else { + /* single argument, force quoting if unquoted */ + quote_next = TRUE; + } + } } } @@ -3384,9 +3420,30 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv) STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; DWORD create = 0; - - char *cmd = create_command_line(cmdname, argv); + char *cmd; char *fullcmd = Nullch; + char *cname = (char *)cmdname; + STRLEN clen = 0; + + if (cname) { + clen = strlen(cname); + /* if command name contains dquotes, must remove them */ + if (strchr(cname, '"')) { + cmd = cname; + New(0,cname,clen+1,char); + clen = 0; + while (*cmd) { + if (*cmd != '"') { + cname[clen] = *cmd; + ++clen; + } + ++cmd; + } + cname[clen] = '\0'; + } + } + + cmd = create_command_line(cname, clen, argv); env = PerlEnv_get_childenv(); dir = PerlEnv_get_childdir(); @@ -3433,9 +3490,9 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv) } DEBUG_p(PerlIO_printf(Perl_debug_log, "Spawning [%s] with [%s]\n", - cmdname,cmd)); + cname,cmd)); RETRY: - if (!CreateProcess(cmdname, /* search PATH to find executable */ + if (!CreateProcess(cname, /* search PATH to find executable */ cmd, /* executable, and its arguments */ NULL, /* process attributes */ NULL, /* thread attributes */ @@ -3453,12 +3510,14 @@ RETRY: * jump through our own hoops by picking out the path * we really want it to use. */ if (!fullcmd) { - fullcmd = qualified_path(cmdname); + fullcmd = qualified_path(cname); if (fullcmd) { - cmdname = fullcmd; + if (cname != cmdname) + Safefree(cname); + cname = fullcmd; DEBUG_p(PerlIO_printf(Perl_debug_log, "Retrying [%s] with same args\n", - cmdname)); + cname)); goto RETRY; } } @@ -3491,7 +3550,8 @@ RETVAL: PerlEnv_free_childenv(env); PerlEnv_free_childdir(dir); Safefree(cmd); - Safefree(fullcmd); + if (cname != cmdname) + Safefree(cname); return ret; #endif } |