#!/usr/bin/env ruby # # acvfs.rb: Archive FileSystem Based on FUSE/Ruby # (C)2006 by HIROSE, Yuuji [yuuji@yatex.org] # # Usage: acvfs.rb mountpoint # # This file system provides zip/tar.gz/tar.bz2 auto inspection. # # Requirements: # http://fuse.sourceforge.net/ - Filesystem in Userspace # http://rubyforge.org/projects/fusefs - Ruby binding of FUSE-FS # # Todo: # Support other archivers than arc, arj, rar, ... and so on. # But it might be easy. Try it by yourself. # require 'fusefs' class ArchiveDir def initialize @cachedacv = Hash.new end def zip?(path) /\.zip/i =~ path end def targz?(path) /\.(tar\.gz|tgz)/i =~ path end def tarbz2?(path) /\.(tar\.bz2|tbz)/i =~ path end def acv?(path) # path is archived file or within it, or not zip?(path) || targz?(path) || tarbz2(path) end def acvfile?(path) # path IS archived file, or not /\.(zip|tar\.(gz|bz2))$/i =~ path end def listcmd(arc) # Return the array of [ListingCmd, Regexp, [$size, $name], EndRattern] if zip?(arc) ["| unzip -v \"#{arc}\" | tail +4", /(\d+) +(\d+)% +(\d+)-(\d+)-(\d+) +(\d\d):(\d\d) .* (\S+)$/, [1, 8], /^---/] elsif targz?(arc)||tarbz2?(arc) c = targz?(arc) ? "z" : "j" ["| tar v#{c}tf \"#{arc}\"", /(\d+) +(\d{4})-(\d+)-(\d+) +(\d\d):(\d\d):(\d\d) +(\S+)$/, [1, 8], /^$/] end end # cache all filenames, sizes and whether they are directory def acvinfo(acv) %r,(.*\.(zip|tar\.(gz|bz2)))(/(.*))?, =~ acv dir = $1 prefix = $5 if !@cachedacv[acv] @cachedacv[acv] = Hash.new listinfo = listcmd(dir) open(listinfo[0], "r") do |zls| while line = zls.gets case line when listinfo[3] break when listinfo[1] md = Regexp.last_match dirp = nil # is_directory flag name = md[listinfo[2][1]] if prefix next unless name.index(prefix) == 0 next if name == prefix name.sub!(prefix+"/", "") # strip prefix directory end next if name == "" # skip prefix dir itself next if %r,/., =~ name # skip files in subdirectory if %r,/$, =~ name name.chop! dirp = true end @cachedacv[acv][name] = Hash.new @cachedacv[acv][name]['dir'] = dirp @cachedacv[acv][name]['size'] = md[listinfo[2][0]].to_i # p ["set", acv, name, dir, md[listinfo[2][0]].to_i, dirp] end end end end @cachedacv[acv] end # Return the list of files in directory: path def contents(path) mntpt=ARGV[0].gsub(%r|/+|, "/") if test(?d, path) Dir.entries(path).select{|f| n = (path+"/"+f).gsub(%r|/+|, "/") next if ARGV[0] == n test(?d, n) || test(?f, n) && acvfile?(f) } elsif acv?(path) acvinfo(path).keys end end def file?(path) test(?f, path) || if acv?(path) acv = File.dirname(path) file = File.basename(path) acvinfo(acv).has_key?(file) && !acvinfo(acv)[file]['dir'] end end def executable?(path) test(?d, path) ? test(x, path) : false # shoud be safer end def directory?(path) test(?d, path) || acvfile?(path) || if acv?(path) acv = File.dirname(path) file = File.basename(path) acvinfo(acv)[file]['dir'] end end def can_write?(path) false end def read_file(path) %r,(.*\.(tar\.(gz|bz2)|zip))(/(.*))?, =~ path acv, file = $1, $5 if acv && file cmd = if zip?(acv) "unzip -cqq \"#{acv}\" \"#{file}\"" elsif targz?(acv) "tar zxOf \"#{acv}\" \"#{file}\"" elsif tarbz2?(acv) "tar jxOf \"#{acv}\" \"#{file}\"" end IO.popen(cmd, "r").read else "Not an archived file\n" end end def size(path) acv = File.dirname(path) file = File.basename(path) if acv?(acv) acvinfo(acv)[file]['size'] else test(?s, path) end end end adir = ArchiveDir.new FuseFS.set_root( adir ) # Mount under a directory given on the command line. FuseFS.mount_under ARGV[0] FuseFS.run