poise-archive 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ec56142b2e9ccaaea7f4505c6d051b298240a994
4
- data.tar.gz: 8dd841a5801e1e28fa8f25bfa9b4e3ef6ab6ace1
3
+ metadata.gz: ed1a6f15b6ab1ebc7f37e710fb97e3eb57875c24
4
+ data.tar.gz: 72373e0a9bc17188f4d551c5c682b271e4d3f0c3
5
5
  SHA512:
6
- metadata.gz: 3eb00c33c0dca67c88e1842620e75d47b3b154a32669c84dcc520743e453adfd7533a09430f4a02ad3b3c1f85b2c746d5b360d26e78421229f854f54b87929e3
7
- data.tar.gz: 237a0c5da1ada7dd038a1e65912008ac79ee9891f51340efdbdff9799eafbdca312ec1bd3a6769048666c81ffd68414e1a412bb431b78ceb158e42c90366c370
6
+ metadata.gz: b40e2e071adb5e3bf1141ce6a28b06ac6ac18949c2f59eb7c3a0435dd55b8447eb98e6a3655d013be2725bda178cde72c0ab59144788e409a3ddb9ee982275b9
7
+ data.tar.gz: 896dd2cc1d7344aebd8b10709522168ad57f5d385a4e8268772b4641a0f6293765f26e175cc5aa5de7332664fc89674f47b899e851a76d1f1beccdae798917db
@@ -1,5 +1,10 @@
1
1
  # Poise-Archive Changelog
2
2
 
3
+ ## v1.1.0
4
+
5
+ * Scrap the original tar implementation in favor of a 100% pure-Ruby solution.
6
+ This should work on all platforms exactly the same. Hopefully.
7
+
3
8
  ## v1.0.0
4
9
 
5
10
  * Initial release!
data/Gemfile CHANGED
@@ -32,3 +32,5 @@ dev_gem 'poise'
32
32
  dev_gem 'poise-boiler'
33
33
  dev_gem 'poise-profiler'
34
34
 
35
+ # To make life easier for unit testing.
36
+ gem 'rubyzip'
data/README.md CHANGED
@@ -77,3 +77,6 @@ distributed under the License is distributed on an "AS IS" BASIS,
77
77
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
78
78
  See the License for the specific language governing permissions and
79
79
  limitations under the License.
80
+
81
+ BZip2 implementation is based on RBzip2. Copyright Sebastian Staudt, Brian Lopez.
82
+ RBzip2 code used under the terms of the new BSD license.
@@ -15,105 +15,111 @@
15
15
  #
16
16
 
17
17
  require 'fileutils'
18
- require 'tmpdir'
18
+ require 'rubygems/package'
19
+ require 'zlib'
19
20
 
20
21
  require 'poise_archive/archive_providers/base'
22
+ require 'poise_archive/bzip2'
21
23
 
22
24
 
23
25
  module PoiseArchive
24
26
  module ArchiveProviders
25
- # The `tar` provider class for `poise_archive` to install from TAR archives.
27
+ # The `tar` provider class for `poise_archive` to install from tar archives.
26
28
  #
27
29
  # @see PoiseArchive::Resources::PoiseArchive::Resource
28
30
  # @provides poise_archive
29
31
  class Tar < Base
30
- provides_extension(/\.t(ar|gz|bz|xz)/)
32
+ provides_extension(/\.t(ar|gz|bz)/)
33
+
34
+ # Hack that GNU tar uses for paths over 100 bytes.
35
+ #
36
+ # @api private
37
+ # @see #unpack_tar
38
+ TAR_LONGLINK = '././@LongLink'
31
39
 
32
40
  private
33
41
 
34
42
  def unpack_archive
35
- notifying_block do
36
- install_prereqs
37
- end
38
43
  unpack_tar
39
44
  end
40
45
 
41
- # Install any needed prereqs.
46
+ # Unpack the archive.
42
47
  #
43
48
  # @return [void]
