This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
add missing flag in change#4892
[perl5.git] / win32 / win32.c
index 3f56f60..af5e454 100644 (file)
@@ -15,6 +15,8 @@
 #define Win32_Winsock
 #endif
 #include <windows.h>
+#include <winnt.h>
+#include <io.h>
 
 /* #include "config.h" */
 
@@ -95,33 +97,19 @@ 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);
+#ifdef USE_ITHREADS
+static void            remove_dead_pseudo_process(long child);
+static long            find_pseudo_pid(int pid);
+#endif
 
+START_EXTERN_C
 HANDLE w32_perldll_handle = INVALID_HANDLE_VALUE;
 char   w32_module_name[MAX_PATH+1];
+END_EXTERN_C
+
 static DWORD   w32_platform = (DWORD)-1;
 
-#ifdef USE_THREADS
-#  ifdef USE_DECLSPEC_THREAD
-__declspec(thread) char        strerror_buffer[512];
-__declspec(thread) char        getlogin_buffer[128];
-__declspec(thread) char        w32_perllib_root[MAX_PATH+1];
-#    ifdef HAVE_DES_FCRYPT
-__declspec(thread) char        crypt_buffer[30];
-#    endif
-#  else
-#    define strerror_buffer    (thr->i.Wstrerror_buffer)
-#    define getlogin_buffer    (thr->i.Wgetlogin_buffer)
-#    define w32_perllib_root   (thr->i.Ww32_perllib_root)
-#    define crypt_buffer       (thr->i.Wcrypt_buffer)
-#  endif
-#else
-static char    strerror_buffer[512];
-static char    getlogin_buffer[128];
-static char    w32_perllib_root[MAX_PATH+1];
-#  ifdef HAVE_DES_FCRYPT
-static char    crypt_buffer[30];
-#  endif
-#endif
+#define ONE_K_BUFSIZE  1024
 
 int 
 IsWin95(void)
@@ -188,12 +176,13 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...)
     char *optr;
     char *strip;
     int oldsize, newsize;
+    STRLEN baselen;
 
     va_start(ap, trailing_path);
     strip = va_arg(ap, char *);
 
-    sprintf(base, "%5.3f",
-           (double)PERL_REVISION + ((double)PERL_VERSION / (double)1000));
+    sprintf(base, "%d.%d", (int)PERL_REVISION, (int)PERL_VERSION);
+    baselen = strlen(base);
 
     if (!*w32_module_name) {
        GetModuleFileName((HMODULE)((w32_perldll_handle == INVALID_HANDLE_VALUE)
@@ -225,10 +214,10 @@ get_emd_part(SV **prev_pathp, char *trailing_path, ...)
        /* avoid stripping component if there is no slash,
         * or it doesn't match ... */
        if (!ptr || stricmp(ptr+1, strip) != 0) {
-           /* ... but not if component matches 5.00X* */
+           /* ... but not if component matches m|5\.$patchlevel.*| */
            if (!ptr || !(*strip == '5' && *(ptr+1) == '5'
-                         && strncmp(strip, base, 5) == 0
-                         && strncmp(ptr+1, base, 5) == 0))
+                         && strncmp(strip, base, baselen) == 0
+                         && strncmp(ptr+1, base, baselen) == 0))
            {
                *optr = '/';
                ptr = optr;
@@ -295,12 +284,6 @@ win32_get_sitelib(char *pl)
      * ";$EMD/" . ((-d $EMD/../../../$]) ? "../../.." : "../.."). "/site/$]/lib";  */
     sprintf(pathstr, "site/%s/lib", pl);
     (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch);
-    if (!sv1 && strlen(pl) == 7) {
-       /* pl may have been SUBVERSION-specific; try again without
-        * SUBVERSION */
-       sprintf(pathstr, "site/%.5s/lib", pl);
-       (void)get_emd_part(&sv1, pathstr, ARCHNAME, "bin", pl, Nullch);
-    }
 
     /* $HKCU{'sitelib'} || $HKLM{'sitelib'} . ---; */
     (void)get_regstr(sitelib, &sv2);
@@ -372,17 +355,17 @@ PerlIO *
 Perl_my_popen(pTHX_ char *cmd, char *mode)
 {
 #ifdef FIXCMD
-#define fixcmd(x)      {                                       \
-                           char *pspace = strchr((x),' ');     \
-                           if (pspace) {                       \
-                               char *p = (x);                  \
-                               while (p < pspace) {            \
-                                   if (*p == '/')              \
-                                       *p = '\\';              \
-                                   p++;                        \
-                               }                               \
-                           }                                   \
-                       }
+#define fixcmd(x)   {                                  \
+                       char *pspace = strchr((x),' '); \
+                       if (pspace) {                   \
+                           char *p = (x);              \
+                           while (p < pspace) {        \
+                               if (*p == '/')          \
+                                   *p = '\\';          \
+                               p++;                    \
+                           }                           \
+                       }                               \
+                   }
 #else
 #define fixcmd(x)
 #endif
@@ -412,6 +395,17 @@ win32_os_id(void)
     return (unsigned long)w32_platform;
 }
 
+DllExport int
+win32_getpid(void)
+{
+#ifdef USE_ITHREADS
+    dTHXo;
+    if (w32_pseudo_id)
+       return -((int)w32_pseudo_id);
+#endif
+    return _getpid();
+}
+
 /* Tokenize a string.  Words are null-separated, and the list
  * ends with a doubled null.  Any character (except null and
  * including backslash) may be escaped by preceding it with a
@@ -677,7 +671,7 @@ win32_opendir(char *filename)
     WIN32_FIND_DATAW   wFindData;
     HANDLE             fh;
     char               buffer[MAX_PATH*2];
-    WCHAR              wbuffer[MAX_PATH];
+    WCHAR              wbuffer[MAX_PATH+1];
     char*              ptr;
 
     len = strlen(filename);
@@ -708,10 +702,10 @@ win32_opendir(char *filename)
     /* do the FindFirstFile call */
     if (USING_WIDE()) {
        A2WHELPER(scanname, wbuffer, sizeof(wbuffer));
-       fh = FindFirstFileW(wbuffer, &wFindData);
+       fh = FindFirstFileW(PerlDir_mapW(wbuffer), &wFindData);
     }
     else {
-       fh = FindFirstFileA(scanname, &aFindData);
+       fh = FindFirstFileA(PerlDir_mapA(scanname), &aFindData);
     }
     dirp->handle = fh;
     if (fh == INVALID_HANDLE_VALUE) {
@@ -916,8 +910,8 @@ char *
 getlogin(void)
 {
     dTHXo;
-    char *buf = getlogin_buffer;
-    DWORD size = sizeof(getlogin_buffer);
+    char *buf = w32_getlogin_buffer;
+    DWORD size = sizeof(w32_getlogin_buffer);
     if (GetUserName(buf,&size))
        return buf;
     return (char*)NULL;
@@ -934,8 +928,8 @@ static long
 find_pid(int pid)
 {
     dTHXo;
-    long child;
-    for (child = 0 ; child < w32_num_children ; ++child) {
+    long child = w32_num_children;
+    while (--child >= 0) {
        if (w32_child_pids[child] == pid)
            return child;
     }
@@ -948,26 +942,80 @@ remove_dead_process(long child)
     if (child >= 0) {
        dTHXo;
        CloseHandle(w32_child_handles[child]);
-       Copy(&w32_child_handles[child+1], &w32_child_handles[child],
+       Move(&w32_child_handles[child+1], &w32_child_handles[child],
             (w32_num_children-child-1), HANDLE);
-       Copy(&w32_child_pids[child+1], &w32_child_pids[child],
+       Move(&w32_child_pids[child+1], &w32_child_pids[child],
             (w32_num_children-child-1), DWORD);
        w32_num_children--;
     }
 }
 
+#ifdef USE_ITHREADS
+static long
+find_pseudo_pid(int pid)
+{
+    dTHXo;
+    long child = w32_num_pseudo_children;
+    while (--child >= 0) {
+       if (w32_pseudo_child_pids[child] == pid)
+           return child;
+    }
+    return -1;
+}
+
+static void
+remove_dead_pseudo_process(long child)
+{
+    if (child >= 0) {
+       dTHXo;
+       CloseHandle(w32_pseudo_child_handles[child]);
+       Move(&w32_pseudo_child_handles[child+1], &w32_pseudo_child_handles[child],
+            (w32_num_pseudo_children-child-1), HANDLE);
+       Move(&w32_pseudo_child_pids[child+1], &w32_pseudo_child_pids[child],
+            (w32_num_pseudo_children-child-1), DWORD);
+       w32_num_pseudo_children--;
+    }
+}
+#endif
+
 DllExport int
 win32_kill(int pid, int sig)
 {
+    dTHXo;
     HANDLE hProcess;
-    hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
-    if (hProcess && TerminateProcess(hProcess, sig))
-       CloseHandle(hProcess);
-    else {
-       errno = EINVAL;
-       return -1;
+#ifdef USE_ITHREADS
+    if (pid < 0) {
+       /* it is a pseudo-forked child */
+       long child = find_pseudo_pid(-pid);
+       if (child >= 0) {
+           hProcess = w32_pseudo_child_handles[child];
+           if (TerminateThread(hProcess, sig)) {
+               remove_dead_pseudo_process(child);
+               return 0;
+           }
+       }
     }
-    return 0;
+    else
+#endif
+    {
+       long child = find_pid(pid);
+       if (child >= 0) {
+           hProcess = w32_child_handles[child];
+           if (TerminateProcess(hProcess, sig)) {
+               remove_dead_process(child);
+               return 0;
+           }
+       }
+       else {
+           hProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, pid);
+           if (hProcess && TerminateProcess(hProcess, sig)) {
+               CloseHandle(hProcess);
+               return 0;
+           }
+       }
+    }
+    errno = EINVAL;
+    return -1;
 }
 
 /*
@@ -988,7 +1036,9 @@ win32_stat(const char *path, struct stat *buffer)
     char       t[MAX_PATH+1]; 
     int                l = strlen(path);
     int                res;
-    WCHAR      wbuffer[MAX_PATH];
+    WCHAR      wbuffer[MAX_PATH+1];
+    HANDLE      handle;
+    int         nlink = 1;
 
     if (l > 1) {
        switch(path[l - 1]) {
@@ -1010,13 +1060,35 @@ win32_stat(const char *path, struct stat *buffer)
            break;
        }
     }
+
+    /* 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.    */
     if (USING_WIDE()) {
        A2WHELPER(path, wbuffer, sizeof(wbuffer));
+       wcscpy(wbuffer, PerlDir_mapW(wbuffer));
+       handle = CreateFileW(wbuffer, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+    }
+    else {
+       path = PerlDir_mapA(path);
+       handle = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+    }
+    if (handle != INVALID_HANDLE_VALUE) {
+       BY_HANDLE_FILE_INFORMATION bhi;
+       if (GetFileInformationByHandle(handle, &bhi))
+           nlink = bhi.nNumberOfLinks;
+       CloseHandle(handle);
+    }
+
+    /* wbuffer or path will be mapped correctly above */
+    if (USING_WIDE()) {
        res = _wstat(wbuffer, (struct _stat *)buffer);
     }
     else {
        res = stat(path, buffer);
     }
+    buffer->st_nlink = nlink;
+
     if (res < 0) {
        /* CRT is buggy on sharenames, so make sure it really isn't.
         * XXX using GetFileAttributesEx() will enable us to set
@@ -1156,7 +1228,7 @@ DllExport char *
 win32_getenv(const char *name)
 {
     dTHXo;
-    WCHAR wBuffer[MAX_PATH];
+    WCHAR wBuffer[MAX_PATH+1];
     DWORD needlen;
     SV *curitem = Nullsv;
 
@@ -1217,9 +1289,9 @@ win32_putenv(const char *name)
            New(1309,wCuritem,length,WCHAR);
            A2WHELPER(name, wCuritem, length*sizeof(WCHAR));
            wVal = wcschr(wCuritem, '=');
-           if(wVal) {
+           if (wVal) {
                *wVal++ = '\0';
-               if(SetEnvironmentVariableW(wCuritem, *wVal ? wVal : NULL))
+               if (SetEnvironmentVariableW(wCuritem, *wVal ? wVal : NULL))
                    relval = 0;
            }
            Safefree(wCuritem);
@@ -1228,7 +1300,7 @@ win32_putenv(const char *name)
            New(1309,curitem,strlen(name)+1,char);
            strcpy(curitem, name);
            val = strchr(curitem, '=');
-           if(val) {
+           if (val) {
                /* The sane way to deal with the environment.
                 * Has these advantages over putenv() & co.:
                 *  * enables us to store a truly empty value in the
@@ -1244,7 +1316,7 @@ win32_putenv(const char *name)
                 * GSAR 97-06-07
                 */
                *val++ = '\0';
-               if(SetEnvironmentVariableA(curitem, *val ? val : NULL))
+               if (SetEnvironmentVariableA(curitem, *val ? val : NULL))
                    relval = 0;
            }
            Safefree(curitem);
@@ -1258,11 +1330,11 @@ win32_putenv(const char *name)
 static long
 filetime_to_clock(PFILETIME ft)
 {
- __int64 qw = ft->dwHighDateTime;
- qw <<= 32;
- qw |= ft->dwLowDateTime;
- qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
- return (long) qw;
   __int64 qw = ft->dwHighDateTime;
   qw <<= 32;
   qw |= ft->dwLowDateTime;
   qw /= 10000;  /* File time ticks at 0.1uS, clock at 1mS */
   return (long) qw;
 }
 
 DllExport int
@@ -1313,6 +1385,50 @@ filetime_from_time(PFILETIME pFileTime, time_t Time)
 }
 
 DllExport int
+win32_unlink(const char *filename)
+{
+    dTHXo;
+    int ret;
+    DWORD attrs;
+
+    if (USING_WIDE()) {
+       WCHAR wBuffer[MAX_PATH+1];
+
+       A2WHELPER(filename, wBuffer, sizeof(wBuffer));
+       wcscpy(wBuffer, PerlDir_mapW(wBuffer));
+       attrs = GetFileAttributesW(wBuffer);
+       if (attrs == 0xFFFFFFFF)
+           goto fail;
+       if (attrs & FILE_ATTRIBUTE_READONLY) {
+           (void)SetFileAttributesW(wBuffer, attrs & ~FILE_ATTRIBUTE_READONLY);
+           ret = _wunlink(wBuffer);
+           if (ret == -1)
+               (void)SetFileAttributesW(wBuffer, attrs);
+       }
+       else
+           ret = _wunlink(wBuffer);
+    }
+    else {
+       filename = PerlDir_mapA(filename);
+       attrs = GetFileAttributesA(filename);
+       if (attrs == 0xFFFFFFFF)
+           goto fail;
+       if (attrs & FILE_ATTRIBUTE_READONLY) {
+           (void)SetFileAttributesA(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
+           ret = unlink(filename);
+           if (ret == -1)
+               (void)SetFileAttributesA(filename, attrs);
+       }
+       else
+           ret = unlink(filename);
+    }
+    return ret;
+fail:
+    errno = ENOENT;
+    return -1;
+}
+
+DllExport int
 win32_utime(const char *filename, struct utimbuf *times)
 {
     dTHXo;
@@ -1321,14 +1437,16 @@ win32_utime(const char *filename, struct utimbuf *times)
     FILETIME ftAccess;
     FILETIME ftWrite;
     struct utimbuf TimeBuffer;
-    WCHAR wbuffer[MAX_PATH];
+    WCHAR wbuffer[MAX_PATH+1];
 
     int rc;
     if (USING_WIDE()) {
        A2WHELPER(filename, wbuffer, sizeof(wbuffer));
+       wcscpy(wbuffer, PerlDir_mapW(wbuffer));
        rc = _wutime(wbuffer, (struct _utimbuf*)times);
     }
     else {
+       filename = PerlDir_mapA(filename);
        rc = utime(filename, times);
     }
     /* EACCES: path specifies directory or readonly file */
@@ -1462,8 +1580,27 @@ win32_waitpid(int pid, int *status, int flags)
 {
     dTHXo;
     int retval = -1;
-    if (pid == -1) 
+    if (pid == -1)                             /* XXX threadid == 1 ? */
        return win32_wait(status);
+#ifdef USE_ITHREADS
+    else if (pid < 0) {
+       long child = find_pseudo_pid(-pid);
+       if (child >= 0) {
+           HANDLE hThread = w32_pseudo_child_handles[child];
+           DWORD waitcode = WaitForSingleObject(hThread, INFINITE);
+           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;
+               }
+           }
+           else
+               errno = ECHILD;
+       }
+    }
+#endif
     else {
        long child = find_pid(pid);
        if (child >= 0) {
@@ -1502,6 +1639,28 @@ win32_wait(int *status)
     int i, retval;
     DWORD exitcode, waitcode;
 
+#ifdef USE_ITHREADS
+    if (w32_num_pseudo_children) {
+       waitcode = WaitForMultipleObjects(w32_num_pseudo_children,
+                                         w32_pseudo_child_handles,
+                                         FALSE,
+                                         INFINITE);
+       if (waitcode != WAIT_FAILED) {
+           if (waitcode >= WAIT_ABANDONED_0
+               && waitcode < WAIT_ABANDONED_0 + w32_num_pseudo_children)
+               i = waitcode - WAIT_ABANDONED_0;
+           else
+               i = waitcode - WAIT_OBJECT_0;
+           if (GetExitCodeThread(w32_pseudo_child_handles[i], &exitcode)) {
+               *status = (int)((exitcode & 0xff) << 8);
+               retval = (int)w32_pseudo_child_pids[i];
+               remove_dead_pseudo_process(i);
+               return retval;
+           }
+       }
+    }
+#endif
+
     if (!w32_num_children) {
        errno = ECHILD;
        return -1;
@@ -1571,7 +1730,6 @@ win32_alarm(unsigned int sec)
     return 0;
 }
 
-#if defined(HAVE_DES_FCRYPT) || defined(PERL_OBJECT)
 #ifdef HAVE_DES_FCRYPT
 extern char *  des_fcrypt(const char *txt, const char *salt, char *cbuf);
 #endif
@@ -1582,61 +1740,78 @@ win32_crypt(const char *txt, const char *salt)
     dTHXo;
 #ifdef HAVE_DES_FCRYPT
     dTHR;
-    return des_fcrypt(txt, salt, crypt_buffer);
+    return des_fcrypt(txt, salt, w32_crypt_buffer);
 #else
-    die("The crypt() function is unimplemented due to excessive paranoia.");
+    Perl_croak(aTHX_ "The crypt() function is unimplemented due to excessive paranoia.");
     return Nullch;
 #endif
 }
-#endif
 
-#ifdef USE_FIXED_OSFHANDLE
+/* C doesn't like repeat struct definitions */
+
+#if defined(USE_FIXED_OSFHANDLE) || defined(PERL_MSVCRT_READFIX)
 
-EXTERN_C int __cdecl _alloc_osfhnd(void);
-EXTERN_C int __cdecl _set_osfhnd(int fh, long value);
-EXTERN_C void __cdecl _lock_fhandle(int);
-EXTERN_C void __cdecl _unlock_fhandle(int);
-EXTERN_C void __cdecl _unlock(int);
+#ifndef _CRTIMP
+#define _CRTIMP __declspec(dllimport)
+#endif
 
-#if    (_MSC_VER >= 1000)
-typedef struct {
+/*
+ * 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 */
-#if defined (_MT) && !defined (DLL_FOR_WIN32S)
     int lockinitflag;
     CRITICAL_SECTION lock;
-#endif  /* defined (_MT) && !defined (DLL_FOR_WIN32S) */
-}      ioinfo;
+} ioinfo;
 
-EXTERN_C ioinfo * __pioinfo[];
 
-#define IOINFO_L2E                     5
-#define IOINFO_ARRAY_ELTS      (1 << IOINFO_L2E)
-#define _pioinfo(i)    (__pioinfo[i >> IOINFO_L2E] + (i & (IOINFO_ARRAY_ELTS - 1)))
-#define _osfile(i)     (_pioinfo(i)->osfile)
+/*
+ * Array of arrays of control structures for lowio files.
+ */
+EXTERN_C _CRTIMP ioinfo* __pioinfo[];
 
-#else  /* (_MSC_VER >= 1000) */
-extern char _osfile[];
-#endif /* (_MSC_VER >= 1000) */
+/*
+ * 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 */
+#define FNOINHERIT             0x10    /* file handle opened O_NOINHERIT */
 #define FAPPEND                        0x20    /* file handle opened O_APPEND */
 #define FDEV                   0x40    /* file handle refers to device */
 #define FTEXT                  0x80    /* file handle is in text mode */
 
-#define _STREAM_LOCKS   26             /* Table of stream locks */
-#define _LAST_STREAM_LOCK  (_STREAM_LOCKS+_NSTREAM_-1) /* Last stream lock */
-#define _FH_LOCKS          (_LAST_STREAM_LOCK+1)       /* Table of fh locks */
-
 /***
 *int my_open_osfhandle(long osfhandle, int flags) - open C Runtime file handle
 *
 *Purpose:
 *       This function allocates a free C Runtime file handle and associates
 *       it with the Win32 HANDLE specified by the first parameter. This is a
-*              temperary fix for WIN95's brain damage GetFileType() error on socket
-*              we just bypass that call for socket
+*      temperary fix for WIN95's brain damage GetFileType() error on socket
+*      we just bypass that call for socket
+*
+*      This works with MSVC++ 4.0+ or GCC/Mingw32
 *
 *Entry:
 *       long osfhandle - Win32 HANDLE to associate with C Runtime file handle.
@@ -1650,6 +1825,31 @@ extern char _osfile[];
 *
 *******************************************************************************/
 
+/*
+ * we fake up some parts of the CRT that aren't exported by MSVCRT.dll
+ * this lets sockets work on Win9X with GCC and should fix the problems
+ * with perl95.exe
+ *     -- 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
+_alloc_osfhnd(void)
+{
+    HANDLE hF = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL);
+    int fh = _open_osfhandle((long)hF, 0);
+    CloseHandle(hF);
+    if (fh == -1)
+        return fh;
+    EnterCriticalSection(&(_pioinfo(fh)->lock));
+    return fh;
+}
+
 static int
 my_open_osfhandle(long osfhandle, int flags)
 {
@@ -1665,6 +1865,9 @@ my_open_osfhandle(long osfhandle, int flags)
     if (flags & O_TEXT)
        fileflags |= FTEXT;
 
+    if (flags & O_NOINHERIT)
+       fileflags |= FNOINHERIT;
+
     /* attempt to allocate a C Runtime file handle */
     if ((fh = _alloc_osfhnd()) == -1) {
        errno = EMFILE;         /* too many open files */
@@ -1677,18 +1880,12 @@ my_open_osfhandle(long osfhandle, int flags)
 
     fileflags |= FOPEN;                /* mark as open */
 
-#if (_MSC_VER >= 1000)
     _osfile(fh) = fileflags;   /* set osfile entry */
-    _unlock_fhandle(fh);
-#else
-    _osfile[fh] = fileflags;   /* set osfile entry */
-    _unlock(fh+_FH_LOCKS);             /* unlock handle */
-#endif
+    LeaveCriticalSection(&_pioinfo(fh)->lock);
 
     return fh;                 /* return handle */
 }
 
-#define _open_osfhandle my_open_osfhandle
 #endif /* USE_FIXED_OSFHANDLE */
 
 /* simulate flock by locking a range on the file */
@@ -1808,10 +2005,11 @@ win32_strerror(int e)
            e = GetLastError();
 
        if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, &source, e, 0,
-                        strerror_buffer, sizeof(strerror_buffer), NULL) == 0) 
-           strcpy(strerror_buffer, "Unknown Error");
+                         w32_strerror_buffer,
+                         sizeof(w32_strerror_buffer), NULL) == 0) 
+           strcpy(w32_strerror_buffer, "Unknown Error");
 
-       return strerror_buffer;
+       return w32_strerror_buffer;
     }
     return strerror(e);
 }
@@ -1897,7 +2095,8 @@ DllExport FILE *
 win32_fopen(const char *filename, const char *mode)
 {
     dTHXo;
-    WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH];
+    WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH+1];
+    FILE *f;
     
     if (!*filename)
        return NULL;
@@ -1908,9 +2107,14 @@ win32_fopen(const char *filename, const char *mode)
     if (USING_WIDE()) {
        A2WHELPER(mode, wMode, sizeof(wMode));
        A2WHELPER(filename, wBuffer, sizeof(wBuffer));
-       return _wfopen(wBuffer, wMode);
+       f = _wfopen(PerlDir_mapW(wBuffer), wMode);
     }
-    return fopen(filename, mode);
+    else
+       f = fopen(PerlDir_mapA(filename), mode);
+    /* avoid buffering headaches for child processes */
+    if (f && *mode == 'a')
+       win32_fseek(f, 0, SEEK_END);
+    return f;
 }
 
 #ifndef USE_SOCKETS_AS_HANDLES
@@ -1923,27 +2127,33 @@ win32_fdopen(int handle, const char *mode)
 {
     dTHXo;
     WCHAR wMode[MODE_SIZE];
+    FILE *f;
     if (USING_WIDE()) {
        A2WHELPER(mode, wMode, sizeof(wMode));
-       return _wfdopen(handle, wMode);
+       f = _wfdopen(handle, wMode);
     }
-    return fdopen(handle, (char *) mode);
+    else
+       f = fdopen(handle, (char *) mode);
+    /* avoid buffering headaches for child processes */
+    if (f && *mode == 'a')
+       win32_fseek(f, 0, SEEK_END);
+    return f;
 }
 
 DllExport FILE *
 win32_freopen(const char *path, const char *mode, FILE *stream)
 {
     dTHXo;
-    WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH];
+    WCHAR wMode[MODE_SIZE], wBuffer[MAX_PATH+1];
     if (stricmp(path, "/dev/null")==0)
        path = "NUL";
 
     if (USING_WIDE()) {
        A2WHELPER(mode, wMode, sizeof(wMode));
        A2WHELPER(path, wBuffer, sizeof(wBuffer));
-       return _wfreopen(wBuffer, wMode, stream);
+       return _wfreopen(PerlDir_mapW(wBuffer), wMode, stream);
     }
