re PR fortran/66082 (memory leak with automatic array dummy argument with derived...
[gcc.git] / libgfortran / io / format.c
index c7188a8a0d6d72bc26e6a7e242e284d96c8a6166..2068af7eb849397fffded4403b2348227c6114be 100644 (file)
@@ -1,9 +1,8 @@
-/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+/* Copyright (C) 2002-2015 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
@@ -32,30 +31,8 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 #include "format.h"
 #include <ctype.h>
 #include <string.h>
-#include <stdbool.h>
+#include <stdlib.h>
 
-#define FARRAY_SIZE 64
-
-typedef struct fnode_array
-{
-  struct fnode_array *next;
-  fnode array[FARRAY_SIZE];
-}
-fnode_array;
-
-typedef struct format_data
-{
-  char *format_string, *string;
-  const char *error;
-  char error_element;
-  format_token saved_token;
-  int value, format_string_len, reversion_ok;
-  fnode *avail;
-  const fnode *saved_format;
-  fnode_array *last;
-  fnode_array array;
-}
-format_data;
 
 static const fnode colon_node = { FMT_COLON, 0, NULL, NULL, {{ 0, 0, 0 }}, 0,
                                  NULL };
@@ -90,7 +67,7 @@ free_format_hash_table (gfc_unit *u)
       if (u->format_hash_table[i].hashed_fmt != NULL)
        {
          free_format_data (u->format_hash_table[i].hashed_fmt);
-         free_mem (u->format_hash_table[i].key);
+         free (u->format_hash_table[i].key);
        }
       u->format_hash_table[i].key = NULL;
       u->format_hash_table[i].key_len = 0;      
@@ -137,8 +114,8 @@ reset_fnode_counters (st_parameter_dt *dtp)
 
 /* A simple hashing function to generate an index into the hash table.  */
 
-static inline
-uint32_t format_hash (st_parameter_dt *dtp)
+static uint32_t
+format_hash (st_parameter_dt *dtp)
 {
   char *key;
   gfc_charlen_type key_len;
@@ -170,10 +147,8 @@ save_parsed_format (st_parameter_dt *dtp)
     free_format_data (u->format_hash_table[hash].hashed_fmt);
   u->format_hash_table[hash].hashed_fmt = NULL;
 
-  if (u->format_hash_table[hash].key != NULL)
-    free_mem (u->format_hash_table[hash].key);
-  u->format_hash_table[hash].key = get_mem (dtp->format_len);
-  memcpy (u->format_hash_table[hash].key, dtp->format, dtp->format_len);
+  free (u->format_hash_table[hash].key);
+  u->format_hash_table[hash].key = dtp->format;
 
   u->format_hash_table[hash].key_len = dtp->format_len;
   u->format_hash_table[hash].hashed_fmt = dtp->u.p.fmt;
@@ -245,7 +220,7 @@ get_fnode (format_data *fmt, fnode **head, fnode **tail, format_token t)
 
   if (fmt->avail == &fmt->last->array[FARRAY_SIZE])
     {
-      fmt->last->next = get_mem (sizeof (fnode_array));
+      fmt->last->next = xmalloc (sizeof (fnode_array));
       fmt->last = fmt->last->next;
       fmt->last->next = NULL;
       fmt->avail = &fmt->last->array[0];
@@ -268,6 +243,18 @@ get_fnode (format_data *fmt, fnode **head, fnode **tail, format_token t)
 }
 
 
+/* free_format()-- Free allocated format string.  */
+void
+free_format (st_parameter_dt *dtp)
+{
+  if ((dtp->common.flags & IOPARM_DT_HAS_FORMAT) && dtp->format)
+    {
+      free (dtp->format);
+      dtp->format = NULL;
+    }
+}
+
+
 /* free_format_data()-- Free all allocated format data.  */
 
 void
@@ -282,10 +269,10 @@ free_format_data (format_data *fmt)
   for (fa = fmt->array.next; fa; fa = fa_next)
     {
       fa_next = fa->next;
-      free_mem (fa);
+      free (fa);
     }
 
-  free_mem (fmt);
+  free (fmt);
   fmt = NULL;
 }
 
@@ -611,16 +598,15 @@ format_lex (format_data *fmt)
  * parenthesis node which contains the rest of the list. */
 
 static fnode *
-parse_format_list (st_parameter_dt *dtp, bool *save_ok)
+parse_format_list (st_parameter_dt *dtp, bool *seen_dd)
 {
   fnode *head, *tail;
   format_token t, u, t2;
   int repeat;
   format_data *fmt = dtp->u.p.fmt;
-  bool saveit;
+  bool seen_data_desc = false;
 
   head = tail = NULL;
-  saveit = *save_ok;
 
   /* Get the next format item */
  format_item:
@@ -637,10 +623,15 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
        }
       get_fnode (fmt, &head, &tail, FMT_LPAREN);
       tail->repeat = -2;  /* Signifies unlimited format.  */
-      tail->u.child = parse_format_list (dtp, &saveit);
+      tail->u.child = parse_format_list (dtp, &seen_data_desc);
+      *seen_dd = seen_data_desc;
       if (fmt->error != NULL)
        goto finished;
-
+      if (!seen_data_desc)
+       {
+         fmt->error = "'*' requires at least one associated data descriptor";
+         goto finished;
+       }
       goto between_desc;
 
     case FMT_POSINT:
@@ -652,7 +643,8 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
        case FMT_LPAREN:
          get_fnode (fmt, &head, &tail, FMT_LPAREN);
          tail->repeat = repeat;
-         tail->u.child = parse_format_list (dtp, &saveit);
+         tail->u.child = parse_format_list (dtp, &seen_data_desc);
+         *seen_dd = seen_data_desc;
          if (fmt->error != NULL)
            goto finished;
 
@@ -679,7 +671,8 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
     case FMT_LPAREN:
       get_fnode (fmt, &head, &tail, FMT_LPAREN);
       tail->repeat = 1;
-      tail->u.child = parse_format_list (dtp, &saveit);
+      tail->u.child = parse_format_list (dtp, &seen_data_desc);
+      *seen_dd = seen_data_desc;
       if (fmt->error != NULL)
        goto finished;
 
@@ -742,8 +735,6 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
       goto between_desc;
 
     case FMT_STRING:
-      /* TODO: Find out why it is necessary to turn off format caching.  */
-      saveit = false;
       get_fnode (fmt, &head, &tail, FMT_STRING);
       tail->u.string.p = fmt->string;
       tail->u.string.length = fmt->value;
@@ -820,6 +811,7 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
     case FMT_F:
     case FMT_G:
       repeat = 1;
+      *seen_dd = true;
       goto data_desc;
 
     case FMT_H:
@@ -860,6 +852,7 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
   switch (t)
     {
     case FMT_L:
+      *seen_dd = true;
       t = format_lex (fmt);
       if (t != FMT_POSINT)
        {
@@ -882,6 +875,7 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
       break;
 
     case FMT_A:
+      *seen_dd = true;
       t = format_lex (fmt);
       if (t == FMT_ZERO)
        {
@@ -906,12 +900,14 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
     case FMT_G:
     case FMT_EN:
     case FMT_ES:
+      *seen_dd = true;
       get_fnode (fmt, &head, &tail, t);
       tail->repeat = repeat;
 
       u = format_lex (fmt);
       if (t == FMT_G && u == FMT_ZERO)
        {
+         *seen_dd = true;
          if (notification_std (GFC_STD_F2008) == NOTIFICATION_ERROR
              || dtp->u.p.mode == READING)
            {
@@ -937,6 +933,7 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
        }
       if (t == FMT_F && dtp->u.p.mode == WRITING)
        {
+         *seen_dd = true;
          if (u != FMT_POSINT && u != FMT_ZERO)
            {
              fmt->error = nonneg_required;
@@ -978,8 +975,10 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
       tail->u.real.e = -1;
 
       if (t2 == FMT_D || t2 == FMT_F)
-       break;
-
+       {
+         *seen_dd = true;
+         break;
+       }
 
       /* Look for optional exponent */
       t = format_lex (fmt);
@@ -1020,6 +1019,7 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
     case FMT_B:
     case FMT_O:
     case FMT_Z:
+      *seen_dd = true;
       get_fnode (fmt, &head, &tail, t);
       tail->repeat = repeat;
 
@@ -1122,8 +1122,6 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
 
  finished:
 
-  *save_ok = saveit;
-  
   return head;
 }
 
@@ -1140,24 +1138,26 @@ parse_format_list (st_parameter_dt *dtp, bool *save_ok)
 void
 format_error (st_parameter_dt *dtp, const fnode *f, const char *message)
 {
-  int width, i, j, offset;
-  char *p, buffer[300];
+  int width, i, offset;
+#define BUFLEN 300
+  char *p, buffer[BUFLEN];
   format_data *fmt = dtp->u.p.fmt;
 
   if (f != NULL)
-    fmt->format_string = f->source;
+    p = f->source;
+  else                /* This should not happen.  */
+    p = dtp->format;
 
   if (message == unexpected_element)
-    sprintf (buffer, message, fmt->error_element);
+    snprintf (buffer, BUFLEN, message, fmt->error_element);
   else
-    sprintf (buffer, "%s\n", message);
-
-  j = fmt->format_string - dtp->format;
+    snprintf (buffer, BUFLEN, "%s\n", message);
 
-  offset = (j > 60) ? j - 40 : 0;
+  /* Get the offset into the format string where the error occurred.  */
+  offset = dtp->format_len - (fmt->reversion_ok ?
+                             (int) strlen(p) : fmt->format_string_len);
 
-  j -= offset;
-  width = dtp->format_len - offset;
+  width = dtp->format_len;
 
   if (width > 80)
     width = 80;
@@ -1166,19 +1166,40 @@ format_error (st_parameter_dt *dtp, const fnode *f, const char *message)
 
   p = strchr (buffer, '\0');
 
-  memcpy (p, dtp->format + offset, width);
+  if (dtp->format)
+    memcpy (p, dtp->format, width);
 
   p += width;
   *p++ = '\n';
 
   /* Show where the problem is */
 
-  for (i = 1; i < j; i++)
+  for (i = 1; i < offset; i++)
     *p++ = ' ';
 
   *p++ = '^';
   *p = '\0';
 
+  /* Cleanup any left over memory allocations before calling generate
+     error.  */
+  if (is_internal_unit (dtp))
+    {
+      if (dtp->format != NULL)
+       {
+         free (dtp->format);
+         dtp->format = NULL;
+       }
+
+      /* Leave these alone if IOSTAT was given because execution will
+        return from generate error in those cases.  */
+      if (!(dtp->common.flags & IOPARM_HAS_IOSTAT))
+       {
+         free (dtp->u.p.fmt);
+         free_format_hash_table (dtp->u.p.current_unit);
+         free_internal_unit (dtp);
+       }
+    }
+
   generate_error (&dtp->common, LIBERROR_FORMAT, buffer);
 }
 
@@ -1216,7 +1237,7 @@ void
 parse_format (st_parameter_dt *dtp)
 {
   format_data *fmt;
-  bool format_cache_ok;
+  bool format_cache_ok, seen_data_desc = false;
 
   /* Don't cache for internal units and set an arbitrary limit on the size of
      format strings we will cache.  (Avoids memory issues.)  */
@@ -1239,7 +1260,10 @@ parse_format (st_parameter_dt *dtp)
 
   /* Not found so proceed as follows.  */
 
-  dtp->u.p.fmt = fmt = get_mem (sizeof (format_data));
+  char *fmt_string = fc_strdup_notrim (dtp->format, dtp->format_len);
+  dtp->format = fmt_string;
+
+  dtp->u.p.fmt = fmt = xmalloc (sizeof (format_data));
   fmt->format_string = dtp->format;
   fmt->format_string_len = dtp->format_len;
 
@@ -1265,21 +1289,17 @@ parse_format (st_parameter_dt *dtp)
   fmt->avail++;
 
   if (format_lex (fmt) == FMT_LPAREN)
-    fmt->array.array[0].u.child = parse_format_list (dtp, &format_cache_ok);
+    fmt->array.array[0].u.child = parse_format_list (dtp, &seen_data_desc);
   else
     fmt->error = "Missing initial left parenthesis in format";
 
-  if (fmt->error)
-    {
-      format_error (dtp, NULL, fmt->error);
-      free_format_hash_table (dtp->u.p.current_unit);
-      return;
-    }
-
   if (format_cache_ok)
     save_parsed_format (dtp);
   else
     dtp->u.p.format_not_saved = 1;
+
+  if (fmt->error)
+    format_error (dtp, NULL, fmt->error);
 }