44
- def install_prereqs
45
- # Various platforms that either already come with tar or that we don't
46
- # want to try installing it on yet (read: Windows). This is mostly here
47
- # for minimalist Linux container images, most normal Linux servers have
48
- # all of these already.
49
- return if node.platform_family?('windows', 'mac_os_x', 'aix', 'solaris2')
50
- utils = ['tar']
51
- utils << 'bzip2' if new_resource.path =~ /\.t?bz/
52
- utils << 'xz-utils' if new_resource.path =~ /\.t?xz/
53
- package utils
49
+ def unpack_tar
50
+ tar_each do |entry|
51
+ entry_name = if entry.full_name == TAR_LONGLINK
52
+ entry.read.strip
53
+ else
54
+ entry.full_name
55
+ end.split(/\//).drop(new_resource.strip_components).join('/')
56
+ next if entry_name.empty?
57
+ dest = ::File.join(new_resource.destination, entry_name)
58
+ if entry.directory?
59
+ Dir.mkdir(dest, entry.header.mode)
60
+ elsif entry.file?
61
+ ::File.open(dest, 'wb', entry.header.mode) do |dest_f|
62
+ while buf = entry.read(4096)
63
+ dest_f.write(buf)
64
+ end
65
+ end
66
+ elsif entry.symlink?
67
+ ::File.symlink(entry.header.linkname, dest)
68
+ else
69
+ raise RuntimeError.new("Unknown tar entry type #{entry.header.typeflag.inspect} in #{new_resource.path}")
70
+ end
71
+ FileUtils.chown(new_resource.user, new_resource.group, dest)
72
+ end
54
73
  end
55
74
 
56
- # Unpack the archive and process `strip_components`.
75
+ # Sequence the opening, iteration, and closing.
57
76
  #
77
+ # @param block [Proc] Block to process each tar entry.
58
78
  # @return [void]
59
- def unpack_tar
60
- # Build the tar command. -J for xz isn't going to work on non-GNU tar,
61
- # cry me a river.
62
- cmd = %w{tar}
63
- cmd << if new_resource.path =~ /\.t?gz/
64
- '-xzvf'
65
- elsif new_resource.path =~ /\.t?bz/
66
- '-xjvf'
67
- elsif new_resource.path =~ /\.t?xz/
68
- '-xJvf'
69
- else
70
- '-xvf'
71
- end
72
- cmd << new_resource.path
73
-
74
- # Create a temp directory to unpack in to. Do I want to try and force
75
- # this to be on the same filesystem as the target?
76
- self.class.mktmpdir do |dir|
77
- # Change the temp dir to be owned by the unpack user if needed.
78
- FileUtils.chown(new_resource.user, new_resource.group, dir) if new_resource.user || new_resource.group
79
-
80
- # Run the unpack into the temp dir.
81
- poise_shell_out!(cmd, cwd: dir, group: new_resource.group, user: new_resource.user)
82
-
83
- # Re-implementation of the logic for tar --strip-components because
84
- # that option isn't part of non-GNU tar (read: Solaris and AIX).
85
- entries_at_depth(dir, new_resource.strip_components).each do |source|
86
- # At some point this might need to fall back to a real copy.
87
- ::File.rename(source, ::File.join(new_resource.absolute_destination, ::File.basename(source)))
88
- end
89
- end
79
+ def tar_each(&block)
80
+ # In case of extreme weirdness where this happens twice.
81
+ close_file!
82
+ open_file!
83
+ @tar_reader.each(&block)
84
+ ensure
85
+ close_file!
90
86
  end
91
87
 
92
- # Find the absolute paths for entries under a path at a depth.
88
+ # Open a file handle of the correct flavor.
93
89
  #
94
- # @param path [String] Base path to search under.
95
- # @param depth [Integer] Number of intermediary directories to skip.
96
- # @return [Array<String>]
97
- def entries_at_depth(path, depth)
98
- entries = [path]
99
- current_depth = 0
100
- while current_depth <= depth
101
- entries.map! do |ent|
102
- if ::File.directory?(ent)
103
- Dir.entries(ent).select {|e| e != '.' && e != '..' }.map {|e| ::File.join(ent, e) }
104
- else
105
- []
106
- end
107
- end
108
- entries.flatten!
109
- current_depth += 1
90
+ # @return [void]
91
+ def open_file!
92
+ @raw_file = ::File.open(new_resource.path, 'rb')
93
+ @file = case new_resource.path
94
+ when /\.tar$/
95
+ nil # So it uses @raw_file instead.
96
+ when /\.t?gz/
97
+ Zlib::GzipReader.wrap(@raw_file)
98
+ when /\.t?bz/
99
+ # This can't take a block, hence the gross non-block forms for everything.
100
+ PoiseArchive::Bzip2::Decompressor.new(@raw_file)
101
+ else
102
+ raise RuntimeError.new("Unknown or unsupported file extension for #{new_resource.path}")
110
103
  end
111
- entries
104
+ @tar_reader = Gem::Package::TarReader.new(@file || @raw_file)
112
105
  end
113
106
 
114
- # Indirection so I can stub this for testing without breaking RSpec.
115
- def self.mktmpdir(*args, &block)
116
- Dir.mktmpdir(*args, &block)
107
+ # Close all the various file handles.
108
+ #
109
+ # @return [void]
110
+ def close_file!
111
+ if @tar_reader
112
+ @tar_reader.close
113
+ @tar_reader = nil
114
+ end
115
+ if @file
116
+ @file.close
117
+ @file = nil
118
+ end
119
+ if @raw_file
120
+ @raw_file.close unless @raw_file.closed?
121
+ @raw_file = nil
122
+ end
117
123
  end
118
124
 
119
125
  end
@@ -0,0 +1,16 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2013, Sebastian Staudt
5
+
6
+
7
+ module PoiseArchive::Bzip2
8
+
9
+ autoload :CRC, 'poise_archive/bzip2/crc'
10
+ autoload :Constants, 'poise_archive/bzip2/constants'
11
+ autoload :Decompressor, 'poise_archive/bzip2/decompressor'
12
+ autoload :IO, 'poise_archive/bzip2/io'
13
+ autoload :InputData, 'poise_archive/bzip2/input_data'
14
+ autoload :OutputData, 'poise_archive/bzip2/output_data'
15
+
16
+ end
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2011, Sebastian Staudt
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of the author nor the names of its contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,83 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011-2013, Sebastian Staudt
5
+
6
+
7
+ module PoiseArchive::Bzip2::Constants
8
+
9
+ BASEBLOCKSIZE = 100000
10
+ MAX_ALPHA_SIZE = 258
11
+ MAX_CODE_LEN = 23
12
+ RUNA = 0
13
+ RUNB = 1
14
+ N_GROUPS = 6
15
+ G_SIZE = 50
16
+ N_ITERS = 4
17
+ MAX_SELECTORS = (2 + (900000 / G_SIZE))
18
+ NUM_OVERSHOOT_BYTES = 20
19
+
20
+ EOF = 0
21
+ START_BLOCK_STATE = 1
22
+ RAND_PART_A_STATE = 2
23
+ RAND_PART_B_STATE = 3
24
+ RAND_PART_C_STATE = 4
25
+ NO_RAND_PART_A_STATE = 5
26
+ NO_RAND_PART_B_STATE = 6
27
+ NO_RAND_PART_C_STATE = 7
28
+
29
+ RNUMS = [
30
+ 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, 985, 724, 205, 454, 863,
31
+ 491, 741, 242, 949, 214, 733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
32
+ 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, 878, 465, 811, 169, 869,
33
+ 675, 611, 697, 867, 561, 862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
34
+ 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, 170, 607, 520, 932, 727,
35
+ 476, 693, 425, 174, 647, 73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
36
+ 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, 641, 801, 220, 162, 819,
37
+ 984, 589, 513, 495, 799, 161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
38
+ 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, 98, 553, 163, 354, 666,
39
+ 933, 424, 341, 533, 870, 227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
40
+ 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, 184, 943, 795, 384, 383,
41
+ 461, 404, 758, 839, 887, 715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
42
+ 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, 652, 934, 970, 447, 318,
43
+ 353, 859, 672, 112, 785, 645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
44
+ 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, 653, 282, 762, 623, 680,
45
+ 81, 927, 626, 789, 125, 411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
46
+ 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, 857, 956, 358, 619, 580,
47
+ 124, 737, 594, 701, 612, 669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
48
+ 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, 344, 805, 988, 739, 511,
49
+ 655, 814, 334, 249, 515, 897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
50
+ 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, 686, 754, 806, 760, 493,
51
+ 403, 415, 394, 687, 700, 946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
52
+ 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, 680, 879, 194, 572, 640,
53
+ 724, 926, 56, 204, 700, 707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
54
+ 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, 134, 108, 571, 364, 631,
55
+ 212, 174, 643, 304, 329, 343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
56
+ 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, 170, 514, 364, 692, 829,
57
+ 82, 855, 953, 676, 246, 369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
58
+ 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, 896, 831, 547, 261, 524,
59
+ 462, 293, 465, 502, 56, 661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
60
+ 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, 61, 688, 793, 644, 986,
61
+ 403, 106, 366, 905, 644, 372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
62
+ 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, 920, 176, 193, 713, 857,
63
+ 265, 203, 50, 668, 108, 645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
64
+ 936, 638
65
+ ]
66
+
67
+ MIN_BLOCK_SIZE = 1
68
+ MAX_BLOCK_SIZE = 9
69
+ SETMASK = (1 << 21)
70
+ CLEARMASK = (~SETMASK)
71
+ GREATER_ICOST = 15
72
+ LESSER_ICOST = 0
73
+ SMALL_THRESH = 20
74
+ DEPTH_THRESH = 10
75
+ WORK_FACTOR = 30
76
+ QSORT_STACK_SIZE = 1000
77
+
78
+ INCS = [
79
+ 1, 4, 13, 40, 121, 364, 1093, 3280, 9841, 29524, 88573, 265720, 797161,
80
+ 2391484
81
+ ]
82
+
83
+ end
@@ -0,0 +1,73 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011-2013, Sebastian Staudt
5
+
6
+
7
+ class PoiseArchive::Bzip2::CRC
8
+
9
+ CRC32_TABLE = [
10
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
11
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
12
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
13
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
14
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
15
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
16
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
17
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
18
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
19
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
20
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
21
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
22
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
23
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
24
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
25
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
26
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
27
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
28
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
29
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
30
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
31
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
32
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
33
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
34
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
35
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
36
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
37
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
38
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
39
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
40
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
41
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
42
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
43
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
44
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
45
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
46
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
47
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
48
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
49
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
50
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
51
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
52
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
53
+ ]
54
+
55
+ attr_accessor :global_crc
56
+
57
+ def initialize
58
+ initialize_crc
59
+ end
60
+
61
+ def initialize_crc
62
+ @global_crc = 0xffffffff
63
+ end
64
+
65
+ def final_crc
66
+ @global_crc ^ 0xffffffff
67
+ end
68
+
69
+ def update_crc(in_ch)
70
+ @global_crc = ((@global_crc << 8) & 0xffffffff) ^ CRC32_TABLE[(@global_crc >> 24) ^ in_ch]
71
+ end
72
+
73
+ end
@@ -0,0 +1,704 @@
1
+ # This code is free software; you can redistribute it and/or modify it under
2
+ # the terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2011-2013, Sebastian Staudt
5
+
6
+
7
+ class PoiseArchive::Bzip2::Decompressor
8
+
9
+ include PoiseArchive::Bzip2::Constants
10
+
11
+ def initialize(io)
12
+ @buff = 0
13
+ @bytes_read = 0
14
+ @computed_combined_crc = 0
15
+ @crc = PoiseArchive::Bzip2::CRC.new
16
+ @current_char = -1
17
+ @io = io
18
+ @live = 0
19
+ @stored_combined_crc = 0
20
+ @su_t_pos = 0
21
+ init
22
+ end
23
+
24
+ def count(read)
25
+ @bytes_read += read if read != -1
26
+ end
27
+
28
+ # ADDED METHODS
29
+ def pos
30
+ @bytes_read
31
+ end
32
+
33
+ def eof?
34
+ @current_state == EOF
35
+ end
36
+ # /ADDED METHODS
37
+
38
+ def read(length = nil)
39
+ raise 'stream closed' if @io.nil?
40
+
41
+ if length == 1
42
+ r = read0
43
+ count (r < 0 ? -1 : 1)
44
+ r
45
+ else
46
+ r = ''
47
+ if length == nil
48
+ while true do
49
+ b = read0
50
+ break if b < 0
51
+ r << b.chr
52
+ end
53
+ count r.size # ADDED LINE
54
+ elsif length > 0
55
+ length.times do
56
+ b = read0
57
+ break if b < 0
58
+ r << b.chr
59
+ end
60
+ count r.size
61
+ end
62
+ r
63
+ end
64
+ end
65
+
66
+ def read0
67
+ ret_char = @current_char
68
+
69
+ if @current_state == RAND_PART_B_STATE
70
+ setup_rand_part_b
71
+ elsif @current_state == NO_RAND_PART_B_STATE
72
+ setup_no_rand_part_b
73
+ elsif @current_state == RAND_PART_C_STATE
74
+ setup_rand_part_c
75
+ elsif @current_state == NO_RAND_PART_C_STATE
76
+ setup_no_rand_part_c
77
+ elsif @current_state == EOF
78
+ return -1
79
+ else
80
+ raise 'illegal state'
81
+ end
82
+
83
+ ret_char
84
+ end
85
+
86
+ def make_maps
87
+ in_use = @data.in_use
88
+ seq_to_unseq = @data.seq_to_unseq
89
+
90
+ n_in_use_shadow = 0
91
+
92
+ 256.times do |i|
93
+ if in_use[i]
94
+ seq_to_unseq[n_in_use_shadow] = i
95
+ n_in_use_shadow += 1
96
+ end
97
+ end
98
+
99
+ @n_in_use = n_in_use_shadow
100
+ end
101
+
102
+ def init
103
+ check_magic
104
+
105
+ block_size = @io.read(1).to_i
106
+ raise 'Illegal block size.' if block_size < 1 || block_size > 9
107
+ @block_size = block_size
108
+
109
+ init_block
110
+ setup_block
111
+ end
112
+
113
+ def check_magic
114
+ raise 'Magic number does not match "BZh".' unless @io.read(3) == 'BZh'
115
+ end
116
+
117
+ def init_block
118
+ magic = [ubyte, ubyte, ubyte, ubyte, ubyte, ubyte]
119
+
120
+ if magic == [0x17, 0x72, 0x45, 0x38, 0x50, 0x90]
121
+ complete
122
+ elsif magic != [0x31, 0x41, 0x59, 0x26, 0x53, 0x59]
123
+ @current_state = EOF
124
+
125
+ raise 'Bad block header.'
126
+ else
127
+ @stored_block_crc = int
128
+ @block_randomised = bit
129
+
130
+ @data = PoiseArchive::Bzip2::InputData.new @block_size if @data.nil?
131
+
132
+ get_and_move_to_front_decode
133
+
134
+ @crc.initialize_crc
135
+ @current_state = START_BLOCK_STATE
136
+ end
137
+ end
138
+
139
+ def end_block
140
+ @computed_block_crc = @crc.final_crc
141
+
142
+ if @stored_block_crc != @computed_block_crc
143
+ @computed_combined_crc = (@stored_combined_crc << 1) | (@stored_combined_crc >> 31)
144
+ @computed_combined_crc ^= @stored_block_crc
145
+
146
+ raise 'BZip2 CRC error'
147
+ end
148
+
149
+ @computed_combined_crc = (@computed_combined_crc << 1) | (@computed_combined_crc >> 31)
150
+ @computed_combined_crc ^= @computed_block_crc
151
+ end
152
+
153
+ def complete
154
+ @stored_combined_crc = int
155
+ @current_state = EOF
156
+ @data = nil
157
+
158
+ raise 'BZip2 CRC error' if @stored_combined_crc != @computed_combined_crc
159
+ end
160
+
161
+ def close
162
+ if @io != $stdin
163
+ @io = nil
164
+ @data = nil
165
+ end
166
+ end
167
+
168
+ def r(n)
169
+ live_shadow = @live
170
+ buff_shadow = @buff
171
+
172
+ if live_shadow < n
173
+ begin
174
+ thech = @io.readbyte
175
+
176
+ raise 'unexpected end of stream' if thech < 0
177
+
178
+ buff_shadow = (buff_shadow << 8) | thech
179
+ live_shadow += 8
180
+ end while live_shadow < n
181
+
182
+ @buff = buff_shadow
183
+ end
184
+
185
+ @live = live_shadow - n
186
+
187
+ (buff_shadow >> (live_shadow - n)) & ((1 << n) - 1)
188
+ end
189
+
190
+ def bit
191
+ r(1) != 0
192
+ end
193
+
194
+ def ubyte
195
+ r 8
196
+ end
197
+
198
+ def int
199
+ (((((r(8) << 8) | r(8)) << 8) | r(8)) << 8) | r(8)
200
+ end
201
+
202
+ def create_decode_tables(limit, base, perm, length, min_len, max_len, alpha_size)
203
+ pp = 0
204
+ (min_len..max_len).each do |i|
205
+ alpha_size.times do |j|
206
+ if length[j] == i
207
+ perm[pp] = j
208
+ pp += 1
209
+ end
210
+ end
211
+ end
212
+
213
+ MAX_CODE_LEN.downto 1 do |i|
214
+ base[i] = 0
215
+ limit[i] = 0
216
+ end
217
+
218
+ alpha_size.times do |i|
219
+ base[length[i] + 1] += 1
220
+ end
221
+
222
+ b = 0
223
+ 1.upto(MAX_CODE_LEN - 1) do |i|
224
+ b += base[i]
225
+ base[i] = b
226
+ end
227
+
228
+ vec = 0
229
+ min_len.upto(max_len) do |i|
230
+ b = base[i]
231
+ nb = base[i + 1]
232
+ vec += nb - b
233
+ b = nb
234
+ limit[i] = vec - 1
235
+ vec = vec << 1
236
+ end
237
+
238
+ (min_len + 1).upto(max_len) do |i|
239
+ base[i] = ((limit[i - 1] + 1) << 1) - base[i]
240
+ end
241
+ end
242
+
243
+ def receive_decoding_tables
244
+ in_use = @data.in_use
245
+ pos = @data.receive_decoding_tables_pos
246
+ selector = @data.selector
247
+ selector_mtf = @data.selector_mtf
248
+
249
+ in_use16 = 0
250
+
251
+ 16.times do |i|
252
+ in_use16 |= 1 << i if bit
253
+ end
254
+
255
+ 255.downto(0) do |i|
256
+ in_use[i] = false
257
+ end
258
+
259
+ 16.times do |i|
260
+ if (in_use16 & (1 << i)) != 0
261
+ i16 = i << 4
262
+ 16.times do |j|
263
+ in_use[i16 + j] = true if bit
264
+ end
265
+ end
266
+ end
267
+
268
+ make_maps
269
+ alpha_size = @n_in_use + 2
270
+
271
+ groups = r 3
272
+ selectors = r 15
273
+
274
+ selectors.times do |i|
275
+ j = 0
276
+ while bit
277
+ j += 1
278
+ end
279
+ selector_mtf[i] = j
280
+ end
281
+
282
+ groups.downto(0) do |v|
283
+ pos[v] = v
284
+ end
285
+
286
+ selectors.times do |i|
287
+ v = selector_mtf[i] & 0xff
288
+ tmp = pos[v]
289
+
290
+ while v > 0 do
291
+ pos[v] = pos[v -= 1]
292
+ end
293
+
294
+ pos[0] = tmp
295
+ selector[i] = tmp
296
+ end
297
+
298
+ len = @data.temp_char_array_2d
299
+
300
+ groups.times do |t|
301
+ curr = r 5
302
+ len_t = len[t]
303
+ alpha_size.times do |i|
304
+ while bit
305
+ curr += bit ? -1 : 1
306
+ end
307
+ len_t[i] = curr
308
+ end
309
+ @data.temp_char_array_2d[t] = len_t
310
+ end
311
+
312
+ create_huffman_decoding_tables alpha_size, groups
313
+ end
314
+
315
+ def create_huffman_decoding_tables(alpha_size, groups)
316
+ len = @data.temp_char_array_2d
317
+ min_lens = @data.min_lens
318
+ limit = @data.limit
319
+ base = @data.base
320
+ perm = @data.perm
321
+
322
+ groups.times do |t|
323
+ min_len = 32
324
+ max_len = 0
325
+ len_t = len[t]
326
+
327
+ (alpha_size - 1).downto 0 do |i|
328
+ lent = len_t[i]
329
+ max_len = lent if lent > max_len
330
+ min_len = lent if lent < min_len
331
+ end
332
+
333
+ create_decode_tables limit[t], base[t], perm[t], len[t], min_len, max_len, alpha_size
334
+ min_lens[t] = min_len
335
+ end
336
+ end
337
+
338
+ def get_and_move_to_front_decode
339
+ @orig_ptr = r 24
340
+ receive_decoding_tables
341
+
342
+ ll8 = @data.ll8
343
+ unzftab = @data.unzftab
344
+ selector = @data.selector
345
+ seq_to_unseq = @data.seq_to_unseq
346
+ yy = @data.get_and_move_to_front_decode_yy
347
+ min_lens = @data.min_lens
348
+ limit = @data.limit
349
+ base = @data.base
350
+ perm = @data.perm
351
+ limit_last = @block_size * BASEBLOCKSIZE
352
+
353
+ 256.downto(0) do |i|
354
+ yy[i] = i
355
+ unzftab[i] = 0
356
+ end
357
+
358
+ group_no = 0
359
+ group_pos = G_SIZE - 1
360
+ eob = @n_in_use + 1
361
+ next_sym = get_and_move_to_front_decode0 0
362
+ buff_shadow = @buff
363
+ live_shadow = @live
364
+ last_shadow = -1
365
+ zt = selector[group_no] & 0xff
366
+ base_zt = base[zt]
367
+ limit_zt = limit[zt]
368
+ perm_zt = perm[zt]
369
+ min_lens_zt = min_lens[zt]
370
+
371
+ while next_sym != eob
372
+ if (next_sym == RUNA) || (next_sym == RUNB)
373
+ s = -1
374
+
375
+ n = 1
376
+ while true do
377
+ if next_sym == RUNA
378
+ s += n
379
+ elsif next_sym == RUNB
380
+ s += n << 1
381
+ else
382
+ break
383
+ end
384
+
385
+ if group_pos == 0
386
+ group_pos = G_SIZE - 1
387
+ group_no += 1
388
+ zt = selector[group_no] & 0xff
389
+ base_zt = base[zt]
390
+ limit_zt = limit[zt]
391
+ perm_zt = perm[zt]
392
+ min_lens_zt = min_lens[zt]
393
+ else
394
+ group_pos -= 1
395
+ end
396
+
397
+ zn = min_lens_zt
398
+
399
+ while live_shadow < zn
400
+ thech = @io.readbyte
401
+
402
+ raise 'unexpected end of stream' if thech < 0
403
+
404
+ buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech
405
+ live_shadow += 8
406
+ end
407
+
408
+ zvec = ((buff_shadow >> (live_shadow - zn)) & 0xffffffff) & ((1 << zn) - 1)
409
+ live_shadow -= zn
410
+
411
+ while zvec > limit_zt[zn]
412
+ zn += 1
413
+
414
+ while live_shadow < 1
415
+ thech = @io.readbyte
416
+
417
+ raise 'unexpected end of stream' if thech < 0
418
+
419
+ buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech
420
+ live_shadow += 8
421
+ end
422
+
423
+ live_shadow -= 1
424
+ zvec = (zvec << 1) | ((buff_shadow >> live_shadow) & 1)
425
+ end
426
+
427
+ next_sym = perm_zt[zvec - base_zt[zn]]
428
+
429
+ n = n << 1
430
+ end
431
+
432
+ ch = seq_to_unseq[yy[0]]
433
+ unzftab[ch & 0xff] += s + 1
434
+
435
+ while s >= 0
436
+ last_shadow += 1
437
+ ll8[last_shadow] = ch
438
+ s -= 1
439
+ end
440
+
441
+ raise 'block overrun' if last_shadow >= limit_last
442
+ else
443
+ last_shadow += 1
444
+ raise 'block overrun' if last_shadow >= limit_last
445
+
446
+ tmp = yy[next_sym - 1]
447
+ unzftab[seq_to_unseq[tmp] & 0xff] += 1
448
+ ll8[last_shadow] = seq_to_unseq[tmp]
449
+
450
+ yy[1, next_sym - 1] = yy[0, next_sym - 1]
451
+ yy[0] = tmp
452
+
453
+ if group_pos == 0
454
+ group_pos = G_SIZE - 1
455
+ group_no += 1
456
+ zt = selector[group_no] & 0xff
457
+ base_zt = base[zt]
458
+ limit_zt = limit[zt]
459
+ perm_zt = perm[zt]
460
+ min_lens_zt = min_lens[zt]
461
+ else
462
+ group_pos -= 1
463
+ end
464
+
465
+ zn = min_lens_zt
466
+
467
+ while live_shadow < zn
468
+ thech = @io.readbyte
469
+
470
+ raise 'unexpected end of stream' if thech < 0
471
+
472
+ buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech
473
+ live_shadow += 8
474
+ end
475
+ zvec = (buff_shadow >> (live_shadow - zn)) & ((1 << zn) - 1)
476
+ live_shadow -= zn
477
+
478
+ while zvec > limit_zt[zn]
479
+ zn += 1
480
+ while live_shadow < 1
481
+ thech = @io.readbyte
482
+
483
+ raise 'unexpected end of stream' if thech < 0
484
+
485
+ buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech
486
+ live_shadow += 8
487
+ end
488
+ live_shadow -= 1
489
+ zvec = (zvec << 1) | ((buff_shadow >> live_shadow) & 1)
490
+ end
491
+
492
+ next_sym = perm_zt[zvec - base_zt[zn]]
493
+ end
494
+ end
495
+
496
+ @last = last_shadow
497
+ @live = live_shadow
498
+ @buff = buff_shadow
499
+ end
500
+
501
+ def get_and_move_to_front_decode0(group_no)
502
+ zt = @data.selector[group_no] & 0xff
503
+ limit_zt = @data.limit[zt]
504
+ zn = @data.min_lens[zt]
505
+ zvec = r zn
506
+ live_shadow = @live
507
+ buff_shadow = @buff
508
+
509
+ while zvec > limit_zt[zn]
510
+ zn += 1
511
+
512
+ while live_shadow < 1
513
+ thech = @io.readbyte
514
+
515
+ raise 'unexpected end of stream' if thech < 0
516
+
517
+ buff_shadow = ((buff_shadow << 8) & 0xffffffff) | thech
518
+ live_shadow += 8
519
+ end
520
+
521
+ live_shadow -=1
522
+ zvec = (zvec << 1) | ((buff_shadow >> live_shadow) & 1)
523
+ end
524
+
525
+ @live = live_shadow
526
+ @buff = buff_shadow
527
+
528
+ @data.perm[zt][zvec - @data.base[zt][zn]]
529
+ end
530
+
531
+ def setup_block
532
+ return if @data.nil?
533
+
534
+ cftab = @data.cftab
535
+ tt = @data.init_tt @last + 1
536
+ ll8 = @data.ll8
537
+ cftab[0] = 0
538
+ cftab[1, 256] = @data.unzftab[0, 256]
539
+
540
+ c = cftab[0]
541
+ 1.upto(256) do |i|
542
+ c += cftab[i]
543
+ cftab[i] = c
544
+ end
545
+
546
+ last_shadow = @last
547
+ (last_shadow + 1).times do |i|
548
+ cftab_i = ll8[i] & 0xff
549
+ tt[cftab[cftab_i]] = i
550
+ cftab[cftab_i] += 1
551
+ end
552
+
553
+ raise 'stream corrupted' if @orig_ptr < 0 || @orig_ptr >= tt.size
554
+
555
+ @su_t_pos = tt[@orig_ptr]
556
+ @su_count = 0
557
+ @su_i2 = 0
558
+ @su_ch2 = 256
559
+
560
+ if @block_randomised
561
+ @su_r_n_to_go = 0
562
+ @su_r_t_pos = 0
563
+
564
+ setup_rand_part_a
565
+ else
566
+ setup_no_rand_part_a
567
+ end
568
+ end
569
+
570
+ def setup_rand_part_a
571
+ if @su_i2 <= @last
572
+ @su_ch_prev = @su_ch2
573
+ su_ch2_shadow = @data.ll8[@su_t_pos] & 0xff
574
+ @su_t_pos = @data.tt[@su_t_pos]
575
+
576
+ if @su_r_n_to_go == 0
577
+ @su_r_n_to_go = RNUMS[@su_r_t_pos] - 1
578
+ @su_r_t_pos += 1
579
+ @su_r_t_pos = 0 if @su_r_t_pos == 512
580
+ else
581
+ @su_r_n_to_go -= 1
582
+ end
583
+
584
+ @su_ch2 = su_ch2_shadow ^= (@su_r_n_to_go == 1) ? 1 : 0
585
+ @su_i2 += 1
586
+ @current_char = su_ch2_shadow
587
+ @current_state = RAND_PART_B_STATE
588
+ @crc.update_crc su_ch2_shadow
589
+ else
590
+ end_block
591
+ init_block
592
+ setup_block
593
+ end
594
+ end
595
+
596
+ def setup_no_rand_part_a
597
+ if @su_i2 <= @last
598
+ @su_ch_prev = @su_ch2
599
+ su_ch2_shadow = @data.ll8[@su_t_pos] & 0xff
600
+ @su_ch2 = su_ch2_shadow
601
+ @su_t_pos = @data.tt[@su_t_pos]
602
+ @su_i2 += 1
603
+ @current_char = su_ch2_shadow
604
+ @current_state = NO_RAND_PART_B_STATE
605
+ @crc.update_crc su_ch2_shadow
606
+ else
607
+ @current_state = NO_RAND_PART_A_STATE
608
+ end_block
609
+ init_block
610
+ setup_block
611
+ end
612
+ end
613
+
614
+ def setup_rand_part_b
615
+ if @su_ch2 != @su_ch_prev
616
+ @current_state = RAND_PART_A_STATE
617
+ @su_count = 1
618
+ setup_rand_part_a
619
+ else
620
+ @su_count += 1
621
+ if @su_count >= 4
622
+ @su_z = @data.ll8[@su_t_pos] & 0xff
623
+ @su_t_pos = @data.tt[@su_t_pos]
624
+
625
+ if @su_r_n_to_go == 0
626
+ @su_r_n_to_go = RNUMS[@su_r_t_pos] - 1
627
+ @su_r_t_pos += 1
628
+ @su_r_t_pos = 0 if @su_r_t_pos == 512
629
+ else
630
+ @su_r_n_to_go -= 1
631
+ end
632
+
633
+ @su_j2 = 0
634
+ @current_state = RAND_PART_C_STATE
635
+ @su_z ^= 1 if @su_r_n_to_go == 1
636
+ setup_rand_part_c
637
+ else
638
+ @current_state = RAND_PART_A_STATE
639
+ setup_rand_part_a
640
+ end
641
+ end
642
+ end
643
+
644
+ def setup_rand_part_c
645
+ if @su_j2 < @su_z
646
+ @current_char = @su_ch2
647
+ @crc.update_crc @su_ch2
648
+ @su_j2 += 1
649
+ else
650
+ @current_state = RAND_PART_A_STATE
651
+ @su_i2 += 1
652
+ @su_count = 0
653
+ setup_rand_part_a
654
+ end
655
+ end
656
+
657
+ def setup_no_rand_part_b
658
+ if @su_ch2 != @su_ch_prev
659
+ @su_count = 1
660
+ setup_no_rand_part_a
661
+ else
662
+ @su_count += 1
663
+ if @su_count >= 4
664
+ @su_z = @data.ll8[@su_t_pos] & 0xff
665
+ @su_t_pos = @data.tt[@su_t_pos]
666
+ @su_j2 = 0
667
+ setup_no_rand_part_c
668
+ else
669
+ setup_no_rand_part_a
670
+ end
671
+ end
672
+ end
673
+
674
+ def setup_no_rand_part_c
675
+ if @su_j2 < @su_z
676
+ su_ch2_shadow = @su_ch2
677
+ @current_char = su_ch2_shadow
678
+ @crc.update_crc su_ch2_shadow
679
+ @su_j2 += 1
680
+ @current_state = NO_RAND_PART_C_STATE
681
+ else
682
+ @su_i2 += 1
683
+ @su_count = 0
684
+ setup_no_rand_part_a
685
+ end
686
+ end
687
+
688
+ def size
689
+ if @io.is_a? StringIO
690
+ @io.size
691
+ elsif @io.is_a? File
692
+ @io.stat.size
693
+ end
694
+ end
695
+
696
+ def uncompressed
697
+ @last + 1
698
+ end
699
+
700
+ def inspect
701
+ "#<#{self.class}: @io=#{@io.inspect} size=#{size} uncompressed=#{uncompressed}>"
702
+ end
703
+
704
+ end