This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
win32_chdir() et al don't handle a NULL argument gracefully
[perl5.git] / win32 / win32.c
index 71097ea..682b133 100644 (file)
 #define Win32_Winsock
 #endif
 #include <windows.h>
+#ifndef __MINGW32__    /* GCC/Mingw32-2.95.2 forgot the WINAPI on CommandLineToArgvW() */
+#  include <shellapi.h>
+#else
+   LPWSTR* WINAPI CommandLineToArgvW(LPCWSTR lpCommandLine, int * pNumArgs);
+#endif
 #include <winnt.h>
 #include <io.h>
 
@@ -48,7 +53,6 @@
 #else
 #include <utime.h>
 #endif
-
 #ifdef __GNUC__
 /* Mingw32 defaults to globing command line 
  * So we turn it off like this:
@@ -57,7 +61,12 @@ int _CRT_glob = 0;
 #endif
 
 #if defined(__MINGW32__)
-#  define _stat stat
+/* Mingw32 is missing some prototypes */
+FILE * _wfopen(LPCWSTR wszFileName, LPCWSTR wszMode);
+FILE * _wfdopen(int nFd, LPCWSTR wszMode);
+FILE * _freopen(LPCWSTR wszFileName, LPCWSTR wszMode, FILE * pOldStream);
+int _flushall();
+int _fcloseall();
 #endif
 
 #if defined(__BORLANDC__)
@@ -74,6 +83,8 @@ int _CRT_glob = 0;
 #  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 do_spawn
 #  define do_spawn g_do_spawn
 #  undef getlogin
@@ -97,6 +108,9 @@ static char *                get_emd_part(SV **leading, char *trailing, ...);
 static void            remove_dead_process(long deceased);
 static long            find_pid(int pid);
 static char *          qualified_path(const char *cmd);
+static char *          win32_get_xlib(const char *pl, const char *xlib,
+                                      const char *libname);
+
 #ifdef USE_ITHREADS
 static void            remove_dead_pseudo_process(long child);
 static long            find_pseudo_pid(int pid);
@@ -123,6 +137,30 @@ IsWinNT(void)
     return (win32_os_id() == VER_PLATFORM_WIN32_NT);
 }
 
+EXTERN_C void
+set_w32_module_name(void)
+{
+    char* ptr;
+    GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
+                               ? GetModuleHandle(NULL)
+                               : w32_perldll_handle),
+                     w32_module_name, sizeof(w32_module_name));
+
+    /* 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);*/
+    (void)win32_longpath(w32_module_name);
+    /*PerlIO_printf(Perl_debug_log, "After  %s\n", w32_module_name);*/
+
+    /* normalize to forward slashes */
+    ptr = w32_module_name;
+    while (*ptr) {
+       if (*ptr == '\\')
+           *ptr = '/';
+       ++ptr;
+    }
+}
+
 /* *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)
@@ -138,7 +176,9 @@ get_regstr_from(HKEY hkey, const char *valuename, SV **svp)
     if (retval == ERROR_SUCCESS) {
        DWORD datalen;
        retval = RegQueryValueEx(handle, valuename, 0, &type, NULL, &datalen);
-       if (retval == ERROR_SUCCESS && type == REG_SZ) {
+       if (retval == ERROR_SUCCESS
+           && (type == REG_SZ || type == REG_EXPAND_SZ))
+       {
            dTHXo;
            if (!*svp)
                *svp = sv_2mortal(newSVpvn("",0));
@@ -175,7 +215,6 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...)
     char *ptr;
     char *optr;
     char *strip;
-    int oldsize, newsize;
     STRLEN baselen;
 
     va_start(ap, trailing_path);
@@ -185,24 +224,7 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...)
     baselen = strlen(base);
 
     if (!*w32_module_name) {
-       GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
-                                   ? GetModuleHandle(NULL)
-                                   : w32_perldll_handle),
-                         w32_module_name, sizeof(w32_module_name));
-
-       /* 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);*/
-       (void)win32_longpath(w32_module_name);
-       /*PerlIO_printf(Perl_debug_log, "After  %s\n", w32_module_name);*/
-
-       /* normalize to forward slashes */
-       ptr = w32_module_name;
-       while (*ptr) {
-           if (*ptr == '\\')
-               *ptr = '/';
-           ++ptr;
-       }
+       set_w32_module_name();
     }
     strcpy(mod_name, w32_module_name);
     ptr = strrchr(mod_name, '/');
