mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			303 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			303 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C
		
	
	
	
/* Copyright (C) 2008-2016 Free Software Foundation, Inc.
 | 
						|
   Contributed by Janne Blomqvist
 | 
						|
 | 
						|
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 3, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
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.
 | 
						|
 | 
						|
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 "io.h"
 | 
						|
#include "fbuf.h"
 | 
						|
#include "unix.h"
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
 | 
						|
 | 
						|
//#define FBUF_DEBUG
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
fbuf_init (gfc_unit * u, int len)
 | 
						|
{
 | 
						|
  if (len == 0)
 | 
						|
    len = 512;			/* Default size.  */
 | 
						|
 | 
						|
  u->fbuf = xmalloc (sizeof (struct fbuf));
 | 
						|
  u->fbuf->buf = xmalloc (len);
 | 
						|
  u->fbuf->len = len;
 | 
						|
  u->fbuf->act = u->fbuf->pos = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void
 | 
						|
fbuf_destroy (gfc_unit * u)
 | 
						|
{
 | 
						|
  if (u->fbuf == NULL)
 | 
						|
    return;
 | 
						|
  free (u->fbuf->buf);
 | 
						|
  free (u->fbuf);
 | 
						|
  u->fbuf = NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static void
 | 
						|
#ifdef FBUF_DEBUG
 | 
						|
fbuf_debug (gfc_unit * u, const char * format, ...)
 | 
						|
{
 | 
						|
  va_list args;
 | 
						|
  va_start(args, format);
 | 
						|
  vfprintf(stderr, format, args);
 | 
						|
  va_end(args);
 | 
						|
  fprintf (stderr, "fbuf_debug pos: %d, act: %d, buf: ''", 
 | 
						|
           u->fbuf->pos, u->fbuf->act);
 | 
						|
  for (int ii = 0; ii < u->fbuf->act; ii++)
 | 
						|
    {
 | 
						|
      putc (u->fbuf->buf[ii], stderr);
 | 
						|
    }
 | 
						|
  fprintf (stderr, "''\n");
 | 
						|
}
 | 
						|
#else
 | 
						|
fbuf_debug (gfc_unit * u __attribute__ ((unused)),
 | 
						|
            const char * format __attribute__ ((unused)),
 | 
						|
            ...) {}
 | 
						|
#endif
 | 
						|
 | 
						|
  
 | 
						|
 | 
						|
/* You should probably call this before doing a physical seek on the
 | 
						|
   underlying device.  Returns how much the physical position was
 | 
						|
   modified.  */
 | 
						|
 | 
						|
int
 | 
						|
fbuf_reset (gfc_unit * u)
 | 
						|
{
 | 
						|
  int seekval = 0;
 | 
						|
 | 
						|
  if (!u->fbuf)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  fbuf_debug (u, "fbuf_reset: ");
 | 
						|
  fbuf_flush (u, u->mode);
 | 
						|
  /* If we read past the current position, seek the underlying device
 | 
						|
     back.  */
 | 
						|
  if (u->mode == READING && u->fbuf->act > u->fbuf->pos)
 | 
						|
    {
 | 
						|
      seekval = - (u->fbuf->act - u->fbuf->pos);
 | 
						|
      fbuf_debug (u, "fbuf_reset seekval %d, ", seekval);
 | 
						|
    }
 | 
						|
  u->fbuf->act = u->fbuf->pos = 0;
 | 
						|
  return seekval;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Return a pointer to the current position in the buffer, and increase
 | 
						|
   the pointer by len. Makes sure that the buffer is big enough, 
 | 
						|
   reallocating if necessary.  */
 | 
						|
 | 
						|
char *
 | 
						|
fbuf_alloc (gfc_unit * u, int len)
 | 
						|
{
 | 
						|
  int newlen;
 | 
						|
  char *dest;
 | 
						|
  fbuf_debug (u, "fbuf_alloc len %d, ", len);
 | 
						|
  if (u->fbuf->pos + len > u->fbuf->len)
 | 
						|
    {
 | 
						|
      /* Round up to nearest multiple of the current buffer length.  */
 | 
						|
      newlen = ((u->fbuf->pos + len) / u->fbuf->len + 1) * u->fbuf->len;
 | 
						|
      u->fbuf->buf = xrealloc (u->fbuf->buf, newlen);
 | 
						|
      u->fbuf->len = newlen;
 | 
						|
    }
 | 
						|
 | 
						|
  dest = u->fbuf->buf + u->fbuf->pos;
 | 
						|
  u->fbuf->pos += len;
 | 
						|
  if (u->fbuf->pos > u->fbuf->act)
 | 
						|
    u->fbuf->act = u->fbuf->pos;
 | 
						|
  return dest;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* mode argument is WRITING for write mode and READING for read
 | 
						|
   mode. Return value is 0 for success, -1 on failure.  */
 | 
						|
 | 
						|
int
 | 
						|
fbuf_flush (gfc_unit * u, unit_mode mode)
 | 
						|
{
 | 
						|
  int nwritten;
 | 
						|
 | 
						|
  if (!u->fbuf)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  fbuf_debug (u, "fbuf_flush with mode %d: ", mode);
 | 
						|
 | 
						|
  if (mode == WRITING)
 | 
						|
    {
 | 
						|
      if (u->fbuf->pos > 0)
 | 
						|
	{
 | 
						|
	  nwritten = swrite (u->s, u->fbuf->buf, u->fbuf->pos);
 | 
						|
	  if (nwritten < 0)
 | 
						|
	    return -1;
 | 
						|
	}
 | 
						|
    }
 | 
						|
  /* Salvage remaining bytes for both reading and writing. This
 | 
						|
     happens with the combination of advance='no' and T edit
 | 
						|
     descriptors leaving the final position somewhere not at the end
 | 
						|
     of the record. For reading, this also happens if we sread() past
 | 
						|
     the record boundary.  */ 
 | 
						|
  if (u->fbuf->act > u->fbuf->pos && u->fbuf->pos > 0)
 | 
						|
    memmove (u->fbuf->buf, u->fbuf->buf + u->fbuf->pos, 
 | 
						|
             u->fbuf->act - u->fbuf->pos);
 | 
						|
 | 
						|
  u->fbuf->act -= u->fbuf->pos;
 | 
						|
  u->fbuf->pos = 0;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* The mode argument is LIST_WRITING for write mode and LIST_READING for
 | 
						|
   read.  This should only be used for list directed  I/O.
 | 
						|
   Return value is 0 for success, -1 on failure.  */
 | 
						|
 | 
						|
int
 | 
						|
fbuf_flush_list (gfc_unit * u, unit_mode mode)
 | 
						|
{
 | 
						|
  int nwritten;
 | 
						|
 | 
						|
  if (!u->fbuf)
 | 
						|
    return 0;
 | 
						|
 | 
						|
  if (u->fbuf->pos < 524288) /* Upper limit for list writing.  */
 | 
						|
    return 0;
 | 
						|
 | 
						|
  fbuf_debug (u, "fbuf_flush_list with mode %d: ", mode);
 | 
						|
 | 
						|
  if (mode == LIST_WRITING)
 | 
						|
    {
 | 
						|
      nwritten = swrite (u->s, u->fbuf->buf, u->fbuf->pos);
 | 
						|
      if (nwritten < 0)
 | 
						|
	return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  /* Salvage remaining bytes for both reading and writing.  */ 
 | 
						|
  if (u->fbuf->act > u->fbuf->pos)
 | 
						|
    memmove (u->fbuf->buf, u->fbuf->buf + u->fbuf->pos, 
 | 
						|
             u->fbuf->act - u->fbuf->pos);
 | 
						|
 | 
						|
  u->fbuf->act -= u->fbuf->pos;
 | 
						|
  u->fbuf->pos = 0;
 | 
						|
 | 
						|
  return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
fbuf_seek (gfc_unit * u, int off, int whence)
 | 
						|
{
 | 
						|
  if (!u->fbuf)
 | 
						|
    return -1;
 | 
						|
 | 
						|
  switch (whence)
 | 
						|
    {
 | 
						|
    case SEEK_SET:
 | 
						|
      break;
 | 
						|
    case SEEK_CUR:
 | 
						|
      off += u->fbuf->pos;
 | 
						|
      break;
 | 
						|
    case SEEK_END:
 | 
						|
      off += u->fbuf->act;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return -1;
 | 
						|
    }
 | 
						|
 | 
						|
  fbuf_debug (u, "fbuf_seek, off %d ", off);
 | 
						|
  /* The start of the buffer is always equal to the left tab
 | 
						|
     limit. Moving to the left past the buffer is illegal in C and
 | 
						|
     would also imply moving past the left tab limit, which is never
 | 
						|
     allowed in Fortran. Similarly, seeking past the end of the buffer
 | 
						|
     is not possible, in that case the user must make sure to allocate
 | 
						|
     space with fbuf_alloc().  So return error if that is
 | 
						|
     attempted.  */
 | 
						|
  if (off < 0 || off > u->fbuf->act)
 | 
						|
    return -1;
 | 
						|
  u->fbuf->pos = off;
 | 
						|
  return off;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Fill the buffer with bytes for reading.  Returns a pointer to start
 | 
						|
   reading from. If we hit EOF, returns a short read count. If any
 | 
						|
   other error occurs, return NULL.  After reading, the caller is
 | 
						|
   expected to call fbuf_seek to update the position with the number
 | 
						|
   of bytes actually processed. */
 | 
						|
 | 
						|
char *
 | 
						|
fbuf_read (gfc_unit * u, int * len)
 | 
						|
{
 | 
						|
  char *ptr;
 | 
						|
  int oldact, oldpos;
 | 
						|
  int readlen = 0;
 | 
						|
 | 
						|
  fbuf_debug (u, "fbuf_read, len %d: ", *len);
 | 
						|
  oldact = u->fbuf->act;
 | 
						|
  oldpos = u->fbuf->pos;
 | 
						|
  ptr = fbuf_alloc (u, *len);
 | 
						|
  u->fbuf->pos = oldpos;
 | 
						|
  if (oldpos + *len > oldact)
 | 
						|
    {
 | 
						|
      fbuf_debug (u, "reading %d bytes starting at %d ", 
 | 
						|
                  oldpos + *len - oldact, oldact);
 | 
						|
      readlen = sread (u->s, u->fbuf->buf + oldact, oldpos + *len - oldact);
 | 
						|
      if (readlen < 0)
 | 
						|
	return NULL;
 | 
						|
      *len = oldact - oldpos + readlen;
 | 
						|
    }
 | 
						|
  u->fbuf->act = oldact + readlen;
 | 
						|
  fbuf_debug (u, "fbuf_read done: ");
 | 
						|
  return ptr;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* When the fbuf_getc() inline function runs out of buffer space, it
 | 
						|
   calls this function to fill the buffer with bytes for
 | 
						|
   reading. Never call this function directly.  */
 | 
						|
 | 
						|
int
 | 
						|
fbuf_getc_refill (gfc_unit * u)
 | 
						|
{
 | 
						|
  int nread;
 | 
						|
  char *p;
 | 
						|
 | 
						|
  fbuf_debug (u, "fbuf_getc_refill ");
 | 
						|
 | 
						|
  /* Read 80 bytes (average line length?).  This is a compromise
 | 
						|
     between not needing to call the read() syscall all the time and
 | 
						|
     not having to memmove unnecessary stuff when switching to the
 | 
						|
     next record.  */
 | 
						|
  nread = 80;
 | 
						|
 | 
						|
  p = fbuf_read (u, &nread);
 | 
						|
 | 
						|
  if (p && nread > 0)
 | 
						|
    return (unsigned char) u->fbuf->buf[u->fbuf->pos++];
 | 
						|
  else
 | 
						|
    return EOF;
 | 
						|
}
 |