1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . T E X T _ I O --
9 -- Copyright (C) 1992-2010, 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 with System; use System;
33 with System.Storage_Elements; use System.Storage_Elements;
35 separate (Ada.Text_IO)
41 Chunk_Size : constant := 80;
42 -- We read into a fixed size auxiliary buffer. Because this buffer
43 -- needs to be pre-initialized, there is a trade-off between size and
44 -- speed. Experiments find returns are diminishing after 50 and this
45 -- size allows most lines to be processed with a single read.
50 procedure memcpy (s1, s2 : chars; n : size_t);
51 pragma Import (C, memcpy);
53 function memchr (s : chars; ch : int; n : size_t) return chars;
54 pragma Import (C, memchr);
56 procedure memset (b : chars; ch : int; n : size_t);
57 pragma Import (C, memset);
59 function Get_Chunk (N : Positive) return Natural;
60 -- Reads at most N - 1 characters into Item (Last + 1 .. Item'Last),
61 -- updating Last. Raises End_Error if nothing was read (End_Of_File).
62 -- Returns number of characters still to read (either 0 or 1) in
69 function Get_Chunk (N : Positive) return Natural is
70 Buf : String (1 .. Chunk_Size);
71 S : constant chars := Buf (1)'Address;
79 memset (S, 10, size_t (N));
81 if fgets (S, N, File.Stream) = Null_Address then
82 if ferror (File.Stream) /= 0 then
85 -- If incomplete last line, pretend we found a LM
87 elsif Last >= Item'First then
95 P := memchr (S, LM, size_t (N));
97 -- If no LM is found, the buffer got filled without reading a new
98 -- line. Otherwise, the LM is either one from the input, or else one
99 -- from the initialization, which means an incomplete end-of-line was
100 -- encountered. Only in first case the LM will be followed by a 0.
102 if P = Null_Address then
103 pragma Assert (Buf (N) = ASCII.NUL);
104 memcpy (Item (Last + 1)'Address,
105 Buf (1)'Address, size_t (N - 1));
106 Last := Last + N - 1;
111 -- P points to the LM character. Set K so Buf (K) is the character
115 K : Natural := Natural (P - S);
118 -- Now Buf (K + 2) should be 0, or otherwise Buf (K) is the 0
119 -- put in by fgets, so compensate.
121 if K + 2 > Buf'Last or else Buf (K + 2) /= ASCII.NUL then
123 -- Incomplete last line, so remove the extra 0
125 pragma Assert (Buf (K) = ASCII.NUL);
129 memcpy (Item (Last + 1)'Address,
130 Buf (1)'Address, size_t (K));
138 -- Start of processing for Get_Line
141 FIO.Check_Read_Status (AP (File));
143 -- Immediate exit for null string, this is a case in which we do not
144 -- need to test for end of file and we do not skip a line mark under
145 -- any circumstances.
147 if Item'First > Item'Last then
151 N := Item'Last - Item'First + 1;
153 Last := Item'First - 1;
155 -- Here we have at least one character, if we are immediately before
156 -- a line mark, then we will just skip past it storing no characters.
158 if File.Before_LM then
159 File.Before_LM := False;
160 File.Before_LM_PM := False;
162 -- Otherwise we need to read some characters
165 while N >= Chunk_Size loop
166 if Get_Chunk (Chunk_Size) = 0 then
169 N := N - Chunk_Size + 1;
177 -- Almost there, only a little bit more to read
182 -- If we get EOF after already reading data, this is an incomplete
183 -- last line, in which case no End_Error should be raised.
185 if ch = EOF and then Last < Item'First then
190 -- Buffer really is full without having seen LM, update col
193 Item (Last) := Character'Val (ch);
194 File.Col := File.Col + Count (Last - Item'First + 1);
200 -- We have skipped past, but not stored, a line mark. Skip following
201 -- page mark if one follows, but do not do this for a non-regular file
202 -- (since otherwise we get annoying wait for an extra character)
204 File.Line := File.Line + 1;
207 if File.Before_LM_PM then
209 File.Before_LM_PM := False;
210 File.Page := File.Page + 1;
212 elsif File.Is_Regular_File then
215 if ch = PM and then File.Is_Regular_File then
217 File.Page := File.Page + 1;