rubyzip/filearchive.rb

185 lines
5.2 KiB
Ruby
Executable File

#!/usr/bin/env ruby
require 'ftools'
require 'zip'
module Glob
class GlobPattern
def initialize(globPatternString)
@globPatternString = globPatternString
@trailingSlash = globPatternString.endsWith(File::SEPARATOR)
@globPatternElements = globPatternString.split(File::SEPARATOR).map {
|globElement|
GlobPattern.toRegexp(globElement)
}
end
def ===(aFilePath)
return false if FilePath.size(aFilePath) != size
return false if @trailingSlash && ! FilePath.isDirectory(aFilePath)
@globPatternElements.each_with_index {
|globElement, index|
return false if ! (globElement === FilePath.elements(aFilePath)[index])
}
true
end
def self.toRegexp(globPattern)
return Regexp.new("^"+globPattern.
gsub(/\?/, "#{NOT_PATH_SEPARATOR}").
gsub(/\*/, "#{NOT_PATH_SEPARATOR}*")+
%Q{/?$})
end
def size
@globPatternElements.size
end
NOT_PATH_SEPARATOR = "[^\\#{File::SEPARATOR}]"
end
# This class follows the Fly-weight pattern
class FilePath
def self.size(aFilePath)
elements(aFilePath).size
end
def self.elements(aFilePath)
aFilePath.to_s.split(File::SEPARATOR)
end
def self.isDirectory(aFilePath)
aFilePath.to_s.endsWith(File::SEPARATOR)
end
def self.basename(aFilePath)
aFilePath.to_s.slice(Regexp.new("#{GlobPattern::NOT_PATH_SEPARATOR}*(#{File::SEPARATOR})?$"))
end
def self.dirname(aFilePath)
aFilePath.to_s.sub(Regexp.new("#{GlobPattern::NOT_PATH_SEPARATOR}*(#{File::SEPARATOR})?$"),
"")
end
end
def self.glob(pathList, globPattern)
expandPathList(pathList).grep(GlobPattern.new(globPattern))
end
def self.expandPathList(pathList)
result = Hash.new
pathList.each {
|path|
expandPath(path).each { |path| result[path] = path }
}
result.keys
end
# expands "rip/rap/rup" to ["rip/", "rip/rap/", "rip/rap/rup"]
def self.expandPath(path)
elements = path.scan(/[^\/]+\/?/)
accumulatedList = []
elements.map {
|element|
(accumulatedList << element).join
}
end
end # end of Glob module
# Relies on:
# * extractEntry(src, dst)
# ** src may be a string or an entry object native to the container (e.g. ZipEntry for ZipFile)
# ** dst is a string
module FileArchive
RECURSIVE = true
NONRECURSIVE = false
def add(src, dst, recursive = NONRECURSIVE,
continueOnExistsProc = proc { false },
createDestDirectoryProc = proc { true } )
puts "implement FileArchive.add"
end
# src can be String(glob pattern), regex, ZipEntry or Enumerable
def extract(src, dst, recursive = NONRECURSIVE,
continueOnExistsProc = proc { false },
createDestDirectoryProc = proc { true } )
selectedEntries = expandSelection(src)
case (selectedEntries.size)
when 0
raise Errno::ENOENT, "'#{src}' not found in archive #{self.to_s}"
when 1
extractSingle(selectedEntries[0], dst, recursive,
continueOnExistsProc, createDestDirectoryProc)
else
extractMultiple(selectedEntries, dst, recursive,
continueOnExistsProc, createDestDirectoryProc)
end
end
def extractMultiple(srcList, dst, recursive, continueOnExistsProc, createDestDirectoryProc)
FileArchive.ensureDirectory(dst, &createDestDirectoryProc)
srcList.each {
|srcFilename|
extractSingle(srcFilename, dst, recursive, continueOnExistsProc, createDestDirectoryProc)
}
end
private :extractMultiple
def extractSingle(src, dst, recursive, continueOnExistsProc, createDestDirectoryProc)
destFilename = destinationFilename(src, dst)
extractEntry(src, destFilename, &continueOnExistsProc)
if (recursive && Glob::FilePath.isDirectory(src))
extract(src+"*", destFilename.ensureEnd(File::SEPARATOR),
recursive, continueOnExistsProc, createDestDirectoryProc)
end
end
private :extractSingle
def destinationFilename(sourceFilePath, destinationPath)
if File.directory?(destinationPath)
return destinationPath.ensureEnd(File::SEPARATOR) + Glob::FilePath.basename(sourceFilePath)
else
return sourceFilePath.endsWith(File::SEPARATOR)?
destinationPath.ensureEnd(File::SEPARATOR) : destinationPath
end
end
private :destinationFilename
# if selection is a string or a regexp it is expanded to a list of entries
# otherwise selection is returned unmodified
def expandSelection(selection)
case selection
when String then return Glob.glob(entries, selection)
when Regexp then return entries.select { |entry| entry.to_s =~ selection }
else return selection
end
end
# If filepath is a file raises exception.
# If filepath doesn't exist create if createDirectoryProc
def self.ensureDirectory(filepath, &createDirectoryProc)
if File.exists?(filepath) && File.directory?(filepath)
return
elsif File.exists?(filepath) && ! File.directory?(filepath)
raise Errno::EEXIST,
"Could not create directory '#{filepath}' - a file already exists with that name"
elsif createDirectoryProc.call(filepath)
Dir.mkdir(filepath) # replace with something that does mkdir -p (create all dirs)
else
raise Errno::ENOENT, "No such file or directory - '#{filepath}'"
end
end
end