add workaround for textmode read() bug in MSVCRT; make chdir() do
[p5sagit/p5-mst-13.2.git] / win32 / win32.c
index 4c13d4a..78955fc 100644 (file)
@@ -15,6 +15,8 @@
 #define Win32_Winsock
 #endif
 #include <windows.h>
+#include <winnt.h>
+#include <io.h>
 
 /* #include "config.h" */
 
@@ -174,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)
@@ -211,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;
@@ -281,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);
@@ -2369,8 +2366,13 @@ Nt4CreateHardLinkW(
     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);
@@ -2593,10 +2595,240 @@ 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 */
+
+/*
+ * 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)
+
+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
@@ -3681,31 +3913,6 @@ Perl_win32_init(int *argcp, char ***argvp)
     MALLOC_INIT;
 }
 
-#ifdef USE_BINMODE_SCRIPTS
-
-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)); 
-}
-
-#endif
-
 #ifdef USE_ITHREADS
 
 #  ifdef PERL_OBJECT