PR 78534, 83704 Handle large formatted I/O
[gcc.git] / libgfortran / io / read.c
index 962c0a2581feb999758b866cfe76033b8fe6e8f6..87adfb8a41db1c9f776aa292788ee8786af623d5 100644 (file)
@@ -1,11 +1,12 @@
-/* Copyright (C) 2002-2003 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2018 Free Software Foundation, Inc.
    Contributed by Andy Vaught
+   F2003 I/O support contributed by Jerry DeLisle
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
 Libgfortran is distributed in the hope that it will be useful,
@@ -13,127 +14,270 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
-You should have received a copy of the GNU General Public License
-along with Libgfortran; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
 
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
 
-#include "config.h"
+#include "io.h"
+#include "fbuf.h"
+#include "format.h"
+#include "unix.h"
 #include <string.h>
-#include <errno.h>
 #include <ctype.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include "libgfortran.h"
-#include "io.h"
+#include <assert.h>
+
+typedef unsigned char uchar;
 
 /* read.c -- Deal with formatted reads */
 
+
 /* set_integer()-- All of the integer assignments come here to
* actually place the value into memory.  */
  actually place the value into memory.  */
 
 void
-set_integer (void *dest, int64_t value, int length)
+set_integer (void *dest, GFC_INTEGER_LARGEST value, int length)
 {
   switch (length)
     {
+#ifdef HAVE_GFC_INTEGER_16
+/* length=10 comes about for kind=10 real/complex BOZ, cf. PR41711. */
+    case 10:
+    case 16:
+      {
+       GFC_INTEGER_16 tmp = value;
+       memcpy (dest, (void *) &tmp, length);
+      }
+      break;
+#endif
     case 8:
-      *((int64_t *) dest) = value;
+      {
+       GFC_INTEGER_8 tmp = value;
+       memcpy (dest, (void *) &tmp, length);
+      }
       break;
     case 4:
-      *((int32_t *) dest) = value;
+      {
+       GFC_INTEGER_4 tmp = value;
+       memcpy (dest, (void *) &tmp, length);
+      }
       break;
     case 2:
-      *((int16_t *) dest) = value;
+      {
+       GFC_INTEGER_2 tmp = value;
+       memcpy (dest, (void *) &tmp, length);
+      }
       break;
     case 1:
-      *((int8_t *) dest) = value;
+      {
+       GFC_INTEGER_1 tmp = value;
+       memcpy (dest, (void *) &tmp, length);
+      }
       break;
     default:
-      internal_error ("Bad integer kind");
+      internal_error (NULL, "Bad integer kind");
     }
 }
 
 
-/* max_value()-- Given a length (kind), return the maximum signed or
- * unsigned value */
+/* Max signed value of size give by length argument.  */
 
