add workaround for textmode read() bug in MSVCRT; make chdir() do
[p5sagit/p5-mst-13.2.git] / win32 / win32.c
index 43e9b5e..78955fc 100644 (file)
@@ -2595,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