This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
regcomp.c: Replace invalid assertion
[perl5.git] / win32 / win32.c
index a16410f..651b97b 100644 (file)
@@ -42,9 +42,8 @@
 
 /* #include "config.h" */
 
-#if !defined(PERLIO_IS_STDIO) && !defined(USE_SFIO)
-#  define PerlIO FILE
-#endif
+
+#define PerlIO FILE
 
 #include <sys/stat.h>
 #include "EXTERN.h"
@@ -89,12 +88,6 @@ END_EXTERN_C
 #define EXECF_SPAWN_NOWAIT 3
 
 #if defined(PERL_IMPLICIT_SYS)
-#  undef win32_get_privlib
-#  define win32_get_privlib g_win32_get_privlib
-#  undef win32_get_sitelib
-#  define win32_get_sitelib g_win32_get_sitelib
-#  undef win32_get_vendorlib
-#  define win32_get_vendorlib g_win32_get_vendorlib
 #  undef getlogin
 #  define getlogin g_getlogin
 #endif
@@ -121,34 +114,46 @@ static void       my_invalid_parameter_handler(const wchar_t* expression,
                        unsigned int line, uintptr_t pReserved);
 #endif
 
+#ifndef WIN32_NO_REGISTRY
 static char*   get_regstr_from(HKEY hkey, const char *valuename, SV **svp);
 static char*   get_regstr(const char *valuename, SV **svp);
+#endif
+
 static char*   get_emd_part(SV **prev_pathp, STRLEN *const len,
                        char *trailing, ...);
-static char*   win32_get_xlib(const char *pl, const char *xlib,
+static char*   win32_get_xlib(const char *pl,
+                       WIN32_NO_REGISTRY_M_(const char *xlib)
                        const char *libname, STRLEN *const len);
+
 static BOOL    has_shell_metachars(const char *ptr);
 static long    tokenize(const char *str, char **dest, char ***destv);
 static void    get_shell(void);
 static char*   find_next_space(const char *s);
 static int     do_spawn2(pTHX_ const char *cmd, int exectype);
-static long    find_pid(int pid);
+static int     do_spawn2_handles(pTHX_ const char *cmd, int exectype,
+                        const int *handles);
+static int     do_spawnvp_handles(int mode, const char *cmdname,
+                        const char * const *argv, const int *handles);
+static PerlIO * do_popen(const char *mode, const char *command, IV narg,
+                        SV **args);
+static long    find_pid(pTHX_ int pid);
 static void    remove_dead_process(long child);
 static int     terminate_process(DWORD pid, HANDLE process_handle, int sig);
+static int     my_killpg(int pid, int sig);
 static int     my_kill(int pid, int sig);
 static void    out_of_memory(void);
 static char*   wstr_to_str(const wchar_t* wstr);
 static long    filetime_to_clock(PFILETIME ft);
 static BOOL    filetime_from_time(PFILETIME ft, time_t t);
 static char*   create_command_line(char *cname, STRLEN clen,
-                       const char * const *args);
-static char*   qualified_path(const char *cmd);
+                                   const char * const *args);
+static char*   qualified_path(const char *cmd, bool other_exts);
 static void    ansify_path(void);
 static LRESULT win32_process_message(HWND hwnd, UINT msg,
                        WPARAM wParam, LPARAM lParam);
 
 #ifdef USE_ITHREADS
-static long    find_pseudo_pid(int pid);
+static long    find_pseudo_pid(pTHX_ int pid);
 static void    remove_dead_pseudo_process(long child);
 static HWND    get_hwnd_delay(pTHX, long child, DWORD tries);
 #endif
@@ -160,10 +165,19 @@ static void       win32_csighandler(int sig);
 START_EXTERN_C
 HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
 char   w32_module_name[MAX_PATH+1];
+#ifdef WIN32_DYN_IOINFO_SIZE
+Size_t w32_ioinfo_size;/* avoid 0 extend op b4 mul, otherwise could be a U8 */
+#endif
 END_EXTERN_C
 
 static OSVERSIONINFO g_osver = {0, 0, 0, 0, 0, ""};
 
+#ifndef WIN32_NO_REGISTRY
+/* initialized by Perl_win32_init/PERL_SYS_INIT */
+static HKEY HKCU_Perl_hnd;
+static HKEY HKLM_Perl_hnd;
+#endif
+
 #ifdef SET_INVALID_PARAMETER_HANDLER
 static BOOL silent_invalid_parameter_handler = FALSE;
 
@@ -212,54 +226,35 @@ set_w32_module_name(void)
                                ? GetModuleHandle(NULL)
                                : w32_perldll_handle);
 
-    OSVERSIONINFO osver; /* g_osver may not yet be initialized */
-    osver.dwOSVersionInfoSize = sizeof(osver);
-    GetVersionEx(&osver);
-
-    if (osver.dwMajorVersion > 4) {
-        WCHAR modulename[MAX_PATH];
-        WCHAR fullname[MAX_PATH];
-        char *ansi;
-
-        DWORD (__stdcall *pfnGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD) =
-            (DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD))
-            GetProcAddress(GetModuleHandle("kernel32.dll"), "GetLongPathNameW");
+    WCHAR modulename[MAX_PATH];
+    WCHAR fullname[MAX_PATH];
+    char *ansi;
 
-        GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR));
+    DWORD (__stdcall *pfnGetLongPathNameW)(LPCWSTR, LPWSTR, DWORD) =
+        (DWORD (__stdcall *)(LPCWSTR, LPWSTR, DWORD))
+        GetProcAddress(GetModuleHandle("kernel32.dll"), "GetLongPathNameW");
 
-        /* Make sure we get an absolute pathname in case the module was loaded
-         * explicitly by LoadLibrary() with a relative path. */
-        GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL);
+    GetModuleFileNameW(module, modulename, sizeof(modulename)/sizeof(WCHAR));
 
-        /* Make sure we start with the long path name of the module because we
-         * later scan for pathname components to match "5.xx" to locate
-         * compatible sitelib directories, and the short pathname might mangle
-         * this path segment (e.g. by removing the dot on NTFS to something
-         * like "5xx~1.yy") */
-        if (pfnGetLongPathNameW)
-            pfnGetLongPathNameW(fullname, fullname, sizeof(fullname)/sizeof(WCHAR));
+    /* Make sure we get an absolute pathname in case the module was loaded
+     * explicitly by LoadLibrary() with a relative path. */
+    GetFullPathNameW(modulename, sizeof(fullname)/sizeof(WCHAR), fullname, NULL);
 
-        /* remove \\?\ prefix */
-        if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0)
-            memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR));
-
-        ansi = win32_ansipath(fullname);
-        my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name));
-        win32_free(ansi);
-    }
-    else {
-        GetModuleFileName(module, w32_module_name, sizeof(w32_module_name));
+    /* Make sure we start with the long path name of the module because we
+     * later scan for pathname components to match "5.xx" to locate
+     * compatible sitelib directories, and the short pathname might mangle
+     * this path segment (e.g. by removing the dot on NTFS to something
+     * like "5xx~1.yy") */
+    if (pfnGetLongPathNameW)
+        pfnGetLongPathNameW(fullname, fullname, sizeof(fullname)/sizeof(WCHAR));
 
-        /* remove \\?\ prefix */
-        if (memcmp(w32_module_name, "\\\\?\\", 4) == 0)
-            memmove(w32_module_name, w32_module_name+4, strlen(w32_module_name+4)+1);
+    /* remove \\?\ prefix */
+    if (memcmp(fullname, L"\\\\?\\", 4*sizeof(WCHAR)) == 0)
+        memmove(fullname, fullname+4, (wcslen(fullname+4)+1)*sizeof(WCHAR));
 
-        /* try to get full path to binary (which may be mangled when perl is
-         * run from a 16-bit app) */
-        /*PerlIO_printf(Perl_debug_log, "Before %s\n", w32_module_name);*/
-        win32_longpath(w32_module_name);
-        /*PerlIO_printf(Perl_debug_log, "After  %s\n", w32_module_name);*/
-    }
+    ansi = win32_ansipath(fullname);
+    my_strlcpy(w32_module_name, ansi, sizeof(w32_module_name));
+    win32_free(ansi);
 
     /* normalize to forward slashes */
     ptr = w32_module_name;
@@ -270,36 +265,31 @@ set_w32_module_name(void)
     }
 }
 
+#ifndef WIN32_NO_REGISTRY
 /* *svp (if non-NULL) is expected to be POK (valid allocated SvPVX(*svp)) */
 static char*
-get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
+get_regstr_from(HKEY handle, const char *valuename, SV **svp)
 {
     /* Retrieve a REG_SZ or REG_EXPAND_SZ from the registry */
-    HKEY handle;
     DWORD type;
-    const char *subkey = "Software\\Perl";
     char *str = NULL;
     long retval;
+    DWORD datalen;
 
-    retval = RegOpenKeyEx(hkey, subkey, 0, KEY_READ, &handle);
-    if (retval == ERROR_SUCCESS) {
-       DWORD datalen;
-       retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
-       if (retval == ERROR_SUCCESS
-           && (type == REG_SZ || type == REG_EXPAND_SZ))
-       {
-           dTHX;
-           if (!*svp)
-               *svp = sv_2mortal(newSVpvn("",0));
-           SvGROW(*svp, datalen);
-           retval = RegQueryValueEx(handle, valuename, 0, NULL,
-                                    (PBYTE)SvPVX(*svp), &datalen);
-           if (retval == ERROR_SUCCESS) {
-               str = SvPVX(*svp);
-               SvCUR_set(*svp,datalen-1);
-           }
+    retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
+    if (retval == ERROR_SUCCESS
+       && (type == REG_SZ || type == REG_EXPAND_SZ))
+    {
+       dTHX;
+       if (!*svp)
+           *svp = sv_2mortal(newSVpvs(""));
+       SvGROW(*svp, datalen);
+       retval = RegQueryValueEx(handle, valuename, 0, NULL,
+                                (PBYTE)SvPVX(*svp), &datalen);
+       if (retval == ERROR_SUCCESS) {
+           str = SvPVX(*svp);
+           SvCUR_set(*svp,datalen-1);
        }
-       RegCloseKey(handle);
     }
     return str;
 }
@@ -308,11 +298,22 @@ get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
 static char*
 get_regstr(const char *valuename, SV **svp)
 {
-    char *str = get_regstr_from(HKEY_CURRENT_USER, valuename, svp);
-    if (!str)
-       str = get_regstr_from(HKEY_LOCAL_MACHINE, valuename, svp);
+    char *str;
+    if (HKCU_Perl_hnd) {
+       str = get_regstr_from(HKCU_Perl_hnd, valuename, svp);
+       if (!str)
+           goto try_HKLM;
+    }
+    else {
+       try_HKLM:
+       if (HKLM_Perl_hnd)
+           str = get_regstr_from(HKLM_Perl_hnd, valuename, svp);
+       else
+           str = NULL;
+    }
     return str;
 }
+#endif /* ifndef WIN32_NO_REGISTRY */
 
 /* *prev_pathp (if non-NULL) is expected to be POK (valid allocated SvPVX(sv)) */
 static char *
@@ -369,9 +370,9 @@ get_emd_part(SV **prev_pathp, STRLEN *const len, char *trailing_path, ...)
        /* directory exists */
        dTHX;
        if (!*prev_pathp)
-           *prev_pathp = sv_2mortal(newSVpvn("",0));
+           *prev_pathp = sv_2mortal(newSVpvs(""));
        else if (SvPVX(*prev_pathp))
-           sv_catpvn(*prev_pathp, ";", 1);
+           sv_catpvs(*prev_pathp, ";");
        sv_catpv(*prev_pathp, mod_name);
        if(len)
            *len = SvCUR(*prev_pathp);
@@ -381,44 +382,50 @@ get_emd_part(SV **prev_pathp, STRLEN *const len, char *trailing_path, ...)
     return NULL;
 }
 
-char *
-win32_get_privlib(const char *pl, STRLEN *const len)
+EXTERN_C char *
+win32_get_privlib(WIN32_NO_REGISTRY_M_(const char *pl) STRLEN *const len)
 {
-    dTHX;
     char *stdlib = "lib";
-    char buffer[MAX_PATH+1];
     SV *sv = NULL;
+#ifndef WIN32_NO_REGISTRY
+    char buffer[MAX_PATH+1];
 
     /* $stdlib = $HKCU{"lib-$]"} || $HKLM{"lib-$]"} || $HKCU{"lib"} || $HKLM{"lib"} || "";  */
     sprintf(buffer, "%s-%s", stdlib, pl);
     if (!get_regstr(buffer, &sv))
        (void)get_regstr(stdlib, &sv);
+#endif
 
     /* $stdlib .= ";$EMD/../../lib" */
     return get_emd_part(&sv, len, stdlib, ARCHNAME, "bin", NULL);
 }
 
 static char *
-win32_get_xlib(const char *pl, const char *xlib, const char *libname,
-              STRLEN *const len)
+win32_get_xlib(const char *pl, WIN32_NO_REGISTRY_M_(const char *xlib)
+              const char *libname, STRLEN *const len)
 {
-    dTHX;
+#ifndef WIN32_NO_REGISTRY
     char regstr[40];
+#endif
     char pathstr[MAX_PATH+1];
     SV *sv1 = NULL;
     SV *sv2 = NULL;
 
+#ifndef WIN32_NO_REGISTRY
     /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
     sprintf(regstr, "%s-%s", xlib, pl);
     (void)get_regstr(regstr, &sv1);
+#endif
 
     /* $xlib .=
      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib";  */
     sprintf(pathstr, "%s/%s/lib", libname, pl);
     (void)get_emd_part(&sv1, NULL, pathstr, ARCHNAME, "bin", pl, NULL);
 
+#ifndef WIN32_NO_REGISTRY
     /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
     (void)get_regstr(xlib, &sv2);
+#endif
 
     /* $xlib .=
      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib";  */
@@ -430,7 +437,8 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname,
     if (!sv1) {
        sv1 = sv2;
     } else if (sv2) {
-       sv_catpvn(sv1, ";", 1);
+        dTHX;
+       sv_catpv(sv1, ";");
        sv_catsv(sv1, sv2);
     }
 
@@ -439,20 +447,20 @@ win32_get_xlib(const char *pl, const char *xlib, const char *libname,
     return SvPVX(sv1);
 }
 
-char *
+EXTERN_C char *
 win32_get_sitelib(const char *pl, STRLEN *const len)
 {
-    return win32_get_xlib(pl, "sitelib", "site", len);
+    return win32_get_xlib(pl, WIN32_NO_REGISTRY_M_("sitelib") "site", len);
 }
 
 #ifndef PERL_VENDORLIB_NAME
 #  define PERL_VENDORLIB_NAME  "vendor"
 #endif
 
-char *
+EXTERN_C char *
 win32_get_vendorlib(const char *pl, STRLEN *const len)
 {
-    return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME, len);
+    return win32_get_xlib(pl, WIN32_NO_REGISTRY_M_("vendorlib") PERL_VENDORLIB_NAME, len);
 }
 
 static BOOL
@@ -544,7 +552,6 @@ tokenize(const char *str, char **dest, char ***destv)
     char **retvstart = 0;
     int items = -1;
     if (str) {
-       dTHX;
        int slen = strlen(str);
        char *ret;
        char **retv;
@@ -694,7 +701,12 @@ find_next_space(const char *s)
 }
 
 static int
-do_spawn2(pTHX_ const char *cmd, int exectype)
+do_spawn2(pTHX_ const char *cmd, int exectype) {
+    return do_spawn2_handles(aTHX_ cmd, exectype, NULL);
+}
+
+static int
+do_spawn2_handles(pTHX_ const char *cmd, int exectype, const int *handles)
 {
     char **a;
     char *s;
@@ -727,8 +739,8 @@ do_spawn2(pTHX_ const char *cmd, int exectype)
                                       (const char* const*)argv);
                break;
            case EXECF_SPAWN_NOWAIT:
-               status = win32_spawnvp(P_NOWAIT, argv[0],
-                                      (const char* const*)argv);
+               status = do_spawnvp_handles(P_NOWAIT, argv[0],
+                                           (const char* const*)argv, handles);
                break;
            case EXECF_EXEC:
                status = win32_execvp(argv[0], (const char* const*)argv);
@@ -755,8 +767,8 @@ do_spawn2(pTHX_ const char *cmd, int exectype)
                                   (const char* const*)argv);
            break;
        case EXECF_SPAWN_NOWAIT:
