PR 78534, 83704 Handle large formatted I/O
[gcc.git] / libgfortran / io / transfer.c
index bb93009f59fe5793fb2a8f6189072c74e3f400c5..f9c8696766f43b62e5f51bf3241f7fa7fccbbaa2 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002-2013 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
    Contributed by Andy Vaught
    Namelist transfer functions contributed by Paul Thomas
    F2003 I/O support contributed by Jerry DeLisle
@@ -32,8 +32,6 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "format.h"
 #include "unix.h"
 #include <string.h>
-#include <assert.h>
-#include <stdlib.h>
 #include <errno.h>
 
 
@@ -57,7 +55,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
       transfer_complex
       transfer_real128
       transfer_complex128
-   
+
     and for WRITE
 
       transfer_integer_write
@@ -95,17 +93,17 @@ export_proto(transfer_logical);
 extern void transfer_logical_write (st_parameter_dt *, void *, int);
 export_proto(transfer_logical_write);
 
-extern void transfer_character (st_parameter_dt *, void *, int);
+extern void transfer_character (st_parameter_dt *, void *, gfc_charlen_type);
 export_proto(transfer_character);
 
-extern void transfer_character_write (st_parameter_dt *, void *, int);
+extern void transfer_character_write (st_parameter_dt *, void *, gfc_charlen_type);
 export_proto(transfer_character_write);
 
-extern void transfer_character_wide (st_parameter_dt *, void *, int, int);
+extern void transfer_character_wide (st_parameter_dt *, void *, gfc_charlen_type, int);
 export_proto(transfer_character_wide);
 
 extern void transfer_character_wide_write (st_parameter_dt *,
-                                          void *, int, int);
+                                          void *, gfc_charlen_type, int);
 export_proto(transfer_character_wide_write);
 
 extern void transfer_complex (st_parameter_dt *, void *, int);
@@ -122,6 +120,15 @@ extern void transfer_array_write (st_parameter_dt *, gfc_array_char *, int,
                            gfc_charlen_type);
 export_proto(transfer_array_write);
 
+/* User defined derived type input/output.  */
+extern void
+transfer_derived (st_parameter_dt *dtp, void *dtio_source, void *dtio_proc);
+export_proto(transfer_derived);
+
+extern void
+transfer_derived_write (st_parameter_dt *dtp, void *dtio_source, void *dtio_proc);
+export_proto(transfer_derived_write);
+
 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);
@@ -216,11 +223,11 @@ current_mode (st_parameter_dt *dtp)
 /* Read sequential file - internal unit  */
 
 static char *
