rubyzip 0.9.1 → 2.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +354 -0
- data/Rakefile +15 -104
- data/TODO +0 -1
- data/lib/zip/central_directory.rb +212 -0
- data/lib/zip/compressor.rb +9 -0
- data/lib/zip/constants.rb +115 -0
- data/lib/zip/crypto/decrypted_io.rb +40 -0
- data/lib/zip/crypto/encryption.rb +11 -0
- data/lib/zip/crypto/null_encryption.rb +43 -0
- data/lib/zip/crypto/traditional_encryption.rb +99 -0
- data/lib/zip/decompressor.rb +31 -0
- data/lib/zip/deflater.rb +34 -0
- data/lib/zip/dos_time.rb +53 -0
- data/lib/zip/entry.rb +719 -0
- data/lib/zip/entry_set.rb +88 -0
- data/lib/zip/errors.rb +19 -0
- data/lib/zip/extra_field/generic.rb +44 -0
- data/lib/zip/extra_field/ntfs.rb +94 -0
- data/lib/zip/extra_field/old_unix.rb +46 -0
- data/lib/zip/extra_field/universal_time.rb +77 -0
- data/lib/zip/extra_field/unix.rb +39 -0
- data/lib/zip/extra_field/zip64.rb +70 -0
- data/lib/zip/extra_field/zip64_placeholder.rb +15 -0
- data/lib/zip/extra_field.rb +103 -0
- data/lib/zip/file.rb +468 -0
- data/lib/zip/filesystem.rb +643 -0
- data/lib/zip/inflater.rb +54 -0
- data/lib/zip/input_stream.rb +180 -0
- data/lib/zip/ioextras/abstract_input_stream.rb +122 -0
- data/lib/zip/ioextras/abstract_output_stream.rb +43 -0
- data/lib/zip/ioextras.rb +21 -140
- data/lib/zip/null_compressor.rb +15 -0
- data/lib/zip/null_decompressor.rb +19 -0
- data/lib/zip/null_input_stream.rb +10 -0
- data/lib/zip/output_stream.rb +198 -0
- data/lib/zip/pass_thru_compressor.rb +23 -0
- data/lib/zip/pass_thru_decompressor.rb +31 -0
- data/lib/zip/streamable_directory.rb +15 -0
- data/lib/zip/streamable_stream.rb +52 -0
- data/lib/zip/version.rb +3 -0
- data/lib/zip.rb +72 -0
- data/samples/example.rb +44 -32
- data/samples/example_filesystem.rb +16 -19
- data/samples/example_recursive.rb +54 -0
- data/samples/gtk_ruby_zip.rb +84 -0
- data/samples/qtzip.rb +25 -34
- data/samples/write_simple.rb +10 -13
- data/samples/zipfind.rb +38 -45
- metadata +182 -91
- data/ChangeLog +0 -1504
- data/NEWS +0 -144
- data/README +0 -72
- data/install.rb +0 -22
- data/lib/download_quizzes.rb +0 -119
- data/lib/quiz1/t/solutions/Bill Guindon/solitaire.rb +0 -205
- data/lib/quiz1/t/solutions/Carlos/solitaire.rb +0 -111
- data/lib/quiz1/t/solutions/Dennis Ranke/solitaire.rb +0 -111
- data/lib/quiz1/t/solutions/Florian Gross/solitaire.rb +0 -301
- data/lib/quiz1/t/solutions/Glen M. Lewis/solitaire.rb +0 -268
- data/lib/quiz1/t/solutions/James Edward Gray II/solitaire.rb +0 -132
- data/lib/quiz1/t/solutions/Jamis Buck/bin/main.rb +0 -13
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cipher.rb +0 -230
- data/lib/quiz1/t/solutions/Jamis Buck/lib/cli.rb +0 -24
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_deck.rb +0 -30
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_key-stream.rb +0 -19
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_keying-algorithms.rb +0 -31
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_solitaire-cipher.rb +0 -66
- data/lib/quiz1/t/solutions/Jamis Buck/test/tc_unkeyed-algorithm.rb +0 -17
- data/lib/quiz1/t/solutions/Jamis Buck/test/tests.rb +0 -2
- data/lib/quiz1/t/solutions/Jim Menard/solitaire_cypher.rb +0 -204
- data/lib/quiz1/t/solutions/Jim Menard/test.rb +0 -47
- data/lib/quiz1/t/solutions/Moses Hohman/cipher.rb +0 -97
- data/lib/quiz1/t/solutions/Moses Hohman/deck.rb +0 -140
- data/lib/quiz1/t/solutions/Moses Hohman/solitaire.rb +0 -14
- data/lib/quiz1/t/solutions/Moses Hohman/test_cipher.rb +0 -68
- data/lib/quiz1/t/solutions/Moses Hohman/test_deck.rb +0 -146
- data/lib/quiz1/t/solutions/Moses Hohman/test_util.rb +0 -38
- data/lib/quiz1/t/solutions/Moses Hohman/testsuite.rb +0 -5
- data/lib/quiz1/t/solutions/Moses Hohman/util.rb +0 -27
- data/lib/quiz1/t/solutions/Niklas Frykholm/solitaire.rb +0 -151
- data/lib/quiz1/t/solutions/Thomas Leitner/solitaire.rb +0 -198
- data/lib/zip/stdrubyext.rb +0 -111
- data/lib/zip/tempfile_bugfixed.rb +0 -195
- data/lib/zip/zip.rb +0 -1847
- data/lib/zip/zipfilesystem.rb +0 -609
- data/lib/zip/ziprequire.rb +0 -90
- data/samples/gtkRubyzip.rb +0 -86
- data/test/alltests.rb +0 -9
- data/test/data/file1.txt +0 -46
- data/test/data/file1.txt.deflatedData +0 -0
- data/test/data/file2.txt +0 -1504
- data/test/data/notzippedruby.rb +0 -7
- data/test/data/rubycode.zip +0 -0
- data/test/data/rubycode2.zip +0 -0
- data/test/data/testDirectory.bin +0 -0
- data/test/data/zipWithDirs.zip +0 -0
- data/test/gentestfiles.rb +0 -157
- data/test/ioextrastest.rb +0 -208
- data/test/stdrubyexttest.rb +0 -52
- data/test/zipfilesystemtest.rb +0 -831
- data/test/ziprequiretest.rb +0 -43
- data/test/ziptest.rb +0 -1599
data/NEWS
DELETED
@@ -1,144 +0,0 @@
|
|
1
|
-
= Version 0.9.1
|
2
|
-
|
3
|
-
Added symlink support and support for unix file permissions. Reduced
|
4
|
-
memory usage during decompression.
|
5
|
-
|
6
|
-
New methods ZipFile::[follow_symlinks, restore_times, restore_permissions, restore_ownership].
|
7
|
-
New methods ZipEntry::unix_perms, ZipInputStream::eof?.
|
8
|
-
Added documentation and test for new ZipFile::extract.
|
9
|
-
Added some of the API suggestions from sf.net #1281314.
|
10
|
-
Applied patch for sf.net bug #1446926.
|
11
|
-
Applied patch for sf.net bug #1459902.
|
12
|
-
Rework ZipEntry and delegate classes.
|
13
|
-
|
14
|
-
= Version 0.5.12
|
15
|
-
|
16
|
-
Fixed problem with writing binary content to a ZipFile in MS Windows.
|
17
|
-
|
18
|
-
= Version 0.5.11
|
19
|
-
|
20
|
-
Fixed name clash file method copy_stream from fileutils.rb. Fixed
|
21
|
-
problem with references to constant CHUNK_SIZE.
|
22
|
-
ZipInputStream/AbstractInputStream read is now buffered like ruby IO's
|
23
|
-
read method, which means that read and gets etc can be mixed. The
|
24
|
-
unbuffered read method has been renamed to sysread.
|
25
|
-
|
26
|
-
= Version 0.5.10
|
27
|
-
|
28
|
-
Fixed method name resolution problem with FileUtils::copy_stream and
|
29
|
-
IOExtras::copy_stream.
|
30
|
-
|
31
|
-
= Version 0.5.9
|
32
|
-
|
33
|
-
Fixed serious memory consumption issue
|
34
|
-
|
35
|
-
= Version 0.5.8
|
36
|
-
|
37
|
-
Fixed install script.
|
38
|
-
|
39
|
-
= Version 0.5.7
|
40
|
-
|
41
|
-
install.rb no longer assumes it is being run from the toplevel source
|
42
|
-
dir. Directory structure changed to reflect common ruby library
|
43
|
-
project structure. Migrated from RubyUnit to Test::Unit format. Now
|
44
|
-
uses Rake to build source packages and gems and run unit tests.
|
45
|
-
|
46
|
-
= Version 0.5.6
|
47
|
-
|
48
|
-
Fix for FreeBSD 4.9 which returns Errno::EFBIG instead of
|
49
|
-
Errno::EINVAL for some invalid seeks. Fixed 'version needed to
|
50
|
-
extract'-field incorrect in local headers.
|
51
|
-
|
52
|
-
= Version 0.5.5
|
53
|
-
|
54
|
-
Fix for a problem with writing zip files that concerns only ruby 1.8.1.
|
55
|
-
|
56
|
-
= Version 0.5.4
|
57
|
-
|
58
|
-
Significantly reduced memory footprint when modifying zip files.
|
59
|
-
|
60
|
-
= Version 0.5.3
|
61
|
-
|
62
|
-
Added optimization to avoid decompressing and recompressing individual
|
63
|
-
entries when modifying a zip archive.
|
64
|
-
|
65
|
-
= Version 0.5.2
|
66
|
-
|
67
|
-
Fixed ZipFile corruption bug in ZipFile class. Added basic unix
|
68
|
-
extra-field support.
|
69
|
-
|
70
|
-
= Version 0.5.1
|
71
|
-
|
72
|
-
Fixed ZipFile.get_output_stream bug.
|
73
|
-
|
74
|
-
= Version 0.5.0
|
75
|
-
|
76
|
-
List of changes:
|
77
|
-
* Ruby 1.8.0 and ruby-zlib 0.6.0 compatibility
|
78
|
-
* Changed method names from camelCase to rubys underscore style.
|
79
|
-
* Installs to zip/ subdir instead of directly to site_ruby
|
80
|
-
* Added ZipFile.directory and ZipFile.file - each method return an
|
81
|
-
object that can be used like Dir and File only for the contents of the
|
82
|
-
zip file.
|
83
|
-
* Added sample application zipfind which works like Find.find, only
|
84
|
-
Zip::ZipFind.find traverses into zip archives too.
|
85
|
-
|
86
|
-
Bug fixes:
|
87
|
-
* AbstractInputStream.each_line with non-default separator
|
88
|
-
|
89
|
-
|
90
|
-
= Version 0.5.0a
|
91
|
-
|
92
|
-
Source reorganized. Added ziprequire, which can be used to load ruby
|
93
|
-
modules from a zip file, in a fashion similar to jar files in
|
94
|
-
Java. Added gtkRubyzip, another sample application. Implemented
|
95
|
-
ZipInputStream.lineno and ZipInputStream.rewind
|
96
|
-
|
97
|
-
Bug fixes:
|
98
|
-
|
99
|
-
* Read and write date and time information correctly for zip entries.
|
100
|
-
* Fixed read() using separate buffer, causing mix of gets/readline/read to
|
101
|
-
cause problems.
|
102
|
-
|
103
|
-
= Version 0.4.2
|
104
|
-
|
105
|
-
Performance optimizations. Test suite runs in half the time.
|
106
|
-
|
107
|
-
= Version 0.4.1
|
108
|
-
|
109
|
-
Windows compatibility fixes.
|
110
|
-
|
111
|
-
= Version 0.4.0
|
112
|
-
|
113
|
-
Zip::ZipFile is now mutable and provides a more convenient way of
|
114
|
-
modifying zip archives than Zip::ZipOutputStream. Operations for
|
115
|
-
adding, extracting, renaming, replacing and removing entries to zip
|
116
|
-
archives are now available.
|
117
|
-
|
118
|
-
Runs without warnings with -w switch.
|
119
|
-
|
120
|
-
Install script install.rb added.
|
121
|
-
|
122
|
-
|
123
|
-
= Version 0.3.1
|
124
|
-
|
125
|
-
Rudimentary support for writing zip archives.
|
126
|
-
|
127
|
-
|
128
|
-
= Version 0.2.2
|
129
|
-
|
130
|
-
Fixed and extended unit test suite. Updated to work with ruby/zlib
|
131
|
-
0.5. It doesn't work with earlier versions of ruby/zlib.
|
132
|
-
|
133
|
-
|
134
|
-
= Version 0.2.0
|
135
|
-
|
136
|
-
Class ZipFile added. Where ZipInputStream is used to read the
|
137
|
-
individual entries in a zip file, ZipFile reads the central directory
|
138
|
-
in the zip archive, so you can get to any entry in the zip archive
|
139
|
-
without having to skipping through all the preceeding entries.
|
140
|
-
|
141
|
-
|
142
|
-
= Version 0.1.0
|
143
|
-
|
144
|
-
First working version of ZipInputStream.
|
data/README
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
= rubyzip
|
2
|
-
|
3
|
-
rubyzip is a ruby library for reading and writing zip files.
|
4
|
-
|
5
|
-
= Install
|
6
|
-
|
7
|
-
If you have rubygems you can install rubyzip directly from the gem
|
8
|
-
repository
|
9
|
-
|
10
|
-
gem install rubyzip
|
11
|
-
|
12
|
-
Otherwise obtain the source (see below) and run
|
13
|
-
|
14
|
-
ruby install.rb
|
15
|
-
|
16
|
-
To run the unit tests you need to have test::unit installed
|
17
|
-
|
18
|
-
rake test
|
19
|
-
|
20
|
-
|
21
|
-
= Documentation
|
22
|
-
|
23
|
-
There is more than one way to access or create a zip archive with
|
24
|
-
rubyzip. The basic API is modeled after the classes in
|
25
|
-
java.util.zip from the Java SDK. This means there are classes such
|
26
|
-
as Zip::ZipInputStream, Zip::ZipOutputStream and
|
27
|
-
Zip::ZipFile. Zip::ZipInputStream provides a basic interface for
|
28
|
-
iterating through the entries in a zip archive and reading from the
|
29
|
-
entries in the same way as from a regular File or IO
|
30
|
-
object. ZipOutputStream is the corresponding basic output
|
31
|
-
facility. Zip::ZipFile provides a mean for accessing the archives
|
32
|
-
central directory and provides means for accessing any entry without
|
33
|
-
having to iterate through the archive. Unlike Java's
|
34
|
-
java.util.zip.ZipFile rubyzip's Zip::ZipFile is mutable, which means
|
35
|
-
it can be used to change zip files as well.
|
36
|
-
|
37
|
-
Another way to access a zip archive with rubyzip is to use rubyzip's
|
38
|
-
Zip::ZipFileSystem API. Using this API files can be read from and
|
39
|
-
written to the archive in much the same manner as ruby's builtin
|
40
|
-
classes allows files to be read from and written to the file system.
|
41
|
-
|
42
|
-
rubyzip also features the
|
43
|
-
zip/ziprequire.rb[link:files/lib/zip/ziprequire_rb.html] module which
|
44
|
-
allows ruby to load ruby modules from zip archives.
|
45
|
-
|
46
|
-
For details about the specific behaviour of classes and methods refer
|
47
|
-
to the test suite. Finally you can generate the rdoc documentation or
|
48
|
-
visit http://rubyzip.sourceforge.net.
|
49
|
-
|
50
|
-
= License
|
51
|
-
|
52
|
-
rubyzip is distributed under the same license as ruby. See
|
53
|
-
http://www.ruby-lang.org/en/LICENSE.txt
|
54
|
-
|
55
|
-
|
56
|
-
= Website and Project Home
|
57
|
-
|
58
|
-
http://rubyzip.sourceforge.net
|
59
|
-
|
60
|
-
http://sourceforge.net/projects/rubyzip
|
61
|
-
|
62
|
-
== Download (tarballs and gems)
|
63
|
-
|
64
|
-
http://sourceforge.net/project/showfiles.php?group_id=43107&package_id=35377
|
65
|
-
|
66
|
-
= Authors
|
67
|
-
|
68
|
-
Thomas Sondergaard (thomas at sondergaard.cc)
|
69
|
-
|
70
|
-
Technorama Ltd. (oss-ruby-zip at technorama.net)
|
71
|
-
|
72
|
-
extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org)
|
data/install.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$VERBOSE = true
|
4
|
-
|
5
|
-
require 'rbconfig'
|
6
|
-
require 'find'
|
7
|
-
require 'ftools'
|
8
|
-
|
9
|
-
include Config
|
10
|
-
|
11
|
-
files = %w{ stdrubyext.rb ioextras.rb zip.rb zipfilesystem.rb ziprequire.rb tempfile_bugfixed.rb }
|
12
|
-
|
13
|
-
INSTALL_DIR = File.join(CONFIG["sitelibdir"], "zip")
|
14
|
-
File.makedirs(INSTALL_DIR)
|
15
|
-
|
16
|
-
SOURCE_DIR = File.join(File.dirname($0), "lib/zip")
|
17
|
-
|
18
|
-
files.each {
|
19
|
-
|filename|
|
20
|
-
installPath = File.join(INSTALL_DIR, filename)
|
21
|
-
File::install(File.join(SOURCE_DIR, filename), installPath, 0644, true)
|
22
|
-
}
|
data/lib/download_quizzes.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
#!/bin/env ruby
|
2
|
-
|
3
|
-
rubygems = false
|
4
|
-
begin
|
5
|
-
require 'rubygems'
|
6
|
-
rubygems = true
|
7
|
-
rescue LoadError
|
8
|
-
end
|
9
|
-
|
10
|
-
require 'open-uri'
|
11
|
-
require 'fileutils'
|
12
|
-
|
13
|
-
rubyzip = false
|
14
|
-
begin
|
15
|
-
require 'zip/zipfilesystem'
|
16
|
-
rubyzip = true
|
17
|
-
rescue LoadError
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
@address = "www.rubyquiz.com"
|
22
|
-
first = last = nil
|
23
|
-
|
24
|
-
first = ARGV[0].to_i if ARGV.size > 1
|
25
|
-
last = ARGV[1].to_i if ARGV.size > 2
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
#
|
30
|
-
# Download a binary file from the rubyquiz url
|
31
|
-
#
|
32
|
-
def download(file, todir = '.')
|
33
|
-
begin
|
34
|
-
puts "Downloading file #{file} from #{@address}"
|
35
|
-
c = open("http://#{@address}/#{file}").read
|
36
|
-
Dir.mkdir(todir) if not File.directory?(todir)
|
37
|
-
f = open("#{todir}/#{file}", 'wb')
|
38
|
-
f.puts c
|
39
|
-
f.close
|
40
|
-
rescue => e
|
41
|
-
if not File.exists?(fullfile)
|
42
|
-
$stderr.puts "Could not download file #{file} form #{@address}."
|
43
|
-
$stderr.puts e.to_s
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
#
|
50
|
-
# Unzip the file using GNU's stand-alone unzip utility.
|
51
|
-
#
|
52
|
-
def unzip_gnu(file)
|
53
|
-
puts `unzip -o #{file}`
|
54
|
-
end
|
55
|
-
|
56
|
-
|
57
|
-
#
|
58
|
-
# Unzip the file using rubyzip library.
|
59
|
-
#
|
60
|
-
def unzip(x)
|
61
|
-
outdir = x.sub(/.*\//, '')
|
62
|
-
outdir = '.' if outdir == ""
|
63
|
-
Zip::ZipFile::open(x) { |zf|
|
64
|
-
zf.each { |e|
|
65
|
-
fpath = File.join(outdir, e.name)
|
66
|
-
FileUtils.mkdir_p(File.dirname(fpath))
|
67
|
-
zf.extract(e, fpath)
|
68
|
-
}
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
def get_index
|
75
|
-
c = open("http://#{@address}/").read
|
76
|
-
quizzes = c.scan /quiz(\d+).html/
|
77
|
-
return [ quizzes[0][0], quizzes[-1][0] ]
|
78
|
-
end
|
79
|
-
|
80
|
-
if not first or not last
|
81
|
-
f, l = get_index()
|
82
|
-
last = l unless last
|
83
|
-
first = f unless first
|
84
|
-
end
|
85
|
-
|
86
|
-
first = first.to_i
|
87
|
-
last = last.to_i
|
88
|
-
|
89
|
-
|
90
|
-
puts "Downloading quizzess #{first} to #{last}"
|
91
|
-
quizzes = (first..last)
|
92
|
-
quizzes.each { |q|
|
93
|
-
dir = "quiz#{q}"
|
94
|
-
|
95
|
-
#### Download HTML description
|
96
|
-
file = "quiz#{q}.html"
|
97
|
-
fullfile = "#{dir}/#{file}"
|
98
|
-
if not File.exists?(fullfile)
|
99
|
-
download( file, dir )
|
100
|
-
end
|
101
|
-
|
102
|
-
#### Download zip file
|
103
|
-
file = "quiz#{q}_sols.zip"
|
104
|
-
fullfile = "#{dir}/#{file}"
|
105
|
-
|
106
|
-
if not File.exists?(fullfile)
|
107
|
-
download( file, dir )
|
108
|
-
end
|
109
|
-
|
110
|
-
# Unzip and remove .zip file
|
111
|
-
Dir.chdir dir
|
112
|
-
if rubyzip
|
113
|
-
unzip file
|
114
|
-
else
|
115
|
-
unzip_gnu file
|
116
|
-
end
|
117
|
-
FileUtils.rm file
|
118
|
-
Dir.chdir '..'
|
119
|
-
}
|
@@ -1,205 +0,0 @@
|
|
1
|
-
# bail if you have nothing to do.
|
2
|
-
unless ARGV[0]
|
3
|
-
print <<-STOP_PRINT
|
4
|
-
Encrypts/decrypts a string of text.
|
5
|
-
Usage: Give it some text!
|
6
|
-
STOP_PRINT
|
7
|
-
exit
|
8
|
-
end
|
9
|
-
|
10
|
-
# the obligatory deck of cards.
|
11
|
-
###############################################################################
|
12
|
-
class Deck
|
13
|
-
attr_reader :cards, :order
|
14
|
-
|
15
|
-
def initialize
|
16
|
-
@cards = []
|
17
|
-
@order = []
|
18
|
-
build_deck
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_s
|
22
|
-
return @cards.to_s
|
23
|
-
end
|
24
|
-
|
25
|
-
def build_deck
|
26
|
-
[:Clubs, :Diamonds, :Hearts, :Spades].each do |suit|
|
27
|
-
rank = 0
|
28
|
-
'A23456789TJQK'.each_byte do |name|
|
29
|
-
# 'real' cards have a rank value
|
30
|
-
rank += 1
|
31
|
-
add_card(name.chr, suit, rank)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Jokers have no rank value
|
36
|
-
'AB'.each_byte {|name| add_card(name.chr, :Joker, 0)}
|
37
|
-
end
|
38
|
-
|
39
|
-
# build order while adding.
|
40
|
-
def add_card(name, suit, rank)
|
41
|
-
card = Card.new(name, suit, rank)
|
42
|
-
@cards << card
|
43
|
-
@order << card.to_s
|
44
|
-
end
|
45
|
-
|
46
|
-
# Uses order to hunt for cards (Joker searches).
|
47
|
-
def find_card(name, suit)
|
48
|
-
return @order.index(Card.to_s(name, suit))
|
49
|
-
end
|
50
|
-
|
51
|
-
# does as many cuts as you give it.
|
52
|
-
def cut_cards(cuts)
|
53
|
-
cards = []
|
54
|
-
loc = 0
|
55
|
-
[cuts].flatten.each_with_index do |cut, idx|
|
56
|
-
cards[idx] = @cards[loc...cut]
|
57
|
-
loc = cut
|
58
|
-
end
|
59
|
-
cards << @cards[loc...@cards.length]
|
60
|
-
end
|
61
|
-
|
62
|
-
def cards=(cards)
|
63
|
-
# flatten to handle cut results.
|
64
|
-
@cards = cards.flatten
|
65
|
-
# rebuild @order each time the deck changes.
|
66
|
-
update_order
|
67
|
-
end
|
68
|
-
|
69
|
-
# simple, but not very efficient.
|
70
|
-
def update_order
|
71
|
-
@order = @cards.collect {|card| card.to_s}
|
72
|
-
end
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
# the above deck is made up of...
|
77
|
-
###############################################################################
|
78
|
-
class Card
|
79
|
-
@@SUITS = {
|
80
|
-
:Clubs => 0,
|
81
|
-
:Diamonds => 13,
|
82
|
-
:Hearts => 26,
|
83
|
-
:Spades => 39,
|
84
|
-
:Joker => 53
|
85
|
-
}
|
86
|
-
|
87
|
-
def self.to_s(name, suit)
|
88
|
-
return name + ' ' + suit.to_s + "\n"
|
89
|
-
end
|
90
|
-
|
91
|
-
attr_reader :name, :suit, :rank
|
92
|
-
|
93
|
-
def initialize(name, suit, rank)
|
94
|
-
@name = name
|
95
|
-
@suit = suit
|
96
|
-
@rank = rank + @@SUITS[suit]
|
97
|
-
end
|
98
|
-
|
99
|
-
def to_s
|
100
|
-
Card.to_s(@name, @suit)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
###############################################################################
|
105
|
-
class Solitaire
|
106
|
-
|
107
|
-
attr_reader :deck
|
108
|
-
|
109
|
-
def initialize(text)
|
110
|
-
@deck = Deck.new
|
111
|
-
@text = text.to_s
|
112
|
-
end
|
113
|
-
|
114
|
-
def process
|
115
|
-
# does it look encrypted? 5 letter blocks all uppercase?
|
116
|
-
looks_encrypted = @text.gsub(/[A-Z]{5}\s?/, '').empty?
|
117
|
-
results = ''
|
118
|
-
|
119
|
-
# prep the text for parsing.
|
120
|
-
if looks_encrypted
|
121
|
-
# strip off the blanks for consistency
|
122
|
-
text = @text.gsub(/\s/, '')
|
123
|
-
else
|
124
|
-
# Discard any non A to Z characters, and uppercase all remaining
|
125
|
-
text = @text.upcase.gsub!(/[^A-Z]/, '')
|
126
|
-
# Split the message into five character groups,
|
127
|
-
words, padding = word_count(text, 5)
|
128
|
-
# using Xs to pad the last group
|
129
|
-
text += padding
|
130
|
-
end
|
131
|
-
|
132
|
-
# parse it, and build up results.
|
133
|
-
text.each_byte do |char|
|
134
|
-
if looks_encrypted
|
135
|
-
char -= next_key
|
136
|
-
char += 26 if char < 65
|
137
|
-
else
|
138
|
-
char += next_key
|
139
|
-
char -= 26 if char > 90
|
140
|
-
end
|
141
|
-
results += char.chr
|
142
|
-
end
|
143
|
-
|
144
|
-
return space_text(results, 5)
|
145
|
-
end
|
146
|
-
|
147
|
-
# counts words as 5 char blocks
|
148
|
-
def word_count(text, len)
|
149
|
-
words, strays = text.length.divmod(len)
|
150
|
-
words += 1 if strays > 0
|
151
|
-
pad = "X" * (len - strays)
|
152
|
-
return [words, pad]
|
153
|
-
end
|
154
|
-
|
155
|
-
def space_text(text, len)
|
156
|
-
# adds a space every 5 letters.
|
157
|
-
# not sure how efficient this is.
|
158
|
-
return text.unpack(('A' + len.to_s) * word_count(text, len)[0]).join(' ')
|
159
|
-
end
|
160
|
-
|
161
|
-
def shift_card(name, suit, count)
|
162
|
-
# find the card
|
163
|
-
idx = @deck.find_card(name, suit)
|
164
|
-
# remove it from the deck.
|
165
|
-
card = @deck.cards.slice!(idx)
|
166
|
-
# calculate new placement.
|
167
|
-
idx += count
|
168
|
-
# the slice above makes length 'look' zero-based
|
169
|
-
idx -= @deck.cards.length if idx > @deck.cards.length
|
170
|
-
|
171
|
-
# glue the deck together as cards before, card, cards after.
|
172
|
-
@deck.cards = @deck.cards[0...idx] + [card] +
|
173
|
-
@deck.cards[idx...@deck.cards.length]
|
174
|
-
end
|
175
|
-
|
176
|
-
def next_key
|
177
|
-
shift_card('A', :Joker, 1)
|
178
|
-
shift_card('B', :Joker, 2)
|
179
|
-
|
180
|
-
# find the 2 jokers, and sort them for the cut.
|
181
|
-
jokers = [@deck.find_card('A', :Joker), @deck.find_card('B', :Joker)].sort
|
182
|
-
# increment the 2nd joker pos -- cut uses 'up to, but not including'
|
183
|
-
jokers[1] += 1
|
184
|
-
# reverse works nicely for the triple cut.
|
185
|
-
@deck.cards = @deck.cut_cards(jokers).reverse
|
186
|
-
|
187
|
-
# get the value from the last card, and cut up to it.
|
188
|
-
cuts = @deck.cut_cards([@deck.cards.last.rank, @deck.cards.length - 1])
|
189
|
-
@deck.cards = cuts[1] + cuts[0] + cuts[2]
|
190
|
-
|
191
|
-
# read top card value, count down that many cards + 1
|
192
|
-
key = @deck.cards[@deck.cards[0].rank].rank
|
193
|
-
# convert it to a letter, adjust if needed.
|
194
|
-
key -= 26 if key > 26
|
195
|
-
|
196
|
-
# if key is still > 26, then it's a joker!
|
197
|
-
return (key) unless key > 26
|
198
|
-
# try again if it's a joker!
|
199
|
-
next_key
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
test = Solitaire.new(ARGV[0])
|
204
|
-
puts test.process
|
205
|
-
puts test.deck
|
@@ -1,111 +0,0 @@
|
|
1
|
-
class Numeric
|
2
|
-
def value
|
3
|
-
self
|
4
|
-
end
|
5
|
-
|
6
|
-
def to_letter
|
7
|
-
((self-1)%26 + ?A).chr
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
class String
|
12
|
-
# returns an array with the code of the letters,
|
13
|
-
# padded with the code of X
|
14
|
-
def to_numbers
|
15
|
-
res=upcase.unpack("C*").collect { |b|
|
16
|
-
if b.between? ?A, ?Z
|
17
|
-
b - ?A + 1
|
18
|
-
else
|
19
|
-
nil
|
20
|
-
end
|
21
|
-
}.compact
|
22
|
-
# 24 == X
|
23
|
-
res.fill 24, res.length, (5 - res.length % 5) % 5
|
24
|
-
end
|
25
|
-
|
26
|
-
def crypt (deck, decrypt=false)
|
27
|
-
numbers = to_numbers
|
28
|
-
keystream = deck.generate_keystream numbers.length
|
29
|
-
result = ""
|
30
|
-
numbers.zip(keystream) do |n, k|
|
31
|
-
k = -k if decrypt
|
32
|
-
result << (n+k).to_letter
|
33
|
-
end
|
34
|
-
result
|
35
|
-
end
|
36
|
-
|
37
|
-
def encrypt (deck)
|
38
|
-
crypt deck, false
|
39
|
-
end
|
40
|
-
|
41
|
-
def decrypt (deck)
|
42
|
-
crypt deck, true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class Joker
|
47
|
-
def value
|
48
|
-
53
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
A = Joker.new
|
53
|
-
B = Joker.new
|
54
|
-
|
55
|
-
class Array
|
56
|
-
def wrap_down pos
|
57
|
-
pos %= length
|
58
|
-
if pos == 0
|
59
|
-
pos = length
|
60
|
-
end
|
61
|
-
pos
|
62
|
-
end
|
63
|
-
|
64
|
-
def next_key
|
65
|
-
# step 2: move A joker down 1 card
|
66
|
-
pos = index A
|
67
|
-
slice! pos
|
68
|
-
pos = wrap_down(pos + 1)
|
69
|
-
self[pos, 0] = A
|
70
|
-
|
71
|
-
# step 3: move B joker down 2 cards
|
72
|
-
pos = index B
|
73
|
-
slice! pos
|
74
|
-
pos = wrap_down(pos + 2)
|
75
|
-
self[pos, 0] = B
|
76
|
-
|
77
|
-
# step 4: triple cut
|
78
|
-
first_joker, second_joker = [index(A), index(B)].sort
|
79
|
-
cards_above = slice! 0...first_joker
|
80
|
-
second_joker -= cards_above.length
|
81
|
-
cards_below = slice! second_joker+1..-1
|
82
|
-
push *cards_above
|
83
|
-
unshift *cards_below
|
84
|
-
|
85
|
-
# step 5: count cut using the value of the bottom card.
|
86
|
-
# reinsert above the last card
|
87
|
-
cut = slice! 0, last.value
|
88
|
-
self[-1,0] = cut
|
89
|
-
|
90
|
-
# step 6: find the letter
|
91
|
-
card = self[first.value]
|
92
|
-
|
93
|
-
return Joker===card ? nil : card.value
|
94
|
-
end
|
95
|
-
|
96
|
-
def generate_keystream len
|
97
|
-
(1..len).collect {|i| next_key or redo }
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def new_deck
|
102
|
-
(1..52).to_a + [A, B]
|
103
|
-
end
|
104
|
-
|
105
|
-
res = if ARGV[0] == "-d"
|
106
|
-
ARGV[1..-1].join("").decrypt(new_deck)
|
107
|
-
else
|
108
|
-
ARGV.join("").encrypt(new_deck)
|
109
|
-
end
|
110
|
-
|
111
|
-
puts res.scan(/.{5}/).join(" ")
|