This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Thomas.Koenig@online.de: PR libfortran/29568 (subrecord patch)]


*sigh* I forgot gcc-patches.

----- Forwarded message from Thomas Koenig <Thomas.Koenig@online.de> -----

:ADDPATCH fortran:

Hello world,

after quite some time, here it is:  The gfortran subrecord patch
(aka implementing the Intel format for unformatted files).  This was
regression-tested on i686-pc-linux-gnu.  A slightly earlier version
(which was missing some casts) was extensively tested on 64-bit
Linux and FreeBSD by Jerry DeLisle (thanks a lot, Jerry!)

I introduced a new flag with this patch to specify a smaller
maximum length of subrecords.  This enables automatic
regression-testing of the subrecod feature, which I think is
important.  It is very easy to break something in this
area of the library, and not having a regression-test at all
would be bad.

With this patch, we're having a flag day:  Unformatted sequential
files written with default options in eariler versions of gfortran
(including 4.1) will not be readable with default options.
The minutes from the IRC planning meeting showed that this was
preferred way over being incompatible with just about anybody
else (and I agree, or I wouldn't have written the patch :-)  We
should update the wiki accordingly, and make an announcement on
comp.lang.fortran.

Still missing:  a description of the subrecord format.

I propose to apply this to 4.3, wait for some time (more than
the customary week) for any problems to appear, and then backport
to 4.2.

OK?

	Thomas

2006-11-30  Thomas Koenig  <Thomas.Koenig@online.de>

	PR libfortran/29568
	* gfortran.dg/convert_implied_open.f90:  Change to
	new default record length.
	* gfortran.dg/unf_short_record_1.f90:  Adapt to
	new error message.
	* gfortran.dg/unformatted_subrecords_1.f90:  New test.

2006-11-30  Thomas Koenig  <Thomas.Koenig@online.de>

	PR libfortran/29568
	* gfortran.h (gfc_option_t):  Add max_subrecord_length.
	(top level): Define MAX_SUBRECORD_LENGTH.
	* lang.opt:  Add option -fmax-subrecord-length=.
	* trans-decl.c:  Add new function set_max_subrecord_length.
	(gfc_generate_function_code): If we are within the main
	program and max_subrecord_length has been set, call
	set_max_subrecord_length.
	* options.c (gfc_handle_option):  Add handling for
	-fmax_subrecord_length.
	* invoke.texi:  Document the new default for
	-frecord-marker=<n>.

2006-11-30  Thomas Koenig  <Thomas.Koenig@online.de>

	PR libfortran/29568
	* libgfortran/libgfortran.h (compile_options_t):  Add
	record_marker. (top level):  Define GFC_MAX_SUBRECORD_LENGTH.
	* runtime/compile_options.c (set_record_marker):  Change
	default to four-byte record marker.
	(set_max_subrecord_length):  New function.
	* runtime/error.c (translate_error):  Change error message
	for short record on unformatted read.
	* io/io.h (gfc_unit):  Add recl_subrecord, bytes_left_subrecord
	and continued.
	* io/file_pos.c (unformatted_backspace):  Change default of record
	marker size to four bytes.  Loop over subrecords.
	* io/open.c:  Default recl is max_offset.  If
	compile_options.max_subrecord_length has been set, set set
	u->recl_subrecord to its value, to the maximum value otherwise.
	* io/transfer.c (top level):  Add prototypes for us_read, us_write,
	next_record_r_unf and next_record_w_unf.
	(read_block_direct):  Separate codepaths for unformatted direct
	and unformatted sequential.  If a recl has been set by the
	user, use the number of bytes left for the record if it is smaller
	than the read request.  Loop over subrecords.  Set an error if the
	user has set a recl and the read was short.
	(write_buf):  Separate codepaths for unformatted direct and
	unformatted sequential. If a recl has been set by the
	user, use the number of bytes left for the record if it is smaller
	than the read request.  Loop over subrecords.  Set an error if the
	user has set a recl and the read was short.
	(us_read):  Add parameter continued (to indicate that bytes_left
	should not be intialized).  Change default of record marker size
	to four bytes. Use subrecord.  If the subrecord length is smaller than
	zero, this indicates a continuation.
	(us_write):  Add parameter continued (to indicate that the continued
	flag should be set).  Use subrecord.
	(pre_position):  Use 0 for continued on us_write and us_read calls.
	(skip_record):  New function.
	(next_record_r_unf):  New function.
	(next_record_r):  Use next_record_r_unf.
	(write_us_marker):  Default size for record markers is four bytes.
	(next_record_w_unf):  New function.
	(next_record_w):  Use next_record_w_unf.

Index: gcc/testsuite/gfortran.dg/convert_implied_open.f90
===================================================================
--- gcc/testsuite/gfortran.dg/convert_implied_open.f90	(revision 119261)
+++ gcc/testsuite/gfortran.dg/convert_implied_open.f90	(working copy)
@@ -3,13 +3,13 @@
 ! PR 26735 - implied open didn't use to honor -fconvert
 program main
   implicit none
-  integer (kind=8) :: i1, i2, i3
-  write (10) 1_8
+  integer (kind=4) :: i1, i2, i3
+  write (10) 1_4
   close (10)
-  open (10, form="unformatted", access="direct", recl=8)
+  open (10, form="unformatted", access="direct", recl=4)
   read (10,rec=1) i1
   read (10,rec=2) i2
   read (10,rec=3) i3
-  if (i1 /= 8 .or. i2 /= 1 .or. i3 /= 8) call abort
+  if (i1 /= 4 .or. i2 /= 1 .or. i3 /= 4) call abort
   close (10,status="delete")
 end program main
Index: gcc/testsuite/gfortran.dg/unf_short_record_1.f90
===================================================================
--- gcc/testsuite/gfortran.dg/unf_short_record_1.f90	(revision 119261)
+++ gcc/testsuite/gfortran.dg/unf_short_record_1.f90	(working copy)
@@ -11,7 +11,7 @@ program main
   read (10, err=20, iomsg=msg) a
   call abort
 20 continue
-  if (msg .ne. "Short record on unformatted read") call abort
+  if (msg .ne. "I/O past end of record on unformatted file") call abort
   if (a(1) .ne. 'a' .or. a(2) .ne. 'b' .or. a(3) .ne. 'b') call abort
   close (10, status="delete")
 end program main
Index: gcc/fortran/gfortran.h
===================================================================
--- gcc/fortran/gfortran.h	(revision 119261)
+++ gcc/fortran/gfortran.h	(working copy)
@@ -1661,11 +1661,13 @@ typedef struct
   int fshort_enums;
   int convert;
   int record_marker;
+  int max_subrecord_length;
 }
 gfc_option_t;
 
 extern gfc_option_t gfc_option;
 
