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;
 | |
| }
 |