PR 78534, 83704 Handle large formatted I/O
[gcc.git] / libgfortran / io / transfer.c
index b47f4e07c82cf16481e4dc14cfb55385c84efae0..f9c8696766f43b62e5f51bf3241f7fa7fccbbaa2 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002-2017 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
@@ -93,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);
@@ -223,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
@@ -244,14 +244,22 @@ read_sf_internal (st_parameter_dt *dtp, int * length)
       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
@@ -288,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.  */
@@ -304,6 +313,15 @@ read_sf (st_parameter_dt *dtp, int * length)
       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.  */
@@ -421,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))
     {
@@ -433,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
            {
@@ -446,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;
@@ -503,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;
@@ -712,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;
 
@@ -726,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
            {
@@ -1244,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)
 {
@@ -1436,6 +1487,9 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind
        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;
@@ -1476,6 +1530,7 @@ formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind
 
          /* 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);
@@ -1737,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;
@@ -1801,10 +1856,10 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
        {
          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);
+             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;
@@ -1820,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;
@@ -1938,8 +1993,12 @@ formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kin
              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);
@@ -2172,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;
     }
 
@@ -2272,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];
 
@@ -2290,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];
 
@@ -2314,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);
 }
@@ -2351,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++)
@@ -2612,7 +2671,7 @@ 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)
@@ -2664,8 +2723,6 @@ 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:
@@ -2673,11 +2730,11 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
          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:
@@ -2707,6 +2764,8 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
       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.  */
 
@@ -2795,6 +2854,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag)
          return;
        }
     }
+
   /* Process the ADVANCE option.  */
 
   dtp->u.p.advance_status
@@ -2828,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;
@@ -3225,7 +3290,7 @@ 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;
 #define MAX_READ 4096
@@ -3302,7 +3367,6 @@ static void
 next_record_r (st_parameter_dt *dtp, int done)
 {
   gfc_offset record;
-  int bytes_left;
   char p;
   int cc;
 
@@ -3354,7 +3418,7 @@ next_record_r (st_parameter_dt *dtp, int done)
            }
          else
            {
-             bytes_left = (int) dtp->u.p.current_unit->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));
@@ -3525,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)
 {
 #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);
@@ -3564,7 +3629,7 @@ next_record_cc (st_parameter_dt *dtp)
   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);
+      char *p = fbuf_alloc (dtp->u.p.current_unit, dtp->u.p.cc.len);
       if (!p)
        generate_error (&dtp->common, LIBERROR_OS, NULL);
 
@@ -3580,11 +3645,10 @@ next_record_cc (st_parameter_dt *dtp)
 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))
@@ -3609,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;
        }
@@ -3626,11 +3690,14 @@ 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
@@ -3640,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);
+                 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);
@@ -3670,7 +3737,7 @@ next_record_w (st_parameter_dt *dtp, int done)
                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)
                {
@@ -3693,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);
+                     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)
                {
@@ -3737,7 +3805,7 @@ next_record_w (st_parameter_dt *dtp, int done)
          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);
+             char *p = fbuf_alloc (dtp->u.p.current_unit, len);
              if (!p)
                goto io_error;
 #ifdef HAVE_CRLF
@@ -3822,12 +3890,22 @@ finalize_transfer (st_parameter_dt *dtp)
   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.current_unit->size_used;
+
+  if (dtp->u.p.eor_condition)
+    {
+      generate_error (&dtp->common, LIBERROR_EOR, NULL);
+      goto done;
+    }
+
   if (dtp->u.p.current_unit && (dtp->u.p.current_unit->child_dtio  > 0))
     {
       if (cf & IOPARM_DT_HAS_FORMAT)
@@ -3838,15 +3916,6 @@ finalize_transfer (st_parameter_dt *dtp)
       return;
     }
 
-  if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0)
-    *dtp->size = dtp->u.p.current_unit->size_used;
-
-  if (dtp->u.p.eor_condition)
-    {
-      generate_error (&dtp->common, LIBERROR_EOR, NULL);
-      goto done;
-    }
-
   if ((dtp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK)
     {
       if (dtp->u.p.current_unit && current_mode (dtp) == UNFORMATTED_SEQUENTIAL)
@@ -3912,10 +3981,23 @@ finalize_transfer (st_parameter_dt *dtp)
       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)
     {
@@ -4015,24 +4097,23 @@ st_read_done (st_parameter_dt *dtp)
   free_ionml (dtp);
 
   /* If this is a parent READ statement we do not need to retain the
-     internal unit structure for child use.  Free it and stash the unit
-     number for reuse.  */
+     internal unit structure for child use.  */
   if (dtp->u.p.current_unit != NULL
       && dtp->u.p.current_unit->child_dtio == 0)
     {
-      if (is_internal_unit (dtp) &&
-         (dtp->common.flags & IOPARM_DT_HAS_UDTIO) == 0)
-        {
-         free (dtp->u.p.current_unit->filename);
-         dtp->u.p.current_unit->filename = NULL;
-         free (dtp->u.p.current_unit->s);
-         dtp->u.p.current_unit->s = NULL;
-         if (dtp->u.p.current_unit->ls)
-           free (dtp->u.p.current_unit->ls);
-         dtp->u.p.current_unit->ls = NULL;
-         stash_internal_unit (dtp);
+      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 (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
+      if (dtp->u.p.unit_is_internal || dtp->u.p.format_not_saved)
        {
          free_format_data (dtp->u.p.fmt);
          free_format (dtp);
@@ -4088,21 +4169,20 @@ st_write_done (st_parameter_dt *dtp)
       free_ionml (dtp);
 
       /* If this is a parent WRITE statement we do not need to retain the
-        internal unit structure for child use.  Free it and stash the
-        unit number for reuse.  */
-      if (is_internal_unit (dtp) &&
-         (dtp->common.flags & IOPARM_DT_HAS_UDTIO) == 0)
+        internal unit structure for child use.  */
+      if (dtp->u.p.unit_is_internal)
        {
-         free (dtp->u.p.current_unit->filename);
-         dtp->u.p.current_unit->filename = NULL;
-         free (dtp->u.p.current_unit->s);
-         dtp->u.p.current_unit->s = NULL;
-         if (dtp->u.p.current_unit->ls)
-           free (dtp->u.p.current_unit->ls);
-         dtp->u.p.current_unit->ls = NULL;
-         stash_internal_unit (dtp);
+         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 (is_internal_unit (dtp) || dtp->u.p.format_not_saved)
+      if (dtp->u.p.unit_is_internal || dtp->u.p.format_not_saved)
        {
          free_format_data (dtp->u.p.fmt);
          free_format (dtp);
@@ -4124,7 +4204,7 @@ st_wait (st_parameter_wait *wtp __attribute__((unused)))
    in a linked list of namelist_info types.  */
 
 static void
-set_nml_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
+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)
 {
@@ -4181,7 +4261,7 @@ extern void st_set_nml_var (st_parameter_dt *dtp, void *, char *,
 export_proto(st_set_nml_var);
 
 void
-st_set_nml_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
+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)
 {
@@ -4199,7 +4279,7 @@ export_proto(st_set_nml_dtio_var);
 
 
 void
-st_set_nml_dtio_var (st_parameter_dt *dtp, void * var_addr, char * var_name,
+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)
 {
@@ -4218,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;
@@ -4237,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;