+#define MAX_SUBRECORD_LENGTH ((int) ((1u<<31u) - 9u))
 
 /* Constructor nodes for array and structure constructors.  */
 typedef struct gfc_constructor
Index: gcc/fortran/lang.opt
===================================================================
--- gcc/fortran/lang.opt	(revision 119261)
+++ gcc/fortran/lang.opt	(working copy)
@@ -189,6 +189,10 @@ fmax-identifier-length=
 Fortran RejectNegative Joined UInteger
 -fmax-identifier-length=<n>	Maximum identifier length
 
+fmax-subrecord-length=
+Fortran RejectNegative Joined UInteger
+-fmax-subrecord-length=<n>	Maximum length for subrecords
+
 fmax-stack-var-size=
 Fortran RejectNegative Joined UInteger
 -fmax-stack-var-size=<n>	Size in bytes of the largest array that will be put on the stack
Index: gcc/fortran/trans-decl.c
===================================================================
--- gcc/fortran/trans-decl.c	(revision 119261)
+++ gcc/fortran/trans-decl.c	(working copy)
@@ -94,6 +94,7 @@ tree gfor_fndecl_set_fpe;
 tree gfor_fndecl_set_std;
 tree gfor_fndecl_set_convert;
 tree gfor_fndecl_set_record_marker;
+tree gfor_fndecl_set_max_subrecord_length;
 tree gfor_fndecl_ctime;
 tree gfor_fndecl_fdate;
 tree gfor_fndecl_ttynam;
@@ -2379,6 +2380,10 @@ gfc_build_builtin_function_decls (void)
     gfc_build_library_function_decl (get_identifier (PREFIX("set_record_marker")),
 				     void_type_node, 1, gfc_c_int_type_node);
 
+  gfor_fndecl_set_max_subrecord_length =
+    gfc_build_library_function_decl (get_identifier (PREFIX("set_max_subrecord_length")),
+				     void_type_node, 1, gfc_c_int_type_node);
+
   gfor_fndecl_in_pack = gfc_build_library_function_decl (
         get_identifier (PREFIX("internal_pack")),
         pvoid_type_node, 1, pvoid_type_node);
@@ -3187,6 +3192,18 @@ gfc_generate_function_code (gfc_namespac
 
     }
 
+  if (sym->attr.is_main_program && gfc_option.max_subrecord_length != 0)
+    {
+      tree arglist, gfc_c_int_type_node;
+
+      gfc_c_int_type_node = gfc_get_int_type (gfc_c_int_kind);
+      arglist = gfc_chainon_list (NULL_TREE,
+				  build_int_cst (gfc_c_int_type_node,
+						 gfc_option.max_subrecord_length));
+      tmp = build_function_call_expr (gfor_fndecl_set_max_subrecord_length, arglist);
+      gfc_add_expr_to_block (&body, tmp);
+    }
+
   if (TREE_TYPE (DECL_RESULT (fndecl)) != void_type_node
       && sym->attr.subroutine)
     {
Index: gcc/fortran/options.c
===================================================================
--- gcc/fortran/options.c	(revision 119261)
+++ gcc/fortran/options.c	(working copy)
@@ -636,6 +636,12 @@ gfc_handle_option (size_t scode, const c
     case OPT_frecord_marker_8:
       gfc_option.record_marker = 8;
       break;
+
+    case OPT_fmax_subrecord_length_:
+      if (value > MAX_SUBRECORD_LENGTH || value < 1)
+	gfc_fatal_error ("Maximum subrecord length is %d", MAX_SUBRECORD_LENGTH);
+
+      gfc_option.max_subrecord_length = value;
     }
 
   return result;
Index: libgfortran/runtime/compile_options.c
===================================================================
--- libgfortran/runtime/compile_options.c	(revision 119261)
+++ libgfortran/runtime/compile_options.c	(working copy)
@@ -86,13 +86,11 @@ set_record_marker (int val)
   switch(val)
     {
     case 4:
-      if (sizeof (GFC_INTEGER_4) != sizeof (gfc_offset))
-	compile_options.record_marker = sizeof (GFC_INTEGER_4);
+      compile_options.record_marker = sizeof (GFC_INTEGER_4);
       break;
 
     case 8:
-      if (sizeof (GFC_INTEGER_8) != sizeof (gfc_offset))
-	compile_options.record_marker = sizeof (GFC_INTEGER_8);
+      compile_options.record_marker = sizeof (GFC_INTEGER_8);
       break;
 
     default:
@@ -100,3 +98,17 @@ set_record_marker (int val)
       break;
     }
 }
+
+extern void set_max_subrecord_length (int);
+export_proto (set_max_subrecord_length);
+
+void set_max_subrecord_length(int val)
+{
+  if (val > GFC_MAX_SUBRECORD_LENGTH || val < 1)
+    {
+      runtime_error ("Invalid value for maximum subrecord length");
+      return;
+    }
+
+  compile_options.max_subrecord_length = val;
+}
Index: libgfortran/runtime/error.c
===================================================================
--- libgfortran/runtime/error.c	(revision 119261)
+++ libgfortran/runtime/error.c	(working copy)
@@ -437,7 +437,7 @@ translate_error (int code)
       break;
 
     case ERROR_SHORT_RECORD:
-      p = "Short record on unformatted read";
+      p = "I/O past end of record on unformatted file";
       break;
 
     default:
Index: libgfortran/libgfortran.h
===================================================================
--- libgfortran/libgfortran.h	(revision 119261)
+++ libgfortran/libgfortran.h	(working copy)
@@ -370,6 +370,7 @@ typedef struct
   int pedantic;
   int convert;
   size_t record_marker;
+  int max_subrecord_length;
 }
 compile_options_t;
 
@@ -379,6 +380,7 @@ internal_proto(compile_options);
 extern void init_compile_options (void);
 internal_proto(init_compile_options);
 
+#define GFC_MAX_SUBRECORD_LENGTH (GFC_INTEGER_4_HUGE - 8)
 
 /* Structure for statement options.  */
 
Index: libgfortran/io/file_pos.c
===================================================================
--- libgfortran/io/file_pos.c	(revision 119261)
+++ libgfortran/io/file_pos.c	(working copy)
@@ -98,7 +98,7 @@ formatted_backspace (st_parameter_filepo
 
 /* unformatted_backspace(fpp) -- Move the file backwards for an unformatted
    sequential file.  We are guaranteed to be between records on entry and 
-   we have to shift to the previous record.  */
+   we have to shift to the previous record.  Loop over subrecords.  */
 
 static void
 unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u)
@@ -107,74 +107,74 @@ unformatted_backspace (st_parameter_file
   GFC_INTEGER_4 m4;
   GFC_INTEGER_8 m8;
   int length, length_read;
+  int continued;
   char *p;
 
   if (compile_options.record_marker == 0)
-    length = sizeof (gfc_offset);
+    length = sizeof (GFC_INTEGER_4);
   else
     length = compile_options.record_marker;
 
-  length_read = length;
+  do
+    {
+      length_read = length;
 
-  p = salloc_r_at (u->s, &length_read,
-		   file_position (u->s) - length);
-  if (p == NULL || length_read != length)
-    goto io_error;
+      p = salloc_r_at (u->s, &length_read,
+		       file_position (u->s) - length);
+      if (p == NULL || length_read != length)
+	goto io_error;
 
-  /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here.  */
-  if (u->flags.convert == CONVERT_NATIVE)
-    {
-      switch (compile_options.record_marker)
+      /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here.  */
+      if (u->flags.convert == CONVERT_NATIVE)
 	{
-	case 0:
-	  memcpy (&m, p, sizeof(gfc_offset));
-	  break;
-
-	case sizeof(GFC_INTEGER_4):
-	  memcpy (&m4, p, sizeof (m4));
-	  m = m4;
-	  break;
-
-	case sizeof(GFC_INTEGER_8):
-	  memcpy (&m8, p, sizeof (m8));
-	  m = m8;
-	  break;
-
-	default:
-	  runtime_error ("Illegal value for record marker");
-	  break;
+	  switch (length)
+	    {
+	    case sizeof(GFC_INTEGER_4):
+	      memcpy (&m4, p, sizeof (m4));
+	      m = m4;
+	      break;
+
+	    case sizeof(GFC_INTEGER_8):
+	      memcpy (&m8, p, sizeof (m8));
+	      m = m8;
+	      break;
+
+	    default:
+	      runtime_error ("Illegal value for record marker");
+	      break;
+	    }
 	}
-    }
-  else
-    {
-      switch (compile_options.record_marker)
+      else
 	{
-	case 0:
-	  reverse_memcpy (&m, p, sizeof(gfc_offset));
-	  break;
-
-	case sizeof(GFC_INTEGER_4):
-	  reverse_memcpy (&m4, p, sizeof (m4));
-	  m = m4;
-	  break;
-
-	case sizeof(GFC_INTEGER_8):
-	  reverse_memcpy (&m8, p, sizeof (m8));
-	  m = m8;
-	  break;
-
-	default:
-	  runtime_error ("Illegal value for record marker");
-	  break;
-	}
+	  switch (length)
+	    {
+	    case sizeof(GFC_INTEGER_4):
+	      reverse_memcpy (&m4, p, sizeof (m4));
+	      m = m4;
+	      break;
+
+	    case sizeof(GFC_INTEGER_8):
+	      reverse_memcpy (&m8, p, sizeof (m8));
+	      m = m8;
+	      break;
+
+	    default:
+	      runtime_error ("Illegal value for record marker");
+	      break;
+	    }
 
-    }
-
-  if ((new = file_position (u->s) - m - 2*length) < 0)
-    new = 0;
+	}
 
-  if (sseek (u->s, new) == FAILURE)
-    goto io_error;
+      continued = m < 0;
+      if (continued)
+	m = -m;
+
+      if ((new = file_position (u->s) - m - 2*length) < 0)
+	new = 0;
+
+      if (sseek (u->s, new) == FAILURE)
+	goto io_error;
+    } while (continued);
 
   u->last_record--;
   return;
