Only handle PL_rs differently on VMS for record-oriented files.
authorCraig A. Berry <craigberry@mac.com>
Fri, 16 Mar 2012 19:20:29 +0000 (14:20 -0500)
committerTony Cook <tony@develop-help.com>
Sat, 8 Dec 2012 22:32:47 +0000 (09:32 +1100)
For stream-oriented files, the effects of buffering and other
layers should be exactly as they are on other platforms. For true,
record-oriented files, though, setting $/ = \number must provide
exactly one low-level read per record.  If a read were ever to
return less than a full record (due to, for example, filling up
the perlio buffer), a subsequent read would get the *next* record,
losing whatever data remained in the partially-read record.

sv.c

diff --git a/sv.c b/sv.c
index da10129..c611e97 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -7671,22 +7671,31 @@ S_sv_gets_read_record(pTHX_ SV *const sv, PerlIO *const fp, I32 append)
     const STRLEN recsize = SvUV(SvRV(PL_rs)); /* RsRECORD() guarantees > 0. */
       /* Grab the size of the record we're getting */
     char *buffer = SvGROW(sv, (STRLEN)(recsize + append + 1)) + append;
+    
+    /* Go yank in */
 #ifdef VMS
+#include <rms.h>
     int fd;
-#endif
+    Stat_t st;
 
-    /* Go yank in */
-#ifdef VMS
-    /* VMS wants read instead of fread, because fread doesn't respect */
-    /* RMS record boundaries. This is not necessarily a good thing to be */
-    /* doing, but we've got no other real choice - except avoid stdio
-       as implementation - perhaps write a :vms layer ?
-    */
+    /* With a true, record-oriented file on VMS, we need to use read directly
+     * to ensure that we respect RMS record boundaries.  The user is responsible
+     * for providing a PL_rs value that corresponds to the FAB$W_MRS (maximum
+     * record size) field.  N.B. This is likely to produce invalid results on
+     * varying-width character data when a record ends mid-character.
+     */
     fd = PerlIO_fileno(fp);
-    if (fd != -1) {
+    if (fd != -1
+       && PerlLIO_fstat(fd, &st) == 0
+       && (st.st_fab_rfm == FAB$C_VAR
+           || st.st_fab_rfm == FAB$C_VFC
+           || st.st_fab_rfm == FAB$C_FIX)) {
+
        bytesread = PerlLIO_read(fd, buffer, recsize);
     }
-    else /* in-memory file from PerlIO::Scalar */
+    else /* in-memory file from PerlIO::Scalar
+          * or not a record-oriented file
+          */
 #endif
     {
        bytesread = PerlIO_read(fp, buffer, recsize);