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.
- data/CONTRIBUTORS +13 -0
- data/GPL +676 -0
- data/HACKING +123 -0
- data/LEGAL +8 -0
- data/LICENSE +57 -0
- data/MANIFEST +10 -0
- data/NEWS +13 -0
- data/README +135 -0
- data/lib/io/like.rb +1329 -0
- data/test/lib/likestringio.rb +167 -0
- 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
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.
|
data/MANIFEST
ADDED
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.
|
data/lib/io/like.rb
ADDED
@@ -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
|