/src/lib/io/basic/text_file_read.e
Specman e | 327 lines | 253 code | 28 blank | 46 comment | 22 complexity | ad6eee2a62669354b9ca039009f4134d MD5 | raw file
1-- This file is part of a Liberty Eiffel library. 2-- See the full copyright at the end. 3-- 4class TEXT_FILE_READ 5 -- 6 -- Basic input facilities to read a named file on the disc. 7 -- 8 -- Note: most features are common with STD_INPUT so you can test your program on the 9 -- screen first and then, just changing of instance (STD_INPUT/TEXT_FILE_READ), doing the 10 -- same in a file. 11 -- 12 -- Input stream usage is available in tutorial/io and Liberty Eiffel FAQ. 13 -- 14 15inherit 16 FILE_STREAM 17 redefine out_in_tagged_out_memory 18 end 19 TERMINAL_INPUT_STREAM 20 redefine filtered_read_line_in, filtered_read_available_in, out_in_tagged_out_memory 21 end 22 23insert 24 STRING_HANDLER 25 redefine out_in_tagged_out_memory 26 end 27 28create {ANY} 29 make, connect_to 30 31feature {ANY} 32 connect_to (new_path: ABSTRACT_STRING) 33 -- Open text file for reading. The stream is positioned at the 34 -- beginning of the file. 35 local 36 p: POINTER 37 do 38 p := new_path.to_external 39 input_stream := text_file_read_open(p) 40 if input_stream.is_not_null then 41 end_of_input := False 42 set_path(new_path) 43 if capacity = 0 then 44 buffer := buffer.calloc(4096) 45 capacity := 4096 46 end 47 end_reached := False 48 buffer_position := 0 49 buffer_size := 0 50 filtered_last_character := '%U' 51 end 52 ensure then 53 is_connected implies not end_of_input 54 end 55 56 disconnect 57 do 58 io_fclose(input_stream) 59 path := Void 60 filter := Void 61 end 62 63 can_unread_character: BOOLEAN 64 do 65 Result := buffer_position > 0 66 end 67 68 end_of_input: BOOLEAN 69 70 out_in_tagged_out_memory 71 do 72 tagged_out_memory.append(once "{TEXT_FILE_READ ") 73 tagged_out_memory.append(path) 74 tagged_out_memory.extend('}') 75 end 76 77feature {FILTER_INPUT_STREAM} 78 filtered_read_character 79 do 80 if buffer_position >= buffer_size then 81 fill_buffer 82 end 83 filtered_last_character := buffer.item(buffer_position) 84 buffer_position := buffer_position + 1 85 end_of_input := end_reached 86 check 87 valid_last_character 88 end 89 end 90 91 filtered_unread_character 92 do 93 end_of_input := False 94 buffer_position := buffer_position - 1 95 if valid_last_character then 96 filtered_last_character := buffer.item(buffer_position - 1) 97 end 98 end 99 100 filtered_last_character: CHARACTER 101 102 filtered_read_line_in (str: STRING) 103 local 104 i: INTEGER; stop: BOOLEAN; old_count, new_count: INTEGER; initial_count: INTEGER 105 do 106 from 107 initial_count := str.count 108 until 109 stop 110 loop 111 -- search %N in buffer 112 from 113 i := buffer_position 114 until 115 i >= buffer_size or else buffer.item(i) = '%N' 116 loop 117 i := i + 1 118 end 119 -- block copy (but slice_copy copies char by char...) 120 if i > buffer_position then 121 old_count := str.count 122 new_count := old_count + i - buffer_position 123 if str.capacity < new_count then 124 str.resize((old_count * 2).max(new_count)) 125 end 126 str.storage.slice_copy(old_count, buffer, buffer_position, i - 1) 127 str.set_count(new_count) 128 end 129 -- next buffer if needed 130 if i < buffer_size and then buffer.item(i) = '%N' then 131 stop := True 132 buffer_position := i + 1 133 if str.count > initial_count and then str.last = '%R' then 134 str.remove_last 135 -- UNIX uses the Linefeed character (ASCII character 10) to 136 -- denote the end of a line. DOS uses the Carriage Return 137 -- followed by the Linefeed character (ASCII character 13 138 -- & ASCII character 10) to denote a new line. 139 end 140 else 141 if not end_reached then 142 fill_buffer 143 end 144 stop := end_reached 145 end 146 end 147 end_of_input := end_reached 148 end 149 150 filtered_read_available_in (str: STRING; limit: INTEGER) 151 -- Limit reading to what the buffer contains. If the buffer is already exhausted, just fill it once. 152 local 153 old_count, new_count, i: INTEGER 154 do 155 if buffer_position >= buffer_size then 156 fill_buffer 157 end 158 old_count := str.count 159 i := limit.min(buffer_size - buffer_position) 160 new_count := old_count + i 161 if str.capacity < new_count then 162 str.resize((old_count * 2).max(new_count)) 163 end 164 str.storage.slice_copy(old_count, buffer, buffer_position, buffer_position + i - 1) 165 str.set_count(new_count) 166 buffer_position := buffer_position + i 167 end 168 169feature {FILTER} 170 filtered_descriptor: INTEGER 171 do 172 Result := sequencer_descriptor(input_stream) 173 end 174 175 filtered_has_descriptor: BOOLEAN True 176 177 filtered_stream_pointer: POINTER 178 do 179 Result := input_stream 180 end 181 182 filtered_has_stream_pointer: BOOLEAN True 183 184feature {FILE_TOOLS} 185 same_as (other: like Current): BOOLEAN 186 require 187 is_connected 188 other.is_connected 189 local 190 b1, b2: NATIVE_ARRAY[CHARACTER]; i: INTEGER 191 do 192 from 193 fill_buffer 194 b1 := buffer 195 other.fill_buffer 196 b2 := other.buffer 197 Result := True 198 until 199 not Result or else end_reached or else other.end_reached 200 loop 201 if buffer_size = other.buffer_size then 202 if b1.item(0) /= b2.item(0) then 203 Result := False 204 else 205 -- make first character different for loop end 206 b1.put('%R', 0) 207 b2.put('%N', 0) 208 from 209 i := buffer_size - 1 210 variant 211 i 212 until 213 b1.item(i) /= b2.item(i) 214 loop 215 i := i - 1 216 end 217 Result := i = 0 218 end 219 if Result then 220 fill_buffer 221 other.fill_buffer 222 end 223 else 224 from 225 read_character 226 other.read_character 227 until 228 not Result or else end_of_input or else other.end_of_input 229 loop 230 Result := last_character = other.last_character 231 read_character 232 other.read_character 233 end 234 end 235 end 236 Result := Result and then end_reached and then other.end_reached 237 disconnect 238 other.disconnect 239 ensure 240 not is_connected 241 not other.is_connected 242 end 243 244feature {INPUT_STREAM} 245 input_stream: POINTER 246 247feature {TEXT_FILE_READ} 248 buffer: NATIVE_ARRAY[CHARACTER] 249 250 end_reached: BOOLEAN 251 252 buffer_position, buffer_size: INTEGER 253 254 capacity: INTEGER 255 256 fill_buffer 257 local 258 last: CHARACTER; more: BOOLEAN 259 do 260 more := buffer_size > 0 261 if more then 262 last := buffer.item(buffer_size - 1) 263 end 264 buffer_size := io_fread(buffer, capacity, input_stream) 265 buffer_position := 0 266 if buffer_size = 0 then 267 end_reached := True 268 if more then 269 -- needed for unread_character service 270 buffer.put(last, 0) 271 buffer_size := 1 272 buffer_position := 1 273 else 274 buffer_size := 0 275 end 276 elseif buffer_size < 0 then 277 -- ??? 278 end 279 end 280 281feature {} 282 make 283 -- The new created object is not connected. (See also `connect_to'.) 284 do 285 ensure 286 not is_connected 287 end 288 289 text_file_read_open (path_pointer: POINTER): POINTER 290 external "plug_in" 291 alias "{ 292 location: "${sys}/plugins" 293 module_name: "io" 294 feature_name: "text_file_read_open" 295 }" 296 end 297 298 io_fclose (stream: POINTER) 299 external "plug_in" 300 alias "{ 301 location: "${sys}/plugins" 302 module_name: "io" 303 feature_name: "io_fclose" 304 }" 305 end 306 307end -- class TEXT_FILE_READ 308-- 309-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file. 310-- 311-- Permission is hereby granted, free of charge, to any person obtaining a copy 312-- of this software and associated documentation files (the "Software"), to deal 313-- in the Software without restriction, including without limitation the rights 314-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 315-- copies of the Software, and to permit persons to whom the Software is 316-- furnished to do so, subject to the following conditions: 317-- 318-- The above copyright notice and this permission notice shall be included in 319-- all copies or substantial portions of the Software. 320-- 321-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 322-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 323-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 324-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 325-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 326-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 327-- THE SOFTWARE.