archive-zip 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTORS +13 -0
- data/GPL +676 -0
- data/HACKING +122 -0
- data/LEGAL +8 -0
- data/LICENSE +57 -0
- data/MANIFEST +26 -0
- data/NEWS +22 -0
- data/README +130 -0
- data/lib/archive/support/io-like.rb +12 -0
- data/lib/archive/support/io.rb +14 -0
- data/lib/archive/support/iowindow.rb +123 -0
- data/lib/archive/support/stringio.rb +22 -0
- data/lib/archive/support/time.rb +85 -0
- data/lib/archive/support/zlib.rb +211 -0
- data/lib/archive/zip.rb +643 -0
- data/lib/archive/zip/codec.rb +30 -0
- data/lib/archive/zip/codec/deflate.rb +206 -0
- data/lib/archive/zip/codec/store.rb +241 -0
- data/lib/archive/zip/datadescriptor.rb +54 -0
- data/lib/archive/zip/entry.rb +991 -0
- data/lib/archive/zip/error.rb +22 -0
- data/lib/archive/zip/extrafield.rb +23 -0
- data/lib/archive/zip/extrafield/extendedtimestamp.rb +101 -0
- data/lib/archive/zip/extrafield/raw.rb +32 -0
- data/lib/archive/zip/extrafield/unix.rb +101 -0
- data/test/test_archive.rb +8 -0
- metadata +98 -0
data/HACKING
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
= Guide to Hacking Archive::Zip
|
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 or greater
|
21
|
+
* allison 2.0.3 (optional - used for documentation only, if available)
|
22
|
+
* rsync (optional - used for publishing documentation)
|
23
|
+
|
24
|
+
|
25
|
+
=== Install
|
26
|
+
|
27
|
+
* rubygems 0.9.0 or greater
|
28
|
+
|
29
|
+
|
30
|
+
== Versioning Policy
|
31
|
+
|
32
|
+
Version numbers will be in <em>x.y.z</em> format, where <em>x</em>, <em>y</em>,
|
33
|
+
and <em>z</em> are integers starting from 0. The version increment rules are
|
34
|
+
as follows:
|
35
|
+
|
36
|
+
<b>x</b>:: Planned releases which implement significant changes and/or break API
|
37
|
+
compatibility. An exception is to be made for the transition from
|
38
|
+
the <em>0.y.z</em> series to the <em>1.y.z</em> series since the
|
39
|
+
<em>0.y.z</em> series is expected to be unstable throughout
|
40
|
+
development. When incremented, <em>y</em> and <em>z</em> are reset
|
41
|
+
to 0.
|
42
|
+
<b>y</b>:: Planned releases which incorporate numerous bug fixes and/or new
|
43
|
+
features which do not break backward compatibility. When
|
44
|
+
incremented, <em>z</em> is reset to 0.
|
45
|
+
<b>z</b>:: Generally, unplanned releases which incorporate a single fix for a
|
46
|
+
critical defect.
|
47
|
+
|
48
|
+
This is the {Rational Versioning Policy}[http://www.rubygems.org/read/chapter/7]
|
49
|
+
as outlined in the {RubyGems User Guide}[http://www.rubygems.org/read/book/1].
|
50
|
+
|
51
|
+
|
52
|
+
== Support Policy
|
53
|
+
|
54
|
+
Due to limitations in resources (time/money/manpower), this project will focus
|
55
|
+
primarily upon the development line of the current release at any given time.
|
56
|
+
Fixes and new features should be applied first to that development line and then
|
57
|
+
backported to earlier releases if necessary and feasible. Long term maintenance
|
58
|
+
of previous releases is not planned. Users are generally expected to upgrade to
|
59
|
+
the latest release in order to receive updates unless an explicit declaration of
|
60
|
+
support for a previous release is made.
|
61
|
+
|
62
|
+
|
63
|
+
== Coding Style
|
64
|
+
|
65
|
+
The following points are not necessarily set in stone but should rather be used
|
66
|
+
as a good guideline. Consistency is the goal of coding style, and changes will
|
67
|
+
be more easily accepted if they are consistent with the rest of the code.
|
68
|
+
|
69
|
+
<b>File Encoding</b>:: UTF-8
|
70
|
+
<b>Indentation</b>:: Two spaces; no tabs
|
71
|
+
<b>Comments</b>:: Document classes, attributes, methods, and code
|
72
|
+
<b>Boolean Operators</b>:: Use <tt>&&</tt> and <tt>||</tt> for boolean tests;
|
73
|
+
avoid <tt>and</tt> and <tt>or</tt>
|
74
|
+
<b>Method Calls</b>:: Use <tt>a_method(arg, arg, etc)</tt>; <b>not</b>
|
75
|
+
<tt>a_method( arg, arg, etc )</tt>,
|
76
|
+
<tt>a_method arg, arg, etc</tt>, or any other
|
77
|
+
variation
|
78
|
+
<b>Blocks</b>:: <tt>do end</tt> for multi-line blocks and
|
79
|
+
<tt>{ }</tt> for single-line blocks
|
80
|
+
<b>Line length</b>:: Limit lines to a maximum of 80 characters
|
81
|
+
<b>General</b>:: Try to follow the flow and style of the rest of the
|
82
|
+
code
|
83
|
+
|
84
|
+
|
85
|
+
== Generating Patches
|
86
|
+
|
87
|
+
Patches should usually be generated against the <em>HEAD</em> revision of the
|
88
|
+
<em>master</em> branch. When generating patches, please try to implement only
|
89
|
+
a single feature or bug fix per patch. Documentation describing a patch should
|
90
|
+
be included along with the patch so that the maintainer can more easily
|
91
|
+
determine whether or not a patch is acceptable. Patches lacking the necessary
|
92
|
+
documentation will be ignored.
|
93
|
+
|
94
|
+
Patches will be much more readily accepted if test cases are provided which
|
95
|
+
verify correct operation. Such test cases should be provided within the patch
|
96
|
+
rather than as a separate patch. Proper documentation, especially for
|
97
|
+
user-visible APIs, is highly prized; providing accurate and detailed
|
98
|
+
documentation, often in the form of rubydocs, throughout new code contributions
|
99
|
+
will also increase the desirability of a patch.
|
100
|
+
|
101
|
+
If a series of patches is generated which cannot be applied individually, make
|
102
|
+
sure to mention the dependency relationships in whatever medium is being used
|
103
|
+
to distribute the patches. For instance, if a bug is discovered while
|
104
|
+
implementing a new feature, create a patch which fixes the bug followed by a
|
105
|
+
separate patch adding the feature. If the feature patch requires the bug fix
|
106
|
+
patch in order to work, note that dependency in the comments for the feature
|
107
|
+
patch by somehow referencing the bug fix patch.
|
108
|
+
|
109
|
+
The patch generation process in general:
|
110
|
+
$ git clone git://rubyforge.org/archive-zip.git # Clone the repo and check out
|
111
|
+
# the master branch.
|
112
|
+
$ cd archive-zip # Enter the workspace.
|
113
|
+
(make and test changes)
|
114
|
+
$ git add file1 file2 .. # Add new/modified files.
|
115
|
+
$ git commit # Commit changes.
|
116
|
+
$ git format-patch -C HEAD^ # Create a patch for the last
|
117
|
+
# commit.
|
118
|
+
|
119
|
+
Repeat as necessary until all patches are generated. Then either attach them to
|
120
|
+
1 or more email messages addressed to the maintainer or attach them to tickets
|
121
|
+
in the issue tracker for the project. Remember to include a brief description
|
122
|
+
of the patch and its dependencies, if any.
|
data/LEGAL
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
LICENSE text follows:
|
2
|
+
|
3
|
+
Archive::Zip 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
@@ -0,0 +1,26 @@
|
|
1
|
+
CONTRIBUTORS
|
2
|
+
GPL
|
3
|
+
HACKING
|
4
|
+
LEGAL
|
5
|
+
LICENSE
|
6
|
+
MANIFEST
|
7
|
+
NEWS
|
8
|
+
README
|
9
|
+
lib/archive/support/io-like.rb
|
10
|
+
lib/archive/support/io.rb
|
11
|
+
lib/archive/support/iowindow.rb
|
12
|
+
lib/archive/support/stringio.rb
|
13
|
+
lib/archive/support/time.rb
|
14
|
+
lib/archive/support/zlib.rb
|
15
|
+
lib/archive/zip.rb
|
16
|
+
lib/archive/zip/codec.rb
|
17
|
+
lib/archive/zip/codec/deflate.rb
|
18
|
+
lib/archive/zip/codec/store.rb
|
19
|
+
lib/archive/zip/entry.rb
|
20
|
+
lib/archive/zip/datadescriptor.rb
|
21
|
+
lib/archive/zip/extrafield.rb
|
22
|
+
lib/archive/zip/extrafield/extendedtimestamp.rb
|
23
|
+
lib/archive/zip/extrafield/raw.rb
|
24
|
+
lib/archive/zip/extrafield/unix.rb
|
25
|
+
lib/archive/zip/error.rb
|
26
|
+
test/test_archive.rb
|
data/NEWS
ADDED
@@ -0,0 +1,22 @@
|
|
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/10)
|
10
|
+
|
11
|
+
* Initial release
|
12
|
+
* Archive creation and extraction is supported with only a few lines of code
|
13
|
+
(See README)
|
14
|
+
* Archives can be updated "in place" or dumped out to other files or pipes
|
15
|
+
* Files, symlinks, and directories are supported within archives
|
16
|
+
* Unix permission/mode bits are supported
|
17
|
+
* Unix user and group ownerships are supported.
|
18
|
+
* Unix last accessed and last modified times are supported.
|
19
|
+
* Entry extension (AKA extra field) implementations can be added on the fly.
|
20
|
+
* Unknown entry extension types are preserved during archive processing.
|
21
|
+
* Deflate and Store compression codecs supported out of the box
|
22
|
+
* More compression codecs can be added on the fly
|
data/README
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
= Archive::Zip - ZIP Archival Made Easy
|
2
|
+
|
3
|
+
The Archive::Zip library intends to provide a simple, yet complete and
|
4
|
+
Ruby-esque, interface to working with ZIP archives.
|
5
|
+
|
6
|
+
Basic archive creation and extraction can be handled using only a few methods.
|
7
|
+
More complex operations involving the manipulation of existing archives in place
|
8
|
+
(adding, removing, and modifying entries) are also possible with a little more
|
9
|
+
work. Even adding advanced features such as new compression codecs are
|
10
|
+
supported with a moderate amount of effort.
|
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 archive-zip-VERSION.gem
|
25
|
+
|
26
|
+
or directly with:
|
27
|
+
% sudo gem install archive-zip
|
28
|
+
|
29
|
+
Removal is the same in either case:
|
30
|
+
% sudo gem uninstall archive-zip
|
31
|
+
|
32
|
+
|
33
|
+
== Example
|
34
|
+
More examples can be found in the +examples+ directory of the source
|
35
|
+
distribution.
|
36
|
+
|
37
|
+
Create a few archives:
|
38
|
+
gem 'archive-zip' # Use require_gem for rubygems versions older than 0.9.0.
|
39
|
+
require 'archive/zip'
|
40
|
+
|
41
|
+
# Add a_directory and its contents to example1.zip.
|
42
|
+
Archive::Zip.archive('example1.zip', 'a_directory')
|
43
|
+
|
44
|
+
# Add the contents of a_directory to example2.zip.
|
45
|
+
Archive::Zip.archive('example2.zip', 'a_directory/.')
|
46
|
+
|
47
|
+
# Add a_file and a_directory and its contents to example3.zip.
|
48
|
+
Archive::Zip.archive('example3.zip', ['a_directory', 'a_file'])
|
49
|
+
|
50
|
+
# Add only the files and symlinks contained in a_directory under the path
|
51
|
+
# a/b/c/a_directory in example4.zip.
|
52
|
+
Archive::Zip.archive(
|
53
|
+
'example4.zip',
|
54
|
+
'a_directory',
|
55
|
+
:directories => false,
|
56
|
+
:path_prefix => 'a/b/c'
|
57
|
+
)
|
58
|
+
|
59
|
+
# Create a new archive which will be written to a pipe.
|
60
|
+
# Assume $stdout is the write end a pipe.
|
61
|
+
# (ruby example.rb | cat >example.zip)
|
62
|
+
Archive::Zip.open(nil, $stdout) do |z|
|
63
|
+
z.archive('a_directory')
|
64
|
+
end
|
65
|
+
|
66
|
+
Now extract those archives:
|
67
|
+
gem 'archive-zip' # Use require_gem for rubygems versions older than 0.9.0.
|
68
|
+
require 'archive/zip'
|
69
|
+
|
70
|
+
# Extract example1.zip to a_destination.
|
71
|
+
Archive::Zip.extract('example1.zip', 'a_destination')
|
72
|
+
|
73
|
+
# Extract example2.zip to a_destination, skipping directory entries.
|
74
|
+
Archive::Zip.extract(
|
75
|
+
'example2.zip',
|
76
|
+
'a_destination',
|
77
|
+
:directories => false
|
78
|
+
)
|
79
|
+
|
80
|
+
# Extract example3.zip to a_destination, skipping symlinks.
|
81
|
+
Archive::Zip.extract(
|
82
|
+
'example3.zip',
|
83
|
+
'a_destination',
|
84
|
+
:symlinks => false
|
85
|
+
)
|
86
|
+
|
87
|
+
# Extract example4.zip to a_destination, skipping entries for which files
|
88
|
+
# already exist but are newer or for which files do not exist at all.
|
89
|
+
Archive::Zip.extract(
|
90
|
+
'example4.zip',
|
91
|
+
'a_destination',
|
92
|
+
:create => false,
|
93
|
+
:overwrite => :older
|
94
|
+
)
|
95
|
+
|
96
|
+
|
97
|
+
== Features
|
98
|
+
|
99
|
+
1. 100% native Ruby. (Well, almost... depends on zlib.)
|
100
|
+
2. Archive creation and extraction is supported with only a few lines of code.
|
101
|
+
3. Archives can be updated "in place" or dumped out to other files or pipes.
|
102
|
+
4. Files, symlinks, and directories are supported within archives.
|
103
|
+
5. Unix permission/mode bits are supported.
|
104
|
+
6. Unix user and group ownerships are supported.
|
105
|
+
7. Unix last accessed and last modified times are supported.
|
106
|
+
8. Entry extension (AKA extra field) implementations can be added on the fly.
|
107
|
+
9. Unknown entry extension types are preserved during archive processing.
|
108
|
+
10. The Deflate and Store compression codecs are supported out of the box.
|
109
|
+
11. More compression codecs can be added on the fly.
|
110
|
+
|
111
|
+
|
112
|
+
== Known Bugs/Limitations
|
113
|
+
|
114
|
+
1. More testcases are needed.
|
115
|
+
2. All file entries are archived and extracted in binary mode. No attempt is
|
116
|
+
made to normalize text files to the line ending convention of any target
|
117
|
+
system.
|
118
|
+
3. Hard links and device files are not currently supported within archives.
|
119
|
+
4. Reading archives from non-seekable IO, such as pipes and sockets, is not
|
120
|
+
supported.
|
121
|
+
5. MSDOS permission attributes are not supported.
|
122
|
+
6. Encryption is not supported.
|
123
|
+
7. Zip64 is not supported.
|
124
|
+
8. Digital signatures are not supported.
|
125
|
+
|
126
|
+
|
127
|
+
== Contributing
|
128
|
+
|
129
|
+
Contributions for bug fixes, documentation, extensions, tests, etc. are
|
130
|
+
encouraged. Please read the file HACKING for details.
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# Try to first load io-like from rubygems and then fall back to assuming a
|
2
|
+
# standard installation.
|
3
|
+
begin
|
4
|
+
require 'rubygems'
|
5
|
+
gem 'io-like', '>= 0.1.0'
|
6
|
+
rescue LoadError
|
7
|
+
# Failed to load via rubygems.
|
8
|
+
end
|
9
|
+
|
10
|
+
# This will work for the gem and standard install assuming io-like is available
|
11
|
+
# at all.
|
12
|
+
require 'io/like'
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'archive/support/io-like'
|
2
|
+
|
3
|
+
# IOWindow represents an IO object which wraps another one allowing read access
|
4
|
+
# to a subset of the data within the stream.
|
5
|
+
#
|
6
|
+
# <b>NOTE:</b> This object is NOT thread safe.
|
7
|
+
class IOWindow
|
8
|
+
include IO::Like
|
9
|
+
|
10
|
+
# Creates a new instance of this class using _io_ as the data source
|
11
|
+
# and where _window_position_ and _window_size_ define the location and size
|
12
|
+
# of data window respectively.
|
13
|
+
#
|
14
|
+
# _io_ must be opened for reading and must be seekable. _window_position_
|
15
|
+
# must be an integer greater than or equal to 0. _window_size_ must be an
|
16
|
+
# integer greater than or equal to 0.
|
17
|
+
def initialize(io, window_position, window_size)
|
18
|
+
raise ArgumentError, 'non-seekable IO object given' unless io.seekable?
|
19
|
+
|
20
|
+
@io = io
|
21
|
+
@unbuffered_pos = 0
|
22
|
+
self.window_position = window_position
|
23
|
+
self.window_size = window_size
|
24
|
+
end
|
25
|
+
|
26
|
+
# The file position at which this window begins.
|
27
|
+
attr_reader :window_position
|
28
|
+
|
29
|
+
# Set the file position at which this window begins.
|
30
|
+
# _window_position_ must be an integer greater than or equal to 0.
|
31
|
+
def window_position=(window_position)
|
32
|
+
unless window_position.kind_of?(Fixnum) then
|
33
|
+
raise ArgumentError, 'non-integer window position given'
|
34
|
+
end
|
35
|
+
if window_position < 0 then
|
36
|
+
raise ArgumentError, 'non-positive window position given'
|
37
|
+
end
|
38
|
+
|
39
|
+
@window_position = window_position
|
40
|
+
end
|
41
|
+
|
42
|
+
# The size of the window.
|
43
|
+
attr_reader :window_size
|
44
|
+
|
45
|
+
# Set the size of the window.
|
46
|
+
# _window_size_ must be an integer greater than or equal to 0.
|
47
|
+
def window_size=(window_size)
|
48
|
+
unless window_size.kind_of?(Fixnum) then
|
49
|
+
raise ArgumentError, 'non-integer window size given'
|
50
|
+
end
|
51
|
+
raise ArgumentError, 'non-positive window size given' if window_size < 0
|
52
|
+
|
53
|
+
@window_size = window_size
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def unbuffered_read(length)
|
59
|
+
restore_self
|
60
|
+
|
61
|
+
# Error out if the end of the window is reached.
|
62
|
+
raise EOFError, 'end of file reached' if @unbuffered_pos >= @window_size
|
63
|
+
|
64
|
+
# Limit the read operation to the window.
|
65
|
+
length = @window_size - @unbuffered_pos if @unbuffered_pos + length > @window_size
|
66
|
+
|
67
|
+
# Fill a buffer with the data from the delegate.
|
68
|
+
buffer = @io.read(length)
|
69
|
+
# Error out if the end of the delegate is reached.
|
70
|
+
raise EOFError, 'end of file reached' if buffer.nil?
|
71
|
+
|
72
|
+
# Update the position.
|
73
|
+
@unbuffered_pos += buffer.length
|
74
|
+
|
75
|
+
buffer
|
76
|
+
ensure
|
77
|
+
restore_delegate
|
78
|
+
end
|
79
|
+
|
80
|
+
def unbuffered_seek(offset, whence = IO::SEEK_SET)
|
81
|
+
# Convert the offset and whence into an absolute position.
|
82
|
+
case whence
|
83
|
+
when IO::SEEK_SET
|
84
|
+
new_pos = offset
|
85
|
+
when IO::SEEK_CUR
|
86
|
+
new_pos = @unbuffered_pos + offset
|
87
|
+
when IO::SEEK_END
|
88
|
+
new_pos = @window_size + offset
|
89
|
+
end
|
90
|
+
|
91
|
+
# Error out if the position is outside the window.
|
92
|
+
raise Errno::EINVAL, 'Invalid argument' if new_pos < 0 or new_pos > @window_size
|
93
|
+
|
94
|
+
# Set the new position.
|
95
|
+
@unbuffered_pos = new_pos
|
96
|
+
end
|
97
|
+
|
98
|
+
def unbuffered_write(string)
|
99
|
+
restore_self
|
100
|
+
|
101
|
+
# Ensure that the outputted string will not extend past the window.
|
102
|
+
string = string.slice(0, @window_size - @unbuffered_pos)
|
103
|
+
@io.write(string)
|
104
|
+
ensure
|
105
|
+
restore_delegate
|
106
|
+
end
|
107
|
+
|
108
|
+
# Restores the state of the delegate IO object to that saved by a prior call
|
109
|
+
# to #restore_self.
|
110
|
+
def restore_delegate
|
111
|
+
@io.pos = @delegate_pos
|
112
|
+
@io.lineno = @delegate_lineno
|
113
|
+
end
|
114
|
+
|
115
|
+
# Saves the state of the delegate IO object so that it can be restored later
|
116
|
+
# using #restore_delegate and then configures the delegate so as to restore
|
117
|
+
# the state of this object.
|
118
|
+
def restore_self
|
119
|
+
@delegate_pos = @io.pos
|
120
|
+
@delegate_lineno = @io.lineno
|
121
|
+
@io.pos = @window_position + @unbuffered_pos
|
122
|
+
end
|
123
|
+
end
|