Index: libgfortran/io/open.c
===================================================================
--- libgfortran/io/open.c	(revision 119261)
+++ libgfortran/io/open.c	(working copy)
@@ -413,23 +413,29 @@ new_unit (st_parameter_open *opp, gfc_un
   else
     {
       u->flags.has_recl = 0;
-      switch (compile_options.record_marker)
+      u->recl = max_offset;
+      if (compile_options.max_subrecord_length)
 	{
-	case 0:
-	  u->recl = max_offset;
-	  break;
-
-	case sizeof (GFC_INTEGER_4):
-	  u->recl = GFC_INTEGER_4_HUGE;
-	  break;
-
-	case sizeof (GFC_INTEGER_8):
-	  u->recl = max_offset;
-	  break;
-
-	default:
-	  runtime_error ("Illegal value for record marker");
-	  break;
+	  u->recl_subrecord = compile_options.max_subrecord_length;
+	}
+      else
+	{
+	  switch (compile_options.record_marker)
+	    {
+	    case 0:
+	      /* Fall through */
+	    case sizeof (GFC_INTEGER_4):
+	      u->recl_subrecord = GFC_MAX_SUBRECORD_LENGTH;
+	      break;
+
+	    case sizeof (GFC_INTEGER_8):
+	      u->recl_subrecord = max_offset - 16;
+	      break;
+
+	    default:
+	      runtime_error ("Illegal value for record marker");
+	      break;
+	    }
 	}
     }
 
Index: libgfortran/io/io.h
===================================================================
--- libgfortran/io/io.h	(revision 119261)
+++ libgfortran/io/io.h	(working copy)
@@ -499,12 +499,19 @@ typedef struct gfc_unit
   unit_mode mode;
   unit_flags flags;
 
-  /* recl           -- Record length of the file.
-     last_record    -- Last record number read or written
-     maxrec         -- Maximum record number in a direct access file
-     bytes_left     -- Bytes left in current record.
-     strm_pos       -- Current position in file for STREAM I/O.  */
-  gfc_offset recl, last_record, maxrec, bytes_left, strm_pos;
+  /* recl                 -- Record length of the file.
+     last_record          -- Last record number read or written
+     maxrec               -- Maximum record number in a direct access file
+     bytes_left           -- Bytes left in current record.
+     strm_pos             -- Current position in file for STREAM I/O.
+     recl_subrecord       -- Maximum length for subrecord.
+     bytes_left_subrecord -- Bytes left in current subrecord.  */
+  gfc_offset recl, last_record, maxrec, bytes_left, strm_pos,
+    recl_subrecord, bytes_left_subrecord;
+
+  /* Set to 1 if we have read a subrecord.  */
+
+  int continued;
 
   __gthread_mutex_t lock;
   /* Number of threads waiting to acquire this unit's lock.
Index: libgfortran/io/transfer.c
===================================================================
--- libgfortran/io/transfer.c	(revision 119261)
+++ libgfortran/io/transfer.c	(working copy)
@@ -82,6 +82,11 @@ extern void transfer_array (st_parameter
 			    gfc_charlen_type);
 export_proto(transfer_array);
 
+static void us_read (st_parameter_dt *, int);
+static void us_write (st_parameter_dt *, int);
+static void next_record_r_unf (st_parameter_dt *, int);
+static void next_record_w_unf (st_parameter_dt *, int);
+
 static const st_option advance_opt[] = {
   {"yes", ADVANCE_YES},
   {"no", ADVANCE_NO},
@@ -336,12 +341,16 @@ read_block (st_parameter_dt *dtp, int *l
 }
 
 
-/* Reads a block directly into application data space.  */
+/* Reads a block directly into application data space.  This is for
+   unformatted files.  */
 
 static void
 read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes)
 {
-  size_t nread;
+  size_t to_read_record;
+  size_t have_read_record;
+  size_t to_read_subrecord;
+  size_t have_read_subrecord;
   int short_record;
 
   if (is_stream_io (dtp))
@@ -353,62 +362,169 @@ read_block_direct (st_parameter_dt *dtp,
 	  return;
 	}
 
-      nread = *nbytes;
-      if (sread (dtp->u.p.current_unit->s, buf, &nread) != 0)
+      to_read_record = *nbytes;
+      have_read_record = to_read_record;
+      if (sread (dtp->u.p.current_unit->s, buf, &have_read_record) != 0)
 	{
 	  generate_error (&dtp->common, ERROR_OS, NULL);
 	  return;
 	}
 
-      dtp->u.p.current_unit->strm_pos += (gfc_offset) nread; 
-
-      if (nread != *nbytes)  /* Short read, e.g. if we hit EOF.  */
-	generate_error (&dtp->common, ERROR_END, NULL);	  
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) have_read_record; 
 
+      if (to_read_record != have_read_record)
+	{
+	  /* Short read,  e.g. if we hit EOF.  */
+	  generate_error (&dtp->common, ERROR_END, NULL);
+	  return;
+	}
       return;
     }
 
-  /* Unformatted file with records */
-  if (dtp->u.p.current_unit->bytes_left < (gfc_offset) *nbytes)
+  if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT)
     {
-      short_record = 1;
-      nread = (size_t) dtp->u.p.current_unit->bytes_left;
-      *nbytes = nread;
+      if (dtp->u.p.current_unit->bytes_left < (gfc_offset) *nbytes)
+	{
+	  short_record = 1;
+	  to_read_record = (size_t) dtp->u.p.current_unit->bytes_left;
+	  *nbytes = to_read_record;
 
-      if (dtp->u.p.current_unit->bytes_left == 0)
+	  if (dtp->u.p.current_unit->bytes_left == 0)
+	    {
+	      dtp->u.p.current_unit->endfile = AT_ENDFILE;
+	      generate_error (&dtp->common, ERROR_END, NULL);
+	      return;
+	    }
+	}
+
+      else
+	{
+	  short_record = 0;
+	  to_read_record = *nbytes;
+	}
+
+      dtp->u.p.current_unit->bytes_left -= to_read_record;
+
+      if (sread (dtp->u.p.current_unit->s, buf, &to_read_record) != 0)
+	{
+	  generate_error (&dtp->common, ERROR_OS, NULL);
+	  return;
+	}
+
+      if (to_read_record != *nbytes)  /* Short read, e.g. if we hit EOF.  */
 	{
-	  dtp->u.p.current_unit->endfile = AT_ENDFILE;
+	  *nbytes = to_read_record;
 	  generate_error (&dtp->common, ERROR_END, NULL);
 	  return;
 	}
+
+      if (short_record)
+	{
+	  generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL);
+	  return;
+	}
+      return;
     }
 