-           status = win32_spawnvp(P_NOWAIT, argv[0],
-                                  (const char* const*)argv);
+           status = do_spawnvp_handles(P_NOWAIT, argv[0],
+                                       (const char* const*)argv, handles);
            break;
        case EXECF_EXEC:
            status = win32_execvp(argv[0], (const char* const*)argv);
@@ -815,7 +827,7 @@ Perl_do_exec(pTHX_ const char *cmd)
 DllExport DIR *
 win32_opendir(const char *filename)
 {
-    dTHX;
+    dTHXa(NULL);
     DIR                        *dirp;
     long               len;
     long               idx;
@@ -854,6 +866,7 @@ win32_opendir(const char *filename)
 
     /* do the FindFirstFile call */
     MultiByteToWideChar(CP_ACP, 0, scanname, -1, wscanname, sizeof(wscanname)/sizeof(WCHAR));
+    aTHXa(PERL_GET_THX);
     dirp->handle = FindFirstFileW(PerlDir_mapW(wscanname), &wFindData);
 
     if (dirp->handle == INVALID_HANDLE_VALUE) {
@@ -924,7 +937,6 @@ win32_readdir(DIR *dirp)
        /* Now set up for the next call to readdir */
        dirp->curr += len + 1;
        if (dirp->curr >= dirp->end) {
-           dTHX;
            BOOL res;
            char buffer[MAX_PATH*2];
 
@@ -1006,7 +1018,6 @@ win32_rewinddir(DIR *dirp)
 DllExport int
 win32_closedir(DIR *dirp)
 {
-    dTHX;
     if (dirp->handle != INVALID_HANDLE_VALUE)
        FindClose(dirp->handle);
     Safefree(dirp->start);
@@ -1020,7 +1031,7 @@ win32_dirp_dup(DIR *const dirp, CLONE_PARAMS *const param)
 {
     dVAR;
     PerlInterpreter *const from = param->proto_perl;
-    PerlInterpreter *const to   = PERL_GET_THX;
+    PerlInterpreter *const to   = (PerlInterpreter *)PERL_GET_THX;
 
     long pos;
     DIR *dup;
@@ -1109,7 +1120,7 @@ setgid(gid_t agid)
     return (agid == ROOT_GID ? 0 : -1);
 }
 
-char *
+EXTERN_C char *
 getlogin(void)
 {
     dTHX;
@@ -1131,6 +1142,7 @@ chown(const char *path, uid_t owner, gid_t group)
  * XXX this needs strengthening  (for PerlIO)
  *   -- BKS, 11-11-200
 */
+#if !defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 4
 int mkstemp(const char *path)
 {
     dTHX;
@@ -1151,11 +1163,11 @@ retry:
        goto retry;
     return fd;
 }
+#endif
 
 static long
-find_pid(int pid)
+find_pid(pTHX_ int pid)
 {
-    dTHX;
     long child = w32_num_children;
     while (--child >= 0) {
        if ((int)w32_child_pids[child] == pid)
@@ -1180,9 +1192,8 @@ remove_dead_process(long child)
 
 #ifdef USE_ITHREADS
 static long
-find_pseudo_pid(int pid)
+find_pseudo_pid(pTHX_ int pid)
 {
-    dTHX;
     long child = w32_num_pseudo_children;
     while (--child >= 0) {
        if ((int)w32_pseudo_child_pids[child] == pid)
@@ -1262,8 +1273,9 @@ terminate_process(DWORD pid, HANDLE process_handle, int sig)
     return 0;
 }
 
-int
-killpg(int pid, int sig)
+/* returns number of processes killed */
+static int
+my_killpg(int pid, int sig)
 {
     HANDLE process_handle;
     HANDLE snapshot_handle;
@@ -1283,7 +1295,7 @@ killpg(int pid, int sig)
         if (Process32First(snapshot_handle, &entry)) {
             do {
                 if (entry.th32ParentProcessID == (DWORD)pid)
-                    killed += killpg(entry.th32ProcessID, sig);
+                    killed += my_killpg(entry.th32ProcessID, sig);
                 entry.dwSize = sizeof(entry);
             }
             while (Process32Next(snapshot_handle, &entry));
@@ -1294,6 +1306,7 @@ killpg(int pid, int sig)
     return killed;
 }
 
+/* returns number of processes killed */
 static int
 my_kill(int pid, int sig)
 {
@@ -1301,7 +1314,7 @@ my_kill(int pid, int sig)
     HANDLE process_handle;
 
     if (sig < 0)
-        return killpg(pid, -sig);
+        return my_killpg(pid, -sig);
 
     process_handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
     /* OpenProcess() returns NULL on error, *not* INVALID_HANDLE_VALUE */
@@ -1364,7 +1377,7 @@ win32_kill(int pid, int sig)
 #ifdef USE_ITHREADS
     if (pid < 0) {
        /* it is a pseudo-forked child */
-       child = find_pseudo_pid(-pid);
+       child = find_pseudo_pid(aTHX_ -pid);
        if (child >= 0) {
            HANDLE hProcess = w32_pseudo_child_handles[child];
            switch (sig) {
@@ -1419,7 +1432,7 @@ win32_kill(int pid, int sig)
     else
 #endif
     {
-       child = find_pid(pid);
+       child = find_pid(aTHX_ pid);
        if (child >= 0) {
             if (my_kill(pid, sig)) {
                 DWORD exitcode = 0;
@@ -1443,17 +1456,13 @@ win32_kill(int pid, int sig)
 DllExport int
 win32_stat(const char *path, Stat_t *sbuf)
 {
-    dTHX;
     char       buffer[MAX_PATH+1];
     int                l = strlen(path);
+    dTHX;
     int                res;
     int         nlink = 1;
     BOOL        expect_dir = FALSE;
 
-    GV          *gv_sloppy = gv_fetchpvs("\027IN32_SLOPPY_STAT",
-                                         GV_NOTQUAL, SVt_PV);
-    BOOL        sloppy = gv_sloppy && SvTRUE(GvSV(gv_sloppy));
-
     if (l > 1) {
        switch(path[l - 1]) {
        /* FindFirstFile() and stat() are buggy with a trailing
@@ -1494,7 +1503,7 @@ win32_stat(const char *path, Stat_t *sbuf)
     path = PerlDir_mapA(path);
     l = strlen(path);
 
-    if (!sloppy) {
+    if (!w32_sloppystat) {
         /* We must open & close the file once; otherwise file attribute changes  */
         /* might not yet have propagated to "other" hard links of the same file. */
         /* This also gives us an opportunity to determine the number of links.   */
@@ -1505,6 +1514,14 @@ win32_stat(const char *path, Stat_t *sbuf)
                 nlink = bhi.nNumberOfLinks;
             CloseHandle(handle);
         }
+       else {
+           DWORD err = GetLastError();
+           /* very common case, skip CRT stat and its also failing syscalls */
+           if(err == ERROR_FILE_NOT_FOUND) {
+               errno = ENOENT;
+               return -1;
+           }
+       }
     }
 
     /* path will be mapped correctly above */
@@ -1666,16 +1683,19 @@ win32_longpath(char *path)
 static void
 out_of_memory(void)
 {
-    if (PL_curinterp) {
-        dTHX;
-        /* Can't use PerlIO to write as it allocates memory */
-        PerlLIO_write(PerlIO_fileno(Perl_error_log),
-                      PL_no_mem, strlen(PL_no_mem));
-        my_exit(1);
-    }
+    if (PL_curinterp)
+       croak_no_mem();
     exit(1);
 }
 
+void
+win32_croak_not_implemented(const char * fname)
+{
+    PERL_ARGS_ASSERT_WIN32_CROAK_NOT_IMPLEMENTED;
+
+    Perl_croak_nocontext("%s not implemented!\n", fname);
+}
+
 /* Converts a wide character (UTF-16) string to the Windows ANSI code page,
  * potentially using the system's default replacement character for any
  * unrepresentable characters. The caller must free() the returned string. */
@@ -1686,7 +1706,7 @@ wstr_to_str(const wchar_t* wstr)
     size_t wlen = wcslen(wstr) + 1;
     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen,
                                    NULL, 0, NULL, NULL);
-    char* str = malloc(len);
+    char* str = (char*)malloc(len);
     if (!str)
         out_of_memory();
     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wstr, wlen,
@@ -1719,7 +1739,7 @@ win32_ansipath(const WCHAR *widename)
     size_t widelen = wcslen(widename)+1;
     int len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, widename, widelen,
                                   NULL, 0, NULL, NULL);
-    name = win32_malloc(len);
+    name = (char*)win32_malloc(len);
     if (!name)
         out_of_memory();
 
@@ -1728,14 +1748,14 @@ win32_ansipath(const WCHAR *widename)
     if (use_default) {
         DWORD shortlen = GetShortPathNameW(widename, NULL, 0);
         if (shortlen) {
-            WCHAR *shortname = win32_malloc(shortlen*sizeof(WCHAR));
+            WCHAR *shortname = (WCHAR*)win32_malloc(shortlen*sizeof(WCHAR));
             if (!shortname)
                 out_of_memory();
             shortlen = GetShortPathNameW(widename, shortname, shortlen)+1;
 
             len = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
                                       NULL, 0, NULL, NULL);
-            name = win32_realloc(name, len);
+            name = (char*)win32_realloc(name, len);
             if (!name)
                 out_of_memory();
             WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, shortname, shortlen,
@@ -1746,6 +1766,10 @@ win32_ansipath(const WCHAR *widename)
     return name;
 }
 
+/* the returned string must be freed with win32_freeenvironmentstrings which is
+ * implemented as a macro
+ * void win32_freeenvironmentstrings(void* block)
+ */
 DllExport char *
 win32_getenvironmentstrings(void)
 {
@@ -1772,13 +1796,9 @@ win32_getenvironmentstrings(void)
     WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, lpWStr, wenvstrings_len, lpStr, 
                         aenvstrings_len, NULL, NULL);
 
-    return(lpStr);
-}
+    FreeEnvironmentStringsW(lpWStr);
 
-DllExport void
-win32_freeenvironmentstrings(void* block)
-{
-    win32_free(block);
+    return(lpStr);
 }
 
 DllExport char *
@@ -1791,7 +1811,7 @@ win32_getenv(const char *name)
 
     needlen = GetEnvironmentVariableA(name,NULL,0);
     if (needlen != 0) {
-       curitem = sv_2mortal(newSVpvn("", 0));
+       curitem = sv_2mortal(newSVpvs(""));
         do {
             SvGROW(curitem, needlen+1);
             needlen = GetEnvironmentVariableA(name,SvPVX(curitem),
@@ -1825,12 +1845,14 @@ win32_getenv(const char *name)
            }
            FreeEnvironmentStrings(envv);
        }
+#ifndef WIN32_NO_REGISTRY
        else {
            /* last ditch: allow any environment variables that begin with 'PERL'
               to be obtained from the registry, if found there */
            if (strncmp(name, "PERL", 4) == 0)
                (void)get_regstr(name, &curitem);
        }
+#endif
     }
     if (curitem && SvCUR(curitem))
        return SvPVX(curitem);
@@ -1841,13 +1863,12 @@ win32_getenv(const char *name)
 DllExport int
 win32_putenv(const char *name)
 {
-    dTHX;
     char* curitem;
     char* val;
     int relval = -1;
 
     if (name) {
-        Newx(curitem,strlen(name)+1,char);
+        curitem = (char *) win32_malloc(strlen(name)+1);
         strcpy(curitem, name);
         val = strchr(curitem, '=');
         if (val) {
@@ -1871,7 +1892,7 @@ win32_putenv(const char *name)
             if (SetEnvironmentVariableA(curitem, *val ? val : NULL))
                 relval = 0;
         }
-        Safefree(curitem);
+        win32_free(curitem);
     }
     return relval;
 }
@@ -2147,7 +2168,7 @@ do_raise(pTHX_ int sig)
            }
        }
     }
-    /* Tell caller to exit thread/process as approriate */
+    /* Tell caller to exit thread/process as appropriate */
     return 1;
 }
 
@@ -2233,7 +2254,7 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result
      * This scenario can only be created if the timespan from the return of
      * MsgWaitForMultipleObjects to GetSystemTimeAsFileTime exceeds 1 ms. To
      * generate the scenario, manual breakpoints in a C debugger are required,
-     * or a context switch occured in win32_async_check in PeekMessage, or random
+     * or a context switch occurred in win32_async_check in PeekMessage, or random
      * messages are delivered to the *thread* message queue of the Perl thread
      * from another process (msctf.dll doing IPC among its instances, VS debugger
      * causes msctf.dll to be loaded into Perl by kernel), see [perl #33096].
@@ -2275,12 +2296,11 @@ win32_msgwait(pTHX_ DWORD count, LPHANDLE handles, DWORD timeout, LPDWORD result
 }
 
 int
-win32_internal_wait(int *status, DWORD timeout)
+win32_internal_wait(pTHX_ int *status, DWORD timeout)
 {
     /* XXX this wait emulation only knows about processes
      * spawned via win32_spawnvp(P_NOWAIT, ...).
      */
-    dTHX;
     int i, retval;
     DWORD exitcode, waitcode;
 
@@ -2346,10 +2366,10 @@ win32_waitpid(int pid, int *status, int flags)
     int retval = -1;
     long child;
     if (pid == -1)                             /* XXX threadid == 1 ? */
-       return win32_internal_wait(status, timeout);
+       return win32_internal_wait(aTHX_ status, timeout);
 #ifdef USE_ITHREADS
     else if (pid < 0) {
-       child = find_pseudo_pid(-pid);
+       child = find_pseudo_pid(aTHX_ -pid);
        if (child >= 0) {
            HANDLE hThread = w32_pseudo_child_handles[child];
            DWORD waitcode;
@@ -2373,7 +2393,7 @@ win32_waitpid(int pid, int *status, int flags)
     else {
        HANDLE hProcess;
        DWORD waitcode;
-       child = find_pid(pid);
+       child = find_pid(aTHX_ pid);
        if (child >= 0) {
            hProcess = w32_child_handles[child];
            win32_msgwait(aTHX_ 1, &hProcess, timeout, &waitcode);
@@ -2418,7 +2438,8 @@ win32_waitpid(int pid, int *status, int flags)
 DllExport int
 win32_wait(int *status)
 {
-    return win32_internal_wait(status, INFINITE);
+    dTHX;
+    return win32_internal_wait(aTHX_ status, INFINITE);
 }
 
 DllExport unsigned int
@@ -2426,7 +2447,11 @@ win32_sleep(unsigned int t)
 {
     dTHX;
     /* Win32 times are in ms so *1000 in and /1000 out */
-    return win32_msgwait(aTHX_ 0, NULL, t*1000, NULL)/1000;
+    if (t > UINT_MAX / 1000) {
+       Perl_ck_warner(aTHX_ packWARN(WARN_OVERFLOW),
+                       "sleep(%lu) too large", t);
+    }
+    return win32_msgwait(aTHX_ 0, NULL, t * 1000, NULL) / 1000;
 }
 
 DllExport unsigned int
@@ -2514,7 +2539,7 @@ win32_flock(int fd, int oper)
     }
     if (i == -1) {
         if (GetLastError() == ERROR_LOCK_VIOLATION)
-            errno = WSAEWOULDBLOCK;
+            errno = EWOULDBLOCK;
         else
             errno = EINVAL;
     }
@@ -2523,6 +2548,21 @@ win32_flock(int fd, int oper)
 
 #undef LK_LEN
 
+extern int convert_wsa_error_to_errno(int wsaerr); /* in win32sck.c */
+
+/* Get the errno value corresponding to the given err. This function is not
+ * intended to handle conversion of general GetLastError() codes. It only exists
+ * to translate Windows sockets error codes from WSAGetLastError(). Such codes
+ * used to be assigned to errno/$! in earlier versions of perl; this function is
+ * used to catch any old Perl code which is still trying to assign such values
+ * to $! and convert them to errno values instead.
+ */
+int
+win32_get_errno(int err)
+{
+    return convert_wsa_error_to_errno(err);
+}
+
 /*
  *  redirected io subsystem for all XS modules
  *
@@ -2572,10 +2612,16 @@ win32_feof(FILE *fp)
     return (feof(fp));
 }
 
+#ifdef ERRNO_HAS_POSIX_SUPPLEMENT
+extern int convert_errno_to_wsa_error(int err); /* in win32sck.c */
+#endif
+
 /*
  * Since the errors returned by the socket error function
  * WSAGetLastError() are not known by the library routine strerror
- * we have to roll our own.
+ * we have to roll our own to cover the case of socket errors
+ * that could not be converted to regular errno values by
+ * get_last_socket_error() in win32/win32sck.c.
  */
 
 DllExport char *
@@ -2586,10 +2632,23 @@ win32_strerror(int e)
 #endif
 
     if (e < 0 || e > sys_nerr) {
-        dTHX;
+        dTHXa(NULL);
        if (e < 0)
            e = GetLastError();
+#ifdef ERRNO_HAS_POSIX_SUPPLEMENT
+       /* VC10+ and some MinGW/gcc-4.8+ define a "POSIX supplement" of errno
+        * values ranging from EADDRINUSE (100) to EWOULDBLOCK (140), but
+        * sys_nerr is still 43 and strerror() returns "Unknown error" for them.
+        * We must therefore still roll our own messages for these codes, and
+        * additionally map them to corresponding Windows (sockets) error codes
+        * first to avoid getting the wrong system message.
+        */
+       else if (e >= EADDRINUSE && e <= EWOULDBLOCK) {
+           e = convert_errno_to_wsa_error(e);
+       }
+#endif
 
+       aTHXa(PERL_GET_THX);
        if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
                          |FORMAT_MESSAGE_IGNORE_INSERTS, NULL, e, 0,
                          w32_strerror_buffer, sizeof(w32_strerror_buffer),
@@ -2683,7 +2742,7 @@ win32_fwrite(const void *buf, size_t size, size_t count, FILE *fp)
 DllExport FILE *
 win32_fopen(const char *filename, const char *mode)
 {
-    dTHX;
+    dTHXa(NULL);
     FILE *f;
 
     if (!*filename)
@@ -2692,6 +2751,7 @@ win32_fopen(const char *filename, const char *mode)
     if (stricmp(filename, "/dev/null")==0)
        filename = "NUL";
 
+    aTHXa(PERL_GET_THX);
     f = fopen(PerlDir_mapA(filename), mode);
     /* avoid buffering headaches for child processes */
     if (f && *mode == 'a')
@@ -2699,15 +2759,9 @@ win32_fopen(const char *filename, const char *mode)
     return f;
 }
 
-#ifndef USE_SOCKETS_AS_HANDLES
-#undef fdopen
-#define fdopen my_fdopen
-#endif
-
 DllExport FILE *
 win32_fdopen(int handle, const char *mode)
 {
-    dTHX;
     FILE *f;
     f = fdopen(handle, (char *) mode);
     /* avoid buffering headaches for child processes */
@@ -2719,17 +2773,22 @@ win32_fdopen(int handle, const char *mode)
 DllExport FILE *
 win32_freopen(const char *path, const char *mode, FILE *stream)
 {
-    dTHX;
+    dTHXa(NULL);
     if (stricmp(path, "/dev/null")==0)
        path = "NUL";
 
+    aTHXa(PERL_GET_THX);
     return freopen(PerlDir_mapA(path), mode, stream);
 }
 
 DllExport int
 win32_fclose(FILE *pf)
 {
+#ifdef WIN32_NO_SOCKETS
+    return fclose(pf);
+#else
     return my_fclose(pf);      /* defined in win32sck.c */
+#endif
 }
 
 DllExport int
@@ -2838,7 +2897,6 @@ win32_rewind(FILE *pf)
 DllExport int
 win32_tmpfd(void)
 {
-    dTHX;
     char prefix[MAX_PATH+1];
     char filename[MAX_PATH+1];
     DWORD len = GetTempPath(MAX_PATH, prefix);
@@ -2855,6 +2913,7 @@ win32_tmpfd(void)
            if (fh != INVALID_HANDLE_VALUE) {
                int fd = win32_open_osfhandle((intptr_t)fh, 0);
                if (fd >= 0) {
+                   PERL_DEB(dTHX;)
                    DEBUG_p(PerlIO_printf(Perl_debug_log,
                                          "Created tmpfile=%s\n",filename));
                    return fd;
@@ -2900,32 +2959,22 @@ win32_pipe(int *pfd, unsigned int size, int mode)
 DllExport PerlIO*
 win32_popenlist(const char *mode, IV narg, SV **args)
 {
- dTHX;
- Perl_croak(aTHX_ "List form of pipe open not implemented");
- return NULL;
-}
+    get_shell();
 
-/*
- * a popen() clone that respects PERL5SHELL
- *
- * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
- */
+    return do_popen(mode, NULL, narg, args);
+}
 
-DllExport PerlIO*
-win32_popen(const char *command, const char *mode)
-{
-#ifdef USE_RTL_POPEN
-    return _popen(command, mode);
-#else
-    dTHX;
+STATIC PerlIO*
+do_popen(const char *mode, const char *command, IV narg, SV **args) {
     int p[2];
+    int handles[3];
     int parent, child;
-    int stdfd, oldfd;
+    int stdfd;
     int ourmode;
     int childpid;
     DWORD nhandle;
-    HANDLE old_h;
     int lock_held = 0;
+    const char **args_pvs = NULL;
 
     /* establish which ends read and write */
     if (strchr(mode,'w')) {
@@ -2957,48 +3006,54 @@ win32_popen(const char *command, const char *mode)
     if (win32_pipe(p, 512, ourmode) == -1)
         return NULL;
 
-    /* save the old std handle (this needs to happen before the
-     * dup2(), since that might call SetStdHandle() too) */
-    OP_REFCNT_LOCK;
-    lock_held = 1;
-    old_h = GetStdHandle(nhandle);
+    /* Previously this code redirected stdin/out temporarily so the
+       child process inherited those handles, this caused race
+       conditions when another thread was writing/reading those
+       handles.
 
-    /* save current stdfd */
-    if ((oldfd = win32_dup(stdfd)) == -1)
-        goto cleanup;
+       To avoid that we just feed the handles to CreateProcess() so
+       the handles are redirected only in the child.
+     */
+    handles[child] = p[child];
+    handles[parent] = -1;
+    handles[2] = -1;
 
-    /* make stdfd go to child end of pipe (implicitly closes stdfd) */
-    /* stdfd will be inherited by the child */
-    if (win32_dup2(p[child], stdfd) == -1)
+    /* CreateProcess() requires inheritable handles */
+    if (!SetHandleInformation((HANDLE)_get_osfhandle(p[child]), HANDLE_FLAG_INHERIT,
+                             HANDLE_FLAG_INHERIT)) {
         goto cleanup;
-
-    /* close the child end in parent */
-    win32_close(p[child]);
-
-    /* set the new std handle (in case dup2() above didn't) */
-    SetStdHandle(nhandle, (HANDLE)_get_osfhandle(stdfd));
+    }
 
     /* start the child */
     {
        dTHX;
-       if ((childpid = do_spawn_nowait((char*)command)) == -1)
-           goto cleanup;
-
-       /* revert stdfd to whatever it was before */
-       if (win32_dup2(oldfd, stdfd) == -1)
-           goto cleanup;
-
-       /* close saved handle */
-       win32_close(oldfd);
-
-       /* restore the old std handle (this needs to happen after the
-        * dup2(), since that might call SetStdHandle() too */
-       if (lock_held) {
-           SetStdHandle(nhandle, old_h);
-           OP_REFCNT_UNLOCK;
-           lock_held = 0;
+
+       if (command) {
+           if ((childpid = do_spawn2_handles(aTHX_ command, EXECF_SPAWN_NOWAIT, handles)) == -1)
+               goto cleanup;
+
+       }
+       else {
+           int i;
+           const char *exe_name;
+
+           Newx(args_pvs, narg + 1 + w32_perlshell_items, const char *);
+           SAVEFREEPV(args_pvs);
+           for (i = 0; i < narg; ++i)
+               args_pvs[i] = SvPV_nolen(args[i]);
+           args_pvs[i] = NULL;
+           exe_name = qualified_path(args_pvs[0], TRUE);
+           if (!exe_name)
+               /* let CreateProcess() try to find it instead */
+               exe_name = args_pvs[0];
+
+           if ((childpid = do_spawnvp_handles(P_NOWAIT, exe_name, args_pvs, handles)) == -1) {
+               goto cleanup;
+           }
        }
 
+       win32_close(p[child]);
+
        sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
 
        /* set process id so that it can be returned by perl's open() */
@@ -3012,17 +3067,23 @@ cleanup:
     /* we don't need to check for errors here */
     win32_close(p[0]);
     win32_close(p[1]);
-    if (oldfd != -1) {
-        win32_dup2(oldfd, stdfd);
-        win32_close(oldfd);
-    }
-    if (lock_held) {
-       SetStdHandle(nhandle, old_h);
-       OP_REFCNT_UNLOCK;
-       lock_held = 0;
-    }
+
     return (NULL);
+}
+
+/*
+ * a popen() clone that respects PERL5SHELL
+ *
+ * changed to return PerlIO* rather than FILE * by BKS, 11-11-2000
+ */
 
+DllExport PerlIO*
+win32_popen(const char *command, const char *mode)
+{
+#ifdef USE_RTL_POPEN
+    return _popen(command, mode);
+#else
+    return do_popen(mode, command, 0, NULL);
 #endif /* USE_RTL_POPEN */
 }
 
@@ -3070,13 +3131,13 @@ win32_pclose(PerlIO *pf)
 DllExport int
 win32_link(const char *oldname, const char *newname)
 {
-    dTHX;
+    dTHXa(NULL);
     WCHAR wOldName[MAX_PATH+1];
     WCHAR wNewName[MAX_PATH+1];
 
     if (MultiByteToWideChar(CP_ACP, 0, oldname, -1, wOldName, MAX_PATH+1) &&
         MultiByteToWideChar(CP_ACP, 0, newname, -1, wNewName, MAX_PATH+1) &&
-       (wcscpy(wOldName, PerlDir_mapW(wOldName)),
+       ((aTHXa(PERL_GET_THX)), wcscpy(wOldName, PerlDir_mapW(wOldName)),
         CreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL)))
     {
        return 0;
@@ -3107,6 +3168,12 @@ win32_link(const char *oldname, const char *newname)
     case ERROR_NOT_SAME_DEVICE:
       errno = EXDEV;
       break;
+    case ERROR_DISK_FULL:
+      errno = ENOSPC;
+      break;
+    case ERROR_NOT_ENOUGH_QUOTA:
+      errno = EDQUOT;
+      break;
     default:
       /* ERROR_INVALID_FUNCTION - eg. on a FAT volume */
       errno = EINVAL;
@@ -3141,6 +3208,12 @@ win32_rename(const char *oname, const char *newname)
         case ERROR_PATH_NOT_FOUND:
             errno = ENOENT;
             break;
+        case ERROR_DISK_FULL:
+            errno = ENOSPC;
+            break;
+        case ERROR_NOT_ENOUGH_QUOTA:
+            errno = EDQUOT;
+            break;
         default:
             errno = EACCES;
             break;
@@ -3197,7 +3270,6 @@ win32_chsize(int fd, Off_t size)
            retval = -1;
        }
     }
-finish:
     win32_lseek(fd, cur, SEEK_SET);
     return retval;
 #else
@@ -3228,7 +3300,7 @@ win32_tell(int fd)
 DllExport int
 win32_open(const char *path, int flag, ...)
 {
-    dTHX;
+    dTHXa(NULL);
     va_list ap;
     int pmode;
 
@@ -3239,6 +3311,7 @@ win32_open(const char *path, int flag, ...)
     if (stricmp(path, "/dev/null")==0)
        path = "NUL";
 
+    aTHXa(PERL_GET_THX);
     return open(PerlDir_mapA(path), flag, pmode);
 }
 
@@ -3248,7 +3321,11 @@ extern int my_close(int);        /* in win32sck.c */
 DllExport int
 win32_close(int fd)
 {
+#ifdef WIN32_NO_SOCKETS
+    return close(fd);
+#else
     return my_close(fd);
+#endif
 }
 
 DllExport int
@@ -3319,8 +3396,7 @@ win32_rmdir(const char *dir)
 DllExport int
 win32_chdir(const char *dir)
 {
-    dTHX;
-    if (!dir) {
+    if (!dir || !*dir) {
        errno = ENOENT;
        return -1;
     }
@@ -3345,7 +3421,7 @@ win32_chmod(const char *path, int mode)
 static char *
 create_command_line(char *cname, STRLEN clen, const char * const *args)
 {
-    dTHX;
+    PERL_DEB(dTHX;)
     int index, argc;
     char *cmd, *ptr;
     const char *arg;
@@ -3497,10 +3573,16 @@ create_command_line(char *cname, STRLEN clen, const char * const *args)
     return cmd;
 }
 
+static const char *exe_extensions[] =
+  {
+    ".exe", /* this must be first */
+    ".cmd",
+    ".bat"
+  };
+
 static char *
-qualified_path(const char *cmd)
+qualified_path(const char *cmd, bool other_exts)
 {
-    dTHX;
     char *pathstr;
     char *fullcmd, *curfullcmd;
     STRLEN cmdlen = 0;
@@ -3517,8 +3599,10 @@ qualified_path(const char *cmd)
     }
 
     /* look in PATH */
-    pathstr = PerlEnv_getenv("PATH");
-
+    {
+       dTHX;
+       pathstr = PerlEnv_getenv("PATH");
+    }
     /* worst case: PATH is a single directory; we need additional space
      * to append "/", ".exe" and trailing "\0" */
     Newx(fullcmd, (pathstr ? strlen(pathstr) : 0) + cmdlen + 6, char);
@@ -3536,10 +3620,16 @@ qualified_path(const char *cmd)
        if (cmd[cmdlen-1] != '.'
            && (cmdlen < 4 || cmd[cmdlen-4] != '.'))
        {
-           strcpy(curfullcmd, ".exe");
-           res = GetFileAttributes(fullcmd);
-           if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
-               return fullcmd;
+           int i;
+           /* first extension is .exe */
+           int ext_limit = other_exts ? C_ARRAY_LENGTH(exe_extensions) : 1;
+           for (i = 0; i < ext_limit; ++i) {
+               strcpy(curfullcmd, exe_extensions[i]);
+               res = GetFileAttributes(fullcmd);
+               if (res != 0xFFFFFFFF && !(res & FILE_ATTRIBUTE_DIRECTORY))
+                   return fullcmd;
+           }
+
            *curfullcmd = '\0';
        }
 
@@ -3624,7 +3714,6 @@ win32_clearenv(void)
 DllExport char*
 win32_get_childdir(void)
 {
-    dTHX;
     char* ptr;
     char szfilename[MAX_PATH+1];
 
@@ -3637,7 +3726,6 @@ win32_get_childdir(void)
 DllExport void
 win32_free_childdir(char* d)
 {
-    dTHX;
     Safefree(d);
 }
 
@@ -3657,9 +3745,16 @@ DllExport int
 win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
 {
 #ifdef USE_RTL_SPAWNVP
-    return spawnvp(mode, cmdname, (char * const *)argv);
+    return _spawnvp(mode, cmdname, (char * const *)argv);
 #else
-    dTHX;
+    return do_spawnvp_handles(mode, cmdname, argv, NULL);
+#endif
+}
+
+static int
+do_spawnvp_handles(int mode, const char *cmdname, const char *const *argv,
+                const int *handles) {
+    dTHXa(NULL);
     int ret;
     void* env;
     char* dir;
@@ -3692,6 +3787,7 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
 
     cmd = create_command_line(cname, clen, argv);
 
+    aTHXa(PERL_GET_THX);
     env = PerlEnv_get_childenv();
     dir = PerlEnv_get_childdir();
 
@@ -3715,6 +3811,7 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
        ret = -1;
        goto RETVAL;
     }
+
     memset(&StartupInfo,0,sizeof(StartupInfo));
     StartupInfo.cb = sizeof(StartupInfo);
     memset(&tbl,0,sizeof(tbl));
@@ -3728,9 +3825,12 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
     StartupInfo.dwYCountChars  = tbl.dwYCountChars;
     StartupInfo.dwFillAttribute        = tbl.dwFillAttribute;
     StartupInfo.wShowWindow    = tbl.wShowWindow;
-    StartupInfo.hStdInput      = tbl.childStdIn;
-    StartupInfo.hStdOutput     = tbl.childStdOut;
-    StartupInfo.hStdError      = tbl.childStdErr;
+    StartupInfo.hStdInput      = handles && handles[0] != -1 ?
+            (HANDLE)_get_osfhandle(handles[0]) : tbl.childStdIn;
+    StartupInfo.hStdOutput     = handles && handles[1] != -1 ?
+            (HANDLE)_get_osfhandle(handles[1]) : tbl.childStdOut;
+    StartupInfo.hStdError      = handles && handles[2] != -1 ?
+           (HANDLE)_get_osfhandle(handles[2]) : tbl.childStdErr;
     if (StartupInfo.hStdInput == INVALID_HANDLE_VALUE &&
        StartupInfo.hStdOutput == INVALID_HANDLE_VALUE &&
        StartupInfo.hStdError == INVALID_HANDLE_VALUE)
@@ -3766,7 +3866,7 @@ RETRY:
         * jump through our own hoops by picking out the path
         * we really want it to use. */
        if (!fullcmd) {
-           fullcmd = qualified_path(cname);
+           fullcmd = qualified_path(cname, FALSE);
            if (fullcmd) {
                if (cname != cmdname)
                    Safefree(cname);
@@ -3810,7 +3910,6 @@ RETVAL:
     if (cname != cmdname)
        Safefree(cname);
     return ret;
-#endif
 }
 
 DllExport int
@@ -3821,9 +3920,9 @@ win32_execv(const char *cmdname, const char *const *argv)
     /* if this is a pseudo-forked child, we just want to spawn
      * the new program, and return */
     if (w32_pseudo_id)
-       return spawnv(P_WAIT, cmdname, argv);
+       return _spawnv(P_WAIT, cmdname, argv);
 #endif
-    return execv(cmdname, argv);
+    return _execv(cmdname, argv);
 }
 
 DllExport int
@@ -3843,7 +3942,7 @@ win32_execvp(const char *cmdname, const char *const *argv)
            return status;
     }
 #endif
-    return execvp(cmdname, argv);
+    return _execvp(cmdname, argv);
 }
 
 DllExport void
@@ -4092,9 +4191,9 @@ win32_fdupopen(FILE *pf)
 DllExport void*
 win32_dynaload(const char* filename)
 {
-    dTHX;
+    dTHXa(NULL);
     char buf[MAX_PATH+1];
-    char *first;
+    const char *first;
 
     /* LoadLibrary() doesn't recognize forward slashes correctly,
      * so turn 'em back. */
@@ -4112,6 +4211,7 @@ win32_dynaload(const char* filename)
            filename = buf;
        }
     }
+    aTHXa(PERL_GET_THX);
     return LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
 }
 
@@ -4123,7 +4223,7 @@ XS(w32_SetChildShowWindow)
     unsigned short showwindow = w32_showwindow;
 
     if (items > 1)
-       Perl_croak(aTHX_ "usage: Win32::SetChildShowWindow($showwindow)");
+       croak_xs_usage(cv, "[showwindow]");
 
     if (items == 0 || !SvOK(ST(0)))
         w32_use_showwindow = FALSE;
@@ -4140,19 +4240,59 @@ XS(w32_SetChildShowWindow)
     XSRETURN(1);
 }
 
+
+#ifdef PERL_IS_MINIPERL
+/* shelling out is much slower, full perl uses Win32.pm */
+XS(w32_GetCwd)
+{
+    dXSARGS;
+    /* Make the host for current directory */
+    char* ptr = PerlEnv_get_childdir();
+    /*
+     * If ptr != Nullch
+     *   then it worked, set PV valid,
+     *   else return 'undef'
+     */
+    if (ptr) {
+       SV *sv = sv_newmortal();
+       sv_setpv(sv, ptr);
+       PerlEnv_free_childdir(ptr);
+
+#ifndef INCOMPLETE_TAINTS
+       SvTAINTED_on(sv);
+#endif
+
+       ST(0) = sv;
+       XSRETURN(1);
+    }
+    XSRETURN_UNDEF;
+}
+#endif
+
 void
 Perl_init_os_extras(void)
 {
-    dTHX;
+    dTHXa(NULL);
     char *file = __FILE__;
 
     /* Initialize Win32CORE if it has been statically linked. */
+#ifndef PERL_IS_MINIPERL
     void (*pfn_init)(pTHX);
-    pfn_init = (void (*)(pTHX))GetProcAddress((HMODULE)w32_perldll_handle, "init_Win32CORE");
+    HMODULE module = (HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
+                               ? GetModuleHandle(NULL)
+                               : w32_perldll_handle);
+    pfn_init = (void (*)(pTHX))GetProcAddress(module, "init_Win32CORE");
+    aTHXa(PERL_GET_THX);
     if (pfn_init)
         pfn_init(aTHX);
+#else
+    aTHXa(PERL_GET_THX);
+#endif
 
     newXS("Win32::SetChildShowWindow", w32_SetChildShowWindow, file);
+#ifdef PERL_IS_MINIPERL
+    newXS("Win32::GetCwd", w32_GetCwd, file);
+#endif
 }
 
 void *
@@ -4238,13 +4378,17 @@ ansify_path(void)
 
     /* fetch Unicode version of PATH */
     len = 2000;
-    wide_path = win32_malloc(len*sizeof(WCHAR));
+    wide_path = (WCHAR*)win32_malloc(len*sizeof(WCHAR));
     while (wide_path) {
         size_t newlen = GetEnvironmentVariableW(L"PATH", wide_path, len);
+        if (newlen == 0) {
+            win32_free(wide_path);
+            return;
+        }
         if (newlen < len)
             break;
         len = newlen;
-        wide_path = win32_realloc(wide_path, len*sizeof(WCHAR));
+        wide_path = (WCHAR*)win32_realloc(wide_path, len*sizeof(WCHAR));
     }
     if (!wide_path)
         return;
@@ -4273,7 +4417,7 @@ ansify_path(void)
         ansi_len = strlen(ansi_dir);
         if (ansi_path) {
             size_t newlen = len + 1 + ansi_len;
-            ansi_path = win32_realloc(ansi_path, newlen+1);
+            ansi_path = (char*)win32_realloc(ansi_path, newlen+1);
             if (!ansi_path)
                 break;
             ansi_path[len] = ';';
@@ -4282,7 +4426,7 @@ ansify_path(void)
         }
         else {
             len = ansi_len;
-            ansi_path = win32_malloc(5+len+1);
+            ansi_path = (char*)win32_malloc(5+len+1);
             if (!ansi_path)
                 break;
             memcpy(ansi_path, "PATH=", 5);
@@ -4311,13 +4455,7 @@ ansify_path(void)
          * will not call mg_set() if it initializes %ENV from `environ`.
          */
         SetEnvironmentVariableA("PATH", ansi_path+5);
-        /* We are intentionally leaking the ansi_path string here because
-         * the some runtime libraries puts it directly into the environ
-         * array.  The Microsoft runtime library seems to make a copy,
-         * but will leak the copy should it be replaced again later.
-         * Since this code is only called once during PERL_SYS_INIT this
-         * shouldn't really matter.
-         */
+        win32_free(ansi_path);
     }
     win32_free(wide_path);
 }
@@ -4355,17 +4493,49 @@ Perl_win32_init(int *argcp, char ***argvp)
     g_osver.dwOSVersionInfoSize = sizeof(g_osver);
     GetVersionEx(&g_osver);
 
+#ifdef WIN32_DYN_IOINFO_SIZE
+    {
+       Size_t ioinfo_size = _msize((void*)__pioinfo[0]);;
+       if((SSize_t)ioinfo_size <= 0) { /* -1 is err */
+           fprintf(stderr, "panic: invalid size for ioinfo\n"); /* no interp */
+           exit(1);
+       }
+       ioinfo_size /= IOINFO_ARRAY_ELTS;
+       w32_ioinfo_size = ioinfo_size;
+    }
+#endif
+
     ansify_path();
+
+#ifndef WIN32_NO_REGISTRY
+    {
+       LONG retval;
+       retval = RegOpenKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\Perl", 0, KEY_READ, &HKCU_Perl_hnd);
+       if (retval != ERROR_SUCCESS) {
+           HKCU_Perl_hnd = NULL;
+       }
+       retval = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Perl", 0, KEY_READ, &HKLM_Perl_hnd);
+       if (retval != ERROR_SUCCESS) {
+           HKLM_Perl_hnd = NULL;
+       }
+    }
+#endif
 }
 
 void
 Perl_win32_term(void)
 {
-    dTHX;
     HINTS_REFCNT_TERM;
     OP_REFCNT_TERM;
     PERLIO_TERM;
     MALLOC_TERM;
+#ifndef WIN32_NO_REGISTRY
+    /* handles might be NULL, RegCloseKey then returns ERROR_INVALID_HANDLE
+       but no point of checking and we can't die() at this point */
+    RegCloseKey(HKLM_Perl_hnd);
+    RegCloseKey(HKCU_Perl_hnd);
+    /* the handles are in an undefined state until the next PERL_SYS_INIT3 */
+#endif
 }
 
 void
@@ -4379,7 +4549,7 @@ win32_get_child_IO(child_IO_table* ptbl)
 Sighandler_t
 win32_signal(int sig, Sighandler_t subcode)
 {
-    dTHX;
+    dTHXa(NULL);
     if (sig < SIG_SIZE) {
        int save_errno = errno;
        Sighandler_t result;
@@ -4392,6 +4562,7 @@ win32_signal(int sig, Sighandler_t subcode)
 #ifdef SET_INVALID_PARAMETER_HANDLER
        set_silent_invalid_parameter_handler(oldvalue);
 #endif
+       aTHXa(PERL_GET_THX);
        if (result == SIG_ERR) {
            result = w32_sighandler[sig];
            errno = save_errno;
@@ -4431,14 +4602,14 @@ win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
      * are relativley infrequent code-paths, is better than the added
      * complexity of getting the correct context passed into
      * win32_create_message_window() */
+    dTHX;
 
     switch(msg) {
 
 #ifdef USE_ITHREADS
         case WM_USER_MESSAGE: {
-            long child = find_pseudo_pid((int)wParam);
+            long child = find_pseudo_pid(aTHX_ (int)wParam);
             if (child >= 0) {
-                dTHX;
                 w32_pseudo_child_message_hwnds[child] = (HWND)lParam;
                 return 1;
             }
@@ -4447,7 +4618,6 @@ win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 #endif
 
         case WM_USER_KILL: {
-            dTHX;
             /* We use WM_USER_KILL to fake kill() with other signals */
             int sig = (int)wParam;
             if (do_raise(aTHX_ sig))
@@ -4457,7 +4627,6 @@ win32_process_message(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
         }
 
         case WM_TIMER: {
-            dTHX;
             /* alarm() is a one-shot but SetTimer() repeats so kill it */
             if (w32_timerid && w32_timerid==(UINT)wParam) {
                 KillTimer(w32_message_hwnd, w32_timerid);
@@ -4548,6 +4717,11 @@ Perl_sys_intern_init(pTHX)
     w32_timerid                 = 0;
     w32_message_hwnd            = CAST_HWND__(INVALID_HANDLE_VALUE);
     w32_poll_count              = 0;
+#ifdef PERL_IS_MINIPERL
+    w32_sloppystat              = TRUE;
+#else
+    w32_sloppystat              = FALSE;
+#endif
     for (i=0; i < SIG_SIZE; i++) {
        w32_sighandler[i] = SIG_DFL;
     }
@@ -4615,6 +4789,7 @@ Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
     dst->timerid                = 0;
     dst->message_hwnd          = CAST_HWND__(INVALID_HANDLE_VALUE);
     dst->poll_count             = 0;
+    dst->sloppystat             = src->sloppystat;
     Copy(src->sigtable,dst->sigtable,SIG_SIZE,Sighandler_t);
 }
 #  endif /* USE_ITHREADS */