-uint64_t
-max_value (int length, int signed_flag)
+GFC_UINTEGER_LARGEST
+si_max (int length)
 {
-  uint64_t value;
+#if defined HAVE_GFC_REAL_16 || defined HAVE_GFC_REAL_10
+  GFC_UINTEGER_LARGEST value;
+#endif
 
   switch (length)
-    {
+      {
+#if defined HAVE_GFC_REAL_16 || defined HAVE_GFC_REAL_10
+    case 16:
+    case 10:
+      value = 1;
+      for (int n = 1; n < 4 * length; n++)
+        value = (value << 2) + 3;
+      return value;
+#endif
     case 8:
-      value = signed_flag ? 0x7fffffffffffffff : 0xffffffffffffffff;
-      break;
+      return GFC_INTEGER_8_HUGE;
     case 4:
-      value = signed_flag ? 0x7fffffff : 0xffffffff;
-      break;
+      return GFC_INTEGER_4_HUGE;
     case 2:
-      value = signed_flag ? 0x7fff : 0xffff;
-      break;
+      return GFC_INTEGER_2_HUGE;
     case 1:
-      value = signed_flag ? 0x7f : 0xff;
-      break;
+      return GFC_INTEGER_1_HUGE;
     default:
-      internal_error ("Bad integer kind");
+      internal_error (NULL, "Bad integer kind");
     }
-
-  return value;
 }
 
 
 /* convert_real()-- Convert a character representation of a floating
- * point number to the machine number.  Returns nonzero if there is a
- * range problem during conversion.  TODO: handle not-a-numbers and
- * infinities.  */
+   point number to the machine number.  Returns nonzero if there is an
+   invalid input.  Note: many architectures (e.g. IA-64, HP-PA)
+   require that the storage pointed to by the dest argument is
+   properly aligned for the type in question.  */
 
 int
-convert_real (void *dest, const char *buffer, int length)
+convert_real (st_parameter_dt *dtp, void *dest, const char *buffer, int length)
 {
-  errno = 0;
+  char *endptr = NULL;
+  int round_mode, old_round_mode;
+
+  switch (dtp->u.p.current_unit->round_status)
+    {
+      case ROUND_COMPATIBLE:
+       /* FIXME: As NEAREST but round away from zero for a tie.  */
+      case ROUND_UNSPECIFIED:
+       /* Should not occur.  */
+      case ROUND_PROCDEFINED:
+       round_mode = ROUND_NEAREST;
+       break;
+      default:
+       round_mode = dtp->u.p.current_unit->round_status;
+       break;
+    }
+
+  old_round_mode = get_fpu_rounding_mode();
+  set_fpu_rounding_mode (round_mode);
 
   switch (length)
     {
     case 4:
-      *((float *) dest) =
+      *((GFC_REAL_4*) dest) =
 #if defined(HAVE_STRTOF)
-       strtof (buffer, NULL);
+       gfc_strtof (buffer, &endptr);
 #else
-       (float) strtod (buffer, NULL);
+       (GFC_REAL_4) gfc_strtod (buffer, &endptr);
 #endif
       break;
+
     case 8:
-      *((double *) dest) = strtod (buffer, NULL);
+      *((GFC_REAL_8*) dest) = gfc_strtod (buffer, &endptr);
+      break;
+
+#if defined(HAVE_GFC_REAL_10) && defined (HAVE_STRTOLD)
+    case 10:
+      *((GFC_REAL_10*) dest) = gfc_strtold (buffer, &endptr);
+      break;
+#endif
+
+#if defined(HAVE_GFC_REAL_16)
+# if defined(GFC_REAL_16_IS_FLOAT128)
+    case 16:
+      *((GFC_REAL_16*) dest) = __qmath_(strtoflt128) (buffer, &endptr);
+      break;
+# elif defined(HAVE_STRTOLD)
+    case 16:
+      *((GFC_REAL_16*) dest) = gfc_strtold (buffer, &endptr);
       break;
+# endif
+#endif
+
     default:
-      internal_error ("Unsupported real kind during IO");
+      internal_error (&dtp->common, "Unsupported real kind during IO");
     }
 
-  if (errno != 0)
+  set_fpu_rounding_mode (old_round_mode);
+
+  if (buffer == endptr)
     {
-      generate_error (ERROR_READ_VALUE,
-                     "Range error during floating point read");
+      generate_error (&dtp->common, LIBERROR_READ_VALUE,
+                     "Error during floating point read");
+      next_record (dtp, 1);
       return 1;
     }
 
   return 0;
 }
 
+/* convert_infnan()-- Convert character INF/NAN representation to the
+   machine number.  Note: many architectures (e.g. IA-64, HP-PA) require
+   that the storage pointed to by the dest argument is properly aligned
+   for the type in question.  */
+
+int
+convert_infnan (st_parameter_dt *dtp, void *dest, const char *buffer,
+               int length)
+{
+  const char *s = buffer;
+  int is_inf, plus = 1;
+
+  if (*s == '+')
+    s++;
+  else if (*s == '-')
+    {
+      s++;
+      plus = 0;
+    }
+
+  is_inf = *s == 'i';
+
+  switch (length)
+    {
+    case 4:
+      if (is_inf)
+       *((GFC_REAL_4*) dest) = plus ? __builtin_inff () : -__builtin_inff ();
+      else
+       *((GFC_REAL_4*) dest) = plus ? __builtin_nanf ("") : -__builtin_nanf ("");
+      break;
+
+    case 8:
+      if (is_inf)
+       *((GFC_REAL_8*) dest) = plus ? __builtin_inf () : -__builtin_inf ();
+      else
+       *((GFC_REAL_8*) dest) = plus ? __builtin_nan ("") : -__builtin_nan ("");
+      break;
+
+#if defined(HAVE_GFC_REAL_10)
+    case 10:
+      if (is_inf)
+       *((GFC_REAL_10*) dest) = plus ? __builtin_infl () : -__builtin_infl ();
+      else
+       *((GFC_REAL_10*) dest) = plus ? __builtin_nanl ("") : -__builtin_nanl ("");
+      break;
+#endif
+
+#if defined(HAVE_GFC_REAL_16)
+# if defined(GFC_REAL_16_IS_FLOAT128)
+    case 16:
+      *((GFC_REAL_16*) dest) = __qmath_(strtoflt128) (buffer, NULL);
+      break;
+# else
+    case 16:
+      if (is_inf)
+       *((GFC_REAL_16*) dest) = plus ? __builtin_infl () : -__builtin_infl ();
+      else
+       *((GFC_REAL_16*) dest) = plus ? __builtin_nanl ("") : -__builtin_nanl ("");
+      break;
+# endif
+#endif
+
+    default:
+      internal_error (&dtp->common, "Unsupported real kind during IO");
+    }
+
+  return 0;
+}
+
 
 /* read_l()-- Read a logical value */
 
 void
-read_l (fnode * f, char *dest, int length)
+read_l (st_parameter_dt *dtp, const fnode *f, char *dest, int length)
 {
   char *p;
-  int w;
+  size_t w;
 
   w = f->u.w;
-  p = read_block (&w);
+
+  p = read_block_form (dtp, &w);
+
   if (p == NULL)
     return;
 
@@ -155,52 +299,279 @@ read_l (fnode * f, char *dest, int length)
     {
     case 't':
     case 'T':
-      set_integer (dest, 1, length);
+      set_integer (dest, (GFC_INTEGER_LARGEST) 1, length);
       break;
     case 'f':
     case 'F':
-      set_integer (dest, 0, length);
+      set_integer (dest, (GFC_INTEGER_LARGEST) 0, length);
       break;
     default:
     bad:
-      generate_error (ERROR_READ_VALUE, "Bad value on logical read");
+      generate_error (&dtp->common, LIBERROR_READ_VALUE,
+                     "Bad value on logical read");
+      next_record (dtp, 1);
       break;
     }
 }
 
 
-/* read_a()-- Read a character record.  This one is pretty easy. */
+static gfc_char4_t
+read_utf8 (st_parameter_dt *dtp, size_t *nbytes)
+{
+  static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x02, 0x01 };
+  static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+  size_t nb, nread;
+  gfc_char4_t c;
+  char *s;
+
+  *nbytes = 1;
+
+  s = read_block_form (dtp, nbytes);
+  if (s == NULL)
+    return 0;
+
+  /* If this is a short read, just return.  */
+  if (*nbytes == 0)
+    return 0;
+
+  c = (uchar) s[0];
+  if (c < 0x80)
+    return c;
+
+  /* The number of leading 1-bits in the first byte indicates how many
+     bytes follow.  */
+  for (nb = 2; nb < 7; nb++)
+    if ((c & ~masks[nb-1]) == patns[nb-1])
+      goto found;
+  goto invalid;
+       
+ found:
+  c = (c & masks[nb-1]);
+  nread = nb - 1;
+
+  s = read_block_form (dtp, &nread);
+  if (s == NULL)
+    return 0;
+  /* Decode the bytes read.  */
+  for (size_t i = 1; i < nb; i++)
+    {
+      gfc_char4_t n = *s++;
+
+      if ((n & 0xC0) != 0x80)
+       goto invalid;
+
+      c = ((c << 6) + (n & 0x3F));
+    }
+
+  /* Make sure the shortest possible encoding was used.  */
+  if (c <=      0x7F && nb > 1) goto invalid;
+  if (c <=     0x7FF && nb > 2) goto invalid;
+  if (c <=    0xFFFF && nb > 3) goto invalid;
+  if (c <=  0x1FFFFF && nb > 4) goto invalid;
+  if (c <= 0x3FFFFFF && nb > 5) goto invalid;
+
+  /* Make sure the character is valid.  */
+  if (c > 0x7FFFFFFF || (c >= 0xD800 && c <= 0xDFFF))
+    goto invalid;
+
+  return c;
+      
+ invalid:
+  generate_error (&dtp->common, LIBERROR_READ_VALUE, "Invalid UTF-8 encoding");
+  return (gfc_char4_t) '?';
+}
+
+
+static void
+read_utf8_char1 (st_parameter_dt *dtp, char *p, size_t len, size_t width)
+{
+  gfc_char4_t c;
+  char *dest;
+  size_t nbytes, j;
+
+  len = (width < len) ? len : width;
+
+  dest = (char *) p;
+
+  /* Proceed with decoding one character at a time.  */
+  for (j = 0; j < len; j++, dest++)
+    {
+      c = read_utf8 (dtp, &nbytes);
+
+      /* Check for a short read and if so, break out.  */
+      if (nbytes == 0)
+       break;
+
+      *dest = c > 255 ? '?' : (uchar) c;
+    }
+
+  /* If there was a short read, pad the remaining characters.  */
+  for (size_t i = j; i < len; i++)
+    *dest++ = ' ';
+  return;
+}
+
+static void
+read_default_char1 (st_parameter_dt *dtp, char *p, size_t len, size_t width)
+{
+  char *s;
+  size_t m;
+
+  s = read_block_form (dtp, &width);
+  
+  if (s == NULL)
+    return;
+  if (width > len)
+     s += (width - len);
+
+  m = (width > len) ? len : width;
+  memcpy (p, s, m);
+
+  if (len > width)
+    memset (p + m, ' ', len - width);
+}
+
+
+static void
+read_utf8_char4 (st_parameter_dt *dtp, void *p, size_t len, size_t width)
+{
+  gfc_char4_t *dest;
+  size_t nbytes, j;
+
+  len = (width < len) ? len : width;
+
+  dest = (gfc_char4_t *) p;
+
+  /* Proceed with decoding one character at a time.  */
+  for (j = 0; j < len; j++, dest++)
+    {
+      *dest = read_utf8 (dtp, &nbytes);
+
+      /* Check for a short read and if so, break out.  */
+      if (nbytes == 0)
+       break;
+    }
+
+  /* If there was a short read, pad the remaining characters.  */
+  for (size_t i = j; i < len; i++)
+    *dest++ = (gfc_char4_t) ' ';
+  return;
+}
+
+
+static void
+read_default_char4 (st_parameter_dt *dtp, char *p, size_t len, size_t width)
+{
+  size_t m, n;
+  gfc_char4_t *dest;
+
+  if (is_char4_unit(dtp))
+    {
+      gfc_char4_t *s4;
+
+      s4 = (gfc_char4_t *) read_block_form4 (dtp, &width);
+
+      if (s4 == NULL)
+       return;
+      if (width > len)
+        s4 += (width - len);
+
+      m = (width > len) ? len : width;
+
+      dest = (gfc_char4_t *) p;
+
+      for (n = 0; n < m; n++)
+       *dest++ = *s4++;
+
+      if (len > width)
+       {
+         for (n = 0; n < len - width; n++)
+           *dest++ = (gfc_char4_t) ' ';
+       }
+    }
+  else
+    {
+      char *s;
+
+      s = read_block_form (dtp, &width);
+
+      if (s == NULL)
+       return;
+      if (width > len)
+        s += (width - len);
+
+      m = (width > len) ? len : width;
+
+      dest = (gfc_char4_t *) p;
+
+      for (n = 0; n < m; n++, dest++, s++)
+       *dest = (unsigned char ) *s;
+
+      if (len > width)
+       {
+         for (n = 0; n < len - width; n++, dest++)
+           *dest = (unsigned char) ' ';
+       }
+    }
+}
+
+
+/* read_a()-- Read a character record into a KIND=1 character destination,
+   processing UTF-8 encoding if necessary.  */
 
 void
