Index: gcc/testsuite/ChangeLog =================================================================== --- gcc/testsuite/ChangeLog (revision 119411) +++ gcc/testsuite/ChangeLog (working copy) @@ -1,3 +1,12 @@ +2006-12-01 Thomas Koenig + + PR libfortran/29568 + * gfortran.dg/convert_implied_open.f90: Change to + new default record length. + * gfortran.dg/unf_short_record_1.f90: Adapt to + new error message. + * gfortran.dg/unformatted_subrecords_1.f90: New test. + 2006-12-01 Andrew MacLeod * gcc.dg/max-1.c: Remove reference to -fno-tree-lrs option. Index: gcc/testsuite/gfortran.dg/convert_implied_open.f90 =================================================================== --- gcc/testsuite/gfortran.dg/convert_implied_open.f90 (revision 119411) +++ gcc/testsuite/gfortran.dg/convert_implied_open.f90 (working copy) @@ -3,13 +3,13 @@ ! PR 26735 - implied open didn't use to honor -fconvert program main implicit none - integer (kind=8) :: i1, i2, i3 - write (10) 1_8 + integer (kind=4) :: i1, i2, i3 + write (10) 1_4 close (10) - open (10, form="unformatted", access="direct", recl=8) + open (10, form="unformatted", access="direct", recl=4) read (10,rec=1) i1 read (10,rec=2) i2 read (10,rec=3) i3 - if (i1 /= 8 .or. i2 /= 1 .or. i3 /= 8) call abort + if (i1 /= 4 .or. i2 /= 1 .or. i3 /= 4) call abort close (10,status="delete") end program main Index: gcc/testsuite/gfortran.dg/unformatted_subrecord_1.f90 =================================================================== --- gcc/testsuite/gfortran.dg/unformatted_subrecord_1.f90 (revision 0) +++ gcc/testsuite/gfortran.dg/unformatted_subrecord_1.f90 (revision 0) @@ -0,0 +1,45 @@ +! { dg-do run } +! { dg-options "-fmax-subrecord-length=16" } +! Test Intel record markers with 16-byte subrecord sizes. +program main + implicit none + integer, dimension(20) :: n + integer, dimension(30) :: m + integer :: i + real :: r + integer :: k + ! Maximum subrecord length is 16 here, or the test will fail. + open (10, file="f10.dat", & + form="unformatted", access="sequential") + n = (/ (i**2, i=1, 20) /) + write (10) n + close (10) + ! Read back the file, including record markers. + open (10, file="f10.dat", form="unformatted", access="stream") + read (10) m + if (any(m .ne. (/ -16, 1, 4, 9, 16, 16, -16, 25, 36, 49, 64, & + -16, -16, 81, 100, 121, 144, -16, -16, 169, 196, 225, & + 256, -16, 16, 289, 324, 361, 400, -16 /))) call abort + close (10) + open (10, file="f10.dat", form="unformatted", & + access="sequential") + m = 42 + read (10) m(1:5) + if (any(m(1:5) .ne. (/ 1, 4, 9, 16, 25 /))) call abort + if (any(m(6:30) .ne. 42)) call abort + backspace 10 + n = 0 + read (10) n(1:5) + if (any(n(1:5) .ne. (/ 1, 4, 9, 16, 25 /))) call abort + if (any(n(6:20) .ne. 0)) call abort + ! Append to the end of the file + write (10) 3.14 + ! Test multiple backspace statements + backspace 10 + backspace 10 + read (10) k + if (k .ne. 1) call abort + read (10) r + if (abs(r-3.14) .gt. 1e-7) call abort + close (10, status="delete") +end program main Index: gcc/testsuite/gfortran.dg/unf_short_record_1.f90 =================================================================== --- gcc/testsuite/gfortran.dg/unf_short_record_1.f90 (revision 119411) +++ gcc/testsuite/gfortran.dg/unf_short_record_1.f90 (working copy) @@ -11,7 +11,7 @@ program main read (10, err=20, iomsg=msg) a call abort 20 continue - if (msg .ne. "Short record on unformatted read") call abort + if (msg .ne. "I/O past end of record on unformatted file") call abort if (a(1) .ne. 'a' .or. a(2) .ne. 'b' .or. a(3) .ne. 'b') call abort close (10, status="delete") end program main Index: gcc/fortran/gfortran.h =================================================================== --- gcc/fortran/gfortran.h (revision 119411) +++ gcc/fortran/gfortran.h (working copy) @@ -59,6 +59,9 @@ char *alloca (); #define GFC_MAX_DIMENSIONS 7 /* Maximum dimensions in an array. */ #define GFC_LETTERS 26 /* Number of letters in the alphabet. */ +#define MAX_SUBRECORD_LENGTH 2147483639 /* 2**31-9 */ + + #define free(x) Use_gfc_free_instead_of_free() #define gfc_is_whitespace(c) ((c==' ') || (c=='\t')) @@ -1661,12 +1664,12 @@ typedef struct int fshort_enums; int convert; int record_marker; + int max_subrecord_length; } gfc_option_t; extern gfc_option_t gfc_option; - /* Constructor nodes for array and structure constructors. */ typedef struct gfc_constructor { Index: gcc/fortran/lang.opt =================================================================== --- gcc/fortran/lang.opt (revision 119411) +++ gcc/fortran/lang.opt (working copy) @@ -189,6 +189,10 @@ fmax-identifier-length= Fortran RejectNegative Joined UInteger -fmax-identifier-length= Maximum identifier length +fmax-subrecord-length= +Fortran RejectNegative Joined UInteger +-fmax-subrecord-length= Maximum length for subrecords + fmax-stack-var-size= Fortran RejectNegative Joined UInteger -fmax-stack-var-size= Size in bytes of the largest array that will be put on the stack Index: gcc/fortran/ChangeLog =================================================================== --- gcc/fortran/ChangeLog (revision 119411) +++ gcc/fortran/ChangeLog (working copy) @@ -1,3 +1,20 @@ +2006-12-01 Thomas Koenig + + PR libfortran/29568 + * gfortran.h (gfc_option_t): Add max_subrecord_length. + (top level): Define MAX_SUBRECORD_LENGTH. + * lang.opt: Add option -fmax-subrecord-length=. + * trans-decl.c: Add new function set_max_subrecord_length. + (gfc_generate_function_code): If we are within the main + program and max_subrecord_length has been set, call + set_max_subrecord_length. + * options.c (gfc_init_options): Add defaults for + max_subrecord_lenght, convert and record_marker. + (gfc_handle_option): Add handling for + -fmax_subrecord_length. + * invoke.texi: Document the new default for + -frecord-marker=. + 2006-11-28 Paul Thomas PR fortran/29976 Index: gcc/fortran/invoke.texi =================================================================== --- gcc/fortran/invoke.texi (revision 119411) +++ gcc/fortran/invoke.texi (working copy) @@ -650,13 +650,17 @@ variable override the default specified @cindex -frecord-marker=@var{length} @item -frecord-marker=@var{length} Specify the length of record markers for unformatted files. -Valid values for @var{length} are 4 and 8. Default is whatever -@code{off_t} is specified to be on that particular system. -Note that specifying @var{length} as 4 limits the record -length of unformatted files to 2 GB. This option does not -extend the maximum possible record length on systems where -@code{off_t} is a four_byte quantity. - +Valid values for @var{length} are 4 and 8. Default is 4. +@emph{This is different from previous versions of gfortran}, +which specified a default record marker length of 8 on most +systems. If you want to read or write files compatible +with earlier versions of gfortran, use @samp{-frecord-marker=8}. + +@cindex -fmax-subrecord-length=@var{length} +@item -fmax-subrecord-length=@var{length} +Specify the maximum length for a subrecord. The maximum permitted +value for length is 2147483639, which is also the default. Only +really useful for use by the gfortran testsuite. @end table @node Code Gen Options Index: gcc/fortran/trans-decl.c =================================================================== --- gcc/fortran/trans-decl.c (revision 119411) +++ gcc/fortran/trans-decl.c (working copy) @@ -94,6 +94,7 @@ tree gfor_fndecl_set_fpe; tree gfor_fndecl_set_std; tree gfor_fndecl_set_convert; tree gfor_fndecl_set_record_marker; +tree gfor_fndecl_set_max_subrecord_length; tree gfor_fndecl_ctime; tree gfor_fndecl_fdate; tree gfor_fndecl_ttynam; @@ -2379,6 +2380,10 @@ gfc_build_builtin_function_decls (void) gfc_build_library_function_decl (get_identifier (PREFIX("set_record_marker")), void_type_node, 1, gfc_c_int_type_node); + gfor_fndecl_set_max_subrecord_length = + gfc_build_library_function_decl (get_identifier (PREFIX("set_max_subrecord_length")), + void_type_node, 1, gfc_c_int_type_node); + gfor_fndecl_in_pack = gfc_build_library_function_decl ( get_identifier (PREFIX("internal_pack")), pvoid_type_node, 1, pvoid_type_node); @@ -3187,6 +3192,18 @@ gfc_generate_function_code (gfc_namespac } + if (sym->attr.is_main_program && gfc_option.max_subrecord_length != 0) + { + tree arglist, gfc_c_int_type_node; + + gfc_c_int_type_node = gfc_get_int_type (gfc_c_int_kind); + arglist = gfc_chainon_list (NULL_TREE, + build_int_cst (gfc_c_int_type_node, + gfc_option.max_subrecord_length)); + tmp = build_function_call_expr (gfor_fndecl_set_max_subrecord_length, arglist); + gfc_add_expr_to_block (&body, tmp); + } + if (TREE_TYPE (DECL_RESULT (fndecl)) != void_type_node && sym->attr.subroutine) { Index: gcc/fortran/options.c =================================================================== --- gcc/fortran/options.c (revision 119411) +++ gcc/fortran/options.c (working copy) @@ -51,6 +51,9 @@ gfc_init_options (unsigned int argc ATTR gfc_option.max_continue_fixed = 19; gfc_option.max_continue_free = 39; gfc_option.max_identifier_length = GFC_MAX_SYMBOL_LEN; + gfc_option.max_subrecord_length = 0; + gfc_option.convert = CONVERT_NATIVE; + gfc_option.record_marker = 0; gfc_option.verbose = 0; gfc_option.warn_aliasing = 0; @@ -636,6 +639,12 @@ gfc_handle_option (size_t scode, const c case OPT_frecord_marker_8: gfc_option.record_marker = 8; break; + + case OPT_fmax_subrecord_length_: + if (value > MAX_SUBRECORD_LENGTH) + gfc_fatal_error ("Maximum subrecord length cannot exceed %d", MAX_SUBRECORD_LENGTH); + + gfc_option.max_subrecord_length = value; } return result; Index: libgfortran/runtime/compile_options.c =================================================================== --- libgfortran/runtime/compile_options.c (revision 119411) +++ libgfortran/runtime/compile_options.c (working copy) @@ -86,13 +86,11 @@ set_record_marker (int val) switch(val) { case 4: - if (sizeof (GFC_INTEGER_4) != sizeof (gfc_offset)) - compile_options.record_marker = sizeof (GFC_INTEGER_4); + compile_options.record_marker = sizeof (GFC_INTEGER_4); break; case 8: - if (sizeof (GFC_INTEGER_8) != sizeof (gfc_offset)) - compile_options.record_marker = sizeof (GFC_INTEGER_8); + compile_options.record_marker = sizeof (GFC_INTEGER_8); break; default: @@ -100,3 +98,17 @@ set_record_marker (int val) break; } } + +extern void set_max_subrecord_length (int); +export_proto (set_max_subrecord_length); + +void set_max_subrecord_length(int val) +{ + if (val > GFC_MAX_SUBRECORD_LENGTH || val < 1) + { + runtime_error ("Invalid value for maximum subrecord length"); + return; + } + + compile_options.max_subrecord_length = val; +} Index: libgfortran/runtime/error.c =================================================================== --- libgfortran/runtime/error.c (revision 119411) +++ libgfortran/runtime/error.c (working copy) @@ -437,7 +437,7 @@ translate_error (int code) break; case ERROR_SHORT_RECORD: - p = "Short record on unformatted read"; + p = "I/O past end of record on unformatted file"; break; default: Index: libgfortran/ChangeLog =================================================================== --- libgfortran/ChangeLog (revision 119411) +++ libgfortran/ChangeLog (working copy) @@ -1,3 +1,46 @@ +2006-12-01 Thomas Koenig + + PR libfortran/29568 + * libgfortran/libgfortran.h (compile_options_t): Add + record_marker. (top level): Define GFC_MAX_SUBRECORD_LENGTH. + * runtime/compile_options.c (set_record_marker): Change + default to four-byte record marker. + (set_max_subrecord_length): New function. + * runtime/error.c (translate_error): Change error message + for short record on unformatted read. + * io/io.h (gfc_unit): Add recl_subrecord, bytes_left_subrecord + and continued. + * io/file_pos.c (unformatted_backspace): Change default of record + marker size to four bytes. Loop over subrecords. + * io/open.c: Default recl is max_offset. If + compile_options.max_subrecord_length has been set, set set + u->recl_subrecord to its value, to the maximum value otherwise. + * io/transfer.c (top level): Add prototypes for us_read, us_write, + next_record_r_unf and next_record_w_unf. + (read_block_direct): Separate codepaths for unformatted direct + and unformatted sequential. If a recl has been set by the + user, use the number of bytes left for the record if it is smaller + than the read request. Loop over subrecords. Set an error if the + user has set a recl and the read was short. + (write_buf): Separate codepaths for unformatted direct and + unformatted sequential. If a recl has been set by the + user, use the number of bytes left for the record if it is smaller + than the read request. Loop over subrecords. Set an error if the + user has set a recl and the read was short. + (us_read): Add parameter continued (to indicate that bytes_left + should not be intialized). Change default of record marker size + to four bytes. Use subrecord. If the subrecord length is smaller than + zero, this indicates a continuation. + (us_write): Add parameter continued (to indicate that the continued + flag should be set). Use subrecord. + (pre_position): Use 0 for continued on us_write and us_read calls. + (skip_record): New function. + (next_record_r_unf): New function. + (next_record_r): Use next_record_r_unf. + (write_us_marker): Default size for record markers is four bytes. + (next_record_w_unf): New function. + (next_record_w): Use next_record_w_unf. + 2006-11-25 Francois-Xavier Coudert * Makefile.am: Remove intrinsics/erf.c and intrinsics/bessel.c. Index: libgfortran/libgfortran.h =================================================================== --- libgfortran/libgfortran.h (revision 119411) +++ libgfortran/libgfortran.h (working copy) @@ -370,6 +370,7 @@ typedef struct int pedantic; int convert; size_t record_marker; + int max_subrecord_length; } compile_options_t; @@ -379,6 +380,7 @@ internal_proto(compile_options); extern void init_compile_options (void); internal_proto(init_compile_options); +#define GFC_MAX_SUBRECORD_LENGTH 2147483639 /* 2**31 - 9 */ /* Structure for statement options. */ Index: libgfortran/io/file_pos.c =================================================================== --- libgfortran/io/file_pos.c (revision 119411) +++ libgfortran/io/file_pos.c (working copy) @@ -98,7 +98,7 @@ formatted_backspace (st_parameter_filepo /* unformatted_backspace(fpp) -- Move the file backwards for an unformatted sequential file. We are guaranteed to be between records on entry and - we have to shift to the previous record. */ + we have to shift to the previous record. Loop over subrecords. */ static void unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u) @@ -107,74 +107,74 @@ unformatted_backspace (st_parameter_file GFC_INTEGER_4 m4; GFC_INTEGER_8 m8; int length, length_read; + int continued; char *p; if (compile_options.record_marker == 0) - length = sizeof (gfc_offset); + length = sizeof (GFC_INTEGER_4); else length = compile_options.record_marker; - length_read = length; + do + { + length_read = length; - p = salloc_r_at (u->s, &length_read, - file_position (u->s) - length); - if (p == NULL || length_read != length) - goto io_error; + p = salloc_r_at (u->s, &length_read, + file_position (u->s) - length); + if (p == NULL || length_read != length) + goto io_error; - /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ - if (u->flags.convert == CONVERT_NATIVE) - { - switch (compile_options.record_marker) + /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ + if (u->flags.convert == CONVERT_NATIVE) { - case 0: - memcpy (&m, p, sizeof(gfc_offset)); - break; - - case sizeof(GFC_INTEGER_4): - memcpy (&m4, p, sizeof (m4)); - m = m4; - break; - - case sizeof(GFC_INTEGER_8): - memcpy (&m8, p, sizeof (m8)); - m = m8; - break; - - default: - runtime_error ("Illegal value for record marker"); - break; + switch (length) + { + case sizeof(GFC_INTEGER_4): + memcpy (&m4, p, sizeof (m4)); + m = m4; + break; + + case sizeof(GFC_INTEGER_8): + memcpy (&m8, p, sizeof (m8)); + m = m8; + break; + + default: + runtime_error ("Illegal value for record marker"); + break; + } } - } - else - { - switch (compile_options.record_marker) + else { - case 0: - reverse_memcpy (&m, p, sizeof(gfc_offset)); - break; - - case sizeof(GFC_INTEGER_4): - reverse_memcpy (&m4, p, sizeof (m4)); - m = m4; - break; - - case sizeof(GFC_INTEGER_8): - reverse_memcpy (&m8, p, sizeof (m8)); - m = m8; - break; - - default: - runtime_error ("Illegal value for record marker"); - break; - } + switch (length) + { + case sizeof(GFC_INTEGER_4): + reverse_memcpy (&m4, p, sizeof (m4)); + m = m4; + break; + + case sizeof(GFC_INTEGER_8): + reverse_memcpy (&m8, p, sizeof (m8)); + m = m8; + break; + + default: + runtime_error ("Illegal value for record marker"); + break; + } - } - - if ((new = file_position (u->s) - m - 2*length) < 0) - new = 0; + } - if (sseek (u->s, new) == FAILURE) - goto io_error; + continued = m < 0; + if (continued) + m = -m; + + if ((new = file_position (u->s) - m - 2*length) < 0) + new = 0; + + if (sseek (u->s, new) == FAILURE) + goto io_error; + } while (continued); u->last_record--; return; Index: libgfortran/io/open.c =================================================================== --- libgfortran/io/open.c (revision 119411) +++ libgfortran/io/open.c (working copy) @@ -413,23 +413,29 @@ new_unit (st_parameter_open *opp, gfc_un else { u->flags.has_recl = 0; - switch (compile_options.record_marker) + u->recl = max_offset; + if (compile_options.max_subrecord_length) { - case 0: - u->recl = max_offset; - break; - - case sizeof (GFC_INTEGER_4): - u->recl = GFC_INTEGER_4_HUGE; - break; - - case sizeof (GFC_INTEGER_8): - u->recl = max_offset; - break; - - default: - runtime_error ("Illegal value for record marker"); - break; + u->recl_subrecord = compile_options.max_subrecord_length; + } + else + { + switch (compile_options.record_marker) + { + case 0: + /* Fall through */ + case sizeof (GFC_INTEGER_4): + u->recl_subrecord = GFC_MAX_SUBRECORD_LENGTH; + break; + + case sizeof (GFC_INTEGER_8): + u->recl_subrecord = max_offset - 16; + break; + + default: + runtime_error ("Illegal value for record marker"); + break; + } } } Index: libgfortran/io/io.h =================================================================== --- libgfortran/io/io.h (revision 119411) +++ libgfortran/io/io.h (working copy) @@ -499,12 +499,19 @@ typedef struct gfc_unit unit_mode mode; unit_flags flags; - /* recl -- Record length of the file. - last_record -- Last record number read or written - maxrec -- Maximum record number in a direct access file - bytes_left -- Bytes left in current record. - strm_pos -- Current position in file for STREAM I/O. */ - gfc_offset recl, last_record, maxrec, bytes_left, strm_pos; + /* recl -- Record length of the file. + last_record -- Last record number read or written + maxrec -- Maximum record number in a direct access file + bytes_left -- Bytes left in current record. + strm_pos -- Current position in file for STREAM I/O. + recl_subrecord -- Maximum length for subrecord. + bytes_left_subrecord -- Bytes left in current subrecord. */ + gfc_offset recl, last_record, maxrec, bytes_left, strm_pos, + recl_subrecord, bytes_left_subrecord; + + /* Set to 1 if we have read a subrecord. */ + + int continued; __gthread_mutex_t lock; /* Number of threads waiting to acquire this unit's lock. Index: libgfortran/io/transfer.c =================================================================== --- libgfortran/io/transfer.c (revision 119411) +++ libgfortran/io/transfer.c (working copy) @@ -82,6 +82,11 @@ extern void transfer_array (st_parameter gfc_charlen_type); export_proto(transfer_array); +static void us_read (st_parameter_dt *, int); +static void us_write (st_parameter_dt *, int); +static void next_record_r_unf (st_parameter_dt *, int); +static void next_record_w_unf (st_parameter_dt *, int); + static const st_option advance_opt[] = { {"yes", ADVANCE_YES}, {"no", ADVANCE_NO}, @@ -336,12 +341,16 @@ read_block (st_parameter_dt *dtp, int *l } -/* Reads a block directly into application data space. */ +/* Reads a block directly into application data space. This is for + unformatted files. */ static void read_block_direct (st_parameter_dt *dtp, void *buf, size_t *nbytes) { - size_t nread; + size_t to_read_record; + size_t have_read_record; + size_t to_read_subrecord; + size_t have_read_subrecord; int short_record; if (is_stream_io (dtp)) @@ -353,62 +362,169 @@ read_block_direct (st_parameter_dt *dtp, return; } - nread = *nbytes; - if (sread (dtp->u.p.current_unit->s, buf, &nread) != 0) + to_read_record = *nbytes; + have_read_record = to_read_record; + if (sread (dtp->u.p.current_unit->s, buf, &have_read_record) != 0) { generate_error (&dtp->common, ERROR_OS, NULL); return; } - dtp->u.p.current_unit->strm_pos += (gfc_offset) nread; - - if (nread != *nbytes) /* Short read, e.g. if we hit EOF. */ - generate_error (&dtp->common, ERROR_END, NULL); + dtp->u.p.current_unit->strm_pos += (gfc_offset) have_read_record; + if (to_read_record != have_read_record) + { + /* Short read, e.g. if we hit EOF. */ + generate_error (&dtp->common, ERROR_END, NULL); + return; + } return; } - /* Unformatted file with records */ - if (dtp->u.p.current_unit->bytes_left < (gfc_offset) *nbytes) + if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT) { - short_record = 1; - nread = (size_t) dtp->u.p.current_unit->bytes_left; - *nbytes = nread; + if (dtp->u.p.current_unit->bytes_left < (gfc_offset) *nbytes) + { + short_record = 1; + to_read_record = (size_t) dtp->u.p.current_unit->bytes_left; + *nbytes = to_read_record; - if (dtp->u.p.current_unit->bytes_left == 0) + if (dtp->u.p.current_unit->bytes_left == 0) + { + dtp->u.p.current_unit->endfile = AT_ENDFILE; + generate_error (&dtp->common, ERROR_END, NULL); + return; + } + } + + else + { + short_record = 0; + to_read_record = *nbytes; + } + + dtp->u.p.current_unit->bytes_left -= to_read_record; + + if (sread (dtp->u.p.current_unit->s, buf, &to_read_record) != 0) + { + generate_error (&dtp->common, ERROR_OS, NULL); + return; + } + + if (to_read_record != *nbytes) /* Short read, e.g. if we hit EOF. */ { - dtp->u.p.current_unit->endfile = AT_ENDFILE; + *nbytes = to_read_record; generate_error (&dtp->common, ERROR_END, NULL); return; } + + if (short_record) + { + generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL); + return; + } + return; } + /* Unformatted sequential. We loop over the subrecords, reading + until the request has been fulfilled or the record has run out + of continuation subrecords. */ + + /* Check whether we exceed the total record length. */ + + if (dtp->u.p.current_unit->flags.has_recl) + { + to_read_record = + *nbytes > (size_t) dtp->u.p.current_unit->bytes_left ? + *nbytes : (size_t) dtp->u.p.current_unit->bytes_left; + short_record = 1; + } else { + to_read_record = *nbytes; short_record = 0; - nread = *nbytes; } + have_read_record = 0; - dtp->u.p.current_unit->bytes_left -= nread; - - if (sread (dtp->u.p.current_unit->s, buf, &nread) != 0) + while(1) { - generate_error (&dtp->common, ERROR_OS, NULL); - return; - } + 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_record -= to_read_subrecord; - if (nread != *nbytes) /* Short read, e.g. if we hit EOF. */ - { - *nbytes = nread; - generate_error (&dtp->common, ERROR_END, NULL); - return; + if (dtp->u.p.current_unit->bytes_left_subrecord == 0) + { + if (dtp->u.p.current_unit->continued) + { + /* Skip to the next subrecord */ + next_record_r_unf (dtp, 0); + us_read (dtp, 1); + continue; + } + else + { + dtp->u.p.current_unit->endfile = AT_ENDFILE; + generate_error (&dtp->common, ERROR_END, NULL); + return; + } + } + } + + else + { + to_read_subrecord = to_read_record; + to_read_record = 0; + } + + dtp->u.p.current_unit->bytes_left_subrecord -= to_read_subrecord; + + have_read_subrecord = to_read_subrecord; + if (sread (dtp->u.p.current_unit->s, buf + have_read_record, + &have_read_subrecord) != 0) + { + generate_error (&dtp->common, ERROR_OS, NULL); + return; + } + + have_read_record += have_read_subrecord; + + if (to_read_subrecord != have_read_subrecord) /* Short read, + e.g. if we hit EOF. */ + { + *nbytes = have_read_record; + generate_error (&dtp->common, ERROR_END, NULL); + return; + } + + if (to_read_record > 0) + { + if (dtp->u.p.current_unit->continued) + { + next_record_r_unf (dtp, 0); + us_read (dtp, 1); + } + else + { + generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL); + return; + } + } + else + { + /* Normal exit, the read request has been fulfilled. */ + break; + } } + dtp->u.p.current_unit->bytes_left -= have_read_record; if (short_record) { generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL); return; } + return; } @@ -471,11 +587,20 @@ write_block (st_parameter_dt *dtp, int l } -/* High level interface to swrite(), taking care of errors. */ +/* High level interface to swrite(), taking care of errors. This is only + called for unformatted files. There are three cases to consider: + Stream I/O, unformatted direct, unformatted sequential. */ static try write_buf (st_parameter_dt *dtp, void *buf, size_t nbytes) { + + size_t have_written, to_write_subrecord; + int short_record; + + + /* Stream I/O. */ + if (is_stream_io (dtp)) { if (sseek (dtp->u.p.current_unit->s, @@ -484,42 +609,88 @@ write_buf (st_parameter_dt *dtp, void *b generate_error (&dtp->common, ERROR_OS, NULL); return FAILURE; } + + if (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0) + { + generate_error (&dtp->common, ERROR_OS, NULL); + return FAILURE; + } + + dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; + + return SUCCESS; } - else + + /* Unformatted direct access. */ + + if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT) { if (dtp->u.p.current_unit->bytes_left < (gfc_offset) nbytes) { - /* For preconnected units with default record length, set - bytes left to unit record length and proceed, otherwise - error. */ - if ((dtp->u.p.current_unit->unit_number == options.stdout_unit - || dtp->u.p.current_unit->unit_number == options.stderr_unit) - && dtp->u.p.current_unit->recl == DEFAULT_RECL) - dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl; - else - { - if (dtp->u.p.current_unit->flags.access == ACCESS_DIRECT) - generate_error (&dtp->common, ERROR_DIRECT_EOR, NULL); - else - generate_error (&dtp->common, ERROR_EOR, NULL); - return FAILURE; - } + generate_error (&dtp->common, ERROR_DIRECT_EOR, NULL); + return FAILURE; + } + + if (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0) + { + generate_error (&dtp->common, ERROR_OS, NULL); + return FAILURE; } - dtp->u.p.current_unit->bytes_left -= (gfc_offset) nbytes; + dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; + + return SUCCESS; + } - if (swrite (dtp->u.p.current_unit->s, buf, &nbytes) != 0) + /* Unformatted sequential. */ + + have_written = 0; + + if (dtp->u.p.current_unit->flags.has_recl + && (gfc_offset) nbytes > dtp->u.p.current_unit->bytes_left) { - generate_error (&dtp->common, ERROR_OS, NULL); - return FAILURE; + nbytes = dtp->u.p.current_unit->bytes_left; + short_record = 1; + } + else + { + short_record = 0; } - if ((dtp->common.flags & IOPARM_DT_HAS_SIZE) != 0) - dtp->u.p.size_used += (gfc_offset) nbytes; + while (1) + { + + to_write_subrecord = + (size_t) dtp->u.p.current_unit->bytes_left_subrecord < nbytes ? + (size_t) dtp->u.p.current_unit->bytes_left_subrecord : nbytes; + + dtp->u.p.current_unit->bytes_left_subrecord -= + (gfc_offset) to_write_subrecord; - dtp->u.p.current_unit->strm_pos += (gfc_offset) nbytes; + if (swrite (dtp->u.p.current_unit->s, buf + have_written, + &to_write_subrecord) != 0) + { + generate_error (&dtp->common, ERROR_OS, NULL); + return FAILURE; + } + + dtp->u.p.current_unit->strm_pos += (gfc_offset) to_write_subrecord; + nbytes -= to_write_subrecord; + have_written += to_write_subrecord; + if (nbytes == 0) + break; + + next_record_w_unf (dtp, 1); + us_write (dtp, 1); + } + dtp->u.p.current_unit->bytes_left -= have_written; + if (short_record) + { + generate_error (&dtp->common, ERROR_SHORT_RECORD, NULL); + return FAILURE; + } return SUCCESS; } @@ -1357,7 +1528,7 @@ transfer_array (st_parameter_dt *dtp, gf /* Preposition a sequential unformatted file while reading. */ static void -us_read (st_parameter_dt *dtp) +us_read (st_parameter_dt *dtp, int continued) { char *p; int n; @@ -1370,7 +1541,7 @@ us_read (st_parameter_dt *dtp) return; if (compile_options.record_marker == 0) - n = sizeof (gfc_offset); + n = sizeof (GFC_INTEGER_4); else n = compile_options.record_marker; @@ -1393,12 +1564,8 @@ us_read (st_parameter_dt *dtp) /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE) { - switch (compile_options.record_marker) + switch (nr) { - case 0: - memcpy (&i, p, sizeof(gfc_offset)); - break; - case sizeof(GFC_INTEGER_4): memcpy (&i4, p, sizeof (i4)); i = i4; @@ -1415,12 +1582,8 @@ us_read (st_parameter_dt *dtp) } } else - switch (compile_options.record_marker) + switch (nr) { - case 0: - reverse_memcpy (&i, p, sizeof(gfc_offset)); - break; - case sizeof(GFC_INTEGER_4): reverse_memcpy (&i4, p, sizeof (i4)); i = i4; @@ -1436,7 +1599,19 @@ us_read (st_parameter_dt *dtp) break; } - dtp->u.p.current_unit->bytes_left = i; + if (i >= 0) + { + dtp->u.p.current_unit->bytes_left_subrecord = i; + dtp->u.p.current_unit->continued = 0; + } + else + { + dtp->u.p.current_unit->bytes_left_subrecord = -i; + dtp->u.p.current_unit->continued = 1; + } + + if (! continued) + dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl; } @@ -1444,7 +1619,7 @@ us_read (st_parameter_dt *dtp) amount to writing a bogus length that will be filled in later. */ static void -us_write (st_parameter_dt *dtp) +us_write (st_parameter_dt *dtp, int continued) { size_t nbytes; gfc_offset dummy; @@ -1452,7 +1627,7 @@ us_write (st_parameter_dt *dtp) dummy = 0; if (compile_options.record_marker == 0) - nbytes = sizeof (gfc_offset); + nbytes = sizeof (GFC_INTEGER_4); else nbytes = compile_options.record_marker ; @@ -1460,12 +1635,12 @@ us_write (st_parameter_dt *dtp) generate_error (&dtp->common, ERROR_OS, NULL); /* For sequential unformatted, if RECL= was not specified in the OPEN - we write until we have more bytes than can fit in the record markers. - If disk space runs out first, it will error on the write. */ - if (dtp->u.p.current_unit->flags.has_recl == 0) - dtp->u.p.current_unit->recl = max_offset; + we write until we have more bytes than can fit in the subrecord + markers, then we write a new subrecord. */ - dtp->u.p.current_unit->bytes_left = dtp->u.p.current_unit->recl; + dtp->u.p.current_unit->bytes_left_subrecord = + dtp->u.p.current_unit->recl_subrecord; + dtp->u.p.current_unit->continued = continued; } @@ -1491,9 +1666,9 @@ pre_position (st_parameter_dt *dtp) case UNFORMATTED_SEQUENTIAL: if (dtp->u.p.mode == READING) - us_read (dtp); + us_read (dtp, 0); else - us_write (dtp); + us_write (dtp, 0); break; @@ -1886,17 +2061,92 @@ next_array_record (st_parameter_dt *dtp, return index; } -/* Space to the next record for read mode. If the file is not - seekable, we read MAX_READ chunks until we get to the right + + +/* Skip to the end of the current record, taking care of an optional + record marker of size bytes. If the file is not seekable, we + read chunks of size MAX_READ until we get to the right position. */ #define MAX_READ 4096 static void +skip_record (st_parameter_dt *dtp, size_t bytes) +{ + gfc_offset new; + int rlength, length; + char *p; + + dtp->u.p.current_unit->bytes_left_subrecord += bytes; + if (dtp->u.p.current_unit->bytes_left_subrecord == 0) + return; + + 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) + generate_error (&dtp->common, ERROR_OS, NULL); + } + else + { /* Seek by reading data. */ + while (dtp->u.p.current_unit->bytes_left_subrecord > 0) + { + rlength = length = + (MAX_READ > dtp->u.p.current_unit->bytes_left_subrecord) ? + MAX_READ : dtp->u.p.current_unit->bytes_left_subrecord; + + p = salloc_r (dtp->u.p.current_unit->s, &rlength); + if (p == NULL) + { + generate_error (&dtp->common, ERROR_OS, NULL); + return; + } + + dtp->u.p.current_unit->bytes_left_subrecord -= length; + } + } + +} + +#undef MAX_READ + +/* Advance to the next record reading unformatted files, taking + care of subrecords. If complete_record is nonzero, we loop + until all subrecords are cleared. */ + +static void +next_record_r_unf (st_parameter_dt *dtp, int complete_record) +{ + size_t bytes; + + bytes = compile_options.record_marker == 0 ? + sizeof (GFC_INTEGER_4) : compile_options.record_marker; + + while(1) + { + + /* Skip over tail */ + + skip_record (dtp, bytes); + + if ( ! (complete_record && dtp->u.p.current_unit->continued)) + return; + + us_read (dtp, 1); + } +} + +/* Space to the next record for read mode. */ + +static void next_record_r (st_parameter_dt *dtp) { - gfc_offset new, record; - int bytes_left, rlength, length; + gfc_offset record; + int length, bytes_left; char *p; switch (current_mode (dtp)) @@ -1906,47 +2156,12 @@ next_record_r (st_parameter_dt *dtp) return; case UNFORMATTED_SEQUENTIAL: - - /* Skip over tail */ - dtp->u.p.current_unit->bytes_left += - compile_options.record_marker == 0 ? - sizeof (gfc_offset) : compile_options.record_marker; - - /* Fall through... */ + next_record_r_unf (dtp, 1); + break; case FORMATTED_DIRECT: case UNFORMATTED_DIRECT: - if (dtp->u.p.current_unit->bytes_left == 0) - break; - - 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; - - /* Direct access files do not generate END conditions, - only I/O errors. */ - if (sseek (dtp->u.p.current_unit->s, new) == FAILURE) - generate_error (&dtp->common, ERROR_OS, NULL); - - } - else - { /* Seek by reading data. */ - while (dtp->u.p.current_unit->bytes_left > 0) - { - rlength = length = (MAX_READ > dtp->u.p.current_unit->bytes_left) ? - MAX_READ : dtp->u.p.current_unit->bytes_left; - - p = salloc_r (dtp->u.p.current_unit->s, &rlength); - if (p == NULL) - { - generate_error (&dtp->common, ERROR_OS, NULL); - break; - } - - dtp->u.p.current_unit->bytes_left -= length; - } - } + skip_record (dtp, 0); break; case FORMATTED_STREAM: @@ -2025,19 +2240,15 @@ write_us_marker (st_parameter_dt *dtp, c char p[sizeof (GFC_INTEGER_8)]; if (compile_options.record_marker == 0) - len = sizeof (gfc_offset); + len = sizeof (GFC_INTEGER_4); else len = compile_options.record_marker; /* Only CONVERT_NATIVE and CONVERT_SWAP are valid here. */ if (dtp->u.p.current_unit->flags.convert == CONVERT_NATIVE) { - switch (compile_options.record_marker) + switch (len) { - case 0: - return swrite (dtp->u.p.current_unit->s, &buf, &len); - break; - case sizeof (GFC_INTEGER_4): buf4 = buf; return swrite (dtp->u.p.current_unit->s, &buf4, &len); @@ -2055,13 +2266,8 @@ write_us_marker (st_parameter_dt *dtp, c } else { - switch (compile_options.record_marker) + switch (len) { - case 0: - reverse_memcpy (p, &buf, sizeof (gfc_offset)); - return swrite (dtp->u.p.current_unit->s, p, &len); - break; - case sizeof (GFC_INTEGER_4): buf4 = buf; reverse_memcpy (p, &buf4, sizeof (GFC_INTEGER_4)); @@ -2070,7 +2276,7 @@ write_us_marker (st_parameter_dt *dtp, c case sizeof (GFC_INTEGER_8): buf8 = buf; - reverse_memcpy (p, &buf8, sizeof (GFC_INTEGER_4)); + reverse_memcpy (p, &buf8, sizeof (GFC_INTEGER_8)); return swrite (dtp->u.p.current_unit->s, p, &len); break; @@ -2082,16 +2288,72 @@ write_us_marker (st_parameter_dt *dtp, c } +/* Position to the next (sub)record in write mode for + unformatted sequential files. */ + +static void +next_record_w_unf (st_parameter_dt *dtp, int next_subrecord) +{ + gfc_offset c, m, m_write; + size_t record_marker; + + /* 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); + + /* Write the length tail. If we finish a record containing + subrecords, we write out the negative length. */ + + if (dtp->u.p.current_unit->continued) + m_write = -m; + else + m_write = m; + + if (write_us_marker (dtp, m_write) != 0) + goto io_error; + + if (compile_options.record_marker == 0) + record_marker = sizeof (GFC_INTEGER_4); + else + record_marker = compile_options.record_marker; + + /* Seek to the head and overwrite the bogus length with the real + length. */ + + if (sseek (dtp->u.p.current_unit->s, c - m - record_marker) + == FAILURE) + goto io_error; + + if (next_subrecord) + m_write = -m; + else + m_write = m; + + if (write_us_marker (dtp, m_write) != 0) + goto io_error; + + /* Seek past the end of the current record. */ + + if (sseek (dtp->u.p.current_unit->s, c + record_marker) == FAILURE) + goto io_error; + + return; + + io_error: + generate_error (&dtp->common, ERROR_OS, NULL); + return; + +} /* Position to the next record in write mode. */ static void next_record_w (st_parameter_dt *dtp, int done) { - gfc_offset c, m, record, max_pos; + gfc_offset m, record, max_pos; int length; char *p; - size_t record_marker; /* Zero counters for X- and T-editing. */ max_pos = dtp->u.p.max_pos; @@ -2119,35 +2381,7 @@ next_record_w (st_parameter_dt *dtp, int break; case UNFORMATTED_SEQUENTIAL: - /* Bytes written. */ - m = dtp->u.p.current_unit->recl - dtp->u.p.current_unit->bytes_left; - c = file_position (dtp->u.p.current_unit->s); - - /* Write the length tail. */ - - if (write_us_marker (dtp, m) != 0) - goto io_error; - - if (compile_options.record_marker == 4) - record_marker = sizeof(GFC_INTEGER_4); - else - record_marker = sizeof (gfc_offset); - - /* Seek to the head and overwrite the bogus length with the real - length. */ - - if (sseek (dtp->u.p.current_unit->s, c - m - record_marker) - == FAILURE) - goto io_error; - - if (write_us_marker (dtp, m) != 0) - goto io_error; - - /* Seek past the end of the current record. */ - - if (sseek (dtp->u.p.current_unit->s, c + record_marker) == FAILURE) - goto io_error; - + next_record_w_unf (dtp, 0); break; case FORMATTED_STREAM: