Skip to content

Commit e16e6c1

Browse files
committed
Handle disconnected units and pad='no'
1 parent cf1abe2 commit e16e6c1

File tree

3 files changed

+132
-7
lines changed

3 files changed

+132
-7
lines changed

doc/specs/stdlib_io.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,14 @@ Read a whole line from a formatted unit into a string variable
236236

237237
### Syntax
238238

239-
`call [[stdlib_io(module):getline(interface)]](unit, line[, iostat][, iomsg])`
239+
`call [[stdlib_io(module):getline(interface)]] (unit, line[, iostat][, iomsg])`
240+
`call [[stdlib_io(module):getline(interface)]] (line[, iostat][, iomsg])`
240241

241242
### Arguments
242243

243244
`unit`: Formatted input unit.
244245
This argument is `intent(in)`.
246+
If `unit` is not specified standard input is used.
245247

246248
`line`: Deferred length character or `string_type` variable.
247249
This argument is `intent(out)`.

src/stdlib_io.fypp

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module stdlib_io
66
!! Provides a support for file handling
77
!! ([Specification](../page/specs/stdlib_io.html))
88

9+
use, intrinsic :: iso_fortran_env, only : input_unit
910
use stdlib_kinds, only: sp, dp, xdp, qp, &
1011
int8, int16, int32, int64
1112
use stdlib_error, only: error_stop
@@ -38,6 +39,8 @@ module stdlib_io
3839
interface getline
3940
module procedure :: getline_char
4041
module procedure :: getline_string
42+
module procedure :: getline_input_char
43+
module procedure :: getline_input_string
4144
end interface getline
4245

4346
interface loadtxt
@@ -356,17 +359,28 @@ contains
356359
integer, parameter :: bufsize = 512
357360
character(len=bufsize) :: buffer, msg
358361
integer :: chunk, stat
362+
logical :: opened
363+
364+
if (unit /= -1) then
365+
inquire(unit=unit, opened=opened)
366+
else
367+
opened = .false.
368+
end if
369+
370+
if (opened) then
371+
open(unit=unit, pad="yes", iostat=stat, iomsg=msg)
372+
else
373+
stat = 1
374+
msg = "Unit is not connected"
375+
end if
359376

360377
line = ""
361-
do
378+
do while (stat == 0)
362379
read(unit, '(a)', advance='no', iostat=stat, iomsg=msg, size=chunk) buffer
363380
if (stat > 0) exit
364381
line = line // buffer(:chunk)
365-
if (stat < 0) then
366-
if (is_iostat_eor(stat)) stat = 0
367-
exit
368-
end if
369382
end do
383+
if (is_iostat_eor(stat)) stat = 0
370384

371385
if (stat /= 0 .and. present(iomsg)) iomsg = trim(msg)
372386
if (present(iostat)) then
@@ -395,4 +409,32 @@ contains
395409
line = string_type(buffer)
396410
end subroutine getline_string
397411

412+
!> Version: experimental
413+
!>
414+
!> Read a whole line from the standard input into a deferred length character variable
415+
subroutine getline_input_char(line, iostat, iomsg)
416+
!> Line to read
417+
character(len=:), allocatable, intent(out) :: line
418+
!> Status of operation
419+
integer, intent(out), optional :: iostat
420+
!> Error message
421+
character(len=:), allocatable, optional :: iomsg
422+
423+
call getline(input_unit, line, iostat, iomsg)
424+
end subroutine getline_input_char
425+
426+
!> Version: experimental
427+
!>
428+
!> Read a whole line from the standard input into a string variable
429+
subroutine getline_input_string(line, iostat, iomsg)
430+
!> Line to read
431+
type(string_type), intent(out) :: line
432+
!> Status of operation
433+
integer, intent(out), optional :: iostat
434+
!> Error message
435+
character(len=:), allocatable, optional :: iomsg
436+
437+
call getline(input_unit, line, iostat, iomsg)
438+
end subroutine getline_input_string
439+
398440
end module stdlib_io

