:utf8 works only with perlio.
[p5sagit/p5-mst-13.2.git] / win32 / win32.c
index f50436e..b23ce65 100644 (file)
@@ -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
 }