1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . T E X T _ I O . G E T _ L I N E --
9 -- Copyright (C) 1992-2018, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- The implementation of Ada.Text_IO.Get_Line is split into a subunit so that
33 -- different implementations can be used on different systems. This is the
34 -- standard implementation (it uses low level features not suitable for use
35 -- on virtual machines).
37 with System
; use System
;
38 with System
.Storage_Elements
; use System
.Storage_Elements
;
40 separate (Ada
.Text_IO
)
46 Chunk_Size
: constant := 80;
47 -- We read into a fixed size auxiliary buffer. Because this buffer
48 -- needs to be pre-initialized, there is a trade-off between size and
49 -- speed. Experiments find returns are diminishing after 50 and this
50 -- size allows most lines to be processed with a single read.
55 procedure memcpy
(s1
, s2
: chars
; n
: size_t
);
56 pragma Import
(C
, memcpy
);
58 function memchr
(s
: chars
; ch
: int
; n
: size_t
) return chars
;
59 pragma Import
(C
, memchr
);
61 procedure memset
(b
: chars
; ch
: int
; n
: size_t
);
62 pragma Import
(C
, memset
);
64 function Get_Chunk
(N
: Positive) return Natural;
65 -- Reads at most N - 1 characters into Item (Last + 1 .. Item'Last),
66 -- updating Last. Raises End_Error if nothing was read (End_Of_File).
67 -- Returns number of characters still to read (either 0 or 1) in
74 function Get_Chunk
(N
: Positive) return Natural is
75 Buf
: String (1 .. Chunk_Size
);
76 S
: constant chars
:= Buf
(1)'Address;
84 memset
(S
, 10, size_t
(N
));
86 if fgets
(S
, N
, File
.Stream
) = Null_Address
then
87 if ferror
(File
.Stream
) /= 0 then
90 -- If incomplete last line, pretend we found a LM
92 elsif Last
>= Item
'First then
100 P
:= memchr
(S
, LM
, size_t
(N
));
102 -- If no LM is found, the buffer got filled without reading a new
103 -- line. Otherwise, the LM is either one from the input, or else one
104 -- from the initialization, which means an incomplete end-of-line was
105 -- encountered. Only in first case the LM will be followed by a 0.
107 if P
= Null_Address
then
108 pragma Assert
(Buf
(N
) = ASCII
.NUL
);
109 memcpy
(Item
(Last
+ 1)'Address,
110 Buf
(1)'Address, size_t
(N
- 1));
111 Last
:= Last
+ N
- 1;
116 -- P points to the LM character. Set K so Buf (K) is the character
120 K
: Natural := Natural (P
- S
);
123 -- If K + 2 is greater than N, then Buf (K + 1) cannot be a LM
124 -- character from the source file, as the call to fgets copied at
125 -- most N - 1 characters. Otherwise, either LM is a character from
126 -- the source file and then Buf (K + 2) should be 0, or LM is a
127 -- character put in Buf by memset and then Buf (K) is the 0 put in
128 -- by fgets. In both cases where LM does not come from the source
131 if K
+ 2 > N
or else Buf
(K
+ 2) /= ASCII
.NUL
then
133 -- Incomplete last line, so remove the extra 0
135 pragma Assert
(Buf
(K
) = ASCII
.NUL
);
139 memcpy
(Item
(Last
+ 1)'Address,
140 Buf
(1)'Address, size_t
(K
));
148 -- Start of processing for Get_Line
151 FIO
.Check_Read_Status
(AP
(File
));
153 -- Set Last to Item'First - 1 when no characters are read, as mandated by
154 -- Ada RM. In the case where Item'First is negative or null, this results
155 -- in Constraint_Error being raised.
157 Last
:= Item
'First - 1;
159 -- Immediate exit for null string, this is a case in which we do not
160 -- need to test for end of file and we do not skip a line mark under
161 -- any circumstances.
163 if Item
'First > Item
'Last then
167 N
:= Item
'Last - Item
'First + 1;
169 -- Here we have at least one character, if we are immediately before
170 -- a line mark, then we will just skip past it storing no characters.
172 if File
.Before_LM
then
173 File
.Before_LM
:= False;
174 File
.Before_LM_PM
:= False;
176 -- Otherwise we need to read some characters
179 while N
>= Chunk_Size
loop
180 if Get_Chunk
(Chunk_Size
) = 0 then
183 N
:= N
- Chunk_Size
+ 1;
191 -- Almost there, only a little bit more to read
196 -- If we get EOF after already reading data, this is an incomplete
197 -- last line, in which case no End_Error should be raised.
200 if Last
< Item
'First then
209 -- Buffer really is full without having seen LM, update col
212 Item
(Last
) := Character'Val (ch
);
213 File
.Col
:= File
.Col
+ Count
(Last
- Item
'First + 1);
219 -- We have skipped past, but not stored, a line mark. Skip following
220 -- page mark if one follows, but do not do this for a non-regular file
221 -- (since otherwise we get annoying wait for an extra character)
223 File
.Line
:= File
.Line
+ 1;
226 if File
.Before_LM_PM
then
228 File
.Before_LM_PM
:= False;
229 File
.Page
:= File
.Page
+ 1;
231 elsif File
.Is_Regular_File
then
234 if ch
= PM
and then File
.Is_Regular_File
then
236 File
.Page
:= File
.Page
+ 1;