-/* Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2014 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.
-In addition to the permissions in the GNU General Public License, the
-Free Software Foundation gives you unlimited permission to link the
-compiled version of this file into combinations with other programs,
-and to distribute those combinations without any restriction coming
-from the use of this file. (The General Public License restrictions
-do apply in other respects; for example, they cover modification of
-the file, and distribution when not linked into a combine
-executable.)
-
Libgfortran is distributed in the hope that it will be useful,
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, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, 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 <stdlib.h>
#include <string.h>
-#include "libgfortran.h"
-#include "io.h"
/* IO locking rules:
/* Subroutines related to units */
+/* Unit number to be assigned when NEWUNIT is used in an OPEN statement. */
+#define GFC_FIRST_NEWUNIT -10
+static GFC_INTEGER_4 next_available_newunit = GFC_FIRST_NEWUNIT;
#define CACHE_SIZE 3
-static gfc_unit internal_unit, *unit_cache[CACHE_SIZE];
+static gfc_unit *unit_cache[CACHE_SIZE];
gfc_offset max_offset;
gfc_unit *unit_root;
#ifdef __GTHREAD_MUTEX_INIT
__gthread_mutex_t unit_lock;
#endif
+/* We use these filenames for error reporting. */
+
+static char stdin_name[] = "stdin";
+static char stdout_name[] = "stdout";
+static char stderr_name[] = "stderr";
+
/* This implementation is based on Stefan Nilsson's article in the
* July 1997 Doctor Dobb's Journal, "Treaps in Java". */
}
-
static int
compare (int a, int b)
{
static gfc_unit *
insert_unit (int n)
{
- gfc_unit *u = get_mem (sizeof (gfc_unit));
- memset (u, '\0', sizeof (gfc_unit));
+ gfc_unit *u = xcalloc (1, sizeof (gfc_unit));
u->unit_number = n;
#ifdef __GTHREAD_MUTEX_INIT
{
}
+/* destroy_unit_mutex()-- Destroy the mutex and free memory of unit. */
+
+static void
+destroy_unit_mutex (gfc_unit * u)
+{
+ __gthread_mutex_destroy (&u->lock);
+ free (u);
+}
+
+
static gfc_unit *
delete_root (gfc_unit * t)
{
}
-/* find_unit()-- Given an integer, return a pointer to the unit
+/* get_external_unit()-- Given an integer, return a pointer to the unit
* structure. Returns NULL if the unit does not exist,
* otherwise returns a locked unit. */
static gfc_unit *
-find_unit_1 (int n, int do_create)
+get_external_unit (int n, int do_create)
{
gfc_unit *p;
int c, created = 0;
__gthread_mutex_lock (&unit_lock);
__gthread_mutex_unlock (&p->lock);
if (predec_waiting_locked (p) == 0)
- free_mem (p);
+ destroy_unit_mutex (p);
goto retry;
}
return p;
}
+
gfc_unit *
find_unit (int n)
{
- return find_unit_1 (n, 0);
+ return get_external_unit (n, 0);
}
+
gfc_unit *
find_or_create_unit (int n)
{
- return find_unit_1 (n, 1);
+ return get_external_unit (n, 1);
}
-/* get_unit()-- Returns the unit structure associated with the integer
- * unit or the internal file. */
-gfc_unit *
-get_unit (st_parameter_dt *dtp, int do_create)
+/* Helper function to check rank, stride, format string, and namelist.
+ This is used for optimization. You can't trim out blanks or shorten
+ the string if trailing spaces are significant. */
+static bool
+is_trim_ok (st_parameter_dt *dtp)
{
- if ((dtp->common.flags & IOPARM_DT_HAS_INTERNAL_UNIT) != 0)
+ /* Check rank and stride. */
+ if (dtp->internal_unit_desc)
+ return false;
+ /* Format strings can not have 'BZ' or '/'. */
+ if (dtp->common.flags & IOPARM_DT_HAS_FORMAT)
{
- __gthread_mutex_lock (&internal_unit.lock);
- internal_unit.recl = dtp->internal_unit_len;
- if (is_array_io (dtp))
+ char *p = dtp->format;
+ off_t i;
+ if (dtp->common.flags & IOPARM_DT_HAS_BLANK)
+ return false;
+ for (i = 0; i < dtp->format_len; i++)
{
- internal_unit.rank = GFC_DESCRIPTOR_RANK (dtp->internal_unit_desc);
- internal_unit.ls = (array_loop_spec *)
- get_mem (internal_unit.rank * sizeof (array_loop_spec));
- dtp->internal_unit_len *=
- init_loop_spec (dtp->internal_unit_desc, internal_unit.ls);
+ if (p[i] == '/') return false;
+ if (p[i] == 'b' || p[i] == 'B')
+ if (p[i+1] == 'z' || p[i+1] == 'Z')
+ return false;
}
+ }
+ if (dtp->u.p.ionml) /* A namelist. */
+ return false;
+ return true;
+}
+
- internal_unit.s =
- open_internal (dtp->internal_unit, dtp->internal_unit_len);
- internal_unit.bytes_left = internal_unit.recl;
- internal_unit.last_record=0;
- internal_unit.maxrec=0;
- internal_unit.current_record=0;
+gfc_unit *
+get_internal_unit (st_parameter_dt *dtp)
+{
+ gfc_unit * iunit;
+ gfc_offset start_record = 0;
- if (dtp->u.p.mode==WRITING && !is_array_io (dtp))
- empty_internal_buffer (internal_unit.s);
+ /* Allocate memory for a unit structure. */
- /* Set flags for the internal unit */
+ iunit = xcalloc (1, sizeof (gfc_unit));
+
+#ifdef __GTHREAD_MUTEX_INIT
+ {
+ __gthread_mutex_t tmp = __GTHREAD_MUTEX_INIT;
+ iunit->lock = tmp;
+ }
+#else
+ __GTHREAD_MUTEX_INIT_FUNCTION (&iunit->lock);
+#endif
+ __gthread_mutex_lock (&iunit->lock);
- internal_unit.flags.access = ACCESS_SEQUENTIAL;
- internal_unit.flags.action = ACTION_READWRITE;
- internal_unit.flags.form = FORM_FORMATTED;
- internal_unit.flags.delim = DELIM_NONE;
- internal_unit.flags.pad = PAD_YES;
+ iunit->recl = dtp->internal_unit_len;
- return &internal_unit;
+ /* For internal units we set the unit number to -1.
+ Otherwise internal units can be mistaken for a pre-connected unit or
+ some other file I/O unit. */
+ iunit->unit_number = -1;
+
+ /* As an optimization, adjust the unit record length to not
+ include trailing blanks. This will not work under certain conditions
+ where trailing blanks have significance. */
+ if (dtp->u.p.mode == READING && is_trim_ok (dtp))
+ {
+ int len;
+ if (dtp->common.unit == 0)
+ len = string_len_trim (dtp->internal_unit_len,
+ dtp->internal_unit);
+ else
+ len = string_len_trim_char4 (dtp->internal_unit_len,
+ (const gfc_char4_t*) dtp->internal_unit);
+ dtp->internal_unit_len = len;
+ iunit->recl = dtp->internal_unit_len;
}
- /* Has to be an external unit */
+ /* Set up the looping specification from the array descriptor, if any. */
- return find_unit_1 (dtp->common.unit, do_create);
-}
+ if (is_array_io (dtp))
+ {
+ iunit->rank = GFC_DESCRIPTOR_RANK (dtp->internal_unit_desc);
+ iunit->ls = (array_loop_spec *)
+ xmallocarray (iunit->rank, sizeof (array_loop_spec));
+ dtp->internal_unit_len *=
+ init_loop_spec (dtp->internal_unit_desc, iunit->ls, &start_record);
+ start_record *= iunit->recl;
+ }
-/* is_internal_unit()-- Determine if the current unit is internal or not */
+ /* Set initial values for unit parameters. */
+ if (dtp->common.unit)
+ {
+ iunit->s = open_internal4 (dtp->internal_unit - start_record,
+ dtp->internal_unit_len, -start_record);
+ fbuf_init (iunit, 256);
+ }
+ else
+ iunit->s = open_internal (dtp->internal_unit - start_record,
+ dtp->internal_unit_len, -start_record);
+
+ iunit->bytes_left = iunit->recl;
+ iunit->last_record=0;
+ iunit->maxrec=0;
+ iunit->current_record=0;
+ iunit->read_bad = 0;
+ iunit->endfile = NO_ENDFILE;
+
+ /* Set flags for the internal unit. */
+
+ iunit->flags.access = ACCESS_SEQUENTIAL;
+ iunit->flags.action = ACTION_READWRITE;
+ iunit->flags.blank = BLANK_NULL;
+ iunit->flags.form = FORM_FORMATTED;
+ iunit->flags.pad = PAD_YES;
+ iunit->flags.status = STATUS_UNSPECIFIED;
+ iunit->flags.sign = SIGN_SUPPRESS;
+ iunit->flags.decimal = DECIMAL_POINT;
+ iunit->flags.delim = DELIM_UNSPECIFIED;
+ iunit->flags.encoding = ENCODING_DEFAULT;
+ iunit->flags.async = ASYNC_NO;
+ iunit->flags.round = ROUND_UNSPECIFIED;
+
+ /* Initialize the data transfer parameters. */
+
+ dtp->u.p.advance_status = ADVANCE_YES;
+ dtp->u.p.seen_dollar = 0;
+ dtp->u.p.skips = 0;
+ dtp->u.p.pending_spaces = 0;
+ dtp->u.p.max_pos = 0;
+ dtp->u.p.at_eof = 0;
+
+ /* This flag tells us the unit is assigned to internal I/O. */
+
+ dtp->u.p.unit_is_internal = 1;
+
+ return iunit;
+}
-int
-is_internal_unit (st_parameter_dt *dtp)
+
+/* free_internal_unit()-- Free memory allocated for internal units if any. */
+void
+free_internal_unit (st_parameter_dt *dtp)
{
- return dtp->u.p.current_unit == &internal_unit;
+ if (!is_internal_unit (dtp))
+ return;
+
+ if (unlikely (is_char4_unit (dtp)))
+ fbuf_destroy (dtp->u.p.current_unit);
+
+ if (dtp->u.p.current_unit != NULL)
+ {
+ free (dtp->u.p.current_unit->ls);
+
+ free (dtp->u.p.current_unit->s);
+
+ destroy_unit_mutex (dtp->u.p.current_unit);
+ }
}
+
-/* is_array_io ()-- Determine if the I/O is to/from an array */
+/* get_unit()-- Returns the unit structure associated with the integer
+ unit or the internal file. */
-int
-is_array_io (st_parameter_dt *dtp)
+gfc_unit *
+get_unit (st_parameter_dt *dtp, int do_create)
{
- return dtp->internal_unit_desc != NULL;
+
+ if ((dtp->common.flags & IOPARM_DT_HAS_INTERNAL_UNIT) != 0)
+ return get_internal_unit (dtp);
+
+ /* Has to be an external unit. */
+
+ dtp->u.p.unit_is_internal = 0;
+ dtp->internal_unit_desc = NULL;
+
+ return get_external_unit (dtp->common.unit, do_create);
}
/*************************/
-/* Initialize everything */
+/* Initialize everything. */
void
init_units (void)
__GTHREAD_MUTEX_INIT_FUNCTION (&unit_lock);
#endif
-#ifdef __GTHREAD_MUTEX_INIT
- {
- __gthread_mutex_t tmp = __GTHREAD_MUTEX_INIT;
- internal_unit.lock = tmp;
- }
-#else
- __GTHREAD_MUTEX_INIT_FUNCTION (&internal_unit.lock);
-#endif
-
if (options.stdin_unit >= 0)
{ /* STDIN */
u = insert_unit (options.stdin_unit);
u->flags.blank = BLANK_NULL;
u->flags.pad = PAD_YES;
u->flags.position = POSITION_ASIS;
-
+ u->flags.sign = SIGN_SUPPRESS;
+ u->flags.decimal = DECIMAL_POINT;
+ u->flags.encoding = ENCODING_DEFAULT;
+ u->flags.async = ASYNC_NO;
+ u->flags.round = ROUND_UNSPECIFIED;
+
u->recl = options.default_recl;
u->endfile = NO_ENDFILE;
+ u->file_len = strlen (stdin_name);
+ u->file = xmalloc (u->file_len);
+ memmove (u->file, stdin_name, u->file_len);
+
+ fbuf_init (u, 0);
+
__gthread_mutex_unlock (&u->lock);
}
u->flags.status = STATUS_OLD;
u->flags.blank = BLANK_NULL;
u->flags.position = POSITION_ASIS;
+ u->flags.sign = SIGN_SUPPRESS;
+ u->flags.decimal = DECIMAL_POINT;
+ u->flags.delim = DELIM_UNSPECIFIED;
+ u->flags.encoding = ENCODING_DEFAULT;
+ u->flags.async = ASYNC_NO;
+ u->flags.round = ROUND_UNSPECIFIED;
u->recl = options.default_recl;
u->endfile = AT_ENDFILE;
+
+ u->file_len = strlen (stdout_name);
+ u->file = xmalloc (u->file_len);
+ memmove (u->file, stdout_name, u->file_len);
+
+ fbuf_init (u, 0);
__gthread_mutex_unlock (&u->lock);
}
u->flags.status = STATUS_OLD;
u->flags.blank = BLANK_NULL;
u->flags.position = POSITION_ASIS;
+ u->flags.sign = SIGN_SUPPRESS;
+ u->flags.decimal = DECIMAL_POINT;
+ u->flags.encoding = ENCODING_DEFAULT;
+ u->flags.async = ASYNC_NO;
+ u->flags.round = ROUND_UNSPECIFIED;
u->recl = options.default_recl;
u->endfile = AT_ENDFILE;
+ u->file_len = strlen (stderr_name);
+ u->file = xmalloc (u->file_len);
+ memmove (u->file, stderr_name, u->file_len);
+
+ fbuf_init (u, 256); /* 256 bytes should be enough, probably not doing
+ any kind of exotic formatting to stderr. */
+
__gthread_mutex_unlock (&u->lock);
}
/* Calculate the maximum file offset in a portable manner.
- * max will be the largest signed number for the type gfc_offset.
- *
- * set a 1 in the LSB and keep a running sum, stopping at MSB-1 bit. */
-
+ max will be the largest signed number for the type gfc_offset.
+ set a 1 in the LSB and keep a running sum, stopping at MSB-1 bit. */
max_offset = 0;
for (i = 0; i < sizeof (max_offset) * 8 - 1; i++)
max_offset = max_offset + ((gfc_offset) 1 << i);
close_unit_1 (gfc_unit *u, int locked)
{
int i, rc;
+
+ /* If there are previously written bytes from a write with ADVANCE="no"
+ Reposition the buffer before closing. */
+ if (u->previous_nonadvancing_write)
+ finish_last_advance_record (u);
- rc = (u->s == NULL) ? 0 : sclose (u->s) == FAILURE;
+ rc = (u->s == NULL) ? 0 : sclose (u->s) == -1;
u->closed = 1;
if (!locked)
delete_unit (u);
- if (u->file)
- free_mem (u->file);
+ free (u->file);
u->file = NULL;
u->file_len = 0;
+ free_format_hash_table (u);
+ fbuf_destroy (u);
+
if (!locked)
__gthread_mutex_unlock (&u->lock);
avoid freeing the memory, the last such thread will free it
instead. */
if (u->waiting == 0)
- free_mem (u);
+ destroy_unit_mutex (u);
if (!locked)
__gthread_mutex_unlock (&unit_lock);
}
/* close_unit()-- Close a unit. The stream is closed, and any memory
- * associated with the stream is freed. Returns nonzero on I/O error.
- * Should be called with the u->lock locked. */
+ associated with the stream is freed. Returns nonzero on I/O error.
+ Should be called with the u->lock locked. */
int
close_unit (gfc_unit *u)
/* close_units()-- Delete units on completion. We just keep deleting
- * the root of the treap until there is nothing left.
- * Not sure what to do with locking here. Some other thread might be
- * holding some unit's lock and perhaps hold it indefinitely
- * (e.g. waiting for input from some pipe) and close_units shouldn't
- * delay the program too much. */
+ the root of the treap until there is nothing left.
+ Not sure what to do with locking here. Some other thread might be
+ holding some unit's lock and perhaps hold it indefinitely
+ (e.g. waiting for input from some pipe) and close_units shouldn't
+ delay the program too much. */
void
close_units (void)
close_unit_1 (unit_root, 1);
__gthread_mutex_unlock (&unit_lock);
}
+
+
+/* High level interface to truncate a file, i.e. flush format buffers,
+ and generate an error or set some flags. Just like POSIX
+ ftruncate, returns 0 on success, -1 on failure. */
+
+int
+unit_truncate (gfc_unit * u, gfc_offset pos, st_parameter_common * common)
+{
+ int ret;
+
+ /* Make sure format buffer is flushed. */
+ if (u->flags.form == FORM_FORMATTED)
+ {
+ if (u->mode == READING)
+ pos += fbuf_reset (u);
+ else
+ fbuf_flush (u, u->mode);
+ }
+
+ /* struncate() should flush the stream buffer if necessary, so don't
+ bother calling sflush() here. */
+ ret = struncate (u->s, pos);
+
+ if (ret != 0)
+ generate_error (common, LIBERROR_OS, NULL);
+ else
+ {
+ u->endfile = AT_ENDFILE;
+ u->flags.position = POSITION_APPEND;
+ }
+
+ return ret;
+}
+
+
+/* filename_from_unit()-- If the unit_number exists, return a pointer to the
+ name of the associated file, otherwise return the empty string. The caller
+ must free memory allocated for the filename string. */
+
+char *
+filename_from_unit (int n)
+{
+ gfc_unit *u;
+ int c;
+
+ /* Find the unit. */
+ u = unit_root;
+ while (u != NULL)
+ {
+ c = compare (n, u->unit_number);
+ if (c < 0)
+ u = u->left;
+ if (c > 0)
+ u = u->right;
+ if (c == 0)
+ break;
+ }
+
+ /* Get the filename. */
+ if (u != NULL)
+ return fc_strdup (u->file, u->file_len);
+ else
+ return (char *) NULL;
+}
+
+void
+finish_last_advance_record (gfc_unit *u)
+{
+
+ if (u->saved_pos > 0)
+ fbuf_seek (u, u->saved_pos, SEEK_CUR);
+
+ if (!(u->unit_number == options.stdout_unit
+ || u->unit_number == options.stderr_unit))
+ {
+#ifdef HAVE_CRLF
+ const int len = 2;
+#else
+ const int len = 1;
+#endif
+ char *p = fbuf_alloc (u, len);
+ if (!p)
+ os_error ("Completing record after ADVANCE_NO failed");
+#ifdef HAVE_CRLF
+ *(p++) = '\r';
+#endif
+ *p = '\n';
+ }
+
+ fbuf_flush (u, u->mode);
+}
+
+/* Assign a negative number for NEWUNIT in OPEN statements. */
+GFC_INTEGER_4
+get_unique_unit_number (st_parameter_open *opp)
+{
+ GFC_INTEGER_4 num;
+
+#ifdef HAVE_SYNC_FETCH_AND_ADD
+ num = __sync_fetch_and_add (&next_available_newunit, -1);
+#else
+ __gthread_mutex_lock (&unit_lock);
+ num = next_available_newunit--;
+ __gthread_mutex_unlock (&unit_lock);
+#endif
+
+ /* Do not allow NEWUNIT numbers to wrap. */
+ if (num > GFC_FIRST_NEWUNIT)
+ {
+ generate_error (&opp->common, LIBERROR_INTERNAL, "NEWUNIT exhausted");
+ return 0;
+ }
+ return num;
+}