+  /* Unformatted sequential.  We loop over the subrecords, reading
+     until the request has been fulfilled or the record has run out
+     of continuation subrecords.  */
+
+  /* Check whether we exceed the total record length.  */
+
+  if (dtp->u.p.current_unit->flags.has_recl)
+    {
+      to_read_record =
+	*nbytes > (size_t) dtp->u.p.current_unit->bytes_left ?
+	*nbytes : (size_t) dtp->u.p.current_unit->bytes_left;
+      short_record = 1;
+    }
   else
     {
+      to_read_record = *nbytes;
       short_record = 0;
-      nread = *nbytes;
     }
+  have_read_record = 0;
 
-  dtp->u.p.current_unit->bytes_left -= nread;
-
-  if (sread (dtp->u.p.current_unit->s, buf, &nread) != 0)
+  while(1)
     {
-      generate_error (&dtp->common, ERROR_OS, NULL);
-      return;
-    }
+      if (dtp->u.p.current_unit->bytes_left_subrecord
+	  < (gfc_offset) to_read_record)
+	{
+	  to_read_subrecord = (size_t) dtp->u.p.current_unit->bytes_left_subrecord;
+	  to_read_record -= to_read_subrecord;
 
-  if (nread != *nbytes)  /* Short read, e.g. if we hit EOF.  */
-    {
-      *nbytes = nread;
-      generate_error (&dtp->common, ERROR_END, NULL);
-      return;
+	  if (dtp->u.p.current_unit->bytes_left_subrecord == 0)
+	    {
+	      if (dtp->u.p.current_unit->continued)
+		{
+		  /* Skip to the next subrecord */
+		  next_record_r_unf (dtp, 0);
+		  us_read (dtp, 1);
+		  continue;
+		}
+	      else
+		{
+		  dtp->u.p.current_unit->endfile = AT_ENDFILE;
+		  generate_error (&dtp->common, ERROR_END, NULL);
+		  return;
+		}
+	    }
+	}
+
+      else
+	{
+	  to_read_subrecord = to_read_record;
+	  to_read_record = 0;
+	}
+
+      dtp->u.p.current_unit->bytes_left_subrecord -= to_read_subrecord;
+
+      have_read_subrecord = to_read_subrecord;
+      if (sread (dtp->u.p.current_unit->s, buf + have_read_record,
+		 &have_read_subrecord) != 0)
+	{
+	  generate_error (&dtp->common, ERROR_OS, NULL);
+	  return;
+	}
+
+      have_read_record += have_read_subrecord;
+
+      if (to_read_subrecord != have_read_subrecord)  /* Short read,
+							e.g. if we hit EOF.  */
+	{
+	  *nbytes = have_read_record;
+	  generate_error (&dtp->common, ERROR_END, NULL);
+	  return;
+	}
+
+      if (to_read_record > 0)
+	{
+	  if (dtp->u.p.current_unit->continued)
+	    {
+	      next_record_r_unf (dtp, 0);
+	      us_read (dtp, 1);
+	    }
+	  else
+	    {
+	      generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL);
+	      return;
+	    }
+	}
+      else
+	{
+	  /* Normal exit, the read request has been fulfilled.  */
+	  break;
+	}
     }
 
+  dtp->u.p.current_unit->bytes_left -= have_read_record;
   if (short_record)
     {
       generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL);
       return;
     }
+  return;
 }
 
 
@@ -471,11 +587,20 @@ write_block (st_parameter_dt *dtp, int l
 }
 
 
-/* High level interface to swrite(), taking care of errors.  */
+/* High level interface to swrite(), taking care of errors.  This is only
+   called for unformatted files.  There are three cases to consider:
+   Stream I/O, unformatted direct, unformatted sequential.  */
 
 static try
 write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes)
 {
+
+  size_t have_written, to_write_subrecord;
+  int short_record;
+
+
+  /* Stream I/O.  */
+
   if (is_stream_io (dtp))
     {
       if (sseek (dtp->u.p.current_unit->s,
@@ -484,42 +609,88 @@ write_buf (st_parameter_dt *dtp, void *b
 	  generate_error (&dtp->common, ERROR_OS, NULL);
 	  return FAILURE;
 	}
+
+      if (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0)
+	{
+	  generate_error (&dtp->common, ERROR_OS, NULL);
+	  return FAILURE;
+	}
+
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; 
+
+      return SUCCESS;
     }
-  else
+
+  /* Unformatted direct access.  */
+
+  if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT)
     {
       if (dtp->u.p.current_unit->bytes_left < (gfc_offset) nbytes)
 	{
-	  /* For preconnected units with default record length, set
-	     bytes left to unit record length and proceed, otherwise
-	     error.  */
-	  if ((dtp->u.p.current_unit->unit_number == options.stdout_unit
-	       || dtp->u.p.current_unit->unit_number == options.stderr_unit)
-	      && dtp->u.p.current_unit->recl == DEFAULT_RECL)
-	    dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
-	  else
-	    {
-	      if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT)
-		generate_error (&dtp->common, ERROR_DIRECT_EOR, NULL);
-	      else
-		generate_error (&dtp->common, ERROR_EOR, NULL);
-	      return FAILURE;
-	    }
+	  generate_error (&dtp->common, ERROR_DIRECT_EOR, NULL);
+	  return FAILURE;
+	}
+
+      if (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0)
+	{
+	  generate_error (&dtp->common, ERROR_OS, NULL);
+	  return FAILURE;
 	}
 
-      dtp->u.p.current_unit->bytes_left -= (gfc_offset) nbytes;
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; 
+
+      return SUCCESS;
+
     }
 
-  if (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0)
+  /* Unformatted sequential.  */
+
+  have_written = 0;
+
+  if (dtp->u.p.current_unit->flags.has_recl
+      && (gfc_offset) nbytes > dtp->u.p.current_unit->bytes_left)
     {
-      generate_error (&dtp->common, ERROR_OS, NULL);
-      return FAILURE;
+      nbytes = dtp->u.p.current_unit->bytes_left;
+      short_record = 1;
+    }
+  else
+    {
+      short_record = 0;
     }
 
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-	dtp->u.p.size_used += (gfc_offset) nbytes;
+  while (1)
+    {
+
+      to_write_subrecord =
+	(size_t) dtp->u.p.current_unit->bytes_left_subrecord < nbytes ?
+	(size_t) dtp->u.p.current_unit->bytes_left_subrecord : nbytes;
+
+      dtp->u.p.current_unit->bytes_left_subrecord -=
+	(gfc_offset) to_write_subrecord;
 
-  dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; 
+      if (swrite (dtp->u.p.current_unit->s, buf + have_written,
+		  &to_write_subrecord) != 0)
+	{
+	  generate_error (&dtp->common, ERROR_OS, NULL);
+	  return FAILURE;
+	}
+
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) to_write_subrecord; 
+      nbytes -= to_write_subrecord;
+      have_written += to_write_subrecord;
 
+      if (nbytes == 0)
+	break;
+
+      next_record_w_unf (dtp, 1);
+      us_write (dtp, 1);
+    }
+  dtp->u.p.current_unit->bytes_left -= have_written;
+  if (short_record)
+    {
+      generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL);
+      return FAILURE;
+    }
   return SUCCESS;
 }
 
