io-like 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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