src/tests/io/test_getline.f90

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ subroutine collect_getline(testsuite)
1616

1717
testsuite = [ &
1818
new_unittest("read-char", test_read_char), &
19-
new_unittest("read-string", test_read_string) &
19+
new_unittest("read-string", test_read_string), &
20+
new_unittest("pad-no", test_pad_no), &
21+
new_unittest("iostat-end", test_iostat_end), &
22+
new_unittest("closed-unit", test_closed_unit, should_fail=.true.), &
23+
new_unittest("no-unit", test_no_unit, should_fail=.true.) &
2024
]
2125
end subroutine collect_getline
2226

@@ -34,7 +38,9 @@ subroutine test_read_char(error)
3438
do i = 1, 3
3539
call getline(io, line, stat)
3640
call check(error, stat)
41+
if (allocated(error)) exit
3742
call check(error, len(line), 3*10**i)
43+
if (allocated(error)) exit
3844
end do
3945
close(io)
4046
end subroutine test_read_char
@@ -53,11 +59,86 @@ subroutine test_read_string(error)
5359
do i = 1, 3
5460
call getline(io, line, stat)
5561
call check(error, stat)
62+
if (allocated(error)) exit
5663
call check(error, len(line), 3*10**i)
64+
if (allocated(error)) exit
5765
end do
5866
close(io)
5967
end subroutine test_read_string
6068

69+
subroutine test_pad_no(error)
70+
!> Error handling
71+
type(error_type), allocatable, intent(out) :: error
72+
73+
integer :: io, i, stat
74+
character(len=:), allocatable :: line
75+
76+
open(newunit=io, status="scratch", pad="no")
77+
write(io, "(a)") repeat("abc", 10), repeat("def", 100), repeat("ghi", 1000)
78+
rewind(io)
79+
80+
do i = 1, 3
81+
call getline(io, line, stat)
82+
call check(error, stat)
83+
if (allocated(error)) exit
84+
call check(error, len(line), 3*10**i)
85+
if (allocated(error)) exit
86+
end do
87+
close(io)
88+
end subroutine test_pad_no
89+
90+
subroutine test_iostat_end(error)
91+
use, intrinsic :: iso_fortran_env, only : iostat_end
92+
!> Error handling
93+
type(error_type), allocatable, intent(out) :: error
94+
95+
integer :: io, i, stat
96+
character(len=:), allocatable :: line
97+
98+
open(newunit=io, status="scratch")
99+
write(io, "(a)") repeat("abc", 10), repeat("def", 100), repeat("ghi", 1000)
100+
rewind(io)
101+
102+
do i = 1, 3
103+
call getline(io, line, stat)
104+
call check(error, stat)
105+
if (allocated(error)) exit
106+
call check(error, len(line), 3*10**i)
107+
if (allocated(error)) exit
108+
end do
109+
if (.not.allocated(error)) then
110+
call getline(io, line, stat)
111+
call check(error, stat, iostat_end)
112+
end if
113+
close(io)
114+
end subroutine test_iostat_end
115+
116+
subroutine test_closed_unit(error)
117+
!> Error handling
118+
type(error_type), allocatable, intent(out) :: error
119+
120+
integer :: io, stat
121+
character(len=:), allocatable :: line, msg
122+
123+
open(newunit=io, status="scratch")
124+
close(io)
125+
126+
call getline(io, line, stat, msg)
127+
call check(error, stat, msg)
128+
end subroutine test_closed_unit
129+
130+
subroutine test_no_unit(error)
131+
!> Error handling
132+
type(error_type), allocatable, intent(out) :: error
133+
134+
integer :: io, stat
135+
character(len=:), allocatable :: line, msg
136+
137+
io = -1
138+
call getline(io, line, stat, msg)
139+
call check(error, stat, msg)
140+
end subroutine test_no_unit
141+
61142
end module test_getline
62143

63144

0 commit comments

Comments
 (0)