#
# = ftools.rb: Extra tools for the File class
#
# Author:: WATANABE, Hirofumi
# Documentation:: Zachary Landau
#
# This library can be distributed under the terms of the Ruby license.
# You can freely distribute/modify this library.
#
# It is included in the Ruby standard library.
#
# == Description
#
# ftools adds several (class, not instance) methods to the File class, for
# copying, moving, deleting, installing, and comparing files, as well as
# creating a directory path. See the File class for details.
#
# FileUtils contains all or nearly all the same functionality and more, and
# is a recommended option over ftools
#
# When you
#
# require 'ftools'
#
# then the File class aquires some utility methods for copying, moving, and
# deleting files, and more.
#
# See the method descriptions below, and consider using FileUtils as it is
# more comprehensive.
#
class File
end
class << File
BUFSIZE = 8 * 1024
#
# If +to+ is a valid directory, +from+ will be appended to +to+, adding
# and escaping backslashes as necessary. Otherwise, +to+ will be returned.
# Useful for appending +from+ to +to+ only if the filename was not specified
# in +to+.
#
def catname(from, to)
if directory? to
join to.sub(%r([/\\]$), ''), basename(from)
else
to
end
end
#
# Copies a file +from+ to +to+. If +to+ is a directory, copies +from+
# to to/from.
#
def syscopy(from, to)
to = catname(from, to)
fmode = stat(from).mode
tpath = to
not_exist = !exist?(tpath)
from = open(from, "rb")
to = open(to, "wb")
begin
while true
to.syswrite from.sysread(BUFSIZE)
end
rescue EOFError
ret = true
rescue
ret = false
ensure
to.close
from.close
end
chmod(fmode, tpath) if not_exist
ret
end
#
# Copies a file +from+ to +to+ using #syscopy. If +to+ is a directory,
# copies +from+ to to/from. If +verbose+ is true, from -> to
# is printed.
#
def copy(from, to, verbose = false)
$stderr.print from, " -> ", catname(from, to), "\n" if verbose
syscopy from, to
end
alias cp copy
#
# Moves a file +from+ to +to+ using #syscopy. If +to+ is a directory,
# copies from +from+ to to/from. If +verbose+ is true, from ->
# to is printed.
#
def move(from, to, verbose = false)
to = catname(from, to)
$stderr.print from, " -> ", to, "\n" if verbose
if RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ and file? to
unlink to
end
fstat = stat(from)
begin
rename from, to
rescue
begin
symlink readlink(from), to and unlink from
rescue
from_stat = stat(from)
syscopy from, to and unlink from
utime(from_stat.atime, from_stat.mtime, to)
begin
chown(fstat.uid, fstat.gid, to)
rescue
end
end
end
end
alias mv move
#
# Returns +true+ if and only if the contents of files +from+ and +to+ are
# identical. If +verbose+ is +true+, from <=> to is printed.
#
def compare(from, to, verbose = false)
$stderr.print from, " <=> ", to, "\n" if verbose
return false if stat(from).size != stat(to).size
from = open(from, "rb")
to = open(to, "rb")
ret = false
fr = tr = ''
begin
while fr == tr
fr = from.read(BUFSIZE)
if fr
tr = to.read(fr.size)
else
ret = to.read(BUFSIZE)
ret = !ret || ret.length == 0
break
end
end
rescue
ret = false
ensure
to.close
from.close
end
ret
end
alias cmp compare
#
# Removes a list of files. Each parameter should be the name of the file to
# delete. If the last parameter isn't a String, verbose mode will be enabled.
# Returns the number of files deleted.
#
def safe_unlink(*files)
verbose = if files[-1].is_a? String then false else files.pop end
files.each do |file|
begin
unlink file
$stderr.print "removing ", file, "\n" if verbose
rescue Errno::EACCES # for Windows
continue if symlink? file
begin
mode = stat(file).mode
o_chmod mode | 0200, file
unlink file
$stderr.print "removing ", file, "\n" if verbose
rescue
o_chmod mode, file rescue nil
end
rescue
end
end
end
alias rm_f safe_unlink
#
# Creates a directory and all its parent directories.
# For example,
#
# File.makedirs '/usr/lib/ruby'
#
# causes the following directories to be made, if they do not exist.
# * /usr
# * /usr/lib
# * /usr/lib/ruby
#
# You can pass several directories, each as a parameter. If the last
# parameter isn't a String, verbose mode will be enabled.
#
def makedirs(*dirs)
verbose = if dirs[-1].is_a? String then false else dirs.pop end
mode = 0755
for dir in dirs
parent = dirname(dir)
next if parent == dir or directory? dir
makedirs parent unless directory? parent
$stderr.print "mkdir ", dir, "\n" if verbose
if basename(dir) != ""
begin
Dir.mkdir dir, mode
rescue SystemCallError
raise unless directory? dir
end
end
end
end
alias mkpath makedirs
alias o_chmod chmod
vsave, $VERBOSE = $VERBOSE, false
#
# Changes permission bits on +files+ to the bit pattern represented
# by +mode+. If the last parameter isn't a String, verbose mode will
# be enabled.
#
# File.chmod 0755, 'somecommand'
# File.chmod 0644, 'my.rb', 'your.rb', true
#
def chmod(mode, *files)
verbose = if files[-1].is_a? String then false else files.pop end
$stderr.printf "chmod %04o %s\n", mode, files.join(" ") if verbose
o_chmod mode, *files
end
$VERBOSE = vsave
#
# If +src+ is not the same as +dest+, copies it and changes the permission
# mode to +mode+. If +dest+ is a directory, destination is dest/src.
# If +mode+ is not set, default is used. If +verbose+ is set to true, the
# name of each file copied will be printed.
#
def install(from, to, mode = nil, verbose = false)
to = catname(from, to)
unless exist? to and cmp from, to
safe_unlink to if exist? to
cp from, to, verbose
chmod mode, to, verbose if mode
end
end
end
# vi:set sw=2: