-/* Copyright (C) 2002, 2003, 2004, 2005
+/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
Contributed by Andy Vaught
#include <unistd.h>
#include <stdio.h>
+#include <stdarg.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include "libgfortran.h"
#include "io.h"
-#include "unix.h"
#ifndef SSIZE_MAX
#define SSIZE_MAX SHRT_MAX
#define S_IWOTH 0
#endif
+
+/* Unix stream I/O module */
+
+#define BUFFER_SIZE 8192
+
+typedef struct
+{
+ stream st;
+
+ int fd;
+ gfc_offset buffer_offset; /* File offset of the start of the buffer */
+ gfc_offset physical_offset; /* Current physical file offset */
+ gfc_offset logical_offset; /* Current logical file offset */
+ gfc_offset dirty_offset; /* Start of modified bytes in buffer */
+ gfc_offset file_length; /* Length of the file, -1 if not seekable. */
+
+ char *buffer;
+ int len; /* Physical length of the current buffer */
+ int active; /* Length of valid bytes in the buffer */
+
+ int prot;
+ int ndirty; /* Dirty bytes starting at dirty_offset */
+
+ int special_file; /* =1 if the fd refers to a special file */
+
+ unsigned unbuffered:1;
+
+ char small_buffer[BUFFER_SIZE];
+
+}
+unix_stream;
+
+extern stream *init_error_stream (unix_stream *);
+internal_proto(init_error_stream);
+
+
/* This implementation of stream I/O is based on the paper:
*
* "Exploiting the advantages of mapped files for stream I/O",
}
-/* sys_exit()-- Terminate the program with an exit code */
-
-void
-sys_exit (int code)
-{
- exit (code);
-}
-
-
/*********************************************************************
File descriptor stream functions
*********************************************************************/
size_t writelen;
if (s->ndirty == 0)
- return SUCCESS;;
-
- if (s->physical_offset != s->dirty_offset &&
+ return SUCCESS;
+
+ if (s->file_length != -1 && s->physical_offset != s->dirty_offset &&
lseek (s->fd, s->dirty_offset, SEEK_SET) < 0)
return FAILURE;
s->logical_offset = where + *len;
- if (where + *len > s->file_length)
- s->file_length = where + *len;
+ /* Don't increment file_length if the file is non-seekable. */
+
+ if (s->file_length != -1 && s->logical_offset > s->file_length)
+ s->file_length = s->logical_offset;
n = s->logical_offset - s->buffer_offset;
if (n > s->active)
static try
fd_seek (unix_stream * s, gfc_offset offset)
{
+
+ if (s->file_length == -1)
+ return SUCCESS;
+
if (s->physical_offset == offset) /* Are we lucky and avoid syscall? */
{
s->logical_offset = offset;
}
s->physical_offset = s->logical_offset = offset;
+ s->active = 0;
return (lseek (s->fd, offset, SEEK_SET) < 0) ? FAILURE : SUCCESS;
}
static try
fd_truncate (unix_stream * s)
{
+ /* Non-seekable files, like terminals and fifo's fail the lseek so just
+ return success, there is nothing to truncate. If its not a pipe there
+ is a real problem. */
if (lseek (s->fd, s->logical_offset, SEEK_SET) == -1)
- return FAILURE;
+ {
+ if (errno == ESPIPE)
+ return SUCCESS;
+ else
+ return FAILURE;
+ }
- /* non-seekable files, like terminals and fifo's fail the lseek.
- Using ftruncate on a seekable special file (like /dev/null)
- is undefined, so we treat it as if the ftruncate failed.
- */
+ /* Using ftruncate on a seekable special file (like /dev/null)
+ is undefined, so we treat it as if the ftruncate succeeded. */
#ifdef HAVE_FTRUNCATE
if (s->special_file || ftruncate (s->fd, s->logical_offset))
#else
#endif
{
s->physical_offset = s->file_length = 0;
- return FAILURE;
+ return SUCCESS;
}
s->physical_offset = s->file_length = s->logical_offset;
}
+/* Similar to memset(), but operating on a stream instead of a string.
+ Takes care of not using too much memory. */
+
+static try
+fd_sset (unix_stream * s, int c, size_t n)
+{
+ size_t bytes_left;
+ int trans;
+ void *p;
+
+ bytes_left = n;
+
+ while (bytes_left > 0)
+ {
+ /* memset() in chunks of BUFFER_SIZE. */
+ trans = (bytes_left < BUFFER_SIZE) ? bytes_left : BUFFER_SIZE;
+
+ p = fd_alloc_w_at (s, &trans, -1);
+ if (p)
+ memset (p, c, trans);
+ else
+ return FAILURE;
+
+ bytes_left -= trans;
+ }
+
+ return SUCCESS;
+}
/* Stream read function. Avoids using a buffer for big reads. The
s->st.truncate = (void *) fd_truncate;
s->st.read = (void *) fd_read;
s->st.write = (void *) fd_write;
+ s->st.set = (void *) fd_sset;
s->buffer = NULL;
}
}
+static try
+mem_set (unix_stream * s, int c, size_t n)
+{
+ void *p;
+ int len;
+
+ len = n;
+
+ p = mem_alloc_w_at (s, &len, -1);
+ if (p)
+ {
+ memset (p, c, len);
+ return SUCCESS;
+ }
+ else
+ return FAILURE;
+}
+
+
static int
mem_truncate (unix_stream * s __attribute__ ((unused)))
{
static try
mem_close (unix_stream * s)
{
- free_mem (s);
+ if (s != NULL)
+ free_mem (s);
return SUCCESS;
}
s->st.truncate = (void *) mem_truncate;
s->st.read = (void *) mem_read;
s->st.write = (void *) mem_write;
+ s->st.set = (void *) mem_set;
return (stream *) s;
}
/* Get the current length of the file. */
fstat (fd, &statbuf);
- s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
+
+ if (lseek (fd, 0, SEEK_CUR) == (off_t) -1)
+ s->file_length = -1;
+ else
+ s->file_length = S_ISREG (statbuf.st_mode) ? statbuf.st_size : -1;
+
s->special_file = !S_ISREG (statbuf.st_mode);
fd_open (s);
stream *
output_stream (void)
{
+#if defined(HAVE_CRLF) && defined(HAVE_SETMODE)
+ setmode (STDOUT_FILENO, O_BINARY);
+#endif
return fd_to_stream (STDOUT_FILENO, PROT_WRITE);
}
stream *
error_stream (void)
{
+#if defined(HAVE_CRLF) && defined(HAVE_SETMODE)
+ setmode (STDERR_FILENO, O_BINARY);
+#endif
return fd_to_stream (STDERR_FILENO, PROT_WRITE);
}
return (stream *) error;
}
+/* st_printf()-- simple printf() function for streams that handles the
+ * formats %d, %s and %c. This function handles printing of error
+ * messages that originate within the library itself, not from a user
+ * program. */
+
+int
+st_printf (const char *format, ...)
+{
+ int count, total;
+ va_list arg;
+ char *p;
+ const char *q;
+ stream *s;
+ char itoa_buf[GFC_ITOA_BUF_SIZE];
+ unix_stream err_stream;
+
+ total = 0;
+ s = init_error_stream (&err_stream);
+ va_start (arg, format);
+
+ for (;;)
+ {
+ count = 0;
+
+ while (format[count] != '%' && format[count] != '\0')
+ count++;
+
+ if (count != 0)
+ {
+ p = salloc_w (s, &count);
+ memmove (p, format, count);
+ sfree (s);
+ }
+
+ total += count;
+ format += count;
+ if (*format++ == '\0')
+ break;
+
+ switch (*format)
+ {
+ case 'c':
+ count = 1;
+
+ p = salloc_w (s, &count);
+ *p = (char) va_arg (arg, int);
+
+ sfree (s);
+ break;
+
+ case 'd':
+ q = gfc_itoa (va_arg (arg, int), itoa_buf, sizeof (itoa_buf));
+ count = strlen (q);
+
+ p = salloc_w (s, &count);
+ memmove (p, q, count);
+ sfree (s);
+ break;
+
+ case 'x':
+ q = xtoa (va_arg (arg, unsigned), itoa_buf, sizeof (itoa_buf));
+ count = strlen (q);
+
+ p = salloc_w (s, &count);
+ memmove (p, q, count);
+ sfree (s);
+ break;
+
+ case 's':
+ q = va_arg (arg, char *);
+ count = strlen (q);
+
+ p = salloc_w (s, &count);
+ memmove (p, q, count);
+ sfree (s);
+ break;
+
+ case '\0':
+ return total;
+
+ default:
+ count = 2;
+ p = salloc_w (s, &count);
+ p[0] = format[-1];
+ p[1] = format[0];
+ sfree (s);
+ break;
+ }
+
+ total += count;
+ format++;
+ }
+
+ va_end (arg);
+ return total;
+}
+
/* compare_file_filename()-- Given an open stream and a fortran string
* that is a filename, figure out if the file is the same as the