@@ -248,7 +270,7 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...)
 }
 
 char *
-win32_get_privlib(char *pl)
+win32_get_privlib(const char *pl)
 {
     dTHXo;
     char *stdlib = "lib";
@@ -264,33 +286,31 @@ win32_get_privlib(char *pl)
     return get_emd_part(&sv, stdlib, ARCHNAME, "bin", Nullch);
 }
 
-char *
-win32_get_sitelib(char *pl)
+static char *
+win32_get_xlib(const char *pl, const char *xlib, const char *libname)
 {
     dTHXo;
-    char *sitelib = "sitelib";
     char regstr[40];
     char pathstr[MAX_PATH+1];
-    DWORD datalen;
-    int len, newsize;
     SV *sv1 = Nullsv;
     SV *sv2 = Nullsv;
 
-    /* $HKCU{"sitelib-$]"} || $HKLM{"sitelib-$]"} . ---; */
-    sprintf(regstr, "%s-%s", sitelib, pl);
+    /* $HKCU{"$xlib-$]"} || $HKLM{"$xlib-$]"} . ---; */
+    sprintf(regstr, "%s-%s", xlib, pl);
     (void)get_regstr(regstr, &sv1);
 
-    /* $sitelib .=
-     * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/$]/lib";  */
-    sprintf(pathstr, "site/%s/lib", pl);
+    /* $xlib .=
+     * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/$]/lib";  */
+    sprintf(pathstr, "%s/%s/lib", libname, pl);
     (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch);
 
-    /* $HKCU{'sitelib'} || $HKLM{'sitelib'} . ---; */
-    (void)get_regstr(sitelib, &sv2);
+    /* $HKCU{$xlib} || $HKLM{$xlib} . ---; */
+    (void)get_regstr(xlib, &sv2);
 
-    /* $sitelib .=
-     * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/lib";  */
-    (void)get_emd_part(&sv2, "site/lib", ARCHNAME, "bin", pl, Nullch);
+    /* $xlib .=
+     * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/$libname/lib";  */
+    sprintf(pathstr, "%s/lib", libname);
+    (void)get_emd_part(&sv2, pathstr, ARCHNAME, "bin", pl, Nullch);
 
     if (!sv1 && !sv2)
        return Nullch;
@@ -305,6 +325,21 @@ win32_get_sitelib(char *pl)
     return SvPVX(sv1);
 }
 
+char *
+win32_get_sitelib(const char *pl)
+{
+    return win32_get_xlib(pl, "sitelib", "site");
+}
+
+#ifndef PERL_VENDORLIB_NAME
+#  define PERL_VENDORLIB_NAME  "vendor"
+#endif
+
+char *
+win32_get_vendorlib(const char *pl)
+{
+    return win32_get_xlib(pl, "vendorlib", PERL_VENDORLIB_NAME);
+}
 
 static BOOL
 has_shell_metachars(char *ptr)
@@ -398,12 +433,19 @@ win32_os_id(void)
 DllExport int
 win32_getpid(void)
 {
+    int pid;
 #ifdef USE_ITHREADS
     dTHXo;
     if (w32_pseudo_id)
        return -((int)w32_pseudo_id);
 #endif
-    return _getpid();
+    pid = _getpid();
+    /* Windows 9x appears to always reports a pid for threads and processes
+     * that has the high bit set. So we treat the lower 31 bits as the
+     * "real" PID for Perl's purposes. */
+    if (IsWin95() && pid < 0)
+       pid = -pid;
+    return pid;
 }
 
 /* Tokenize a string.  Words are null-separated, and the list
@@ -473,7 +515,7 @@ get_shell(void)
         */
        const char* defaultshell = (IsWinNT()
                                    ? "cmd.exe /x/c" : "command.com /c");
-       const char *usershell = getenv("PERL5SHELL");
+       const char *usershell = PerlEnv_getenv("PERL5SHELL");
        w32_perlshell_items = tokenize(usershell ? usershell : defaultshell,
                                       &w32_perlshell_tokens,
                                       &w32_perlshell_vec);
@@ -530,9 +572,12 @@ do_aspawn(void *vreally, void **vmark, void **vsp)
                               (const char* const*)argv);
     }
 
