/src/lib/io/basic/text_file_read.e

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