-read_sf_internal (st_parameter_dt *dtp, int * length)
+read_sf_internal (st_parameter_dt *dtp, size_t *length)
 {
   static char *empty_string[0];
-  char *base;
-  int lorig;
+  char *base = NULL;
+  size_t lorig;
 
   /* Zero size array gives internal unit len of 0.  Nothing to read. */
   if (dtp->internal_unit_len == 0
@@ -233,18 +240,26 @@ read_sf_internal (st_parameter_dt *dtp, int * length)
     {
       *length = 0;
       /* Just return something that isn't a NULL pointer, otherwise the
-         caller thinks an error occured.  */
+         caller thinks an error occurred.  */
       return (char*) empty_string;
     }
 
+  /* There are some cases with mixed DTIO where we have read a character
+     and saved it in the last character buffer, so we need to backup.  */
+  if (unlikely (dtp->u.p.current_unit->child_dtio > 0 &&
+               dtp->u.p.current_unit->last_char != EOF - 1))
+    {
+      dtp->u.p.current_unit->last_char = EOF - 1;
+      sseek (dtp->u.p.current_unit->s, -1, SEEK_CUR);
+    }
+
   lorig = *length;
   if (is_char4_unit(dtp))
     {
-      int i;
       gfc_char4_t *p = (gfc_char4_t *) mem_alloc_r4 (dtp->u.p.current_unit->s,
                        length);
       base = fbuf_alloc (dtp->u.p.current_unit, lorig);
-      for (i = 0; i < *length; i++, p++)
+      for (size_t i = 0; i < *length; i++, p++)
        base[i] = *p > 255 ? '?' : (unsigned char) *p;
     }
   else
@@ -258,8 +273,9 @@ read_sf_internal (st_parameter_dt *dtp, int * length)
 
   dtp->u.p.current_unit->bytes_left -= *length;
 
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    dtp->u.p.size_used += (GFC_IO_INT) *length;
+  if (((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) ||
+      dtp->u.p.current_unit->has_size)
+    dtp->u.p.current_unit->size_used += (GFC_IO_INT) *length;
 
   return base;
 
@@ -280,11 +296,12 @@ read_sf_internal (st_parameter_dt *dtp, int * length)
 /* Read sequential file - external unit */
 
 static char *
-read_sf (st_parameter_dt *dtp, int * length)
+read_sf (st_parameter_dt *dtp, size_t *length)
 {
   static char *empty_string[0];
+  size_t lorig, n;
   int q, q2;
-  int n, lorig, seen_comma;
+  int seen_comma;
 
   /* If we have seen an eor previously, return a length of 0.  The
      caller is responsible for correctly padding the input field.  */
@@ -292,10 +309,19 @@ read_sf (st_parameter_dt *dtp, int * length)
     {
       *length = 0;
       /* Just return something that isn't a NULL pointer, otherwise the
-         caller thinks an error occured.  */
+         caller thinks an error occurred.  */
       return (char*) empty_string;
     }
 
+  /* There are some cases with mixed DTIO where we have read a character
+     and saved it in the last character buffer, so we need to backup.  */
+  if (unlikely (dtp->u.p.current_unit->child_dtio > 0 &&
+               dtp->u.p.current_unit->last_char != EOF - 1))
+    {
+      dtp->u.p.current_unit->last_char = EOF - 1;
+      fbuf_seek (dtp->u.p.current_unit, -1, SEEK_CUR);
+    }
+
   n = seen_comma = 0;
 
   /* Read data into format buffer and scan through it.  */
@@ -306,7 +332,8 @@ read_sf (st_parameter_dt *dtp, int * length)
       q = fbuf_getc (dtp->u.p.current_unit);
       if (q == EOF)
        break;
-      else if (q == '\n' || q == '\r')
+      else if (dtp->u.p.current_unit->flags.cc != CC_NONE
+              && (q == '\n' || q == '\r'))
        {
          /* Unexpected end of line. Set the position.  */
          dtp->u.p.sf_seen_eor = 1;
@@ -315,7 +342,7 @@ read_sf (st_parameter_dt *dtp, int * length)
             the rest of the I/O statement.  Set the corresponding flag.  */
          if (dtp->u.p.advance_status == ADVANCE_NO || dtp->u.p.seen_dollar)
            dtp->u.p.eor_condition = 1;
-           
+
          /* If we encounter a CR, it might be a CRLF.  */
          if (q == '\r') /* Probably a CRLF */
            {
@@ -388,8 +415,9 @@ read_sf (st_parameter_dt *dtp, int * length)
 
   dtp->u.p.current_unit->bytes_left -= n;
 
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    dtp->u.p.size_used += (GFC_IO_INT) n;
+  if (((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) ||
+      dtp->u.p.current_unit->has_size)
+    dtp->u.p.current_unit->size_used += (GFC_IO_INT) n;
 
   /* We can't call fbuf_getptr before the loop doing fbuf_getc, because
      fbuf_getc might reallocate the buffer.  So return current pointer
@@ -411,10 +439,10 @@ read_sf (st_parameter_dt *dtp, int * length)
    short reads.  */
 
 void *
-read_block_form (st_parameter_dt *dtp, int * nbytes)
+read_block_form (st_parameter_dt *dtp, size_t *nbytes)
 {
   char *source;
-  int norig;
+  size_t norig;
 
   if (!is_stream_io (dtp))
     {
@@ -423,7 +451,7 @@ read_block_form (st_parameter_dt *dtp, int * 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.stdin_unit
-             && dtp->u.p.current_unit->recl == DEFAULT_RECL)
+             && dtp->u.p.current_unit->recl == default_recl)
             dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
          else
            {
@@ -436,11 +464,24 @@ read_block_form (st_parameter_dt *dtp, int * nbytes)
                }
            }
 
-         if (unlikely (dtp->u.p.current_unit->bytes_left == 0
-             && !is_internal_unit(dtp)))
+         if (is_internal_unit(dtp))
            {
-             hit_eof (dtp);
-             return NULL;
+             if (*nbytes > 0 && dtp->u.p.current_unit->bytes_left == 0)
+               {
+                 if (dtp->u.p.advance_status == ADVANCE_NO)
+                   {
+                     generate_error (&dtp->common, LIBERROR_EOR, NULL);
+                     return NULL;
+                   }
+               }
+           }
+         else
+           {
+             if (unlikely (dtp->u.p.current_unit->bytes_left == 0))
+               {
+                 hit_eof (dtp);
+                 return NULL;
+               }
            }
 
          *nbytes = dtp->u.p.current_unit->bytes_left;
@@ -469,13 +510,14 @@ read_block_form (st_parameter_dt *dtp, int * nbytes)
   source = fbuf_read (dtp->u.p.current_unit, nbytes);
   fbuf_seek (dtp->u.p.current_unit, *nbytes, SEEK_CUR);
 
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    dtp->u.p.size_used += (GFC_IO_INT) *nbytes;
+  if (((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) ||
+      dtp->u.p.current_unit->has_size)
+    dtp->u.p.current_unit->size_used += (GFC_IO_INT) *nbytes;
 
   if (norig != *nbytes)
     {
       /* Short read, this shouldn't happen.  */
-      if (!dtp->u.p.current_unit->pad_status == PAD_YES)
+      if (dtp->u.p.current_unit->pad_status == PAD_NO)
        {
          generate_error (&dtp->common, LIBERROR_EOR, NULL);
          source = NULL;
@@ -492,11 +534,11 @@ read_block_form (st_parameter_dt *dtp, int * nbytes)
    a character(kind=4) variable.  Note: Portions of this code borrowed from
    read_sf_internal.  */
 void *
-read_block_form4 (st_parameter_dt *dtp, int * nbytes)
+read_block_form4 (st_parameter_dt *dtp, size_t *nbytes)
 {
   static gfc_char4_t *empty_string[0];
   gfc_char4_t *source;
-  int lorig;
+  size_t lorig;
 
   if (dtp->u.p.current_unit->bytes_left < (gfc_offset) *nbytes)
     *nbytes = dtp->u.p.current_unit->bytes_left;
@@ -512,7 +554,7 @@ read_block_form4 (st_parameter_dt *dtp, int * nbytes)
     {
       *nbytes = 0;
       /* Just return something that isn't a NULL pointer, otherwise the
-         caller thinks an error occured.  */
+         caller thinks an error occurred.  */
       return empty_string;
     }
 
@@ -527,8 +569,9 @@ read_block_form4 (st_parameter_dt *dtp, int * nbytes)
 
   dtp->u.p.current_unit->bytes_left -= *nbytes;
 
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    dtp->u.p.size_used += (GFC_IO_INT) *nbytes;
+  if (((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) ||
+      dtp->u.p.current_unit->has_size)
+    dtp->u.p.current_unit->size_used += (GFC_IO_INT) *nbytes;
 
   return source;
 }
@@ -548,7 +591,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t nbytes)
 
   if (is_stream_io (dtp))
     {
-      have_read_record = sread (dtp->u.p.current_unit->s, buf, 
+      have_read_record = sread (dtp->u.p.current_unit->s, buf,
                                nbytes);
       if (unlikely (have_read_record < 0))
        {
@@ -556,7 +599,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t nbytes)
          return;
        }
 
-      dtp->u.p.current_unit->strm_pos += (gfc_offset) have_read_record; 
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) have_read_record;
 
       if (unlikely ((ssize_t) nbytes != have_read_record))
        {
@@ -590,7 +633,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t nbytes)
          return;
        }
 
-      if (to_read_record != (ssize_t) nbytes)  
+      if (to_read_record != (ssize_t) nbytes)
        {
          /* Short read, e.g. if we hit EOF.  Apparently, we read
           more than was written to the last record.  */
@@ -639,7 +682,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t nbytes)
 
       dtp->u.p.current_unit->bytes_left_subrecord -= to_read_subrecord;
 
-      have_read_subrecord = sread (dtp->u.p.current_unit->s, 
+      have_read_subrecord = sread (dtp->u.p.current_unit->s,
                                   buf + have_read_record, to_read_subrecord);
       if (unlikely (have_read_subrecord < 0))
        {
@@ -700,7 +743,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t nbytes)
    fill in.  Returns NULL on error.  */
 
 void *
-write_block (st_parameter_dt *dtp, int length)
+write_block (st_parameter_dt *dtp, size_t length)
 {
   char *dest;
 
@@ -714,7 +757,7 @@ write_block (st_parameter_dt *dtp, int length)
                       == 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->recl == default_recl))
            dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
          else
            {
@@ -728,7 +771,7 @@ write_block (st_parameter_dt *dtp, int length)
 
   if (is_internal_unit (dtp))
     {
-      if (dtp->common.unit) /* char4 internel unit.  */
+      if (is_char4_unit(dtp)) /* char4 internel unit.  */
        {
          gfc_char4_t *dest4;
          dest4 = mem_alloc_w4 (dtp->u.p.current_unit->s, &length);
@@ -760,9 +803,10 @@ write_block (st_parameter_dt *dtp, int length)
          return NULL;
        }
     }
-    
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    dtp->u.p.size_used += (GFC_IO_INT) length;
+
+  if (((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) ||
+      dtp->u.p.current_unit->has_size)
+    dtp->u.p.current_unit->size_used += (GFC_IO_INT) length;
 
   dtp->u.p.current_unit->strm_pos += (gfc_offset) length;
 
@@ -793,7 +837,7 @@ write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes)
          return false;
        }
 
-      dtp->u.p.current_unit->strm_pos += (gfc_offset) have_written; 
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) have_written;
 
       return true;
     }
@@ -811,7 +855,7 @@ write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes)
       if (buf == NULL && nbytes == 0)
        return true;
 
-      have_written = swrite (dtp->u.p.current_unit->s, buf, nbytes); 
+      have_written = swrite (dtp->u.p.current_unit->s, buf, nbytes);
       if (unlikely (have_written < 0))
        {
          generate_error (&dtp->common, LIBERROR_OS, NULL);
@@ -849,7 +893,7 @@ write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes)
       dtp->u.p.current_unit->bytes_left_subrecord -=
        (gfc_offset) to_write_subrecord;
 
-      to_write_subrecord = swrite (dtp->u.p.current_unit->s, 
+      to_write_subrecord = swrite (dtp->u.p.current_unit->s,
                                   buf + have_written, to_write_subrecord);
       if (unlikely (to_write_subrecord < 0))
        {
@@ -857,7 +901,7 @@ write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes)
          return false;
        }
 
-      dtp->u.p.current_unit->strm_pos += (gfc_offset) to_write_subrecord; 
+      dtp->u.p.current_unit->strm_pos += (gfc_offset) to_write_subrecord;
       nbytes -= to_write_subrecord;
       have_written += to_write_subrecord;
 
@@ -903,7 +947,7 @@ reverse_memcpy (void *dest, const void *src, size_t n)
 static void
 bswap_array (void *dest, const void *src, size_t size, size_t nelems)
 {
-  const char *ps; 
+  const char *ps;
   char *pd;
 
   switch (size)
@@ -988,6 +1032,40 @@ static void
 unformatted_read (st_parameter_dt *dtp, bt type,
                  void *dest, int kind, size_t size, size_t nelems)
 {
+  if (type == BT_CLASS)
+    {
+         int unit = dtp->u.p.current_unit->unit_number;
+         char tmp_iomsg[IOMSG_LEN] = "";
+         char *child_iomsg;
+         gfc_charlen_type child_iomsg_len;
+         int noiostat;
+         int *child_iostat = NULL;
+
+         /* Set iostat, intent(out).  */
+         noiostat = 0;
+         child_iostat = (dtp->common.flags & IOPARM_HAS_IOSTAT) ?
+                         dtp->common.iostat : &noiostat;
+
+         /* Set iomsg, intent(inout).  */
+         if (dtp->common.flags & IOPARM_HAS_IOMSG)
+           {
+             child_iomsg = dtp->common.iomsg;
+             child_iomsg_len = dtp->common.iomsg_len;
+           }
+         else
+           {
+             child_iomsg = tmp_iomsg;
+             child_iomsg_len = IOMSG_LEN;
+           }
+
+         /* Call the user defined unformatted READ procedure.  */
+         dtp->u.p.current_unit->child_dtio++;
+         dtp->u.p.ufdtio_ptr (dest, &unit, child_iostat, child_iomsg,
+                             child_iomsg_len);
+         dtp->u.p.current_unit->child_dtio--;
+         return;
+    }
+
   if (type == BT_CHARACTER)
     size *= GFC_SIZE_OF_CHAR_KIND(kind);
   read_block_direct (dtp, dest, size * nelems);
@@ -1016,13 +1094,47 @@ unformatted_read (st_parameter_dt *dtp, bt type,
 /* Master function for unformatted writes.  NOTE: For kind=10 the size is 16
    bytes on 64 bit machines.  The unused bytes are not initialized and never
    used, which can show an error with memory checking analyzers like
-   valgrind.  */
+   valgrind.  We us BT_CLASS to denote a User Defined I/O call.  */
 
 static void
 unformatted_write (st_parameter_dt *dtp, bt type,
                   void *source, int kind, size_t size, size_t nelems)
 {
-  if (likely (dtp->u.p.current_unit->flags.convert == GFC_CONVERT_NATIVE) 
+  if (type == BT_CLASS)
+    {
+         int unit = dtp->u.p.current_unit->unit_number;
+         char tmp_iomsg[IOMSG_LEN] = "";
+         char *child_iomsg;
+         gfc_charlen_type child_iomsg_len;
+         int noiostat;
+         int *child_iostat = NULL;
+
+         /* Set iostat, intent(out).  */
+         noiostat = 0;
+         child_iostat = (dtp->common.flags & IOPARM_HAS_IOSTAT) ?
+                         dtp->common.iostat : &noiostat;
+
+         /* Set iomsg, intent(inout).  */
+         if (dtp->common.flags & IOPARM_HAS_IOMSG)
+           {
+             child_iomsg = dtp->common.iomsg;
+             child_iomsg_len = dtp->common.iomsg_len;
+           }
+         else
+           {
+             child_iomsg = tmp_iomsg;
+             child_iomsg_len = IOMSG_LEN;
+           }
+
+         /* Call the user defined unformatted WRITE procedure.  */
+         dtp->u.p.current_unit->child_dtio++;
+         dtp->u.p.ufdtio_ptr (source, &unit, child_iostat, child_iomsg,
+                             child_iomsg_len);
+         dtp->u.p.current_unit->child_dtio--;
+         return;
+    }
+
+  if (likely (dtp->u.p.current_unit->flags.convert == GFC_CONVERT_NATIVE)
       || kind == 1)
     {
       size_t stride = type == BT_CHARACTER ?
@@ -1045,13 +1157,13 @@ unformatted_write (st_parameter_dt *dtp, bt type,
          nelems *= size;
          size = kind;
        }
-  
+
       /* Break up complex into its constituent reals.  */
       if (type == BT_COMPLEX)
        {
          nelems *= 2;
          size /= 2;
-       }      
+       }
 
       /* By now, all complex variables have been split into their
         constituent reals.  */
@@ -1099,6 +1211,9 @@ type_name (bt type)
     case BT_COMPLEX:
       p = "COMPLEX";
       break;
+    case BT_CLASS:
+      p = "CLASS or DERIVED";
+      break;
     default:
       internal_error (NULL, "type_name(): Bad type");
     }
@@ -1115,7 +1230,7 @@ static void
 write_constant_string (st_parameter_dt *dtp, const fnode *f)
 {
   char c, delimiter, *p, *q;
-  int length; 
+  int length;
 
   length = f->u.string.length;
   if (length == 0)
@@ -1124,7 +1239,7 @@ write_constant_string (st_parameter_dt *dtp, const fnode *f)
   p = write_block (dtp, length);
   if (p == NULL)
     return;
-    
+
   q = f->u.string.p;
   delimiter = q[-1];
 
@@ -1151,7 +1266,7 @@ require_type (st_parameter_dt *dtp, bt expected, bt actual, const fnode *f)
     return 0;
 
   /* Adjust item_count before emitting error message.  */
-  snprintf (buffer, BUFLEN, 
+  snprintf (buffer, BUFLEN,
            "Expected %s for item %d in formatted transfer, got %s",
           type_name (expected), dtp->u.p.item_count - 1, type_name (actual));
 
@@ -1160,6 +1275,26 @@ require_type (st_parameter_dt *dtp, bt expected, bt actual, const fnode *f)
 }
 
 
+/* Check that the dtio procedure required for formatted IO is present.  */
+
+static int
+check_dtio_proc (st_parameter_dt *dtp, const fnode *f)
+{
+  char buffer[BUFLEN];
+
+  if (dtp->u.p.fdtio_ptr != NULL)
+    return 0;
+
+  snprintf (buffer, BUFLEN,
+           "Missing DTIO procedure or intrinsic type passed for item %d "
+           "in formatted transfer",
+           dtp->u.p.item_count - 1);
+
+  format_error (dtp, f, buffer);
+  return 1;
+}
+
+
 static int
 require_numeric_type (st_parameter_dt *dtp, bt actual, const fnode *f)
 {
@@ -1170,7 +1305,7 @@ require_numeric_type (st_parameter_dt *dtp, bt actual, const fnode *f)
     return 0;
 
   /* Adjust item_count before emitting error message.  */
-  snprintf (buffer, BUFLEN, 
+  snprintf (buffer, BUFLEN,
            "Expected numeric type for item %d in formatted transfer, got %s",
            dtp->u.p.item_count - 1, type_name (actual));
 
@@ -1178,6 +1313,33 @@ require_numeric_type (st_parameter_dt *dtp, bt actual, const fnode *f)
   return 1;
 }
 
+static char *
+get_dt_format (char *p, gfc_charlen_type *length)
+{
+  char delim = p[-1];  /* The delimiter is always the first character back.  */
+  char c, *q, *res;
+  gfc_charlen_type len = *length; /* This length already correct, less 'DT'.  */
+
+  res = q = xmalloc (len + 2);
+
+  /* Set the beginning of the string to 'DT', length adjusted below.  */
+  *q++ = 'D';
+  *q++ = 'T';
+
+  /* The string may contain doubled quotes so scan and skip as needed.  */
+  for (; len > 0; len--)
+    {
+      c = *q++ = *p++;
+      if (c == delim)
+       p++;  /* Skip the doubled delimiter.  */
+    }
+
+  /* Adjust the string length by two now that we are done.  */
+  *length += 2;
+
+  return res;
+}
+
 
 /* This function is in the main loop for a formatted data transfer
    statement.  It would be natural to implement this as a coroutine
@@ -1273,7 +1435,7 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind
 
        case FMT_O:
          if (n == 0)
-           goto need_read_data; 
+           goto need_read_data;
          if (!(compile_options.allow_std & GFC_STD_GNU)
              && require_numeric_type (dtp, type, f))
            return;
@@ -1322,6 +1484,63 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind
          read_f (dtp, f, p, kind);
          break;
 
+       case FMT_DT:
+         if (n == 0)
+           goto need_read_data;
+
+         if (check_dtio_proc (dtp, f))
+           return;
+         if (require_type (dtp, BT_CLASS, type, f))
+           return;
+         int unit = dtp->u.p.current_unit->unit_number;
+         char dt[] = "DT";
+         char tmp_iomsg[IOMSG_LEN] = "";
+         char *child_iomsg;
+         gfc_charlen_type child_iomsg_len;
+         int noiostat;
+         int *child_iostat = NULL;
+         char *iotype;
+         gfc_charlen_type iotype_len = f->u.udf.string_len;
+
+         /* Build the iotype string.  */
+         if (iotype_len == 0)
+           {
+             iotype_len = 2;
+             iotype = dt;
+           }
+         else
+           iotype = get_dt_format (f->u.udf.string, &iotype_len);
+
+         /* Set iostat, intent(out).  */
+         noiostat = 0;
+         child_iostat = (dtp->common.flags & IOPARM_HAS_IOSTAT) ?
+                         dtp->common.iostat : &noiostat;
+
+         /* Set iomsg, intent(inout).  */
+         if (dtp->common.flags & IOPARM_HAS_IOMSG)
+           {
+             child_iomsg = dtp->common.iomsg;
+             child_iomsg_len = dtp->common.iomsg_len;
+           }
+         else
+           {
+             child_iomsg = tmp_iomsg;
+             child_iomsg_len = IOMSG_LEN;
+           }
+
+         /* Call the user defined formatted READ procedure.  */
+         dtp->u.p.current_unit->child_dtio++;
+         dtp->u.p.current_unit->last_char = EOF - 1;
+         dtp->u.p.fdtio_ptr (p, &unit, iotype, f->u.udf.vlist,
+                             child_iostat, child_iomsg,
+                             iotype_len, child_iomsg_len);
+         dtp->u.p.current_unit->child_dtio--;
+
+         if (f->u.udf.string_len != 0)
+           free (iotype);
+         /* Note: vlist is freed in free_format_data.  */
+         break;
+
        case FMT_E:
          if (n == 0)
            goto need_read_data;
@@ -1434,11 +1653,12 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind
               dtp->u.p.current_unit->bytes_left -= dtp->u.p.sf_seen_eor;
               dtp->u.p.skips -= dtp->u.p.sf_seen_eor;
              bytes_used = pos;
-             dtp->u.p.sf_seen_eor = 0;
+             if (dtp->u.p.pending_spaces == 0)
+               dtp->u.p.sf_seen_eor = 0;
            }
          if (dtp->u.p.skips < 0)
            {
-              if (is_internal_unit (dtp))  
+              if (is_internal_unit (dtp))
                 sseek (dtp->u.p.current_unit->s, dtp->u.p.skips, SEEK_CUR);
               else
                 fbuf_seek (dtp->u.p.current_unit, dtp->u.p.skips, SEEK_CUR);
@@ -1572,7 +1792,7 @@ static void
 formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kind,
                                 size_t size)
 {
-  int pos, bytes_used;
+  gfc_offset pos, bytes_used;
   const fnode *f;
   format_token t;
   int n;
@@ -1624,27 +1844,29 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
 
       /* Now discharge T, TR and X movements to the right.  This is delayed
         until a data producing format to suppress trailing spaces.  */
-        
+
       t = f->format;
       if (dtp->u.p.mode == WRITING && dtp->u.p.skips != 0
        && ((n>0 && (  t == FMT_I  || t == FMT_B  || t == FMT_O
                    || t == FMT_Z  || t == FMT_F  || t == FMT_E
                    || t == FMT_EN || t == FMT_ES || t == FMT_G
-                   || t == FMT_L  || t == FMT_A  || t == FMT_D))
+                   || t == FMT_L  || t == FMT_A  || t == FMT_D
+                   || t == FMT_DT))
            || t == FMT_STRING))
        {
          if (dtp->u.p.skips > 0)
            {
-             int tmp;
+             gfc_offset tmp;
              write_x (dtp, dtp->u.p.skips, dtp->u.p.pending_spaces);
-             tmp = (int)(dtp->u.p.current_unit->recl
-                         - dtp->u.p.current_unit->bytes_left);
-             dtp->u.p.max_pos = 
+             tmp = dtp->u.p.current_unit->recl
+                         - dtp->u.p.current_unit->bytes_left;
+             dtp->u.p.max_pos =
                dtp->u.p.max_pos > tmp ? dtp->u.p.max_pos : tmp;
+             dtp->u.p.skips = 0;
            }
          if (dtp->u.p.skips < 0)
            {
-              if (is_internal_unit (dtp))  
+              if (is_internal_unit (dtp))
                sseek (dtp->u.p.current_unit->s, dtp->u.p.skips, SEEK_CUR);
               else
                 fbuf_seek (dtp->u.p.current_unit, dtp->u.p.skips, SEEK_CUR);
@@ -1653,8 +1875,8 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
          dtp->u.p.skips = dtp->u.p.pending_spaces = 0;
        }
 
-      bytes_used = (int)(dtp->u.p.current_unit->recl
-                  - dtp->u.p.current_unit->bytes_left);
+      bytes_used = dtp->u.p.current_unit->recl
+                  - dtp->u.p.current_unit->bytes_left;
 
       if (is_stream_io(dtp))
        bytes_used = 0;
@@ -1683,7 +1905,7 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
 
        case FMT_O:
          if (n == 0)
-           goto need_data; 
+           goto need_data;
          if (!(compile_options.allow_std & GFC_STD_GNU)
              && require_numeric_type (dtp, type, f))
            return;
@@ -1732,6 +1954,61 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
          write_d (dtp, f, p, kind);
          break;
 
+       case FMT_DT:
+         if (n == 0)
+           goto need_data;
+         int unit = dtp->u.p.current_unit->unit_number;
+         char dt[] = "DT";
+         char tmp_iomsg[IOMSG_LEN] = "";
+         char *child_iomsg;
+         gfc_charlen_type child_iomsg_len;
+         int noiostat;
+         int *child_iostat = NULL;
+         char *iotype;
+         gfc_charlen_type iotype_len = f->u.udf.string_len;
+
+         /* Build the iotype string.  */
+         if (iotype_len == 0)
+           {
+             iotype_len = 2;
+             iotype = dt;
+           }
+         else
+           iotype = get_dt_format (f->u.udf.string, &iotype_len);
+
+         /* Set iostat, intent(out).  */
+         noiostat = 0;
+         child_iostat = (dtp->common.flags & IOPARM_HAS_IOSTAT) ?
+                         dtp->common.iostat : &noiostat;
+
+         /* Set iomsg, intent(inout).  */
+         if (dtp->common.flags & IOPARM_HAS_IOMSG)
+           {
+             child_iomsg = dtp->common.iomsg;
+             child_iomsg_len = dtp->common.iomsg_len;
+           }
+         else
+           {
+             child_iomsg = tmp_iomsg;
+             child_iomsg_len = IOMSG_LEN;
+           }
+
+         if (check_dtio_proc (dtp, f))
+           return;
+
+         /* Call the user defined formatted WRITE procedure.  */
+         dtp->u.p.current_unit->child_dtio++;
+
+         dtp->u.p.fdtio_ptr (p, &unit, iotype, f->u.udf.vlist,
+                             child_iostat, child_iomsg,
+                             iotype_len, child_iomsg_len);
+         dtp->u.p.current_unit->child_dtio--;
+
+         if (f->u.udf.string_len != 0)
+           free (iotype);
+         /* Note: vlist is freed in free_format_data.  */
+         break;
+
        case FMT_E:
          if (n == 0)
            goto need_data;
@@ -1954,7 +2231,7 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
          p = ((char *) p) + size;
        }
 
-      pos = (int)(dtp->u.p.current_unit->recl - dtp->u.p.current_unit->bytes_left);
+      pos = dtp->u.p.current_unit->recl - dtp->u.p.current_unit->bytes_left;
       dtp->u.p.max_pos = (dtp->u.p.max_pos > pos) ? dtp->u.p.max_pos : pos;
     }
 
@@ -2054,7 +2331,7 @@ transfer_logical_write (st_parameter_dt *dtp, void *p, int kind)
 }
 
 void
-transfer_character (st_parameter_dt *dtp, void *p, int len)
+transfer_character (st_parameter_dt *dtp, void *p, gfc_charlen_type len)
 {
   static char *empty_string[0];
 
@@ -2072,13 +2349,13 @@ transfer_character (st_parameter_dt *dtp, void *p, int len)
 }
 
 void
-transfer_character_write (st_parameter_dt *dtp, void *p, int len)
+transfer_character_write (st_parameter_dt *dtp, void *p, gfc_charlen_type len)
 {
   transfer_character (dtp, p, len);
 }
 
 void
-transfer_character_wide (st_parameter_dt *dtp, void *p, int len, int kind)
+transfer_character_wide (st_parameter_dt *dtp, void *p, gfc_charlen_type len, int kind)
 {
   static char *empty_string[0];
 
@@ -2096,7 +2373,7 @@ transfer_character_wide (st_parameter_dt *dtp, void *p, int len, int kind)
 }
 
 void
-transfer_character_wide_write (st_parameter_dt *dtp, void *p, int len, int kind)
+transfer_character_wide_write (st_parameter_dt *dtp, void *p, gfc_charlen_type len, int kind)
 {
   transfer_character_wide (dtp, p, len, kind);
 }
@@ -2133,7 +2410,7 @@ transfer_array (st_parameter_dt *dtp, gfc_array_char *desc, int kind,
     return;
 
   iotype = (bt) GFC_DESCRIPTOR_TYPE (desc);
-  size = iotype == BT_CHARACTER ? charlen : GFC_DESCRIPTOR_SIZE (desc);
+  size = iotype == BT_CHARACTER ? (index_type) charlen : GFC_DESCRIPTOR_SIZE (desc);
 
   rank = GFC_DESCRIPTOR_RANK (desc);
   for (n = 0; n < rank; n++)
@@ -2197,6 +2474,25 @@ transfer_array_write (st_parameter_dt *dtp, gfc_array_char *desc, int kind,
   transfer_array (dtp, desc, kind, charlen);
 }
 
+
+/* User defined input/output iomsg. */
+
+#define IOMSG_LEN 256
+
+void
+transfer_derived (st_parameter_dt *parent, void *dtio_source, void *dtio_proc)
+{
+  if (parent->u.p.current_unit)
+    {
+      if (parent->u.p.current_unit->flags.form == FORM_UNFORMATTED)
+       parent->u.p.ufdtio_ptr = (unformatted_dtio) dtio_proc;
+      else
+       parent->u.p.fdtio_ptr = (formatted_dtio) dtio_proc;
+    }
+  parent->u.p.transfer (parent, BT_CLASS, dtio_source, 0, 0, 1);
+}
+
+
 /* Preposition a sequential unformatted file while reading.  */
 
 static void
@@ -2339,7 +2635,7 @@ pre_position (st_parameter_dt *dtp)
         was specified, we continue from where we last left off.  I.e.
         there is nothing to do here.  */
       break;
-    
+
     case UNFORMATTED_SEQUENTIAL:
       if (dtp->u.p.mode == READING)
        us_read (dtp, 0);
@@ -2375,27 +2671,28 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
 
   dtp->u.p.ionml = ionml;
   dtp->u.p.mode = read_flag ? READING : WRITING;
+  dtp->u.p.namelist_mode = 0;
+  dtp->u.p.cc.len = 0;
 
   if ((dtp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
     return;
 
-  if ((cf & IOPARM_DT_HAS_SIZE) != 0)
-    dtp->u.p.size_used = 0;  /* Initialize the count.  */
-
   dtp->u.p.current_unit = get_unit (dtp, 1);
-  if (dtp->u.p.current_unit->s == NULL)
+
+  if (dtp->u.p.current_unit == NULL)
+    {
+      /* This means we tried to access an external unit < 0 without
+        having opened it first with NEWUNIT=.  */
+      generate_error (&dtp->common, LIBERROR_BAD_OPTION,
+                     "Unit number is negative and unit was not already "
+                     "opened with OPEN(NEWUNIT=...)");
+      return;
+    }
+  else if (dtp->u.p.current_unit->s == NULL)
     {  /* Open the unit with some default flags.  */
        st_parameter_open opp;
        unit_convert conv;
 
-      if (dtp->common.unit < 0)
-       {
-         close_unit (dtp->u.p.current_unit);
-         dtp->u.p.current_unit = NULL;
-         generate_error (&dtp->common, LIBERROR_BAD_OPTION,
-                         "Bad unit number in statement");
-         return;
-       }
       memset (&u_flags, '\0', sizeof (u_flags));
       u_flags.access = ACCESS_SEQUENTIAL;
       u_flags.action = ACTION_READWRITE;
@@ -2415,6 +2712,9 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
       u_flags.async = ASYNC_UNSPECIFIED;
       u_flags.round = ROUND_UNSPECIFIED;
       u_flags.sign = SIGN_UNSPECIFIED;
+      u_flags.share = SHARE_UNSPECIFIED;
+      u_flags.cc = CC_UNSPECIFIED;
+      u_flags.readonly = 0;
 
       u_flags.status = STATUS_UNKNOWN;
 
@@ -2423,22 +2723,20 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
       if (conv == GFC_CONVERT_NONE)
        conv = compile_options.convert;
 
-      /* We use big_endian, which is 0 on little-endian machines
-        and 1 on big-endian machines.  */
       switch (conv)
        {
        case GFC_CONVERT_NATIVE:
        case GFC_CONVERT_SWAP:
          break;
-        
+
        case GFC_CONVERT_BIG:
-         conv = big_endian ? GFC_CONVERT_NATIVE : GFC_CONVERT_SWAP;
+         conv = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ? GFC_CONVERT_NATIVE : GFC_CONVERT_SWAP;
          break;
-      
+
        case GFC_CONVERT_LITTLE:
-         conv = big_endian ? GFC_CONVERT_SWAP : GFC_CONVERT_NATIVE;
+         conv = __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ ? GFC_CONVERT_SWAP : GFC_CONVERT_NATIVE;
          break;
-        
+
        default:
          internal_error (&opp.common, "Illegal value for CONVERT");
          break;
@@ -2455,6 +2753,20 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        return;
     }
 
+  if (dtp->u.p.current_unit->child_dtio == 0)
+    {
+      if ((cf & IOPARM_DT_HAS_SIZE) != 0)
+       {
+         dtp->u.p.current_unit->has_size = true;
+         /* Initialize the count.  */
+         dtp->u.p.current_unit->size_used = 0;
+       }
+      else
+       dtp->u.p.current_unit->has_size = false;
+    }
+  else if (dtp->u.p.current_unit->internal_unit_kind > 0)
+    dtp->u.p.unit_is_internal = 1;
+
   /* Check the action.  */
 
   if (read_flag && dtp->u.p.current_unit->flags.action == ACTION_WRITE)
@@ -2490,14 +2802,18 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
   if ((cf & IOPARM_DT_HAS_NAMELIST_NAME) != 0 && dtp->u.p.ionml != NULL)
      {
        if ((cf & IOPARM_DT_HAS_FORMAT) != 0)
-          generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT,
-                   "A format cannot be specified with a namelist");
+         {
+           generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT,
+                       "A format cannot be specified with a namelist");
+           return;
+         }
      }
   else if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED &&
           !(cf & (IOPARM_DT_HAS_FORMAT | IOPARM_DT_LIST_FORMAT)))
     {
       generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT,
                      "Missing format for FORMATTED data transfer");
+      return;
     }
 
   if (is_internal_unit (dtp)
@@ -2529,15 +2845,16 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
          return;
        }
 
-      if (dtp->u.p.current_unit->endfile == AFTER_ENDFILE)
+      if (compile_options.warn_std &&
+         dtp->u.p.current_unit->endfile == AFTER_ENDFILE)
        {
          generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT,
                        "Sequential READ or WRITE not allowed after "
                        "EOF marker, possibly use REWIND or BACKSPACE");
          return;
        }
-
     }
+
   /* Process the ADVANCE option.  */
 
   dtp->u.p.advance_status
@@ -2571,6 +2888,11 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        }
     }
 
+  /* Child IO is non-advancing and any ADVANCE= specifier is ignored.
+     F2008 9.6.2.4  */
+  if (dtp->u.p.current_unit->child_dtio  > 0)
+    dtp->u.p.advance_status = ADVANCE_NO;
+
   if (read_flag)
     {
       dtp->u.p.current_unit->previous_nonadvancing_write = 0;
@@ -2583,7 +2905,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
          return;
        }
 
-      if ((cf & IOPARM_DT_HAS_SIZE) != 0 
+      if ((cf & IOPARM_DT_HAS_SIZE) != 0
          && dtp->u.p.advance_status != ADVANCE_NO)
        {
          generate_error (&dtp->common, LIBERROR_MISSING_OPTION,
@@ -2647,7 +2969,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        = !(cf & IOPARM_DT_HAS_SIGN) ? SIGN_UNSPECIFIED :
          find_option (&dtp->common, dtp->sign, dtp->sign_len, sign_opt,
                        "Bad SIGN parameter in data transfer statement");
-  
+
   if (dtp->u.p.sign_status == SIGN_UNSPECIFIED)
        dtp->u.p.sign_status = dtp->u.p.current_unit->flags.sign;
 
@@ -2657,7 +2979,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
          find_option (&dtp->common, dtp->blank, dtp->blank_len,
                        blank_opt,
                        "Bad BLANK parameter in data transfer statement");
-  
+
   if (dtp->u.p.blank_status == BLANK_UNSPECIFIED)
        dtp->u.p.blank_status = dtp->u.p.current_unit->flags.blank;
 
@@ -2666,16 +2988,21 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        = !(cf & IOPARM_DT_HAS_DELIM) ? DELIM_UNSPECIFIED :
          find_option (&dtp->common, dtp->delim, dtp->delim_len,
          delim_opt, "Bad DELIM parameter in data transfer statement");
-  
+
   if (dtp->u.p.current_unit->delim_status == DELIM_UNSPECIFIED)
-    dtp->u.p.current_unit->delim_status = dtp->u.p.current_unit->flags.delim;
+    {
+      if (ionml && dtp->u.p.current_unit->flags.delim == DELIM_UNSPECIFIED)
+       dtp->u.p.current_unit->delim_status = DELIM_QUOTE;
+      else
+       dtp->u.p.current_unit->delim_status = dtp->u.p.current_unit->flags.delim;
+    }
 
   /* Check the pad mode.  */
   dtp->u.p.current_unit->pad_status
        = !(cf & IOPARM_DT_HAS_PAD) ? PAD_UNSPECIFIED :
          find_option (&dtp->common, dtp->pad, dtp->pad_len, pad_opt,
                        "Bad PAD parameter in data transfer statement");
-  
+
   if (dtp->u.p.current_unit->pad_status == PAD_UNSPECIFIED)
        dtp->u.p.current_unit->pad_status = dtp->u.p.current_unit->flags.pad;
 
@@ -2692,28 +3019,28 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
 
   /* Check the POS= specifier: that it is in range and that it is used with a
      unit that has been connected for STREAM access. F2003 9.5.1.10.  */
-  
+
   if (((cf & IOPARM_DT_HAS_POS) != 0))
     {
       if (is_stream_io (dtp))
         {
-          
+
           if (dtp->pos <= 0)
             {
               generate_error (&dtp->common, LIBERROR_BAD_OPTION,
                               "POS=specifier must be positive");
               return;
             }
-          
+
           if (dtp->pos >= dtp->u.p.current_unit->maxrec)
             {
               generate_error (&dtp->common, LIBERROR_BAD_OPTION,
                               "POS=specifier too large");
               return;
             }
-          
+
           dtp->rec = dtp->pos;
-          
+
           if (dtp->u.p.mode == READING)
             {
               /* Reset the endfile flag; if we hit EOF during reading
@@ -2721,7 +3048,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
                  rather than worrying about it here.  */
               dtp->u.p.current_unit->endfile = NO_ENDFILE;
             }
-         
+
           if (dtp->pos != dtp->u.p.current_unit->strm_pos)
             {
               fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode);
@@ -2741,7 +3068,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
           return;
         }
     }
-  
+
 
   /* Sanity checks on the record number.  */
   if ((cf & IOPARM_DT_HAS_REC) != 0)
@@ -2778,26 +3105,19 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
 
       /* Position the file.  */
       if (sseek (dtp->u.p.current_unit->s, (gfc_offset) (dtp->rec - 1)
-                 * dtp->u.p.current_unit->recl, SEEK_SET) < 0)
-        {
-          generate_error (&dtp->common, LIBERROR_OS, NULL);
-          return;
-        }
-
-      /* TODO: This is required to maintain compatibility between
-         4.3 and 4.4 runtime. Remove when ABI changes from 4.3 */
-
-      if (is_stream_io (dtp))
-        dtp->u.p.current_unit->strm_pos = dtp->rec;
+                * dtp->u.p.current_unit->recl, SEEK_SET) < 0)
+       {
+         generate_error (&dtp->common, LIBERROR_OS, NULL);
+         return;
+       }
 
-      /* TODO: Un-comment this code when ABI changes from 4.3.
       if (dtp->u.p.current_unit->flags.access == ACCESS_STREAM)
        {
          generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT,
                      "Record number not allowed for stream access "
                      "data transfer");
          return;
-       }  */
+       }
     }
 
   /* Bugware for badly written mixed C-Fortran I/O.  */
@@ -2811,7 +3131,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
   dtp->u.p.max_pos = dtp->u.p.current_unit->saved_pos;
 
   pre_position (dtp);
-  
+
 
   /* Set up the subroutine that will handle the transfers.  */
 
@@ -2823,8 +3143,9 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        {
          if ((cf & IOPARM_DT_LIST_FORMAT) != 0)
            {
-               dtp->u.p.last_char = EOF - 1;
-               dtp->u.p.transfer = list_formatted_read;
+             if (dtp->u.p.current_unit->child_dtio  == 0)
+               dtp->u.p.current_unit->last_char = EOF - 1;
+             dtp->u.p.transfer = list_formatted_read;
            }
          else
            dtp->u.p.transfer = formatted_transfer;
@@ -2860,25 +3181,39 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
        dtp->u.p.current_unit->read_bad = 1;
     }
 
-  /* Start the data transfer if we are doing a formatted transfer.  */
-  if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED
-      && ((cf & (IOPARM_DT_LIST_FORMAT | IOPARM_DT_HAS_NAMELIST_NAME)) == 0)
-      && dtp->u.p.ionml == NULL)
-    formatted_transfer (dtp, 0, NULL, 0, 0, 1);
+  if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED)
+    {
+#ifdef HAVE_USELOCALE
+      dtp->u.p.old_locale = uselocale (c_locale);
+#else
+      __gthread_mutex_lock (&old_locale_lock);
+      if (!old_locale_ctr++)
+       {
+         old_locale = setlocale (LC_NUMERIC, NULL);
+         setlocale (LC_NUMERIC, "C");
+       }
+      __gthread_mutex_unlock (&old_locale_lock);
+#endif
+      /* Start the data transfer if we are doing a formatted transfer.  */
+      if ((cf & (IOPARM_DT_LIST_FORMAT | IOPARM_DT_HAS_NAMELIST_NAME)) == 0
+       && dtp->u.p.ionml == NULL)
+       formatted_transfer (dtp, 0, NULL, 0, 0, 1);
+    }
 }
 
