archive-zip 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/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
@@ -0,0 +1,8 @@
1
+ = Other Legalities
2
+
3
+ == Files Licensed Differently
4
+
5
+ The following file(s) are provided under a license or licenses separate from
6
+ this project.
7
+
8
+ None at present
data/LICENSE ADDED
@@ -0,0 +1,57 @@
1
+ LICENSE text follows:
2
+
3
+ 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,14 @@
1
+ require 'readbytes'
2
+
3
+ class IO
4
+ # Returns +true+ if the seek method of this IO instance would succeed, +false+
5
+ # otherwise.
6
+ def seekable?
7
+ begin
8
+ pos
9
+ true
10
+ rescue SystemCallError
11
+ false
12
+ end
13
+ end
14
+ end
@@ -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