forked from autolab/Autolab
-
Notifications
You must be signed in to change notification settings - Fork 0
/
archive.rb
139 lines (117 loc) · 3.73 KB
/
archive.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
require "rubygems"
require "rubygems/package"
require "tempfile"
require "zlib"
require "zip"
##
# This module provides functionality for dealing with Archives, including zips,
# tars, and gunzipped tars
#
module Archive
def self.get_files(archive_path)
archive_type = get_archive_type(archive_path)
archive_extract = get_archive(archive_path, archive_type)
files = []
# Parse archive header
archive_extract.each_with_index do |entry, i|
# Obtain path name depending for tar/zip entry
pathname = get_entry_name(entry)
files << {
pathname: pathname,
header_position: i,
mac_bs_file: pathname.include?("__MACOSX") ||
pathname.include?(".DS_Store") ||
pathname.include?(".metadata"),
directory: looks_like_directory?(pathname)
}
end
archive_extract.close
files
end
def self.get_nth_file(archive_path, n)
archive_type = get_archive_type(archive_path)
archive_extract = get_archive(archive_path, archive_type)
# Parse archive header
res = nil, nil
archive_extract.each_with_index do |entry, i|
# Obtain path name depending for tar/zip entry
pathname = get_entry_name(entry)
next if pathname.include?("__MACOSX") ||
pathname.include?(".DS_Store") ||
pathname.include?(".metadata") ||
i != n
if looks_like_directory?(pathname)
res = nil, pathname
else
res = read_entry_file(entry), get_entry_name(entry)
end
break
end
archive_extract.close
res
end
def self.get_nth_filename(files, n)
files[n][:pathname]
end
def self.get_archive_type(filename)
IO.popen(["file", "--brief", "--mime-type", filename], in: :close, err: :close) do |io|
io.read.chomp
end
end
def self.archive?(filename)
return nil unless filename
archive_type = get_archive_type(filename)
(archive_type.include?("tar") || archive_type.include?("gzip") || archive_type.include?("zip"))
end
def self.get_archive(filename, archive_type = nil)
archive_type = get_archive_type(filename) if archive_type.nil?
if archive_type.include? "tar"
archive_extract = Gem::Package::TarReader.new(File.new(filename))
archive_extract.rewind # The extract has to be rewinded after every iteration
elsif archive_type.include? "gzip"
archive_extract = Gem::Package::TarReader.new(Zlib::GzipReader.open(filename))
archive_extract.rewind
elsif archive_type.include? "zip"
archive_extract = Zip::File.open(filename)
else
fail "Unrecognized archive type!"
end
archive_extract
end
def self.get_entry_name(entry)
# tar/tgz vs zip
name = entry.respond_to?(:full_name) ? entry.full_name : entry.name
if ! name.ascii_only?
name = String.new(name)
name.force_encoding("UTF-8")
if ! name.valid_encoding?
# not utf-8. Assume single byte and choose windows western, since
# iso8859-1 printables are a subset
name.force_encoding("Windows-1252")
name.encode!()
end
end
name
end
def self.read_entry_file(entry)
# tar/tgz vs zip
entry.respond_to?(:read) ? entry.read : entry.get_input_stream.read
end
##
# returns a zip archive containing every file in the given path array
#
def self.create_zip(paths)
return nil if paths.nil? || paths.empty?
Tempfile.open(["submissions", ".zip"]) do |t|
Zip::File.open(t.path, Zip::File::CREATE) do |z|
paths.each { |p| z.add(File.basename(p), p) }
z
end
t
end
# the return value should be the return value of the outer block, which is the tempfile
end
def self.looks_like_directory?(pathname)
pathname.ends_with?("/")
end
end