274 lines
6.6 KiB
Ruby
274 lines
6.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative 'file_stat'
|
|
|
|
module Zip
|
|
module FileSystem
|
|
# Instances of this class are normally accessed via the accessor
|
|
# Zip::File::file. An instance of File behaves like ruby's
|
|
# builtin File (class) object, except it works on Zip::File entries.
|
|
#
|
|
# The individual methods are not documented due to their
|
|
# similarity with the methods in File
|
|
class File # :nodoc:all
|
|
attr_writer :dir
|
|
|
|
def initialize(mapped_zip)
|
|
@mapped_zip = mapped_zip
|
|
end
|
|
|
|
def find_entry(filename)
|
|
unless exists?(filename)
|
|
raise Errno::ENOENT, "No such file or directory - #{filename}"
|
|
end
|
|
|
|
@mapped_zip.find_entry(filename)
|
|
end
|
|
|
|
def unix_mode_cmp(filename, mode)
|
|
e = find_entry(filename)
|
|
e.fstype == FSTYPE_UNIX && ((e.external_file_attributes >> 16) & mode) != 0
|
|
rescue Errno::ENOENT
|
|
false
|
|
end
|
|
private :unix_mode_cmp
|
|
|
|
def exists?(filename)
|
|
expand_path(filename) == '/' || !@mapped_zip.find_entry(filename).nil?
|
|
end
|
|
alias exist? exists?
|
|
|
|
# Permissions not implemented, so if the file exists it is accessible
|
|
alias owned? exists?
|
|
alias grpowned? exists?
|
|
|
|
def readable?(filename)
|
|
unix_mode_cmp(filename, 0o444)
|
|
end
|
|
alias readable_real? readable?
|
|
|
|
def writable?(filename)
|
|
unix_mode_cmp(filename, 0o222)
|
|
end
|
|
alias writable_real? writable?
|
|
|
|
def executable?(filename)
|
|
unix_mode_cmp(filename, 0o111)
|
|
end
|
|
alias executable_real? executable?
|
|
|
|
def setuid?(filename)
|
|
unix_mode_cmp(filename, 0o4000)
|
|
end
|
|
|
|
def setgid?(filename)
|
|
unix_mode_cmp(filename, 0o2000)
|
|
end
|
|
|
|
def sticky?(filename)
|
|
unix_mode_cmp(filename, 0o1000)
|
|
end
|
|
|
|
def umask(*args)
|
|
::File.umask(*args)
|
|
end
|
|
|
|
def truncate(_filename, _len)
|
|
raise StandardError, 'truncate not supported'
|
|
end
|
|
|
|
def directory?(filename)
|
|
entry = @mapped_zip.find_entry(filename)
|
|
expand_path(filename) == '/' || (!entry.nil? && entry.directory?)
|
|
end
|
|
|
|
def open(filename, mode = 'r', permissions = 0o644, &block)
|
|
mode = mode.tr('b', '') # ignore b option
|
|
case mode
|
|
when 'r'
|
|
@mapped_zip.get_input_stream(filename, &block)
|
|
when 'w'
|
|
@mapped_zip.get_output_stream(filename, permissions, &block)
|
|
else
|
|
raise StandardError, "openmode '#{mode} not supported" unless mode == 'r'
|
|
end
|
|
end
|
|
|
|
def new(filename, mode = 'r')
|
|
self.open(filename, mode)
|
|
end
|
|
|
|
def size(filename)
|
|
@mapped_zip.get_entry(filename).size
|
|
end
|
|
|
|
# Returns nil for not found and nil for directories
|
|
def size?(filename)
|
|
entry = @mapped_zip.find_entry(filename)
|
|
entry.nil? || entry.directory? ? nil : entry.size
|
|
end
|
|
|
|
def chown(owner, group, *filenames)
|
|
filenames.each do |filename|
|
|
e = find_entry(filename)
|
|
e.extra.create('IUnix') unless e.extra.member?('IUnix')
|
|
e.extra['IUnix'].uid = owner
|
|
e.extra['IUnix'].gid = group
|
|
end
|
|
filenames.size
|
|
end
|
|
|
|
def chmod(mode, *filenames)
|
|
filenames.each do |filename|
|
|
e = find_entry(filename)
|
|
e.fstype = FSTYPE_UNIX # Force conversion filesystem type to unix.
|
|
e.unix_perms = mode
|
|
e.external_file_attributes = mode << 16
|
|
e.dirty = true
|
|
end
|
|
filenames.size
|
|
end
|
|
|
|
def zero?(filename)
|
|
sz = size(filename)
|
|
sz.nil? || sz == 0
|
|
rescue Errno::ENOENT
|
|
false
|
|
end
|
|
|
|
def file?(filename)
|
|
entry = @mapped_zip.find_entry(filename)
|
|
!entry.nil? && entry.file?
|
|
end
|
|
|
|
def dirname(filename)
|
|
::File.dirname(filename)
|
|
end
|
|
|
|
def basename(filename)
|
|
::File.basename(filename)
|
|
end
|
|
|
|
def split(filename)
|
|
::File.split(filename)
|
|
end
|
|
|
|
def join(*fragments)
|
|
::File.join(*fragments)
|
|
end
|
|
|
|
def utime(modified_time, *filenames)
|
|
filenames.each do |filename|
|
|
find_entry(filename).time = modified_time
|
|
end
|
|
end
|
|
|
|
def mtime(filename)
|
|
@mapped_zip.get_entry(filename).mtime
|
|
end
|
|
|
|
def atime(filename)
|
|
e = find_entry(filename)
|
|
if e.extra.member? 'UniversalTime'
|
|
e.extra['UniversalTime'].atime
|
|
elsif e.extra.member? 'NTFS'
|
|
e.extra['NTFS'].atime
|
|
end
|
|
end
|
|
|
|
def ctime(filename)
|
|
e = find_entry(filename)
|
|
if e.extra.member? 'UniversalTime'
|
|
e.extra['UniversalTime'].ctime
|
|
elsif e.extra.member? 'NTFS'
|
|
e.extra['NTFS'].ctime
|
|
end
|
|
end
|
|
|
|
def pipe?(_filename)
|
|
false
|
|
end
|
|
|
|
def blockdev?(_filename)
|
|
false
|
|
end
|
|
|
|
def chardev?(_filename)
|
|
false
|
|
end
|
|
|
|
def symlink?(_filename)
|
|
false
|
|
end
|
|
|
|
def socket?(_filename)
|
|
false
|
|
end
|
|
|
|
def ftype(filename)
|
|
@mapped_zip.get_entry(filename).directory? ? 'directory' : 'file'
|
|
end
|
|
|
|
def readlink(_filename)
|
|
raise NotImplementedError, 'The readlink() function is not implemented'
|
|
end
|
|
|
|
def symlink(_filename, _symlink_name)
|
|
raise NotImplementedError, 'The symlink() function is not implemented'
|
|
end
|
|
|
|
def link(_filename, _symlink_name)
|
|
raise NotImplementedError, 'The link() function is not implemented'
|
|
end
|
|
|
|
def pipe
|
|
raise NotImplementedError, 'The pipe() function is not implemented'
|
|
end
|
|
|
|
def stat(filename)
|
|
raise Errno::ENOENT, filename unless exists?(filename)
|
|
|
|
Stat.new(self, filename)
|
|
end
|
|
|
|
alias lstat stat
|
|
|
|
def readlines(filename)
|
|
self.open(filename, &:readlines)
|
|
end
|
|
|
|
def read(filename)
|
|
@mapped_zip.read(filename)
|
|
end
|
|
|
|
def popen(*args, &a_proc)
|
|
::File.popen(*args, &a_proc)
|
|
end
|
|
|
|
def foreach(filename, sep = $INPUT_RECORD_SEPARATOR, &a_proc)
|
|
self.open(filename) { |is| is.each_line(sep, &a_proc) }
|
|
end
|
|
|
|
def delete(*args)
|
|
args.each do |filename|
|
|
if directory?(filename)
|
|
raise Errno::EISDIR, "Is a directory - \"#{filename}\""
|
|
end
|
|
|
|
@mapped_zip.remove(filename)
|
|
end
|
|
end
|
|
|
|
def rename(file_to_rename, new_name)
|
|
@mapped_zip.rename(file_to_rename, new_name) { true }
|
|
end
|
|
|
|
alias unlink delete
|
|
|
|
def expand_path(path)
|
|
@mapped_zip.expand_path(path)
|
|
end
|
|
end
|
|
end
|
|
end
|