-read_a (fnode * f, char *p, int length)
+read_a (st_parameter_dt *dtp, const fnode *f, char *p, size_t length)
 {
-  char *source;
-  int w, m, n;
+  size_t w;
 
-  w = f->u.w;
-  if (w == -1) /* '(A)' edit descriptor  */
+  if (f->u.w == -1) /* '(A)' edit descriptor  */
     w = length;
+  else
+    w = f->u.w;
 
-  source = read_block (&w);
-  if (source == NULL)
-    return;
-  if (w > length)
-     source += (w - length);
+  /* Read in w characters, treating comma as not a separator.  */
+  dtp->u.p.sf_read_comma = 0;
 
-  m = (w > length) ? length : w;
-  memcpy (p, source, m);
+  if (dtp->u.p.current_unit->flags.encoding == ENCODING_UTF8)
+    read_utf8_char1 (dtp, p, length, w);
+  else
+    read_default_char1 (dtp, p, length, w);
 
-  n = length - w;
-  if (n > 0)
-    memset (p + m, ' ', n);
+  dtp->u.p.sf_read_comma =
+    dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA ? 0 : 1;
 }
 
 
+/* read_a_char4()-- Read a character record into a KIND=4 character destination,
+   processing UTF-8 encoding if necessary.  */
+
+void
+read_a_char4 (st_parameter_dt *dtp, const fnode *f, char *p, size_t length)
+{
+  size_t w;
+
+  if (f->u.w == -1) /* '(A)' edit descriptor  */
+    w = length;
+  else
+    w = f->u.w;
+
+  /* Read in w characters, treating comma as not a separator.  */
+  dtp->u.p.sf_read_comma = 0;
+
+  if (dtp->u.p.current_unit->flags.encoding == ENCODING_UTF8)
+    read_utf8_char4 (dtp, p, length, w);
+  else
+    read_default_char4 (dtp, p, length, w);
+  
+  dtp->u.p.sf_read_comma =
+    dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA ? 0 : 1;
+}
+
 /* eat_leading_spaces()-- Given a character pointer and a width,
* ignore the leading spaces.  */
  ignore the leading spaces.  */
 
 static char *
