OSDN Git Service

2007-02-02 Paul Thomas <pault@gcc.gnu.org>
[pf3gnuchains/gcc-fork.git] / libgfortran / io / unix.c
index 237f09e..08f3287 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002, 2003, 2004, 2005
+/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007
    Free Software Foundation, Inc.
    Contributed by Andy Vaught
 
@@ -36,6 +36,7 @@ Boston, MA 02110-1301, USA.  */
 
 #include <unistd.h>
 #include <stdio.h>
+#include <stdarg.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <assert.h>
@@ -45,7 +46,6 @@ Boston, MA 02110-1301, USA.  */
 
 #include "libgfortran.h"
 #include "io.h"
-#include "unix.h"
 
 #ifndef SSIZE_MAX
 #define SSIZE_MAX SHRT_MAX
@@ -81,6 +81,42 @@ Boston, MA 02110-1301, USA.  */
 #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",
@@ -327,15 +363,6 @@ get_oserror (void)
 }
 
 
-/* sys_exit()-- Terminate the program with an exit code */
-
-void
-sys_exit (int code)
-{
-  exit (code);
-}
-
-
 /*********************************************************************
     File descriptor stream functions
 *********************************************************************/
@@ -349,9 +376,9 @@ fd_flush (unix_stream * s)
   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;
 
@@ -536,8 +563,10 @@ fd_alloc_w_at (unix_stream * s, int *len, gfc_offset where)
 
   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)
@@ -562,6 +591,10 @@ fd_sfree (unix_stream * s)
 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;
@@ -569,6 +602,7 @@ fd_seek (unix_stream * s, gfc_offset offset)
     }
 
   s->physical_offset = s->logical_offset = offset;
+  s->active = 0;
 
   return (lseek (s->fd, offset, SEEK_SET) < 0) ? FAILURE : SUCCESS;
 }
@@ -581,13 +615,19 @@ fd_seek (unix_stream * s, gfc_offset offset)
 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
@@ -597,7 +637,7 @@ fd_truncate (unix_stream * s)
 #endif
     {
       s->physical_offset = s->file_length = 0;
-      return FAILURE;
+      return SUCCESS;
     }
 
   s->physical_offset = s->file_length = s->logical_offset;
@@ -606,6 +646,34 @@ fd_truncate (unix_stream * s)
 }
 
 
+/* 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
@@ -739,6 +807,7 @@ fd_open (unix_stream * s)
   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;
 }
@@ -870,6 +939,25 @@ mem_seek (unix_stream * s, gfc_offset offset)
 }
 
 
+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)))
 {
@@ -880,7 +968,8 @@ 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;
 }
@@ -932,6 +1021,7 @@ open_internal (char *base, int length)
   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;
 }
@@ -958,7 +1048,12 @@ fd_to_stream (int fd, int prot)
   /* 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);
@@ -1238,6 +1333,9 @@ input_stream (void)
 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);
 }
 
@@ -1248,6 +1346,9 @@ output_stream (void)
 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);
 }
 
@@ -1272,6 +1373,103 @@ init_error_stream (unix_stream *error)
   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