Module: MultiZip::Backend::Cli::InfoZip

Defined in:
lib/multi_zip/backend/cli/info_zip.rb

Defined Under Namespace

Modules: InstanceMethods Classes: ResponseError

Constant Summary collapse

BUFFER_SIZE =
8192
WHICH_PROGRAM =

TODO: better way to find full path to programs?

'which'
ZIP_AND_UNZIP_ARE_SAME_PROGRAM =
false
ZIP_PROGRAM =
'zip'
ZIP_PROGRAM_SIGNATURE =
/This is Zip [2-3].\d+\s.+, by Info-ZIP/
ZIP_PROGRAM_SIGNATURE_SWITCH =
'-v'
ZIP_PROGRAM_REMOVE_MEMBER_SWITCH =
'-d'
UNZIP_PROGRAM =
'unzip'
UNZIP_PROGRAM_SIGNATURE =
/UnZip [5-6]\.\d+ of .+, by Info-ZIP/
UNZIP_PROGRAM_SIGNATURE_SWITCH =
'-v'
UNZIP_PROGRAM_LIST_MEMBERS_SWITCHES =
['-Z', '-1']
UNZIP_PROGRAM_READ_MEMBER_SWITCH =
'-p'
UNZIP_PROGRAM_MEMBER_INFO_SWITCHES =
['-Z']
UNZIP_PROGRAM_EMPTY_ZIPFILE_MESSAGE =

TODO: does this change between versions? TODO: does this change with system language?

'Empty zipfile.'
UNZIP_PROGRAM_MEMBER_NOT_FOUND_MESSAGE =
'caution: filename not matched:'
UNZIP_PROGRAM_INVALID_FILE_MESSAGE =
'End-of-central-directory signature not found'

Class Method Summary collapse

Class Method Details

._spawn(argv, input, output, error) ⇒ Object



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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 109

def self._spawn(argv, input, output, error)
  # lifted from posix-spawn
  # https://github.com/rtomayko/posix-spawn/blob/master/lib/posix/spawn/child.rb
  Open3.popen3(*argv) do |stdin, stdout, stderr|
    readers = [stdout, stderr]
    if RUBY_DESCRIPTION =~ /jruby 1.7.0/
      readers.delete stderr
    end
    writers = if input
      [stdin]
    else
      stdin.close
      []
    end
    while readers.any? or writers.any?
      ready = IO.select(readers, writers, readers + writers)
      # write to stdin stream
      ready[1].each do |fd|
        begin
          boom = nil
          size = fd.write input.read(BUFFER_SIZE)
        rescue Errno::EPIPE => boom
        rescue Errno::EAGAIN, Errno::EINTR
        end
        if boom || size < BUFFER_SIZE
          stdin.close
          input.close
          writers.delete stdin
        end
      end
      # read from stdout and stderr streams
      ready[0].each do |fd|
        buf = (fd == stdout) ? output : error
        if fd.eof?
          readers.delete fd
          fd.close
        else
          begin
            # buf << fd.gets(BUFFER_SIZE) # maybe?
            buf << fd.readpartial(BUFFER_SIZE)
          rescue Errno::EAGAIN, Errno::EINTR
          end
        end
      end
    end
    # thanks @tmm1 and @rtomayko for showing how it's done!
  end
end

.available?Boolean

Returns:

  • (Boolean)


43
44
45
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 43

def self.available?
  @available ||= programs_found?
end

.extend_classObject



35
36
37
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 35

def self.extend_class
  lambda { MultiZip::Backend::Cli::InfoZip::InstanceMethods }
end

.human_nameObject



39
40
41
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 39

def self.human_name
  'Info-ZIP - zip(1L)/unzip(1L)'
end

.programs_found?Boolean

Returns:

  • (Boolean)


47
48
49
50
51
52
53
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 47

def self.programs_found?
  if ZIP_AND_UNZIP_ARE_SAME_PROGRAM
    zip_program_found?
  else
    zip_program_found? && unzip_program_found?
  end
end

.require_nameObject



31
32
33
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 31

def self.require_name
  'info_zip'
end

.spawn(argv, options = {}) ⇒ Object



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
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 72

def self.spawn(argv, options = {}) # :nodoc:
  input = if (read_from = options[:read_from])
    if RUBY_DESCRIPTION =~ /jruby 1.7.0/
      raise "MultiZip: Can't use `#{argv.first}` since JRuby 1.7.0 has a broken IO implementation!"
    end
    File.open(read_from, 'r')
  end
  output = if (write_to = options[:write_to])
    output_redirected = true
    File.open(write_to, 'wb')
  else
    output_redirected = false
    StringIO.new
  end
  error = StringIO.new
  if (chdir = options[:chdir])
    Dir.chdir(chdir) do
      _spawn argv, input, output, error
    end
  else
    _spawn argv, input, output, error
  end
  error.rewind
  whole_error = error.read
  unless whole_error.empty?
    $stderr.puts "MultiZip: `#{argv.join(' ')}` STDERR:"
    $stderr.puts whole_error
  end
  unless output_redirected
    output.rewind
    [output.read, whole_error].map{|o| o.empty? ? nil : o}
  end
ensure
  [input, output, error].each { |io| io.close if io and not io.closed? }
end

.unzip_program_found?Boolean

Returns:

  • (Boolean)


63
64
65
66
67
68
69
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 63

def self.unzip_program_found?
  unzip_program_path = `#{WHICH_PROGRAM} #{UNZIP_PROGRAM}`.strip
  return false unless unzip_program_path =~ /#{UNZIP_PROGRAM}/
  return false unless File.exists?(unzip_program_path)

  spawn([UNZIP_PROGRAM, UNZIP_PROGRAM_SIGNATURE_SWITCH]).first =~ UNZIP_PROGRAM_SIGNATURE
end

.zip_program_found?Boolean

Returns:

  • (Boolean)


55
56
57
58
59
60
61
# File 'lib/multi_zip/backend/cli/info_zip.rb', line 55

def self.zip_program_found?
  zip_program_path = `#{WHICH_PROGRAM} #{ZIP_PROGRAM}`.strip
  return false unless zip_program_path =~ /#{ZIP_PROGRAM}/
  return false unless File.exists?(zip_program_path)

  spawn([ZIP_PROGRAM, ZIP_PROGRAM_SIGNATURE_SWITCH]).first =~ ZIP_PROGRAM_SIGNATURE
end