+
 /* Initialize an array_loop_spec given the array descriptor.  The function
    returns the index of the last element of the array, and also returns
    starting record, where the first I/O goes to (necessary in case of
    negative strides).  */
-   
+
 gfc_offset
 init_loop_spec (gfc_array_char *desc, array_loop_spec *ls,
                gfc_offset *start_record)
 {
   int rank = GFC_DESCRIPTOR_RANK(desc);
   int i;
-  gfc_offset index; 
+  gfc_offset index;
   int empty;
 
   empty = 0;
@@ -2891,7 +3226,7 @@ init_loop_spec (gfc_array_char *desc, array_loop_spec *ls,
       ls[i].start = GFC_DESCRIPTOR_LBOUND(desc,i);
       ls[i].end = GFC_DESCRIPTOR_UBOUND(desc,i);
       ls[i].step = GFC_DESCRIPTOR_STRIDE(desc,i);
-      empty = empty || (GFC_DESCRIPTOR_UBOUND(desc,i) 
+      empty = empty || (GFC_DESCRIPTOR_UBOUND(desc,i)
                        < GFC_DESCRIPTOR_LBOUND(desc,i));
 
       if (GFC_DESCRIPTOR_STRIDE(desc,i) > 0)
@@ -2916,13 +3251,13 @@ init_loop_spec (gfc_array_char *desc, array_loop_spec *ls,
 
 /* Determine the index to the next record in an internal unit array by
    by incrementing through the array_loop_spec.  */
-   
+
 gfc_offset
 next_array_record (st_parameter_dt *dtp, array_loop_spec *ls, int *finished)
 {
   int i, carry;
   gfc_offset index;
-  
+
   carry = 1;
   index = 0;
 
@@ -2955,10 +3290,10 @@ next_array_record (st_parameter_dt *dtp, array_loop_spec *ls, int *finished)
    position.  */
 
 static void
-skip_record (st_parameter_dt *dtp, ssize_t bytes)
+skip_record (st_parameter_dt *dtp, gfc_offset bytes)
 {
   ssize_t rlength, readb;
-  static const ssize_t MAX_READ = 4096;
+#define MAX_READ 4096
   char p[MAX_READ];
 
   dtp->u.p.current_unit->bytes_left_subrecord += bytes;
@@ -2967,13 +3302,13 @@ skip_record (st_parameter_dt *dtp, ssize_t bytes)
 
   /* Direct access files do not generate END conditions,
      only I/O errors.  */
-  if (sseek (dtp->u.p.current_unit->s, 
+  if (sseek (dtp->u.p.current_unit->s,
             dtp->u.p.current_unit->bytes_left_subrecord, SEEK_CUR) < 0)
     {
       /* Seeking failed, fall back to seeking by reading data.  */
       while (dtp->u.p.current_unit->bytes_left_subrecord > 0)
        {
-         rlength = 
+         rlength =
            (MAX_READ < dtp->u.p.current_unit->bytes_left_subrecord) ?
            MAX_READ : dtp->u.p.current_unit->bytes_left_subrecord;
 
@@ -3032,7 +3367,6 @@ static void
 next_record_r (st_parameter_dt *dtp, int done)
 {
   gfc_offset record;
-  int bytes_left;
   char p;
   int cc;
 
@@ -3041,7 +3375,7 @@ next_record_r (st_parameter_dt *dtp, int done)
     /* No records in unformatted STREAM I/O.  */
     case UNFORMATTED_STREAM:
       return;
-    
+
     case UNFORMATTED_SEQUENTIAL:
       next_record_r_unf (dtp, 1);
       dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
@@ -3082,13 +3416,13 @@ next_record_r (st_parameter_dt *dtp, int done)
                }
              dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl;
            }
-         else  
+         else
            {
-             bytes_left = (int) dtp->u.p.current_unit->bytes_left;
-             bytes_left = min_off (bytes_left, 
+             gfc_offset bytes_left = dtp->u.p.current_unit->bytes_left;
+             bytes_left = min_off (bytes_left,
                      ssize (dtp->u.p.current_unit->s)
                      - stell (dtp->u.p.current_unit->s));
-             if (sseek (dtp->u.p.current_unit->s, 
+             if (sseek (dtp->u.p.current_unit->s,
                         bytes_left, SEEK_CUR) < 0)
                {
                  generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL);
@@ -3096,16 +3430,16 @@ next_record_r (st_parameter_dt *dtp, int done)
                }
              dtp->u.p.current_unit->bytes_left
                = dtp->u.p.current_unit->recl;
-           } 
+           }
          break;
        }
-      else 
+      else if (dtp->u.p.current_unit->flags.cc != CC_NONE)
        {
          do
            {
               errno = 0;
               cc = fbuf_getc (dtp->u.p.current_unit);
-             if (cc == EOF) 
+             if (cc == EOF)
                {
                   if (errno != 0)
                     generate_error (&dtp->common, LIBERROR_OS, NULL);
@@ -3119,10 +3453,10 @@ next_record_r (st_parameter_dt *dtp, int done)
                    }
                  break;
                 }
-             
+
              if (is_stream_io (dtp))
                dtp->u.p.current_unit->strm_pos++;
-              
+
               p = (char) cc;
            }
          while (p != '\n');
@@ -3207,17 +3541,6 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord)
   m = dtp->u.p.current_unit->recl_subrecord
     - dtp->u.p.current_unit->bytes_left_subrecord;
 
-  /* 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 (unlikely (write_us_marker (dtp, m_write) < 0))
-    goto io_error;
-
   if (compile_options.record_marker == 0)
     record_marker = sizeof (GFC_INTEGER_4);
   else
@@ -3226,7 +3549,7 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord)
   /* Seek to the head and overwrite the bogus length with the real
      length.  */
 
-  if (unlikely (sseek (dtp->u.p.current_unit->s, - m - 2 * record_marker, 
+  if (unlikely (sseek (dtp->u.p.current_unit->s, - m - record_marker,
                       SEEK_CUR) < 0))
     goto io_error;
 
@@ -3240,8 +3563,18 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord)
 
   /* Seek past the end of the current record.  */
 
-  if (unlikely (sseek (dtp->u.p.current_unit->s, m + record_marker, 
-                      SEEK_CUR) < 0))
+  if (unlikely (sseek (dtp->u.p.current_unit->s, m, SEEK_CUR) < 0))
+    goto io_error;
+
+  /* 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 (unlikely (write_us_marker (dtp, m_write) < 0))
     goto io_error;
 
   return;
@@ -3256,12 +3589,13 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord)
 /* Utility function like memset() but operating on streams. Return
    value is same as for POSIX write().  */
 
-static ssize_t
-sset (stream * s, int c, ssize_t nbyte)
+static gfc_offset
+sset (stream *s, int c, gfc_offset nbyte)
 {
-  static const int WRITE_CHUNK = 256;
+#define WRITE_CHUNK 256
   char p[WRITE_CHUNK];
-  ssize_t bytes_left, trans;
+  gfc_offset bytes_left;
+  ssize_t trans;
 
   if (nbyte < WRITE_CHUNK)
     memset (p, c, nbyte);
@@ -3277,21 +3611,44 @@ sset (stream * s, int c, ssize_t nbyte)
        return trans;
       bytes_left -= trans;
     }
-              
+
   return nbyte - bytes_left;
 }
 
 
+/* Finish up a record according to the legacy carriagecontrol type, based
+   on the first character in the record.  */
+
+static void
+next_record_cc (st_parameter_dt *dtp)
+{
+  /* Only valid with CARRIAGECONTROL=FORTRAN.  */
+  if (dtp->u.p.current_unit->flags.cc != CC_FORTRAN)
+    return;
+
+  fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
+  if (dtp->u.p.cc.len > 0)
+    {
+      char *p = fbuf_alloc (dtp->u.p.current_unit, dtp->u.p.cc.len);
+      if (!p)
+       generate_error (&dtp->common, LIBERROR_OS, NULL);
+
+      /* Output CR for the first character with default CC setting.  */
+      *(p++) = dtp->u.p.cc.u.end;
+      if (dtp->u.p.cc.len > 1)
+       *p = dtp->u.p.cc.u.end;
+    }
+}
+
 /* Position to the next record in write mode.  */
 
 static void
 next_record_w (st_parameter_dt *dtp, int done)
 {
-  gfc_offset m, record, max_pos;
-  int length;
+  gfc_offset max_pos_off;
 
   /* Zero counters for X- and T-editing.  */
-  max_pos = dtp->u.p.max_pos;
+  max_pos_off = dtp->u.p.max_pos;
   dtp->u.p.max_pos = dtp->u.p.skips = dtp->u.p.pending_spaces = 0;
 
   switch (current_mode (dtp))
@@ -3306,8 +3663,8 @@ next_record_w (st_parameter_dt *dtp, int done)
 
       fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
       fbuf_flush (dtp->u.p.current_unit, WRITING);
-      if (sset (dtp->u.p.current_unit->s, ' ', 
-               dtp->u.p.current_unit->bytes_left) 
+      if (sset (dtp->u.p.current_unit->s, ' ',
+               dtp->u.p.current_unit->bytes_left)
          != dtp->u.p.current_unit->bytes_left)
        goto io_error;
 
@@ -3316,7 +3673,7 @@ next_record_w (st_parameter_dt *dtp, int done)
     case UNFORMATTED_DIRECT:
       if (dtp->u.p.current_unit->bytes_left > 0)
        {
-         length = (int) dtp->u.p.current_unit->bytes_left;
+         gfc_offset length = dtp->u.p.current_unit->bytes_left;
          if (sset (dtp->u.p.current_unit->s, 0, length) != length)
            goto io_error;
        }
@@ -3333,12 +3690,15 @@ next_record_w (st_parameter_dt *dtp, int done)
       if (is_internal_unit (dtp))
        {
          char *p;
+         /* Internal unit, so must fit in memory.  */
+         size_t length, m, record;
+         size_t max_pos = max_pos_off;
          if (is_array_io (dtp))
            {
              int finished;
 
-             length = (int) dtp->u.p.current_unit->bytes_left;
-             
+             length = dtp->u.p.current_unit->bytes_left;
+
              /* If the farthest position reached is greater than current
              position, adjust the position and set length to pad out
              whats left.  Otherwise just pad whats left.
@@ -3347,14 +3707,14 @@ next_record_w (st_parameter_dt *dtp, int done)
                        - dtp->u.p.current_unit->bytes_left;
              if (max_pos > m)
                {
-                 length = (int) (max_pos - m);
-                 if (sseek (dtp->u.p.current_unit->s, 
+                 length = (max_pos - m);
+                 if (sseek (dtp->u.p.current_unit->s,
                             length, SEEK_CUR) < 0)
                    {
                      generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL);
                      return;
                    }
-                 length = (int) (dtp->u.p.current_unit->recl - max_pos);
+                 length = ((size_t) dtp->u.p.current_unit->recl - max_pos);
                }
 
              p = write_block (dtp, length);
@@ -3375,9 +3735,9 @@ next_record_w (st_parameter_dt *dtp, int done)
                                          &finished);
              if (finished)
                dtp->u.p.current_unit->endfile = AT_ENDFILE;
-             
+
              /* Now seek to this record */
-             record = record * dtp->u.p.current_unit->recl;
+             record = record * ((size_t) dtp->u.p.current_unit->recl);
 
              if (sseek (dtp->u.p.current_unit->s, record, SEEK_SET) < 0)
                {
@@ -3400,17 +3760,18 @@ next_record_w (st_parameter_dt *dtp, int done)
                        - dtp->u.p.current_unit->bytes_left;
                  if (max_pos > m)
                    {
-                     length = (int) (max_pos - m);
-                     if (sseek (dtp->u.p.current_unit->s, 
+                     length = max_pos - m;
+                     if (sseek (dtp->u.p.current_unit->s,
                                 length, SEEK_CUR) < 0)
                        {
                          generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL);
                          return;
                        }
-                     length = (int) (dtp->u.p.current_unit->recl - max_pos);
+                     length = (size_t) dtp->u.p.current_unit->recl
+                       - max_pos;
                    }
                  else
-                   length = (int) dtp->u.p.current_unit->bytes_left;
+                   length = dtp->u.p.current_unit->bytes_left;
                }
              if (length > 0)
                {
@@ -3428,21 +3789,30 @@ next_record_w (st_parameter_dt *dtp, int done)
                }
            }
        }
+      /* Handle legacy CARRIAGECONTROL line endings.  */
+      else if (dtp->u.p.current_unit->flags.cc == CC_FORTRAN)
+       next_record_cc (dtp);
       else
        {
+         /* Skip newlines for CC=CC_NONE.  */
+         const int len = (dtp->u.p.current_unit->flags.cc == CC_NONE)
+           ? 0
 #ifdef HAVE_CRLF
-         const int len = 2;
+           : 2;
 #else
-         const int len = 1;
+           : 1;
 #endif
-          fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
-          char * p = fbuf_alloc (dtp->u.p.current_unit, len);
-          if (!p)
-            goto io_error;
+         fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
+         if (dtp->u.p.current_unit->flags.cc != CC_NONE)
+           {
+             char *p = fbuf_alloc (dtp->u.p.current_unit, len);
+             if (!p)
+               goto io_error;
 #ifdef HAVE_CRLF
-          *(p++) = '\r';
+             *(p++) = '\r';
 #endif
-          *p = '\n';
+             *p = '\n';
+           }
          if (is_stream_io (dtp))
            {
              dtp->u.p.current_unit->strm_pos += len;
@@ -3479,6 +3849,8 @@ next_record (st_parameter_dt *dtp, int done)
   else
     next_record_w (dtp, done);
 
+  fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode);
+
   if (!is_stream_io (dtp))
     {
       /* Since we have changed the position, set it to unspecified so
@@ -3492,8 +3864,8 @@ next_record (st_parameter_dt *dtp, int done)
          fp = stell (dtp->u.p.current_unit->s);
          /* Calculate next record, rounding up partial records.  */
          dtp->u.p.current_unit->last_record =
-           (fp + dtp->u.p.current_unit->recl - 1) /
-             dtp->u.p.current_unit->recl;
+           (fp + dtp->u.p.current_unit->recl) /
+             dtp->u.p.current_unit->recl - 1;
        }
       else
        dtp->u.p.current_unit->last_record++;
@@ -3502,7 +3874,7 @@ next_record (st_parameter_dt *dtp, int done)
   if (!done)
     pre_position (dtp);
 
-  fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode);
+  smarkeor (dtp->u.p.current_unit->s);
 }
 
 
@@ -3515,39 +3887,50 @@ finalize_transfer (st_parameter_dt *dtp)
 {
   GFC_INTEGER_4 cf = dtp->common.flags;
 
+  if ((dtp->u.p.ionml != NULL)
+      && (cf & IOPARM_DT_HAS_NAMELIST_NAME) != 0)
+    {
+       dtp->u.p.namelist_mode = 1;
+       if ((cf & IOPARM_DT_NAMELIST_READ_MODE) != 0)
+        namelist_read (dtp);
+       else
+        namelist_write (dtp);
+    }
+
   if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    *dtp->size = dtp->u.p.size_used;
+    *dtp->size = dtp->u.p.current_unit->size_used;
 
   if (dtp->u.p.eor_condition)
     {
       generate_error (&dtp->common, LIBERROR_EOR, NULL);
-      return;
+      goto done;
     }
 
-  if ((dtp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
+  if (dtp->u.p.current_unit && (dtp->u.p.current_unit->child_dtio  > 0))
     {
-      if (dtp->u.p.current_unit && current_mode (dtp) == UNFORMATTED_SEQUENTIAL)
-       dtp->u.p.current_unit->current_record = 0;
+      if (cf & IOPARM_DT_HAS_FORMAT)
+        {
+         free (dtp->u.p.fmt);
+         free (dtp->format);
+       }
       return;
     }
 
-  if ((dtp->u.p.ionml != NULL)
-      && (cf & IOPARM_DT_HAS_NAMELIST_NAME) != 0)
+  if ((dtp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
     {
-       if ((cf & IOPARM_DT_NAMELIST_READ_MODE) != 0)
-        namelist_read (dtp);
-       else
-        namelist_write (dtp);
+      if (dtp->u.p.current_unit && current_mode (dtp) == UNFORMATTED_SEQUENTIAL)
+       dtp->u.p.current_unit->current_record = 0;
+      goto done;
     }
 
   dtp->u.p.transfer = NULL;
   if (dtp->u.p.current_unit == NULL)
-    return;
+    goto done;
 
   if ((cf & IOPARM_DT_LIST_FORMAT) != 0 && dtp->u.p.mode == READING)
     {
       finish_list_read (dtp);
-      return;
+      goto done;
     }
 
   if (dtp->u.p.mode == WRITING)
@@ -3560,7 +3943,7 @@ finalize_transfer (st_parameter_dt *dtp)
          && dtp->u.p.advance_status != ADVANCE_NO)
        next_record (dtp, 1);
 
-      return;
+      goto done;
     }
 
   dtp->u.p.current_unit->current_record = 0;
@@ -3569,36 +3952,76 @@ finalize_transfer (st_parameter_dt *dtp)
     {
       fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode);
       dtp->u.p.seen_dollar = 0;
-      return;
+      goto done;
     }
 
   /* For non-advancing I/O, save the current maximum position for use in the
      next I/O operation if needed.  */
   if (dtp->u.p.advance_status == ADVANCE_NO)
     {
+      if (dtp->u.p.skips > 0)
+       {
+         int tmp;
+         write_x (dtp, dtp->u.p.skips, dtp->u.p.pending_spaces);
+         tmp = (int)(dtp->u.p.current_unit->recl
+                     - dtp->u.p.current_unit->bytes_left);
+         dtp->u.p.max_pos =
+           dtp->u.p.max_pos > tmp ? dtp->u.p.max_pos : tmp;
+         dtp->u.p.skips = 0;
+       }
       int bytes_written = (int) (dtp->u.p.current_unit->recl
        - dtp->u.p.current_unit->bytes_left);
       dtp->u.p.current_unit->saved_pos =
        dtp->u.p.max_pos > 0 ? dtp->u.p.max_pos - bytes_written : 0;
       fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode);
-      return;
+      goto done;
     }
-  else if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED 
+  else if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED
            && dtp->u.p.mode == WRITING && !is_internal_unit (dtp))
-      fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);    
+      fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END);
 
   dtp->u.p.current_unit->saved_pos = 0;
-
+  dtp->u.p.current_unit->last_char = EOF - 1;
   next_record (dtp, 1);
+
+ done:
+
+  if (dtp->u.p.unit_is_internal)
+    {
+      fbuf_destroy (dtp->u.p.current_unit);
+      if (dtp->u.p.current_unit
+         && (dtp->u.p.current_unit->child_dtio  == 0)
+         && dtp->u.p.current_unit->s)
+       {
+         sclose (dtp->u.p.current_unit->s);
+         dtp->u.p.current_unit->s = NULL;
+       }
+    }
+
+#ifdef HAVE_USELOCALE
+  if (dtp->u.p.old_locale != (locale_t) 0)
+    {
+      uselocale (dtp->u.p.old_locale);
+      dtp->u.p.old_locale = (locale_t) 0;
+    }
+#else
+  __gthread_mutex_lock (&old_locale_lock);
+  if (!--old_locale_ctr)
+    {
+      setlocale (LC_NUMERIC, old_locale);
+      old_locale = NULL;
+    }
+  __gthread_mutex_unlock (&old_locale_lock);
+#endif
 }
 
 /* Transfer function for IOLENGTH. It doesn't actually do any
    data transfer, it just updates the length counter.  */
 
 static void
-iolength_transfer (st_parameter_dt *dtp, bt type __attribute__((unused)), 
+iolength_transfer (st_parameter_dt *dtp, bt type __attribute__((unused)),
                   void *dest __attribute__ ((unused)),
-                  int kind __attribute__((unused)), 
+                  int kind __attribute__((unused)),
                   size_t size, size_t nelems)
 {
   if ((dtp->common.flags & IOPARM_DT_HAS_IOLENGTH) != 0)
@@ -3670,14 +4093,34 @@ void
 st_read_done (st_parameter_dt *dtp)
 {
   finalize_transfer (dtp);
-  if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
-    free_format_data (dtp->u.p.fmt);
+
   free_ionml (dtp);
-  if (dtp->u.p.current_unit != NULL)
-    unlock_unit (dtp->u.p.current_unit);
 
-  free_internal_unit (dtp);
-  
+  /* If this is a parent READ statement we do not need to retain the
+     internal unit structure for child use.  */
+  if (dtp->u.p.current_unit != NULL
+      && dtp->u.p.current_unit->child_dtio == 0)
+    {
+      if (dtp->u.p.unit_is_internal)
+       {
+         if ((dtp->common.flags & IOPARM_DT_HAS_UDTIO) == 0)
+           {
+             free (dtp->u.p.current_unit->filename);
+             dtp->u.p.current_unit->filename = NULL;
+             if (dtp->u.p.current_unit->ls)
+               free (dtp->u.p.current_unit->ls);
+             dtp->u.p.current_unit->ls = NULL;
+           }
+         newunit_free (dtp->common.unit);
+       }
+      if (dtp->u.p.unit_is_internal || dtp->u.p.format_not_saved)
+       {
+         free_format_data (dtp->u.p.fmt);
+         free_format (dtp);
+       }
+      unlock_unit (dtp->u.p.current_unit);
+    }
+
   library_end ();
 }
 
@@ -3699,37 +4142,53 @@ st_write_done (st_parameter_dt *dtp)
 {
   finalize_transfer (dtp);
 
-  /* Deal with endfile conditions associated with sequential files.  */
-
-  if (dtp->u.p.current_unit != NULL 
-      && dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL)
-    switch (dtp->u.p.current_unit->endfile)
-      {
-      case AT_ENDFILE:         /* Remain at the endfile record.  */
-       break;
+  if (dtp->u.p.current_unit != NULL
+      && dtp->u.p.current_unit->child_dtio == 0)
+    {
+      /* Deal with endfile conditions associated with sequential files.  */
+      if (dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL)
+       switch (dtp->u.p.current_unit->endfile)
+         {
+         case AT_ENDFILE:              /* Remain at the endfile record.  */
+           break;
 
-      case AFTER_ENDFILE:
-       dtp->u.p.current_unit->endfile = AT_ENDFILE;    /* Just at it now.  */
-       break;
+         case AFTER_ENDFILE:
+           dtp->u.p.current_unit->endfile = AT_ENDFILE; /* Just at it now.  */
+           break;
 
-      case NO_ENDFILE:
-       /* Get rid of whatever is after this record.  */
-        if (!is_internal_unit (dtp))
-          unit_truncate (dtp->u.p.current_unit, 
-                         stell (dtp->u.p.current_unit->s),
-                         &dtp->common);
-       dtp->u.p.current_unit->endfile = AT_ENDFILE;
-       break;
-      }
+         case NO_ENDFILE:
+           /* Get rid of whatever is after this record.  */
+           if (!is_internal_unit (dtp))
+             unit_truncate (dtp->u.p.current_unit,
+                            stell (dtp->u.p.current_unit->s),
+                            &dtp->common);
+           dtp->u.p.current_unit->endfile = AT_ENDFILE;
+           break;
+         }
 
-  if (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
-    free_format_data (dtp->u.p.fmt);
-  free_ionml (dtp);
-  if (dtp->u.p.current_unit != NULL)
-    unlock_unit (dtp->u.p.current_unit);
-  
-  free_internal_unit (dtp);
+      free_ionml (dtp);
 
+      /* If this is a parent WRITE statement we do not need to retain the
+        internal unit structure for child use.  */
+      if (dtp->u.p.unit_is_internal)
+       {
+         if ((dtp->common.flags & IOPARM_DT_HAS_UDTIO) == 0)
+           {
+             free (dtp->u.p.current_unit->filename);
+             dtp->u.p.current_unit->filename = NULL;
+             if (dtp->u.p.current_unit->ls)
+               free (dtp->u.p.current_unit->ls);
+             dtp->u.p.current_unit->ls = NULL;
+           }
+         newunit_free (dtp->common.unit);
+       }
+      if (dtp->u.p.unit_is_internal || dtp->u.p.format_not_saved)
+       {
+         free_format_data (dtp->u.p.fmt);
+         free_format (dtp);
+       }
+      unlock_unit (dtp->u.p.current_unit);
+    }
   library_end ();
 }
 
@@ -3744,15 +4203,10 @@ st_wait (st_parameter_wait *wtp __attribute__((unused)))
 /* Receives the scalar information for namelist objects and stores it
    in a linked list of namelist_info types.  */
 
-extern void st_set_nml_var (st_parameter_dt *dtp, void *, char *,
-                           GFC_INTEGER_4, gfc_charlen_type, GFC_INTEGER_4);
-export_proto(st_set_nml_var);
-
-
-void
-st_set_nml_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
-               GFC_INTEGER_4 len, gfc_charlen_type string_length,
-               GFC_INTEGER_4 dtype)
+static void
+set_nml_var (st_parameter_dt *dtp, void *var_addr, char *var_name,
+            GFC_INTEGER_4 len, gfc_charlen_type string_length,
+            GFC_INTEGER_4 dtype, void *dtio_sub, void *vtable)
 {
   namelist_info *t1 = NULL;
   namelist_info *nml;
@@ -3761,6 +4215,8 @@ st_set_nml_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
   nml = (namelist_info*) xmalloc (sizeof (namelist_info));
 
   nml->mem_pos = var_addr;
+  nml->dtio_sub = dtio_sub;
+  nml->vtable = vtable;
 
   nml->var_name = (char*) xmalloc (var_name_len + 1);
   memcpy (nml->var_name, var_name, var_name_len);
@@ -3776,9 +4232,9 @@ st_set_nml_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
   if (nml->var_rank > 0)
     {
       nml->dim = (descriptor_dimension*)
-                  xmalloc (nml->var_rank * sizeof (descriptor_dimension));
+       xmallocarray (nml->var_rank, sizeof (descriptor_dimension));
       nml->ls = (array_loop_spec*)
-                 xmalloc (nml->var_rank * sizeof (array_loop_spec));
+       xmallocarray (nml->var_rank, sizeof (array_loop_spec));
     }
   else
     {
@@ -3800,6 +4256,37 @@ st_set_nml_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
     }
 }
 
+extern void st_set_nml_var (st_parameter_dt *dtp, void *, char *,
+                           GFC_INTEGER_4, gfc_charlen_type, GFC_INTEGER_4);
+export_proto(st_set_nml_var);
+
+void
+st_set_nml_var (st_parameter_dt *dtp, void *var_addr, char *var_name,
+               GFC_INTEGER_4 len, gfc_charlen_type string_length,
+               GFC_INTEGER_4 dtype)
+{
+  set_nml_var (dtp, var_addr, var_name, len, string_length,
+              dtype, NULL, NULL);
+}
+
+
+/* Essentially the same as previous but carrying the dtio procedure
+   and the vtable as additional arguments.  */
+extern void st_set_nml_dtio_var (st_parameter_dt *dtp, void *, char *,
+                                GFC_INTEGER_4, gfc_charlen_type, GFC_INTEGER_4,
+                                void *, void *);
+export_proto(st_set_nml_dtio_var);
+
+
+void
+st_set_nml_dtio_var (st_parameter_dt *dtp, void *var_addr, char *var_name,
+                    GFC_INTEGER_4 len, gfc_charlen_type string_length,
+                    GFC_INTEGER_4 dtype, void *dtio_sub, void *vtable)
+{
+  set_nml_var (dtp, var_addr, var_name, len, string_length,
+              dtype, dtio_sub, vtable);
+}
+
 /* Store the dimensional information for the namelist object.  */
 extern void st_set_nml_var_dim (st_parameter_dt *, GFC_INTEGER_4,
                                index_type, index_type,
@@ -3811,7 +4298,7 @@ st_set_nml_var_dim (st_parameter_dt *dtp, GFC_INTEGER_4 n_dim,
                    index_type stride, index_type lbound,
                    index_type ubound)
 {
-  namelist_info * nml;
+  namelist_info *nml;
   int n;
 
   n = (int)n_dim;
@@ -3830,7 +4317,7 @@ st_set_nml_var_dim (st_parameter_dt *dtp, GFC_INTEGER_4 n_dim,
    9.10.2 in F2003.  */
 
 void
-hit_eof (st_parameter_dt * dtp)
+hit_eof (st_parameter_dt *dtp)
 {
   dtp->u.p.current_unit->flags.position = POSITION_APPEND;
 
@@ -3848,7 +4335,7 @@ hit_eof (st_parameter_dt * dtp)
         else
           dtp->u.p.current_unit->endfile = AT_ENDFILE;
        break;
-        
+
       case AFTER_ENDFILE:
        generate_error (&dtp->common, LIBERROR_ENDFILE, NULL);
        dtp->u.p.current_unit->current_record = 0;