diff options
author | jvdelisle <jvdelisle@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-04-05 20:13:56 +0000 |
---|---|---|
committer | jvdelisle <jvdelisle@138bc75d-0d04-0410-961f-82ee72b054a4> | 2009-04-05 20:13:56 +0000 |
commit | 65f150109497089b84160c7cb3219f8dc69587a2 (patch) | |
tree | 9713a11310a5202d06a5ad2750bf0992f9c0e3f6 /libgfortran | |
parent | eb04d2ded6999d6662bc1efe4bed158072773ca8 (diff) | |
download | gcc-65f150109497089b84160c7cb3219f8dc69587a2.tar.gz |
2009-04-05 Daniel Kraft <d@domob.eu>
PR fortran/38654
* io/read.c (read_f): Reworked to speed up floating point parsing.
(convert_real): Use pointer-casting instead of memcpy and temporaries.
2009-04-05 Jerry DeLisle <jvdelisle@gcc.gnu.org>
PR libfortran/37754
* io/io.h (format_hash_entry): New structure for hash table.
(format_hash_table): The hash table itself.
(free_format_data): Revise function prototype.
(free_format_hash_table, init_format_hash,
free_format_hash): New function prototypes.
* io/unit.c (close_unit_1): Use free_format_hash_table.
* io/transfer.c (st_read_done, st_write_done): Free format data if
internal unit.
* io/format.c (free_format_hash_table): New function that frees any
memory allocated previously for cached format data.
(reset_node): New static helper function to reset the format counters
for a format node.
(reset_fnode_counters): New static function recursively calls reset_node
to traverse the fnode tree.
(format_hash): New simple hash function based on XOR, probabalistic,
tosses collisions.
(save_parsed_format): New static function to save the parsed format
data to use again.
(find_parsed_format): New static function searches the hash table
looking for a match.
(free_format_data): Revised to accept pointer to format data rather than
the dtp pointer so that the function can be used in more places.
(format_lex): Editorial.
(parse_format_list): Set flag used to determine of format data hashing
is to be used. Internal units are not persistent enough for this.
(revert): Move to ne location in file.
(parse_format): Use new functions to look for previously parsed
format strings and use them rather than re-parse. If not found, saves
the parsed format data for later use.
2009-04-05 Jerry DeLisle <jvdelisle@gcc.gnu.org>
PR libfortran/37754
* io/transfer.c (formatted_transfer_scalar): Remove this function by
factoring it into two new functions, one for read and one for write,
eliminating all the conditionals for read or write mode.
(formatted transfer_scalar_read): New function.
(formatted transfer_scalar_write): New function.
(formatted_transfer): Use new functions.
2009-04-05 Janne Blomqvist <jb@gcc.gnu.org>
PR libfortran/25561 libfortran/37754
* io/io.h (struct stream): Define new stream interface function
pointers, and inline functions for accessing it.
(struct fbuf): Use int instead of size_t, remove flushed element.
(mem_alloc_w): New prototype.
(mem_alloc_r): New prototype.
(stream_at_bof): Remove prototype.
(stream_at_eof): Remove prototype.
(file_position): Remove prototype.
(flush): Remove prototype.
(stream_offset): Remove prototype.
(unit_truncate): New prototype.
(read_block_form): Change to return pointer, int* argument.
(hit_eof): New prototype.
(fbuf_init): Change prototype.
(fbuf_reset): Change prototype.
(fbuf_alloc): Change prototype.
(fbuf_flush): Change prototype.
(fbuf_seek): Change prototype.
(fbuf_read): New prototype.
(fbuf_getc_refill): New prototype.
(fbuf_getc): New inline function.
* io/fbuf.c (fbuf_init): Use int, get rid of flushed.
(fbuf_debug): New function.
(fbuf_reset): Flush, and return position offset.
(fbuf_alloc): Simplify, don't flush, just realloc.
(fbuf_flush): Make usable for read mode, salvage remaining bytes.
(fbuf_seek): New whence argument.
(fbuf_read): New function.
(fbuf_getc_refill): New function.
* io/file_pos.c (formatted_backspace): Use new stream interface.
(unformatted_backspace): Likewise.
(st_backspace): Make sure format buffer is reset, use new stream
interface, use unit_truncate.
(st_endfile): Likewise.
(st_rewind): Likewise.
* io/intrinsics.c: Use new stream interface.
* io/list_read.c (push_char): Don't use u.p.scratch, use realloc
to resize.
(free_saved): Don't check u.p.scratch.
(next_char): Use new stream interface, use fbuf_getc() for external files.
(finish_list_read): flush format buffer.
(nml_query): Update to use modified interface:s
* io/open.c (test_endfile): Use new stream interface.
(edit_modes): Likewise.
(new_unit): Likewise, set bytes_left to 1 for stream files.
* io/read.c (read_l): Use new read_block_form interface.
(read_utf8): Likewise.
(read_utf8_char1): Likewise.
(read_default_char1): Likewise.
(read_utf8_char4): Likewise.
(read_default_char4): Likewise.
(read_a): Likewise.
(read_a_char4): Likewise.
(read_decimal): Likewise.
(read_radix): Likewise.
(read_f): Likewise.
* io/transfer.c (read_sf): Use fbuf_read and mem_alloc_r, remove
usage of u.p.line_buffer.
(read_block_form): Update interface to return pointer, use
fbuf_read for direct access.
(read_block_direct): Update to new stream interface.
(write_block): Use mem_alloc_w for internal I/O.
(write_buf): Update to new stream interface.
(formatted_transfer_scalar): Don't use u.p.line_buffer, use
fbuf_seek for external files.
(us_read): Update to new stream interface.
(us_write): Likewise.
(data_transfer_init): Always check if we switch modes and flush.
(skip_record): Use new stream interface, fix comparison.
(next_record_r): Check for and reset u.p.at_eof, use new stream
interface, use fbuf_getc for spacing.
(write_us_marker): Update to new stream interface, don't inline.
(next_record_w_unf): Likewise.
(sset): New function.
(next_record_w): Use new stream interface, use fbuf for printing
newline.
(next_record): Use new stream interface.
(finalize_transfer): Remove sfree call, use new stream interface.
(st_iolength_done): Don't use u.p.scratch.
(st_read): Don't check for end of file.
(st_read_done): Don't use u.p.scratch, use unit_truncate.
(hit_eof): New function.
* io/unit.c (init_units): Always init fbuf for formatted units.
(update_position): Use new stream interface.
(unit_truncate): New function.
(finish_last_advance_record): Use fbuf to print newline.
* io/unix.c: Remove unused SSIZE_MAX macro.
(BUFFER_SIZE): Make static const variable rather than macro.
(struct unix_stream): Remove dirty_offset, len, method,
small_buffer. Order elements by decreasing size.
(struct int_stream): Remove.
(move_pos_offset): Remove usage of dirty_offset.
(reset_stream): Remove.
(do_read): Rename to raw_read, update to match new stream
interface.
(do_write): Rename to raw_write, update to new stream interface.
(raw_seek): New function.
(raw_tell): New function.
(raw_truncate): New function.
(raw_close): New function.
(raw_flush): New function.
(raw_init): New function.
(fd_alloc): Remove.
(fd_alloc_r_at): Remove.
(fd_alloc_w_at): Remove.
(fd_sfree): Remove.
(fd_seek): Remove.
(fd_truncate): Remove.
(fd_sset): Remove.
(fd_read): Remove.
(fd_write): Remove.
(fd_close): Remove.
(fd_open): Remove.
(fd_flush): Rename to buf_flush, update to new stream interface
and unix_stream.
(buf_read): New function.
(buf_write): New function.
(buf_seek): New function.
(buf_tell): New function.
(buf_truncate): New function.
(buf_close): New function.
(buf_init): New function.
(mem_alloc_r_at): Rename to mem_alloc_r, change prototype.
(mem_alloc_w_at): Rename to mem_alloc_w, change prototype.
(mem_read): Change to match new stream interface.
(mem_write): Likewise.
(mem_seek): Likewise.
(mem_tell): Likewise.
(mem_truncate): Likewise.
(mem_close): Likewise.
(mem_flush): New function.
(mem_sfree): Remove.
(empty_internal_buffer): Cast to correct type.
(open_internal): Use correct type, init function pointers.
(fd_to_stream): Test whether to open file as buffered or raw.
(output_stream): Remove mode set.
(error_stream): Likewise.
(flush_all_units_1): Use new stream interface.
(flush_all_units): Likewise.
(stream_at_bof): Remove.
(stream_at_eof): Remove.
(file_position): Remove.
(file_length): Update logic to use stream interface.
(flush): Remove.
(stream_offset): Remove.
* io/write.c (write_utf8_char4): Use int instead of size_t.
(write_x): Extra safety check.
(namelist_write_newline): Use new stream interface.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@145571 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgfortran')
-rw-r--r-- | libgfortran/ChangeLog | 201 | ||||
-rw-r--r-- | libgfortran/io/fbuf.c | 257 | ||||
-rw-r--r-- | libgfortran/io/file_pos.c | 88 | ||||
-rw-r--r-- | libgfortran/io/format.c | 233 | ||||
-rw-r--r-- | libgfortran/io/intrinsics.c | 35 | ||||
-rw-r--r-- | libgfortran/io/io.h | 153 | ||||
-rw-r--r-- | libgfortran/io/list_read.c | 70 | ||||
-rw-r--r-- | libgfortran/io/open.c | 13 | ||||
-rw-r--r-- | libgfortran/io/read.c | 490 | ||||
-rw-r--r-- | libgfortran/io/transfer.c | 1247 | ||||
-rw-r--r-- | libgfortran/io/unit.c | 76 | ||||
-rw-r--r-- | libgfortran/io/unix.c | 1025 | ||||
-rw-r--r-- | libgfortran/io/write.c | 7 |
13 files changed, 2143 insertions, 1752 deletions
diff --git a/libgfortran/ChangeLog b/libgfortran/ChangeLog index e0ec2507052..761110f4e3c 100644 --- a/libgfortran/ChangeLog +++ b/libgfortran/ChangeLog @@ -1,3 +1,204 @@ +2009-04-05 Daniel Kraft <d@domob.eu> + + PR fortran/38654 + * io/read.c (read_f): Reworked to speed up floating point parsing. + (convert_real): Use pointer-casting instead of memcpy and temporaries. + +2009-04-05 Jerry DeLisle <jvdelisle@gcc.gnu.org> + + PR libfortran/37754 + * io/io.h (format_hash_entry): New structure for hash table. + (format_hash_table): The hash table itself. + (free_format_data): Revise function prototype. + (free_format_hash_table, init_format_hash, + free_format_hash): New function prototypes. + * io/unit.c (close_unit_1): Use free_format_hash_table. + * io/transfer.c (st_read_done, st_write_done): Free format data if + internal unit. + * io/format.c (free_format_hash_table): New function that frees any + memory allocated previously for cached format data. + (reset_node): New static helper function to reset the format counters + for a format node. + (reset_fnode_counters): New static function recursively calls reset_node + to traverse the fnode tree. + (format_hash): New simple hash function based on XOR, probabalistic, + tosses collisions. + (save_parsed_format): New static function to save the parsed format + data to use again. + (find_parsed_format): New static function searches the hash table + looking for a match. + (free_format_data): Revised to accept pointer to format data rather than + the dtp pointer so that the function can be used in more places. + (format_lex): Editorial. + (parse_format_list): Set flag used to determine of format data hashing + is to be used. Internal units are not persistent enough for this. + (revert): Move to ne location in file. + (parse_format): Use new functions to look for previously parsed + format strings and use them rather than re-parse. If not found, saves + the parsed format data for later use. + +2009-04-05 Jerry DeLisle <jvdelisle@gcc.gnu.org> + + PR libfortran/37754 + * io/transfer.c (formatted_transfer_scalar): Remove this function by + factoring it into two new functions, one for read and one for write, + eliminating all the conditionals for read or write mode. + (formatted transfer_scalar_read): New function. + (formatted transfer_scalar_write): New function. + (formatted_transfer): Use new functions. + +2009-04-05 Janne Blomqvist <jb@gcc.gnu.org> + + PR libfortran/25561 libfortran/37754 + * io/io.h (struct stream): Define new stream interface function + pointers, and inline functions for accessing it. + (struct fbuf): Use int instead of size_t, remove flushed element. + (mem_alloc_w): New prototype. + (mem_alloc_r): New prototype. + (stream_at_bof): Remove prototype. + (stream_at_eof): Remove prototype. + (file_position): Remove prototype. + (flush): Remove prototype. + (stream_offset): Remove prototype. + (unit_truncate): New prototype. + (read_block_form): Change to return pointer, int* argument. + (hit_eof): New prototype. + (fbuf_init): Change prototype. + (fbuf_reset): Change prototype. + (fbuf_alloc): Change prototype. + (fbuf_flush): Change prototype. + (fbuf_seek): Change prototype. + (fbuf_read): New prototype. + (fbuf_getc_refill): New prototype. + (fbuf_getc): New inline function. + * io/fbuf.c (fbuf_init): Use int, get rid of flushed. + (fbuf_debug): New function. + (fbuf_reset): Flush, and return position offset. + (fbuf_alloc): Simplify, don't flush, just realloc. + (fbuf_flush): Make usable for read mode, salvage remaining bytes. + (fbuf_seek): New whence argument. + (fbuf_read): New function. + (fbuf_getc_refill): New function. + * io/file_pos.c (formatted_backspace): Use new stream interface. + (unformatted_backspace): Likewise. + (st_backspace): Make sure format buffer is reset, use new stream + interface, use unit_truncate. + (st_endfile): Likewise. + (st_rewind): Likewise. + * io/intrinsics.c: Use new stream interface. + * io/list_read.c (push_char): Don't use u.p.scratch, use realloc + to resize. + (free_saved): Don't check u.p.scratch. + (next_char): Use new stream interface, use fbuf_getc() for external files. + (finish_list_read): flush format buffer. + (nml_query): Update to use modified interface:s + * io/open.c (test_endfile): Use new stream interface. + (edit_modes): Likewise. + (new_unit): Likewise, set bytes_left to 1 for stream files. + * io/read.c (read_l): Use new read_block_form interface. + (read_utf8): Likewise. + (read_utf8_char1): Likewise. + (read_default_char1): Likewise. + (read_utf8_char4): Likewise. + (read_default_char4): Likewise. + (read_a): Likewise. + (read_a_char4): Likewise. + (read_decimal): Likewise. + (read_radix): Likewise. + (read_f): Likewise. + * io/transfer.c (read_sf): Use fbuf_read and mem_alloc_r, remove + usage of u.p.line_buffer. + (read_block_form): Update interface to return pointer, use + fbuf_read for direct access. + (read_block_direct): Update to new stream interface. + (write_block): Use mem_alloc_w for internal I/O. + (write_buf): Update to new stream interface. + (formatted_transfer_scalar): Don't use u.p.line_buffer, use + fbuf_seek for external files. + (us_read): Update to new stream interface. + (us_write): Likewise. + (data_transfer_init): Always check if we switch modes and flush. + (skip_record): Use new stream interface, fix comparison. + (next_record_r): Check for and reset u.p.at_eof, use new stream + interface, use fbuf_getc for spacing. + (write_us_marker): Update to new stream interface, don't inline. + (next_record_w_unf): Likewise. + (sset): New function. + (next_record_w): Use new stream interface, use fbuf for printing + newline. + (next_record): Use new stream interface. + (finalize_transfer): Remove sfree call, use new stream interface. + (st_iolength_done): Don't use u.p.scratch. + (st_read): Don't check for end of file. + (st_read_done): Don't use u.p.scratch, use unit_truncate. + (hit_eof): New function. + * io/unit.c (init_units): Always init fbuf for formatted units. + (update_position): Use new stream interface. + (unit_truncate): New function. + (finish_last_advance_record): Use fbuf to print newline. + * io/unix.c: Remove unused SSIZE_MAX macro. + (BUFFER_SIZE): Make static const variable rather than macro. + (struct unix_stream): Remove dirty_offset, len, method, + small_buffer. Order elements by decreasing size. + (struct int_stream): Remove. + (move_pos_offset): Remove usage of dirty_offset. + (reset_stream): Remove. + (do_read): Rename to raw_read, update to match new stream + interface. + (do_write): Rename to raw_write, update to new stream interface. + (raw_seek): New function. + (raw_tell): New function. + (raw_truncate): New function. + (raw_close): New function. + (raw_flush): New function. + (raw_init): New function. + (fd_alloc): Remove. + (fd_alloc_r_at): Remove. + (fd_alloc_w_at): Remove. + (fd_sfree): Remove. + (fd_seek): Remove. + (fd_truncate): Remove. + (fd_sset): Remove. + (fd_read): Remove. + (fd_write): Remove. + (fd_close): Remove. + (fd_open): Remove. + (fd_flush): Rename to buf_flush, update to new stream interface + and unix_stream. + (buf_read): New function. + (buf_write): New function. + (buf_seek): New function. + (buf_tell): New function. + (buf_truncate): New function. + (buf_close): New function. + (buf_init): New function. + (mem_alloc_r_at): Rename to mem_alloc_r, change prototype. + (mem_alloc_w_at): Rename to mem_alloc_w, change prototype. + (mem_read): Change to match new stream interface. + (mem_write): Likewise. + (mem_seek): Likewise. + (mem_tell): Likewise. + (mem_truncate): Likewise. + (mem_close): Likewise. + (mem_flush): New function. + (mem_sfree): Remove. + (empty_internal_buffer): Cast to correct type. + (open_internal): Use correct type, init function pointers. + (fd_to_stream): Test whether to open file as buffered or raw. + (output_stream): Remove mode set. + (error_stream): Likewise. + (flush_all_units_1): Use new stream interface. + (flush_all_units): Likewise. + (stream_at_bof): Remove. + (stream_at_eof): Remove. + (file_position): Remove. + (file_length): Update logic to use stream interface. + (flush): Remove. + (stream_offset): Remove. + * io/write.c (write_utf8_char4): Use int instead of size_t. + (write_x): Extra safety check. + (namelist_write_newline): Use new stream interface. + 2009-03-29 John David Anglin <dave.anglin@nrc-cnrc.gc.ca> PR fortran/33595 diff --git a/libgfortran/io/fbuf.c b/libgfortran/io/fbuf.c index f2b1599ed87..a496365f928 100644 --- a/libgfortran/io/fbuf.c +++ b/libgfortran/io/fbuf.c @@ -33,8 +33,11 @@ Boston, MA 02110-1301, USA. */ #include <stdlib.h> +//#define FBUF_DEBUG + + void -fbuf_init (gfc_unit * u, size_t len) +fbuf_init (gfc_unit * u, int len) { if (len == 0) len = 512; /* Default size. */ @@ -42,14 +45,7 @@ fbuf_init (gfc_unit * u, size_t len) u->fbuf = get_mem (sizeof (fbuf)); u->fbuf->buf = get_mem (len); u->fbuf->len = len; - u->fbuf->act = u->fbuf->flushed = u->fbuf->pos = 0; -} - - -void -fbuf_reset (gfc_unit * u) -{ - u->fbuf->act = u->fbuf->flushed = u->fbuf->pos = 0; + u->fbuf->act = u->fbuf->pos = 0; } @@ -61,58 +57,79 @@ fbuf_destroy (gfc_unit * u) if (u->fbuf->buf) free_mem (u->fbuf->buf); free_mem (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. If the buffer is not big enough, there are - three cases to consider: - 1. If we haven't flushed anything, realloc - 2. If we have flushed enough that by discarding the flushed bytes - the request fits into the buffer, do that. - 3. Else allocate a new buffer, memcpy unflushed active bytes from old - buffer. */ + reallocating if necessary. */ char * -fbuf_alloc (gfc_unit * u, size_t len) +fbuf_alloc (gfc_unit * u, int len) { - size_t newlen; + int newlen; char *dest; + fbuf_debug (u, "fbuf_alloc len %d, ", len); if (u->fbuf->pos + len > u->fbuf->len) { - if (u->fbuf->flushed == 0) - { - /* Round up to nearest multiple of the current buffer length. */ - newlen = ((u->fbuf->pos + len) / u->fbuf->len + 1) * u->fbuf->len; - dest = realloc (u->fbuf->buf, newlen); - if (dest == NULL) - return NULL; - u->fbuf->buf = dest; - u->fbuf->len = newlen; - } - else if (u->fbuf->act - u->fbuf->flushed + len < u->fbuf->len) - { - memmove (u->fbuf->buf, u->fbuf->buf + u->fbuf->flushed, - u->fbuf->act - u->fbuf->flushed); - u->fbuf->act -= u->fbuf->flushed; - u->fbuf->pos -= u->fbuf->flushed; - u->fbuf->flushed = 0; - } - else - { - /* Most general case, flushed != 0, request doesn't fit. */ - newlen = ((u->fbuf->pos - u->fbuf->flushed + len) - / u->fbuf->len + 1) * u->fbuf->len; - dest = get_mem (newlen); - memcpy (dest, u->fbuf->buf + u->fbuf->flushed, - u->fbuf->act - u->fbuf->flushed); - u->fbuf->act -= u->fbuf->flushed; - u->fbuf->pos -= u->fbuf->flushed; - u->fbuf->flushed = 0; - u->fbuf->buf = dest; - u->fbuf->len = newlen; - } + /* Round up to nearest multiple of the current buffer length. */ + newlen = ((u->fbuf->pos + len) / u->fbuf->len + 1) * u->fbuf->len; + dest = realloc (u->fbuf->buf, newlen); + if (dest == NULL) + return NULL; + u->fbuf->buf = dest; + u->fbuf->len = newlen; } dest = u->fbuf->buf + u->fbuf->pos; @@ -123,42 +140,134 @@ fbuf_alloc (gfc_unit * u, size_t len) } - +/* 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, int record_done) +fbuf_flush (gfc_unit * u, unit_mode mode) { - int status; - size_t nbytes; + int nwritten; if (!u->fbuf) return 0; - if (u->fbuf->act - u->fbuf->flushed != 0) + + fbuf_debug (u, "fbuf_flush with mode %d: ", mode); + + if (mode == WRITING) { - if (record_done) - nbytes = u->fbuf->act - u->fbuf->flushed; - else - nbytes = u->fbuf->pos - u->fbuf->flushed; - status = swrite (u->s, u->fbuf->buf + u->fbuf->flushed, &nbytes); - u->fbuf->flushed += nbytes; + if (u->fbuf->pos > 0) + { + nwritten = swrite (u->s, u->fbuf->buf, u->fbuf->pos); + if (nwritten < 0) + return -1; + } } - else - status = 0; - if (record_done) - fbuf_reset (u); - return status; + /* 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; } int -fbuf_seek (gfc_unit * u, gfc_offset off) +fbuf_seek (gfc_unit * u, int off, int whence) { - gfc_offset pos = u->fbuf->pos + off; - /* Moving to the left past the flushed marked would imply moving past - the left tab limit, which is never allowed. So return error if - that is attempted. */ - if (pos < (gfc_offset) u->fbuf->flushed) + if (!u->fbuf) return -1; - u->fbuf->pos = pos; - return 0; + + 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; } diff --git a/libgfortran/io/file_pos.c b/libgfortran/io/file_pos.c index 4054b3a5bb1..ecee101b679 100644 --- a/libgfortran/io/file_pos.c +++ b/libgfortran/io/file_pos.c @@ -46,17 +46,17 @@ formatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) { gfc_offset base; char p[READ_CHUNK]; - size_t n; + ssize_t n; - base = file_position (u->s) - 1; + base = stell (u->s) - 1; do { n = (base < READ_CHUNK) ? base : READ_CHUNK; base -= n; - if (sseek (u->s, base) == FAILURE) + if (sseek (u->s, base, SEEK_SET) < 0) goto io_error; - if (sread (u->s, p, &n) != 0) + if (sread (u->s, p, n) != n) goto io_error; /* We have moved backwards from the current position, it should @@ -81,7 +81,7 @@ formatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) /* base is the new pointer. Seek to it exactly. */ done: - if (sseek (u->s, base) == FAILURE) + if (sseek (u->s, base, SEEK_SET) < 0) goto io_error; u->last_record--; u->endfile = NO_ENDFILE; @@ -100,10 +100,10 @@ formatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) static void unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) { - gfc_offset m, new; + gfc_offset m, slen; GFC_INTEGER_4 m4; GFC_INTEGER_8 m8; - size_t length; + ssize_t length; int continued; char p[sizeof (GFC_INTEGER_8)]; @@ -114,9 +114,10 @@ unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) do { - if (sseek (u->s, file_position (u->s) - length) == FAILURE) + slen = - (gfc_offset) length; + if (sseek (u->s, slen, SEEK_CUR) < 0) goto io_error; - if (sread (u->s, p, &length) != 0) + if (sread (u->s, p, length) != length) goto io_error; /* Only GFC_CONVERT_NATIVE and GFC_CONVERT_SWAP are valid here. */ @@ -164,10 +165,7 @@ unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) if (continued) m = -m; - if ((new = file_position (u->s) - m - 2*length) < 0) - new = 0; - - if (sseek (u->s, new) == FAILURE) + if (sseek (u->s, -m -2 * length, SEEK_CUR) < 0) goto io_error; } while (continued); @@ -206,15 +204,21 @@ st_backspace (st_parameter_filepos *fpp) goto done; } - if (u->flags.access == ACCESS_STREAM && u->flags.form == FORM_UNFORMATTED) - { - generate_error (&fpp->common, LIBERROR_OPTION_CONFLICT, - "Cannot BACKSPACE an unformatted stream file"); - goto done; - } + if (u->flags.access == ACCESS_STREAM && u->flags.form == FORM_UNFORMATTED) + { + generate_error (&fpp->common, LIBERROR_OPTION_CONFLICT, + "Cannot BACKSPACE an unformatted stream file"); + goto done; + } + + /* Make sure format buffer is flushed and reset. */ + if (u->flags.form == FORM_FORMATTED) + { + int pos = fbuf_reset (u); + if (pos != 0) + sseek (u->s, pos, SEEK_CUR); + } - /* Make sure format buffer is flushed. */ - fbuf_flush (u, 1); /* Check for special cases involving the ENDFILE record first. */ @@ -222,11 +226,11 @@ st_backspace (st_parameter_filepos *fpp) { u->endfile = AT_ENDFILE; u->flags.position = POSITION_APPEND; - flush (u->s); + sflush (u->s); } else { - if (file_position (u->s) == 0) + if (stell (u->s) == 0) { u->flags.position = POSITION_REWIND; goto done; /* Common special case */ @@ -243,8 +247,7 @@ st_backspace (st_parameter_filepos *fpp) u->previous_nonadvancing_write = 0; - flush (u->s); - struncate (u->s); + unit_truncate (u, stell (u->s), &fpp->common); u->mode = READING; } @@ -253,7 +256,7 @@ st_backspace (st_parameter_filepos *fpp) else unformatted_backspace (fpp, u); - update_position (u); + u->flags.position = POSITION_UNSPECIFIED; u->endfile = NO_ENDFILE; u->current_record = 0; u->bytes_left = 0; @@ -305,10 +308,10 @@ st_endfile (st_parameter_filepos *fpp) next_record (&dtp, 1); } - flush (u->s); - struncate (u->s); + unit_truncate (u, stell (u->s), &fpp->common); u->endfile = AFTER_ENDFILE; - update_position (u); + if (0 == stell (u->s)) + u->flags.position = POSITION_REWIND; done: unlock_unit (u); } @@ -347,14 +350,25 @@ st_rewind (st_parameter_filepos *fpp) written record is the last record in the file, so truncate the file now. Reset to read mode so two consecutive rewind statements do not delete the file contents. */ - flush (u->s); - if (u->mode == WRITING && u->flags.access != ACCESS_STREAM) - struncate (u->s); + if (u->mode == WRITING) + { + /* unit_truncate takes care of flushing. */ + unit_truncate (u, stell (u->s), &fpp->common); + /* .. but we still need to reset since we're going to seek. */ + fbuf_reset (u); + } + else + { + /* Make sure buffers are reset. */ + if (u->flags.form == FORM_FORMATTED) + fbuf_reset (u); + sflush (u->s); + } u->mode = READING; u->last_record = 0; - if (file_position (u->s) != 0 && sseek (u->s, 0) == FAILURE) + if (sseek (u->s, 0, SEEK_SET) < 0) generate_error (&fpp->common, LIBERROR_OS, NULL); /* Handle special files like /dev/null differently. */ @@ -366,7 +380,7 @@ st_rewind (st_parameter_filepos *fpp) else { /* Set this for compatibilty with g77 for /dev/null. */ - if (file_length (u->s) == 0 && file_position (u->s) == 0) + if (file_length (u->s) == 0 && stell (u->s) == 0) u->endfile = AT_ENDFILE; /* Future refinements on special files can go here. */ } @@ -397,7 +411,11 @@ st_flush (st_parameter_filepos *fpp) u = find_unit (fpp->common.unit); if (u != NULL) { - flush (u->s); + /* Make sure format buffer is flushed. */ + if (u->flags.form == FORM_FORMATTED) + fbuf_flush (u, u->mode); + + sflush (u->s); unlock_unit (u); } else diff --git a/libgfortran/io/format.c b/libgfortran/io/format.c index 667797fd1c0..4e654aec6b5 100644 --- a/libgfortran/io/format.c +++ b/libgfortran/io/format.c @@ -36,6 +36,7 @@ Boston, MA 02110-1301, USA. */ #include "io.h" #include <ctype.h> #include <string.h> +#include <stdbool.h> #define FARRAY_SIZE 64 @@ -63,7 +64,7 @@ format_data; static const fnode colon_node = { FMT_COLON, 0, NULL, NULL, {{ 0, 0, 0 }}, 0, NULL }; -/* Error messages */ +/* Error messages. */ static const char posint_required[] = "Positive width required in format", period_required[] = "Period required in format", @@ -75,6 +76,129 @@ static const char posint_required[] = "Positive width required in format", reversion_error[] = "Exhausted data descriptors in format", zero_width[] = "Zero width in format descriptor"; +/* The following routines support caching format data from parsed format strings + into a hash table. This avoids repeatedly parsing duplicate format strings + or format strings in I/O statements that are repeated in loops. */ + + +/* Traverse the table and free all data. */ + +void +free_format_hash_table (gfc_unit *u) +{ + size_t i; + + /* free_format_data handles any NULL pointers. */ + for (i = 0; i < FORMAT_HASH_SIZE; i++) + { + if (u->format_hash_table[i].hashed_fmt != NULL) + free_format_data (u->format_hash_table[i].hashed_fmt); + u->format_hash_table[i].hashed_fmt = NULL; + } +} + +/* Traverse the format_data structure and reset the fnode counters. */ + +static void +reset_node (fnode *fn) +{ + fnode *f; + + fn->count = 0; + fn->current = NULL; + + if (fn->format != FMT_LPAREN) + return; + + for (f = fn->u.child; f; f = f->next) + { + if (f->format == FMT_RPAREN) + break; + reset_node (f); + } +} + +static void +reset_fnode_counters (st_parameter_dt *dtp) +{ + fnode *f; + format_data *fmt; + + fmt = dtp->u.p.fmt; + + /* Clear this pointer at the head so things start at the right place. */ + fmt->array.array[0].current = NULL; + + for (f = fmt->last->array[0].u.child; f; f = f->next) + reset_node (f); +} + + +/* A simple hashing function to generate an index into the hash table. */ + +static inline +uint32_t format_hash (st_parameter_dt *dtp) +{ + char *key; + size_t key_len; + uint32_t hash = 0; + size_t i; + + /* Hash the format string. Super simple, but what the heck! */ + key = dtp->format; + key_len = dtp->format_len; + for (i = 0; i < key_len; i++) + hash ^= key[i]; + hash &= (FORMAT_HASH_SIZE - 1); + return hash; +} + + +static void +save_parsed_format (st_parameter_dt *dtp) +{ + uint32_t hash; + gfc_unit *u; + + hash = format_hash (dtp); + u = dtp->u.p.current_unit; + + /* Index into the hash table. We are simply replacing whatever is there + relying on probability. */ + if (u->format_hash_table[hash].hashed_fmt != NULL) + free_format_data (u->format_hash_table[hash].hashed_fmt); + u->format_hash_table[hash].hashed_fmt = NULL; + + u->format_hash_table[hash].key = dtp->format; + u->format_hash_table[hash].key_len = dtp->format_len; + u->format_hash_table[hash].hashed_fmt = dtp->u.p.fmt; +} + + +static format_data * +find_parsed_format (st_parameter_dt *dtp) +{ + uint32_t hash; + gfc_unit *u; + + hash = format_hash (dtp); + u = dtp->u.p.current_unit; + + if (u->format_hash_table[hash].key != NULL) + { + /* See if it matches. */ + if (u->format_hash_table[hash].key_len == dtp->format_len) + { + /* So far so good. */ + if (strncmp (u->format_hash_table[hash].key, + dtp->format, dtp->format_len) == 0) + return u->format_hash_table[hash].hashed_fmt; + } + } + return NULL; +} + + /* next_char()-- Return the next character in the format string. * Returns -1 when the string is done. If the literal flag is set, * spaces are significant, otherwise they are not. */ @@ -90,7 +214,8 @@ next_char (format_data *fmt, int literal) return -1; fmt->format_string_len--; - fmt->error_element = c = toupper (*fmt->format_string++); + c = toupper (*fmt->format_string++); + fmt->error_element = c; } while ((c == ' ' || c == '\t') && !literal); @@ -141,10 +266,10 @@ get_fnode (format_data *fmt, fnode **head, fnode **tail, format_token t) /* free_format_data()-- Free all allocated format data. */ void -free_format_data (st_parameter_dt *dtp) +free_format_data (format_data *fmt) { fnode_array *fa, *fa_next; - format_data *fmt = dtp->u.p.fmt; + if (fmt == NULL) return; @@ -156,7 +281,7 @@ free_format_data (st_parameter_dt *dtp) } free_mem (fmt); - dtp->u.p.fmt = NULL; + fmt = NULL; } @@ -184,6 +309,14 @@ format_lex (format_data *fmt) switch (c) { + case '(': + token = FMT_LPAREN; + break; + + case ')': + token = FMT_RPAREN; + break; + case '-': negative_flag = 1; /* Fall Through */ @@ -276,14 +409,6 @@ format_lex (format_data *fmt) break; - case '(': - token = FMT_LPAREN; - break; - - case ')': - token = FMT_RPAREN; - break; - case 'X': token = FMT_X; break; @@ -455,8 +580,10 @@ parse_format_list (st_parameter_dt *dtp) format_token t, u, t2; int repeat; format_data *fmt = dtp->u.p.fmt; + bool save_format; head = tail = NULL; + save_format = !is_internal_unit (dtp); /* Get the next format item */ format_item: @@ -567,6 +694,7 @@ parse_format_list (st_parameter_dt *dtp) case FMT_DP: notify_std (&dtp->common, GFC_STD_F2003, "Fortran 2003: DC or DP " "descriptor not allowed"); + save_format = true; /* Fall through. */ case FMT_S: case FMT_SS: @@ -592,6 +720,7 @@ parse_format_list (st_parameter_dt *dtp) get_fnode (fmt, &head, &tail, FMT_DOLLAR); tail->repeat = 1; notify_std (&dtp->common, GFC_STD_GNU, "Extension: $ descriptor"); + save_format = false; goto between_desc; @@ -689,6 +818,7 @@ parse_format_list (st_parameter_dt *dtp) fmt->saved_token = t; fmt->value = 1; /* Default width */ notify_std (&dtp->common, GFC_STD_GNU, posint_required); + save_format = false; } } @@ -999,6 +1129,33 @@ format_error (st_parameter_dt *dtp, const fnode *f, const char *message) } +/* revert()-- Do reversion of the format. Control reverts to the left + * parenthesis that matches the rightmost right parenthesis. From our + * tree structure, we are looking for the rightmost parenthesis node + * at the second level, the first level always being a single + * parenthesis node. If this node doesn't exit, we use the top + * level. */ + +static void +revert (st_parameter_dt *dtp) +{ + fnode *f, *r; + format_data *fmt = dtp->u.p.fmt; + + dtp->u.p.reversion_flag = 1; + + r = NULL; + + for (f = fmt->array.array[0].u.child; f; f = f->next) + if (f->format == FMT_LPAREN) + r = f; + + /* If r is NULL because no node was found, the whole tree will be used */ + + fmt->array.array[0].current = r; + fmt->array.array[0].count = 0; +} + /* parse_format()-- Parse a format string. */ void @@ -1006,6 +1163,21 @@ parse_format (st_parameter_dt *dtp) { format_data *fmt; + /* Lookup format string to see if it has already been parsed. */ + + dtp->u.p.fmt = find_parsed_format (dtp); + + if (dtp->u.p.fmt != NULL) + { + dtp->u.p.fmt->reversion_ok = 0; + dtp->u.p.fmt->saved_token = FMT_NONE; + dtp->u.p.fmt->saved_format = NULL; + reset_fnode_counters (dtp); + return; + } + + /* Not found so proceed as follows. */ + dtp->u.p.fmt = fmt = get_mem (sizeof (format_data)); fmt->format_string = dtp->format; fmt->format_string_len = dtp->format_len; @@ -1037,35 +1209,12 @@ parse_format (st_parameter_dt *dtp) fmt->error = "Missing initial left parenthesis in format"; if (fmt->error) - format_error (dtp, NULL, fmt->error); -} - - -/* revert()-- Do reversion of the format. Control reverts to the left - * parenthesis that matches the rightmost right parenthesis. From our - * tree structure, we are looking for the rightmost parenthesis node - * at the second level, the first level always being a single - * parenthesis node. If this node doesn't exit, we use the top - * level. */ - -static void -revert (st_parameter_dt *dtp) -{ - fnode *f, *r; - format_data *fmt = dtp->u.p.fmt; - - dtp->u.p.reversion_flag = 1; - - r = NULL; - - for (f = fmt->array.array[0].u.child; f; f = f->next) - if (f->format == FMT_LPAREN) - r = f; - - /* If r is NULL because no node was found, the whole tree will be used */ - - fmt->array.array[0].current = r; - fmt->array.array[0].count = 0; + { + format_error (dtp, NULL, fmt->error); + free_format_hash_table (dtp->u.p.current_unit); + return; + } + save_parsed_format (dtp); } diff --git a/libgfortran/io/intrinsics.c b/libgfortran/io/intrinsics.c index 03493cf0625..5e8283b2096 100644 --- a/libgfortran/io/intrinsics.c +++ b/libgfortran/io/intrinsics.c @@ -54,13 +54,13 @@ PREFIX(fgetc) (const int * unit, char * c, gfc_charlen_type c_len) s = 1; memset (c, ' ', c_len); - ret = sread (u->s, c, &s); + ret = sread (u->s, c, s); unlock_unit (u); - if (ret != 0) + if (ret < 0) return ret; - if (s != 1) + if (ret != 1) return -1; else return 0; @@ -119,17 +119,17 @@ int PREFIX(fputc) (const int * unit, char * c, gfc_charlen_type c_len __attribute__((unused))) { - size_t s; - int ret; + ssize_t s; gfc_unit * u = find_unit (*unit); if (u == NULL) return -1; - s = 1; - ret = swrite (u->s, c, &s); + s = swrite (u->s, c, 1); unlock_unit (u); - return ret; + if (s < 0) + return -1; + return 0; } @@ -196,7 +196,7 @@ flush_i4 (GFC_INTEGER_4 *unit) us = find_unit (*unit); if (us != NULL) { - flush (us->s); + sflush (us->s); unlock_unit (us); } } @@ -219,7 +219,7 @@ flush_i8 (GFC_INTEGER_8 *unit) us = find_unit (*unit); if (us != NULL) { - flush (us->s); + sflush (us->s); unlock_unit (us); } } @@ -234,22 +234,17 @@ void fseek_sub (int * unit, GFC_IO_INT * offset, int * whence, int * status) { gfc_unit * u = find_unit (*unit); - try result = FAILURE; + ssize_t result = -1; if (u != NULL && is_seekable(u->s)) { - if (*whence == 0) - result = sseek(u->s, *offset); /* SEEK_SET */ - else if (*whence == 1) - result = sseek(u->s, file_position(u->s) + *offset); /* SEEK_CUR */ - else if (*whence == 2) - result = sseek(u->s, file_length(u->s) + *offset); /* SEEK_END */ + result = sseek(u->s, *offset, *whence); unlock_unit (u); } if (status) - *status = (result == FAILURE ? -1 : 0); + *status = (result < 0 ? -1 : 0); } @@ -266,7 +261,7 @@ PREFIX(ftell) (int * unit) size_t ret; if (u == NULL) return ((size_t) -1); - ret = (size_t) stream_offset (u->s); + ret = (size_t) stell (u->s); unlock_unit (u); return ret; } @@ -282,7 +277,7 @@ PREFIX(ftell) (int * unit) *offset = -1; \ else \ { \ - *offset = stream_offset (u->s); \ + *offset = stell (u->s); \ unlock_unit (u); \ } \ } diff --git a/libgfortran/io/io.h b/libgfortran/io/io.h index 1993158ef58..0be480e0738 100644 --- a/libgfortran/io/io.h +++ b/libgfortran/io/io.h @@ -49,34 +49,59 @@ struct st_parameter_dt; typedef struct stream { - char *(*alloc_w_at) (struct stream *, int *); - try (*sfree) (struct stream *); - try (*close) (struct stream *); - try (*seek) (struct stream *, gfc_offset); - try (*trunc) (struct stream *); - int (*read) (struct stream *, void *, size_t *); - int (*write) (struct stream *, const void *, size_t *); - try (*set) (struct stream *, int, size_t); + ssize_t (*read) (struct stream *, void *, ssize_t); + ssize_t (*write) (struct stream *, const void *, ssize_t); + off_t (*seek) (struct stream *, off_t, int); + off_t (*tell) (struct stream *); + int (*truncate) (struct stream *, off_t); + int (*flush) (struct stream *); + int (*close) (struct stream *); } stream; -typedef enum -{ SYNC_BUFFERED, SYNC_UNBUFFERED, ASYNC } -io_mode; +/* Inline functions for doing file I/O given a stream. */ +static inline ssize_t +sread (stream * s, void * buf, ssize_t nbyte) +{ + return s->read (s, buf, nbyte); +} -/* Macros for doing file I/O given a stream. */ +static inline ssize_t +swrite (stream * s, const void * buf, ssize_t nbyte) +{ + return s->write (s, buf, nbyte); +} -#define sfree(s) ((s)->sfree)(s) -#define sclose(s) ((s)->close)(s) +static inline off_t +sseek (stream * s, off_t offset, int whence) +{ + return s->seek (s, offset, whence); +} -#define salloc_w(s, len) ((s)->alloc_w_at)(s, len) +static inline off_t +stell (stream * s) +{ + return s->tell (s); +} -#define sseek(s, pos) ((s)->seek)(s, pos) -#define struncate(s) ((s)->trunc)(s) -#define sread(s, buf, nbytes) ((s)->read)(s, buf, nbytes) -#define swrite(s, buf, nbytes) ((s)->write)(s, buf, nbytes) +static inline int +struncate (stream * s, off_t length) +{ + return s->truncate (s, length); +} + +static inline int +sflush (stream * s) +{ + return s->flush (s); +} + +static inline int +sclose (stream * s) +{ + return s->close (s); +} -#define sset(s, c, n) ((s)->set)(s, c, n) /* Macros for testing what kinds of I/O we are doing. */ @@ -106,6 +131,18 @@ typedef struct array_loop_spec } array_loop_spec; +/* A stucture to build a hash table for format data. */ + +#define FORMAT_HASH_SIZE 16 + +typedef struct format_hash_entry +{ + char *key; + gfc_charlen_type key_len; + struct format_data *hashed_fmt; +} +format_hash_entry; + /* Representation of a namelist object in libgfortran Namelist Records @@ -127,7 +164,6 @@ array_loop_spec; typedef struct namelist_type { - /* Object type, stored as GFC_DTYPE_xxxx. */ bt type; @@ -538,10 +574,9 @@ unit_flags; typedef struct fbuf { char *buf; /* Start of buffer. */ - size_t len; /* Length of buffer. */ - size_t act; /* Active bytes in buffer. */ - size_t flushed; /* Flushed bytes from beginning of buffer. */ - size_t pos; /* Current position in buffer. */ + int len; /* Length of buffer. */ + int act; /* Active bytes in buffer. */ + int pos; /* Current position in buffer. */ } fbuf; @@ -599,6 +634,9 @@ typedef struct gfc_unit int file_len; char *file; + + /* The format hash table. */ + struct format_hash_entry format_hash_table[FORMAT_HASH_SIZE]; /* Formatting buffer. */ struct fbuf *fbuf; @@ -683,6 +721,12 @@ internal_proto(open_external); extern stream *open_internal (char *, int, gfc_offset); internal_proto(open_internal); +extern char * mem_alloc_w (stream *, int *); +internal_proto(mem_alloc_w); + +extern char * mem_alloc_r (stream *, int *); +internal_proto(mem_alloc_w); + extern stream *input_stream (void); internal_proto(input_stream); @@ -698,12 +742,6 @@ internal_proto(compare_file_filename); extern gfc_unit *find_file (const char *file, gfc_charlen_type file_len); internal_proto(find_file); -extern int stream_at_bof (stream *); -internal_proto(stream_at_bof); - -extern int stream_at_eof (stream *); -internal_proto(stream_at_eof); - extern int delete_file (gfc_unit *); internal_proto(delete_file); @@ -734,9 +772,6 @@ internal_proto(inquire_readwrite); extern gfc_offset file_length (stream *); internal_proto(file_length); -extern gfc_offset file_position (stream *); -internal_proto(file_position); - extern int is_seekable (stream *); internal_proto(is_seekable); @@ -752,18 +787,12 @@ internal_proto(flush_if_preconnected); extern void empty_internal_buffer(stream *); internal_proto(empty_internal_buffer); -extern try flush (stream *); -internal_proto(flush); - extern int stream_isatty (stream *); internal_proto(stream_isatty); extern char * stream_ttyname (stream *); internal_proto(stream_ttyname); -extern gfc_offset stream_offset (stream *s); -internal_proto(stream_offset); - extern int unpack_filename (char *, const char *, int); internal_proto(unpack_filename); @@ -807,6 +836,9 @@ internal_proto(update_position); extern void finish_last_advance_record (gfc_unit *u); internal_proto (finish_last_advance_record); +extern int unit_truncate (gfc_unit *, gfc_offset, st_parameter_common *); +internal_proto (unit_truncate); + /* open.c */ extern gfc_unit *new_unit (st_parameter_open *, gfc_unit *, unit_flags *); @@ -826,9 +858,18 @@ internal_proto(unget_format); extern void format_error (st_parameter_dt *, const fnode *, const char *); internal_proto(format_error); -extern void free_format_data (st_parameter_dt *); +extern void free_format_data (struct format_data *); internal_proto(free_format_data); +extern void free_format_hash_table (gfc_unit *); +internal_proto(free_format_hash_table); + +extern void init_format_hash (st_parameter_dt *); +internal_proto(init_format_hash); + +extern void free_format_hash (st_parameter_dt *); +internal_proto(free_format_hash); + /* transfer.c */ #define SCRATCH_SIZE 300 @@ -836,7 +877,7 @@ internal_proto(free_format_data); extern const char *type_name (bt); internal_proto(type_name); -extern try read_block_form (st_parameter_dt *, void *, size_t *); +extern void * read_block_form (st_parameter_dt *, int *); internal_proto(read_block_form); extern char *read_sf (st_parameter_dt *, int *, int); @@ -862,6 +903,9 @@ internal_proto (reverse_memcpy); extern void st_wait (st_parameter_wait *); export_proto(st_wait); +extern void hit_eof (st_parameter_dt *); +internal_proto(hit_eof); + /* read.c */ extern void set_integer (void *, GFC_INTEGER_LARGEST, int); @@ -968,24 +1012,39 @@ extern size_t size_from_complex_kind (int); internal_proto(size_from_complex_kind); /* fbuf.c */ -extern void fbuf_init (gfc_unit *, size_t); +extern void fbuf_init (gfc_unit *, int); internal_proto(fbuf_init); extern void fbuf_destroy (gfc_unit *); internal_proto(fbuf_destroy); -extern void fbuf_reset (gfc_unit *); +extern int fbuf_reset (gfc_unit *); internal_proto(fbuf_reset); -extern char * fbuf_alloc (gfc_unit *, size_t); +extern char * fbuf_alloc (gfc_unit *, int); internal_proto(fbuf_alloc); -extern int fbuf_flush (gfc_unit *, int); +extern int fbuf_flush (gfc_unit *, unit_mode); internal_proto(fbuf_flush); -extern int fbuf_seek (gfc_unit *, gfc_offset); +extern int fbuf_seek (gfc_unit *, int, int); internal_proto(fbuf_seek); +extern char * fbuf_read (gfc_unit *, int *); +internal_proto(fbuf_read); + +/* Never call this function, only use fbuf_getc(). */ +extern int fbuf_getc_refill (gfc_unit *); +internal_proto(fbuf_getc_refill); + +static inline int +fbuf_getc (gfc_unit * u) +{ + if (u->fbuf->pos < u->fbuf->act) + return (unsigned char) u->fbuf->buf[u->fbuf->pos++]; + return fbuf_getc_refill (u); +} + /* lock.c */ extern void free_ionml (st_parameter_dt *); internal_proto(free_ionml); diff --git a/libgfortran/io/list_read.c b/libgfortran/io/list_read.c index 6b22d34a0b6..1637957245e 100644 --- a/libgfortran/io/list_read.c +++ b/libgfortran/io/list_read.c @@ -33,6 +33,7 @@ Boston, MA 02110-1301, USA. */ #include "io.h" #include <string.h> +#include <stdlib.h> #include <ctype.h> @@ -79,9 +80,8 @@ push_char (st_parameter_dt *dtp, char c) if (dtp->u.p.saved_string == NULL) { - if (dtp->u.p.scratch == NULL) - dtp->u.p.scratch = get_mem (SCRATCH_SIZE); - dtp->u.p.saved_string = dtp->u.p.scratch; + dtp->u.p.saved_string = get_mem (SCRATCH_SIZE); + // memset below should be commented out. memset (dtp->u.p.saved_string, 0, SCRATCH_SIZE); dtp->u.p.saved_length = SCRATCH_SIZE; dtp->u.p.saved_used = 0; @@ -90,15 +90,15 @@ push_char (st_parameter_dt *dtp, char c) if (dtp->u.p.saved_used >= dtp->u.p.saved_length) { dtp->u.p.saved_length = 2 * dtp->u.p.saved_length; - new = get_mem (2 * dtp->u.p.saved_length); - - memset (new, 0, 2 * dtp->u.p.saved_length); - - memcpy (new, dtp->u.p.saved_string, dtp->u.p.saved_used); - if (dtp->u.p.saved_string != dtp->u.p.scratch) - free_mem (dtp->u.p.saved_string); - + new = realloc (dtp->u.p.saved_string, dtp->u.p.saved_length); + if (new == NULL) + generate_error (&dtp->common, LIBERROR_OS, NULL); dtp->u.p.saved_string = new; + + // Also this should not be necessary. + memset (new + dtp->u.p.saved_used, 0, + dtp->u.p.saved_length - dtp->u.p.saved_used); + } dtp->u.p.saved_string[dtp->u.p.saved_used++] = c; @@ -113,8 +113,7 @@ free_saved (st_parameter_dt *dtp) if (dtp->u.p.saved_string == NULL) return; - if (dtp->u.p.saved_string != dtp->u.p.scratch) - free_mem (dtp->u.p.saved_string); + free_mem (dtp->u.p.saved_string); dtp->u.p.saved_string = NULL; dtp->u.p.saved_used = 0; @@ -140,9 +139,10 @@ free_line (st_parameter_dt *dtp) static char next_char (st_parameter_dt *dtp) { - size_t length; + ssize_t length; gfc_offset record; char c; + int cc; if (dtp->u.p.last_char != '\0') { @@ -194,7 +194,7 @@ next_char (st_parameter_dt *dtp) } record *= dtp->u.p.current_unit->recl; - if (sseek (dtp->u.p.current_unit->s, record) == FAILURE) + if (sseek (dtp->u.p.current_unit->s, record, SEEK_SET) < 0) longjmp (*dtp->u.p.eof_jump, 1); dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl; @@ -204,19 +204,15 @@ next_char (st_parameter_dt *dtp) /* Get the next character and handle end-of-record conditions. */ - length = 1; - - if (sread (dtp->u.p.current_unit->s, &c, &length) != 0) - { - generate_error (&dtp->common, LIBERROR_OS, NULL); - return '\0'; - } - - if (is_stream_io (dtp) && length == 1) - dtp->u.p.current_unit->strm_pos++; - if (is_internal_unit (dtp)) { + length = sread (dtp->u.p.current_unit->s, &c, 1); + if (length < 0) + { + generate_error (&dtp->common, LIBERROR_OS, NULL); + return '\0'; + } + if (is_array_io (dtp)) { /* Check whether we hit EOF. */ @@ -240,13 +236,20 @@ next_char (st_parameter_dt *dtp) } else { - if (length == 0) + cc = fbuf_getc (dtp->u.p.current_unit); + + if (cc == EOF) { if (dtp->u.p.current_unit->endfile == AT_ENDFILE) longjmp (*dtp->u.p.eof_jump, 1); dtp->u.p.current_unit->endfile = AT_ENDFILE; c = '\n'; } + else + c = (char) cc; + if (is_stream_io (dtp) && cc != EOF) + dtp->u.p.current_unit->strm_pos++; + } done: dtp->u.p.at_eol = (c == '\n' || c == '\r'); @@ -1698,7 +1701,7 @@ list_formatted_read_scalar (st_parameter_dt *dtp, volatile bt type, void *p, dtp->u.p.input_complete = 0; dtp->u.p.repeat_count = 1; dtp->u.p.at_eol = 0; - + c = eat_spaces (dtp); if (is_separator (c)) { @@ -1726,6 +1729,9 @@ list_formatted_read_scalar (st_parameter_dt *dtp, volatile bt type, void *p, return; goto set_value; } + + if (dtp->u.p.input_complete) + goto cleanup; if (dtp->u.p.input_complete) goto cleanup; @@ -1853,6 +1859,8 @@ finish_list_read (st_parameter_dt *dtp) free_saved (dtp); + fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode); + if (dtp->u.p.at_eol) { dtp->u.p.at_eol = 0; @@ -2261,8 +2269,8 @@ nml_query (st_parameter_dt *dtp, char c) /* Flush the stream to force immediate output. */ - fbuf_flush (dtp->u.p.current_unit, 1); - flush (dtp->u.p.current_unit->s); + fbuf_flush (dtp->u.p.current_unit, WRITING); + sflush (dtp->u.p.current_unit->s); unlock_unit (dtp->u.p.current_unit); } @@ -2903,7 +2911,7 @@ find_nml_name: st_printf ("%s\n", nml_err_msg); if (u != NULL) { - flush (u->s); + sflush (u->s); unlock_unit (u); } } diff --git a/libgfortran/io/open.c b/libgfortran/io/open.c index 4a78efa01fc..7caa1c9ffdc 100644 --- a/libgfortran/io/open.c +++ b/libgfortran/io/open.c @@ -155,7 +155,7 @@ static const st_option async_opt[] = static void test_endfile (gfc_unit * u) { - if (u->endfile == NO_ENDFILE && file_length (u->s) == file_position (u->s)) + if (u->endfile == NO_ENDFILE && file_length (u->s) == stell (u->s)) u->endfile = AT_ENDFILE; } @@ -271,7 +271,7 @@ edit_modes (st_parameter_open *opp, gfc_unit * u, unit_flags * flags) break; case POSITION_REWIND: - if (sseek (u->s, 0) == FAILURE) + if (sseek (u->s, 0, SEEK_SET) != 0) goto seek_error; u->current_record = 0; @@ -281,7 +281,7 @@ edit_modes (st_parameter_open *opp, gfc_unit * u, unit_flags * flags) break; case POSITION_APPEND: - if (sseek (u->s, file_length (u->s)) == FAILURE) + if (sseek (u->s, 0, SEEK_END) < 0) goto seek_error; if (flags->access != ACCESS_STREAM) @@ -557,7 +557,7 @@ new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags) if (flags->position == POSITION_APPEND) { - if (sseek (u->s, file_length (u->s)) == FAILURE) + if (sseek (u->s, 0, SEEK_END) < 0) generate_error (&opp->common, LIBERROR_OS, NULL); u->endfile = AT_ENDFILE; } @@ -611,7 +611,8 @@ new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags) { u->maxrec = max_offset; u->recl = 1; - u->strm_pos = file_position (u->s) + 1; + u->bytes_left = 1; + u->strm_pos = stell (u->s) + 1; } memmove (u->file, opp->file, opp->file_len); @@ -627,7 +628,7 @@ new_unit (st_parameter_open *opp, gfc_unit *u, unit_flags * flags) if (flags->status == STATUS_SCRATCH && opp->file != NULL) free_mem (opp->file); - if (flags->form == FORM_FORMATTED && (flags->action != ACTION_READ)) + if (flags->form == FORM_FORMATTED) { if ((opp->common.flags & IOPARM_OPEN_HAS_RECL_IN)) fbuf_init (u, u->recl); diff --git a/libgfortran/io/read.c b/libgfortran/io/read.c index a8ae3d73f53..b651665944f 100644 --- a/libgfortran/io/read.c +++ b/libgfortran/io/read.c @@ -33,6 +33,7 @@ Boston, MA 02110-1301, USA. */ #include <errno.h> #include <ctype.h> #include <stdlib.h> +#include <assert.h> typedef unsigned char uchar; @@ -141,38 +142,30 @@ convert_real (st_parameter_dt *dtp, void *dest, const char *buffer, int length) switch (length) { case 4: - { - GFC_REAL_4 tmp = + *((GFC_REAL_4*) dest) = #if defined(HAVE_STRTOF) - strtof (buffer, NULL); + strtof (buffer, NULL); #else - (GFC_REAL_4) strtod (buffer, NULL); + (GFC_REAL_4) strtod (buffer, NULL); #endif - memcpy (dest, (void *) &tmp, length); - } break; + case 8: - { - GFC_REAL_8 tmp = strtod (buffer, NULL); - memcpy (dest, (void *) &tmp, length); - } + *((GFC_REAL_8*) dest) = strtod (buffer, NULL); break; + #if defined(HAVE_GFC_REAL_10) && defined (HAVE_STRTOLD) case 10: - { - GFC_REAL_10 tmp = strtold (buffer, NULL); - memcpy (dest, (void *) &tmp, length); - } + *((GFC_REAL_10*) dest) = strtold (buffer, NULL); break; #endif + #if defined(HAVE_GFC_REAL_16) && defined (HAVE_STRTOLD) case 16: - { - GFC_REAL_16 tmp = strtold (buffer, NULL); - memcpy (dest, (void *) &tmp, length); - } + *((GFC_REAL_16*) dest) = strtold (buffer, NULL); break; #endif + default: internal_error (&dtp->common, "Unsupported real kind during IO"); } @@ -195,13 +188,13 @@ void read_l (st_parameter_dt *dtp, const fnode *f, char *dest, int length) { char *p; - size_t w; + int w; w = f->u.w; - p = gfc_alloca (w); + p = read_block_form (dtp, &w); - if (read_block_form (dtp, p, &w) == FAILURE) + if (p == NULL) return; while (*p == ' ') @@ -238,28 +231,26 @@ read_l (st_parameter_dt *dtp, const fnode *f, char *dest, int length) } -static inline gfc_char4_t -read_utf8 (st_parameter_dt *dtp, size_t *nbytes) +static gfc_char4_t +read_utf8 (st_parameter_dt *dtp, int *nbytes) { static const uchar masks[6] = { 0x7F, 0x1F, 0x0F, 0x07, 0x02, 0x01 }; static const uchar patns[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - static uchar buffer[6]; - size_t i, nb, nread; + int i, nb, nread; gfc_char4_t c; - int status; char *s; *nbytes = 1; - s = (char *) &buffer[0]; - status = read_block_form (dtp, s, nbytes); - if (status == FAILURE) + + s = read_block_form (dtp, nbytes); + if (s == NULL) return 0; /* If this is a short read, just return. */ if (*nbytes == 0) return 0; - c = buffer[0]; + c = (uchar) s[0]; if (c < 0x80) return c; @@ -274,9 +265,8 @@ read_utf8 (st_parameter_dt *dtp, size_t *nbytes) c = (c & masks[nb-1]); nread = nb - 1; - s = (char *) &buffer[1]; - status = read_block_form (dtp, s, &nread); - if (status == FAILURE) + s = read_block_form (dtp, &nread); + if (s == NULL) return 0; /* Decode the bytes read. */ for (i = 1; i < nb; i++) @@ -309,14 +299,14 @@ read_utf8 (st_parameter_dt *dtp, size_t *nbytes) static void -read_utf8_char1 (st_parameter_dt *dtp, char *p, int len, size_t width) +read_utf8_char1 (st_parameter_dt *dtp, char *p, int len, int width) { gfc_char4_t c; char *dest; - size_t nbytes; + int nbytes; int i, j; - len = ((int) width < len) ? len : (int) width; + len = (width < len) ? len : width; dest = (char *) p; @@ -339,21 +329,19 @@ read_utf8_char1 (st_parameter_dt *dtp, char *p, int len, size_t width) } static void -read_default_char1 (st_parameter_dt *dtp, char *p, int len, size_t width) +read_default_char1 (st_parameter_dt *dtp, char *p, int len, int width) { char *s; - int m, n, status; + int m, n; - s = gfc_alloca (width); - - status = read_block_form (dtp, s, &width); + s = read_block_form (dtp, &width); - if (status == FAILURE) + if (s == NULL) return; - if (width > (size_t) len) + if (width > len) s += (width - len); - m = ((int) width > len) ? len : (int) width; + m = (width > len) ? len : width; memcpy (p, s, m); n = len - width; @@ -363,13 +351,13 @@ read_default_char1 (st_parameter_dt *dtp, char *p, int len, size_t width) static void -read_utf8_char4 (st_parameter_dt *dtp, void *p, int len, size_t width) +read_utf8_char4 (st_parameter_dt *dtp, void *p, int len, int width) { gfc_char4_t *dest; - size_t nbytes; + int nbytes; int i, j; - len = ((int) width < len) ? len : (int) width; + len = (width < len) ? len : width; dest = (gfc_char4_t *) p; @@ -391,19 +379,17 @@ read_utf8_char4 (st_parameter_dt *dtp, void *p, int len, size_t width) static void -read_default_char4 (st_parameter_dt *dtp, char *p, int len, size_t width) +read_default_char4 (st_parameter_dt *dtp, char *p, int len, int width) { char *s; gfc_char4_t *dest; - int m, n, status; - - s = gfc_alloca (width); + int m, n; - status = read_block_form (dtp, s, &width); + s = read_block_form (dtp, &width); - if (status == FAILURE) + if (s == NULL) return; - if (width > (size_t) len) + if (width > len) s += (width - len); m = ((int) width > len) ? len : (int) width; @@ -425,7 +411,7 @@ void read_a (st_parameter_dt *dtp, const fnode *f, char *p, int length) { int wi; - size_t w; + int w; wi = f->u.w; if (wi == -1) /* '(A)' edit descriptor */ @@ -451,13 +437,11 @@ read_a (st_parameter_dt *dtp, const fnode *f, char *p, int length) void read_a_char4 (st_parameter_dt *dtp, const fnode *f, char *p, int length) { - int wi; - size_t w; + int w; - wi = f->u.w; - if (wi == -1) /* '(A)' edit descriptor */ - wi = length; - w = wi; + w = f->u.w; + if (w == -1) /* '(A)' edit descriptor */ + w = length; /* Read in w characters, treating comma as not a separator. */ dtp->u.p.sf_read_comma = 0; @@ -532,18 +516,15 @@ read_decimal (st_parameter_dt *dtp, const fnode *f, char *dest, int length) GFC_UINTEGER_LARGEST value, maxv, maxv_10; GFC_INTEGER_LARGEST v; int w, negative; - size_t wu; char c, *p; - wu = f->u.w; + w = f->u.w; - p = gfc_alloca (wu); + p = read_block_form (dtp, &w); - if (read_block_form (dtp, p, &wu) == FAILURE) + if (p == NULL) return; - w = wu; - p = eat_leading_spaces (&w, p); if (w == 0) { @@ -636,17 +617,14 @@ read_radix (st_parameter_dt *dtp, const fnode *f, char *dest, int length, GFC_INTEGER_LARGEST v; int w, negative; char c, *p; - size_t wu; - wu = f->u.w; + w = f->u.w; - p = gfc_alloca (wu); + p = read_block_form (dtp, &w); - if (read_block_form (dtp, p, &wu) == FAILURE) + if (p == NULL) return; - w = wu; - p = eat_leading_spaces (&w, p); if (w == 0) { @@ -783,75 +761,83 @@ read_radix (st_parameter_dt *dtp, const fnode *f, char *dest, int length, void read_f (st_parameter_dt *dtp, const fnode *f, char *dest, int length) { - size_t wu; int w, seen_dp, exponent; - int exponent_sign, val_sign; - int ndigits; - int edigits; - int i; - char *p, *buffer; - char *digits; - char scratch[SCRATCH_SIZE]; - - val_sign = 1; - seen_dp = 0; - wu = f->u.w; + int exponent_sign; + const char *p; + char *buffer; + char *out; + int seen_int_digit; /* Seen a digit before the decimal point? */ + int seen_dec_digit; /* Seen a digit after the decimal point? */ - p = gfc_alloca (wu); + seen_dp = 0; + seen_int_digit = 0; + seen_dec_digit = 0; + exponent_sign = 1; + exponent = 0; + w = f->u.w; - if (read_block_form (dtp, p, &wu) == FAILURE) + /* Read in the next block. */ + p = read_block_form (dtp, &w); + if (p == NULL) return; - - w = wu; - - p = eat_leading_spaces (&w, p); + p = eat_leading_spaces (&w, (char*) p); if (w == 0) goto zero; - /* Optional sign */ + /* In this buffer we're going to re-format the number cleanly to be parsed + by convert_real in the end; this assures we're using strtod from the + C library for parsing and thus probably get the best accuracy possible. + This process may add a '+0.0' in front of the number as well as change the + exponent because of an implicit decimal point or the like. Thus allocating + strlen ("+0.0e-1000") == 10 characters plus one for NUL more than the + original buffer had should be enough. */ + buffer = gfc_alloca (w + 11); + out = buffer; + /* Optional sign */ if (*p == '-' || *p == '+') { if (*p == '-') - val_sign = -1; - p++; - w--; + *(out++) = '-'; + ++p; + --w; } - exponent_sign = 1; - p = eat_leading_spaces (&w, p); + p = eat_leading_spaces (&w, (char*) p); if (w == 0) goto zero; - /* A digit, a '.' or a exponent character ('e', 'E', 'd' or 'D') - is required at this point */ - - if (!isdigit (*p) && *p != '.' && *p != ',' && *p != 'd' && *p != 'D' - && *p != 'e' && *p != 'E') - goto bad_float; - - /* Remember the position of the first digit. */ - digits = p; - ndigits = 0; - - /* Scan through the string to find the exponent. */ + /* Process the mantissa string. */ while (w > 0) { switch (*p) { case ',': - if (dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA - && *p == ',') - *p = '.'; - else + if (dtp->u.p.current_unit->decimal_status != DECIMAL_COMMA) goto bad_float; - /* Fall through */ + /* Fall through. */ case '.': if (seen_dp) goto bad_float; + if (!seen_int_digit) + *(out++) = '0'; + *(out++) = '.'; seen_dp = 1; - /* Fall through */ + break; + case ' ': + if (dtp->u.p.blank_status == BLANK_ZERO) + { + *(out++) = '0'; + goto found_digit; + } + else if (dtp->u.p.blank_status == BLANK_NULL) + break; + else + /* TODO: Should we check instead that there are only trailing + blanks here, as is done below for exponents? */ + goto done; + /* Fall through. */ case '0': case '1': case '2': @@ -862,207 +848,173 @@ read_f (st_parameter_dt *dtp, const fnode *f, char *dest, int length) case '7': case '8': case '9': - case ' ': - ndigits++; - p++; - w--; + *(out++) = *p; +found_digit: + if (!seen_dp) + seen_int_digit = 1; + else + seen_dec_digit = 1; break; case '-': - exponent_sign = -1; - /* Fall through */ - case '+': - p++; - w--; - goto exp2; + goto exponent; - case 'd': case 'e': - case 'D': case 'E': - p++; - w--; - goto exp1; + case 'd': + case 'D': + ++p; + --w; + goto exponent; default: goto bad_float; } - } - /* No exponent has been seen, so we use the current scale factor */ - exponent = -dtp->u.p.scale_factor; - goto done; - - bad_float: - generate_error (&dtp->common, LIBERROR_READ_VALUE, - "Bad value during floating point read"); - next_record (dtp, 1); - return; - - /* The value read is zero */ - zero: - switch (length) - { - case 4: - *((GFC_REAL_4 *) dest) = 0; - break; - - case 8: - *((GFC_REAL_8 *) dest) = 0; - break; - -#ifdef HAVE_GFC_REAL_10 - case 10: - *((GFC_REAL_10 *) dest) = 0; - break; -#endif - -#ifdef HAVE_GFC_REAL_16 - case 16: - *((GFC_REAL_16 *) dest) = 0; - break; -#endif - - default: - internal_error (&dtp->common, "Unsupported real kind during IO"); + ++p; + --w; } - return; + + /* No exponent has been seen, so we use the current scale factor. */ + exponent = - dtp->u.p.scale_factor; + goto done; - /* At this point the start of an exponent has been found */ - exp1: - while (w > 0 && *p == ' ') + /* At this point the start of an exponent has been found. */ +exponent: + p = eat_leading_spaces (&w, (char*) p); + if (*p == '-' || *p == '+') { - w--; - p++; + if (*p == '-') + exponent_sign = -1; + ++p; + --w; } - switch (*p) - { - case '-': - exponent_sign = -1; - /* Fall through */ - - case '+': - p++; - w--; - break; - } + /* At this point a digit string is required. We calculate the value + of the exponent in order to take account of the scale factor and + the d parameter before explict conversion takes place. */ if (w == 0) goto bad_float; - /* At this point a digit string is required. We calculate the value - of the exponent in order to take account of the scale factor and - the d parameter before explict conversion takes place. */ - exp2: - /* Normal processing of exponent */ - exponent = 0; if (dtp->u.p.blank_status == BLANK_UNSPECIFIED) { while (w > 0 && isdigit (*p)) - { - exponent = 10 * exponent + *p - '0'; - p++; - w--; - } - - /* Only allow trailing blanks */ - + { + exponent *= 10; + exponent += *p - '0'; + ++p; + --w; + } + + /* Only allow trailing blanks. */ while (w > 0) - { - if (*p != ' ') + { + if (*p != ' ') goto bad_float; - p++; - w--; - } + ++p; + --w; + } } - else /* BZ or BN status is enabled */ + else /* BZ or BN status is enabled. */ { while (w > 0) - { - if (*p == ' ') - { - if (dtp->u.p.blank_status == BLANK_ZERO) *p = '0'; - if (dtp->u.p.blank_status == BLANK_NULL) - { - p++; - w--; - continue; - } - } - else if (!isdigit (*p)) - goto bad_float; - - exponent = 10 * exponent + *p - '0'; - p++; - w--; - } + { + if (*p == ' ') + { + if (dtp->u.p.blank_status == BLANK_ZERO) + exponent *= 10; + else + assert (dtp->u.p.blank_status == BLANK_NULL); + } + else if (!isdigit (*p)) + goto bad_float; + else + { + exponent *= 10; + exponent += *p - '0'; + } + + ++p; + --w; + } } - exponent = exponent * exponent_sign; + exponent *= exponent_sign; - done: +done: /* Use the precision specified in the format if no decimal point has been seen. */ if (!seen_dp) exponent -= f->u.real.d; - if (exponent > 0) - { - edigits = 2; - i = exponent; - } - else - { - edigits = 3; - i = -exponent; - } + /* Output a trailing '0' after decimal point if not yet found. */ + if (seen_dp && !seen_dec_digit) + *(out++) = '0'; - while (i >= 10) + /* Print out the exponent to finish the reformatted number. Maximum 4 + digits for the exponent. */ + if (exponent != 0) { - i /= 10; - edigits++; - } + int dig; - i = ndigits + edigits + 1; - if (val_sign < 0) - i++; + *(out++) = 'e'; + if (exponent < 0) + { + *(out++) = '-'; + exponent = - exponent; + } - if (i < SCRATCH_SIZE) - buffer = scratch; - else - buffer = get_mem (i); - - /* Reformat the string into a temporary buffer. As we're using atof it's - easiest to just leave the decimal point in place. */ - p = buffer; - if (val_sign < 0) - *(p++) = '-'; - for (; ndigits > 0; ndigits--) - { - if (*digits == ' ') - { - if (dtp->u.p.blank_status == BLANK_ZERO) *digits = '0'; - if (dtp->u.p.blank_status == BLANK_NULL) - { - digits++; - continue; - } - } - *p = *digits; - p++; - digits++; + assert (exponent < 10000); + for (dig = 3; dig >= 0; --dig) + { + out[dig] = (char) ('0' + exponent % 10); + exponent /= 10; + } + out += 4; } - *(p++) = 'e'; - sprintf (p, "%d", exponent); + *(out++) = '\0'; /* Do the actual conversion. */ convert_real (dtp, dest, buffer, length); - if (buffer != scratch) - free_mem (buffer); + return; + /* The value read is zero. */ +zero: + switch (length) + { + case 4: + *((GFC_REAL_4 *) dest) = 0.0; + break; + + case 8: + *((GFC_REAL_8 *) dest) = 0.0; + break; + +#ifdef HAVE_GFC_REAL_10 + case 10: + *((GFC_REAL_10 *) dest) = 0.0; + break; +#endif + +#ifdef HAVE_GFC_REAL_16 + case 16: + *((GFC_REAL_16 *) dest) = 0.0; + break; +#endif + + default: + internal_error (&dtp->common, "Unsupported real kind during IO"); + } + return; + +bad_float: + generate_error (&dtp->common, LIBERROR_READ_VALUE, + "Bad value during floating point read"); + next_record (dtp, 1); + return; } diff --git a/libgfortran/io/transfer.c b/libgfortran/io/transfer.c index d50641bcce5..7a06c5d1232 100644 --- a/libgfortran/io/transfer.c +++ b/libgfortran/io/transfer.c @@ -37,6 +37,7 @@ Boston, MA 02110-1301, USA. */ #include <string.h> #include <assert.h> #include <stdlib.h> +#include <errno.h> /* Calling conventions: Data transfer statements are unlike other @@ -183,60 +184,58 @@ current_mode (st_parameter_dt *dtp) heap. Hopefully this won't happen very often. */ char * -read_sf (st_parameter_dt *dtp, int *length, int no_error) +read_sf (st_parameter_dt *dtp, int * length, int no_error) { + static char *empty_string[0]; char *base, *p, q; - int n, crlf; - gfc_offset pos; - size_t readlen; + int n, lorig, memread, seen_comma; - if (*length > SCRATCH_SIZE) - dtp->u.p.line_buffer = get_mem (*length); - p = base = dtp->u.p.line_buffer; + /* If we hit EOF previously with the no_error flag set (i.e. X, T, + TR edit descriptors), and we now try to read again, this time + without setting no_error. */ + if (!no_error && dtp->u.p.at_eof) + { + *length = 0; + hit_eof (dtp); + return NULL; + } /* If we have seen an eor previously, return a length of 0. The caller is responsible for correctly padding the input field. */ if (dtp->u.p.sf_seen_eor) { *length = 0; - return base; + /* Just return something that isn't a NULL pointer, otherwise the + caller thinks an error occured. */ + return (char*) empty_string; } if (is_internal_unit (dtp)) { - readlen = *length; - if (unlikely (sread (dtp->u.p.current_unit->s, p, &readlen) != 0 - || readlen < (size_t) *length)) + memread = *length; + base = mem_alloc_r (dtp->u.p.current_unit->s, length); + if (unlikely (memread > *length)) { - generate_error (&dtp->common, LIBERROR_END, NULL); + hit_eof (dtp); return NULL; } - + n = *length; goto done; } - readlen = 1; - n = 0; + n = seen_comma = 0; - do - { - if (unlikely (sread (dtp->u.p.current_unit->s, &q, &readlen) != 0)) - { - generate_error (&dtp->common, LIBERROR_END, NULL); - return NULL; - } + /* Read data into format buffer and scan through it. */ + lorig = *length; + base = p = fbuf_read (dtp->u.p.current_unit, length); + if (base == NULL) + return NULL; - /* If we have a line without a terminating \n, drop through to - EOR below. */ - if (readlen < 1 && n == 0) - { - if (likely (no_error)) - break; - generate_error (&dtp->common, LIBERROR_END, NULL); - return NULL; - } + while (n < *length) + { + q = *p; - if (readlen < 1 || q == '\n' || q == '\r') + if (q == '\n' || q == '\r') { /* Unexpected end of line. */ @@ -245,23 +244,14 @@ read_sf (st_parameter_dt *dtp, int *length, int no_error) if (dtp->u.p.advance_status == ADVANCE_NO || dtp->u.p.seen_dollar) dtp->u.p.eor_condition = 1; - crlf = 0; /* If we encounter a CR, it might be a CRLF. */ if (q == '\r') /* Probably a CRLF */ { - readlen = 1; - pos = stream_offset (dtp->u.p.current_unit->s); - if (unlikely (sread (dtp->u.p.current_unit->s, &q, &readlen) - != 0)) - { - generate_error (&dtp->common, LIBERROR_END, NULL); - return NULL; - } - if (q != '\n' && readlen == 1) /* Not a CRLF after all. */ - sseek (dtp->u.p.current_unit->s, pos); - else - crlf = 1; + if (n < *length && *(p + 1) == '\n') + dtp->u.p.sf_seen_eor = 2; } + else + dtp->u.p.sf_seen_eor = 1; /* Without padding, terminate the I/O statement without assigning the value. With padding, the value still needs to be assigned, @@ -275,7 +265,6 @@ read_sf (st_parameter_dt *dtp, int *length, int no_error) } *length = n; - dtp->u.p.sf_seen_eor = (crlf ? 2 : 1); break; } /* Short circuit the read if a comma is found during numeric input. @@ -284,6 +273,7 @@ read_sf (st_parameter_dt *dtp, int *length, int no_error) if (q == ',') if (dtp->u.p.sf_read_comma == 1) { + seen_comma = 1; notify_std (&dtp->common, GFC_STD_GNU, "Comma in formatted numeric read."); *length = n; @@ -291,16 +281,31 @@ read_sf (st_parameter_dt *dtp, int *length, int no_error) } n++; - *p++ = q; - dtp->u.p.sf_seen_eor = 0; + p++; + } + + fbuf_seek (dtp->u.p.current_unit, n + dtp->u.p.sf_seen_eor + seen_comma, + SEEK_CUR); + + /* A short read implies we hit EOF, unless we hit EOR, a comma, or + some other stuff. Set the relevant flags. */ + if (lorig > *length && !dtp->u.p.sf_seen_eor && !seen_comma) + { + if (no_error) + dtp->u.p.at_eof = 1; + else + { + hit_eof (dtp); + return NULL; + } } - while (n < *length); done: - dtp->u.p.current_unit->bytes_left -= *length; + + dtp->u.p.current_unit->bytes_left -= n; if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) - dtp->u.p.size_used += (GFC_IO_INT) *length; + dtp->u.p.size_used += (GFC_IO_INT) n; return base; } @@ -316,12 +321,11 @@ read_sf (st_parameter_dt *dtp, int *length, int no_error) opened with PAD=YES. The caller must assume tailing spaces for short reads. */ -try -read_block_form (st_parameter_dt *dtp, void *buf, size_t *nbytes) +void * +read_block_form (st_parameter_dt *dtp, int * nbytes) { char *source; - size_t nread; - int nb; + int norig; if (!is_stream_io (dtp)) { @@ -338,15 +342,14 @@ read_block_form (st_parameter_dt *dtp, void *buf, size_t *nbytes) { /* Not enough data left. */ generate_error (&dtp->common, LIBERROR_EOR, NULL); - return FAILURE; + return NULL; } } if (unlikely (dtp->u.p.current_unit->bytes_left == 0)) { - dtp->u.p.current_unit->endfile = AT_ENDFILE; - generate_error (&dtp->common, LIBERROR_END, NULL); - return FAILURE; + hit_eof (dtp); + return NULL; } *nbytes = dtp->u.p.current_unit->bytes_left; @@ -357,42 +360,36 @@ read_block_form (st_parameter_dt *dtp, void *buf, size_t *nbytes) (dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL || dtp->u.p.current_unit->flags.access == ACCESS_STREAM)) { - nb = *nbytes; - source = read_sf (dtp, &nb, 0); - *nbytes = nb; + source = read_sf (dtp, nbytes, 0); dtp->u.p.current_unit->strm_pos += (gfc_offset) (*nbytes + dtp->u.p.sf_seen_eor); - if (source == NULL) - return FAILURE; - memcpy (buf, source, *nbytes); - return SUCCESS; + return source; } + + /* If we reach here, we can assume it's direct access. */ + dtp->u.p.current_unit->bytes_left -= (gfc_offset) *nbytes; - nread = *nbytes; - if (unlikely (sread (dtp->u.p.current_unit->s, buf, &nread) != 0)) - { - generate_error (&dtp->common, LIBERROR_OS, NULL); - return FAILURE; - } + norig = *nbytes; + source = fbuf_read (dtp->u.p.current_unit, nbytes); + fbuf_seek (dtp->u.p.current_unit, *nbytes, SEEK_CUR); if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) - dtp->u.p.size_used += (GFC_IO_INT) nread; + dtp->u.p.size_used += (GFC_IO_INT) *nbytes; - if (nread != *nbytes) - { /* Short read, this shouldn't happen. */ - if (likely (dtp->u.p.current_unit->pad_status == PAD_YES)) - *nbytes = nread; - else + if (norig != *nbytes) + { + /* Short read, this shouldn't happen. */ + if (!dtp->u.p.current_unit->pad_status == PAD_YES) { generate_error (&dtp->common, LIBERROR_EOR, NULL); source = NULL; } } - dtp->u.p.current_unit->strm_pos += (gfc_offset) nread; + dtp->u.p.current_unit->strm_pos += (gfc_offset) *nbytes; - return SUCCESS; + return source; } @@ -402,18 +399,18 @@ read_block_form (st_parameter_dt *dtp, void *buf, size_t *nbytes) static void read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) { - size_t to_read_record; - size_t have_read_record; - size_t to_read_subrecord; - size_t have_read_subrecord; + ssize_t to_read_record; + ssize_t have_read_record; + ssize_t to_read_subrecord; + ssize_t have_read_subrecord; int short_record; if (is_stream_io (dtp)) { to_read_record = *nbytes; - have_read_record = to_read_record; - if (unlikely (sread (dtp->u.p.current_unit->s, buf, &have_read_record) - != 0)) + have_read_record = sread (dtp->u.p.current_unit->s, buf, + to_read_record); + if (unlikely (have_read_record < 0)) { generate_error (&dtp->common, LIBERROR_OS, NULL); return; @@ -425,7 +422,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) { /* Short read, e.g. if we hit EOF. For stream files, we have to set the end-of-file condition. */ - generate_error (&dtp->common, LIBERROR_END, NULL); + hit_eof (dtp); return; } return; @@ -448,14 +445,14 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) dtp->u.p.current_unit->bytes_left -= to_read_record; - if (unlikely (sread (dtp->u.p.current_unit->s, buf, &to_read_record) - != 0)) + to_read_record = sread (dtp->u.p.current_unit->s, buf, to_read_record); + if (unlikely (to_read_record < 0)) { generate_error (&dtp->common, LIBERROR_OS, NULL); return; } - if (to_read_record != *nbytes) + if (to_read_record != (ssize_t) *nbytes) { /* Short read, e.g. if we hit EOF. Apparently, we read more than was written to the last record. */ @@ -475,18 +472,12 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) until the request has been fulfilled or the record has run out of continuation subrecords. */ - if (unlikely (dtp->u.p.current_unit->endfile == AT_ENDFILE)) - { - generate_error (&dtp->common, LIBERROR_END, NULL); - return; - } - /* Check whether we exceed the total record length. */ if (dtp->u.p.current_unit->flags.has_recl && (*nbytes > (size_t) dtp->u.p.current_unit->bytes_left)) { - to_read_record = (size_t) dtp->u.p.current_unit->bytes_left; + to_read_record = (ssize_t) dtp->u.p.current_unit->bytes_left; short_record = 1; } else @@ -501,7 +492,7 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) if (dtp->u.p.current_unit->bytes_left_subrecord < (gfc_offset) to_read_record) { - to_read_subrecord = (size_t) dtp->u.p.current_unit->bytes_left_subrecord; + to_read_subrecord = (ssize_t) dtp->u.p.current_unit->bytes_left_subrecord; to_read_record -= to_read_subrecord; } else @@ -512,9 +503,9 @@ read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) dtp->u.p.current_unit->bytes_left_subrecord -= to_read_subrecord; - have_read_subrecord = to_read_subrecord; - if (unlikely (sread (dtp->u.p.current_unit->s, buf + have_read_record, - &have_read_subrecord) != 0)) + have_read_subrecord = sread (dtp->u.p.current_unit->s, + buf + have_read_record, to_read_subrecord); + if (unlikely (have_read_subrecord) < 0) { generate_error (&dtp->common, LIBERROR_OS, NULL); return; @@ -603,7 +594,7 @@ write_block (st_parameter_dt *dtp, int length) if (is_internal_unit (dtp)) { - dest = salloc_w (dtp->u.p.current_unit->s, &length); + dest = mem_alloc_w (dtp->u.p.current_unit->s, &length); if (dest == NULL) { @@ -641,20 +632,22 @@ static try write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes) { - size_t have_written, to_write_subrecord; + ssize_t have_written; + ssize_t to_write_subrecord; int short_record; /* Stream I/O. */ if (is_stream_io (dtp)) { - if (unlikely (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0)) + have_written = swrite (dtp->u.p.current_unit->s, buf, nbytes); + if (unlikely (have_written < 0)) { generate_error (&dtp->common, LIBERROR_OS, NULL); return FAILURE; } - dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; + dtp->u.p.current_unit->strm_pos += (gfc_offset) have_written; return SUCCESS; } @@ -672,14 +665,15 @@ write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes) if (buf == NULL && nbytes == 0) return SUCCESS; - if (unlikely (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0)) + have_written = swrite (dtp->u.p.current_unit->s, buf, nbytes); + if (unlikely (have_written < 0)) { generate_error (&dtp->common, LIBERROR_OS, NULL); return FAILURE; } - dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; - dtp->u.p.current_unit->bytes_left -= (gfc_offset) nbytes; + dtp->u.p.current_unit->strm_pos += (gfc_offset) have_written; + dtp->u.p.current_unit->bytes_left -= (gfc_offset) have_written; return SUCCESS; } @@ -709,8 +703,9 @@ write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes) dtp->u.p.current_unit->bytes_left_subrecord -= (gfc_offset) to_write_subrecord; - if (unlikely (swrite (dtp->u.p.current_unit->s, buf + have_written, - &to_write_subrecord) != 0)) + to_write_subrecord = swrite (dtp->u.p.current_unit->s, + buf + have_written, to_write_subrecord); + if (unlikely (to_write_subrecord < 0)) { generate_error (&dtp->common, LIBERROR_OS, NULL); return FAILURE; @@ -920,19 +915,18 @@ require_type (st_parameter_dt *dtp, bt expected, bt actual, const fnode *f) } -/* This subroutine is the main loop for a formatted data transfer +/* This function is in the main loop for a formatted data transfer statement. It would be natural to implement this as a coroutine with the user program, but C makes that awkward. We loop, processing format elements. When we actually have to transfer data instead of just setting flags, we return control to the user - program which calls a subroutine that supplies the address and type + program which calls a function that supplies the address and type of the next element, then comes back here to process it. */ static void -formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, - size_t size) +formatted_transfer_scalar_read (st_parameter_dt *dtp, bt type, void *p, int kind, + size_t size) { - char scratch[SCRATCH_SIZE]; int pos, bytes_used; const fnode *f; format_token t; @@ -959,7 +953,347 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, dtp->u.p.sf_read_comma = dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA ? 0 : 1; - dtp->u.p.line_buffer = scratch; + for (;;) + { + /* If reversion has occurred and there is another real data item, + then we have to move to the next record. */ + if (dtp->u.p.reversion_flag && n > 0) + { + dtp->u.p.reversion_flag = 0; + next_record (dtp, 0); + } + + consume_data_flag = 1; + if ((dtp->common.flags & IOPARM_LIBRETURN_MASK) != IOPARM_LIBRETURN_OK) + break; + + f = next_format (dtp); + if (f == NULL) + { + /* No data descriptors left. */ + if (unlikely (n > 0)) + generate_error (&dtp->common, LIBERROR_FORMAT, + "Insufficient data descriptors in format after reversion"); + return; + } + + t = f->format; + + bytes_used = (int)(dtp->u.p.current_unit->recl + - dtp->u.p.current_unit->bytes_left); + + if (is_stream_io(dtp)) + bytes_used = 0; + + switch (t) + { + case FMT_I: + if (n == 0) + goto need_read_data; + if (require_type (dtp, BT_INTEGER, type, f)) + return; + read_decimal (dtp, f, p, kind); + break; + + case FMT_B: + if (n == 0) + goto need_read_data; + if (compile_options.allow_std < GFC_STD_GNU + && require_type (dtp, BT_INTEGER, type, f)) + return; + read_radix (dtp, f, p, kind, 2); + break; + + case FMT_O: + if (n == 0) + goto need_read_data; + if (compile_options.allow_std < GFC_STD_GNU + && require_type (dtp, BT_INTEGER, type, f)) + return; + read_radix (dtp, f, p, kind, 8); + break; + + case FMT_Z: + if (n == 0) + goto need_read_data; + if (compile_options.allow_std < GFC_STD_GNU + && require_type (dtp, BT_INTEGER, type, f)) + return; + read_radix (dtp, f, p, kind, 16); + break; + + case FMT_A: + if (n == 0) + goto need_read_data; + + /* It is possible to have FMT_A with something not BT_CHARACTER such + as when writing out hollerith strings, so check both type + and kind before calling wide character routines. */ + if (type == BT_CHARACTER && kind == 4) + read_a_char4 (dtp, f, p, size); + else + read_a (dtp, f, p, size); + break; + + case FMT_L: + if (n == 0) + goto need_read_data; + read_l (dtp, f, p, kind); + break; + + case FMT_D: + if (n == 0) + goto need_read_data; + if (require_type (dtp, BT_REAL, type, f)) + return; + read_f (dtp, f, p, kind); + break; + + case FMT_E: + if (n == 0) + goto need_read_data; + if (require_type (dtp, BT_REAL, type, f)) + return; + read_f (dtp, f, p, kind); + break; + + case FMT_EN: + if (n == 0) + goto need_read_data; + if (require_type (dtp, BT_REAL, type, f)) + return; + read_f (dtp, f, p, kind); + break; + + case FMT_ES: + if (n == 0) + goto need_read_data; + if (require_type (dtp, BT_REAL, type, f)) + return; + read_f (dtp, f, p, kind); + break; + + case FMT_F: + if (n == 0) + goto need_read_data; + if (require_type (dtp, BT_REAL, type, f)) + return; + read_f (dtp, f, p, kind); + break; + + case FMT_G: + if (n == 0) + goto need_read_data; + switch (type) + { + case BT_INTEGER: + read_decimal (dtp, f, p, kind); + break; + case BT_LOGICAL: + read_l (dtp, f, p, kind); + break; + case BT_CHARACTER: + if (kind == 4) + read_a_char4 (dtp, f, p, size); + else + read_a (dtp, f, p, size); + break; + case BT_REAL: + read_f (dtp, f, p, kind); + break; + default: + internal_error (&dtp->common, "formatted_transfer(): Bad type"); + } + break; + + case FMT_STRING: + consume_data_flag = 0; + format_error (dtp, f, "Constant string in input format"); + return; + + /* Format codes that don't transfer data. */ + case FMT_X: + case FMT_TR: + consume_data_flag = 0; + dtp->u.p.skips += f->u.n; + pos = bytes_used + dtp->u.p.skips - 1; + dtp->u.p.pending_spaces = pos - dtp->u.p.max_pos + 1; + read_x (dtp, f->u.n); + break; + + case FMT_TL: + case FMT_T: + consume_data_flag = 0; + + if (f->format == FMT_TL) + { + /* Handle the special case when no bytes have been used yet. + Cannot go below zero. */ + if (bytes_used == 0) + { + dtp->u.p.pending_spaces -= f->u.n; + dtp->u.p.skips -= f->u.n; + dtp->u.p.skips = dtp->u.p.skips < 0 ? 0 : dtp->u.p.skips; + } + + pos = bytes_used - f->u.n; + } + else /* FMT_T */ + pos = f->u.n - 1; + + /* Standard 10.6.1.1: excessive left tabbing is reset to the + left tab limit. We do not check if the position has gone + beyond the end of record because a subsequent tab could + bring us back again. */ + pos = pos < 0 ? 0 : pos; + + dtp->u.p.skips = dtp->u.p.skips + pos - bytes_used; + dtp->u.p.pending_spaces = dtp->u.p.pending_spaces + + pos - dtp->u.p.max_pos; + dtp->u.p.pending_spaces = dtp->u.p.pending_spaces < 0 + ? 0 : dtp->u.p.pending_spaces; + if (dtp->u.p.skips == 0) + break; + + /* Adjust everything for end-of-record condition */ + if (dtp->u.p.sf_seen_eor && !is_internal_unit (dtp)) + { + dtp->u.p.current_unit->bytes_left -= dtp->u.p.sf_seen_eor; + dtp->u.p.skips -= dtp->u.p.sf_seen_eor; + bytes_used = pos; + dtp->u.p.sf_seen_eor = 0; + } + if (dtp->u.p.skips < 0) + { + if (is_internal_unit (dtp)) + move_pos_offset (dtp->u.p.current_unit->s, dtp->u.p.skips); + else + fbuf_seek (dtp->u.p.current_unit, dtp->u.p.skips, SEEK_CUR); + dtp->u.p.current_unit->bytes_left -= (gfc_offset) dtp->u.p.skips; + dtp->u.p.skips = dtp->u.p.pending_spaces = 0; + } + else + read_x (dtp, dtp->u.p.skips); + break; + + case FMT_S: + consume_data_flag = 0; + dtp->u.p.sign_status = SIGN_S; + break; + + case FMT_SS: + consume_data_flag = 0; + dtp->u.p.sign_status = SIGN_SS; + break; + + case FMT_SP: + consume_data_flag = 0; + dtp->u.p.sign_status = SIGN_SP; + break; + + case FMT_BN: + consume_data_flag = 0 ; + dtp->u.p.blank_status = BLANK_NULL; + break; + + case FMT_BZ: + consume_data_flag = 0; + dtp->u.p.blank_status = BLANK_ZERO; + break; + + case FMT_DC: + consume_data_flag = 0; + dtp->u.p.current_unit->decimal_status = DECIMAL_COMMA; + break; + + case FMT_DP: + consume_data_flag = 0; + dtp->u.p.current_unit->decimal_status = DECIMAL_POINT; + break; + + case FMT_P: + consume_data_flag = 0; + dtp->u.p.scale_factor = f->u.k; + break; + + case FMT_DOLLAR: + consume_data_flag = 0; + dtp->u.p.seen_dollar = 1; + break; + + case FMT_SLASH: + consume_data_flag = 0; + dtp->u.p.skips = dtp->u.p.pending_spaces = 0; + next_record (dtp, 0); + break; + + case FMT_COLON: + /* A colon descriptor causes us to exit this loop (in + particular preventing another / descriptor from being + processed) unless there is another data item to be + transferred. */ + consume_data_flag = 0; + if (n == 0) + return; + break; + + default: + internal_error (&dtp->common, "Bad format node"); + } + + /* Adjust the item count and data pointer. */ + + if ((consume_data_flag > 0) && (n > 0)) + { + n--; + p = ((char *) p) + size; + } + + dtp->u.p.skips = 0; + + pos = (int)(dtp->u.p.current_unit->recl - dtp->u.p.current_unit->bytes_left); + dtp->u.p.max_pos = (dtp->u.p.max_pos > pos) ? dtp->u.p.max_pos : pos; + } + + return; + + /* Come here when we need a data descriptor but don't have one. We + push the current format node back onto the input, then return and + let the user program call us back with the data. */ + need_read_data: + unget_format (dtp, f); +} + + +static void +formatted_transfer_scalar_write (st_parameter_dt *dtp, bt type, void *p, int kind, + size_t size) +{ + int pos, bytes_used; + const fnode *f; + format_token t; + int n; + int consume_data_flag; + + /* Change a complex data item into a pair of reals. */ + + n = (p == NULL) ? 0 : ((type != BT_COMPLEX) ? 1 : 2); + if (type == BT_COMPLEX) + { + type = BT_REAL; + size /= 2; + } + + /* If there's an EOR condition, we simulate finalizing the transfer + by doing nothing. */ + if (dtp->u.p.eor_condition) + return; + + /* Set this flag so that commas in reads cause the read to complete before + the entire field has been read. The next read field will start right after + the comma in the stream. (Set to 0 for character reads). */ + dtp->u.p.sf_read_comma = + dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA ? 0 : 1; for (;;) { @@ -1010,7 +1344,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, if (is_internal_unit (dtp)) move_pos_offset (dtp->u.p.current_unit->s, dtp->u.p.skips); else - fbuf_seek (dtp->u.p.current_unit, dtp->u.p.skips); + fbuf_seek (dtp->u.p.current_unit, dtp->u.p.skips, SEEK_CUR); dtp->u.p.current_unit->bytes_left -= (gfc_offset) dtp->u.p.skips; } dtp->u.p.skips = dtp->u.p.pending_spaces = 0; @@ -1029,57 +1363,34 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, goto need_data; if (require_type (dtp, BT_INTEGER, type, f)) return; - - if (dtp->u.p.mode == READING) - read_decimal (dtp, f, p, kind); - else - write_i (dtp, f, p, kind); - + write_i (dtp, f, p, kind); break; case FMT_B: if (n == 0) goto need_data; - if (compile_options.allow_std < GFC_STD_GNU && require_type (dtp, BT_INTEGER, type, f)) return; - - if (dtp->u.p.mode == READING) - read_radix (dtp, f, p, kind, 2); - else - write_b (dtp, f, p, kind); - + write_b (dtp, f, p, kind); break; case FMT_O: if (n == 0) goto need_data; - if (compile_options.allow_std < GFC_STD_GNU && require_type (dtp, BT_INTEGER, type, f)) return; - - if (dtp->u.p.mode == READING) - read_radix (dtp, f, p, kind, 8); - else - write_o (dtp, f, p, kind); - + write_o (dtp, f, p, kind); break; case FMT_Z: if (n == 0) goto need_data; - if (compile_options.allow_std < GFC_STD_GNU && require_type (dtp, BT_INTEGER, type, f)) return; - - if (dtp->u.p.mode == READING) - read_radix (dtp, f, p, kind, 16); - else - write_z (dtp, f, p, kind); - + write_z (dtp, f, p, kind); break; case FMT_A: @@ -1089,31 +1400,16 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, /* It is possible to have FMT_A with something not BT_CHARACTER such as when writing out hollerith strings, so check both type and kind before calling wide character routines. */ - if (dtp->u.p.mode == READING) - { - if (type == BT_CHARACTER && kind == 4) - read_a_char4 (dtp, f, p, size); - else - read_a (dtp, f, p, size); - } + if (type == BT_CHARACTER && kind == 4) + write_a_char4 (dtp, f, p, size); else - { - if (type == BT_CHARACTER && kind == 4) - write_a_char4 (dtp, f, p, size); - else - write_a (dtp, f, p, size); - } + write_a (dtp, f, p, size); break; case FMT_L: if (n == 0) goto need_data; - - if (dtp->u.p.mode == READING) - read_l (dtp, f, p, kind); - else - write_l (dtp, f, p, kind); - + write_l (dtp, f, p, kind); break; case FMT_D: @@ -1121,12 +1417,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, goto need_data; if (require_type (dtp, BT_REAL, type, f)) return; - - if (dtp->u.p.mode == READING) - read_f (dtp, f, p, kind); - else - write_d (dtp, f, p, kind); - + write_d (dtp, f, p, kind); break; case FMT_E: @@ -1134,11 +1425,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, goto need_data; if (require_type (dtp, BT_REAL, type, f)) return; - - if (dtp->u.p.mode == READING) - read_f (dtp, f, p, kind); - else - write_e (dtp, f, p, kind); + write_e (dtp, f, p, kind); break; case FMT_EN: @@ -1146,12 +1433,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, goto need_data; if (require_type (dtp, BT_REAL, type, f)) return; - - if (dtp->u.p.mode == READING) - read_f (dtp, f, p, kind); - else - write_en (dtp, f, p, kind); - + write_en (dtp, f, p, kind); break; case FMT_ES: @@ -1159,12 +1441,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, goto need_data; if (require_type (dtp, BT_REAL, type, f)) return; - - if (dtp->u.p.mode == READING) - read_f (dtp, f, p, kind); - else - write_es (dtp, f, p, kind); - + write_es (dtp, f, p, kind); break; case FMT_F: @@ -1172,41 +1449,14 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, goto need_data; if (require_type (dtp, BT_REAL, type, f)) return; - - if (dtp->u.p.mode == READING) - read_f (dtp, f, p, kind); - else - write_f (dtp, f, p, kind); - + write_f (dtp, f, p, kind); break; case FMT_G: if (n == 0) goto need_data; - if (dtp->u.p.mode == READING) - switch (type) - { - case BT_INTEGER: - read_decimal (dtp, f, p, kind); - break; - case BT_LOGICAL: - read_l (dtp, f, p, kind); - break; - case BT_CHARACTER: - if (kind == 4) - read_a_char4 (dtp, f, p, size); - else - read_a (dtp, f, p, size); - break; - case BT_REAL: - read_f (dtp, f, p, kind); - break; - default: - goto bad_type; - } - else - switch (type) - { + switch (type) + { case BT_INTEGER: write_i (dtp, f, p, kind); break; @@ -1221,25 +1471,18 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, break; case BT_REAL: if (f->u.real.w == 0) - write_real_g0 (dtp, p, kind, f->u.real.d); + write_real_g0 (dtp, p, kind, f->u.real.d); else write_d (dtp, f, p, kind); break; default: - bad_type: internal_error (&dtp->common, "formatted_transfer(): Bad type"); - } - + } break; case FMT_STRING: consume_data_flag = 0; - if (dtp->u.p.mode == READING) - { - format_error (dtp, f, "Constant string in input format"); - return; - } write_constant_string (dtp, f); break; @@ -1251,21 +1494,15 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, dtp->u.p.skips += f->u.n; pos = bytes_used + dtp->u.p.skips - 1; dtp->u.p.pending_spaces = pos - dtp->u.p.max_pos + 1; - /* Writes occur just before the switch on f->format, above, so that trailing blanks are suppressed, unless we are doing a non-advancing write in which case we want to output the blanks now. */ - if (dtp->u.p.mode == WRITING - && dtp->u.p.advance_status == ADVANCE_NO) + if (dtp->u.p.advance_status == ADVANCE_NO) { write_x (dtp, dtp->u.p.skips, dtp->u.p.pending_spaces); dtp->u.p.skips = dtp->u.p.pending_spaces = 0; } - - if (dtp->u.p.mode == READING) - read_x (dtp, f->u.n); - break; case FMT_TL: @@ -1287,12 +1524,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, pos = bytes_used - f->u.n; } else /* FMT_T */ - { - if (dtp->u.p.mode == READING) - pos = f->u.n - 1; - else - pos = f->u.n - dtp->u.p.pending_spaces - 1; - } + pos = f->u.n - dtp->u.p.pending_spaces - 1; /* Standard 10.6.1.1: excessive left tabbing is reset to the left tab limit. We do not check if the position has gone @@ -1305,43 +1537,6 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, + pos - dtp->u.p.max_pos; dtp->u.p.pending_spaces = dtp->u.p.pending_spaces < 0 ? 0 : dtp->u.p.pending_spaces; - - if (dtp->u.p.skips == 0) - break; - - /* Writes occur just before the switch on f->format, above, so that - trailing blanks are suppressed. */ - if (dtp->u.p.mode == READING) - { - /* Adjust everything for end-of-record condition */ - if (dtp->u.p.sf_seen_eor && !is_internal_unit (dtp)) - { - if (dtp->u.p.sf_seen_eor == 2) - { - /* The EOR was a CRLF (two bytes wide). */ - dtp->u.p.current_unit->bytes_left -= 2; - dtp->u.p.skips -= 2; - } - else - { - /* The EOR marker was only one byte wide. */ - dtp->u.p.current_unit->bytes_left--; - dtp->u.p.skips--; - } - bytes_used = pos; - dtp->u.p.sf_seen_eor = 0; - } - if (dtp->u.p.skips < 0) - { - move_pos_offset (dtp->u.p.current_unit->s, dtp->u.p.skips); - dtp->u.p.current_unit->bytes_left - -= (gfc_offset) dtp->u.p.skips; - dtp->u.p.skips = dtp->u.p.pending_spaces = 0; - } - else - read_x (dtp, dtp->u.p.skips); - } - break; case FMT_S: @@ -1409,30 +1604,16 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, internal_error (&dtp->common, "Bad format node"); } - /* Free a buffer that we had to allocate during a sequential - formatted read of a block that was larger than the static - buffer. */ - - if (dtp->u.p.line_buffer != scratch) - { - free_mem (dtp->u.p.line_buffer); - dtp->u.p.line_buffer = scratch; - } - /* Adjust the item count and data pointer. */ if ((consume_data_flag > 0) && (n > 0)) - { - n--; - p = ((char *) p) + size; - } - - if (dtp->u.p.mode == READING) - dtp->u.p.skips = 0; + { + n--; + p = ((char *) p) + size; + } pos = (int)(dtp->u.p.current_unit->recl - dtp->u.p.current_unit->bytes_left); dtp->u.p.max_pos = (dtp->u.p.max_pos > pos) ? dtp->u.p.max_pos : pos; - } return; @@ -1444,6 +1625,7 @@ formatted_transfer_scalar (st_parameter_dt *dtp, bt type, void *p, int kind, unget_format (dtp, f); } + static void formatted_transfer (st_parameter_dt *dtp, bt type, void *p, int kind, size_t size, size_t nelems) @@ -1454,16 +1636,27 @@ formatted_transfer (st_parameter_dt *dtp, bt type, void *p, int kind, tmp = (char *) p; size_t stride = type == BT_CHARACTER ? size * GFC_SIZE_OF_CHAR_KIND(kind) : size; - /* Big loop over all the elements. */ - for (elem = 0; elem < nelems; elem++) + if (dtp->u.p.mode == READING) { - dtp->u.p.item_count++; - formatted_transfer_scalar (dtp, type, tmp + stride*elem, kind, size); + /* Big loop over all the elements. */ + for (elem = 0; elem < nelems; elem++) + { + dtp->u.p.item_count++; + formatted_transfer_scalar_read (dtp, type, tmp + stride*elem, kind, size); + } + } + else + { + /* Big loop over all the elements. */ + for (elem = 0; elem < nelems; elem++) + { + dtp->u.p.item_count++; + formatted_transfer_scalar_write (dtp, type, tmp + stride*elem, kind, size); + } } } - /* Data transfer entry points. The type of the data entity is implicit in the subroutine call. This prevents us from having to share a common enum with the compiler. */ @@ -1657,34 +1850,28 @@ transfer_array (st_parameter_dt *dtp, gfc_array_char *desc, int kind, static void us_read (st_parameter_dt *dtp, int continued) { - size_t n, nr; + ssize_t n, nr; GFC_INTEGER_4 i4; GFC_INTEGER_8 i8; gfc_offset i; - if (dtp->u.p.current_unit->endfile == AT_ENDFILE) - return; - if (compile_options.record_marker == 0) n = sizeof (GFC_INTEGER_4); else n = compile_options.record_marker; - nr = n; - - if (unlikely (sread (dtp->u.p.current_unit->s, &i, &n) != 0)) + nr = sread (dtp->u.p.current_unit->s, &i, n); + if (unlikely (nr < 0)) { generate_error (&dtp->common, LIBERROR_BAD_US, NULL); return; } - - if (n == 0) + else if (nr == 0) { - dtp->u.p.current_unit->endfile = AT_ENDFILE; + hit_eof (dtp); return; /* end of file */ } - - if (unlikely (n != nr)) + else if (unlikely (n != nr)) { generate_error (&dtp->common, LIBERROR_BAD_US, NULL); return; @@ -1750,7 +1937,7 @@ us_read (st_parameter_dt *dtp, int continued) static void us_write (st_parameter_dt *dtp, int continued) { - size_t nbytes; + ssize_t nbytes; gfc_offset dummy; dummy = 0; @@ -1760,7 +1947,7 @@ us_write (st_parameter_dt *dtp, int continued) else nbytes = compile_options.record_marker ; - if (swrite (dtp->u.p.current_unit->s, &dummy, &nbytes) != 0) + if (swrite (dtp->u.p.current_unit->s, &dummy, nbytes) != nbytes) generate_error (&dtp->common, LIBERROR_OS, NULL); /* For sequential unformatted, if RECL= was not specified in the OPEN @@ -1962,7 +2149,7 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag) return; } - /* Check the record number. */ + /* Check the record or position number. */ if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT && (cf & IOPARM_DT_HAS_REC) == 0) @@ -2111,65 +2298,71 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag) if (dtp->u.p.current_unit->pad_status == PAD_UNSPECIFIED) dtp->u.p.current_unit->pad_status = dtp->u.p.current_unit->flags.pad; - + + /* Check to see if we might be reading what we wrote before */ + + if (dtp->u.p.mode != dtp->u.p.current_unit->mode + && !is_internal_unit (dtp)) + { + int pos = fbuf_reset (dtp->u.p.current_unit); + if (pos != 0) + sseek (dtp->u.p.current_unit->s, pos, SEEK_CUR); + sflush(dtp->u.p.current_unit->s); + } + /* Check the POS= specifier: that it is in range and that it is used with a unit that has been connected for STREAM access. F2003 9.5.1.10. */ if (((cf & IOPARM_DT_HAS_POS) != 0)) { if (is_stream_io (dtp)) - { - - if (dtp->pos <= 0) - { - generate_error (&dtp->common, LIBERROR_BAD_OPTION, - "POS=specifier must be positive"); - return; - } - - if (dtp->pos >= dtp->u.p.current_unit->maxrec) - { - generate_error (&dtp->common, LIBERROR_BAD_OPTION, - "POS=specifier too large"); - return; - } - - dtp->rec = dtp->pos; - - if (dtp->u.p.mode == READING) - { - /* Required for compatibility between 4.3 and 4.4 runtime. Check - to see if we might be reading what we wrote before */ - if (dtp->u.p.current_unit->mode == WRITING) - { - fbuf_flush (dtp->u.p.current_unit, 1); - flush(dtp->u.p.current_unit->s); - } - - if (dtp->pos < file_length (dtp->u.p.current_unit->s)) - dtp->u.p.current_unit->endfile = NO_ENDFILE; - } - - if (dtp->pos != dtp->u.p.current_unit->strm_pos) - { - fbuf_flush (dtp->u.p.current_unit, 1); - flush (dtp->u.p.current_unit->s); - if (sseek (dtp->u.p.current_unit->s, dtp->pos - 1) == FAILURE) - { - generate_error (&dtp->common, LIBERROR_OS, NULL); - return; - } - dtp->u.p.current_unit->strm_pos = dtp->pos; - } - } + { + + if (dtp->pos <= 0) + { + generate_error (&dtp->common, LIBERROR_BAD_OPTION, + "POS=specifier must be positive"); + return; + } + + if (dtp->pos >= dtp->u.p.current_unit->maxrec) + { + generate_error (&dtp->common, LIBERROR_BAD_OPTION, + "POS=specifier too large"); + return; + } + + dtp->rec = dtp->pos; + + if (dtp->u.p.mode == READING) + { + /* Reset the endfile flag; if we hit EOF during reading + we'll set the flag and generate an error at that point + rather than worrying about it here. */ + dtp->u.p.current_unit->endfile = NO_ENDFILE; + } + + if (dtp->pos != dtp->u.p.current_unit->strm_pos) + { + fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode); + sflush (dtp->u.p.current_unit->s); + if (sseek (dtp->u.p.current_unit->s, dtp->pos - 1, SEEK_SET) < 0) + { + generate_error (&dtp->common, LIBERROR_OS, NULL); + return; + } + dtp->u.p.current_unit->strm_pos = dtp->pos; + } + } else - { - generate_error (&dtp->common, LIBERROR_BAD_OPTION, - "POS=specifier not allowed, " - "Try OPEN with ACCESS='stream'"); - return; - } + { + generate_error (&dtp->common, LIBERROR_BAD_OPTION, + "POS=specifier not allowed, " + "Try OPEN with ACCESS='stream'"); + return; + } } + /* Sanity checks on the record number. */ if ((cf & IOPARM_DT_HAS_REC) != 0) @@ -2188,15 +2381,10 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag) return; } - /* Check to see if we might be reading what we wrote before */ + /* Make sure format buffer is reset. */ + if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED) + fbuf_reset (dtp->u.p.current_unit); - if (dtp->u.p.mode == READING - && dtp->u.p.current_unit->mode == WRITING - && !is_internal_unit (dtp)) - { - fbuf_flush (dtp->u.p.current_unit, 1); - flush(dtp->u.p.current_unit->s); - } /* Check whether the record exists to be read. Only a partial record needs to exist. */ @@ -2211,37 +2399,28 @@ data_transfer_init (st_parameter_dt *dtp, int read_flag) /* Position the file. */ if (sseek (dtp->u.p.current_unit->s, (gfc_offset) (dtp->rec - 1) - * dtp->u.p.current_unit->recl) == FAILURE) - { - generate_error (&dtp->common, LIBERROR_OS, NULL); - return; - } + * dtp->u.p.current_unit->recl, SEEK_SET) < 0) + { + generate_error (&dtp->common, LIBERROR_OS, NULL); + return; + } /* TODO: This is required to maintain compatibility between - 4.3 and 4.4 runtime. Remove when ABI changes from 4.3 */ + 4.3 and 4.4 runtime. Remove when ABI changes from 4.3 */ if (is_stream_io (dtp)) - dtp->u.p.current_unit->strm_pos = dtp->rec; - + dtp->u.p.current_unit->strm_pos = dtp->rec; + /* TODO: Un-comment this code when ABI changes from 4.3. if (dtp->u.p.current_unit->flags.access == ACCESS_STREAM) - { - generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT, - "Record number not allowed for stream access " - "data transfer"); - return; - } */ - + { + generate_error (&dtp->common, LIBERROR_OPTION_CONFLICT, + "Record number not allowed for stream access " + "data transfer"); + return; + } */ } - /* Overwriting an existing sequential file ? - it is always safe to truncate the file on the first write */ - if (dtp->u.p.mode == WRITING - && dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL - && dtp->u.p.current_unit->last_record == 0 - && !is_preconnected(dtp->u.p.current_unit->s)) - struncate(dtp->u.p.current_unit->s); - /* Bugware for badly written mixed C-Fortran I/O. */ flush_if_preconnected(dtp->u.p.current_unit->s); @@ -2394,8 +2573,8 @@ next_array_record (st_parameter_dt *dtp, array_loop_spec *ls, int *finished) static void skip_record (st_parameter_dt *dtp, size_t bytes) { - gfc_offset new; size_t rlength; + ssize_t readb; static const size_t MAX_READ = 4096; char p[MAX_READ]; @@ -2405,12 +2584,10 @@ skip_record (st_parameter_dt *dtp, size_t bytes) if (is_seekable (dtp->u.p.current_unit->s)) { - new = file_position (dtp->u.p.current_unit->s) - + dtp->u.p.current_unit->bytes_left_subrecord; - /* Direct access files do not generate END conditions, only I/O errors. */ - if (sseek (dtp->u.p.current_unit->s, new) == FAILURE) + if (sseek (dtp->u.p.current_unit->s, + dtp->u.p.current_unit->bytes_left_subrecord, SEEK_CUR) < 0) generate_error (&dtp->common, LIBERROR_OS, NULL); } else @@ -2418,16 +2595,17 @@ skip_record (st_parameter_dt *dtp, size_t bytes) while (dtp->u.p.current_unit->bytes_left_subrecord > 0) { rlength = - (MAX_READ > (size_t) dtp->u.p.current_unit->bytes_left_subrecord) ? + (MAX_READ < (size_t) dtp->u.p.current_unit->bytes_left_subrecord) ? MAX_READ : (size_t) dtp->u.p.current_unit->bytes_left_subrecord; - if (sread (dtp->u.p.current_unit->s, p, &rlength) != 0) + readb = sread (dtp->u.p.current_unit->s, p, rlength); + if (readb < 0) { generate_error (&dtp->common, LIBERROR_OS, NULL); return; } - dtp->u.p.current_unit->bytes_left_subrecord -= rlength; + dtp->u.p.current_unit->bytes_left_subrecord -= readb; } } @@ -2475,8 +2653,8 @@ next_record_r (st_parameter_dt *dtp) { gfc_offset record; int bytes_left; - size_t length; char p; + int cc; switch (current_mode (dtp)) { @@ -2496,11 +2674,12 @@ next_record_r (st_parameter_dt *dtp) case FORMATTED_STREAM: case FORMATTED_SEQUENTIAL: - length = 1; - /* sf_read has already terminated input because of an '\n' */ - if (dtp->u.p.sf_seen_eor) + /* read_sf has already terminated input because of an '\n', or + we have hit EOF. */ + if (dtp->u.p.sf_seen_eor || dtp->u.p.at_eof) { dtp->u.p.sf_seen_eor = 0; + dtp->u.p.at_eof = 0; break; } @@ -2515,7 +2694,7 @@ next_record_r (st_parameter_dt *dtp) /* Now seek to this record. */ record = record * dtp->u.p.current_unit->recl; - if (sseek (dtp->u.p.current_unit->s, record) == FAILURE) + if (sseek (dtp->u.p.current_unit->s, record, SEEK_SET) < 0) { generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL); break; @@ -2527,10 +2706,9 @@ next_record_r (st_parameter_dt *dtp) bytes_left = (int) dtp->u.p.current_unit->bytes_left; bytes_left = min_off (bytes_left, file_length (dtp->u.p.current_unit->s) - - file_position (dtp->u.p.current_unit->s)); + - stell (dtp->u.p.current_unit->s)); if (sseek (dtp->u.p.current_unit->s, - file_position (dtp->u.p.current_unit->s) - + bytes_left) == FAILURE) + bytes_left, SEEK_CUR) < 0) { generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL); break; @@ -2540,42 +2718,37 @@ next_record_r (st_parameter_dt *dtp) } break; } - else do + else { - if (sread (dtp->u.p.current_unit->s, &p, &length) != 0) - { - generate_error (&dtp->common, LIBERROR_OS, NULL); - break; - } - - if (length == 0) + do { - dtp->u.p.current_unit->endfile = AT_ENDFILE; - break; + errno = 0; + cc = fbuf_getc (dtp->u.p.current_unit); + if (cc == EOF) + { + if (errno != 0) + generate_error (&dtp->common, LIBERROR_OS, NULL); + else + hit_eof (dtp); + break; + } + + if (is_stream_io (dtp)) + dtp->u.p.current_unit->strm_pos++; + + p = (char) cc; } - - if (is_stream_io (dtp)) - dtp->u.p.current_unit->strm_pos++; + while (p != '\n'); } - while (p != '\n'); - break; } - - if (dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL - && !dtp->u.p.namelist_mode - && dtp->u.p.current_unit->endfile == NO_ENDFILE - && (file_length (dtp->u.p.current_unit->s) == - file_position (dtp->u.p.current_unit->s))) - dtp->u.p.current_unit->endfile = AT_ENDFILE; - } /* Small utility function to write a record marker, taking care of byte swapping and of choosing the correct size. */ -inline static int +static int write_us_marker (st_parameter_dt *dtp, const gfc_offset buf) { size_t len; @@ -2595,12 +2768,12 @@ write_us_marker (st_parameter_dt *dtp, const gfc_offset buf) { case sizeof (GFC_INTEGER_4): buf4 = buf; - return swrite (dtp->u.p.current_unit->s, &buf4, &len); + return swrite (dtp->u.p.current_unit->s, &buf4, len); break; case sizeof (GFC_INTEGER_8): buf8 = buf; - return swrite (dtp->u.p.current_unit->s, &buf8, &len); + return swrite (dtp->u.p.current_unit->s, &buf8, len); break; default: @@ -2615,13 +2788,13 @@ write_us_marker (st_parameter_dt *dtp, const gfc_offset buf) case sizeof (GFC_INTEGER_4): buf4 = buf; reverse_memcpy (p, &buf4, sizeof (GFC_INTEGER_4)); - return swrite (dtp->u.p.current_unit->s, p, &len); + return swrite (dtp->u.p.current_unit->s, p, len); break; case sizeof (GFC_INTEGER_8): buf8 = buf; reverse_memcpy (p, &buf8, sizeof (GFC_INTEGER_8)); - return swrite (dtp->u.p.current_unit->s, p, &len); + return swrite (dtp->u.p.current_unit->s, p, len); break; default: @@ -2644,7 +2817,7 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord) /* Bytes written. */ m = dtp->u.p.current_unit->recl_subrecord - dtp->u.p.current_unit->bytes_left_subrecord; - c = file_position (dtp->u.p.current_unit->s); + c = stell (dtp->u.p.current_unit->s); /* Write the length tail. If we finish a record containing subrecords, we write out the negative length. */ @@ -2654,7 +2827,7 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord) else m_write = m; - if (unlikely (write_us_marker (dtp, m_write) != 0)) + if (unlikely (write_us_marker (dtp, m_write) < 0)) goto io_error; if (compile_options.record_marker == 0) @@ -2665,8 +2838,8 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord) /* Seek to the head and overwrite the bogus length with the real length. */ - if (unlikely (sseek (dtp->u.p.current_unit->s, c - m - record_marker) - == FAILURE)) + if (unlikely (sseek (dtp->u.p.current_unit->s, c - m - record_marker, + SEEK_SET) < 0)) goto io_error; if (next_subrecord) @@ -2674,13 +2847,13 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord) else m_write = m; - if (unlikely (write_us_marker (dtp, m_write) != 0)) + if (unlikely (write_us_marker (dtp, m_write) < 0)) goto io_error; /* Seek past the end of the current record. */ - if (unlikely (sseek (dtp->u.p.current_unit->s, c + record_marker) - == FAILURE)) + if (unlikely (sseek (dtp->u.p.current_unit->s, c + record_marker, + SEEK_SET) < 0)) goto io_error; return; @@ -2691,6 +2864,35 @@ next_record_w_unf (st_parameter_dt *dtp, int next_subrecord) } + +/* Utility function like memset() but operating on streams. Return + value is same as for POSIX write(). */ + +static ssize_t +sset (stream * s, int c, ssize_t nbyte) +{ + static const int WRITE_CHUNK = 256; + char p[WRITE_CHUNK]; + ssize_t bytes_left, trans; + + if (nbyte < WRITE_CHUNK) + memset (p, c, nbyte); + else + memset (p, c, WRITE_CHUNK); + + bytes_left = nbyte; + while (bytes_left > 0) + { + trans = (bytes_left < WRITE_CHUNK) ? bytes_left : WRITE_CHUNK; + trans = swrite (s, p, trans); + if (trans < 0) + return trans; + bytes_left -= trans; + } + + return nbyte - bytes_left; +} + /* Position to the next record in write mode. */ static void @@ -2699,9 +2901,6 @@ next_record_w (st_parameter_dt *dtp, int done) gfc_offset m, record, max_pos; int length; - /* Flush and reset the format buffer. */ - fbuf_flush (dtp->u.p.current_unit, 1); - /* Zero counters for X- and T-editing. */ max_pos = dtp->u.p.max_pos; dtp->u.p.max_pos = dtp->u.p.skips = dtp->u.p.pending_spaces = 0; @@ -2716,8 +2915,11 @@ next_record_w (st_parameter_dt *dtp, int done) if (dtp->u.p.current_unit->bytes_left == 0) break; + fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END); + fbuf_flush (dtp->u.p.current_unit, WRITING); if (sset (dtp->u.p.current_unit->s, ' ', - dtp->u.p.current_unit->bytes_left) == FAILURE) + dtp->u.p.current_unit->bytes_left) + != dtp->u.p.current_unit->bytes_left) goto io_error; break; @@ -2726,7 +2928,7 @@ next_record_w (st_parameter_dt *dtp, int done) if (dtp->u.p.current_unit->bytes_left > 0) { length = (int) dtp->u.p.current_unit->bytes_left; - if (sset (dtp->u.p.current_unit->s, 0, length) == FAILURE) + if (sset (dtp->u.p.current_unit->s, 0, length) != length) goto io_error; } break; @@ -2757,8 +2959,7 @@ next_record_w (st_parameter_dt *dtp, int done) { length = (int) (max_pos - m); if (sseek (dtp->u.p.current_unit->s, - file_position (dtp->u.p.current_unit->s) - + length) == FAILURE) + length, SEEK_CUR) < 0) { generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL); return; @@ -2766,7 +2967,7 @@ next_record_w (st_parameter_dt *dtp, int done) length = (int) (dtp->u.p.current_unit->recl - max_pos); } - if (sset (dtp->u.p.current_unit->s, ' ', length) == FAILURE) + if (sset (dtp->u.p.current_unit->s, ' ', length) != length) { generate_error (&dtp->common, LIBERROR_END, NULL); return; @@ -2782,7 +2983,7 @@ next_record_w (st_parameter_dt *dtp, int done) /* Now seek to this record */ record = record * dtp->u.p.current_unit->recl; - if (sseek (dtp->u.p.current_unit->s, record) == FAILURE) + if (sseek (dtp->u.p.current_unit->s, record, SEEK_SET) < 0) { generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL); return; @@ -2805,8 +3006,7 @@ next_record_w (st_parameter_dt *dtp, int done) { length = (int) (max_pos - m); if (sseek (dtp->u.p.current_unit->s, - file_position (dtp->u.p.current_unit->s) - + length) == FAILURE) + length, SEEK_CUR) < 0) { generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL); return; @@ -2817,7 +3017,7 @@ next_record_w (st_parameter_dt *dtp, int done) length = (int) dtp->u.p.current_unit->bytes_left; } - if (sset (dtp->u.p.current_unit->s, ' ', length) == FAILURE) + if (sset (dtp->u.p.current_unit->s, ' ', length) != length) { generate_error (&dtp->common, LIBERROR_END, NULL); return; @@ -2826,23 +3026,27 @@ next_record_w (st_parameter_dt *dtp, int done) } else { - size_t len; - const char crlf[] = "\r\n"; - #ifdef HAVE_CRLF - len = 2; + const int len = 2; #else - len = 1; + const int len = 1; #endif - if (swrite (dtp->u.p.current_unit->s, &crlf[2-len], &len) != 0) - goto io_error; - + fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END); + char * p = fbuf_alloc (dtp->u.p.current_unit, len); + if (!p) + goto io_error; +#ifdef HAVE_CRLF + *(p++) = '\r'; +#endif + *p = '\n'; if (is_stream_io (dtp)) { dtp->u.p.current_unit->strm_pos += len; if (dtp->u.p.current_unit->strm_pos < file_length (dtp->u.p.current_unit->s)) - struncate (dtp->u.p.current_unit->s); + unit_truncate (dtp->u.p.current_unit, + dtp->u.p.current_unit->strm_pos - 1, + &dtp->common); } } @@ -2880,7 +3084,7 @@ next_record (st_parameter_dt *dtp, int done) dtp->u.p.current_unit->current_record = 0; if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT) { - fp = file_position (dtp->u.p.current_unit->s); + fp = stell (dtp->u.p.current_unit->s); /* Calculate next record, rounding up partial records. */ dtp->u.p.current_unit->last_record = (fp + dtp->u.p.current_unit->recl - 1) / @@ -2892,6 +3096,8 @@ next_record (st_parameter_dt *dtp, int done) if (!done) pre_position (dtp); + + fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode); } @@ -2940,7 +3146,6 @@ finalize_transfer (st_parameter_dt *dtp) if ((cf & IOPARM_DT_LIST_FORMAT) != 0 && dtp->u.p.mode == READING) { finish_list_read (dtp); - sfree (dtp->u.p.current_unit->s); return; } @@ -2955,10 +3160,9 @@ finalize_transfer (st_parameter_dt *dtp) next_record (dtp, 1); if (dtp->u.p.current_unit->flags.form == FORM_UNFORMATTED - && file_position (dtp->u.p.current_unit->s) >= dtp->rec) + && stell (dtp->u.p.current_unit->s) >= dtp->rec) { - flush (dtp->u.p.current_unit->s); - sfree (dtp->u.p.current_unit->s); + sflush (dtp->u.p.current_unit->s); } return; } @@ -2967,9 +3171,8 @@ finalize_transfer (st_parameter_dt *dtp) if (!is_internal_unit (dtp) && dtp->u.p.seen_dollar) { + fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode); dtp->u.p.seen_dollar = 0; - fbuf_flush (dtp->u.p.current_unit, 1); - sfree (dtp->u.p.current_unit->s); return; } @@ -2981,15 +3184,17 @@ finalize_transfer (st_parameter_dt *dtp) - dtp->u.p.current_unit->bytes_left); dtp->u.p.current_unit->saved_pos = dtp->u.p.max_pos > 0 ? dtp->u.p.max_pos - bytes_written : 0; - fbuf_flush (dtp->u.p.current_unit, 0); - flush (dtp->u.p.current_unit->s); + fbuf_flush (dtp->u.p.current_unit, dtp->u.p.mode); + sflush (dtp->u.p.current_unit->s); return; } + else if (dtp->u.p.current_unit->flags.form == FORM_FORMATTED + && dtp->u.p.mode == WRITING && !is_internal_unit (dtp)) + fbuf_seek (dtp->u.p.current_unit, 0, SEEK_END); dtp->u.p.current_unit->saved_pos = 0; next_record (dtp, 1); - sfree (dtp->u.p.current_unit->s); } /* Transfer function for IOLENGTH. It doesn't actually do any @@ -3046,8 +3251,6 @@ void st_iolength_done (st_parameter_dt *dtp __attribute__((unused))) { free_ionml (dtp); - if (dtp->u.p.scratch != NULL) - free_mem (dtp->u.p.scratch); library_end (); } @@ -3063,29 +3266,6 @@ st_read (st_parameter_dt *dtp) library_start (&dtp->common); data_transfer_init (dtp, 1); - - /* Handle complications dealing with the endfile record. */ - - if (dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL) - switch (dtp->u.p.current_unit->endfile) - { - case NO_ENDFILE: - break; - - case AT_ENDFILE: - if (!is_internal_unit (dtp)) - { - generate_error (&dtp->common, LIBERROR_END, NULL); - dtp->u.p.current_unit->endfile = AFTER_ENDFILE; - dtp->u.p.current_unit->current_record = 0; - } - break; - - case AFTER_ENDFILE: - generate_error (&dtp->common, LIBERROR_ENDFILE, NULL); - dtp->u.p.current_unit->current_record = 0; - break; - } } extern void st_read_done (st_parameter_dt *); @@ -3095,10 +3275,9 @@ void st_read_done (st_parameter_dt *dtp) { finalize_transfer (dtp); - free_format_data (dtp); + if (is_internal_unit (dtp)) + free_format_data (dtp->u.p.fmt); free_ionml (dtp); - if (dtp->u.p.scratch != NULL) - free_mem (dtp->u.p.scratch); if (dtp->u.p.current_unit != NULL) unlock_unit (dtp->u.p.current_unit); @@ -3141,19 +3320,16 @@ st_write_done (st_parameter_dt *dtp) case NO_ENDFILE: /* Get rid of whatever is after this record. */ if (!is_internal_unit (dtp)) - { - flush (dtp->u.p.current_unit->s); - if (struncate (dtp->u.p.current_unit->s) == FAILURE) - generate_error (&dtp->common, LIBERROR_OS, NULL); - } + unit_truncate (dtp->u.p.current_unit, + stell (dtp->u.p.current_unit->s), + &dtp->common); dtp->u.p.current_unit->endfile = AT_ENDFILE; break; } - free_format_data (dtp); + if (is_internal_unit (dtp)) + free_format_data (dtp->u.p.fmt); free_ionml (dtp); - if (dtp->u.p.scratch != NULL) - free_mem (dtp->u.p.scratch); if (dtp->u.p.current_unit != NULL) unlock_unit (dtp->u.p.current_unit); @@ -3267,3 +3443,46 @@ void reverse_memcpy (void *dest, const void *src, size_t n) for (i=0; i<n; i++) *(d++) = *(s--); } + + +/* Once upon a time, a poor innocent Fortran program was reading a + file, when suddenly it hit the end-of-file (EOF). Unfortunately + the OS doesn't tell whether we're at the EOF or whether we already + went past it. Luckily our hero, libgfortran, keeps track of this. + Call this function when you detect an EOF condition. See Section + 9.10.2 in F2003. */ + +void +hit_eof (st_parameter_dt * dtp) +{ + dtp->u.p.current_unit->flags.position = POSITION_APPEND; + + if (dtp->u.p.current_unit->flags.access == ACCESS_SEQUENTIAL) + switch (dtp->u.p.current_unit->endfile) + { + case NO_ENDFILE: + case AT_ENDFILE: + generate_error (&dtp->common, LIBERROR_END, NULL); + if (!is_internal_unit (dtp)) + { + dtp->u.p.current_unit->endfile = AFTER_ENDFILE; + dtp->u.p.current_unit->current_record = 0; + } + else + dtp->u.p.current_unit->endfile = AT_ENDFILE; + break; + + case AFTER_ENDFILE: + generate_error (&dtp->common, LIBERROR_ENDFILE, NULL); + dtp->u.p.current_unit->current_record = 0; + break; + } + else + { + /* Non-sequential files don't have an ENDFILE record, so we + can't be at AFTER_ENDFILE. */ + dtp->u.p.current_unit->endfile = AT_ENDFILE; + generate_error (&dtp->common, LIBERROR_END, NULL); + dtp->u.p.current_unit->current_record = 0; + } +} diff --git a/libgfortran/io/unit.c b/libgfortran/io/unit.c index 0af002d1a95..4c460166152 100644 --- a/libgfortran/io/unit.c +++ b/libgfortran/io/unit.c @@ -540,6 +540,8 @@ init_units (void) u->file_len = strlen (stdin_name); u->file = get_mem (u->file_len); memmove (u->file, stdin_name, u->file_len); + + fbuf_init (u, 0); __gthread_mutex_unlock (&u->lock); } @@ -640,7 +642,8 @@ close_unit_1 (gfc_unit *u, int locked) free_mem (u->file); u->file = NULL; u->file_len = 0; - + + free_format_hash_table (u); fbuf_destroy (u); if (!locked) @@ -697,15 +700,62 @@ close_units (void) void update_position (gfc_unit *u) { - if (file_position (u->s) == 0) + if (stell (u->s) == 0) u->flags.position = POSITION_REWIND; - else if (file_length (u->s) == file_position (u->s)) + else if (file_length (u->s) == stell (u->s)) u->flags.position = POSITION_APPEND; else u->flags.position = POSITION_ASIS; } +/* High level interface to truncate a file safely, i.e. flush format + buffers, check that it's a regular file, and generate error if that + occurs. Just like POSIX ftruncate, returns 0 on success, -1 on + failure. */ + +int +unit_truncate (gfc_unit * u, gfc_offset pos, st_parameter_common * common) +{ + int ret; + + /* Make sure format buffer is flushed. */ + if (u->flags.form == FORM_FORMATTED) + { + if (u->mode == READING) + pos += fbuf_reset (u); + else + fbuf_flush (u, u->mode); + } + + /* Don't try to truncate a special file, just pretend that it + succeeds. */ + if (is_special (u->s) || !is_seekable (u->s)) + { + sflush (u->s); + return 0; + } + + /* struncate() should flush the stream buffer if necessary, so don't + bother calling sflush() here. */ + ret = struncate (u->s, pos); + + if (ret != 0) + { + generate_error (common, LIBERROR_OS, NULL); + u->endfile = NO_ENDFILE; + u->flags.position = POSITION_ASIS; + } + else + { + u->endfile = AT_ENDFILE; + u->flags.position = POSITION_APPEND; + } + + return ret; +} + + /* filename_from_unit()-- If the unit_number exists, return a pointer to the name of the associated file, otherwise return the empty string. The caller must free memory allocated for the filename string. */ @@ -746,23 +796,25 @@ finish_last_advance_record (gfc_unit *u) { if (u->saved_pos > 0) - fbuf_seek (u, u->saved_pos); - - fbuf_flush (u, 1); + fbuf_seek (u, u->saved_pos, SEEK_CUR); if (!(u->unit_number == options.stdout_unit || u->unit_number == options.stderr_unit)) { - size_t len; - - const char crlf[] = "\r\n"; #ifdef HAVE_CRLF - len = 2; + const int len = 2; #else - len = 1; + const int len = 1; #endif - if (swrite (u->s, &crlf[2-len], &len) != 0) + char *p = fbuf_alloc (u, len); + if (!p) os_error ("Completing record after ADVANCE_NO failed"); +#ifdef HAVE_CRLF + *(p++) = '\r'; +#endif + *p = '\n'; } + + fbuf_flush (u, u->mode); } diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c index 29583802285..5cac8ea4a5c 100644 --- a/libgfortran/io/unix.c +++ b/libgfortran/io/unix.c @@ -94,10 +94,6 @@ id_from_fd (const int fd) #endif -#ifndef SSIZE_MAX -#define SSIZE_MAX SHRT_MAX -#endif - #ifndef PATH_MAX #define PATH_MAX 1024 #endif @@ -129,102 +125,32 @@ id_from_fd (const int fd) #endif -/* Unix stream I/O module */ +/* Unix and internal stream I/O module */ -#define BUFFER_SIZE 8192 +static const int 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. */ - int len; /* Physical length of the current buffer */ + char *buffer; /* Pointer to the buffer. */ + int fd; /* The POSIX file descriptor. */ + int active; /* Length of valid bytes in the buffer */ int prot; - int ndirty; /* Dirty bytes starting at dirty_offset */ + int ndirty; /* Dirty bytes starting at buffer_offset */ int special_file; /* =1 if the fd refers to a special file */ - - io_mode method; /* Method of stream I/O being used */ - - char *buffer; - char small_buffer[BUFFER_SIZE]; } unix_stream; -/* Stream structure for internal files. Fields must be kept in sync - with unix_stream above, except for the buffer. For internal files - we point the buffer pointer directly at the destination memory. */ - -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. */ - - 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 */ - - io_mode method; /* Method of stream I/O being used */ - - char *buffer; -} -int_stream; - -/* This implementation of stream I/O is based on the paper: - * - * "Exploiting the advantages of mapped files for stream I/O", - * O. Krieger, M. Stumm and R. Umrau, "Proceedings of the 1992 Winter - * USENIX conference", p. 27-42. - * - * It differs in a number of ways from the version described in the - * paper. First of all, threads are not an issue during I/O and we - * also don't have to worry about having multiple regions, since - * fortran's I/O model only allows you to be one place at a time. - * - * On the other hand, we have to be able to writing at the end of a - * stream, read from the start of a stream or read and write blocks of - * bytes from an arbitrary position. After opening a file, a pointer - * to a stream structure is returned, which is used to handle file - * accesses until the file is closed. - * - * salloc_at_r(stream, len, where)-- Given a stream pointer, return a - * pointer to a block of memory that mirror the file at position - * 'where' that is 'len' bytes long. The len integer is updated to - * reflect how many bytes were actually read. The only reason for a - * short read is end of file. The file pointer is updated. The - * pointer is valid until the next call to salloc_*. - * - * salloc_at_w(stream, len, where)-- Given the stream pointer, returns - * a pointer to a block of memory that is updated to reflect the state - * of the file. The length of the buffer is always equal to that - * requested. The buffer must be completely set by the caller. When - * data has been written, the sfree() function must be called to - * indicate that the caller is done writing data to the buffer. This - * may or may not cause a physical write. - * - * Short forms of these are salloc_r() and salloc_w() which drop the - * 'where' parameter and use the current file pointer. */ - - /*move_pos_offset()-- Move the record pointer right or left *relative to current position */ @@ -236,15 +162,12 @@ move_pos_offset (stream* st, int pos_off) { str->logical_offset += pos_off; - if (str->dirty_offset + str->ndirty > str->logical_offset) + if (str->ndirty > str->logical_offset) { if (str->ndirty + pos_off > 0) str->ndirty += pos_off; else - { - str->dirty_offset += pos_off + pos_off; - str->ndirty = 0; - } + str->ndirty = 0; } return pos_off; @@ -327,580 +250,330 @@ flush_if_preconnected (stream * s) } -/* Reset a stream after reading/writing. Assumes that the buffers have - been flushed. */ +/* get_oserror()-- Get the most recent operating system error. For + * unix, this is errno. */ -inline static void -reset_stream (unix_stream * s, size_t bytes_rw) +const char * +get_oserror (void) { - s->physical_offset += bytes_rw; - s->logical_offset = s->physical_offset; - if (s->file_length != -1 && s->physical_offset > s->file_length) - s->file_length = s->physical_offset; + return strerror (errno); } -/* Read bytes into a buffer, allowing for short reads. If the nbytes - * argument is less on return than on entry, it is because we've hit - * the end of file. */ +/******************************************************************** +Raw I/O functions (read, write, seek, tell, truncate, close). + +These functions wrap the basic POSIX I/O syscalls. Any deviation in +semantics is a bug, except the following: write restarts in case +of being interrupted by a signal, and as the first argument the +functions take the unix_stream struct rather than an integer file +descriptor. Also, for POSIX read() and write() a nbyte argument larger +than SSIZE_MAX is undefined; here the type of nbyte is ssize_t rather +than size_t as for POSIX read/write. +*********************************************************************/ static int -do_read (unix_stream * s, void * buf, size_t * nbytes) +raw_flush (unix_stream * s __attribute__ ((unused))) { - ssize_t trans; - size_t bytes_left; - char *buf_st; - int status; - - status = 0; - bytes_left = *nbytes; - buf_st = (char *) buf; - - /* We must read in a loop since some systems don't restart system - calls in case of a signal. */ - while (bytes_left > 0) - { - /* Requests between SSIZE_MAX and SIZE_MAX are undefined by SUSv3, - so we must read in chunks smaller than SSIZE_MAX. */ - trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX; - trans = read (s->fd, buf_st, trans); - if (trans < 0) - { - if (errno == EINTR) - continue; - else - { - status = errno; - break; - } - } - else if (trans == 0) /* We hit EOF. */ - break; - buf_st += trans; - bytes_left -= trans; - } - - *nbytes -= bytes_left; - return status; + return 0; } +static ssize_t +raw_read (unix_stream * s, void * buf, ssize_t nbyte) +{ + /* For read we can't do I/O in a loop like raw_write does, because + that will break applications that wait for interactive I/O. */ + return read (s->fd, buf, nbyte); +} -/* Write a buffer to a stream, allowing for short writes. */ - -static int -do_write (unix_stream * s, const void * buf, size_t * nbytes) +static ssize_t +raw_write (unix_stream * s, const void * buf, ssize_t nbyte) { - ssize_t trans; - size_t bytes_left; + ssize_t trans, bytes_left; char *buf_st; - int status; - status = 0; - bytes_left = *nbytes; + bytes_left = nbyte; buf_st = (char *) buf; /* We must write in a loop since some systems don't restart system calls in case of a signal. */ while (bytes_left > 0) { - /* Requests between SSIZE_MAX and SIZE_MAX are undefined by SUSv3, - so we must write in chunks smaller than SSIZE_MAX. */ - trans = (bytes_left < SSIZE_MAX) ? bytes_left : SSIZE_MAX; - trans = write (s->fd, buf_st, trans); + trans = write (s->fd, buf_st, bytes_left); if (trans < 0) { if (errno == EINTR) continue; else - { - status = errno; - break; - } + return trans; } buf_st += trans; bytes_left -= trans; } - *nbytes -= bytes_left; - return status; + return nbyte - bytes_left; } +static off_t +raw_seek (unix_stream * s, off_t offset, int whence) +{ + return lseek (s->fd, offset, whence); +} -/* get_oserror()-- Get the most recent operating system error. For - * unix, this is errno. */ +static off_t +raw_tell (unix_stream * s) +{ + return lseek (s->fd, 0, SEEK_CUR); +} -const char * -get_oserror (void) +static int +raw_truncate (unix_stream * s, off_t length) { - return strerror (errno); +#ifdef HAVE_FTRUNCATE + return ftruncate (s->fd, length); +#elif defined HAVE_CHSIZE + return chsize (s->fd, length); +#else + runtime_error ("required ftruncate or chsize support not present"); + return -1; +#endif } +static int +raw_close (unix_stream * s) +{ + int retval; + + retval = close (s->fd); + free_mem (s); + return retval; +} -/********************************************************************* - File descriptor stream functions -*********************************************************************/ +static int +raw_init (unix_stream * s) +{ + s->st.read = (void *) raw_read; + s->st.write = (void *) raw_write; + s->st.seek = (void *) raw_seek; + s->st.tell = (void *) raw_tell; + s->st.truncate = (void *) raw_truncate; + s->st.close = (void *) raw_close; + s->st.flush = (void *) raw_flush; + s->buffer = NULL; + return 0; +} -/* fd_flush()-- Write bytes that need to be written */ -static try -fd_flush (unix_stream * s) +/********************************************************************* +Buffered I/O functions. These functions have the same semantics as the +raw I/O functions above, except that they are buffered in order to +improve performance. The buffer must be flushed when switching from +reading to writing and vice versa. +*********************************************************************/ + +static int +buf_flush (unix_stream * s) { - size_t writelen; + int writelen; + + /* Flushing in read mode means discarding read bytes. */ + s->active = 0; if (s->ndirty == 0) - return SUCCESS; + return 0; - if (s->file_length != -1 && s->physical_offset != s->dirty_offset && - lseek (s->fd, s->dirty_offset, SEEK_SET) < 0) - return FAILURE; + if (s->file_length != -1 && s->physical_offset != s->buffer_offset + && lseek (s->fd, s->buffer_offset, SEEK_SET) < 0) + return -1; - writelen = s->ndirty; - if (do_write (s, s->buffer + (s->dirty_offset - s->buffer_offset), - &writelen) != 0) - return FAILURE; + writelen = raw_write (s, s->buffer, s->ndirty); - s->physical_offset = s->dirty_offset + writelen; + s->physical_offset = s->buffer_offset + writelen; - /* don't increment file_length if the file is non-seekable */ + /* Don't increment file_length if the file is non-seekable. */ if (s->file_length != -1 && s->physical_offset > s->file_length) - s->file_length = s->physical_offset; + s->file_length = s->physical_offset; s->ndirty -= writelen; if (s->ndirty != 0) - return FAILURE; + return -1; - return SUCCESS; + return 0; } - -/* fd_alloc()-- Arrange a buffer such that the salloc() request can be - * satisfied. This subroutine gets the buffer ready for whatever is - * to come next. */ - -static void -fd_alloc (unix_stream * s, gfc_offset where, - int *len __attribute__ ((unused))) +static ssize_t +buf_read (unix_stream * s, void * buf, ssize_t nbyte) { - char *new_buffer; - int n, read_len; + if (s->active == 0) + s->buffer_offset = s->logical_offset; - if (*len <= BUFFER_SIZE) - { - new_buffer = s->small_buffer; - read_len = BUFFER_SIZE; - } + /* Is the data we want in the buffer? */ + if (s->logical_offset + nbyte <= s->buffer_offset + s->active + && s->buffer_offset <= s->logical_offset) + memcpy (buf, s->buffer + (s->logical_offset - s->buffer_offset), nbyte); else { - new_buffer = get_mem (*len); - read_len = *len; - } - - /* Salvage bytes currently within the buffer. This is important for - * devices that cannot seek. */ - - if (s->buffer != NULL && s->buffer_offset <= where && - where <= s->buffer_offset + s->active) - { - - n = s->active - (where - s->buffer_offset); - memmove (new_buffer, s->buffer + (where - s->buffer_offset), n); - - s->active = n; - } - else - { /* new buffer starts off empty */ - s->active = 0; + /* First copy the active bytes if applicable, then read the rest + either directly or filling the buffer. */ + char *p; + int nread = 0; + ssize_t to_read, did_read; + gfc_offset new_logical; + + p = (char *) buf; + if (s->logical_offset >= s->buffer_offset + && s->buffer_offset + s->active >= s->logical_offset) + { + nread = s->active - (s->logical_offset - s->buffer_offset); + memcpy (buf, s->buffer + (s->logical_offset - s->buffer_offset), + nread); + p += nread; + } + /* At this point we consider all bytes in the buffer discarded. */ + to_read = nbyte - nread; + new_logical = s->logical_offset + nread; + if (s->file_length != -1 && s->physical_offset != new_logical + && lseek (s->fd, new_logical, SEEK_SET) < 0) + return -1; + s->buffer_offset = s->physical_offset = new_logical; + if (to_read <= BUFFER_SIZE/2) + { + did_read = raw_read (s, s->buffer, BUFFER_SIZE); + s->physical_offset += did_read; + s->active = did_read; + did_read = (did_read > to_read) ? to_read : did_read; + memcpy (p, s->buffer, did_read); + } + else + { + did_read = raw_read (s, p, to_read); + s->physical_offset += did_read; + s->active = 0; + } + nbyte = did_read + nread; } - - s->buffer_offset = where; - - /* free the old buffer if necessary */ - - if (s->buffer != NULL && s->buffer != s->small_buffer) - free_mem (s->buffer); - - s->buffer = new_buffer; - s->len = read_len; + s->logical_offset += nbyte; + return nbyte; } - -/* fd_alloc_r_at()-- Allocate a stream buffer for reading. Either - * we've already buffered the data or we need to load it. Returns - * NULL on I/O error. */ - -static char * -fd_alloc_r_at (unix_stream * s, int *len) +static ssize_t +buf_write (unix_stream * s, const void * buf, ssize_t nbyte) { - gfc_offset m; - gfc_offset where = s->logical_offset; - - if (s->buffer != NULL && s->buffer_offset <= where && - where + *len <= s->buffer_offset + s->active) - { - - /* Return a position within the current buffer */ - - s->logical_offset = where + *len; - return s->buffer + where - s->buffer_offset; - } - - fd_alloc (s, where, len); - - m = where + s->active; - - if (s->physical_offset != m && lseek (s->fd, m, SEEK_SET) < 0) - return NULL; - - /* do_read() hangs on read from terminals for *BSD-systems. Only - use read() in that case. */ - - if (s->special_file) + if (s->ndirty == 0) + s->buffer_offset = s->logical_offset; + + /* Does the data fit into the buffer? As a special case, if the + buffer is empty and the request is bigger than BUFFER_SIZE/2, + write directly. This avoids the case where the buffer would have + to be flushed at every write. */ + if (!(s->ndirty == 0 && nbyte > BUFFER_SIZE/2) + && s->logical_offset + nbyte <= s->buffer_offset + BUFFER_SIZE + && s->buffer_offset <= s->logical_offset + && s->buffer_offset + s->ndirty >= s->logical_offset) { - ssize_t n; - - n = read (s->fd, s->buffer + s->active, s->len - s->active); - if (n < 0) - return NULL; - - s->physical_offset = m + n; - s->active += n; + memcpy (s->buffer + (s->logical_offset - s->buffer_offset), buf, nbyte); + int nd = (s->logical_offset - s->buffer_offset) + nbyte; + if (nd > s->ndirty) + s->ndirty = nd; } else { - size_t n; - - n = s->len - s->active; - if (do_read (s, s->buffer + s->active, &n) != 0) - return NULL; - - s->physical_offset = m + n; - s->active += n; - } - - if (s->active < *len) - *len = s->active; /* Bytes actually available */ - - s->logical_offset = where + *len; - - return s->buffer; -} - - -/* fd_alloc_w_at()-- Allocate a stream buffer for writing. Either - * we've already buffered the data or we need to load it. */ - -static char * -fd_alloc_w_at (unix_stream * s, int *len) -{ - gfc_offset n; - gfc_offset where = s->logical_offset; - - if (s->buffer == NULL || s->buffer_offset > where || - where + *len > s->buffer_offset + s->len) - { - - if (fd_flush (s) == FAILURE) - return NULL; - fd_alloc (s, where, len); - } - - /* Return a position within the current buffer */ - if (s->ndirty == 0 - || where > s->dirty_offset + s->ndirty - || s->dirty_offset > where + *len) - { /* Discontiguous blocks, start with a clean buffer. */ - /* Flush the buffer. */ - if (s->ndirty != 0) - fd_flush (s); - s->dirty_offset = where; - s->ndirty = *len; - } - else - { - gfc_offset start; /* Merge with the existing data. */ - if (where < s->dirty_offset) - start = where; - else - start = s->dirty_offset; - if (where + *len > s->dirty_offset + s->ndirty) - s->ndirty = where + *len - start; - else - s->ndirty = s->dirty_offset + s->ndirty - start; - s->dirty_offset = start; + /* Flush, and either fill the buffer with the new data, or if + the request is bigger than the buffer size, write directly + bypassing the buffer. */ + buf_flush (s); + if (nbyte <= BUFFER_SIZE/2) + { + memcpy (s->buffer, buf, nbyte); + s->buffer_offset = s->logical_offset; + s->ndirty += nbyte; + } + else + { + if (s->file_length != -1 && s->physical_offset != s->logical_offset + && lseek (s->fd, s->logical_offset, SEEK_SET) < 0) + return -1; + nbyte = raw_write (s, buf, nbyte); + s->physical_offset += nbyte; + } } - - s->logical_offset = where + *len; - + s->logical_offset += nbyte; /* 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) - s->active = n; - - return s->buffer + where - s->buffer_offset; + s->file_length = s->logical_offset; + return nbyte; } - -static try -fd_sfree (unix_stream * s) -{ - if (s->ndirty != 0 && - (s->buffer != s->small_buffer || options.all_unbuffered || - s->method == SYNC_UNBUFFERED)) - return fd_flush (s); - - return SUCCESS; -} - - -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; - return SUCCESS; - } - - if (lseek (s->fd, offset, SEEK_SET) >= 0) - { - s->physical_offset = s->logical_offset = offset; - s->active = 0; - return SUCCESS; - } - - return FAILURE; -} - - -/* truncate_file()-- Given a unit, truncate the file at the current - * position. Sets the physical location to the new end of the file. - * Returns nonzero on error. */ - -static try -fd_truncate (unix_stream * s) +static off_t +buf_seek (unix_stream * s, off_t offset, int whence) { - /* 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) + switch (whence) { - if (errno == ESPIPE) - return SUCCESS; - else - return FAILURE; + case SEEK_SET: + break; + case SEEK_CUR: + offset += s->logical_offset; + break; + case SEEK_END: + offset += s->file_length; + break; + default: + return -1; } - - /* Using ftruncate on a seekable special file (like /dev/null) - is undefined, so we treat it as if the ftruncate succeeded. */ - if (!s->special_file - && ( -#ifdef HAVE_FTRUNCATE - ftruncate (s->fd, s->logical_offset) != 0 -#elif defined HAVE_CHSIZE - chsize (s->fd, s->logical_offset) != 0 -#else - /* If we have neither, always fail and exit, noisily. */ - runtime_error ("required ftruncate or chsize support not present"), 1 -#endif - )) + if (offset < 0) { - /* The truncation failed and we need to handle this gracefully. - The file length remains the same, but the file-descriptor - offset needs adjustment per the successful lseek above. - (Similarly, the contents of the buffer isn't valid anymore.) - A ftruncate call does not affect the physical (file-descriptor) - offset, according to the ftruncate manual, so neither should a - failed call. */ - s->physical_offset = s->logical_offset; - s->active = 0; - return FAILURE; + errno = EINVAL; + return -1; } - - s->physical_offset = s->file_length = s->logical_offset; - s->active = 0; - return SUCCESS; + s->logical_offset = offset; + return 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) +static off_t +buf_tell (unix_stream * s) { - 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); - if (p) - memset (p, c, trans); - else - return FAILURE; - - bytes_left -= trans; - } - - return SUCCESS; + return s->logical_offset; } - -/* Stream read function. Avoids using a buffer for big reads. The - interface is like POSIX read(), but the nbytes argument is a - pointer; on return it contains the number of bytes written. The - function return value is the status indicator (0 for success). */ - static int -fd_read (unix_stream * s, void * buf, size_t * nbytes) +buf_truncate (unix_stream * s, off_t length) { - void *p; - int tmp, status; - - if (*nbytes < BUFFER_SIZE && s->method == SYNC_BUFFERED) - { - tmp = *nbytes; - p = fd_alloc_r_at (s, &tmp); - if (p) - { - *nbytes = tmp; - memcpy (buf, p, *nbytes); - return 0; - } - else - { - *nbytes = 0; - return errno; - } - } - - /* If the request is bigger than BUFFER_SIZE we flush the buffers - and read directly. */ - if (fd_flush (s) == FAILURE) - { - *nbytes = 0; - return errno; - } - - if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE) - { - *nbytes = 0; - return errno; - } + int r; - status = do_read (s, buf, nbytes); - reset_stream (s, *nbytes); - return status; + if (buf_flush (s) != 0) + return -1; + r = raw_truncate (s, length); + if (r == 0) + s->file_length = length; + return r; } - -/* Stream write function. Avoids using a buffer for big writes. The - interface is like POSIX write(), but the nbytes argument is a - pointer; on return it contains the number of bytes written. The - function return value is the status indicator (0 for success). */ - static int -fd_write (unix_stream * s, const void * buf, size_t * nbytes) -{ - void *p; - int tmp, status; - - if (*nbytes < BUFFER_SIZE && s->method == SYNC_BUFFERED) - { - tmp = *nbytes; - p = fd_alloc_w_at (s, &tmp); - if (p) - { - *nbytes = tmp; - memcpy (p, buf, *nbytes); - return 0; - } - else - { - *nbytes = 0; - return errno; - } - } - - /* If the request is bigger than BUFFER_SIZE we flush the buffers - and write directly. */ - if (fd_flush (s) == FAILURE) - { - *nbytes = 0; - return errno; - } - - if (is_seekable ((stream *) s) && fd_seek (s, s->logical_offset) == FAILURE) - { - *nbytes = 0; - return errno; - } - - status = do_write (s, buf, nbytes); - reset_stream (s, *nbytes); - return status; -} - - -static try -fd_close (unix_stream * s) +buf_close (unix_stream * s) { - if (fd_flush (s) == FAILURE) - return FAILURE; - - if (s->buffer != NULL && s->buffer != s->small_buffer) - free_mem (s->buffer); - - if (s->fd != STDOUT_FILENO && s->fd != STDERR_FILENO && s->fd != STDIN_FILENO) - { - if (close (s->fd) < 0) - return FAILURE; - } - - free_mem (s); - - return SUCCESS; + if (buf_flush (s) != 0) + return -1; + free_mem (s->buffer); + return raw_close (s); } - -static void -fd_open (unix_stream * s) +static int +buf_init (unix_stream * s) { - if (isatty (s->fd)) - s->method = SYNC_UNBUFFERED; - else - s->method = SYNC_BUFFERED; - - s->st.alloc_w_at = (void *) fd_alloc_w_at; - s->st.sfree = (void *) fd_sfree; - s->st.close = (void *) fd_close; - s->st.seek = (void *) fd_seek; - s->st.trunc = (void *) fd_truncate; - s->st.read = (void *) fd_read; - s->st.write = (void *) fd_write; - s->st.set = (void *) fd_sset; + s->st.read = (void *) buf_read; + s->st.write = (void *) buf_write; + s->st.seek = (void *) buf_seek; + s->st.tell = (void *) buf_tell; + s->st.truncate = (void *) buf_truncate; + s->st.close = (void *) buf_close; + s->st.flush = (void *) buf_flush; - s->buffer = NULL; + s->buffer = get_mem (BUFFER_SIZE); + return 0; } - - /********************************************************************* memory stream functions - These are used for internal files @@ -912,33 +585,33 @@ fd_open (unix_stream * s) *********************************************************************/ -static char * -mem_alloc_r_at (int_stream * s, int *len) +char * +mem_alloc_r (stream * strm, int * len) { + unix_stream * s = (unix_stream *) strm; gfc_offset n; gfc_offset where = s->logical_offset; if (where < s->buffer_offset || where > s->buffer_offset + s->active) return NULL; - s->logical_offset = where + *len; - n = s->buffer_offset + s->active - where; if (*len > n) *len = n; + s->logical_offset = where + *len; + return s->buffer + (where - s->buffer_offset); } -static char * -mem_alloc_w_at (int_stream * s, int *len) +char * +mem_alloc_w (stream * strm, int * len) { + unix_stream * s = (unix_stream *) strm; gfc_offset m; gfc_offset where = s->logical_offset; - assert (*len >= 0); /* Negative values not allowed. */ - m = where + *len; if (where < s->buffer_offset) @@ -955,25 +628,20 @@ mem_alloc_w_at (int_stream * s, int *len) /* Stream read function for internal units. */ -static int -mem_read (int_stream * s, void * buf, size_t * nbytes) +static ssize_t +mem_read (stream * s, void * buf, ssize_t nbytes) { void *p; - int tmp; + int nb = nbytes; - tmp = *nbytes; - p = mem_alloc_r_at (s, &tmp); + p = mem_alloc_r (s, &nb); if (p) { - *nbytes = tmp; - memcpy (buf, p, *nbytes); - return 0; + memcpy (buf, p, nb); + return (ssize_t) nb; } else - { - *nbytes = 0; - return 0; - } + return 0; } @@ -981,84 +649,90 @@ mem_read (int_stream * s, void * buf, size_t * nbytes) at the moment, as all internal IO is formatted and the formatted IO routines use mem_alloc_w_at. */ -static int -mem_write (int_stream * s, const void * buf, size_t * nbytes) +static ssize_t +mem_write (stream * s, const void * buf, ssize_t nbytes) { void *p; - int tmp; + int nb = nbytes; - tmp = *nbytes; - p = mem_alloc_w_at (s, &tmp); + p = mem_alloc_w (s, &nb); if (p) { - *nbytes = tmp; - memcpy (p, buf, *nbytes); - return 0; + memcpy (p, buf, nb); + return (ssize_t) nb; } else - { - *nbytes = 0; - return 0; - } + return 0; } -static int -mem_seek (int_stream * s, gfc_offset offset) +static off_t +mem_seek (stream * strm, off_t offset, int whence) { + unix_stream * s = (unix_stream *) strm; + switch (whence) + { + case SEEK_SET: + break; + case SEEK_CUR: + offset += s->logical_offset; + break; + case SEEK_END: + offset += s->file_length; + break; + default: + return -1; + } + + /* Note that for internal array I/O it's actually possible to have a + negative offset, so don't check for that. */ if (offset > s->file_length) { - errno = ESPIPE; - return FAILURE; + errno = EINVAL; + return -1; } s->logical_offset = offset; - return SUCCESS; + + /* Returning < 0 is the error indicator for sseek(), so return 0 if + offset is negative. Thus if the return value is 0, the caller + has to use stell() to get the real value of logical_offset. */ + if (offset >= 0) + return offset; + return 0; } -static try -mem_set (int_stream * s, int c, size_t n) +static off_t +mem_tell (stream * s) { - void *p; - int len; - - len = n; - - p = mem_alloc_w_at (s, &len); - if (p) - { - memset (p, c, len); - return SUCCESS; - } - else - return FAILURE; + return ((unix_stream *)s)->logical_offset; } static int -mem_truncate (int_stream * s __attribute__ ((unused))) +mem_truncate (unix_stream * s __attribute__ ((unused)), + off_t length __attribute__ ((unused))) { - return SUCCESS; + return 0; } -static try -mem_close (int_stream * s) +static int +mem_flush (unix_stream * s __attribute__ ((unused))) { - if (s != NULL) - free_mem (s); - - return SUCCESS; + return 0; } -static try -mem_sfree (int_stream * s __attribute__ ((unused))) +static int +mem_close (unix_stream * s) { - return SUCCESS; -} + if (s != NULL) + free_mem (s); + return 0; +} /********************************************************************* @@ -1071,7 +745,7 @@ mem_sfree (int_stream * s __attribute__ ((unused))) void empty_internal_buffer(stream *strm) { - int_stream * s = (int_stream *) strm; + unix_stream * s = (unix_stream *) strm; memset(s->buffer, ' ', s->file_length); } @@ -1080,10 +754,10 @@ empty_internal_buffer(stream *strm) stream * open_internal (char *base, int length, gfc_offset offset) { - int_stream *s; + unix_stream *s; - s = get_mem (sizeof (int_stream)); - memset (s, '\0', sizeof (int_stream)); + s = get_mem (sizeof (unix_stream)); + memset (s, '\0', sizeof (unix_stream)); s->buffer = base; s->buffer_offset = offset; @@ -1091,14 +765,13 @@ open_internal (char *base, int length, gfc_offset offset) s->logical_offset = 0; s->active = s->file_length = length; - s->st.alloc_w_at = (void *) mem_alloc_w_at; - s->st.sfree = (void *) mem_sfree; s->st.close = (void *) mem_close; s->st.seek = (void *) mem_seek; - s->st.trunc = (void *) mem_truncate; + s->st.tell = (void *) mem_tell; + s->st.truncate = (void *) mem_truncate; s->st.read = (void *) mem_read; s->st.write = (void *) mem_write; - s->st.set = (void *) mem_set; + s->st.flush = (void *) mem_flush; return (stream *) s; } @@ -1133,7 +806,14 @@ fd_to_stream (int fd, int prot) s->special_file = !S_ISREG (statbuf.st_mode); - fd_open (s); + if (isatty (s->fd) || options.all_unbuffered + ||(options.unbuffered_preconnected && + (s->fd == STDIN_FILENO + || s->fd == STDOUT_FILENO + || s->fd == STDERR_FILENO))) + raw_init (s); + else + buf_init (s); return (stream *) s; } @@ -1417,8 +1097,6 @@ output_stream (void) #endif s = fd_to_stream (STDOUT_FILENO, PROT_WRITE); - if (options.unbuffered_preconnected) - ((unix_stream *) s)->method = SYNC_UNBUFFERED; return s; } @@ -1436,8 +1114,6 @@ error_stream (void) #endif s = fd_to_stream (STDERR_FILENO, PROT_WRITE); - if (options.unbuffered_preconnected) - ((unix_stream *) s)->method = SYNC_UNBUFFERED; return s; } @@ -1668,7 +1344,7 @@ flush_all_units_1 (gfc_unit *u, int min_unit) if (__gthread_mutex_trylock (&u->lock)) return u; if (u->s) - flush (u->s); + sflush (u->s); __gthread_mutex_unlock (&u->lock); } u = u->right; @@ -1698,7 +1374,7 @@ flush_all_units (void) if (u->closed == 0) { - flush (u->s); + sflush (u->s); __gthread_mutex_lock (&unit_lock); __gthread_mutex_unlock (&u->lock); (void) predec_waiting_locked (u); @@ -1715,40 +1391,6 @@ flush_all_units (void) } -/* stream_at_bof()-- Returns nonzero if the stream is at the beginning - * of the file. */ - -int -stream_at_bof (stream * s) -{ - unix_stream *us; - - if (!is_seekable (s)) - return 0; - - us = (unix_stream *) s; - - return us->logical_offset == 0; -} - - -/* stream_at_eof()-- Returns nonzero if the stream is at the end - * of the file. */ - -int -stream_at_eof (stream * s) -{ - unix_stream *us; - - if (!is_seekable (s)) - return 0; - - us = (unix_stream *) s; - - return us->logical_offset == us->dirty_offset; -} - - /* delete_file()-- Given a unit structure, delete the file associated * with the unit. Returns nonzero if something went wrong. */ @@ -1954,16 +1596,15 @@ inquire_readwrite (const char *string, int len) gfc_offset file_length (stream * s) { - return ((unix_stream *) s)->file_length; -} - - -/* file_position()-- Return the current position of the file */ - -gfc_offset -file_position (stream *s) -{ - return ((unix_stream *) s)->logical_offset; + off_t curr, end; + if (!is_seekable (s)) + return -1; + curr = stell (s); + if (curr == -1) + return curr; + end = sseek (s, 0, SEEK_END); + sseek (s, curr, SEEK_SET); + return end; } @@ -1988,12 +1629,6 @@ is_special (stream *s) } -try -flush (stream *s) -{ - return fd_flush( (unix_stream *) s); -} - int stream_isatty (stream *s) { @@ -2010,12 +1645,6 @@ stream_ttyname (stream *s __attribute__ ((unused))) #endif } -gfc_offset -stream_offset (stream *s) -{ - return (((unix_stream *) s)->logical_offset); -} - /* How files are stored: This is an operating-system specific issue, and therefore belongs here. There are three cases to consider. diff --git a/libgfortran/io/write.c b/libgfortran/io/write.c index e3d38e638e8..0b439dd7bd2 100644 --- a/libgfortran/io/write.c +++ b/libgfortran/io/write.c @@ -113,7 +113,7 @@ write_utf8_char4 (st_parameter_dt *dtp, gfc_char4_t *source, gfc_char4_t c; static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; - size_t nbytes; + int nbytes; uchar buf[6], d, *q; /* Take care of preceding blanks. */ @@ -784,8 +784,7 @@ write_x (st_parameter_dt *dtp, int len, int nspaces) p = write_block (dtp, len); if (p == NULL) return; - - if (nspaces > 0) + if (nspaces > 0 && len - nspaces >= 0) memset (&p[len - nspaces], ' ', nspaces); } @@ -1173,7 +1172,7 @@ namelist_write_newline (st_parameter_dt *dtp) /* Now seek to this record */ record = record * dtp->u.p.current_unit->recl; - if (sseek (dtp->u.p.current_unit->s, record) == FAILURE) + if (sseek (dtp->u.p.current_unit->s, record, SEEK_SET) < 0) { generate_error (&dtp->common, LIBERROR_INTERNAL_UNIT, NULL); return; |