commit 9c8318cd8703d49ad7563e89765f8849ebc14385 Author: Jerry DeLisle Date: Thu Apr 4 13:48:20 2024 -0700 libfortran: Fix handling of formatted separators. PR libfortran/114304 PR libfortran/105473 libgfortran/ChangeLog: * io/list_read.c (eat_separator): Add logic to handle spaces preceding a comma or semicolon such that that a 'null' read occurs without error at the end of comma or semicolon terminated input lines. Add check and error message for ';'. gcc/testsuite/ChangeLog: * gfortran.dg/pr105473.f90: Modify to verify new error message. * gfortran.dg/pr114304.f90: New test. diff --git a/gcc/testsuite/gfortran.dg/pr105473.f90 b/gcc/testsuite/gfortran.dg/pr105473.f90 index 2679f6bb447..863a312c794 100644 --- a/gcc/testsuite/gfortran.dg/pr105473.f90 +++ b/gcc/testsuite/gfortran.dg/pr105473.f90 @@ -9,11 +9,11 @@ n = 999; m = 777; r=1.2345 z = cmplx(0.0,0.0) -! Check that semi-colon is allowed as separator with decimal=point. +! Check that semi-colon is not allowed as separator with decimal=point. ios=0 testinput = '1;17;3.14159' read(testinput,*,decimal='point',iostat=ios) n, m, r - if (ios /= 0) stop 1 + if (ios /= 5010) stop 1 ! Check that semi-colon allowed as a separator with decimal=point. ios=0 diff --git a/gcc/testsuite/gfortran.dg/pr114304.f90 b/gcc/testsuite/gfortran.dg/pr114304.f90 new file mode 100644 index 00000000000..8344a9ea857 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/pr114304.f90 @@ -0,0 +1,101 @@ +! { dg-do run } +! +! PR fortran/114304 +! +! See also PR fortran/105473 +! +! Testing: Does list-directed reading an integer/real allow some non-integer input? +! +! Note: GCC result comments before fix of this PR. + + implicit none + call t(.true., 'comma', ';') ! No error shown + call t(.false., 'point', ';') ! /!\ gfortran: no error, others: error + call t(.false., 'comma', ',') ! Error shown + call t(.true., 'point', ',') ! No error shown + call t(.false., 'comma', '.') ! Error shown + call t(.false., 'point', '.') ! Error shown + call t(.false., 'comma', '5.') ! Error shown + call t(.false., 'point', '5.') ! gfortran/flang: Error shown, ifort: no error + call t(.false., 'comma', '5,') ! gfortran: error; others: no error + call t(.true., 'point', '5,') ! No error shown + call t(.true., 'comma', '5;') ! No error shown + call t(.false., 'point', '5;') ! /!\ gfortran: no error shown, others: error + call t(.true., 'comma', '7 .') ! No error shown + call t(.true., 'point', '7 .') ! No error shown + call t(.true., 'comma', '7 ,') ! /!\ gfortran: error; others: no error + call t(.true., 'point', '7 ,') ! No error shown + call t(.true., 'comma', '7 ;') ! No error shown + call t(.true., 'point', '7 ;') ! No error shown + +! print *, '---------------' + + call t(.false., 'comma', '8.', .true.) ! Error shown + call t(.true., 'point', '8.', .true.) ! gfortran/flang: Error shown, ifort: no error + call t(.true., 'comma', '8,', .true.) ! gfortran: error; others: no error + call t(.true., 'point', '8,', .true.) ! No error shown + call t(.true., 'comma', '8;', .true.) ! No error shown + call t(.false., 'point', '8;', .true.) ! /!\ gfortran: no error shown, others: error + call t(.true., 'comma', '9 .', .true.) ! No error shown + call t(.true., 'point', '9 .', .true.) ! No error shown + call t(.true., 'comma', '9 ,', .true.) ! /!\ gfortran: error; others: no error + call t(.true., 'point', '9 ,', .true.) ! No error shown + call t(.true., 'comma', '9 ;', .true.) ! No error shown + call t(.true., 'point', '9 ;', .true.) ! No error shown + call t(.false., 'comma', '3,3.', .true.) ! Error shown + call t(.false., 'point', '3.3.', .true.) ! Error shown + call t(.false., 'comma', '3,3,', .true.) ! gfortran/flang: no error; ifort: error + call t(.true., 'comma', '3,3;', .true.) ! No error shown + call t(.false., 'point', '3.3;', .true.) ! gfortran/flang: no error; ifort: error + call t(.true., 'comma', '4,4 .', .true.) ! N error shown + call t(.true., 'point', '4.4 .', .true.) ! No error shown + call t(.true., 'comma', '4,4 ,', .true.) ! /!\ gfortran: error; others: no error + call t(.true., 'point', '4.4 ,', .true.) ! No error shown + call t(.true., 'comma', '4,4 ;', .true.) ! No error shown + call t(.true., 'point', '4.4 ;', .true.) ! No error shown + +! print *, '---------------' + + call t(.true., 'comma', '8', .true.) + call t(.true., 'point', '8', .true.) + call t(.true., 'point', '9 ;', .true.) + call t(.true., 'comma', '3;3.', .true.) + call t(.true., 'point', '3,3.', .true.) + call t(.true., 'comma', '3;3,', .true.) + call t(.true., 'comma', '3;3;', .true.) + call t(.true., 'point', '3,3;', .true.) + call t(.true., 'comma', '4;4 .', .true.) + call t(.true., 'point', '4,4 .', .true.) + call t(.true., 'comma', '4;4 ,', .true.) + call t(.true., 'point', '4,4 ,', .true.) + call t(.true., 'comma', '4;4 ;', .true.) + call t(.true., 'point', '4,4 ;', .true.) +contains +subroutine t(valid, dec, testinput, isreal) + logical, value :: valid + character(len=*) :: dec, testinput + logical, optional :: isreal + logical :: isreal2 + integer n,ios + real :: r + r = 42; n = 42 + isreal2 = .false. + if (present(isreal)) isreal2 = isreal + + if (isreal2) then + read(testinput,*,decimal=dec,iostat=ios) r + if ((valid .and. ios /= 0) .or. (.not.valid .and. ios == 0)) then + print '(*(g0))', valid, ', ', dec,', isreal = ',isreal2,', testinput = "',testinput,'"',', r=',r,' ios=',ios + print *, 'ERROR' + stop 1 + end if + else + read(testinput,*,decimal=dec,iostat=ios) n + if ((valid .and. ios /= 0) .or. (.not.valid .and. ios == 0)) then + print '(*(g0))', valid, ', ', dec,', isreal = ',isreal2,', testinput = "',testinput,'"',', n=',n,' ios=',ios + print *, 'ERROR' + stop 1 + end if + end if +end +end program diff --git a/libgfortran/io/list_read.c b/libgfortran/io/list_read.c index fb3f7dbc34d..6bf59329add 100644 --- a/libgfortran/io/list_read.c +++ b/libgfortran/io/list_read.c @@ -461,11 +461,30 @@ eat_separator (st_parameter_dt *dtp) int c, n; int err = 0; - eat_spaces (dtp); dtp->u.p.comma_flag = 0; + c = next_char (dtp); + if (c == ' ') + { + eat_spaces (dtp); + c = next_char (dtp); + if (c == ',') + { + if (dtp->u.p.current_unit->decimal_status == DECIMAL_COMMA) + unget_char (dtp, ';'); + dtp->u.p.comma_flag = 1; + eat_spaces (dtp); + return err; + } + if (c == ';') + { + if (dtp->u.p.current_unit->decimal_status == DECIMAL_POINT) + unget_char (dtp, ','); + dtp->u.p.comma_flag = 1; + eat_spaces (dtp); + return err; + } + } - if ((c = next_char (dtp)) == EOF) - return LIBERROR_END; switch (c) { case ',': @@ -476,8 +495,18 @@ eat_separator (st_parameter_dt *dtp) unget_char (dtp, c); break; } - /* Fall through. */ + dtp->u.p.comma_flag = 1; + eat_spaces (dtp); + break; + case ';': + if (dtp->u.p.current_unit->decimal_status == DECIMAL_POINT) + { + generate_error (&dtp->common, LIBERROR_READ_VALUE, + "Semicolon not allowed as separator with DECIMAL='point'"); + unget_char (dtp, c); + break; + } dtp->u.p.comma_flag = 1; eat_spaces (dtp); break;