1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
|
------------------------------------------------------------------------------
-- --
-- GNAT RUN-TIME COMPONENTS --
-- --
-- A D A . T E X T _ I O . G E T _ L I N E --
-- --
-- B o d y --
-- --
-- Copyright (C) 1992-2018, Free Software Foundation, Inc. --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 3, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. --
-- --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception, --
-- version 3.1, as published by the Free Software Foundation. --
-- --
-- You should have received a copy of the GNU General Public License and --
-- a copy of the GCC Runtime Library Exception along with this program; --
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
-- <http://www.gnu.org/licenses/>. --
-- --
-- GNAT was originally developed by the GNAT team at New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc. --
-- --
------------------------------------------------------------------------------
-- The implementation of Ada.Text_IO.Get_Line is split into a subunit so that
-- different implementations can be used on different systems. This is the
-- standard implementation (it uses low level features not suitable for use
-- on virtual machines).
with System; use System;
with System.Storage_Elements; use System.Storage_Elements;
separate (Ada.Text_IO)
procedure Get_Line
(File : File_Type;
Item : out String;
Last : out Natural)
is
Chunk_Size : constant := 80;
-- We read into a fixed size auxiliary buffer. Because this buffer
-- needs to be pre-initialized, there is a trade-off between size and
-- speed. Experiments find returns are diminishing after 50 and this
-- size allows most lines to be processed with a single read.
ch : int;
N : Natural;
procedure memcpy (s1, s2 : chars; n : size_t);
pragma Import (C, memcpy);
function memchr (s : chars; ch : int; n : size_t) return chars;
pragma Import (C, memchr);
procedure memset (b : chars; ch : int; n : size_t);
pragma Import (C, memset);
function Get_Chunk (N : Positive) return Natural;
-- Reads at most N - 1 characters into Item (Last + 1 .. Item'Last),
-- updating Last. Raises End_Error if nothing was read (End_Of_File).
-- Returns number of characters still to read (either 0 or 1) in
-- case of success.
---------------
-- Get_Chunk --
---------------
function Get_Chunk (N : Positive) return Natural is
Buf : String (1 .. Chunk_Size);
S : constant chars := Buf (1)'Address;
P : chars;
begin
if N = 1 then
return N;
end if;
memset (S, 10, size_t (N));
if fgets (S, N, File.Stream) = Null_Address then
if ferror (File.Stream) /= 0 then
raise Device_Error;
-- If incomplete last line, pretend we found a LM
elsif Last >= Item'First then
return 0;
else
raise End_Error;
end if;
end if;
P := memchr (S, LM, size_t (N));
-- If no LM is found, the buffer got filled without reading a new
-- line. Otherwise, the LM is either one from the input, or else one
-- from the initialization, which means an incomplete end-of-line was
-- encountered. Only in first case the LM will be followed by a 0.
if P = Null_Address then
pragma Assert (Buf (N) = ASCII.NUL);
memcpy (Item (Last + 1)'Address,
Buf (1)'Address, size_t (N - 1));
Last := Last + N - 1;
return 1;
else
-- P points to the LM character. Set K so Buf (K) is the character
-- right before.
declare
K : Natural := Natural (P - S);
begin
-- If K + 2 is greater than N, then Buf (K + 1) cannot be a LM
-- character from the source file, as the call to fgets copied at
-- most N - 1 characters. Otherwise, either LM is a character from
-- the source file and then Buf (K + 2) should be 0, or LM is a
-- character put in Buf by memset and then Buf (K) is the 0 put in
-- by fgets. In both cases where LM does not come from the source
-- file, compensate.
if K + 2 > N or else Buf (K + 2) /= ASCII.NUL then
-- Incomplete last line, so remove the extra 0
pragma Assert (Buf (K) = ASCII.NUL);
K := K - 1;
end if;
memcpy (Item (Last + 1)'Address,
Buf (1)'Address, size_t (K));
Last := Last + K;
end;
return 0;
end if;
end Get_Chunk;
-- Start of processing for Get_Line
begin
FIO.Check_Read_Status (AP (File));
-- Set Last to Item'First - 1 when no characters are read, as mandated by
-- Ada RM. In the case where Item'First is negative or null, this results
-- in Constraint_Error being raised.
Last := Item'First - 1;
-- Immediate exit for null string, this is a case in which we do not
-- need to test for end of file and we do not skip a line mark under
-- any circumstances.
if Item'First > Item'Last then
return;
end if;
N := Item'Last - Item'First + 1;
-- Here we have at least one character, if we are immediately before
-- a line mark, then we will just skip past it storing no characters.
if File.Before_LM then
File.Before_LM := False;
File.Before_LM_PM := False;
-- Otherwise we need to read some characters
else
while N >= Chunk_Size loop
if Get_Chunk (Chunk_Size) = 0 then
N := 0;
else
N := N - Chunk_Size + 1;
end if;
end loop;
if N > 1 then
N := Get_Chunk (N);
end if;
-- Almost there, only a little bit more to read
if N = 1 then
ch := Getc (File);
-- If we get EOF after already reading data, this is an incomplete
-- last line, in which case no End_Error should be raised.
if ch = EOF then
if Last < Item'First then
raise End_Error;
else -- All done
return;
end if;
elsif ch /= LM then
-- Buffer really is full without having seen LM, update col
Last := Last + 1;
Item (Last) := Character'Val (ch);
File.Col := File.Col + Count (Last - Item'First + 1);
return;
end if;
end if;
end if;
-- We have skipped past, but not stored, a line mark. Skip following
-- page mark if one follows, but do not do this for a non-regular file
-- (since otherwise we get annoying wait for an extra character)
File.Line := File.Line + 1;
File.Col := 1;
if File.Before_LM_PM then
File.Line := 1;
File.Before_LM_PM := False;
File.Page := File.Page + 1;
elsif File.Is_Regular_File then
ch := Getc (File);
if ch = PM and then File.Is_Regular_File then
File.Line := 1;
File.Page := File.Page + 1;
else
Ungetc (ch, File);
end if;
end if;
end Get_Line;
|