@@ -1357,7 +1528,7 @@ transfer_array (st_parameter_dt *dtp, gf
 /* Preposition a sequential unformatted file while reading.  */
 
 static void
-us_read (st_parameter_dt *dtp)
+us_read (st_parameter_dt *dtp, int continued)
 {
   char *p;
   int n;
@@ -1370,7 +1541,7 @@ us_read (st_parameter_dt *dtp)
     return;
 
   if (compile_options.record_marker == 0)
-    n = sizeof (gfc_offset);
+    n = sizeof (GFC_INTEGER_4);
   else
     n = compile_options.record_marker;
 
@@ -1393,12 +1564,8 @@ us_read (st_parameter_dt *dtp)
   /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here.  */
   if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE)
     {
-      switch (compile_options.record_marker)
+      switch (nr)
 	{
-	case 0:
-	  memcpy (&i, p, sizeof(gfc_offset));
-	  break;
-
 	case sizeof(GFC_INTEGER_4):
 	  memcpy (&i4, p, sizeof (i4));
 	  i = i4;
@@ -1415,12 +1582,8 @@ us_read (st_parameter_dt *dtp)
 	}
     }
   else
-      switch (compile_options.record_marker)
+      switch (nr)
 	{
-	case 0:
-	  reverse_memcpy (&i, p, sizeof(gfc_offset));
-	  break;
-
 	case sizeof(GFC_INTEGER_4):
 	  reverse_memcpy (&i4, p, sizeof (i4));
 	  i = i4;
@@ -1436,7 +1599,19 @@ us_read (st_parameter_dt *dtp)
 	  break;
 	}
 
-  dtp->u.p.current_unit->bytes_left = i;
+  if (i >= 0)
+    {
+      dtp->u.p.current_unit->bytes_left_subrecord = i;
+      dtp->u.p.current_unit->continued = 0;
+    }
+  else
+    {
+      dtp->u.p.current_unit->bytes_left_subrecord = -i;
+      dtp->u.p.current_unit->continued = 1;
+    }
+
+  if (! continued)
+    dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
 }
 
 
@@ -1444,7 +1619,7 @@ us_read (st_parameter_dt *dtp)
    amount to writing a bogus length that will be filled in later.  */
 
 static void
-us_write (st_parameter_dt *dtp)
+us_write (st_parameter_dt *dtp, int continued)
 {
   size_t nbytes;
   gfc_offset dummy;
@@ -1452,7 +1627,7 @@ us_write (st_parameter_dt *dtp)
   dummy = 0;
 
   if (compile_options.record_marker == 0)
-    nbytes = sizeof (gfc_offset);
+    nbytes = sizeof (GFC_INTEGER_4);
   else
     nbytes = compile_options.record_marker ;
 
@@ -1460,12 +1635,12 @@ us_write (st_parameter_dt *dtp)
     generate_error (&dtp->common, ERROR_OS, NULL);
 
   /* For sequential unformatted, if RECL= was not specified in the OPEN
-     we write until we have more bytes than can fit in the record markers.
-     If disk space runs out first, it will error on the write.  */
-  if (dtp->u.p.current_unit->flags.has_recl == 0)
-    dtp->u.p.current_unit->recl = max_offset;
+     we write until we have more bytes than can fit in the subrecord
+     markers, then we write a new subrecord.  */
 
-  dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
+  dtp->u.p.current_unit->bytes_left_subrecord =
+    dtp->u.p.current_unit->recl_subrecord;
+  dtp->u.p.current_unit->continued = continued;
 }
 
 
@@ -1491,9 +1666,9 @@ pre_position (st_parameter_dt *dtp)
     
     case UNFORMATTED_SEQUENTIAL:
       if (dtp->u.p.mode == READING)
-	us_read (dtp);
+	us_read (dtp, 0);
       else
-	us_write (dtp);
+	us_write (dtp, 0);
 
       break;
 
@@ -1886,17 +2061,92 @@ next_array_record (st_parameter_dt *dtp,
   return index;
 }
 