-    return freopen(path, mode, stream);
+    return freopen(PerlDir_mapA(path), mode, stream);
 }
 
 DllExport int
@@ -2178,26 +2388,119 @@ win32_pclose(FILE *pf)
 #endif /* USE_RTL_POPEN */
 }
 
+static BOOL WINAPI
+Nt4CreateHardLinkW(
+    LPCWSTR lpFileName,
+    LPCWSTR lpExistingFileName,
+    LPSECURITY_ATTRIBUTES lpSecurityAttributes)
+{
+    HANDLE handle;
+    WCHAR wFullName[MAX_PATH+1];
+    LPVOID lpContext = NULL;
+    WIN32_STREAM_ID StreamId;
+    DWORD dwSize = (char*)&StreamId.cStreamName - (char*)&StreamId;
+    DWORD dwWritten;
+    DWORD dwLen;
+    BOOL bSuccess;
+
+    BOOL (__stdcall *pfnBackupWrite)(HANDLE, LPBYTE, DWORD, LPDWORD,
+                                    BOOL, BOOL, LPVOID*) =
+       (BOOL (__stdcall *)(HANDLE, LPBYTE, DWORD, LPDWORD,
+                           BOOL, BOOL, LPVOID*))
+       GetProcAddress(GetModuleHandle("kernel32.dll"), "BackupWrite");
+    if (pfnBackupWrite == NULL)
+       return 0;
+
+    dwLen = GetFullPathNameW(lpFileName, MAX_PATH, wFullName, NULL);
+    if (dwLen == 0)
+       return 0;
+    dwLen = (dwLen+1)*sizeof(WCHAR);
+
+    handle = CreateFileW(lpExistingFileName, FILE_WRITE_ATTRIBUTES,
+                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                        NULL, OPEN_EXISTING, 0, NULL);
+    if (handle == INVALID_HANDLE_VALUE)
+       return 0;
+
+    StreamId.dwStreamId = BACKUP_LINK;
+    StreamId.dwStreamAttributes = 0;
+    StreamId.dwStreamNameSize = 0;
+#if defined(__BORLANDC__) || defined(__MINGW32__)
+    StreamId.Size.u.HighPart = 0;
+    StreamId.Size.u.LowPart = dwLen;
+#else
+    StreamId.Size.HighPart = 0;
+    StreamId.Size.LowPart = dwLen;
+#endif
+
+    bSuccess = pfnBackupWrite(handle, (LPBYTE)&StreamId, dwSize, &dwWritten,
+                             FALSE, FALSE, &lpContext);
+    if (bSuccess) {
+       bSuccess = pfnBackupWrite(handle, (LPBYTE)wFullName, dwLen, &dwWritten,
+                                 FALSE, FALSE, &lpContext);
+       pfnBackupWrite(handle, NULL, 0, &dwWritten, TRUE, FALSE, &lpContext);
+    }
+
+    CloseHandle(handle);
+    return bSuccess;
+}
+
+DllExport int
+win32_link(const char *oldname, const char *newname)
+{
+    dTHXo;
+    BOOL (__stdcall *pfnCreateHardLinkW)(LPCWSTR,LPCWSTR,LPSECURITY_ATTRIBUTES);
+    WCHAR wOldName[MAX_PATH+1];
+    WCHAR wNewName[MAX_PATH+1];
+
+    if (IsWin95())
+       Perl_die(aTHX_ PL_no_func, "link");
+
+    pfnCreateHardLinkW =
+       (BOOL (__stdcall *)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES))
+       GetProcAddress(GetModuleHandle("kernel32.dll"), "CreateHardLinkW");
+    if (pfnCreateHardLinkW == NULL)
+       pfnCreateHardLinkW = Nt4CreateHardLinkW;
+
+    if ((A2WHELPER(oldname, wOldName, sizeof(wOldName))) &&
+       (A2WHELPER(newname, wNewName, sizeof(wNewName))) &&
+       (wcscpy(wOldName, PerlDir_mapW(wOldName)),
+       pfnCreateHardLinkW(PerlDir_mapW(wNewName), wOldName, NULL)))
+    {
+       return 0;
+    }
+    errno = (GetLastError() == ERROR_FILE_NOT_FOUND) ? ENOENT : EINVAL;
+    return -1;
+}
+
 DllExport int
 win32_rename(const char *oname, const char *newname)
 {
-    WCHAR wOldName[MAX_PATH];
-    WCHAR wNewName[MAX_PATH];
+    WCHAR wOldName[MAX_PATH+1];
+    WCHAR wNewName[MAX_PATH+1];
+    char szOldName[MAX_PATH+1];
+    char szNewName[MAX_PATH+1];
     BOOL bResult;
+    dTHXo;
+
     /* XXX despite what the documentation says about MoveFileEx(),
      * it doesn't work under Windows95!
      */
     if (IsWinNT()) {
-       dTHXo;
+       DWORD dwFlags = MOVEFILE_COPY_ALLOWED;
        if (USING_WIDE()) {
            A2WHELPER(oname, wOldName, sizeof(wOldName));
            A2WHELPER(newname, wNewName, sizeof(wNewName));
-           bResult = MoveFileExW(wOldName,wNewName,
-                       MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
+           if (wcsicmp(wNewName, wOldName))
+               dwFlags |= MOVEFILE_REPLACE_EXISTING;
+           wcscpy(wOldName, PerlDir_mapW(wOldName));
+           bResult = MoveFileExW(wOldName,PerlDir_mapW(wNewName), dwFlags);
        }
        else {
-           bResult = MoveFileExA(oname,newname,
-                       MOVEFILE_COPY_ALLOWED|MOVEFILE_REPLACE_EXISTING);
+           if (stricmp(newname, oname))
+               dwFlags |= MOVEFILE_REPLACE_EXISTING;
+           strcpy(szOldName, PerlDir_mapA(oname));
+           bResult = MoveFileExA(szOldName,PerlDir_mapA(newname), dwFlags);
        }
        if (!bResult) {
            DWORD err = GetLastError();
@@ -2222,14 +2525,17 @@ win32_rename(const char *oname, const char *newname)
     }
     else {
        int retval = 0;
-       char tmpname[MAX_PATH+1];
+       char szTmpName[MAX_PATH+1];
        char dname[MAX_PATH+1];
        char *endname = Nullch;
        STRLEN tmplen = 0;
        DWORD from_attr, to_attr;
 
+       strcpy(szOldName, PerlDir_mapA(oname));
+       strcpy(szNewName, PerlDir_mapA(newname));
+
        /* if oname doesn't exist, do nothing */
-       from_attr = GetFileAttributes(oname);
+       from_attr = GetFileAttributes(szOldName);
        if (from_attr == 0xFFFFFFFF) {
            errno = ENOENT;
            return -1;
@@ -2239,7 +2545,7 @@ win32_rename(const char *oname, const char *newname)
         * don't delete it in case oname happens to be the same file
         * (but perhaps accessed via a different path)
         */
-       to_attr = GetFileAttributes(newname);
+       to_attr = GetFileAttributes(szNewName);
        if (to_attr != 0xFFFFFFFF) {
            /* if newname is a directory, we fail
             * XXX could overcome this with yet more convoluted logic */
@@ -2247,29 +2553,29 @@ win32_rename(const char *oname, const char *newname)
                errno = EACCES;
                return -1;
            }
-           tmplen = strlen(newname);
-           strcpy(tmpname,newname);
-           endname = tmpname+tmplen;
-           for (; endname > tmpname ; --endname) {
+           tmplen = strlen(szNewName);
+           strcpy(szTmpName,szNewName);
+           endname = szTmpName+tmplen;
+           for (; endname > szTmpName ; --endname) {
                if (*endname == '/' || *endname == '\\') {
                    *endname = '\0';
                    break;
                }
            }
-           if (endname > tmpname)
-               endname = strcpy(dname,tmpname);
+           if (endname > szTmpName)
+               endname = strcpy(dname,szTmpName);
            else
                endname = ".";
 
            /* get a temporary filename in same directory
             * XXX is this really the best we can do? */
-           if (!GetTempFileName((LPCTSTR)endname, "plr", 0, tmpname)) {
+           if (!GetTempFileName((LPCTSTR)endname, "plr", 0, szTmpName)) {
                errno = ENOENT;
                return -1;
            }
-           DeleteFile(tmpname);
+           DeleteFile(szTmpName);
 
-           retval = rename(newname, tmpname);
+           retval = rename(szNewName, szTmpName);
            if (retval != 0) {
                errno = EACCES;
                return retval;
@@ -2277,16 +2583,16 @@ win32_rename(const char *oname, const char *newname)
        }
 
        /* rename oname to newname */
-       retval = rename(oname, newname);
+       retval = rename(szOldName, szNewName);
 
        /* if we created a temporary file before ... */
        if (endname != Nullch) {
            /* ...and rename succeeded, delete temporary file/directory */
            if (retval == 0)
-               DeleteFile(tmpname);
+               DeleteFile(szTmpName);
            /* else restore it to what it was */
            else
-               (void)rename(tmpname, newname);
+               (void)rename(szTmpName, szNewName);
        }
        return retval;
     }
@@ -2316,7 +2622,7 @@ win32_open(const char *path, int flag, ...)
     dTHXo;
     va_list ap;
     int pmode;
-    WCHAR wBuffer[MAX_PATH];
+    WCHAR wBuffer[MAX_PATH+1];
 
     va_start(ap, flag);
     pmode = va_arg(ap, int);
@@ -2327,9 +2633,9 @@ win32_open(const char *path, int flag, ...)
 
     if (USING_WIDE()) {
        A2WHELPER(path, wBuffer, sizeof(wBuffer));
-       return _wopen(wBuffer, flag, pmode);
+       return _wopen(PerlDir_mapW(wBuffer), flag, pmode);
     }
-    return open(path,flag,pmode);
+    return open(PerlDir_mapA(path), flag, pmode);
 }
 
 DllExport int
@@ -2356,10 +2662,204 @@ win32_dup2(int fd1,int fd2)
     return dup2(fd1,fd2);
 }
 
+#ifdef PERL_MSVCRT_READFIX
+
+#define LF             10      /* line feed */
+#define CR             13      /* carriage return */
+#define CTRLZ          26      /* ctrl-z means eof for text */
+#define FOPEN          0x01    /* file handle open */
+#define FEOFLAG                0x02    /* end of file has been encountered */
+#define FCRLF          0x04    /* CR-LF across read buffer (in text mode) */
+#define FPIPE          0x08    /* file handle refers to a pipe */
+#define FAPPEND                0x20    /* file handle opened O_APPEND */
+#define FDEV           0x40    /* file handle refers to device */
+#define FTEXT          0x80    /* file handle is in text mode */
+#define MAX_DESCRIPTOR_COUNT   (64*32) /* this is the maximun that MSVCRT can handle */
+
+int __cdecl
+_fixed_read(int fh, void *buf, unsigned cnt)
+{
+    int bytes_read;                 /* number of bytes read */
+    char *buffer;                   /* buffer to read to */
+    int os_read;                    /* bytes read on OS call */
+    char *p, *q;                    /* pointers into buffer */
+    char peekchr;                   /* peek-ahead character */
+    ULONG filepos;                  /* file position after seek */
+    ULONG dosretval;                /* o.s. return value */
+
+    /* validate handle */
+    if (((unsigned)fh >= (unsigned)MAX_DESCRIPTOR_COUNT) ||
+         !(_osfile(fh) & FOPEN))
+    {
+       /* out of range -- return error */
+       errno = EBADF;
+       _doserrno = 0;  /* not o.s. error */
+       return -1;
+    }
+
+    EnterCriticalSection(&(_pioinfo(fh)->lock));  /* lock file */
+
+    bytes_read = 0;                 /* nothing read yet */
+    buffer = (char*)buf;
+
+    if (cnt == 0 || (_osfile(fh) & FEOFLAG)) {
+        /* nothing to read or at EOF, so return 0 read */
+        goto functionexit;
+    }
+
+    if ((_osfile(fh) & (FPIPE|FDEV)) && _pipech(fh) != LF) {
+        /* a pipe/device and pipe lookahead non-empty: read the lookahead
+         * char */
+        *buffer++ = _pipech(fh);
+        ++bytes_read;
+        --cnt;
+        _pipech(fh) = LF;           /* mark as empty */
+    }
+
+    /* read the data */
+
+    if (!ReadFile((HANDLE)_osfhnd(fh), buffer, cnt, (LPDWORD)&os_read, NULL))
+    {
+        /* ReadFile has reported an error. recognize two special cases.
+         *
+         *      1. map ERROR_ACCESS_DENIED to EBADF
+         *
+         *      2. just return 0 if ERROR_BROKEN_PIPE has occurred. it
+         *         means the handle is a read-handle on a pipe for which
+         *         all write-handles have been closed and all data has been
+         *         read. */
+
+        if ((dosretval = GetLastError()) == ERROR_ACCESS_DENIED) {
+            /* wrong read/write mode should return EBADF, not EACCES */
+            errno = EBADF;
+            _doserrno = dosretval;
+            bytes_read = -1;
+           goto functionexit;
+        }
+        else if (dosretval == ERROR_BROKEN_PIPE) {
+            bytes_read = 0;
+           goto functionexit;
+        }
+        else {
+            bytes_read = -1;
+           goto functionexit;
+        }
+    }
+
+    bytes_read += os_read;          /* update bytes read */
+
+    if (_osfile(fh) & FTEXT) {
+        /* now must translate CR-LFs to LFs in the buffer */
+
+        /* set CRLF flag to indicate LF at beginning of buffer */
+        /* if ((os_read != 0) && (*(char *)buf == LF))   */
+        /*    _osfile(fh) |= FCRLF;                      */
+        /* else                                          */
+        /*    _osfile(fh) &= ~FCRLF;                     */
+
+        _osfile(fh) &= ~FCRLF;
+
+        /* convert chars in the buffer: p is src, q is dest */
+        p = q = (char*)buf;
+        while (p < (char *)buf + bytes_read) {
+            if (*p == CTRLZ) {
+                /* if fh is not a device, set ctrl-z flag */
+                if (!(_osfile(fh) & FDEV))
+                    _osfile(fh) |= FEOFLAG;
+                break;              /* stop translating */
+            }
+            else if (*p != CR)
+                *q++ = *p++;
+            else {
+                /* *p is CR, so must check next char for LF */
+                if (p < (char *)buf + bytes_read - 1) {
+                    if (*(p+1) == LF) {
+                        p += 2;
+                        *q++ = LF;  /* convert CR-LF to LF */
+                    }
+                    else
+                        *q++ = *p++;    /* store char normally */
+                }
+                else {
+                    /* This is the hard part.  We found a CR at end of
+                       buffer.  We must peek ahead to see if next char
+                       is an LF. */
+                    ++p;
+
+                    dosretval = 0;
+                    if (!ReadFile((HANDLE)_osfhnd(fh), &peekchr, 1,
+                                    (LPDWORD)&os_read, NULL))
+                        dosretval = GetLastError();
+
+                    if (dosretval != 0 || os_read == 0) {
+                        /* couldn't read ahead, store CR */
+                        *q++ = CR;
+                    }
+                    else {
+                        /* peekchr now has the extra character -- we now
+                           have several possibilities:
+                           1. disk file and char is not LF; just seek back
+                              and copy CR
+                           2. disk file and char is LF; store LF, don't seek back
+                           3. pipe/device and char is LF; store LF.
+                           4. pipe/device and char isn't LF, store CR and
+                              put char in pipe lookahead buffer. */
+                        if (_osfile(fh) & (FDEV|FPIPE)) {
+                            /* non-seekable device */
+                            if (peekchr == LF)
+                                *q++ = LF;
+                            else {
+                                *q++ = CR;
+                                _pipech(fh) = peekchr;
+                            }
+                        }
+                        else {
+                            /* disk file */
+                            if (peekchr == LF) {
+                                /* nothing read yet; must make some
+                                   progress */
+                                *q++ = LF;
+                                /* turn on this flag for tell routine */
+                                _osfile(fh) |= FCRLF;
+                            }
+                            else {
+                               HANDLE osHandle;        /* o.s. handle value */
+                                /* seek back */
+                               if ((osHandle = (HANDLE)_get_osfhandle(fh)) != (HANDLE)-1)
+                               {
+                                   if ((filepos = SetFilePointer(osHandle, -1, NULL, FILE_CURRENT)) == -1)
+                                       dosretval = GetLastError();
+                               }
+                                if (peekchr != LF)
+                                    *q++ = CR;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        /* we now change bytes_read to reflect the true number of chars
+           in the buffer */
+        bytes_read = q - (char *)buf;
+    }
+
+functionexit:  
+    LeaveCriticalSection(&(_pioinfo(fh)->lock));    /* unlock file */
+
+    return bytes_read;
+}
+
+#endif /* PERL_MSVCRT_READFIX */
+
 DllExport int
 win32_read(int fd, void *buf, unsigned int cnt)
 {
+#ifdef PERL_MSVCRT_READFIX
+    return _fixed_read(fd, buf, cnt);
+#else
     return read(fd, buf, cnt);
+#endif
 }
 
 DllExport int
@@ -2371,21 +2871,64 @@ win32_write(int fd, const void *buf, unsigned int cnt)
 DllExport int
 win32_mkdir(const char *dir, int mode)
 {
-    return mkdir(dir); /* just ignore mode */
+    dTHXo;
+    if (USING_WIDE()) {
+       WCHAR wBuffer[MAX_PATH+1];
+       A2WHELPER(dir, wBuffer, sizeof(wBuffer));
+       return _wmkdir(PerlDir_mapW(wBuffer));
+    }
+    return mkdir(PerlDir_mapA(dir)); /* just ignore mode */
 }
 
 DllExport int
 win32_rmdir(const char *dir)
 {
-    return rmdir(dir);
+    dTHXo;
+    if (USING_WIDE()) {
+       WCHAR wBuffer[MAX_PATH+1];
+       A2WHELPER(dir, wBuffer, sizeof(wBuffer));
+       return _wrmdir(PerlDir_mapW(wBuffer));
+    }
+    return rmdir(PerlDir_mapA(dir));
 }
 
 DllExport int
 win32_chdir(const char *dir)
 {
+    dTHXo;
+    if (USING_WIDE()) {
+       WCHAR wBuffer[MAX_PATH+1];
+       A2WHELPER(dir, wBuffer, sizeof(wBuffer));
+       return _wchdir(wBuffer);
+    }
     return chdir(dir);
 }
 
+DllExport  int
+win32_access(const char *path, int mode)
+{
+    dTHXo;
+    if (USING_WIDE()) {
+       WCHAR wBuffer[MAX_PATH+1];
+       A2WHELPER(path, wBuffer, sizeof(wBuffer));
+       return _waccess(PerlDir_mapW(wBuffer), mode);
+    }
+    return access(PerlDir_mapA(path), mode);
+}
+
+DllExport  int
+win32_chmod(const char *path, int mode)
+{
+    dTHXo;
+    if (USING_WIDE()) {
+       WCHAR wBuffer[MAX_PATH+1];
+       A2WHELPER(path, wBuffer, sizeof(wBuffer));
+       return _wchmod(PerlDir_mapW(wBuffer), mode);
+    }
+    return chmod(PerlDir_mapA(path), mode);
+}
+
+
 static char *
 create_command_line(const char* command, const char * const *args)
 {
@@ -2518,12 +3061,28 @@ free_childenv(void* d)
 char*
 get_childdir(void)
 {
-    return NULL;
+    dTHXo;
+    char* ptr;
+    char szfilename[(MAX_PATH+1)*2];
+    if (USING_WIDE()) {
+       WCHAR wfilename[MAX_PATH+1];
+       GetCurrentDirectoryW(MAX_PATH+1, wfilename);
+       W2AHELPER(wfilename, szfilename, sizeof(szfilename));
+    }
+    else {
+       GetCurrentDirectoryA(MAX_PATH+1, szfilename);
+    }
+
+    New(0, ptr, strlen(szfilename)+1, char);
+    strcpy(ptr, szfilename);
+    return ptr;
 }
 
 void
 free_childdir(char* d)
 {
+    dTHXo;
+    Safefree(d);
 }
 
 
@@ -2648,12 +3207,26 @@ RETVAL:
 DllExport int
 win32_execv(const char *cmdname, const char *const *argv)
 {
+#ifdef USE_ITHREADS
+    dTHXo;
+    /* 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, (char *const *)argv);
+#endif
     return execv(cmdname, (char *const *)argv);
 }
 
 DllExport int
 win32_execvp(const char *cmdname, const char *const *argv)
 {
+#ifdef USE_ITHREADS
+    dTHXo;
+    /* if this is a pseudo-forked child, we just want to spawn
+     * the new program, and return */
+    if (w32_pseudo_id)
+       return win32_spawnvp(P_WAIT, cmdname, (char *const *)argv);
+#endif
     return execvp(cmdname, (char *const *)argv);
 }
 
@@ -2836,6 +3409,10 @@ win32_free(void *block)
 int
 win32_open_osfhandle(long handle, int flags)
 {
+#ifdef USE_FIXED_OSFHANDLE
+    if (IsWin95())
+       return my_open_osfhandle(handle, flags);
+#endif
     return _open_osfhandle(handle, flags);
 }
 
@@ -2851,46 +3428,16 @@ win32_dynaload(const char* filename)
     dTHXo;
     HMODULE hModule;
     if (USING_WIDE()) {
-       WCHAR wfilename[MAX_PATH];
+       WCHAR wfilename[MAX_PATH+1];
        A2WHELPER(filename, wfilename, sizeof(wfilename));
-       hModule = LoadLibraryExW(wfilename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+       hModule = LoadLibraryExW(PerlDir_mapW(wfilename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
     }
     else {
-       hModule = LoadLibraryExA(filename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
+       hModule = LoadLibraryExA(PerlDir_mapA(filename), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
     }
     return hModule;
 }
 
-DllExport int
-win32_add_host(char *nameId, void *data)
-{
-    /*
-     * This must be called before the script is parsed,
-     * therefore no locking of threads is needed
-     */
-    dTHXo;
-    struct host_link *link;
-    New(1314, link, 1, struct host_link);
-    link->host_data = data;
-    link->nameId = nameId;
-    link->next = w32_host_link;
-    w32_host_link = link;
-    return 1;
-}
-
-DllExport void *
-win32_get_host_data(char *nameId)
-{
-    dTHXo;
-    struct host_link *link = w32_host_link;
-    while(link) {
-       if(strEQ(link->nameId, nameId))
-           return link->host_data;
-       link = link->next;
-    }
-    return Nullch;
-}
-
 /*
  * Extras.
  */
@@ -2899,19 +3446,19 @@ static
 XS(w32_GetCwd)
 {
     dXSARGS;
-    SV *sv = sv_newmortal();
-    /* Make one call with zero size - return value is required size */
-    DWORD len = GetCurrentDirectory((DWORD)0,NULL);
-    SvUPGRADE(sv,SVt_PV);
-    SvGROW(sv,len);
-    SvCUR(sv) = GetCurrentDirectory((DWORD) SvLEN(sv), SvPVX(sv));
+    /* Make the host for current directory */
+    char* ptr = PerlEnv_get_childdir();
     /* 
-     * If result != 0 
+     * If ptr != Nullch 
      *   then it worked, set PV valid, 
-     *   else leave it 'undef' 
+     *   else return 'undef' 
      */
-    EXTEND(SP,1);
-    if (SvCUR(sv)) {
+    if (ptr) {
+       SV *sv = sv_newmortal();
+       sv_setpv(sv, ptr);
+       PerlEnv_free_childdir(ptr);
+
+       EXTEND(SP,1);
        SvPOK_on(sv);
        ST(0) = sv;
        XSRETURN(1);
@@ -2925,7 +3472,7 @@ XS(w32_SetCwd)
     dXSARGS;
     if (items != 1)
        Perl_croak(aTHX_ "usage: Win32::SetCurrentDirectory($cwd)");
-    if (SetCurrentDirectory(SvPV_nolen(ST(0))))
+    if (!PerlDir_chdir(SvPV_nolen(ST(0))))
        XSRETURN_YES;
 
     XSRETURN_NO;
@@ -2971,8 +3518,8 @@ static
 XS(w32_LoginName)
 {
     dXSARGS;
-    char *name = getlogin_buffer;
-    DWORD size = sizeof(getlogin_buffer);
+    char *name = w32_getlogin_buffer;
+    DWORD size = sizeof(w32_getlogin_buffer);
     EXTEND(SP,1);
     if (GetUserName(name,&size)) {
        /* size includes NULL */
@@ -3048,7 +3595,7 @@ XS(w32_DomainName)
        if (hNetApi32)
            FreeLibrary(hNetApi32);
        if (GetUserName(name,&size)) {
-           char sid[1024];
+           char sid[ONE_K_BUFSIZE];
            DWORD sidlen = sizeof(sid);
            char dname[256];
            DWORD dnamelen = sizeof(dname);
@@ -3087,19 +3634,34 @@ static
 XS(w32_GetOSVersion)
 {
     dXSARGS;
-    OSVERSIONINFO osver;
+    OSVERSIONINFOA osver;
 
-    osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
-    if (GetVersionEx(&osver)) {
+    if (USING_WIDE()) {
+       OSVERSIONINFOW osverw;
+       char szCSDVersion[sizeof(osverw.szCSDVersion)];
+       osverw.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+       if (!GetVersionExW(&osverw)) {
+           XSRETURN_EMPTY;
+       }
+       W2AHELPER(osverw.szCSDVersion, szCSDVersion, sizeof(szCSDVersion));
+       XPUSHs(newSVpvn(szCSDVersion, strlen(szCSDVersion)));
+       osver.dwMajorVersion = osverw.dwMajorVersion;
+       osver.dwMinorVersion = osverw.dwMinorVersion;
+       osver.dwBuildNumber = osverw.dwBuildNumber;
+       osver.dwPlatformId = osverw.dwPlatformId;
+    }
+    else {
+       osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+       if (!GetVersionExA(&osver)) {
+           XSRETURN_EMPTY;
+       }
        XPUSHs(newSVpvn(osver.szCSDVersion, strlen(osver.szCSDVersion)));
-       XPUSHs(newSViv(osver.dwMajorVersion));
-       XPUSHs(newSViv(osver.dwMinorVersion));
-       XPUSHs(newSViv(osver.dwBuildNumber));
-       XPUSHs(newSViv(osver.dwPlatformId));
-       PUTBACK;
-       return;
     }
-    XSRETURN_EMPTY;
+    XPUSHs(newSViv(osver.dwMajorVersion));
+    XPUSHs(newSViv(osver.dwMinorVersion));
+    XPUSHs(newSViv(osver.dwBuildNumber));
+    XPUSHs(newSViv(osver.dwPlatformId));
+    PUTBACK;
 }
 
 static
@@ -3123,15 +3685,27 @@ XS(w32_FormatMessage)
 {
     dXSARGS;
     DWORD source = 0;
-    char msgbuf[1024];
+    char msgbuf[ONE_K_BUFSIZE];
 
     if (items != 1)
        Perl_croak(aTHX_ "usage: Win32::FormatMessage($errno)");
 
-    if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
-                     &source, SvIV(ST(0)), 0,
-                     msgbuf, sizeof(msgbuf)-1, NULL))
-       XSRETURN_PV(msgbuf);
+    if (USING_WIDE()) {
+       WCHAR wmsgbuf[ONE_K_BUFSIZE];
+       if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM,
+                         &source, SvIV(ST(0)), 0,
+                         wmsgbuf, ONE_K_BUFSIZE-1, NULL))
+       {
+           W2AHELPER(wmsgbuf, msgbuf, sizeof(msgbuf));
+           XSRETURN_PV(msgbuf);
+       }
+    }
+    else {
+       if (FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
+                         &source, SvIV(ST(0)), 0,
+                         msgbuf, sizeof(msgbuf)-1, NULL))
+           XSRETURN_PV(msgbuf);
+    }
 
     XSRETURN_UNDEF;
 }
@@ -3284,9 +3858,24 @@ static
 XS(w32_CopyFile)
 {
     dXSARGS;
+    BOOL bResult;
     if (items != 3)
        Perl_croak(aTHX_ "usage: Win32::CopyFile($from, $to, $overwrite)");
-    if (CopyFile(SvPV_nolen(ST(0)), SvPV_nolen(ST(1)), !SvTRUE(ST(2))))
+    if (USING_WIDE()) {
+       WCHAR wSourceFile[MAX_PATH+1];
+       WCHAR wDestFile[MAX_PATH+1];
+       A2WHELPER(SvPV_nolen(ST(0)), wSourceFile, sizeof(wSourceFile));
+       wcscpy(wSourceFile, PerlDir_mapW(wSourceFile));
+       A2WHELPER(SvPV_nolen(ST(1)), wDestFile, sizeof(wDestFile));
+       bResult = CopyFileW(wSourceFile, PerlDir_mapW(wDestFile), !SvTRUE(ST(2)));
+    }
+    else {
+       char szSourceFile[MAX_PATH+1];
+       strcpy(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(0))));
+       bResult = CopyFileA(szSourceFile, PerlDir_mapA(SvPV_nolen(ST(1))), !SvTRUE(ST(2)));
+    }
+
+    if (bResult)
        XSRETURN_YES;
     XSRETURN_NO;
 }
@@ -3303,6 +3892,12 @@ Perl_init_os_extras(void)
     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);
@@ -3353,28 +3948,26 @@ Perl_win32_init(int *argcp, char ***argvp)
     MALLOC_INIT;
 }
 
-#ifdef USE_BINMODE_SCRIPTS
+#ifdef USE_ITHREADS
+
+#  ifdef PERL_OBJECT
+#    undef Perl_sys_intern_dup
+#    define Perl_sys_intern_dup CPerlObj::Perl_sys_intern_dup
+#    define pPerl this
+#  endif
 
 void
-win32_strip_return(SV *sv)
-{
- char *s = SvPVX(sv);
- char *e = s+SvCUR(sv);
- char *d = s;
- while (s < e)
-  {
-   if (*s == '\r' && s[1] == '\n')
-    {
-     *d++ = '\n';
-     s += 2;
-    }
-   else 
-    {
-     *d++ = *s++;
-    }   
-  }
- SvCUR_set(sv,d-SvPVX(sv)); 
+Perl_sys_intern_dup(pTHX_ struct interp_intern *src, struct interp_intern *dst)
+{
+    dst->perlshell_tokens      = Nullch;
+    dst->perlshell_vec         = (char**)NULL;
+    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;
 }
-
 #endif