-    if (flag != P_NOWAIT) {
+    if (flag == P_NOWAIT) {
+       if (IsWin95())
+           PL_statusvalue = -1;        /* >16bits hint for pp_system() */
+    }
+    else {
        if (status < 0) {
-           dTHR;
            if (ckWARN(WARN_EXEC))
                Perl_warner(aTHX_ WARN_EXEC, "Can't spawn \"%s\": %s", argv[0], strerror(errno));
            status = 255 * 256;
@@ -619,9 +664,12 @@ do_spawn2(char *cmd, int exectype)
        cmd = argv[0];
        Safefree(argv);
     }
-    if (exectype != EXECF_SPAWN_NOWAIT) {
+    if (exectype == EXECF_SPAWN_NOWAIT) {
+       if (IsWin95())
+           PL_statusvalue = -1;        /* >16bits hint for pp_system() */
+    }
+    else {
        if (status < 0) {
-           dTHR;
            if (ckWARN(WARN_EXEC))
                Perl_warner(aTHX_ WARN_EXEC, "Can't %s \"%s\": %s",
                     (exectype == EXECF_EXEC ? "exec" : "spawn"),
@@ -930,7 +978,7 @@ find_pid(int pid)
     dTHXo;
     long child = w32_num_children;
     while (--child >= 0) {
-       if (w32_child_pids[child] == pid)
+       if ((int)w32_child_pids[child] == pid)
            return child;
     }
     return -1;
@@ -957,7 +1005,7 @@ find_pseudo_pid(int pid)
     dTHXo;
     long child = w32_num_pseudo_children;
     while (--child >= 0) {
-       if (w32_pseudo_child_pids[child] == pid)
+       if ((int)w32_pseudo_child_pids[child] == pid)
            return child;
     }
     return -1;
@@ -983,23 +1031,32 @@ win32_kill(int pid, int sig)
 {
     dTHXo;
     HANDLE hProcess;
+    long child;
 #ifdef USE_ITHREADS
     if (pid < 0) {
        /* it is a pseudo-forked child */
-       long child = find_pseudo_pid(-pid);
+       child = find_pseudo_pid(-pid);
        if (child >= 0) {
+           if (!sig)
+               return 0;
            hProcess = w32_pseudo_child_handles[child];
            if (TerminateThread(hProcess, sig)) {
                remove_dead_pseudo_process(child);
                return 0;
            }
        }
+       else if (IsWin95()) {
+           pid = -pid;
+           goto alien_process;
+       }
     }
     else
 #endif
     {
-       long child = find_pid(pid);
+       child = find_pid(pid);
        if (child >= 0) {
+           if (!sig)
+               return 0;
            hProcess = w32_child_handles[child];
            if (TerminateProcess(hProcess, sig)) {
                remove_dead_process(child);
@@ -1007,10 +1064,16 @@ win32_kill(int pid, int sig)
            }
        }
        else {
-           hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
-           if (hProcess && TerminateProcess(hProcess, sig)) {
-               CloseHandle(hProcess);
-               return 0;
+alien_process:
+           hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE,
+                                  (IsWin95() ? -pid : pid));
+           if (hProcess) {
+               if (!sig)
+                   return 0;
+               if (TerminateProcess(hProcess, sig)) {
+                   CloseHandle(hProcess);
+                   return 0;
+               }
            }
        }
     }
@@ -1132,6 +1195,7 @@ win32_stat(const char *path, struct stat *sbuf)
        if (S_ISDIR(sbuf->st_mode))
            sbuf->st_mode |= S_IWRITE | S_IEXEC;
        else if (S_ISREG(sbuf->st_mode)) {
+           int perms;
            if (l >= 4 && path[l-4] == '.') {
                const char *e = path + l - 3;
                if (strnicmp(e,"exe",3)
@@ -1144,6 +1208,9 @@ win32_stat(const char *path, struct stat *sbuf)
            }
            else
                sbuf->st_mode &= ~S_IEXEC;
+           /* Propagate permissions to _group_ and _others_ */
+           perms = sbuf->st_mode & (S_IREAD|S_IWRITE|S_IEXEC);
+           sbuf->st_mode |= (perms>>3) | (perms>>6);
        }
 #endif
     }
@@ -1227,8 +1294,6 @@ win32_longpath(char *path)
     return path;
 }
 
-#ifndef USE_WIN32_RTL_ENV
-
 DllExport char *
 win32_getenv(const char *name)
 {
@@ -1330,8 +1395,6 @@ win32_putenv(const char *name)
     return relval;
 }
 
-#endif
-
 static long
 filetime_to_clock(PFILETIME ft)
 {
@@ -1561,7 +1624,7 @@ win32_uname(struct utsname *name)
        char *arch;
        GetSystemInfo(&info);
 
-#if defined(__BORLANDC__) || defined(__MINGW32__)
+#if (defined(__BORLANDC__)&&(__BORLANDC__<=0x520)) || defined(__MINGW32__)
        switch (info.u.s.wProcessorArchitecture) {
 #else
        switch (info.wProcessorArchitecture) {
@@ -1586,34 +1649,48 @@ DllExport int
 win32_waitpid(int pid, int *status, int flags)
 {
     dTHXo;
+    DWORD timeout = (flags & WNOHANG) ? 0 : INFINITE;
     int retval = -1;
+    long child;
     if (pid == -1)                             /* XXX threadid == 1 ? */
        return win32_wait(status);
 #ifdef USE_ITHREADS
     else if (pid < 0) {
-       long child = find_pseudo_pid(-pid);
+       child = find_pseudo_pid(-pid);
        if (child >= 0) {
            HANDLE hThread = w32_pseudo_child_handles[child];
-           DWORD waitcode = WaitForSingleObject(hThread, INFINITE);
-           if (waitcode != WAIT_FAILED) {
+           DWORD waitcode = WaitForSingleObject(hThread, timeout);
+           if (waitcode == WAIT_TIMEOUT) {
+               return 0;
+           }
+           else if (waitcode != WAIT_FAILED) {
                if (GetExitCodeThread(hThread, &waitcode)) {
                    *status = (int)((waitcode & 0xff) << 8);
                    retval = (int)w32_pseudo_child_pids[child];
                    remove_dead_pseudo_process(child);
-                   return retval;
+                   return -retval;
                }
            }
            else
                errno = ECHILD;
        }
+       else if (IsWin95()) {
+           pid = -pid;
+           goto alien_process;
+       }
     }
 #endif
     else {
-       long child = find_pid(pid);
+       HANDLE hProcess;
+       DWORD waitcode;
+       child = find_pid(pid);
        if (child >= 0) {
-           HANDLE hProcess = w32_child_handles[child];
-           DWORD waitcode = WaitForSingleObject(hProcess, INFINITE);
-           if (waitcode != WAIT_FAILED) {
+           hProcess = w32_child_handles[child];
+           waitcode = WaitForSingleObject(hProcess, timeout);
+           if (waitcode == WAIT_TIMEOUT) {
+               return 0;
+           }
+           else if (waitcode != WAIT_FAILED) {
                if (GetExitCodeProcess(hProcess, &waitcode)) {
                    *status = (int)((waitcode & 0xff) << 8);
                    retval = (int)w32_child_pids[child];
@@ -1625,12 +1702,25 @@ win32_waitpid(int pid, int *status, int flags)
                errno = ECHILD;
        }
        else {
-           retval = cwait(status, pid, WAIT_CHILD);
-           /* cwait() returns "correctly" on Borland */
-#ifndef __BORLANDC__
-           if (status)
-               *status *= 256;
-#endif
+alien_process:
+           hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE,
+                                  (IsWin95() ? -pid : pid));
+           if (hProcess) {
+               waitcode = WaitForSingleObject(hProcess, timeout);
+               if (waitcode == WAIT_TIMEOUT) {
+                   return 0;
+               }
+               else if (waitcode != WAIT_FAILED) {
+                   if (GetExitCodeProcess(hProcess, &waitcode)) {
+                       *status = (int)((waitcode & 0xff) << 8);
+                       CloseHandle(hProcess);
+                       return pid;
+                   }
+               }
+               CloseHandle(hProcess);
+           }
+           else
+               errno = ECHILD;
        }
     }
     return retval >= 0 ? pid : retval;                
@@ -1662,7 +1752,7 @@ win32_wait(int *status)
                *status = (int)((exitcode & 0xff) << 8);
                retval = (int)w32_pseudo_child_pids[i];
                remove_dead_pseudo_process(i);
-               return retval;
+               return -retval;
            }
        }
     }
@@ -1692,7 +1782,6 @@ win32_wait(int *status)
        }
     }
 
-FAILED:
     errno = GetLastError();
     return -1;
 }
@@ -1706,7 +1795,7 @@ static VOID CALLBACK TimerProc(HWND win, UINT msg, UINT id, DWORD time)
     dTHXo;
     KillTimer(NULL,timerid);
     timerid=0;  
-    sighandler(14);
+    CALL_FPTR(PL_sighandlerp)(14);
 }
 #endif /* !PERL_OBJECT */
 
@@ -1751,7 +1840,6 @@ win32_crypt(const char *txt, const char *salt)
 {
     dTHXo;
 #ifdef HAVE_DES_FCRYPT
-    dTHR;
     return des_fcrypt(txt, salt, w32_crypt_buffer);
 #else
     Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia.");
@@ -1759,53 +1847,6 @@ win32_crypt(const char *txt, const char *salt)
 #endif
 }
 
-/* C doesn't like repeat struct definitions */
-
-#if defined(USE_FIXED_OSFHANDLE) || defined(PERL_MSVCRT_READFIX)
-
-#ifndef _CRTIMP
-#define _CRTIMP __declspec(dllimport)
-#endif
-
-/*
- * Control structure for lowio file handles
- */
-typedef struct {
-    long osfhnd;    /* underlying OS file HANDLE */
-    char osfile;    /* attributes of file (e.g., open in text mode?) */
-    char pipech;    /* one char buffer for handles opened on pipes */
-    int lockinitflag;
-    CRITICAL_SECTION lock;
-} ioinfo;
-
-
-/*
- * Array of arrays of control structures for lowio files.
- */
-EXTERN_C _CRTIMP ioinfo* __pioinfo[];
-
-/*
- * Definition of IOINFO_L2E, the log base 2 of the number of elements in each
- * array of ioinfo structs.
- */
-#define IOINFO_L2E         5
-
-/*
- * Definition of IOINFO_ARRAY_ELTS, the number of elements in ioinfo array
- */
-#define IOINFO_ARRAY_ELTS   (1 << IOINFO_L2E)
-
-/*
- * Access macros for getting at an ioinfo struct and its fields from a
- * file handle
- */
-#define _pioinfo(i) (__pioinfo[(i) >> IOINFO_L2E] + ((i) & (IOINFO_ARRAY_ELTS - 1)))
-#define _osfhnd(i)  (_pioinfo(i)->osfhnd)
-#define _osfile(i)  (_pioinfo(i)->osfile)
-#define _pipech(i)  (_pioinfo(i)->pipech)
-
-#endif
-
 #ifdef USE_FIXED_OSFHANDLE
 
 #define FOPEN                  0x01    /* file handle open */
@@ -1844,10 +1885,6 @@ EXTERN_C _CRTIMP ioinfo* __pioinfo[];
  *     -- BKS, 1-23-2000
 */
 
-/* since we are not doing a dup2(), this works fine */
-
-#define _set_osfhnd(fh, osfh) (void)(_osfhnd(fh) = osfh)
-
 /* create an ioinfo entry, kill its handle, and steal the entry */
 
 static int
@@ -2264,7 +2301,25 @@ win32_abort(void)
 DllExport int
 win32_fstat(int fd,struct stat *sbufptr)
 {
-    return fstat(fd,sbufptr);
+#ifdef __BORLANDC__
+    /* A file designated by filehandle is not shown as accessible
+     * for write operations, probably because it is opened for reading.
+     * --Vadim Konovalov
+     */ 
+    int rc = fstat(fd,sbufptr);
+    BY_HANDLE_FILE_INFORMATION bhfi;
+    if (GetFileInformationByHandle((HANDLE)_get_osfhandle(fd), &bhfi)) {
+        sbufptr->st_mode &= 0xFE00;
+        if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+            sbufptr->st_mode |= (S_IREAD + (S_IREAD >> 3) + (S_IREAD >> 6));
+        else
+            sbufptr->st_mode |= ((S_IREAD|S_IWRITE) + ((S_IREAD|S_IWRITE) >> 3)
+              + ((S_IREAD|S_IWRITE) >> 6));
+    }
+    return rc;
+#else
+    return my_fstat(fd,sbufptr);
+#endif
 }
 
 DllExport int
@@ -2342,7 +2397,9 @@ win32_popen(const char *command, const char *mode)
        /* close saved handle */
        win32_close(oldfd);
 
+       LOCK_FDPID_MUTEX;
        sv_setiv(*av_fetch(w32_fdpid, p[parent], TRUE), childpid);
+       UNLOCK_FDPID_MUTEX;
 
        /* set process id so that it can be returned by perl's open() */
        PL_forkprocess = childpid;
@@ -2378,7 +2435,9 @@ win32_pclose(FILE *pf)
     int childpid, status;
     SV *sv;
 
+    LOCK_FDPID_MUTEX;
     sv = *av_fetch(w32_fdpid, win32_fileno(pf), TRUE);
+
     if (SvIOK(sv))
        childpid = SvIVX(sv);
     else
@@ -2391,6 +2450,7 @@ win32_pclose(FILE *pf)
 
     win32_fclose(pf);
     SvIVX(sv) = 0;
+    UNLOCK_FDPID_MUTEX;
 
     if (win32_waitpid(childpid, &status, 0) == -1)
         return -1;
@@ -2709,7 +2769,12 @@ _fixed_read(int fh, void *buf, unsigned cnt)
        return -1;
     }
 
-    EnterCriticalSection(&(_pioinfo(fh)->lock));  /* lock file */
+    /*
+     * If lockinitflag is FALSE, assume fd is device
+     * lockinitflag is set to TRUE by open.
+     */
+    if (_pioinfo(fh)->lockinitflag)
+       EnterCriticalSection(&(_pioinfo(fh)->lock));  /* lock file */
 
     bytes_read = 0;                 /* nothing read yet */
     buffer = (char*)buf;
@@ -2857,7 +2922,8 @@ _fixed_read(int fh, void *buf, unsigned cnt)
     }
 
 functionexit:  
-    LeaveCriticalSection(&(_pioinfo(fh)->lock));    /* unlock file */
+    if (_pioinfo(fh)->lockinitflag)
+       LeaveCriticalSection(&(_pioinfo(fh)->lock));    /* unlock file */
 
     return bytes_read;
 }
@@ -2908,6 +2974,10 @@ DllExport int
 win32_chdir(const char *dir)
 {
     dTHXo;
+    if (!dir) {
+       errno = ENOENT;
+       return -1;
+    }
     if (USING_WIDE()) {
        WCHAR wBuffer[MAX_PATH+1];
        A2WHELPER(dir, wBuffer, sizeof(wBuffer));
@@ -2985,7 +3055,7 @@ qualified_path(const char *cmd)
     }
 
     /* look in PATH */
-    pathstr = win32_getenv("PATH");
+    pathstr = PerlEnv_getenv("PATH");
     New(0, fullcmd, MAX_PATH+1, char);
     curfullcmd = fullcmd;
 
@@ -3027,7 +3097,7 @@ qualified_path(const char *cmd)
            if (*pathstr == '"') {      /* foo;"baz;etc";bar */
                pathstr++;              /* skip initial '"' */
                while (*pathstr && *pathstr != '"') {
-                   if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
+                   if ((STRLEN)(curfullcmd-fullcmd) < MAX_PATH-cmdlen-5)
                        *curfullcmd++ = *pathstr;
                    pathstr++;
                }
@@ -3035,7 +3105,7 @@ qualified_path(const char *cmd)
                    pathstr++;          /* skip trailing '"' */
            }
            else {
-               if (curfullcmd-fullcmd < MAX_PATH-cmdlen-5)
+               if ((STRLEN)(curfullcmd-fullcmd) < MAX_PATH-cmdlen-5)
                    *curfullcmd++ = *pathstr;
                pathstr++;
            }
@@ -3048,7 +3118,7 @@ qualified_path(const char *cmd)
            *curfullcmd++ = '\\';
        }
     }
-GIVE_UP:
+
     Safefree(fullcmd);
     return Nullch;
 }
@@ -3119,6 +3189,7 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
     int ret;
     void* env;
     char* dir;
+    child_IO_table tbl;
     STARTUPINFO StartupInfo;
     PROCESS_INFORMATION ProcessInformation;
     DWORD create = 0;
@@ -3147,9 +3218,20 @@ win32_spawnvp(int mode, const char *cmdname, const char *const *argv)
     }
     memset(&StartupInfo,0,sizeof(StartupInfo));
     StartupInfo.cb = sizeof(StartupInfo);
