}
+/* Reads a block directly into application data space. */
+
+static void
+read_block_direct (void * buf, size_t * nbytes)
+{
+ int *length;
+ void *data;
+ size_t nread;
+
+ if (current_unit->flags.form == FORM_FORMATTED &&
+ current_unit->flags.access == ACCESS_SEQUENTIAL)
+ {
+ length = (int*) nbytes;
+ data = read_sf (length); /* Special case. */
+ memcpy (buf, data, (size_t) *length);
+ return;
+ }
+
+ if (current_unit->bytes_left < *nbytes)
+ {
+ if (current_unit->flags.pad == PAD_NO)
+ {
+ generate_error (ERROR_EOR, NULL); /* Not enough data left. */
+ return;
+ }
+
+ *nbytes = current_unit->bytes_left;
+ }
+
+ current_unit->bytes_left -= *nbytes;
+
+ nread = *nbytes;
+ if (sread (current_unit->s, buf, &nread) != 0)
+ {
+ generate_error (ERROR_OS, NULL);
+ return;
+ }
+
+ if (ioparm.size != NULL)
+ *ioparm.size += (GFC_INTEGER_4) nread;
+
+ if (nread != *nbytes)
+ { /* Short read, e.g. if we hit EOF. */
+ if (current_unit->flags.pad == PAD_YES)
+ {
+ memset (((char *) buf) + nread, ' ', *nbytes - nread);
+ *nbytes = nread;
+ }
+ else
+ generate_error (ERROR_EOR, NULL);
+ }
+}
+
+
/* Function for writing a block of bytes to the current file at the
current position, advancing the file pointer. We are given a length
and return a pointer to a buffer that the caller must (completely)
}
+/* Writes a block directly without necessarily allocating space in a
+ buffer. */
+
+static void
+write_block_direct (void * buf, size_t * nbytes)
+{
+ if (current_unit->bytes_left < *nbytes)
+ generate_error (ERROR_EOR, NULL);
+
+ current_unit->bytes_left -= (gfc_offset) *nbytes;
+
+ if (swrite (current_unit->s, buf, nbytes) != 0)
+ generate_error (ERROR_OS, NULL);
+
+ if (ioparm.size != NULL)
+ *ioparm.size += (GFC_INTEGER_4) *nbytes;
+}
+
+
/* Master function for unformatted reads. */
static void
unformatted_read (bt type, void *dest, int length, size_t nelems)
{
- void *source;
- int w;
+ size_t len;
- length *= nelems;
+ len = length * nelems;
/* Transfer functions get passed the kind of the entity, so we have
to fix this for COMPLEX data which are twice the size of their
kind. */
if (type == BT_COMPLEX)
- length *= 2;
-
- w = length;
- source = read_block (&w);
+ len *= 2;
- if (source != NULL)
- {
- memcpy (dest, source, w);
- if (length != w)
- memset (((char *) dest) + w, ' ', length - w);
- }
+ read_block_direct (dest, &len);
}
+
/* Master function for unformatted writes. */
static void
unformatted_write (bt type, void *source, int length, size_t nelems)
{
- void *dest;
size_t len;
len = length * nelems;
if (type == BT_COMPLEX)
len *= 2;
- dest = write_block (len);
- if (dest != NULL)
- memcpy (dest, source, len);
+ write_block_direct (source, &len);
}
#include <fcntl.h>
#include <assert.h>
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
#include <string.h>
#include <errno.h>
#define PATH_MAX 1024
#endif
-#ifndef MAP_FAILED
-#define MAP_FAILED ((void *) -1)
-#endif
-
#ifndef PROT_READ
#define PROT_READ 1
#endif
return 0;
}
-/* write()-- Write a buffer to a descriptor, allowing for short writes */
-static int
-writen (int fd, char *buffer, int len)
+/* Reset a stream after reading/writing. Assumes that the buffers have
+ been flushed. */
+
+inline static void
+reset_stream (unix_stream * s, size_t bytes_rw)
{
- int n, n0;
+ s->physical_offset += bytes_rw;
+ s->logical_offset = s->physical_offset;
+ if (s->file_length != -1 && s->physical_offset > s->file_length)
+ s->file_length = s->physical_offset;
+}
- n0 = len;
- while (len > 0)
- {
- n = write (fd, buffer, len);
- if (n < 0)
- return n;
+/* Read bytes into a buffer, allowing for short reads. If the nbytes
+ * argument is less on return than on entry, it is because we've hit
+ * the end of file. */
- buffer += n;
- len -= n;
+static int
+do_read (unix_stream * s, void * buf, size_t * nbytes)
+{
+ ssize_t trans;
+ size_t bytes_left;
+ char *buf_st;
+ int status;
+
+ status = 0;
+ bytes_left = *nbytes;
+ buf_st = (char *) buf;
+
+ /* We must read in a loop since some systems don't restart system
+ calls in case of a signal. */
+ while (bytes_left > 0)
+ {
+ /* Requests between SSIZE_MAX and SIZE_MAX are undefined by SUSv3,
+ so we must read in chunks smaller than SSIZE_MAX. */
+ trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX;
+ trans = read (s->fd, buf_st, trans);
+ if (trans < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ {
+ status = errno;
+ break;
+ }
+ }
+ else if (trans == 0) /* We hit EOF. */
+ break;
+ buf_st += trans;
+ bytes_left -= trans;
}
- return n0;
+ *nbytes -= bytes_left;
+ return status;
}
-#if 0
-/* readn()-- Read bytes into a buffer, allowing for short reads. If
- * fewer than len bytes are returned, it is because we've hit the end
- * of file. */
+/* Write a buffer to a stream, allowing for short writes. */
static int
-readn (int fd, char *buffer, int len)
+do_write (unix_stream * s, const void * buf, size_t * nbytes)
{
- int nread, n;
-
- nread = 0;
-
- while (len > 0)
+ ssize_t trans;
+ size_t bytes_left;
+ char *buf_st;
+ int status;
+
+ status = 0;
+ bytes_left = *nbytes;
+ buf_st = (char *) buf;
+
+ /* We must write in a loop since some systems don't restart system
+ calls in case of a signal. */
+ while (bytes_left > 0)
{
- n = read (fd, buffer, len);
- if (n < 0)
- return n;
-
- if (n == 0)
- return nread;
-
- buffer += n;
- nread += n;
- len -= n;
+ /* Requests between SSIZE_MAX and SIZE_MAX are undefined by SUSv3,
+ so we must write in chunks smaller than SSIZE_MAX. */
+ trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX;
+ trans = write (s->fd, buf_st, trans);
+ if (trans < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ {
+ status = errno;
+ break;
+ }
+ }
+ buf_st += trans;
+ bytes_left -= trans;
}
- return nread;
+ *nbytes -= bytes_left;
+ return status;
}
-#endif
/* get_oserror()-- Get the most recent operating system error. For
File descriptor stream functions
*********************************************************************/
+
/* fd_flush()-- Write bytes that need to be written */
static try
fd_flush (unix_stream * s)
{
+ size_t writelen;
+
if (s->ndirty == 0)
return SUCCESS;;
lseek (s->fd, s->dirty_offset, SEEK_SET) < 0)
return FAILURE;
- if (writen (s->fd, s->buffer + (s->dirty_offset - s->buffer_offset),
- s->ndirty) < 0)
+ writelen = s->ndirty;
+ if (do_write (s, s->buffer + (s->dirty_offset - s->buffer_offset),
+ &writelen) != 0)
return FAILURE;
- s->physical_offset = s->dirty_offset + s->ndirty;
+ s->physical_offset = s->dirty_offset + writelen;
/* don't increment file_length if the file is non-seekable */
if (s->file_length != -1 && s->physical_offset > s->file_length)
- s->file_length = s->physical_offset;
- s->ndirty = 0;
+ s->file_length = s->physical_offset;
+
+ s->ndirty -= writelen;
+ if (s->ndirty != 0)
+ return FAILURE;
return SUCCESS;
}
fd_alloc_r_at (unix_stream * s, int *len, gfc_offset where)
{
gfc_offset m;
- int n;
+ size_t n;
if (where == -1)
where = s->logical_offset;
if (s->physical_offset != m && lseek (s->fd, m, SEEK_SET) < 0)
return NULL;
- n = read (s->fd, s->buffer + s->active, s->len - s->active);
- if (n < 0)
+ n = s->len - s->active;
+ if (do_read (s, s->buffer + s->active, &n) != 0)
return NULL;
s->physical_offset = where + n;
}
-static int
+static try
fd_seek (unix_stream * s, gfc_offset offset)
{
+ if (s->physical_offset == offset) /* Are we lucky and avoid syscall? */
+ {
+ s->logical_offset = offset;
+ return SUCCESS;
+ }
+
s->physical_offset = s->logical_offset = offset;
return (lseek (s->fd, offset, SEEK_SET) < 0) ? FAILURE : SUCCESS;
}
+
+
+/* Stream read function. Avoids using a buffer for big reads. The
+ interface is like POSIX read(), but the nbytes argument is a
+ pointer; on return it contains the number of bytes written. The
+ function return value is the status indicator (0 for success). */
+
+static int
+fd_read (unix_stream * s, void * buf, size_t * nbytes)
+{
+ void *p;
+ int tmp, status;
+
+ if (*nbytes < BUFFER_SIZE && !s->unbuffered)
+ {
+ tmp = *nbytes;
+ p = fd_alloc_r_at (s, &tmp, -1);
+ if (p)
+ {
+ *nbytes = tmp;
+ memcpy (buf, p, *nbytes);
+ return 0;
+ }
+ else
+ {
+ *nbytes = 0;
+ return errno;
+ }
+ }
+
+ /* If the request is bigger than BUFFER_SIZE we flush the buffers
+ and read directly. */
+ if (fd_flush (s) == FAILURE)
+ {
+ *nbytes = 0;
+ return errno;
+ }
+
+ if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE)
+ {
+ *nbytes = 0;
+ return errno;
+ }
+
+ status = do_read (s, buf, nbytes);
+ reset_stream (s, *nbytes);
+ return status;
+}
+
+
+/* Stream write function. Avoids using a buffer for big writes. The
+ interface is like POSIX write(), but the nbytes argument is a
+ pointer; on return it contains the number of bytes written. The
+ function return value is the status indicator (0 for success). */
+
+static int
+fd_write (unix_stream * s, const void * buf, size_t * nbytes)
+{
+ void *p;
+ int tmp, status;
+
+ if (*nbytes < BUFFER_SIZE && !s->unbuffered)
+ {
+ tmp = *nbytes;
+ p = fd_alloc_w_at (s, &tmp, -1);
+ if (p)
+ {
+ *nbytes = tmp;
+ memcpy (p, buf, *nbytes);
+ return 0;
+ }
+ else
+ {
+ *nbytes = 0;
+ return errno;
+ }
+ }
+
+ /* If the request is bigger than BUFFER_SIZE we flush the buffers
+ and write directly. */
+ if (fd_flush (s) == FAILURE)
+ {
+ *nbytes = 0;
+ return errno;
+ }
+
+ if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE)
+ {
+ *nbytes = 0;
+ return errno;
+ }
+
+ status = do_write (s, buf, nbytes);
+ reset_stream (s, *nbytes);
+ return status;
+}
+
+
static try
fd_close (unix_stream * s)
{
s->st.close = (void *) fd_close;
s->st.seek = (void *) fd_seek;
s->st.truncate = (void *) fd_truncate;
+ s->st.read = (void *) fd_read;
+ s->st.write = (void *) fd_write;
s->buffer = NULL;
}
+
/*********************************************************************
memory stream functions - These are used for internal files
}
+/* Stream read function for internal units. This is not actually used
+ at the moment, as all internal IO is formatted and the formatted IO
+ routines use mem_alloc_r_at. */
+
+static int
+mem_read (unix_stream * s, void * buf, size_t * nbytes)
+{
+ void *p;
+ int tmp;
+
+ tmp = *nbytes;
+ p = mem_alloc_r_at (s, &tmp, -1);
+ if (p)
+ {
+ *nbytes = tmp;
+ memcpy (buf, p, *nbytes);
+ return 0;
+ }
+ else
+ {
+ *nbytes = 0;
+ return errno;
+ }
+}
+
+
+/* Stream write function for internal units. This is not actually used
+ at the moment, as all internal IO is formatted and the formatted IO
+ routines use mem_alloc_w_at. */
+
+static int
+mem_write (unix_stream * s, const void * buf, size_t * nbytes)
+{
+ void *p;
+ int tmp;
+
+ errno = 0;
+
+ tmp = *nbytes;
+ p = mem_alloc_w_at (s, &tmp, -1);
+ if (p)
+ {
+ *nbytes = tmp;
+ memcpy (p, buf, *nbytes);
+ return 0;
+ }
+ else
+ {
+ *nbytes = 0;
+ return errno;
+ }
+}
+
+
static int
mem_seek (unix_stream * s, gfc_offset offset)
{
s->st.close = (void *) mem_close;
s->st.seek = (void *) mem_seek;
s->st.truncate = (void *) mem_truncate;
+ s->st.read = (void *) mem_read;
+ s->st.write = (void *) mem_write;
return (stream *) s;
}
int
is_seekable (stream * s)
{
- /* by convention, if file_length == -1, the file is not seekable
- note that a mmapped file is always seekable, an fd_ file may
- or may not be. */
+ /* By convention, if file_length == -1, the file is not
+ seekable. */
return ((unix_stream *) s)->file_length!=-1;
}