-eat_leading_spaces (int *width, char *p)
+eat_leading_spaces (size_t *width, char *p)
 {
   for (;;)
     {
@@ -216,7 +587,7 @@ eat_leading_spaces (int *width, char *p)
 
 
 static char
-next_char (char **p, int *w)
+next_char (st_parameter_dt *dtp, char **p, size_t *w)
 {
   char c, *q;
 
@@ -231,8 +602,8 @@ next_char (char **p, int *w)
 
   if (c != ' ')
     return c;
-  if (g.blank_status == BLANK_ZERO)
-    return '0';
+  if (dtp->u.p.blank_status != BLANK_UNSPECIFIED)
+    return ' ';  /* return a blank to signal a null */ 
 
   /* At this point, the rest of the field has to be trailing blanks */
 
@@ -249,32 +620,32 @@ next_char (char **p, int *w)
 
 
 /* read_decimal()-- Read a decimal integer value.  The values here are
* signed values. */
  signed values. */
 
 void
-read_decimal (fnode * f, char *dest, int length)
+read_decimal (st_parameter_dt *dtp, const fnode *f, char *dest, int length)
 {
-  unsigned value, maxv, maxv_10;
-  int v, w, negative;
+  GFC_UINTEGER_LARGEST value, maxv, maxv_10;
+  GFC_INTEGER_LARGEST v;
+  size_t w;
+  int negative;
   char c, *p;
 
   w = f->u.w;
-  p = read_block (&w);
+
+  p = read_block_form (dtp, &w);
+
   if (p == NULL)
     return;
 
   p = eat_leading_spaces (&w, p);
   if (w == 0)
     {
-      set_integer (dest, 0, length);
+      set_integer (dest, (GFC_INTEGER_LARGEST) 0, length);
       return;
     }
 
-  maxv = max_value (length, 1);
-  maxv_10 = maxv / 10;
-
   negative = 0;
-  value = 0;
 
   switch (*p)
     {
@@ -292,15 +663,32 @@ read_decimal (fnode * f, char *dest, int length)
       break;
     }
 
+  maxv = si_max (length);
+  if (negative)
+    maxv++;
+  maxv_10 = maxv / 10;
+
   /* At this point we have a digit-string */
   value = 0;
 
   for (;;)
     {
-      c = next_char (&p, &w);
+      c = next_char (dtp, &p, &w);
       if (c == '\0')
        break;
-
+       
+      if (c == ' ')
+        {
+         if (dtp->u.p.blank_status == BLANK_NULL)
+           {
+             /* Skip spaces.  */
+             for ( ; w > 0; p++, w--)
+               if (*p != ' ') break; 
+             continue;
+           }
+         if (dtp->u.p.blank_status == BLANK_ZERO) c = '0';
+        }
+        
       if (c < '0' || c > '9')
        goto bad;
 
@@ -315,49 +703,59 @@ read_decimal (fnode * f, char *dest, int length)
       value += c;
     }
 
-  v = (signed int) value;
   if (negative)
-    v = -v;
+    v = -value;
+  else
+    v = value;
 
   set_integer (dest, v, length);
   return;
 
  bad:
-  generate_error (ERROR_READ_VALUE, "Bad value during integer read");
+  generate_error (&dtp->common, LIBERROR_READ_VALUE,
+                 "Bad value during integer read");
+  next_record (dtp, 1);
   return;
 
  overflow:
-  generate_error (ERROR_READ_OVERFLOW,
+  generate_error (&dtp->common, LIBERROR_READ_OVERFLOW,
                  "Value overflowed during integer read");
-  return;
+  next_record (dtp, 1);
+
 }
 
 
 /* read_radix()-- This function reads values for non-decimal radixes.
* The difference here is that we treat the values here as unsigned
* values for the purposes of overflow.  If minus sign is present and
* the top bit is set, the value will be incorrect. */
  The difference here is that we treat the values here as unsigned
  values for the purposes of overflow.  If minus sign is present and
  the top bit is set, the value will be incorrect. */
 
 void
-read_radix (fnode * f, char *dest, int length, int radix)
+read_radix (st_parameter_dt *dtp, const fnode *f, char *dest, int length,
+           int radix)
 {
-  unsigned value, maxv, maxv_r;
-  int v, w, negative;
+  GFC_UINTEGER_LARGEST value, maxv, maxv_r;
+  GFC_INTEGER_LARGEST v;
+  size_t w;
+  int negative;
   char c, *p;
 
   w = f->u.w;
-  p = read_block (&w);
+
+  p = read_block_form (dtp, &w);
+
   if (p == NULL)
     return;
 
   p = eat_leading_spaces (&w, p);
   if (w == 0)
     {
-      set_integer (dest, 0, length);
+      set_integer (dest, (GFC_INTEGER_LARGEST) 0, length);
       return;
     }
 
-  maxv = max_value (length, 0);
+  /* Maximum unsigned value, assuming two's complement.  */
+  maxv = 2 * si_max (length) + 1;
   maxv_r = maxv / radix;
 
   negative = 0;
@@ -384,9 +782,14 @@ read_radix (fnode * f, char *dest, int length, int radix)
 
   for (;;)
     {
-      c = next_char (&p, &w);
+      c = next_char (dtp, &p, &w);
       if (c == '\0')
        break;
+      if (c == ' ')
+        {
+         if (dtp->u.p.blank_status == BLANK_NULL) continue;
+         if (dtp->u.p.blank_status == BLANK_ZERO) c = '0';
+        }
 
       switch (radix)
        {
@@ -451,7 +854,7 @@ read_radix (fnode * f, char *dest, int length, int radix)
       value += c;
     }
 
-  v = (signed int) value;
+  v = value;
   if (negative)
     v = -v;
 
@@ -459,13 +862,16 @@ read_radix (fnode * f, char *dest, int length, int radix)
   return;
 
  bad:
-  generate_error (ERROR_READ_VALUE, "Bad value during integer read");
+  generate_error (&dtp->common, LIBERROR_READ_VALUE,
+                 "Bad value during integer read");
+  next_record (dtp, 1);
   return;
 
  overflow:
-  generate_error (ERROR_READ_OVERFLOW,
+  generate_error (&dtp->common, LIBERROR_READ_OVERFLOW,
                  "Value overflowed during integer read");
-  return;
+  next_record (dtp, 1);
+
 }
 
 
@@ -476,77 +882,155 @@ read_radix (fnode * f, char *dest, int length, int radix)
    the input.  */
 
 void
-read_f (fnode * f, char *dest, int length)
+read_f (st_parameter_dt *dtp, const fnode *f, char *dest, int length)
 {
-  int w, seen_dp, exponent;
-  int exponent_sign, val_sign;
-  int ndigits;
-  int edigits;
-  int i;
-  char *p, *buffer;
-  char *digits;
-
-  val_sign = 1;
+#define READF_TMP 50
+  char tmp[READF_TMP];
+  size_t buf_size = 0;
+  size_t w;
+  int seen_dp, exponent;
+  int exponent_sign;
+  const char *p;
+  char *buffer;
+  char *out;
+  int seen_int_digit; /* Seen a digit before the decimal point?  */
+  int seen_dec_digit; /* Seen a digit after the decimal point?  */
+
   seen_dp = 0;
+  seen_int_digit = 0;
+  seen_dec_digit = 0;
+  exponent_sign = 1;
+  exponent = 0;
   w = f->u.w;
-  p = read_block (&w);
+  buffer = tmp;
+
+  /* Read in the next block.  */
+  p = read_block_form (dtp, &w);
   if (p == NULL)
     return;
-
-  p = eat_leading_spaces (&w, p);
+  p = eat_leading_spaces (&w, (char*) p);
   if (w == 0)
-    {
-      switch (length)
-       {
-       case 4:
-         *((float *) dest) = 0.0f;
-         break;
+    goto zero;
 
-       case 8:
-         *((double *) dest) = 0.0;
-         break;
+  /* In this buffer we're going to re-format the number cleanly to be parsed
+     by convert_real in the end; this assures we're using strtod from the
+     C library for parsing and thus probably get the best accuracy possible.
+     This process may add a '+0.0' in front of the number as well as change the
+     exponent because of an implicit decimal point or the like.  Thus allocating
+     strlen ("+0.0e-1000") == 10 characters plus one for NUL more than the
+     original buffer had should be enough.  */
+  buf_size = w + 11;
+  if (buf_size > READF_TMP)
+    buffer = xmalloc (buf_size);
 
-       default:
-         internal_error ("Unsupported real kind during IO");
-       }
-
-      return;
-    }
+  out = buffer;
 
   /* Optional sign */
-
   if (*p == '-' || *p == '+')
     {
       if (*p == '-')
-        val_sign = -1;
-      p++;
-
-      if (--w == 0)
-       goto bad_float;
+       *(out++) = '-';
+      ++p;
+      --w;
     }
 
-  exponent_sign = 1;
+  p = eat_leading_spaces (&w, (char*) p);
+  if (w == 0)
+    goto zero;
 
-  /* A digit (or a '.') is required at this point */
+  /* Check for Infinity or NaN.  */    
+  if (unlikely ((w >= 3 && (*p == 'i' || *p == 'I' || *p == 'n' || *p == 'N'))))
+    {
+      int seen_paren = 0;
+      char *save = out;
+
+      /* Scan through the buffer keeping track of spaces and parenthesis. We
+        null terminate the string as soon as we see a left paren or if we are
+        BLANK_NULL mode.  Leading spaces have already been skipped above,
+        trailing spaces are ignored by converting to '\0'. A space
+        between "NaN" and the optional perenthesis is not permitted.  */
+      while (w > 0)
+       {
+         *out = tolower (*p);
+         switch (*p)
+           {
+           case ' ':
+             if (dtp->u.p.blank_status == BLANK_ZERO)
+               {
+                 *out = '0';
+                 break;
+               }
+             *out = '\0';
+             if (seen_paren == 1)
+               goto bad_float;
+             break;
+           case '(':
+             seen_paren++;
+             *out = '\0';
+             break;
+           case ')':
+             if (seen_paren++ != 1)
+               goto bad_float;
+             break;
+           default:
+             if (!isalnum (*out))
+               goto bad_float;
+           }
+         --w;
+         ++p;
+         ++out;
+       }
+        
+      *out = '\0';
+      
+      if (seen_paren != 0 && seen_paren != 2)
+       goto bad_float;
 
-  if (!isdigit (*p) && *p != '.')
-    goto bad_float;
+      if ((strcmp (save, "inf") == 0) || (strcmp (save, "infinity") == 0))
+       {
+          if (seen_paren)
+            goto bad_float;
+       }
+      else if (strcmp (save, "nan") != 0)
+       goto bad_float;
 
-  /* Remember the position of the first digit.  */
-  digits = p;
-  ndigits = 0;
+      convert_infnan (dtp, dest, buffer, length);
+      if (buf_size > READF_TMP)
+       free (buffer);
+      return;
+    }
 
-  /* Scan through the string to find the exponent.  */
+  /* Process the mantissa string.  */
   while (w > 0)
     {
       switch (*p)
        {
+       case ',':
+         if (dtp->u.p.current_unit->decimal_status != DECIMAL_COMMA)
+           goto bad_float;
+         /* Fall through.  */
        case '.':
          if (seen_dp)
            goto bad_float;
+         if (!seen_int_digit)
+           *(out++) = '0';
+         *(out++) = '.';
          seen_dp = 1;
-         /* Fall through */
+         break;
 
+       case ' ':
+         if (dtp->u.p.blank_status == BLANK_ZERO)
+           {
+             *(out++) = '0';
+             goto found_digit;
+           }
+         else if (dtp->u.p.blank_status == BLANK_NULL)
+           break;
+         else
+           /* TODO: Should we check instead that there are only trailing
+              blanks here, as is done below for exponents?  */
+           goto done;
+         /* Fall through.  */
        case '0':
        case '1':
        case '2':
@@ -557,164 +1041,263 @@ read_f (fnode * f, char *dest, int length)
        case '7':
        case '8':
        case '9':
-       case ' ':
-         ndigits++;
-         *p++;
-         w--;
+         *(out++) = *p;
+found_digit:
+         if (!seen_dp)
+           seen_int_digit = 1;
+         else
+           seen_dec_digit = 1;
          break;
 
        case '-':
-         exponent_sign = -1;
-         /* Fall through */
-
        case '+':
-         p++;
-         w--;
-         goto exp2;
+         goto exponent;
 
-       case 'd':
        case 'e':
-       case 'D':
        case 'E':
-         p++;
-         w--;
-         goto exp1;
+       case 'd':
+       case 'D':
+       case 'q':
+       case 'Q':
+         ++p;
+         --w;
+         goto exponent;
 
        default:
          goto bad_float;
        }
-    }
 
-  /* No exponent has been seen, so we use the current scale factor */
-  exponent = -g.scale_factor;
-  goto done;
-
- bad_float:
-  generate_error (ERROR_READ_VALUE, "Bad value during floating point read");
-  if (buffer != scratch)
-     free_mem (buffer);
-  return;
-
-  /* At this point the start of an exponent has been found */
- exp1:
-  while (w > 0 && *p == ' ')
-    {
-      w--;
-      p++;
+      ++p;
+      --w;
     }
+  
+  /* No exponent has been seen, so we use the current scale factor.  */
+  exponent = - dtp->u.p.scale_factor;
+  goto done;
 
-  switch (*p)
+  /* At this point the start of an exponent has been found.  */
+exponent:
+  p = eat_leading_spaces (&w, (char*) p);
+  if (*p == '-' || *p == '+')
     {
-    case '-':
-      exponent_sign = -1;
-      /* Fall through */
-
-    case '+':
-      p++;
-      w--;
-      break;
+      if (*p == '-')
+       exponent_sign = -1;
+      ++p;
+      --w;
     }
 
-  if (w == 0)
-    goto bad_float;
-
   /* At this point a digit string is required.  We calculate the value
      of the exponent in order to take account of the scale factor and
-     the d parameter before explict conversion takes place. */
- exp2:
-  if (!isdigit (*p))
-    goto bad_float;
-
-  exponent = *p - '0';
-  p++;
-  w--;
+     the d parameter before explict conversion takes place.  */
 
-  while (w > 0 && isdigit (*p))
+  if (w == 0)
     {
-      exponent = 10 * exponent + *p - '0';
-      p++;
-      w--;
+      /* Extension: allow default exponent of 0 when omitted.  */
+      if (dtp->common.flags & IOPARM_DT_DEFAULT_EXP)
+       goto done;
+      else
+       goto bad_float;
     }
 
-  /* Only allow trailing blanks */
-
-  while (w > 0)
+  if (dtp->u.p.blank_status == BLANK_UNSPECIFIED)
     {
-      if (*p != ' ')
-       goto bad_float;
-      p++;
-      w--;
+      while (w > 0 && isdigit (*p))
+       {
+         exponent *= 10;
+         exponent += *p - '0';
+         ++p;
+         --w;
+       }
+       
+      /* Only allow trailing blanks.  */
+      while (w > 0)
+       {
+         if (*p != ' ')
+           goto bad_float;
+         ++p;
+         --w;
+       }
+    }    
+  else  /* BZ or BN status is enabled.  */
+    {
+      while (w > 0)
+       {
+         if (*p == ' ')
+           {
+             if (dtp->u.p.blank_status == BLANK_ZERO)
+               exponent *= 10;
+             else
+               assert (dtp->u.p.blank_status == BLANK_NULL);
+           }
+         else if (!isdigit (*p))
+           goto bad_float;
+         else
+           {
+             exponent *= 10;
+             exponent += *p - '0';
+           }
+
+         ++p;
+         --w;
+       }
     }
 
-  exponent = exponent * exponent_sign;
+  exponent *= exponent_sign;
 
- done:
+done:
   /* Use the precision specified in the format if no decimal point has been
      seen.  */
   if (!seen_dp)
     exponent -= f->u.real.d;
 
-  if (exponent > 0)
+  /* Output a trailing '0' after decimal point if not yet found.  */
+  if (seen_dp && !seen_dec_digit)
+    *(out++) = '0';
+  /* Handle input of style "E+NN" by inserting a 0 for the
+     significand.  */
+  else if (!seen_int_digit && !seen_dec_digit)
     {
-      edigits = 2;
-      i = exponent;
-    }
-  else
-    {
-      edigits = 3;
-      i = -exponent;
+      notify_std (&dtp->common, GFC_STD_LEGACY, 
+                 "REAL input of style 'E+NN'");
+      *(out++) = '0';
     }
 
-  while (i >= 10)
+  /* Print out the exponent to finish the reformatted number.  Maximum 4
+     digits for the exponent.  */
+  if (exponent != 0)
     {
-      i /= 10;
-      edigits++;
-    }
+      int dig;
 
-  i = ndigits + edigits + 1;
-  if (val_sign < 0)
-    i++;
+      *(out++) = 'e';
+      if (exponent < 0)
+       {
+         *(out++) = '-';
+         exponent = - exponent;
+       }
 
-  if (i < SCRATCH_SIZE) 
-    buffer = scratch;
-  else
-    buffer = get_mem (i);
+      if (exponent >= 10000)
+       goto bad_float;
 
-  /* Reformat the string into a temporary buffer.  As we're using atof it's
-     easiest to just leave the dcimal point in place.  */
-  p = buffer;
-  if (val_sign < 0)
-    *(p++) = '-';
-  for (; ndigits > 0; ndigits--)
-    {
-      if (*digits == ' ' && g.blank_status == BLANK_ZERO)
-       *p = '0';
-      else
-       *p = *digits;
-      p++;
-      digits++;
+      for (dig = 3; dig >= 0; --dig)
+       {
+         out[dig] = (char) ('0' + exponent % 10);
+         exponent /= 10;
+       }
+      out += 4;
     }
-  *(p++) = 'e';
-  sprintf (p, "%d", exponent);
+  *(out++) = '\0';
 
   /* Do the actual conversion.  */
-  convert_real (dest, buffer, length);
+  convert_real (dtp, dest, buffer, length);
+  if (buf_size > READF_TMP)
+    free (buffer);
+  return;
+
+  /* The value read is zero.  */
+zero:
+  switch (length)
+    {
+      case 4:
+       *((GFC_REAL_4 *) dest) = 0.0;
+       break;
+
+      case 8:
+       *((GFC_REAL_8 *) dest) = 0.0;
+       break;
+
+#ifdef HAVE_GFC_REAL_10
+      case 10:
+       *((GFC_REAL_10 *) dest) = 0.0;
+       break;
+#endif
+
+#ifdef HAVE_GFC_REAL_16
+      case 16:
+       *((GFC_REAL_16 *) dest) = 0.0;
+       break;
+#endif
 
-  if (buffer != scratch)
-     free_mem (buffer);
+      default:
+       internal_error (&dtp->common, "Unsupported real kind during IO");
+    }
+  return;
 
+bad_float:
+  if (buf_size > READF_TMP)
+    free (buffer);
+  generate_error (&dtp->common, LIBERROR_READ_VALUE,
+                 "Bad value during floating point read");
+  next_record (dtp, 1);
   return;
 }
 
 
 /* read_x()-- Deal with the X/TR descriptor.  We just read some data
* and never look at it. */
  and never look at it. */
 
 void
-read_x (fnode * f)
+read_x (st_parameter_dt *dtp, size_t n)
 {
-  int n;
+  size_t length;
+  int q, q2;
+
+  if ((dtp->u.p.current_unit->pad_status == PAD_NO || is_internal_unit (dtp))
+      && dtp->u.p.current_unit->bytes_left < (gfc_offset) n)
+    n = dtp->u.p.current_unit->bytes_left;
+    
+  if (n == 0)
+    return;
+
+  length = n;
+
+  if (is_internal_unit (dtp))
+    {
+      mem_alloc_r (dtp->u.p.current_unit->s, &length);
+      if (unlikely (length < n))
+       n = length;
+      goto done;
+    }
+
+  if (dtp->u.p.sf_seen_eor)
+    return;
 
-  n = f->u.n;
-  read_block (&n);
+  n = 0;
+  while (n < length)
+    {
+      q = fbuf_getc (dtp->u.p.current_unit);
+      if (q == EOF)
+       break;
+      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;
+
+         /* If we see an EOR during non-advancing I/O, we need to skip
+            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 */
+           {
+             /* See if there is an LF.  */
+             q2 = fbuf_getc (dtp->u.p.current_unit);
+             if (q2 == '\n')
+               dtp->u.p.sf_seen_eor = 2;
+             else if (q2 != EOF) /* Oops, seek back.  */
+               fbuf_seek (dtp->u.p.current_unit, -1, SEEK_CUR);
+           }
+         goto done;
+       }
+      n++;
+    } 
+
+ done:
+  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;
+  dtp->u.p.current_unit->bytes_left -= n;
+  dtp->u.p.current_unit->strm_pos += (gfc_offset) n;
 }
+