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