-/* Space to the next record for read mode.  If the file is not
-   seekable, we read MAX_READ chunks until we get to the right
+
+
+/* Skip to the end of the current record, taking care of an optional
+   record marker of size bytes.  If the file is not seekable, we
+   read chunks of size MAX_READ until we get to the right
    position.  */
 
 #define MAX_READ 4096
 
 static void
+skip_record (st_parameter_dt *dtp, size_t bytes)
+{
+  gfc_offset new;
+  int rlength, length;
+  char *p;
+
+  dtp->u.p.current_unit->bytes_left_subrecord += bytes;
+  if (dtp->u.p.current_unit->bytes_left_subrecord == 0)
+    return;
+
+  if (is_seekable (dtp->u.p.current_unit->s))
+    {
+      new = file_position (dtp->u.p.current_unit->s)
+	+ dtp->u.p.current_unit->bytes_left_subrecord;
+
+      /* Direct access files do not generate END conditions,
+	 only I/O errors.  */
+      if (sseek (dtp->u.p.current_unit->s, new) == FAILURE)
+	generate_error (&dtp->common, ERROR_OS, NULL);
+    }
+  else
+    {			/* Seek by reading data.  */
+      while (dtp->u.p.current_unit->bytes_left_subrecord > 0)
+	{
+	  rlength = length =
+	    (MAX_READ > dtp->u.p.current_unit->bytes_left_subrecord) ?
+	    MAX_READ : dtp->u.p.current_unit->bytes_left_subrecord;
+
+	  p = salloc_r (dtp->u.p.current_unit->s, &rlength);
+	  if (p == NULL)
+	    {
+	      generate_error (&dtp->common, ERROR_OS, NULL);
+	      return;
+	    }
+
+	  dtp->u.p.current_unit->bytes_left_subrecord -= length;
+	}
+    }
+
+}
+
+#undef MAX_READ
+
+/* Advance to the next record reading unformatted files, taking
+   care of subrecords.  If complete_record is nonzero, we loop
+   until all subrecords are cleared.  */
+
+static void
+next_record_r_unf (st_parameter_dt *dtp, int complete_record)
+{
+  size_t bytes;
+
+  bytes =  compile_options.record_marker == 0 ?
+    sizeof (GFC_INTEGER_4) : compile_options.record_marker;
+
+  while(1)
+    {
+
+      /* Skip over tail */
+
+      skip_record (dtp, bytes);
+
+      if ( ! (complete_record && dtp->u.p.current_unit->continued))
+	return;
+
+      us_read (dtp, 1);
+    }
+}
+
+/* Space to the next record for read mode.  */
+
+static void
 next_record_r (st_parameter_dt *dtp)
 {
-  gfc_offset new, record;
-  int bytes_left, rlength, length;
+  gfc_offset record;
+  int length, bytes_left;
   char *p;
 
   switch (current_mode (dtp))
@@ -1906,47 +2156,12 @@ next_record_r (st_parameter_dt *dtp)
       return;
     
     case UNFORMATTED_SEQUENTIAL:
-
-      /* Skip over tail */
-      dtp->u.p.current_unit->bytes_left +=
-	compile_options.record_marker == 0 ?
-	sizeof (gfc_offset) : compile_options.record_marker;
-      
-      /* Fall through...  */
+      next_record_r_unf (dtp, 1);
+      break;
 
     case FORMATTED_DIRECT:
     case UNFORMATTED_DIRECT:
-      if (dtp->u.p.current_unit->bytes_left == 0)
-	break;
-
-      if (is_seekable (dtp->u.p.current_unit->s))
-	{
-	  new = file_position (dtp->u.p.current_unit->s)
-		+ dtp->u.p.current_unit->bytes_left;
-
-	  /* Direct access files do not generate END conditions,
-	     only I/O errors.  */
-	  if (sseek (dtp->u.p.current_unit->s, new) == FAILURE)
-	    generate_error (&dtp->common, ERROR_OS, NULL);
-
-	}
-      else
-	{			/* Seek by reading data.  */
-	  while (dtp->u.p.current_unit->bytes_left > 0)
-	    {
-	      rlength = length = (MAX_READ > dtp->u.p.current_unit->bytes_left) ?
-		MAX_READ : dtp->u.p.current_unit->bytes_left;
-
-	      p = salloc_r (dtp->u.p.current_unit->s, &rlength);
-	      if (p == NULL)
-		{
-		  generate_error (&dtp->common, ERROR_OS, NULL);
-		  break;
-		}
-
-	      dtp->u.p.current_unit->bytes_left -= length;
-	    }
-	}
+      skip_record (dtp, 0);
       break;
 
     case FORMATTED_STREAM:
@@ -2025,19 +2240,15 @@ write_us_marker (st_parameter_dt *dtp, c
   char p[sizeof (GFC_INTEGER_8)];
 
   if (compile_options.record_marker == 0)
-    len = sizeof (gfc_offset);
+    len = sizeof (GFC_INTEGER_4);
   else
     len = compile_options.record_marker;
 
   /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here.  */
   if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE)
     {
-      switch (compile_options.record_marker)
+      switch (len)
 	{
-	case 0:
-	  return swrite (dtp->u.p.current_unit->s, &buf, &len);
-	  break;
-
 	case sizeof (GFC_INTEGER_4):
 	  buf4 = buf;
 	  return swrite (dtp->u.p.current_unit->s, &buf4, &len);
@@ -2055,13 +2266,8 @@ write_us_marker (st_parameter_dt *dtp, c
     }
   else
     {
-      switch (compile_options.record_marker)
+      switch (len)
 	{
-	case 0:
-	  reverse_memcpy (p, &buf, sizeof (gfc_offset));
-	  return swrite (dtp->u.p.current_unit->s, p, &len);
-	  break;
-
 	case sizeof (GFC_INTEGER_4):
 	  buf4 = buf;
 	  reverse_memcpy (p, &buf4, sizeof (GFC_INTEGER_4));
@@ -2070,7 +2276,7 @@ write_us_marker (st_parameter_dt *dtp, c
 
 	case sizeof (GFC_INTEGER_8):
 	  buf8 = buf;
-	  reverse_memcpy (p, &buf8, sizeof (GFC_INTEGER_4));
+	  reverse_memcpy (p, &buf8, sizeof (GFC_INTEGER_8));
 	  return swrite (dtp->u.p.current_unit->s, p, &len);
 	  break;
 
@@ -2082,16 +2288,72 @@ write_us_marker (st_parameter_dt *dtp, c
 
 }
 
+/* Position to the next (sub)record in write mode for
+   unformatted sequential files.  */
+
+static void
+next_record_w_unf (st_parameter_dt *dtp, int next_subrecord)
+{
+  gfc_offset c, m, m_write;
+  size_t record_marker;
+
+  /* Bytes written.  */
+  m = dtp->u.p.current_unit->recl_subrecord
+    - dtp->u.p.current_unit->bytes_left_subrecord;
+  c = file_position (dtp->u.p.current_unit->s);
+
+  /* Write the length tail.  If we finish a record containing
+     subrecords, we write out the negative length.  */
+
+  if (dtp->u.p.current_unit->continued)
+    m_write = -m;
+  else
+    m_write = m;
+
+  if (write_us_marker (dtp, m_write) != 0)
+    goto io_error;
+
+  if (compile_options.record_marker == 0)
+    record_marker = sizeof (GFC_INTEGER_4);
+  else
+    record_marker = compile_options.record_marker;
+
+  /* Seek to the head and overwrite the bogus length with the real
+     length.  */
+
+  if (sseek (dtp->u.p.current_unit->s, c - m - record_marker)
+      == FAILURE)
+    goto io_error;
+
+  if (next_subrecord)
+    m_write = -m;
+  else
+    m_write = m;
+
+  if (write_us_marker (dtp, m_write) != 0)
+    goto io_error;
+
+  /* Seek past the end of the current record.  */
+
+  if (sseek (dtp->u.p.current_unit->s, c + record_marker) == FAILURE)
+    goto io_error;
+
+  return;
+
+ io_error:
+  generate_error (&dtp->common, ERROR_OS, NULL);
+  return;
+
+}
 
 /* Position to the next record in write mode.  */
 
 static void
 next_record_w (st_parameter_dt *dtp, int done)
 {
-  gfc_offset c, m, record, max_pos;
+  gfc_offset m, record, max_pos;
   int length;
   char *p;
-  size_t record_marker;
 
   /* Zero counters for X- and T-editing.  */
   max_pos = dtp->u.p.max_pos;
@@ -2119,35 +2381,7 @@ next_record_w (st_parameter_dt *dtp, int
       break;
 
     case UNFORMATTED_SEQUENTIAL:
-      /* Bytes written.  */
-      m = dtp->u.p.current_unit->recl - dtp->u.p.current_unit->bytes_left;
-      c = file_position (dtp->u.p.current_unit->s);
-
-      /* Write the length tail.  */
-
-      if (write_us_marker (dtp, m) != 0)
-	goto io_error;
-
-      if (compile_options.record_marker == 4)
-	record_marker = sizeof(GFC_INTEGER_4);
-      else
-	record_marker = sizeof (gfc_offset);
-
-      /* Seek to the head and overwrite the bogus length with the real
-	 length.  */
-
-      if (sseek (dtp->u.p.current_unit->s, c - m - record_marker)
-	  == FAILURE)
-	goto io_error;
-
-      if (write_us_marker (dtp, m) != 0)
-	goto io_error;
-
-      /* Seek past the end of the current record.  */
-
-      if (sseek (dtp->u.p.current_unit->s, c + record_marker) == FAILURE)
-	goto io_error;
-
+      next_record_w_unf (dtp, 0);
       break;
 
     case FORMATTED_STREAM:

! { dg-do run }
! { dg-options "-fmax-subrecord-length=16" }
! Test Intel record markers with 16-byte subrecord sizes.
program main
  implicit none
  integer, dimension(20) :: n
  integer, dimension(30) :: m
  integer :: i
  real :: r
  integer :: k
  ! Maximum subrecord length is 16 here, or the test will fail.
  open (10, file="f10.dat", &
       form="unformatted", access="sequential")
  n = (/ (i**2, i=1, 20) /)
  write (10) n
  close (10)
  ! Read back the file, including record markers.
  open (10, file="f10.dat", form="unformatted", access="stream")
  read (10) m
  if (any(m .ne. (/ -16, 1, 4, 9, 16, 16, -16, 25, 36, 49, 64, &
       -16, -16, 81, 100, 121, 144, -16, -16, 169, 196, 225, & 
       256, -16, 16, 289, 324, 361, 400, -16 /))) call abort
  close (10)
  open (10, file="f10.dat", form="unformatted", &
       access="sequential")
  m = 42
  read (10) m(1:5)
  if (any(m(1:5) .ne. (/ 1, 4, 9, 16, 25 /))) call abort
  if (any(m(6:30) .ne. 42)) call abort
  backspace 10
  n = 0
  read (10) n(1:5)
  if (any(n(1:5) .ne. (/ 1, 4, 9, 16, 25 /))) call abort
  if (any(n(6:20) .ne. 0)) call abort
  ! Append to the end of the file
  write (10) 3.14
  ! Test multiple backspace statements
  backspace 10
  backspace 10
  read (10) k
  if (k .ne. 1) call abort
  read (10) r
  if (abs(r-3.14) .gt. 1e-7) call abort
  close (10, status="delete")
end program main


----- End forwarded message -----


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]