-    StartupInfo.hStdInput  = GetStdHandle(STD_INPUT_HANDLE);
-    StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
-    StartupInfo.hStdError  = GetStdHandle(STD_ERROR_HANDLE);
+    memset(&tbl,0,sizeof(tbl));
+    PerlEnv_get_child_IO(&tbl);
+    StartupInfo.dwFlags                = tbl.dwFlags;
+    StartupInfo.dwX            = tbl.dwX; 
+    StartupInfo.dwY            = tbl.dwY; 
+    StartupInfo.dwXSize                = tbl.dwXSize; 
+    StartupInfo.dwYSize                = tbl.dwYSize; 
+    StartupInfo.dwXCountChars  = tbl.dwXCountChars; 
+    StartupInfo.dwYCountChars  = tbl.dwYCountChars; 
+    StartupInfo.dwFillAttribute        = tbl.dwFillAttribute; 
+    StartupInfo.wShowWindow    = tbl.wShowWindow; 
+    StartupInfo.hStdInput      = tbl.childStdIn;
+    StartupInfo.hStdOutput     = tbl.childStdOut;
+    StartupInfo.hStdError      = tbl.childStdErr;
     if (StartupInfo.hStdInput != INVALID_HANDLE_VALUE &&
        StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
        StartupInfo.hStdError != INVALID_HANDLE_VALUE)
