io-like 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (11) hide show
  1. data/CONTRIBUTORS +13 -0
  2. data/GPL +676 -0
  3. data/HACKING +123 -0
  4. data/LEGAL +8 -0
  5. data/LICENSE +57 -0
  6. data/MANIFEST +10 -0
  7. data/NEWS +13 -0
  8. data/README +135 -0
  9. data/lib/io/like.rb +1329 -0
  10. data/test/lib/likestringio.rb +167 -0
  11. metadata +73 -0
data/HACKING ADDED
@@ -0,0 +1,123 @@
1
+ = Guide to Hacking IO::Like
2
+
3
+ == Licensing
4
+
5
+ Contributed code must be licensed under the same license as this project. See
6
+ the included LICENSE file for details. Special consideration MAY be made in
7
+ some cases, but such cases will be rare.
8
+
9
+
10
+ == Dependencies
11
+
12
+ === Runtime
13
+
14
+ * Ruby 1.8.6 or greater
15
+
16
+
17
+ === Build
18
+
19
+ * rubygems 0.9.0 or greater
20
+ * rake 0.8.1
21
+ * rubyforge 1.0.0 (optional - used for publishing releases)
22
+ * allison 2.0.3 (optional - used for documentation only, if available)
23
+ * rsync (optional - used for publishing documentation)
24
+
25
+
26
+ === Install
27
+
28
+ * rubygems 0.9.0 or greater
29
+
30
+
31
+ == Versioning Policy
32
+
33
+ Version numbers will be in <em>x.y.z</em> format, where <em>x</em>, <em>y</em>,
34
+ and <em>z</em> are integers starting from 0. The version increment rules are
35
+ as follows:
36
+
37
+ <b>x</b>:: Planned releases which implement significant changes and/or break API
38
+ compatibility. An exception is to be made for the transition from
39
+ the <em>0.y.z</em> series to the <em>1.y.z</em> series since the
40
+ <em>0.y.z</em> series is expected to be unstable throughout
41
+ development. When incremented, <em>y</em> and <em>z</em> are reset
42
+ to 0.
43
+ <b>y</b>:: Planned releases which incorporate numerous bug fixes and/or new
44
+ features which do not break backward compatibility. When
45
+ incremented, <em>z</em> is reset to 0.
46
+ <b>z</b>:: Generally, unplanned releases which incorporate a single fix for a
47
+ critical defect.
48
+
49
+ This is the {Rational Versioning Policy}[http://www.rubygems.org/read/chapter/7]
50
+ as outlined in the {RubyGems User Guide}[http://www.rubygems.org/read/book/1].
51
+
52
+
53
+ == Support Policy
54
+
55
+ Due to limitations in resources (time/money/manpower), this project will focus
56
+ primarily upon the development line of the current release at any given time.
57
+ Fixes and new features should be applied first to that development line and then
58
+ backported to earlier releases if necessary and feasible. Long term maintenance
59
+ of previous releases is not planned. Users are generally expected to upgrade to
60
+ the latest release in order to receive updates unless an explicit declaration of
61
+ support for a previous release is made.
62
+
63
+
64
+ == Coding Style
65
+
66
+ The following points are not necessarily set in stone but should rather be used
67
+ as a good guideline. Consistency is the goal of coding style, and changes will
68
+ be more easily accepted if they are consistent with the rest of the code.
69
+
70
+ <b>File Encoding</b>:: UTF-8
71
+ <b>Indentation</b>:: Two spaces; no tabs
72
+ <b>Comments</b>:: Document classes, attributes, methods, and code
73
+ <b>Boolean Operators</b>:: Use <tt>&&</tt> and <tt>||</tt> for boolean tests;
74
+ avoid <tt>and</tt> and <tt>or</tt>
75
+ <b>Method Calls</b>:: Use <tt>a_method(arg, arg, etc)</tt>; <b>not</b>
76
+ <tt>a_method( arg, arg, etc )</tt>,
77
+ <tt>a_method arg, arg, etc</tt>, or any other
78
+ variation
79
+ <b>Blocks</b>:: <tt>do end</tt> for multi-line blocks and
80
+ <tt>{ }</tt> for single-line blocks
81
+ <b>Line length</b>:: Limit lines to a maximum of 80 characters
82
+ <b>General</b>:: Try to follow the flow and style of the rest of the
83
+ code
84
+
85
+
86
+ == Generating Patches
87
+
88
+ Patches should usually be generated against the <em>HEAD</em> revision of the
89
+ <em>master</em> branch. When generating patches, please try to implement only
90
+ a single feature or bug fix per patch. Documentation describing a patch should
91
+ be included along with the patch so that the maintainer can more easily
92
+ determine whether or not a patch is acceptable. Patches lacking the necessary
93
+ documentation will be ignored.
94
+
95
+ Patches will be much more readily accepted if test cases are provided which
96
+ verify correct operation. Such test cases should be provided within the patch
97
+ rather than as a separate patch. Proper documentation, especially for
98
+ user-visible APIs, is highly prized; providing accurate and detailed
99
+ documentation, often in the form of rubydocs, throughout new code contributions
100
+ will also increase the desirability of a patch.
101
+
102
+ If a series of patches is generated which cannot be applied individually, make
103
+ sure to mention the dependency relationships in whatever medium is being used
104
+ to distribute the patches. For instance, if a bug is discovered while
105
+ implementing a new feature, create a patch which fixes the bug followed by a
106
+ separate patch adding the feature. If the feature patch requires the bug fix
107
+ patch in order to work, note that dependency in the comments for the feature
108
+ patch by somehow referencing the bug fix patch.
109
+
110
+ The patch generation process in general:
111
+ $ git clone git://rubyforge.org/io-like.git # Clone the repo and check out
112
+ # the master branch.
113
+ $ cd io-like # Enter the workspace.
114
+ (make and test changes)
115
+ $ git add file1 file2 .. # Add new/modified files.
116
+ $ git commit # Commit changes.
117
+ $ git format-patch -C HEAD^ # Create a patch for the last
118
+ # commit.
119
+
120
+ Repeat as necessary until all patches are generated. Then either attach them to
121
+ 1 or more email messages addressed to the maintainer or attach them to tickets
122
+ in the issue tracker for the project. Remember to include a brief description
123
+ of the patch and its dependencies, if any.
data/LEGAL ADDED
@@ -0,0 +1,8 @@
1
+ = Other Legalities
2
+
3
+ == Files Licensed Differently
4
+
5
+ The following file(s) are provided under a license or licenses separate from
6
+ this project.
7
+
8
+ None at present
data/LICENSE ADDED
@@ -0,0 +1,57 @@
1
+ LICENSE text follows:
2
+
3
+ IO::Like is copyrighted free software by Jeremy Bopp
4
+ <jeremy at bopp dot net>. You can redistribute it and/or modify it under
5
+ either the terms of the GPL (see the included GPL file), or the conditions
6
+ below:
7
+
8
+ 1. You may make and give away verbatim copies of the source form of the
9
+ software without restriction, provided that you duplicate all of the
10
+ original copyright notices and associated disclaimers.
11
+
12
+ 2. You may modify your copy of the software in any way, provided that
13
+ you do at least ONE of the following:
14
+
15
+ a) place your modifications in the Public Domain or otherwise
16
+ make them Freely Available, such as by posting said
17
+ modifications to Usenet or an equivalent medium, or by allowing
18
+ the author to include your modifications in the software.
19
+
20
+ b) use the modified software only within your corporation or
21
+ organization.
22
+
23
+ c) rename any non-standard executables so the names do not conflict
24
+ with standard executables, which must also be provided.
25
+
26
+ d) make other distribution arrangements with the author.
27
+
28
+ 3. You may distribute the software in object code or executable
29
+ form, provided that you do at least ONE of the following:
30
+
31
+ a) distribute the executables and library files of the software,
32
+ together with instructions (in the manual page or equivalent)
33
+ on where to get the original distribution.
34
+
35
+ b) accompany the distribution with the machine-readable source of
36
+ the software.
37
+
38
+ c) give non-standard executables non-standard names, with
39
+ instructions on where to get the original software distribution.
40
+
41
+ d) make other distribution arrangements with the author.
42
+
43
+ 4. You may modify and include the covered part of the software into any
44
+ other software (possibly commercial). But some files in the
45
+ distribution may not be written by the author, such that they are not
46
+ under these terms. (See the file LEGAL for a listing and conditions)
47
+
48
+ 5. The scripts and library files supplied as input to or produced as
49
+ output from the software do not automatically fall under the
50
+ copyright of the software, but belong to whomever generated them,
51
+ and may be sold commercially, and may be aggregated with this
52
+ software.
53
+
54
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
55
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
56
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
57
+ PURPOSE.
@@ -0,0 +1,10 @@
1
+ CONTRIBUTORS
2
+ GPL
3
+ HACKING
4
+ LEGAL
5
+ LICENSE
6
+ MANIFEST
7
+ NEWS
8
+ README
9
+ lib/io/like.rb
10
+ test/lib/likestringio.rb
data/NEWS ADDED
@@ -0,0 +1,13 @@
1
+ = News and Notifications by Version
2
+
3
+ This file lists noteworthy changes which may affect users of this project. More
4
+ detailed information is available in the rest of the documentation.
5
+
6
+ <b>NOTE:</b> Date stamps in the following entries are in YYYY/MM/DD format.
7
+
8
+
9
+ == v0.1.0 (2008/07/03)
10
+
11
+ * Initial release
12
+ * All read, write, and seek functions implemented as defined in Ruby 1.8.6
13
+ * Most other IO methods also provided as no-ops and similar
data/README ADDED
@@ -0,0 +1,135 @@
1
+ = IO::Like - in the Likeness of IO
2
+
3
+ The IO::Like module provides all of the methods of typical IO implementations
4
+ such as File; most importantly the read, write, and seek series of methods. A
5
+ class which includes IO::Like needs to provide only a few methods in order to
6
+ enable the higher level methods. Buffering is automatically provided by default
7
+ for the methods which normally provide it in IO.
8
+
9
+ See the documentation for IO::Like for more details regarding the necessary
10
+ methods.
11
+
12
+
13
+ == License
14
+
15
+ Copyright © 2008 Jeremy Bopp <jeremy at bopp dot net>
16
+
17
+ Licensed under the same terms as Ruby -- See the included LICENSE file for
18
+ details
19
+
20
+
21
+ == Installation/Removal
22
+
23
+ Download the GEM file and install it with:
24
+ % sudo gem install io-like-VERSION.gem
25
+
26
+ or directly with:
27
+ % sudo gem install io-like
28
+
29
+ Removal is the same in either case:
30
+ % sudo gem uninstall io-like
31
+
32
+
33
+ == Example
34
+ More examples can be found in the +examples+ directory of the source
35
+ distribution.
36
+
37
+ A simple ROT13 codec:
38
+ gem 'io-like' # Use require_gem for rubygems versions older than 0.9.0.
39
+ require 'io/like'
40
+
41
+ class ROT13Filter
42
+ include IO::Like
43
+
44
+ def self.open(delegate_io)
45
+ filter = new(delegate_io)
46
+ return filter unless block_given?
47
+
48
+ begin
49
+ yield(filter)
50
+ ensure
51
+ filter.close unless filter.closed?
52
+ end
53
+ end
54
+
55
+ def initialize(delegate_io)
56
+ @delegate_io = delegate_io
57
+ end
58
+
59
+ private
60
+
61
+ def encode_rot13(string)
62
+ result = string.dup
63
+ 0.upto(result.length) do |i|
64
+ case result[i]
65
+ when 65..90
66
+ result[i] = (result[i] - 52) % 26 + 65
67
+ when 97..122
68
+ result[i] = (result[i] - 84) % 26 + 97
69
+ end
70
+ end
71
+ result
72
+ end
73
+
74
+ def unbuffered_read(length)
75
+ encode_rot13(@delegate_io.sysread(length))
76
+ end
77
+
78
+ def unbuffered_seek(offset, whence = IO::SEEKSET)
79
+ @delegate_io.sysseek(offset, whence)
80
+ end
81
+
82
+ def unbuffered_write(string)
83
+ @delegate_io.syswrite(encode_rot13(string))
84
+ end
85
+ end
86
+
87
+ File.open('normal_file.txt', 'w') do |f|
88
+ f.puts('This is a test')
89
+ end
90
+
91
+ File.open('rot13_file.txt', 'w') do |f|
92
+ ROT13Filter.open(f) do |rot13|
93
+ rot13.puts('This is a test')
94
+ end
95
+ end
96
+
97
+ File.open('normal_file.txt') do |f|
98
+ ROT13Filter.open(f) do |rot13|
99
+ puts(rot13.read) # -> Guvf vf n grfg
100
+ end
101
+ end
102
+
103
+ File.open('rot13_file.txt') do |f|
104
+ ROT13Filter.open(f) do |rot13|
105
+ puts(rot13.read) # -> This is a test
106
+ end
107
+ end
108
+
109
+ File.open('normal_file.txt') do |f|
110
+ ROT13Filter.open(f) do |rot13|
111
+ ROT13Filter.open(rot13) do |rot26| # ;-)
112
+ puts(rot26.read) # -> This is a test
113
+ end
114
+ end
115
+ end
116
+
117
+
118
+ == Known Bugs/Limitations
119
+
120
+ 1. Only up to version 1.8.6 of Ruby's IO interface is implemented. Version
121
+ 1.8.7 and eventually 1.9.0/2.0.0 support are coming soon.
122
+ 2. Ruby's finalization capabilities fall a bit short in a few respects, and as a
123
+ result, it is impossible to cause the close, close_read, or close_write
124
+ methods to be called automatically when an including class is garbage
125
+ collected. Define a class open method in the manner of File.open which
126
+ guarantees that an appropriate close method will be called after executing a
127
+ block. Other than that, be diligent about calling the close methods.
128
+ 3. Testcases needed. Maybe use some of rubyspec along with some test classes
129
+ act like the important parts of IO, File, and/or StringIO.
130
+
131
+
132
+ == Contributing
133
+
134
+ Contributions for bug fixes, documentation, extensions, tests, etc. are
135
+ encouraged. Please read the file HACKING for details.
@@ -0,0 +1,1329 @@
1
+ class IO # :nodoc:
2
+ # IO::Like is a module which provides most of the basic input and output
3
+ # functions of IO objects using methods named _unbuffered_read_,
4
+ # _unbuffered_write_, and _unbuffered_seek_.
5
+ #
6
+ # == Readers
7
+ #
8
+ # In order to use this module to provide input methods, a class which
9
+ # includes it must provide the _unbuffered_read_ method which takes one
10
+ # argument, a length, as follows:
11
+ #
12
+ # def unbuffered_read(length)
13
+ # ...
14
+ # end
15
+ #
16
+ # This method must return at most _length_ bytes as a String, raise EOFError
17
+ # if reading begins at the end of data, and raise SystemCallError on error.
18
+ # Errno::EAGAIN should be raised if there is no data to return immediately and
19
+ # the read operation should not block. Errno::EINTR should be raised if the
20
+ # read operation is interrupted before any data is read.
21
+ #
22
+ # == Writers
23
+ #
24
+ # In order to use this module to provide output methods, a class which
25
+ # includes it must provide the _unbuffered_write_ method which takes a single
26
+ # string argument as follows:
27
+ #
28
+ # def unbuffered_write(string)
29
+ # ...
30
+ # end
31
+ #
32
+ # This method must return the number of bytes written to the stream and
33
+ # should raise SystemCallError on errors. Errno::EAGAIN should be raised if
34
+ # no data can be written immediately and the write operation should not block.
35
+ # Errno::EINTR should be raised if the write operation is interrupted before
36
+ # any data is written.
37
+ #
38
+ # == Seekers
39
+ #
40
+ # In order to use this module to provide seeking methods, a class which
41
+ # includes it must provide the _unbuffered_seek_ method which takes two
42
+ # required arguments, an offset and a start position, as follows:
43
+ #
44
+ # def unbuffered_seek(offset, whence)
45
+ # ...
46
+ # end
47
+ #
48
+ # This method must return the new position within the data stream relative to
49
+ # the beginning of the stream and should raise SystemCallError on error.
50
+ # _offset_ can be any integer and _whence_ can be any of IO::SEEK_SET,
51
+ # IO::SEEK_CUR, or IO::SEEK_END. They are interpreted together as follows:
52
+ #
53
+ # whence | resulting position
54
+ # -------------+------------------------------------------------------------
55
+ # IO::SEEK_SET | Add offset to the position of the beginning of the stream.
56
+ # -------------+------------------------------------------------------------
57
+ # IO::SEEK_CUR | Add offset to the current position of the stream.
58
+ # -------------+------------------------------------------------------------
59
+ # IO::SEEK_END | Add offset to the position of the end of the stream.
60
+ #
61
+ # == Duplexed Streams
62
+ #
63
+ # In order to create a duplexed stream where writing and reading happen
64
+ # independently of each other, override the #duplexed? method to return
65
+ # +true+ and then provide the _unbuffered_read_ and _unbuffered_write_
66
+ # methods. Do *NOT* provide an _unbuffered_seek_ method or the contents of
67
+ # the internal read and write buffers may be lost unexpectedly.
68
+ # ---
69
+ # <b>NOTE:</b> Due to limitations of Ruby's finalizer, IO::Like#close is not
70
+ # automatically called when the object is garbage collected, so it must be
71
+ # explicitly called when the object is no longer needed or risk losing
72
+ # whatever data remains in the internal write buffer.
73
+ module Like
74
+ include Enumerable
75
+
76
+ # call-seq:
77
+ # ios << obj -> ios
78
+ #
79
+ # Writes _obj_ to the stream using #write and returns _ios_. _obj_ is
80
+ # converted to a String using _to_s_.
81
+ def <<(obj)
82
+ write(obj)
83
+ self
84
+ end
85
+
86
+ # call-seq:
87
+ # ios.binmode -> ios
88
+ #
89
+ # Returns +self+. Just for compatibility with IO.
90
+ def binmode
91
+ self
92
+ end
93
+
94
+ # call-seq:
95
+ # ios.close -> nil
96
+ #
97
+ # Arranges for #closed? to return +true+. Raises IOError if #closed?
98
+ # already returns +true+. For duplexed objects, calls #close_read and
99
+ # #close_write. For non-duplexed objects, calls #flush if #writable?
100
+ # returns +true+ and then sets a flag so that #closed? will return +true+.
101
+ def close
102
+ raise IOError, 'closed stream' if closed?
103
+ if duplexed? then
104
+ close_read unless closed_read?
105
+ close_write unless closed_write?
106
+ else
107
+ flush if writable?
108
+ @__io_like__closed = true
109
+ end
110
+ nil
111
+ end
112
+
113
+ # call-seq:
114
+ # ios.close_read -> nil
115
+ #
116
+ # For duplexed objects, arranges for #closed_read? to return +true+.
117
+ #
118
+ # Raises IOError if #duplexed returns +false+. Raises IOError if
119
+ # #closed_read? returns +true+.
120
+ def close_read
121
+ raise IOError, 'closed stream' if closed_read?
122
+ raise IOError, 'closing non-duplex IO for reading' unless duplexed?
123
+ @__io_like__closed_read = true
124
+ nil
125
+ end
126
+
127
+ # call-seq:
128
+ # ios.close_write -> nil
129
+ #
130
+ # For duplexed objects, calls #flush and arranges for #closed_write? to
131
+ # return +true+.
132
+ #
133
+ # Raises IOError if #duplexed? returns +false+. Raises IOError if
134
+ # #closed_write? returns +true+.
135
+ def close_write
136
+ raise IOError, 'closed stream' if closed_write?
137
+ raise IOError, 'closing non-duplex IO for writing' unless duplexed?
138
+ flush
139
+ @__io_like__closed_write = true
140
+ nil
141
+ end
142
+
143
+ # call-seq:
144
+ # ios.closed? -> true or false
145
+ #
146
+ # For non-duplexed objects, returns +true+ if #close was called, +false+
147
+ # otherwise. For duplexed objects, returns +true+ if both #closed_read?
148
+ # and #closed_write? return true.
149
+ def closed?
150
+ return closed_read? && closed_write? if duplexed?
151
+ @__io_like__closed || false
152
+ end
153
+
154
+ # call-seq:
155
+ # ios.closed_read? -> true or false
156
+ #
157
+ # Returns the result of calling #closed? for non-duplexed objects. For
158
+ # duplexed objects, returns +true+ if close_read was called, +false+
159
+ # otherwise.
160
+ def closed_read?
161
+ return closed? unless duplexed?
162
+ @__io_like__closed_read || false
163
+ end
164
+
165
+ # call-seq:
166
+ # ios.closed_write? -> true or false
167
+ #
168
+ # Returns the result of calling #closed? for non-duplexed objects. For
169
+ # duplexed objects, returns +true+ if close_write was called, +false+
170
+ # otherwise.
171
+ def closed_write?
172
+ return closed? unless duplexed?
173
+ @__io_like__closed_read || false
174
+ end
175
+
176
+ # call-seq:
177
+ # ios.duplexed? -> true or false
178
+ #
179
+ # Returns +false+. Override this to return +true+ when creating duplexed
180
+ # IO objects.
181
+ def duplexed?
182
+ false
183
+ end
184
+
185
+ # call-seq:
186
+ # ios.each_byte {|byte| block} -> ios
187
+ #
188
+ # Reads each byte (0..255) from the stream using #getc and calls the given
189
+ # block once for each byte, passing the byte as an argument.
190
+ #
191
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
192
+ # #unbuffered_read. Therefore, this method always blocks. Aside from that
193
+ # exception and the conversion of EOFError results into +nil+ results, this
194
+ # method will also raise the same errors and block at the same times as
195
+ # #unbuffered_read.
196
+ def each_byte
197
+ while (byte = getc) do
198
+ yield(byte)
199
+ end
200
+ self
201
+ end
202
+
203
+ # call-seq:
204
+ # ios.each_line(sep_string = $/) {|line| block } -> ios
205
+ # ios.each(sep_string = $/) {|line| block } -> ios
206
+ #
207
+ # Reads each line from the stream using #gets and calls the given block once
208
+ # for each line, passing the line as an argument.
209
+ #
210
+ # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
211
+ # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
212
+ # always blocks. Aside from that exception and the conversion of EOFError
213
+ # results into +nil+ results, this method will also raise the same errors
214
+ # and block at the same times as #unbuffered_read.
215
+ def each_line(sep_string = $/)
216
+ while (line = gets(sep_string)) do
217
+ yield(line)
218
+ end
219
+ self
220
+ end
221
+ alias :each :each_line
222
+
223
+ # call-seq:
224
+ # ios.eof? -> true or false
225
+ # ios.eof -> true or false
226
+ #
227
+ # Returns +true+ if there is no more data to read.
228
+ #
229
+ # This works by using #getc to fetch the next character and using #ungetc to
230
+ # put the character back if one was fetched. It may be a good idea to
231
+ # replace this implementation in derivative classes.
232
+ #
233
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
234
+ # #unbuffered_read. Therefore, this method always blocks. Aside from that
235
+ # exception and the conversion of EOFError results into +nil+ results, this
236
+ # method will also raise the same errors and block at the same times as
237
+ # #unbuffered_read.
238
+ def eof?
239
+ if (char = getc) then
240
+ ungetc(char)
241
+ return false
242
+ end
243
+ true
244
+ end
245
+ alias :eof :eof?
246
+
247
+ # call-seq:
248
+ # ios.fcntl
249
+ #
250
+ # Raises NotImplementedError
251
+ def fcntl(*args)
252
+ raise NotImplementedError, 'not implemented'
253
+ end
254
+
255
+ # call-seq:
256
+ # ios.fileno -> nil
257
+ #
258
+ # Returns +nil+. Just for compatibility with IO.
259
+ def fileno
260
+ nil
261
+ end
262
+
263
+ # call-seq:
264
+ # ios.fill_size -> integer
265
+ #
266
+ # Returns the number of bytes to read as a block whenever the internal
267
+ # buffer needs to be refilled. Unless set explicitly via #fill_size=, this
268
+ # defaults to 4096.
269
+ #
270
+ # Raises IOError if #closed_read? returns +true+. Raises IOError if the
271
+ # stream is not opened for reading.
272
+ def fill_size
273
+ raise IOError, 'closed stream' if closed_read?
274
+ raise IOError, 'not opened for reading' unless readable?
275
+
276
+ @__io_like__fill_size ||= 4096
277
+ end
278
+
279
+ # call-seq:
280
+ # ios.fill_size = integer -> integer
281
+ #
282
+ # Sets the number of bytes to read as a block whenever the internal read
283
+ # buffer needs to be refilled. The new value must be a number greater than
284
+ # or equal to 0. Setting this to 0 effectively disables buffering.
285
+ #
286
+ # Raises IOError if #closed_read? returns +true+. Raises IOError if the
287
+ # stream is not opened for reading.
288
+ def fill_size=(fill_size)
289
+ raise IOError, 'closed stream' if closed_read?
290
+ raise IOError, 'not opened for reading' unless readable?
291
+
292
+ unless fill_size >= 0 then
293
+ raise ArgumentError, "non-positive fill_size #{fill_size} given"
294
+ end
295
+ @__io_like__fill_size = fill_size
296
+ end
297
+
298
+ # call-seq:
299
+ # ios.flush -> ios
300
+ #
301
+ # Flushes the internal write buffer to the underlying data stream.
302
+ #
303
+ # Regardless of the blocking status of the data stream or interruptions
304
+ # during writing, this method will block until either all the data is
305
+ # flushed or until an error is raised.
306
+ #
307
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
308
+ # #writable? returns +true+.
309
+ #
310
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
311
+ # #unbuffered_write. Therefore, this method always blocks if unable to
312
+ # flush the internal write buffer. Aside from that exception, this
313
+ # method will also raise the same errors and block at the same times as
314
+ # #unbuffered_write.
315
+ def flush
316
+ raise IOError, 'closed stream' if closed_write?
317
+
318
+ begin
319
+ buffered_flush
320
+ rescue Errno::EAGAIN, Errno::EINTR
321
+ retry if write_ready?
322
+ end
323
+ self
324
+ end
325
+
326
+ # call-seq:
327
+ # ios.flush_size -> integer
328
+ #
329
+ # Returns the number of bytes at which the internal write buffer is flushed
330
+ # automatically to the data stream. Unless set explicitly via #flush_size=,
331
+ # this defaults to 4096.
332
+ #
333
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
334
+ # #writable? returns +true+.
335
+ def flush_size
336
+ raise IOError, 'closed stream' if closed_write?
337
+ raise IOError, 'not opened for writing' unless writable?
338
+
339
+ @__io_like__flush_size ||= 4096
340
+ end
341
+
342
+ # call-seq:
343
+ # ios.flush_size = integer -> integer
344
+ #
345
+ # Sets the number of bytes at which the internal write buffer is flushed
346
+ # automatically to the data stream. The new value must be a number greater
347
+ # than or equal to 0. Setting this to 0 effectively disables buffering.
348
+ #
349
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
350
+ # #writable? returns +true+.
351
+ def flush_size=(flush_size)
352
+ raise IOError, 'closed stream' if closed_write?
353
+ raise IOError, 'not opened for writing' unless writable?
354
+
355
+ unless flush_size >= 0 then
356
+ raise ArgumentError, "non-positive flush_size #{flush_size} given"
357
+ end
358
+ @__io_like__flush_size = flush_size
359
+ end
360
+
361
+ # call-seq:
362
+ # ios.getc -> nil or integer
363
+ #
364
+ # Calls #readchar and either returns the result or +nil+ if #readchar raises
365
+ # EOFError.
366
+ #
367
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
368
+ # #readable? returns +true+. Raises all errors raised by #unbuffered_read
369
+ # except for EOFError.
370
+ #
371
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
372
+ # #unbuffered_read. Therefore, this method always blocks. Aside from that
373
+ # exception and the conversion of EOFError results into +nil+ results, this
374
+ # method will also raise the same errors and block at the same times as
375
+ # #unbuffered_read.
376
+ def getc
377
+ readchar
378
+ rescue EOFError
379
+ nil
380
+ end
381
+
382
+ # call-seq:
383
+ # ios.gets(sep_string = $/) -> nil or string
384
+ #
385
+ # Calls #readline with _sep_string_ as an argument and either returns the
386
+ # result or +nil+ if #readline raises EOFError. If #readline returns some
387
+ # data, the returned data is assigned to <tt>$_</tt> and <tt>$.</tt> is set
388
+ # to the value of #lineno.
389
+ #
390
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
391
+ # #readable? returns +true+. Raises all errors raised by #unbuffered_read
392
+ # except for EOFError.
393
+ #
394
+ # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
395
+ # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
396
+ # always blocks. Aside from that exception and the conversion of EOFError
397
+ # results into +nil+ results, this method will also raise the same errors
398
+ # and block at the same times as #unbuffered_read.
399
+ def gets(sep_string = $/)
400
+ # Set the last read line in the global.
401
+ $_ = readline(sep_string)
402
+ # Set the last line number in the global.
403
+ $. = lineno
404
+ # Return the last read line.
405
+ $_
406
+ rescue EOFError
407
+ nil
408
+ end
409
+
410
+ # call-seq:
411
+ # ios.isatty -> false
412
+ #
413
+ # Returns +false+. Just for compatibility with IO.
414
+ def isatty
415
+ false
416
+ end
417
+ alias :tty? :isatty
418
+
419
+ # call-seq:
420
+ # ios.lineno -> integer
421
+ #
422
+ # Returns the number of times #gets was called and returned non-+nil+ data.
423
+ # By default this is the number of lines read, but calling #gets or any of
424
+ # the other line-based reading methods with a non-default value for
425
+ # _sep_string_ or after changing <tt>$/</tt> will affect this.
426
+ #
427
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
428
+ # #readable? returns +true+.
429
+ def lineno
430
+ raise IOError, 'closed stream' if closed_read?
431
+ raise IOError, 'not opened for reading' unless readable?
432
+ @__io_like__lineno ||= 0
433
+ end
434
+
435
+ # call-seq:
436
+ # ios.lineno = lineno -> lineno
437
+ #
438
+ # Sets the current line number to the given value. <tt>$.</tt> is updated
439
+ # by the _next_ call to #gets.
440
+ #
441
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
442
+ # #readable? returns +true+.
443
+ def lineno=(integer)
444
+ raise IOError, 'closed stream' if closed_read?
445
+ raise IOError, 'not opened for reading' unless readable?
446
+ @__io_like__lineno = integer
447
+ end
448
+
449
+ # call-seq:
450
+ # ios.path -> nil
451
+ #
452
+ # Returns +nil+. Just for compatibility with IO.
453
+ def path
454
+ nil
455
+ end
456
+
457
+ # call-seq:
458
+ # ios.pos = position -> position
459
+ #
460
+ # Sets the data position to _position_ by calling #seek.
461
+ #
462
+ # As a side effect, the internal read and write buffers are flushed.
463
+ #
464
+ # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
465
+ # #seekable? returns +true+.
466
+ #
467
+ # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
468
+ # (when the internal write buffer is not empty), it will also raise the same
469
+ # errors and block at the same times as those functions.
470
+ def pos=(position)
471
+ seek(position, IO::SEEK_SET)
472
+ position
473
+ end
474
+
475
+ # call-seq:
476
+ # ios.print([obj, ...]) -> nil
477
+ #
478
+ # Writes the given object(s), if any, to the stream using #write after
479
+ # converting them to strings by calling their _to_s_ methods. If no
480
+ # objects are given, <tt>$_</tt> is used. The field separator (<tt>$,</tt>)
481
+ # is written between successive objects if it is not +nil+. The output
482
+ # record separator (<tt>$\\</tt>) is written after all other data if it is
483
+ # not nil.
484
+ #
485
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
486
+ # #writable? returns +true+.
487
+ #
488
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
489
+ # #unbuffered_write. Therefore, this method always blocks if unable to
490
+ # immediately write +[obj, ...]+ completely. Aside from that exception,
491
+ # this method will also raise the same errors and block at the same times as
492
+ # #unbuffered_write.
493
+ def print(*args)
494
+ args << $_ if args.empty?
495
+ first_arg = true
496
+ args.each do |arg|
497
+ # Write a field separator before writing each argument after the first
498
+ # one unless no field separator is specified.
499
+ if first_arg then
500
+ first_arg = false
501
+ elsif ! $,.nil? then
502
+ write($,)
503
+ end
504
+
505
+ # If the argument is nil, write 'nil'; otherwise, write the stringified
506
+ # form of the argument.
507
+ if arg.nil? then
508
+ write('nil')
509
+ else
510
+ write(arg)
511
+ end
512
+ end
513
+
514
+ # Write the output record separator if one is specified.
515
+ write($\) unless $\.nil?
516
+ nil
517
+ end
518
+
519
+ # call-seq:
520
+ # ios.printf(format_string [, obj, ...]) -> nil
521
+ #
522
+ # Writes the String returned by calling Kernel.sprintf using the given
523
+ # arguments.
524
+ #
525
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
526
+ # #writable? returns +true+.
527
+ #
528
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
529
+ # #unbuffered_write. Therefore, this method always blocks if unable to
530
+ # immediately write its arguments completely. Aside from that exception,
531
+ # this method will also raise the same errors and block at the same times as
532
+ # #unbuffered_write.
533
+ def printf(*args)
534
+ write(sprintf(*args))
535
+ nil
536
+ end
537
+
538
+ # call-seq:
539
+ # ios.putc(obj) -> obj
540
+ #
541
+ # If _obj_ is Numeric, write the result of <tt>obj.chr</tt>; otherwise,
542
+ # write the first character of <tt>obj.to_s</tt>.
543
+ #
544
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
545
+ # #writable? returns +true+.
546
+ #
547
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
548
+ # #unbuffered_write. Therefore, this method always blocks if unable to
549
+ # immediately write _obj_ completely. Aside from that exception, this
550
+ # method will also raise the same errors and block at the same times as
551
+ # #unbuffered_write.
552
+ def putc(obj)
553
+ char = case obj
554
+ when Numeric
555
+ obj.chr
556
+ else
557
+ obj.to_s[0].chr
558
+ end
559
+ write(char)
560
+ obj
561
+ end
562
+
563
+ # call-seq:
564
+ # ios.puts([obj, ...]) -> nil
565
+ #
566
+ # Writes the given object(s), if any, to the stream using #write after
567
+ # converting them to strings using their _to_s_ methods. Unlike #print,
568
+ # Array instances are recursively processed. A record separator character
569
+ # is written after each object which does not end with the record separator
570
+ # already. If no objects are given, a single record separator is written.
571
+ #
572
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
573
+ # #writable? returns +true+.
574
+ #
575
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
576
+ # #unbuffered_write. Therefore, this method always blocks if unable to
577
+ # immediately write +[obj, ...]+ completely. Aside from that exception,
578
+ # this method will also raise the same errors and block at the same times as
579
+ # #unbuffered_write.
580
+ #
581
+ # NOTE: In order to be compatible with IO#puts, the record separator is
582
+ # currently hardcoded to be a single newline (<tt>"\n"</tt>) even though the
583
+ # documentation implies that the output record separator (<tt>$\\</tt>)
584
+ # should be used.
585
+ def puts(*args)
586
+ # Set the output record separator such that this method is compatible with
587
+ # IO#puts.
588
+ ors = "\n"
589
+
590
+ # Write only the record separator if no arguments are given.
591
+ if args.length == 0 then
592
+ write(ors)
593
+ return
594
+ end
595
+
596
+ # Write each argument followed by the record separator. Recursively
597
+ # process arguments which are Array instances.
598
+ args.each do |arg|
599
+ if arg.kind_of?(Array) then
600
+ puts(*arg)
601
+ else
602
+ line = arg.nil? ? 'nil' : arg.to_s
603
+ line += ors if line.index(ors, -ors.length).nil?
604
+ write(line)
605
+ end
606
+ end
607
+
608
+ nil
609
+ end
610
+
611
+ # call-seq:
612
+ # ios.read([length[, buffer]]) -> nil, buffer, or string
613
+ #
614
+ # If _length_ is specified and is a positive integer, at most length bytes
615
+ # are returned. Truncated data will occur if there is insufficient data
616
+ # left to fulfill the request. If the read starts at the end of data, +nil+
617
+ # is returned.
618
+ #
619
+ # If _length_ is unspecified or +nil+, all remaining data is returned. If
620
+ # no data would be returned at all, an empty String is returned.
621
+ #
622
+ # If _buffer_ is specified, it is assumed to be a String and will be filled
623
+ # with the returned data if any.
624
+ #
625
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
626
+ # #readable? returns +true+.
627
+ #
628
+ # NOTE: Because this method relies on #unbuffered_read, it will also raise
629
+ # the same errors and block at the same times as that function.
630
+ def read(length = nil, buffer = nil)
631
+ # Check the validity of the method arguments.
632
+ unless length.nil? || length >= 0 then
633
+ raise ArgumentError, "negative length #{length} given"
634
+ end
635
+ buffer = '' if buffer.nil?
636
+ # Flush the buffer.
637
+ buffer.slice!(0..-1)
638
+
639
+ if length.nil? then
640
+ # Read and return everything.
641
+ begin
642
+ loop do
643
+ buffer << buffered_read(4096)
644
+ end
645
+ rescue EOFError
646
+ # Ignore this.
647
+ end
648
+ else
649
+ # Read and return up to length bytes.
650
+ begin
651
+ buffer << buffered_read(length)
652
+ rescue EOFError
653
+ # Return nil to the caller at end of file when requesting a specific
654
+ # amount of data.
655
+ return nil
656
+ end
657
+ end
658
+ buffer
659
+ end
660
+
661
+ # call-seq:
662
+ # ios.read_ready? -> true or false
663
+ #
664
+ # Returns +true+ when the stream may be read without error, +false+
665
+ # otherwise. This method will block until one of the conditions is known.
666
+ #
667
+ # This default implementation of #read_ready? is a hack which should be able
668
+ # to work for both real IO objects and IO-like objects; however, it is
669
+ # inefficient since it merely sleeps for 1 second and then returns +true+ as
670
+ # long as #closed_read? returns +false+. IO.select should be used for real
671
+ # IO objects to wait for a readable condition on platforms with support for
672
+ # IO.select. Other solutions should be found as necessary to improve this
673
+ # implementation on a case by case basis.
674
+ #
675
+ # Basically, this method should be overridden in derivative classes.
676
+ def read_ready?
677
+ return false unless readable?
678
+ sleep(1)
679
+ true
680
+ end
681
+
682
+ # call-seq:
683
+ # ios.readable? -> true or false
684
+ #
685
+ # Returns +true+ if the stream is both open and readable, +false+ otherwise.
686
+ #
687
+ # This implementation calls #closed_read? and checks to see if
688
+ # #unbuffered_read is defined in order to make its determination. Override
689
+ # this if the implementing class always provides the #unbuffered_read method
690
+ # but may not always be open in a readable mode.
691
+ def readable?
692
+ ! closed_read? && respond_to?(:unbuffered_read, true)
693
+ end
694
+
695
+ # call-seq:
696
+ # ios.readbytes(length) -> string
697
+ #
698
+ # Reads and returns _length_ bytes from the data stream.
699
+ #
700
+ # Raises EOFError if reading begins at the end of the stream. Raises
701
+ # IOError if #closed_read? returns +true+. Raises IOError unless
702
+ # #readable? returns +true+. Raises TruncatedDataError if insufficient
703
+ # data is immediately available to satisfy the request.
704
+ #
705
+ # In the case of TruncatedDataError being raised, the retrieved data can be
706
+ # fetched from the _data_ attribute of the exception.
707
+ #
708
+ # This method is basically copied from IO#readbytes.
709
+ #
710
+ # NOTE: Because this method relies on #unbuffered_read, it will also raise
711
+ # the same errors and block at the same times as that function.
712
+ def readbytes(length)
713
+ buffer = read(length)
714
+ if buffer.nil? then
715
+ raise EOFError, "end of file reached"
716
+ end
717
+ if buffer.length < length then
718
+ raise TruncatedDataError.new("data truncated", buffer)
719
+ end
720
+ buffer
721
+ end
722
+
723
+ # call-seq:
724
+ # ios.readchar -> integer
725
+ #
726
+ # Returns the next 8-bit byte (0..255) from the stream.
727
+ #
728
+ # Raises EOFError when there is no more data in the stream. Raises IOError
729
+ # if #closed_read? returns +true+. Raises IOError unless #readable? returns
730
+ # +true+.
731
+ #
732
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
733
+ # #unbuffered_read. Therefore, this method always blocks. Aside from that
734
+ # exception, this method will also raise the same errors and block at the
735
+ # same times as #unbuffered_read.
736
+ def readchar
737
+ raise IOError, 'closed stream' if closed_read?
738
+ buffered_read(1)[0]
739
+ rescue Errno::EAGAIN, Errno::EINTR
740
+ retry if read_ready?
741
+ end
742
+
743
+ # call-seq:
744
+ # ios.readline(sep_string = $/) -> string
745
+ #
746
+ # Returns the next line from the stream, where lines are separated by
747
+ # _sep_string_. Increments #lineno.
748
+ #
749
+ # If _sep_string_ is +nil+, a line is defined as the remaining contents of
750
+ # the stream. If _sep_string_ is empty, a paragraph is returned, where a
751
+ # paragraph is defined as data followed by 2 or more successive newline
752
+ # characters (only 2 newlines are returned at the end of the returned data).
753
+ #
754
+ # In any case, the end of the stream terminates the current line.
755
+ #
756
+ # Raises EOFError when there is no more data in the stream. Raises IOError
757
+ # if #closed_read? returns +true+. Raises IOError unless #readable? returns
758
+ # +true+.
759
+ #
760
+ # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
761
+ # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
762
+ # always blocks. Aside from that exception, this method will also raise the
763
+ # same errors and block at the same times as #unbuffered_read.
764
+ def readline(sep_string = $/)
765
+ raise IOError, 'closed stream' if closed_read?
766
+
767
+ buffer = ''
768
+ begin
769
+ if sep_string.nil? then
770
+ # A nil line separator means that the user wants to capture all the
771
+ # remaining input.
772
+ loop do
773
+ buffer << buffered_read(4096)
774
+ end
775
+ else
776
+ begin
777
+ # Record if the user requested paragraphs rather than lines.
778
+ paragraph_requested = sep_string.empty?
779
+ # An empty line separator string indicates that the user wants to
780
+ # return paragraphs. A pair of newlines in the stream is used to mark
781
+ # this.
782
+ sep_string = "\n\n" if paragraph_requested
783
+
784
+ # Add each character from the input to the buffer until either the
785
+ # buffer has the right ending or the end of the input is reached.
786
+ while buffer.index(sep_string, -sep_string.length).nil? &&
787
+ (char = readchar) do
788
+ buffer << char
789
+ end
790
+
791
+ if paragraph_requested then
792
+ # If the user requested paragraphs instead of lines, we need to
793
+ # consume and discard all newlines remaining at the front of the
794
+ # input.
795
+ while (char = readchar) && char == "\n" do; end
796
+ # Put back the last character.
797
+ ungetc(char[0])
798
+ end
799
+ rescue Errno::EAGAIN, Errno::EINTR
800
+ retry if read_ready?
801
+ end
802
+ end
803
+ rescue EOFError
804
+ raise if buffer.empty?
805
+ end
806
+ # Increment the number of times this method has returned a "line".
807
+ self.lineno += 1
808
+ buffer
809
+ end
810
+
811
+ # call-seq:
812
+ # ios.readlines(sep_string = $/) -> array
813
+ #
814
+ # Returns an Array containing the lines in the stream using #each_line.
815
+ #
816
+ # If _sep_string_ is +nil+, a line is defined as the remaining contents of
817
+ # the stream. If _sep_string_ is empty, a paragraph is returned, where a
818
+ # paragraph is defined as data followed by 2 or more successive newline
819
+ # characters (only 2 newlines are returned at the end of the returned data).
820
+ #
821
+ # In any case, the end of the stream terminates the current line.
822
+ #
823
+ # Raises EOFError when there is no more data in the stream. Raises IOError
824
+ # if #closed_read? returns +true+. Raises IOError unless #readable? returns
825
+ # +true+.
826
+ #
827
+ # NOTE: When _sep_string_ is not +nil+, this method ignores Errno::EAGAIN
828
+ # and Errno::EINTR raised by #unbuffered_read. Therefore, this method
829
+ # always blocks. Aside from that exception, this method will also raise the
830
+ # same errors and block at the same times as #unbuffered_read.
831
+ def readlines(sep_string = $/)
832
+ lines = []
833
+ each_line(sep_string) { |line| lines << line }
834
+ lines
835
+ end
836
+
837
+ # call-seq:
838
+ # ios.readpartial(length[, buffer]) -> string or buffer
839
+ #
840
+ # Returns at most _length_ bytes from the data stream using only the
841
+ # internal read buffer if the buffer is not empty. Falls back to reading
842
+ # from the stream if the buffer is empty. Blocks if no data is available
843
+ # from either the internal read buffer or the data stream regardless of
844
+ # whether or not the data stream would block.
845
+ #
846
+ # Raises EOFError when there is no more data in the stream. Raises IOError
847
+ # if #closed_read? returns +true+. Raises IOError unless #readable? returns
848
+ # +true+.
849
+ #
850
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
851
+ # #unbuffered_read. Therefore, this method always blocks if unable to
852
+ # immediately return _length_ bytes. Aside from that exception, this method
853
+ # will also raise the same errors and block at the same times as
854
+ # #unbuffered_read.
855
+ def readpartial(length, buffer = nil)
856
+ # Check the validity of the method arguments.
857
+ unless length >= 0 then
858
+ raise ArgumentError, "negative length #{length} given"
859
+ end
860
+ buffer = '' if buffer.nil?
861
+ # Flush the buffer.
862
+ buffer.slice!(0..-1)
863
+
864
+ raise IOError, 'closed stream' if closed_read?
865
+ raise IOError, 'not opened for reading' unless readable?
866
+
867
+ # Read and return up to length bytes.
868
+ if internal_read_buffer.empty? then
869
+ begin
870
+ buffer << buffered_read(length)
871
+ rescue Errno::EAGAIN, Errno::EINTR
872
+ retry if read_ready?
873
+ end
874
+ else
875
+ buffer << internal_read_buffer.slice!(0, length)
876
+ end
877
+ buffer
878
+ end
879
+
880
+ # call-seq:
881
+ # ios.rewind -> 0
882
+ #
883
+ # Sets the position of the file pointer to the beginning of the stream and
884
+ # returns 0 when complete. The lineno attribute is reset to 0 if
885
+ # successful.
886
+ #
887
+ # As a side effect, the internal read and write buffers are flushed.
888
+ #
889
+ # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
890
+ # #seekable? returns +true+.
891
+ #
892
+ # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
893
+ # (when the internal write buffer is not empty), it will also raise the same
894
+ # errors and block at the same times as those functions.
895
+ def rewind
896
+ seek(0, IO::SEEK_SET)
897
+ self.lineno = 0
898
+ end
899
+
900
+ # call-seq:
901
+ # seek(offset[, whence]) -> 0
902
+ #
903
+ # Sets the current data position to _offset_ based on the setting of
904
+ # _whence_. If _whence_ is unspecified or IO::SEEK_SET, _offset_ counts
905
+ # from the beginning of the data. If _whence_ is IO::SEEK_END, _offset_
906
+ # counts from the end of the data (_offset_ should be negative here). If
907
+ # _whence_ is IO::SEEK_CUR, _offset_ is relative to the current position.
908
+ #
909
+ # As a side effect, the internal read and write buffers are flushed.
910
+ #
911
+ # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
912
+ # #seekable? returns +true+.
913
+ #
914
+ # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
915
+ # (when the internal write buffer is not empty), it will also raise the same
916
+ # errors and block at the same times as those functions.
917
+ def seek(offset, whence = IO::SEEK_SET)
918
+ raise IOError, 'closed stream' if closed?
919
+
920
+ buffered_seek(offset, whence)
921
+ 0
922
+ end
923
+
924
+ # call-seq:
925
+ # ios.seekable? -> true or false
926
+ #
927
+ # Returns +true+ if the stream is both open and seekable, +false+ otherwise.
928
+ #
929
+ # This implementation calls #closed? and checks to see if #unbuffered_seek
930
+ # is defined in order to make its determination. Override this if the
931
+ # implementing class always provides the #unbuffered_seek method but may not
932
+ # always be seekable.
933
+ def seekable?
934
+ ! closed? && respond_to?(:unbuffered_seek, true)
935
+ end
936
+
937
+ # call-seq:
938
+ # ios.sync -> true or false
939
+ #
940
+ # Returns true if the internal write buffer is currently being bypassed,
941
+ # false otherwise.
942
+ #
943
+ # Raises IOError if #closed_write? returns +true+.
944
+ def sync
945
+ raise IOError, 'closed stream' if closed_write?
946
+ @__io_like__sync ||= false
947
+ end
948
+
949
+ # call-seq:
950
+ # ios.sync = boolean -> boolean
951
+ #
952
+ # When set to +true+ the internal write buffer will be bypassed. Any data
953
+ # currently in the buffer will be flushed prior to the next output
954
+ # operation. When set to +false+, the internal write buffer will be
955
+ # enabled.
956
+ #
957
+ # Raises IOError if #closed_write? returns +true+.
958
+ def sync=(sync)
959
+ raise IOError, 'closed stream' if closed_write?
960
+ @__io_like__sync = sync
961
+ end
962
+
963
+ # call-seq:
964
+ # ios.sysread(length) -> string
965
+ #
966
+ # Reads and returns up to _length_ bytes directly from the data stream,
967
+ # bypassing the internal read buffer.
968
+ #
969
+ # Returns <tt>""</tt> if _length_ is 0 regardless of the status of the data
970
+ # stream. This is for compatibility with IO#sysread.
971
+ #
972
+ # Raises EOFError if reading begins at the end of the stream. Raises
973
+ # IOError if the internal read buffer is not empty. Raises IOError if
974
+ # #closed_read? returns +true+.
975
+ #
976
+ # NOTE: Because this method relies on #unbuffered_read, it will also raise
977
+ # the same errors and block at the same times as that function.
978
+ def sysread(length, buffer = nil)
979
+ buffer = '' if buffer.nil?
980
+ buffer.slice!(0..-1)
981
+ return buffer if length == 0
982
+
983
+ raise IOError, 'closed stream' if closed_read?
984
+ raise IOError, 'not opened for reading' unless readable?
985
+ unless internal_read_buffer.empty? then
986
+ raise IOError, 'sysread on buffered IO'
987
+ end
988
+
989
+ buffer << unbuffered_read(length)
990
+ end
991
+
992
+ # call-seq:
993
+ # ios.sysseek(offset, whence) -> integer
994
+ #
995
+ # Sets the data pointer of the data stream to the position requested by
996
+ # _offset_ and _whence_ and returns the new position.
997
+ #
998
+ # Raises IOError if the internal read buffer is not empty. Raises IOError
999
+ # if #closed? returns +true+.
1000
+ #
1001
+ # See the description of the operation of #unbuffered_seek for information
1002
+ # concerning how to interpret _offset_ and _whence_.
1003
+ #
1004
+ # NOTE: Because this method relies on #unbuffered_seek, it will also raise
1005
+ # the same errors and block at the same times as that function.
1006
+ def sysseek(offset, whence = IO::SEEK_SET)
1007
+ raise IOError, 'closed stream' if closed?
1008
+ raise Errno::ESPIPE, 'Illegal seek' unless seekable?
1009
+ raise IOError, 'sysseek on buffered IO' unless internal_read_buffer.empty?
1010
+ unless internal_write_buffer.empty? then
1011
+ warn('warning: sysseek on buffered IO')
1012
+ end
1013
+ unbuffered_seek(offset, whence)
1014
+ end
1015
+
1016
+ # call-seq:
1017
+ # ios.syswrite(string) -> integer
1018
+ #
1019
+ # Writes _string_ directly to the data stream, bypassing the internal write
1020
+ # buffer and returns the number of bytes written.
1021
+ #
1022
+ # As a side effect for non-duplex objects, the internal read buffer is
1023
+ # flushed.
1024
+ #
1025
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
1026
+ # #writable? returns +true+.
1027
+ #
1028
+ # NOTE: Because this method relies on #unbuffered_write, it will also raise
1029
+ # the same errors and block at the same times as that function.
1030
+ def syswrite(string)
1031
+ raise IOError, 'closed stream' if closed_write?
1032
+ raise IOError, 'not opened for writing' unless writable?
1033
+ unless duplexed? || internal_read_buffer.empty? then
1034
+ internal_read_buffer.slice(0..-1)
1035
+ end
1036
+ unless internal_write_buffer.empty? then
1037
+ warn('warning: syswrite on buffered IO')
1038
+ end
1039
+
1040
+ unbuffered_write(string)
1041
+ end
1042
+
1043
+ # call-seq:
1044
+ # ios.tell -> integer
1045
+ #
1046
+ # Returns the current offest of ios.
1047
+ #
1048
+ # Raises IOError if #closed? returns +true+. Raises Errno::ESPIPE unless
1049
+ # #seekable? returns +true+.
1050
+ #
1051
+ # As a side effect, the internal write buffer is flushed unless this is
1052
+ # a duplexed object. This is for compatibility with the behavior of
1053
+ # IO#tell.
1054
+ #
1055
+ # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
1056
+ # (when the internal write buffer is not empty), it will also raise the same
1057
+ # errors and block at the same times as those functions.
1058
+ def tell
1059
+ raise IOError, 'closed stream' if closed?
1060
+
1061
+ buffered_flush unless internal_write_buffer.empty?
1062
+ buffered_tell
1063
+ end
1064
+ alias :pos :tell
1065
+
1066
+ # call-seq:
1067
+ # ios.to_io -> ios
1068
+ #
1069
+ # Returns _ios_.
1070
+ def to_io
1071
+ self
1072
+ end
1073
+
1074
+ # call-seq:
1075
+ # ios.ungetc(integer) -> nil
1076
+ #
1077
+ # Calls #unread with <tt>integer.chr</tt> as an argument.
1078
+ #
1079
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
1080
+ # #readable? returns +true+.
1081
+ def ungetc(integer)
1082
+ unread(integer.chr)
1083
+ end
1084
+
1085
+ # call-seq:
1086
+ # ios.unread(string) -> nil
1087
+ #
1088
+ # Pushes the given string onto the front of the internal read buffer and
1089
+ # returns +nil+. If _string_ is not a String, it is converted to one using
1090
+ # its +to_s+ method.
1091
+ #
1092
+ # Raises IOError if #closed_read? returns +true+. Raises IOError unless
1093
+ # #readable? returns +true+.
1094
+ def unread(string)
1095
+ raise IOError, 'closed stream' if closed_read?
1096
+ raise IOError, 'not opened for reading' unless readable?
1097
+ internal_read_buffer.insert(0, data.to_s)
1098
+ nil
1099
+ end
1100
+
1101
+ # call-seq:
1102
+ # ios.write_ready? -> true or false
1103
+ #
1104
+ # Returns +true+ when the stream may be written without error, +false+
1105
+ # otherwise. This method will block until one of the conditions is known.
1106
+ #
1107
+ # This default implementation of #write_ready? is a hack which should be
1108
+ # able to work for both real IO objects and IO-like objects; however, it is
1109
+ # inefficient since it merely sleeps for 1 second and then returns +true+ as
1110
+ # long as #closed_write? returns +false+. IO.select should be used for real
1111
+ # IO objects to wait for a writeable condition on platforms with support for
1112
+ # IO.select. Other solutions should be found as necessary to improve this
1113
+ # implementation on a case by case basis.
1114
+ #
1115
+ # Basically, this method should be overridden in derivative classes.
1116
+ def write_ready?
1117
+ return false unless writable?
1118
+ sleep(1)
1119
+ true
1120
+ end
1121
+
1122
+ # call-seq:
1123
+ # ios.writable? -> true or false
1124
+ #
1125
+ # Returns +true+ if the stream is both open and writable, +false+ otherwise.
1126
+ #
1127
+ # This implementation calls #closed_write? and checks to see if
1128
+ # #unbuffered_write is defined in order to make its determination. Override
1129
+ # this if the implementing class always provides the #unbuffered_write
1130
+ # method but may not always be open in a writable mode.
1131
+ def writable?
1132
+ ! closed_write? && respond_to?(:unbuffered_write, true)
1133
+ end
1134
+
1135
+ # call-seq:
1136
+ # ios.write(string) -> integer
1137
+ #
1138
+ # Writes the given string to the stream and returns the number of bytes
1139
+ # written. If _string_ is not a String, its +to_s+ method is used to
1140
+ # convert it into one. The entire contents of _string_ are written,
1141
+ # blocking as necessary even if the data stream does not block.
1142
+ #
1143
+ # Raises IOError if #closed_write? returns +true+. Raises IOError unless
1144
+ # #writable? returns +true+.
1145
+ #
1146
+ # NOTE: This method ignores Errno::EAGAIN and Errno::EINTR raised by
1147
+ # #unbuffered_write. Therefore, this method always blocks if unable to
1148
+ # immediately write _string_ completely. Aside from that exception, this
1149
+ # method will also raise the same errors and block at the same times as
1150
+ # #unbuffered_write.
1151
+ def write(string)
1152
+ raise IOError, 'closed stream' if closed_write?
1153
+
1154
+ string = string.to_s
1155
+ bytes_written = 0
1156
+ while bytes_written < string.length do
1157
+ begin
1158
+ bytes_written += buffered_write(string.to_s.slice(bytes_written..-1))
1159
+ rescue Errno::EAGAIN, Errno::EINTR
1160
+ retry if write_ready?
1161
+ end
1162
+ end
1163
+ bytes_written
1164
+ end
1165
+
1166
+ private
1167
+
1168
+ # call-seq:
1169
+ # ios.buffered_flush -> 0
1170
+ #
1171
+ # Attempts to completely flush the internal write buffer to the data stream.
1172
+ #
1173
+ # Raises IOError unless #writable? returns +true+.
1174
+ #
1175
+ # NOTE: Because this method relies on #unbuffered_write, it raises all
1176
+ # errors raised by #unbuffered_write and blocks when #unbuffered_write
1177
+ # blocks.
1178
+ def buffered_flush # :nodoc:
1179
+ raise IOError, 'not opened for writing' unless writable?
1180
+
1181
+ until internal_write_buffer.empty? do
1182
+ internal_write_buffer.slice!(0, unbuffered_write(internal_write_buffer))
1183
+ end
1184
+ 0
1185
+ end
1186
+
1187
+ # call-seq:
1188
+ # ios.buffered_read(length) -> string
1189
+ #
1190
+ # Reads at most _length_ bytes first from an internal read buffer followed
1191
+ # by the underlying stream if necessary and returns the resulting buffer.
1192
+ #
1193
+ # Raises EOFError if the internal read buffer is empty and reading begins at
1194
+ # the end of the stream. Raises IOError unless #readable? returns +true+.
1195
+ #
1196
+ # NOTE: Because this method relies on #unbuffered_read, it raises all errors
1197
+ # raised by #unbuffered_read and blocks when #unbuffered_read blocks
1198
+ # whenever the internal read buffer is unable to fulfill the request.
1199
+ def buffered_read(length) # :nodoc:
1200
+ # Check the validity of the method arguments.
1201
+ raise ArgumentError, "non-positive length #{length} given" if length < 0
1202
+
1203
+ raise IOError, 'not opened for reading' unless readable?
1204
+
1205
+ # Flush the internal write buffer for non-duplexed objects.
1206
+ buffered_flush unless internal_write_buffer.empty? || duplexed?
1207
+
1208
+ # Ensure that the internal read buffer has at least enough data to satisfy
1209
+ # the request.
1210
+ if internal_read_buffer.length < length then
1211
+ unbuffered_length = length - internal_read_buffer.length
1212
+ unbuffered_length = fill_size if unbuffered_length < fill_size
1213
+
1214
+ begin
1215
+ internal_read_buffer << unbuffered_read(unbuffered_length)
1216
+ rescue EOFError, SystemCallError
1217
+ # Reraise the error if there is no data to return.
1218
+ raise if internal_read_buffer.empty?
1219
+ end
1220
+ end
1221
+
1222
+ # Read from the internal read buffer.
1223
+ buffer = internal_read_buffer.slice!(0, length)
1224
+
1225
+ buffer
1226
+ end
1227
+
1228
+ # call-seq:
1229
+ # ios.buffered_seek(offset[, whence]) -> integer
1230
+ #
1231
+ # Sets the new position for read or write operations using _offset_ and
1232
+ # _whence_ to computer the position. Returns the new position.
1233
+ #
1234
+ # As a side effect, the internal read and write buffers are flushed.
1235
+ #
1236
+ # Raises Errno::ESPIPE unless #seekable? returns +true+.
1237
+ #
1238
+ # See #seek for the usage of _offset_ and _whence_.
1239
+ #
1240
+ # NOTE: Because this method relies on #unbuffered_seek and #unbuffered_write
1241
+ # (when the internal write buffer is not empty), it will raise the same
1242
+ # errors and block at the same times as those functions.
1243
+ def buffered_seek(offset, whence = IO::SEEK_SET) # :nodoc:
1244
+ raise Errno::ESPIPE, 'Illegal seek' unless seekable?
1245
+
1246
+ # Flush the internal buffers.
1247
+ internal_read_buffer.slice!(0..-1)
1248
+ buffered_flush unless internal_write_buffer.empty?
1249
+ # Move the data stream's position as requested.
1250
+ unbuffered_seek(offset, whence)
1251
+ end
1252
+
1253
+ # call-seq:
1254
+ # ios.buffered_tell
1255
+ #
1256
+ # Returns the current position in the stream.
1257
+ #
1258
+ # Raises Errno::ESPIPE unless #seekable? returns +true+.
1259
+ def buffered_tell # :nodoc:
1260
+ raise Errno::ESPIPE, 'Illegal seek' unless seekable?
1261
+
1262
+ unless internal_read_buffer.empty? then
1263
+ unbuffered_seek(0, IO::SEEK_CUR) - internal_read_buffer.length
1264
+ else
1265
+ unbuffered_seek(0, IO::SEEK_CUR) + internal_write_buffer.length
1266
+ end
1267
+ end
1268
+
1269
+ # call-seq:
1270
+ # ios.buffered_write(string) -> integer
1271
+ #
1272
+ # Writes _string_ to the internal write buffer and returns the number of
1273
+ # bytes written. If the internal write buffer is overfilled by _string_, it
1274
+ # is repeatedly flushed until that last of _string_ is consumed. A partial
1275
+ # write will occur if part of _string_ fills the internal write buffer but
1276
+ # the internal write buffer cannot be immediately flushed due to the
1277
+ # underlying stream not blocking when unable to accept more data.
1278
+ #
1279
+ # NOTE: Because this method relies on #unbuffered_write, it raises all
1280
+ # errors raised by #unbuffered_write and blocks when #unbuffered_write
1281
+ # blocks whenever the internal write buffer is unable to fulfill the
1282
+ # request.
1283
+ def buffered_write(string) # :nodoc:
1284
+ raise IOError, 'not opened for writing' unless writable?
1285
+
1286
+ # Flush the internal read buffer and set the unbuffered position to the
1287
+ # buffered position when dealing with non-duplexed objects.
1288
+ if ! (duplexed? || internal_read_buffer.empty?) then
1289
+ unbuffered_seek(-internal_read_buffer.length, IO::SEEK_CUR)
1290
+ internal_read_buffer.slice!(0..-1)
1291
+ end
1292
+
1293
+ bytes_written = 0
1294
+ if sync then
1295
+ # Flush the internal write buffer and then bypass it when in synchronous
1296
+ # mode.
1297
+ buffered_flush
1298
+ bytes_written = unbuffered_write(string)
1299
+ else
1300
+ if internal_write_buffer.length + string.length >= flush_size then
1301
+ # The tipping point for the write buffer would be surpassed by this
1302
+ # request, so flush everything.
1303
+ buffered_flush
1304
+ bytes_written = unbuffered_write(string)
1305
+ else
1306
+ # The buffer can absorb the entire request.
1307
+ internal_write_buffer << string
1308
+ bytes_written = string.length
1309
+ end
1310
+ end
1311
+ rescue SystemCallError
1312
+ raise if bytes_written == 0
1313
+ else
1314
+ return bytes_written
1315
+ end
1316
+
1317
+ # Returns a reference to the internal read buffer.
1318
+ def internal_read_buffer # :nodoc:
1319
+ @__io_like__read_buffer ||= ''
1320
+ end
1321
+
1322
+ # Returns a reference to the internal write buffer.
1323
+ def internal_write_buffer # :nodoc:
1324
+ @__io_like__write_buffer ||= ''
1325
+ end
1326
+ end
1327
+ end
1328
+
1329
+ # vim: ts=2 sw=2 et