@@ -3192,9 +3274,12 @@ RETRY:
 
     if (mode == P_NOWAIT) {
        /* asynchronous spawn -- store handle, return PID */
-       w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
-       w32_child_pids[w32_num_children] = ProcessInformation.dwProcessId;
        ret = (int)ProcessInformation.dwProcessId;
+       if (IsWin95() && ret < 0)
+           ret = -ret;
+
+       w32_child_handles[w32_num_children] = ProcessInformation.hProcess;
+       w32_child_pids[w32_num_children] = (DWORD)ret;
        ++w32_num_children;
     }
     else  {
@@ -3439,6 +3524,25 @@ win32_dynaload(const char* filename)
 {
     dTHXo;
     HMODULE hModule;
+    char buf[MAX_PATH+1];
+    char *first;
+
+    /* LoadLibrary() doesn't recognize forward slashes correctly,
+     * so turn 'em back. */
+    first = strchr(filename, '/');
+    if (first) {
+       STRLEN len = strlen(filename);
+       if (len <= MAX_PATH) {
+           strcpy(buf, filename);
+           filename = &buf[first - filename];
+           while (*filename) {
+               if (*filename == '/')
+                   *(char*)filename = '\\';
+               ++filename;
+           }
+           filename = buf;
+       }
+    }
     if (USING_WIDE()) {
        WCHAR wfilename[MAX_PATH+1];
        A2WHELPER(filename, wfilename, sizeof(wfilename));
@@ -3754,8 +3858,11 @@ XS(w32_Spawn)
                &stStartInfo,           /* -> Startup info */
                &stProcInfo))           /* <- Process info (if OK) */
     {
+       int pid = (int)stProcInfo.dwProcessId;
+       if (IsWin95() && pid < 0)
+           pid = -pid;
+       sv_setiv(ST(2), pid);
        CloseHandle(stProcInfo.hThread);/* library source code does this. */
-       sv_setiv(ST(2), stProcInfo.dwProcessId);
        bSuccess = TRUE;
     }
     XSRETURN_IV(bSuccess);
@@ -3784,6 +3891,9 @@ XS(w32_GetShortPathName)
 
     shortpath = sv_mortalcopy(ST(0));
     SvUPGRADE(shortpath, SVt_PV);
+    if (!SvPVX(shortpath) || !SvLEN(shortpath))
+        XSRETURN_UNDEF;
+
     /* src == target is allowed */
     do {
        len = GetShortPathName(SvPVX(shortpath),
@@ -3813,6 +3923,9 @@ XS(w32_GetFullPathName)
     filename = ST(0);
     fullpath = sv_mortalcopy(filename);
     SvUPGRADE(fullpath, SVt_PV);
+    if (!SvPVX(fullpath) || !SvLEN(fullpath))
+        XSRETURN_UNDEF;
+
     do {
        len = GetFullPathName(SvPVX(filename),
                              SvLEN(fullpath),
@@ -3899,18 +4012,6 @@ Perl_init_os_extras(void)
     char *file = __FILE__;
     dXSUB_SYS;
 
-    w32_perlshell_tokens = Nullch;
-    w32_perlshell_items = -1;
-    w32_fdpid = newAV();               /* XXX needs to be in Perl_win32_init()? */
-    New(1313, w32_children, 1, child_tab);
-    w32_num_children = 0;
-    w32_init_socktype = 0;
-#ifdef USE_ITHREADS
-    w32_pseudo_id = 0;
-    New(1313, w32_pseudo_children, 1, child_tab);
-    w32_num_pseudo_children = 0;
-#endif
-
     /* these names are Activeware compatible */
     newXS("Win32::GetCwd", w32_GetCwd, file);
     newXS("Win32::SetCwd", w32_SetCwd, file);
@@ -3960,15 +4061,58 @@ Perl_win32_init(int *argcp, char ***argvp)
     MALLOC_INIT;
 }
 
-#ifdef USE_ITHREADS
+void
+win32_get_child_IO(child_IO_table* ptbl)
+{
+    ptbl->childStdIn   = GetStdHandle(STD_INPUT_HANDLE);
+    ptbl->childStdOut  = GetStdHandle(STD_OUTPUT_HANDLE);
+    ptbl->childStdErr  = GetStdHandle(STD_ERROR_HANDLE);
+}
+
+#ifdef HAVE_INTERP_INTERN
 
 #  ifdef PERL_OBJECT
+#    undef Perl_sys_intern_init
+#    define Perl_sys_intern_init CPerlObj::Perl_sys_intern_init
 #    undef Perl_sys_intern_dup
 #    define Perl_sys_intern_dup CPerlObj::Perl_sys_intern_dup
+#    undef Perl_sys_intern_clear
+#    define Perl_sys_intern_clear CPerlObj::Perl_sys_intern_clear
 #    define pPerl this
 #  endif
 
 void
+Perl_sys_intern_init(pTHX)
+{
+    w32_perlshell_tokens       = Nullch;
+    w32_perlshell_vec          = (char**)NULL;
+    w32_perlshell_items                = 0;
+    w32_fdpid                  = newAV();
+    New(1313, w32_children, 1, child_tab);
+    w32_num_children           = 0;
+#  ifdef USE_ITHREADS
+    w32_pseudo_id              = 0;
+    New(1313, w32_pseudo_children, 1, child_tab);
+    w32_num_pseudo_children    = 0;
+#  endif
+    w32_init_socktype          = 0;
+}
+
+void
+Perl_sys_intern_clear(pTHX)
+{
+    Safefree(w32_perlshell_tokens);
+    Safefree(w32_perlshell_vec);
+    /* NOTE: w32_fdpid is freed by sv_clean_all() */
+    Safefree(w32_children);
+#  ifdef USE_ITHREADS
+    Safefree(w32_pseudo_children);
+#  endif
+}
+
+#  ifdef USE_ITHREADS
+
+void
 Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
 {
     dst->perlshell_tokens      = Nullch;
@@ -3976,10 +4120,44 @@ Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
     dst->perlshell_items       = 0;
     dst->fdpid                 = newAV();
     Newz(1313, dst->children, 1, child_tab);
-    Newz(1313, dst->pseudo_children, 1, child_tab);
     dst->pseudo_id             = 0;
-    dst->children->num         = 0;
-    dst->thr_intern.Winit_socktype = src->thr_intern.Winit_socktype;
+    Newz(1313, dst->pseudo_children, 1, child_tab);
+    dst->thr_intern.Winit_socktype = 0;
 }
+#  endif /* USE_ITHREADS */
+#endif /* HAVE_INTERP_INTERN */
+
+#ifdef PERL_OBJECT
+#  undef this
+#  define this pPerl
 #endif
 
+static void
+win32_free_argvw(pTHXo_ void *ptr)
+{
+    char** argv = (char**)ptr;
+    while(*argv) {
+       Safefree(*argv);
+       *argv++ = Nullch;
+    }
+}
+
+void
+win32_argv2utf8(int argc, char** argv)
+{
+    dTHXo;
+    char* psz;
+    int length, wargc;
+    LPWSTR* lpwStr = CommandLineToArgvW(GetCommandLineW(), &wargc);
+    if (lpwStr && argc) {
+       while (argc--) {
+           length = WideCharToMultiByte(CP_UTF8, 0, lpwStr[--wargc], -1, NULL, 0, NULL, NULL);
+           Newz(0, psz, length, char);
+           WideCharToMultiByte(CP_UTF8, 0, lpwStr[wargc], -1, psz, length, NULL, NULL);
+           argv[argc] = psz;
+       }
+       call_atexit(win32_free_argvw, argv);
+    }
+    GlobalFree((HGLOBAL)lpwStr);
+}
+