2017-01-07 05:14:07 +03:00
# frozen_string_literal: true
2018-10-03 22:19:33 +03:00
begin
require 'rbconfig'
rescue LoadError
# for make mjit-headers
end
2018-10-20 10:03:55 +03:00
#
2003-02-06 12:56:13 +03:00
# = fileutils.rb
2009-03-06 06:56:38 +03:00
#
2007-03-03 16:53:38 +03:00
# Copyright (c) 2000-2007 Minero Aoki
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# This program is free software.
# You can distribute/modify this program under the same terms of ruby.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# == module FileUtils
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Namespace for several file utility methods for copying, moving, removing, etc.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# === Module Functions
2009-03-06 06:56:38 +03:00
#
2014-05-08 22:32:24 +04:00
# require 'fileutils'
#
2019-03-10 14:09:59 +03:00
# FileUtils.cd(dir, **options)
# FileUtils.cd(dir, **options) {|dir| block }
2014-05-08 22:32:24 +04:00
# FileUtils.pwd()
2019-03-10 14:09:59 +03:00
# FileUtils.mkdir(dir, **options)
# FileUtils.mkdir(list, **options)
# FileUtils.mkdir_p(dir, **options)
# FileUtils.mkdir_p(list, **options)
# FileUtils.rmdir(dir, **options)
# FileUtils.rmdir(list, **options)
# FileUtils.ln(target, link, **options)
# FileUtils.ln(targets, dir, **options)
# FileUtils.ln_s(target, link, **options)
# FileUtils.ln_s(targets, dir, **options)
# FileUtils.ln_sf(target, link, **options)
# FileUtils.cp(src, dest, **options)
# FileUtils.cp(list, dir, **options)
# FileUtils.cp_r(src, dest, **options)
# FileUtils.cp_r(list, dir, **options)
# FileUtils.mv(src, dest, **options)
# FileUtils.mv(list, dir, **options)
# FileUtils.rm(list, **options)
# FileUtils.rm_r(list, **options)
# FileUtils.rm_rf(list, **options)
# FileUtils.install(src, dest, **options)
# FileUtils.chmod(mode, list, **options)
# FileUtils.chmod_R(mode, list, **options)
# FileUtils.chown(user, group, list, **options)
# FileUtils.chown_R(user, group, list, **options)
# FileUtils.touch(list, **options)
2003-01-24 19:13:33 +03:00
#
2019-03-10 14:09:59 +03:00
# Possible <tt>options</tt> are:
#
# <tt>:force</tt> :: forced operation (rewrite files if exist, remove
# directories if not empty, etc.);
# <tt>:verbose</tt> :: print command to be run, in bash syntax, before
# performing it;
# <tt>:preserve</tt> :: preserve object's group, user and modification
# time on copying;
# <tt>:noop</tt> :: no changes are made (usable in combination with
# <tt>:verbose</tt> which will print the command to run)
#
# Each method documents the options that it honours. See also ::commands,
# ::options and ::options_of methods to introspect which command have which
# options.
2003-01-24 19:13:33 +03:00
#
# All methods that have the concept of a "source" file or directory can take
# either one file or a list of files in that argument. See the method
# documentation for examples.
#
2019-03-10 14:09:59 +03:00
# There are some `low level' methods, which do not accept keyword arguments:
2003-02-21 14:57:31 +03:00
#
2018-10-20 10:03:55 +03:00
# FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
2014-05-08 22:32:24 +04:00
# FileUtils.copy_file(src, dest, preserve = false, dereference = true)
# FileUtils.copy_stream(srcstream, deststream)
# FileUtils.remove_entry(path, force = false)
# FileUtils.remove_entry_secure(path, force = false)
# FileUtils.remove_file(path, force = false)
# FileUtils.compare_file(path_a, path_b)
# FileUtils.compare_stream(stream_a, stream_b)
# FileUtils.uptodate?(file, cmp_list)
2003-02-21 14:57:31 +03:00
#
2003-01-24 19:13:33 +03:00
# == module FileUtils::Verbose
2009-03-06 06:56:38 +03:00
#
2003-02-06 12:56:13 +03:00
# This module has all methods of FileUtils module, but it outputs messages
2004-02-13 15:53:42 +03:00
# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
# in FileUtils.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# == module FileUtils::NoWrite
2009-03-06 06:56:38 +03:00
#
2003-02-06 12:56:13 +03:00
# This module has all methods of FileUtils module, but never changes
2004-02-13 15:53:42 +03:00
# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
# in FileUtils.
2009-03-06 06:56:38 +03:00
#
2004-05-07 17:08:12 +04:00
# == module FileUtils::DryRun
2009-03-06 06:56:38 +03:00
#
2004-05-07 17:08:12 +04:00
# This module has all methods of FileUtils module, but never changes
# files/directories. This equates to passing the <tt>:noop</tt> and
# <tt>:verbose</tt> flags to methods in FileUtils.
2018-10-20 10:03:55 +03:00
#
2002-03-26 05:03:04 +03:00
module FileUtils
2019-11-30 12:05:51 +03:00
VERSION = " 1.4.1 "
2002-03-26 05:03:04 +03:00
2013-03-01 06:09:42 +04:00
def self . private_module_function ( name ) #:nodoc:
module_function name
private_class_method name
2005-09-19 00:59:29 +04:00
end
2003-01-24 19:13:33 +03:00
2003-02-09 01:01:53 +03:00
#
# Returns the name of the current directory.
#
def pwd
Dir . pwd
end
2013-03-01 06:09:42 +04:00
module_function :pwd
2003-02-09 01:01:53 +03:00
alias getwd pwd
2013-03-01 06:09:42 +04:00
module_function :getwd
2003-02-09 01:01:53 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Changes the current directory to the directory +dir+.
2009-03-06 06:56:38 +03:00
#
2019-03-27 11:26:09 +03:00
# If this method is called with block, resumes to the previous
# working directory after the block execution has finished.
2009-03-06 06:56:38 +03:00
#
2019-03-27 11:26:09 +03:00
# FileUtils.cd('/') # change directory
2009-03-06 06:56:38 +03:00
#
2019-03-10 14:09:59 +03:00
# FileUtils.cd('/', verbose: true) # change directory and report it
2019-03-27 11:26:09 +03:00
#
# FileUtils.cd('/') do # change directory
2017-03-01 13:17:42 +03:00
# # ... # do something
2011-05-22 06:12:15 +04:00
# end # return to original directory
2012-03-12 01:48:45 +04:00
#
2016-03-01 05:26:44 +03:00
def cd ( dir , verbose : nil , & block ) # :yield: dir
fu_output_message " cd #{ dir } " if verbose
2018-05-15 08:53:18 +03:00
result = Dir . chdir ( dir , & block )
2016-03-01 05:26:44 +03:00
fu_output_message 'cd -' if verbose and block
2018-05-15 08:53:18 +03:00
result
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :cd
2002-03-26 05:03:04 +03:00
alias chdir cd
2013-03-01 06:09:42 +04:00
module_function :chdir
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2014-01-22 10:22:06 +04:00
# Returns true if +new+ is newer than all +old_list+.
2003-01-24 19:13:33 +03:00
# Non-existent files are older than any file.
2009-03-06 06:56:38 +03:00
#
2003-05-02 10:07:54 +04:00
# FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
# system 'make hello.o'
2009-03-06 06:56:38 +03:00
#
2012-07-16 12:15:05 +04:00
def uptodate? ( new , old_list )
2003-11-18 08:09:20 +03:00
return false unless File . exist? ( new )
2003-02-10 13:48:38 +03:00
new_time = File . mtime ( new )
2002-12-27 07:21:27 +03:00
old_list . each do | old |
2003-11-18 08:09:20 +03:00
if File . exist? ( old )
2002-03-26 05:03:04 +03:00
return false unless new_time > File . mtime ( old )
end
end
true
end
2013-03-01 06:09:42 +04:00
module_function :uptodate?
2002-03-26 05:03:04 +03:00
2017-03-01 13:17:42 +03:00
def remove_trailing_slash ( dir ) #:nodoc:
2013-06-03 18:20:15 +04:00
dir == '/' ? dir : dir . chomp ( ?/ )
end
2015-10-26 11:30:20 +03:00
private_module_function :remove_trailing_slash
2013-06-03 18:20:15 +04:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Creates one or more directories.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.mkdir 'test'
2019-03-10 14:09:59 +03:00
# FileUtils.mkdir %w(tmp data)
# FileUtils.mkdir 'notexist', noop: true # Does not really create.
# FileUtils.mkdir 'tmp', mode: 0700
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def mkdir ( list , mode : nil , noop : nil , verbose : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
fu_output_message " mkdir #{ mode ? ( '-m %03o ' % mode ) : '' } #{ list . join ' ' } " if verbose
return if noop
2002-03-26 05:03:04 +03:00
list . each do | dir |
2016-03-01 05:26:44 +03:00
fu_mkdir dir , mode
2002-03-26 05:03:04 +03:00
end
end
2013-03-01 06:09:42 +04:00
module_function :mkdir
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Creates a directory and all its parent directories.
# For example,
2009-03-06 06:56:38 +03:00
#
2003-02-06 12:56:13 +03:00
# FileUtils.mkdir_p '/usr/local/lib/ruby'
2009-03-06 06:56:38 +03:00
#
2019-03-10 14:09:59 +03:00
# causes to make following directories, if they do not exist.
2017-03-01 13:17:42 +03:00
#
# * /usr
# * /usr/local
# * /usr/local/lib
# * /usr/local/lib/ruby
2003-01-24 19:13:33 +03:00
#
# You can pass several directories at a time in a list.
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def mkdir_p ( list , mode : nil , noop : nil , verbose : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
fu_output_message " mkdir -p #{ mode ? ( '-m %03o ' % mode ) : '' } #{ list . join ' ' } " if verbose
return * list if noop
2002-03-26 05:03:04 +03:00
2015-10-26 11:30:20 +03:00
list . map { | path | remove_trailing_slash ( path ) } . each do | path |
2004-08-09 01:35:11 +04:00
# optimize for the most common case
begin
2016-03-01 05:26:44 +03:00
fu_mkdir path , mode
2004-08-09 01:35:11 +04:00
next
rescue SystemCallError
next if File . directory? ( path )
end
2002-03-26 05:03:04 +03:00
stack = [ ]
2003-12-11 13:15:54 +03:00
until path == stack . last # dirname("/")=="/", dirname("C:/")=="C:/"
stack . push path
path = File . dirname ( path )
2002-03-26 05:03:04 +03:00
end
2017-02-16 11:42:22 +03:00
stack . pop # root directory should exist
2006-10-09 18:41:24 +04:00
stack . reverse_each do | dir |
2003-12-11 13:15:54 +03:00
begin
2016-03-01 05:26:44 +03:00
fu_mkdir dir , mode
2010-06-22 09:46:01 +04:00
rescue SystemCallError
2006-10-09 18:41:24 +04:00
raise unless File . directory? ( dir )
2003-12-11 13:15:54 +03:00
end
2002-03-26 05:03:04 +03:00
end
end
return * list
end
2013-03-01 06:09:42 +04:00
module_function :mkdir_p
2002-03-26 05:03:04 +03:00
alias mkpath mkdir_p
alias makedirs mkdir_p
2013-03-01 06:09:42 +04:00
module_function :mkpath
module_function :makedirs
2002-03-26 05:03:04 +03:00
2005-09-19 00:59:29 +04:00
def fu_mkdir ( path , mode ) #:nodoc:
2015-10-26 11:30:20 +03:00
path = remove_trailing_slash ( path )
2004-12-04 14:46:24 +03:00
if mode
Dir . mkdir path , mode
File . chmod mode , path
else
Dir . mkdir path
end
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_mkdir
2004-12-04 14:46:24 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Removes one or more directories.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.rmdir 'somedir'
# FileUtils.rmdir %w(somedir anydir otherdir)
# # Does not really remove directory; outputs message.
2019-03-10 14:09:59 +03:00
# FileUtils.rmdir 'somedir', verbose: true, noop: true
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def rmdir ( list , parents : nil , noop : nil , verbose : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
fu_output_message " rmdir #{ parents ? '-p ' : '' } #{ list . join ' ' } " if verbose
return if noop
2002-03-26 05:03:04 +03:00
list . each do | dir |
2017-09-16 14:59:47 +03:00
Dir . rmdir ( dir = remove_trailing_slash ( dir ) )
if parents
begin
2009-01-25 05:06:29 +03:00
until ( parent = File . dirname ( dir ) ) == '.' or parent == dir
2013-06-13 17:12:35 +04:00
dir = parent
2009-01-25 05:06:29 +03:00
Dir . rmdir ( dir )
end
2017-09-16 14:59:47 +03:00
rescue Errno :: ENOTEMPTY , Errno :: EEXIST , Errno :: ENOENT
2009-01-25 05:06:29 +03:00
end
end
2002-03-26 05:03:04 +03:00
end
end
2013-03-01 06:09:42 +04:00
module_function :rmdir
2002-03-26 05:03:04 +03:00
2003-01-24 19:13:33 +03:00
#
2017-03-08 23:41:38 +03:00
# :call-seq:
# FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil)
# FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil)
# FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil)
2003-01-24 19:13:33 +03:00
#
2017-03-08 23:41:38 +03:00
# In the first form, creates a hard link +link+ which points to +target+.
# If +link+ already exists, raises Errno::EEXIST.
2019-03-10 14:09:59 +03:00
# But if the +force+ option is set, overwrites +link+.
2009-03-06 06:56:38 +03:00
#
2017-03-08 23:41:38 +03:00
# FileUtils.ln 'gcc', 'cc', verbose: true
2003-01-24 19:13:33 +03:00
# FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
2009-03-06 06:56:38 +03:00
#
2017-03-08 23:41:38 +03:00
# In the second form, creates a link +dir/target+ pointing to +target+.
# In the third form, creates several hard links in the directory +dir+,
# pointing to each item in +targets+.
# If +dir+ is not a directory, raises Errno::ENOTDIR.
2009-03-06 06:56:38 +03:00
#
2017-03-08 23:41:38 +03:00
# FileUtils.cd '/sbin'
2005-05-26 15:33:16 +04:00
# FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def ln ( src , dest , force : nil , noop : nil , verbose : nil )
fu_output_message " ln #{ force ? ' -f' : '' } #{ [ src , dest ] . flatten . join ' ' } " if verbose
return if noop
2003-11-18 08:09:20 +03:00
fu_each_src_dest0 ( src , dest ) do | s , d |
2016-03-01 05:26:44 +03:00
remove_file d , true if force
2002-03-26 05:03:04 +03:00
File . link s , d
end
end
2013-03-01 06:09:42 +04:00
module_function :ln
2002-03-26 05:03:04 +03:00
alias link ln
2013-03-01 06:09:42 +04:00
module_function :link
2002-03-26 05:03:04 +03:00
2018-03-13 11:18:03 +03:00
#
# Hard link +src+ to +dest+. If +src+ is a directory, this method links
# all its contents recursively. If +dest+ is a directory, links
# +src+ to +dest/src+.
#
# +src+ can be a list of files.
#
2019-03-10 14:09:59 +03:00
# If +dereference_root+ is true, this method dereference tree root.
#
# If +remove_destination+ is true, this method removes each destination file before copy.
#
# FileUtils.rm_r site_ruby + '/mylib', force: true
2018-03-13 11:18:03 +03:00
# FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
#
2018-03-13 18:10:59 +03:00
# # Examples of linking several files to target directory.
2018-03-13 11:18:03 +03:00
# FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
2019-03-10 14:09:59 +03:00
# FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
2018-03-13 11:18:03 +03:00
#
2018-03-13 18:10:59 +03:00
# # If you want to link all contents of a directory instead of the
2018-03-13 11:18:03 +03:00
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
2018-04-14 19:47:59 +03:00
# # use the following code.
2018-03-13 18:10:59 +03:00
# FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't.
2018-03-13 11:18:03 +03:00
#
def cp_lr ( src , dest , noop : nil , verbose : nil ,
dereference_root : true , remove_destination : false )
fu_output_message " cp -lr #{ remove_destination ? ' --remove-destination' : '' } #{ [ src , dest ] . flatten . join ' ' } " if verbose
return if noop
fu_each_src_dest ( src , dest ) do | s , d |
link_entry s , d , dereference_root , remove_destination
end
end
module_function :cp_lr
2003-01-24 19:13:33 +03:00
#
2017-03-08 23:41:38 +03:00
# :call-seq:
# FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
# FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil)
# FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil)
2009-03-06 06:56:38 +03:00
#
2017-03-08 23:41:38 +03:00
# In the first form, creates a symbolic link +link+ which points to +target+.
# If +link+ already exists, raises Errno::EEXIST.
2019-03-10 14:09:59 +03:00
# But if the <tt>force</tt> option is set, overwrites +link+.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
2017-03-08 23:41:38 +03:00
# FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
2009-03-06 06:56:38 +03:00
#
2017-03-08 23:41:38 +03:00
# In the second form, creates a link +dir/target+ pointing to +target+.
# In the third form, creates several symbolic links in the directory +dir+,
# pointing to each item in +targets+.
# If +dir+ is not a directory, raises Errno::ENOTDIR.
2003-01-24 19:13:33 +03:00
#
2017-03-08 23:41:38 +03:00
# FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin'
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def ln_s ( src , dest , force : nil , noop : nil , verbose : nil )
fu_output_message " ln -s #{ force ? 'f' : '' } #{ [ src , dest ] . flatten . join ' ' } " if verbose
return if noop
2003-11-18 08:09:20 +03:00
fu_each_src_dest0 ( src , dest ) do | s , d |
2016-03-01 05:26:44 +03:00
remove_file d , true if force
2002-03-26 05:03:04 +03:00
File . symlink s , d
end
end
2013-03-01 06:09:42 +04:00
module_function :ln_s
2002-03-26 05:03:04 +03:00
alias symlink ln_s
2013-03-01 06:09:42 +04:00
module_function :symlink
2002-03-26 05:03:04 +03:00
2017-03-08 23:41:38 +03:00
#
# :call-seq:
# FileUtils.ln_sf(*args)
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Same as
2017-03-01 13:17:42 +03:00
#
2017-03-08 23:41:38 +03:00
# FileUtils.ln_s(*args, force: true)
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def ln_sf ( src , dest , noop : nil , verbose : nil )
ln_s src , dest , force : true , noop : noop , verbose : verbose
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :ln_sf
2002-03-26 05:03:04 +03:00
2018-03-13 11:18:03 +03:00
#
# Hard links a file system entry +src+ to +dest+.
# If +src+ is a directory, this method links its contents recursively.
#
# Both of +src+ and +dest+ must be a path name.
# +src+ must exist, +dest+ must not exist.
#
2018-04-14 19:47:59 +03:00
# If +dereference_root+ is true, this method dereferences the tree root.
2018-03-13 11:18:03 +03:00
#
# If +remove_destination+ is true, this method removes each destination file before copy.
#
def link_entry ( src , dest , dereference_root = false , remove_destination = false )
Entry_ . new ( src , nil , dereference_root ) . traverse do | ent |
destent = Entry_ . new ( dest , ent . rel , false )
File . unlink destent . path if remove_destination && File . file? ( destent . path )
ent . link destent . path
end
end
module_function :link_entry
2003-01-24 19:13:33 +03:00
#
2005-05-26 02:41:32 +04:00
# Copies a file content +src+ to +dest+. If +dest+ is a directory,
# copies +src+ to +dest/src+.
2003-01-24 19:13:33 +03:00
#
# If +src+ is a list of files, then +dest+ must be a directory.
#
# FileUtils.cp 'eval.c', 'eval.c.org'
2003-02-06 12:56:13 +03:00
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
2019-03-10 14:09:59 +03:00
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true
2005-05-26 02:41:32 +04:00
# FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def cp ( src , dest , preserve : nil , noop : nil , verbose : nil )
fu_output_message " cp #{ preserve ? ' -p' : '' } #{ [ src , dest ] . flatten . join ' ' } " if verbose
return if noop
2005-05-26 02:41:32 +04:00
fu_each_src_dest ( src , dest ) do | s , d |
2016-03-01 05:26:44 +03:00
copy_file s , d , preserve
2002-03-26 05:03:04 +03:00
end
end
2013-03-01 06:09:42 +04:00
module_function :cp
2002-03-26 05:03:04 +03:00
alias copy cp
2013-03-01 06:09:42 +04:00
module_function :copy
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Copies +src+ to +dest+. If +src+ is a directory, this method copies
# all its contents recursively. If +dest+ is a directory, copies
# +src+ to +dest/src+.
#
# +src+ can be a list of files.
2009-03-06 06:56:38 +03:00
#
2019-03-10 14:09:59 +03:00
# If +dereference_root+ is true, this method dereference tree root.
#
# If +remove_destination+ is true, this method removes each destination file before copy.
#
2013-07-18 17:50:32 +04:00
# # Installing Ruby library "mylib" under the site_ruby
2019-03-10 14:09:59 +03:00
# FileUtils.rm_r site_ruby + '/mylib', force: true
2003-01-24 19:13:33 +03:00
# FileUtils.cp_r 'lib/', site_ruby + '/mylib'
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# # Examples of copying several files to target directory.
# FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
2019-03-10 14:09:59 +03:00
# FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
2004-08-09 01:35:11 +04:00
#
# # If you want to copy all contents of a directory instead of the
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
# # use following code.
2012-05-12 01:02:58 +04:00
# FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
2004-08-09 01:35:11 +04:00
# # but this doesn't.
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def cp_r ( src , dest , preserve : nil , noop : nil , verbose : nil ,
dereference_root : true , remove_destination : nil )
fu_output_message " cp -r #{ preserve ? 'p' : '' } #{ remove_destination ? ' --remove-destination' : '' } #{ [ src , dest ] . flatten . join ' ' } " if verbose
return if noop
2005-05-26 02:41:32 +04:00
fu_each_src_dest ( src , dest ) do | s , d |
2016-03-01 05:26:44 +03:00
copy_entry s , d , preserve , dereference_root , remove_destination
2002-03-26 05:03:04 +03:00
end
end
2013-03-01 06:09:42 +04:00
module_function :cp_r
2002-03-26 05:03:04 +03:00
2004-08-09 01:35:11 +04:00
#
# Copies a file system entry +src+ to +dest+.
2005-05-26 02:41:32 +04:00
# If +src+ is a directory, this method copies its contents recursively.
# This method preserves file types, c.f. symlink, directory...
# (FIFO, device files and etc. are not supported yet)
2004-08-09 01:35:11 +04:00
#
# Both of +src+ and +dest+ must be a path name.
# +src+ must exist, +dest+ must not exist.
#
2014-04-17 05:03:55 +04:00
# If +preserve+ is true, this method preserves owner, group, and
# modified time. Permissions are copied regardless +preserve+.
2004-08-09 01:35:11 +04:00
#
2005-05-26 02:41:32 +04:00
# If +dereference_root+ is true, this method dereference tree root.
#
2006-03-03 15:24:53 +03:00
# If +remove_destination+ is true, this method removes each destination file before copy.
#
def copy_entry ( src , dest , preserve = false , dereference_root = false , remove_destination = false )
2019-08-24 00:52:12 +03:00
if dereference_root
src = File . realpath ( src )
end
Entry_ . new ( src , nil , false ) . wrap_traverse ( proc do | ent |
2005-05-26 02:41:32 +04:00
destent = Entry_ . new ( dest , ent . rel , false )
2017-10-20 10:03:11 +03:00
File . unlink destent . path if remove_destination && ( File . file? ( destent . path ) || File . symlink? ( destent . path ) )
2005-05-26 02:41:32 +04:00
ent . copy destent . path
2013-02-02 07:54:00 +04:00
end , proc do | ent |
destent = Entry_ . new ( dest , ent . rel , false )
2005-05-26 02:41:32 +04:00
ent . copy_metadata destent . path if preserve
2013-02-02 07:54:00 +04:00
end )
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :copy_entry
2002-03-26 05:03:04 +03:00
2003-02-21 14:57:31 +03:00
#
2004-08-09 01:35:11 +04:00
# Copies file contents of +src+ to +dest+.
# Both of +src+ and +dest+ must be a path name.
2003-02-21 14:57:31 +03:00
#
2004-08-09 01:35:11 +04:00
def copy_file ( src , dest , preserve = false , dereference = true )
2005-05-26 02:41:32 +04:00
ent = Entry_ . new ( src , nil , dereference )
ent . copy_file dest
ent . copy_metadata dest if preserve
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :copy_file
2002-03-26 05:03:04 +03:00
2003-02-21 14:57:31 +03:00
#
# Copies stream +src+ to +dest+.
2005-03-26 18:57:16 +03:00
# +src+ must respond to #read(n) and
# +dest+ must respond to #write(str).
2003-02-21 14:57:31 +03:00
#
2003-12-11 12:49:47 +03:00
def copy_stream ( src , dest )
2008-09-01 06:31:56 +04:00
IO . copy_stream ( src , dest )
2004-08-09 01:35:11 +04:00
end
2013-03-01 06:09:42 +04:00
module_function :copy_stream
2004-08-09 01:35:11 +04:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
2009-02-23 08:47:06 +03:00
# disk partition, the file is copied then the original file is removed.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.mv 'badname.rb', 'goodname.rb'
2019-03-10 14:09:59 +03:00
# FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
2009-03-06 06:56:38 +03:00
#
2017-03-08 23:41:38 +03:00
# FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
2019-03-10 14:09:59 +03:00
# FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def mv ( src , dest , force : nil , noop : nil , verbose : nil , secure : nil )
fu_output_message " mv #{ force ? ' -f' : '' } #{ [ src , dest ] . flatten . join ' ' } " if verbose
return if noop
2005-05-26 02:41:32 +04:00
fu_each_src_dest ( src , dest ) do | s , d |
destent = Entry_ . new ( d , nil , true )
2004-12-27 09:15:32 +03:00
begin
2005-05-26 02:41:32 +04:00
if destent . exist?
if destent . directory?
2015-04-01 03:59:14 +03:00
raise Errno :: EEXIST , d
2005-05-26 02:41:32 +04:00
end
2004-12-27 09:15:32 +03:00
end
2004-05-07 17:08:12 +04:00
begin
2004-12-27 09:15:32 +03:00
File . rename s , d
2019-03-11 12:52:31 +03:00
rescue Errno :: EXDEV ,
Errno :: EPERM # move from unencrypted to encrypted dir (ext4)
2004-08-09 01:35:11 +04:00
copy_entry s , d , true
2016-03-01 05:26:44 +03:00
if secure
remove_entry_secure s , force
2007-03-03 16:53:38 +03:00
else
2016-03-01 05:26:44 +03:00
remove_entry s , force
2007-03-03 16:53:38 +03:00
end
2002-03-26 05:03:04 +03:00
end
2004-12-27 09:15:32 +03:00
rescue SystemCallError
2016-03-01 05:26:44 +03:00
raise unless force
2002-03-26 05:03:04 +03:00
end
end
end
2013-03-01 06:09:42 +04:00
module_function :mv
2002-03-26 05:03:04 +03:00
alias move mv
2013-03-01 06:09:42 +04:00
module_function :move
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Remove file(s) specified in +list+. This method cannot remove directories.
2004-08-09 01:35:11 +04:00
# All StandardErrors are ignored when the :force option is set.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.rm %w( junk.txt dust.txt )
2003-02-06 12:56:13 +03:00
# FileUtils.rm Dir.glob('*.so')
2019-03-10 14:09:59 +03:00
# FileUtils.rm 'NotExistFile', force: true # never raises exception
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def rm ( list , force : nil , noop : nil , verbose : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
fu_output_message " rm #{ force ? ' -f' : '' } #{ list . join ' ' } " if verbose
return if noop
2002-03-26 05:03:04 +03:00
2005-05-21 21:40:24 +04:00
list . each do | path |
2016-03-01 05:26:44 +03:00
remove_file path , force
2002-03-26 05:03:04 +03:00
end
end
2013-03-01 06:09:42 +04:00
module_function :rm
2002-03-26 05:03:04 +03:00
alias remove rm
2013-03-01 06:09:42 +04:00
module_function :remove
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2005-05-26 02:41:32 +04:00
# Equivalent to
#
2019-03-10 14:09:59 +03:00
# FileUtils.rm(list, force: true)
2003-01-24 19:13:33 +03:00
#
2016-03-01 05:26:44 +03:00
def rm_f ( list , noop : nil , verbose : nil )
rm list , force : true , noop : noop , verbose : verbose
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :rm_f
2002-03-26 05:03:04 +03:00
alias safe_unlink rm_f
2013-03-01 06:09:42 +04:00
module_function :safe_unlink
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# remove files +list+[0] +list+[1]... If +list+[n] is a directory,
# removes its all contents recursively. This method ignores
# StandardError when :force option is set.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.rm_r Dir.glob('/tmp/*')
2019-03-10 14:09:59 +03:00
# FileUtils.rm_r 'some_dir', force: true
2005-05-21 21:40:24 +04:00
#
2005-05-26 15:33:16 +04:00
# WARNING: This method causes local vulnerability
2005-05-26 02:41:32 +04:00
# if one of parent directories or removing directory tree are world
2005-08-13 17:14:07 +04:00
# writable (including /tmp, whose permission is 1777), and the current
# process has strong privilege such as Unix super user (root), and the
# system has symbolic link. For secure removing, read the documentation
2019-03-10 14:09:59 +03:00
# of remove_entry_secure carefully, and set :secure option to true.
# Default is <tt>secure: false</tt>.
2005-05-25 14:50:14 +04:00
#
2019-03-10 14:09:59 +03:00
# NOTE: This method calls remove_entry_secure if :secure option is set.
# See also remove_entry_secure.
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def rm_r ( list , force : nil , noop : nil , verbose : nil , secure : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
fu_output_message " rm -r #{ force ? 'f' : '' } #{ list . join ' ' } " if verbose
return if noop
2005-05-21 21:40:24 +04:00
list . each do | path |
2016-03-01 05:26:44 +03:00
if secure
remove_entry_secure path , force
2005-05-21 21:40:24 +04:00
else
2016-03-01 05:26:44 +03:00
remove_entry path , force
2005-05-21 21:40:24 +04:00
end
end
end
2013-03-01 06:09:42 +04:00
module_function :rm_r
2005-05-21 21:40:24 +04:00
2009-03-06 06:56:38 +03:00
#
2005-05-26 02:41:32 +04:00
# Equivalent to
#
2019-03-10 14:09:59 +03:00
# FileUtils.rm_r(list, force: true)
2005-05-25 14:50:14 +04:00
#
2005-05-26 15:33:16 +04:00
# WARNING: This method causes local vulnerability.
2019-03-10 14:09:59 +03:00
# Read the documentation of rm_r first.
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def rm_rf ( list , noop : nil , verbose : nil , secure : nil )
rm_r list , force : true , noop : noop , verbose : verbose , secure : secure
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :rm_rf
2002-03-26 05:03:04 +03:00
2003-02-21 14:57:31 +03:00
alias rmtree rm_rf
2013-03-01 06:09:42 +04:00
module_function :rmtree
2003-02-21 14:57:31 +03:00
2005-05-26 02:41:32 +04:00
#
2005-05-26 15:33:16 +04:00
# This method removes a file system entry +path+. +path+ shall be a
# regular file, a directory, or something. If +path+ is a directory,
# remove it recursively. This method is required to avoid TOCTTOU
2019-03-10 14:09:59 +03:00
# (time-of-check-to-time-of-use) local security vulnerability of rm_r.
2005-08-13 17:17:23 +04:00
# #rm_r causes security hole when:
2005-05-26 15:33:16 +04:00
#
2017-03-01 13:17:42 +03:00
# * Parent directory is world writable (including /tmp).
# * Removing directory tree includes world writable directory.
# * The system has symbolic link.
2005-05-26 02:41:32 +04:00
#
2005-05-26 15:33:16 +04:00
# To avoid this security hole, this method applies special preprocess.
2005-05-26 02:41:32 +04:00
# If +path+ is a directory, this method chown(2) and chmod(2) all
2005-05-26 15:33:16 +04:00
# removing directories. This requires the current process is the
2005-08-13 17:06:04 +04:00
# owner of the removing whole directory tree, or is the super user (root).
2005-05-26 02:41:32 +04:00
#
2011-02-18 13:42:50 +03:00
# WARNING: You must ensure that *ALL* parent directories cannot be
# moved by other untrusted users. For example, parent directories
# should not be owned by untrusted users, and should not be world
# writable except when the sticky bit set.
2005-05-26 02:41:32 +04:00
#
2005-08-13 17:14:07 +04:00
# WARNING: Only the owner of the removing directory tree, or Unix super
# user (root) should invoke this method. Otherwise this method does not
# work.
2005-05-26 02:41:32 +04:00
#
# For details of this security vulnerability, see Perl's case:
#
2018-04-16 13:20:15 +03:00
# * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
# * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
2005-05-26 02:41:32 +04:00
#
# For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
#
def remove_entry_secure ( path , force = false )
2005-09-19 00:33:01 +04:00
unless fu_have_symlink?
remove_entry path , force
return
end
2005-08-13 17:06:04 +04:00
fullpath = File . expand_path ( path )
2005-09-19 00:33:01 +04:00
st = File . lstat ( fullpath )
unless st . directory?
File . unlink fullpath
return
end
# is a directory.
parent_st = File . stat ( File . dirname ( fullpath ) )
unless parent_st . world_writable?
2005-08-13 17:06:04 +04:00
remove_entry path , force
2005-05-26 15:33:16 +04:00
return
end
2005-09-19 00:33:01 +04:00
unless parent_st . sticky?
raise ArgumentError , " parent directory is world writable, FileUtils # remove_entry_secure does not work; abort: #{ path . inspect } (parent directory mode #{ '%o' % parent_st . mode } ) "
2005-08-13 17:06:04 +04:00
end
2018-05-15 08:53:18 +03:00
2005-09-19 00:33:01 +04:00
# freeze tree root
2005-08-13 17:06:04 +04:00
euid = Process . euid
2018-05-15 08:53:18 +03:00
dot_file = fullpath + " /. "
begin
File . open ( dot_file ) { | f |
unless fu_stat_identical_entry? ( st , f . stat )
# symlink (TOC-to-TOU attack?)
File . unlink fullpath
return
end
f . chown euid , - 1
f . chmod 0700
}
2019-01-31 17:02:30 +03:00
rescue Errno :: EISDIR # JRuby in non-native mode can't open files as dirs
2018-05-15 08:53:18 +03:00
File . lstat ( dot_file ) . tap { | fstat |
unless fu_stat_identical_entry? ( st , fstat )
# symlink (TOC-to-TOU attack?)
File . unlink fullpath
return
end
File . chown euid , - 1 , dot_file
File . chmod 0700 , dot_file
}
end
unless fu_stat_identical_entry? ( st , File . lstat ( fullpath ) )
# TOC-to-TOU attack?
File . unlink fullpath
return
end
2005-08-13 17:06:04 +04:00
# ---- tree root is frozen ----
2005-05-26 02:41:32 +04:00
root = Entry_ . new ( path )
root . preorder_traverse do | ent |
if ent . directory?
2005-09-19 00:33:01 +04:00
ent . chown euid , - 1
2005-05-26 02:41:32 +04:00
ent . chmod 0700
2002-03-26 05:03:04 +03:00
end
end
2005-05-26 02:41:32 +04:00
root . postorder_traverse do | ent |
begin
ent . remove
rescue
raise unless force
end
end
rescue
raise unless force
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :remove_entry_secure
2002-03-26 05:03:04 +03:00
2011-05-13 02:49:40 +04:00
def fu_have_symlink? #:nodoc:
2005-09-19 00:33:01 +04:00
File . symlink nil , nil
rescue NotImplementedError
return false
2012-03-12 15:09:11 +04:00
rescue TypeError
2005-09-19 00:33:01 +04:00
return true
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_have_symlink?
2005-09-19 00:33:01 +04:00
2005-09-19 00:59:29 +04:00
def fu_stat_identical_entry? ( a , b ) #:nodoc:
2005-08-13 17:06:04 +04:00
a . dev == b . dev and a . ino == b . ino
2005-05-26 02:41:32 +04:00
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_stat_identical_entry?
2005-05-26 02:41:32 +04:00
#
# This method removes a file system entry +path+.
# +path+ might be a regular file, a directory, or something.
# If +path+ is a directory, remove it recursively.
#
2019-03-10 14:09:59 +03:00
# See also remove_entry_secure.
2005-05-26 02:41:32 +04:00
#
def remove_entry ( path , force = false )
Entry_ . new ( path ) . postorder_traverse do | ent |
begin
ent . remove
rescue
raise unless force
2005-04-14 14:00:52 +04:00
end
2002-03-26 05:03:04 +03:00
end
2005-05-26 02:41:32 +04:00
rescue
raise unless force
end
2013-03-01 06:09:42 +04:00
module_function :remove_entry
2005-05-26 02:41:32 +04:00
#
# Removes a file +path+.
# This method ignores StandardError if +force+ is true.
#
def remove_file ( path , force = false )
Entry_ . new ( path ) . remove_file
rescue
raise unless force
end
2013-03-01 06:09:42 +04:00
module_function :remove_file
2005-05-26 02:41:32 +04:00
#
# Removes a directory +dir+ and its contents recursively.
# This method ignores StandardError if +force+ is true.
#
def remove_dir ( path , force = false )
remove_entry path , force # FIXME?? check if it is a directory
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :remove_dir
2002-03-26 05:03:04 +03:00
2003-01-24 19:13:33 +03:00
#
2017-03-01 11:54:37 +03:00
# Returns true if the contents of a file +a+ and a file +b+ are identical.
2009-03-06 06:56:38 +03:00
#
2017-03-01 11:54:37 +03:00
# FileUtils.compare_file('somefile', 'somefile') #=> true
# FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false
2003-01-24 19:13:33 +03:00
#
2003-12-11 12:49:47 +03:00
def compare_file ( a , b )
2003-02-21 14:57:31 +03:00
return false unless File . size ( a ) == File . size ( b )
File . open ( a , 'rb' ) { | fa |
2003-12-12 13:33:24 +03:00
File . open ( b , 'rb' ) { | fb |
2003-02-21 14:57:31 +03:00
return compare_stream ( fa , fb )
2003-12-12 13:33:24 +03:00
}
}
2003-02-21 14:57:31 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :compare_file
2002-03-26 05:03:04 +03:00
2003-02-21 14:57:31 +03:00
alias identical? compare_file
alias cmp compare_file
2013-03-01 06:09:42 +04:00
module_function :identical?
module_function :cmp
2002-03-26 05:03:04 +03:00
2003-02-21 14:57:31 +03:00
#
# Returns true if the contents of a stream +a+ and +b+ are identical.
#
2003-12-11 12:49:47 +03:00
def compare_stream ( a , b )
2003-02-21 14:57:31 +03:00
bsize = fu_stream_blksize ( a , b )
2018-05-15 08:53:18 +03:00
if RUBY_VERSION > " 2.4 "
sa = String . new ( capacity : bsize )
sb = String . new ( capacity : bsize )
else
sa = String . new
sb = String . new
end
2012-11-04 06:56:11 +04:00
begin
a . read ( bsize , sa )
b . read ( bsize , sb )
return true if sa . empty? && sb . empty?
end while sa == sb
2002-03-26 05:03:04 +03:00
false
end
2013-03-01 06:09:42 +04:00
module_function :compare_stream
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# If +src+ is not same as +dest+, copies it and changes the permission
2003-02-06 12:56:13 +03:00
# mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
2006-03-05 12:41:33 +03:00
# This method removes destination before copy.
2009-03-06 06:56:38 +03:00
#
2019-03-10 14:09:59 +03:00
# FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true
# FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
2009-03-06 06:56:38 +03:00
#
2016-06-27 10:55:17 +03:00
def install ( src , dest , mode : nil , owner : nil , group : nil , preserve : nil ,
noop : nil , verbose : nil )
if verbose
msg = + " install -c "
msg << ' -p' if preserve
2016-06-27 10:58:34 +03:00
msg << ' -m ' << mode_to_s ( mode ) if mode
2016-06-27 10:55:17 +03:00
msg << " -o #{ owner } " if owner
msg << " -g #{ group } " if group
msg << ' ' << [ src , dest ] . flatten . join ( ' ' )
fu_output_message msg
end
2016-03-01 05:26:44 +03:00
return if noop
2016-06-27 10:55:17 +03:00
uid = fu_get_uid ( owner )
gid = fu_get_gid ( group )
2014-07-11 11:51:19 +04:00
fu_each_src_dest ( src , dest ) do | s , d |
st = File . stat ( s )
2005-05-26 02:41:32 +04:00
unless File . exist? ( d ) and compare_file ( s , d )
2003-12-12 13:33:24 +03:00
remove_file d , true
copy_file s , d
2016-03-01 05:26:44 +03:00
File . utime st . atime , st . mtime , d if preserve
2016-06-27 10:58:34 +03:00
File . chmod fu_mode ( mode , st ) , d if mode
2016-06-27 10:55:17 +03:00
File . chown uid , gid , d if uid or gid
2002-03-26 05:03:04 +03:00
end
end
end
2013-03-01 06:09:42 +04:00
module_function :install
2002-03-26 05:03:04 +03:00
2011-05-13 02:49:40 +04:00
def user_mask ( target ) #:nodoc:
2013-07-04 21:22:08 +04:00
target . each_char . inject ( 0 ) do | mask , chr |
case chr
when " u "
mask | 04700
when " g "
mask | 02070
when " o "
mask | 01007
when " a "
mask | 07777
else
raise ArgumentError , " invalid `who' symbol in file mode: #{ chr } "
2011-05-01 20:16:05 +04:00
end
end
end
2013-03-01 06:09:42 +04:00
private_module_function :user_mask
2011-05-01 20:16:05 +04:00
2017-03-01 13:17:42 +03:00
def apply_mask ( mode , user_mask , op , mode_mask ) #:nodoc:
2013-07-04 21:22:08 +04:00
case op
when '='
( mode & ~ user_mask ) | ( user_mask & mode_mask )
when '+'
mode | ( user_mask & mode_mask )
when '-'
mode & ~ ( user_mask & mode_mask )
2011-05-01 20:16:05 +04:00
end
end
2013-07-04 21:22:08 +04:00
private_module_function :apply_mask
2011-05-01 20:16:05 +04:00
2013-07-04 21:22:08 +04:00
def symbolic_modes_to_i ( mode_sym , path ) #:nodoc:
2019-10-02 19:39:02 +03:00
path = File . stat ( path ) unless File :: Stat === path
mode = path . mode
2016-06-27 10:58:34 +03:00
mode_sym . split ( / , / ) . inject ( mode & 07777 ) do | current_mode , clause |
2013-07-04 21:22:08 +04:00
target , * actions = clause . split ( / ([=+-]) / )
raise ArgumentError , " invalid file mode: #{ mode_sym } " if actions . empty?
target = 'a' if target . empty?
2011-05-01 20:16:05 +04:00
user_mask = user_mask ( target )
2013-07-04 21:22:08 +04:00
actions . each_slice ( 2 ) do | op , perm |
need_apply = op == '='
mode_mask = ( perm || '' ) . each_char . inject ( 0 ) do | mask , chr |
case chr
when " r "
mask | 0444
when " w "
mask | 0222
when " x "
mask | 0111
when " X "
2019-10-02 19:39:02 +03:00
if path . directory?
2013-07-04 21:22:08 +04:00
mask | 0111
else
mask
end
when " s "
mask | 06000
when " t "
mask | 01000
when " u " , " g " , " o "
if mask . nonzero?
current_mode = apply_mask ( current_mode , user_mask , op , mask )
end
need_apply = false
copy_mask = user_mask ( chr )
( current_mode & copy_mask ) / ( copy_mask & 0111 ) * ( user_mask & 0111 )
else
raise ArgumentError , " invalid `perm' symbol in file mode: #{ chr } "
end
end
if mode_mask . nonzero? || need_apply
current_mode = apply_mask ( current_mode , user_mask , op , mode_mask )
end
2011-05-01 20:16:05 +04:00
end
2013-07-04 21:22:08 +04:00
current_mode
2011-05-01 20:16:05 +04:00
end
end
2013-03-01 06:09:42 +04:00
private_module_function :symbolic_modes_to_i
2011-05-01 20:16:05 +04:00
2011-05-13 02:49:40 +04:00
def fu_mode ( mode , path ) #:nodoc:
2011-05-01 20:16:05 +04:00
mode . is_a? ( String ) ? symbolic_modes_to_i ( mode , path ) : mode
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_mode
2012-02-17 21:59:47 +04:00
2013-02-02 07:07:17 +04:00
def mode_to_s ( mode ) #:nodoc:
mode . is_a? ( String ) ? mode : " %o " % mode
end
2013-07-09 11:02:04 +04:00
private_module_function :mode_to_s
2013-02-02 07:07:17 +04:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Changes permission bits on the named files (in +list+) to the bit pattern
# represented by +mode+.
2009-03-06 06:56:38 +03:00
#
2011-05-01 20:16:05 +04:00
# +mode+ is the symbolic and absolute mode can be used.
#
# Absolute mode is
2003-01-24 19:13:33 +03:00
# FileUtils.chmod 0755, 'somecommand'
2003-05-02 10:07:54 +04:00
# FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
2019-03-10 14:09:59 +03:00
# FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
2009-03-06 06:56:38 +03:00
#
2011-05-01 20:16:05 +04:00
# Symbolic mode is
# FileUtils.chmod "u=wrx,go=rx", 'somecommand'
# FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
2019-03-10 14:09:59 +03:00
# FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
2011-05-01 20:16:05 +04:00
#
2012-11-14 08:26:20 +04:00
# "a" :: is user, group, other mask.
# "u" :: is user's mask.
# "g" :: is group's mask.
# "o" :: is other's mask.
# "w" :: is write permission.
# "r" :: is read permission.
# "x" :: is execute permission.
# "X" ::
# is execute permission for directories only, must be used in conjunction with "+"
# "s" :: is uid, gid.
# "t" :: is sticky bit.
# "+" :: is added to a class given the specified mode.
# "-" :: Is removed from a given class given mode.
# "=" :: Is the exact nature of the class will be given a specified mode.
2011-05-01 20:16:05 +04:00
2016-03-01 05:26:44 +03:00
def chmod ( mode , list , noop : nil , verbose : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
fu_output_message sprintf ( 'chmod %s %s' , mode_to_s ( mode ) , list . join ( ' ' ) ) if verbose
return if noop
2005-05-26 02:41:32 +04:00
list . each do | path |
2011-05-01 20:16:05 +04:00
Entry_ . new ( path ) . chmod ( fu_mode ( mode , path ) )
2005-05-26 02:41:32 +04:00
end
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :chmod
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2005-05-21 21:40:24 +04:00
# Changes permission bits on the named files (in +list+)
# to the bit pattern represented by +mode+.
2009-03-06 06:56:38 +03:00
#
2005-05-21 21:40:24 +04:00
# FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
2011-05-01 20:16:05 +04:00
# FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def chmod_R ( mode , list , noop : nil , verbose : nil , force : nil )
2005-05-21 21:40:24 +04:00
list = fu_list ( list )
2013-02-02 07:07:17 +04:00
fu_output_message sprintf ( 'chmod -R%s %s %s' ,
2016-03-01 05:26:44 +03:00
( force ? 'f' : '' ) ,
mode_to_s ( mode ) , list . join ( ' ' ) ) if verbose
return if noop
2005-05-26 02:41:32 +04:00
list . each do | root |
Entry_ . new ( root ) . traverse do | ent |
begin
2011-05-01 20:16:05 +04:00
ent . chmod ( fu_mode ( mode , ent . path ) )
2005-05-26 02:41:32 +04:00
rescue
2016-03-01 05:26:44 +03:00
raise unless force
2005-05-21 21:40:24 +04:00
end
end
end
end
2013-03-01 06:09:42 +04:00
module_function :chmod_R
2005-05-21 21:40:24 +04:00
2009-03-06 06:56:38 +03:00
#
2004-12-04 14:46:24 +03:00
# Changes owner and group on the named files (in +list+)
2004-12-04 15:29:55 +03:00
# to the user +user+ and the group +group+. +user+ and +group+
2004-12-04 14:46:24 +03:00
# may be an ID (Integer/String) or a name (String).
# If +user+ or +group+ is nil, this method does not change
# the attribute.
2009-03-06 06:56:38 +03:00
#
2004-12-04 14:46:24 +03:00
# FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
2019-03-10 14:09:59 +03:00
# FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def chown ( user , group , list , noop : nil , verbose : nil )
2004-12-04 14:46:24 +03:00
list = fu_list ( list )
2013-07-09 11:01:59 +04:00
fu_output_message sprintf ( 'chown %s %s' ,
( group ? " #{ user } : #{ group } " : user || ':' ) ,
2016-03-01 05:26:44 +03:00
list . join ( ' ' ) ) if verbose
return if noop
2005-05-26 02:41:32 +04:00
uid = fu_get_uid ( user )
gid = fu_get_gid ( group )
list . each do | path |
Entry_ . new ( path ) . chown uid , gid
end
2004-12-04 14:46:24 +03:00
end
2013-03-01 06:09:42 +04:00
module_function :chown
2004-12-04 14:46:24 +03:00
2009-03-06 06:56:38 +03:00
#
2004-12-04 14:46:24 +03:00
# Changes owner and group on the named files (in +list+)
2004-12-04 15:29:55 +03:00
# to the user +user+ and the group +group+ recursively.
# +user+ and +group+ may be an ID (Integer/String) or
# a name (String). If +user+ or +group+ is nil, this
# method does not change the attribute.
2009-03-06 06:56:38 +03:00
#
2004-12-04 15:29:55 +03:00
# FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
2019-03-10 14:09:59 +03:00
# FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def chown_R ( user , group , list , noop : nil , verbose : nil , force : nil )
2004-12-04 14:46:24 +03:00
list = fu_list ( list )
2013-07-09 11:01:59 +04:00
fu_output_message sprintf ( 'chown -R%s %s %s' ,
2016-03-01 05:26:44 +03:00
( force ? 'f' : '' ) ,
2013-07-09 11:01:59 +04:00
( group ? " #{ user } : #{ group } " : user || ':' ) ,
2016-03-01 05:26:44 +03:00
list . join ( ' ' ) ) if verbose
return if noop
2004-12-04 14:46:24 +03:00
uid = fu_get_uid ( user )
gid = fu_get_gid ( group )
2005-05-26 02:41:32 +04:00
list . each do | root |
Entry_ . new ( root ) . traverse do | ent |
begin
ent . chown uid , gid
rescue
2016-03-01 05:26:44 +03:00
raise unless force
2005-05-21 21:40:24 +04:00
end
2004-12-04 14:46:24 +03:00
end
end
end
2013-03-01 06:09:42 +04:00
module_function :chown_R
2004-12-04 14:46:24 +03:00
2014-04-29 07:51:13 +04:00
def fu_get_uid ( user ) #:nodoc:
return nil unless user
case user
when Integer
user
when / \ A \ d+ \ z /
user . to_i
else
2019-03-27 11:26:09 +03:00
require 'etc'
2014-04-29 07:51:13 +04:00
Etc . getpwnam ( user ) ? Etc . getpwnam ( user ) . uid : nil
2004-12-04 14:46:24 +03:00
end
2014-04-29 07:51:13 +04:00
end
private_module_function :fu_get_uid
def fu_get_gid ( group ) #:nodoc:
return nil unless group
case group
when Integer
group
when / \ A \ d+ \ z /
group . to_i
else
2019-03-27 11:26:09 +03:00
require 'etc'
2014-04-29 07:51:13 +04:00
Etc . getgrnam ( group ) ? Etc . getgrnam ( group ) . gid : nil
2004-12-04 14:46:24 +03:00
end
end
2014-04-29 07:51:13 +04:00
private_module_function :fu_get_gid
2004-12-04 14:46:24 +03:00
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# Updates modification time (mtime) and access time (atime) of file(s) in
# +list+. Files are created if they don't exist.
2009-03-06 06:56:38 +03:00
#
2003-01-24 19:13:33 +03:00
# FileUtils.touch 'timestamp'
# FileUtils.touch Dir.glob('*.c'); system 'make'
2009-03-06 06:56:38 +03:00
#
2016-03-01 05:26:44 +03:00
def touch ( list , noop : nil , verbose : nil , mtime : nil , nocreate : nil )
2002-03-26 05:03:04 +03:00
list = fu_list ( list )
2016-03-01 05:26:44 +03:00
t = mtime
if verbose
2011-04-15 10:05:25 +04:00
fu_output_message " touch #{ nocreate ? '-c ' : '' } #{ t ? t . strftime ( '-t %Y%m%d%H%M.%S ' ) : '' } #{ list . join ' ' } "
2007-03-03 16:37:02 +03:00
end
2016-03-01 05:26:44 +03:00
return if noop
2005-05-21 21:40:24 +04:00
list . each do | path |
2007-03-03 16:37:02 +03:00
created = nocreate
2002-03-26 05:03:04 +03:00
begin
2005-05-21 21:40:24 +04:00
File . utime ( t , t , path )
2002-03-26 05:03:04 +03:00
rescue Errno :: ENOENT
2007-03-03 16:37:02 +03:00
raise if created
2005-05-21 21:40:24 +04:00
File . open ( path , 'a' ) {
2003-12-12 13:33:24 +03:00
;
2003-05-02 10:07:54 +04:00
}
2007-03-03 16:37:02 +03:00
created = true
retry if t
2002-03-26 05:03:04 +03:00
end
end
end
2013-03-01 06:09:42 +04:00
module_function :touch
2002-03-26 05:03:04 +03:00
2013-03-01 06:09:42 +04:00
private
2002-03-26 05:03:04 +03:00
2005-05-26 02:41:32 +04:00
module StreamUtils_
private
2018-05-19 03:22:39 +03:00
case ( defined? ( :: RbConfig ) ? :: RbConfig :: CONFIG [ 'host_os' ] : :: RUBY_PLATFORM )
when / mswin|mingw /
def fu_windows? ; true end
else
def fu_windows? ; false end
2005-05-26 02:41:32 +04:00
end
2008-09-01 06:31:56 +04:00
def fu_copy_stream0 ( src , dest , blksize = nil ) #:nodoc:
IO . copy_stream ( src , dest )
2005-05-21 21:40:24 +04:00
end
2005-05-26 02:41:32 +04:00
def fu_stream_blksize ( * streams )
streams . each do | s |
next unless s . respond_to? ( :stat )
size = fu_blksize ( s . stat )
return size if size
2005-05-21 21:40:24 +04:00
end
2005-05-26 02:41:32 +04:00
fu_default_blksize ( )
2005-05-21 21:40:24 +04:00
end
2005-05-26 02:41:32 +04:00
def fu_blksize ( st )
s = st . blksize
return nil unless s
return nil if s == 0
s
end
2005-05-21 21:40:24 +04:00
2005-05-26 02:41:32 +04:00
def fu_default_blksize
1024
2002-03-26 05:03:04 +03:00
end
end
2005-05-26 02:41:32 +04:00
include StreamUtils_
2005-09-19 00:59:29 +04:00
extend StreamUtils_
2005-05-26 02:41:32 +04:00
class Entry_ #:nodoc: internal use only
include StreamUtils_
def initialize ( a , b = nil , deref = false )
@prefix = @rel = @path = nil
if b
@prefix = a
@rel = b
else
@path = a
end
@deref = deref
@stat = nil
@lstat = nil
end
def inspect
" \# < #{ self . class } #{ path ( ) } > "
end
def path
if @path
2005-09-24 03:30:09 +04:00
File . path ( @path )
2005-05-26 02:41:32 +04:00
else
join ( @prefix , @rel )
end
end
def prefix
@prefix || @path
end
def rel
@rel
end
def dereference?
@deref
end
def exist?
2014-07-11 11:51:19 +04:00
begin
lstat
true
rescue Errno :: ENOENT
false
end
2005-05-26 02:41:32 +04:00
end
def file?
s = lstat!
s and s . file?
end
def directory?
s = lstat!
s and s . directory?
end
def symlink?
s = lstat!
s and s . symlink?
end
def chardev?
s = lstat!
s and s . chardev?
end
def blockdev?
s = lstat!
s and s . blockdev?
end
def socket?
s = lstat!
s and s . socket?
end
def pipe?
s = lstat!
s and s . pipe?
end
S_IF_DOOR = 0xD000
def door?
s = lstat!
s and ( s . mode & 0xF000 == S_IF_DOOR )
end
def entries
2010-05-17 04:41:56 +04:00
opts = { }
2020-06-22 12:15:21 +03:00
opts [ :encoding ] = fu_windows? ? :: Encoding :: UTF_8 : path . encoding
2019-03-27 11:26:09 +03:00
files = if Dir . respond_to? ( :children )
Make rb_scan_args handle keywords more similar to Ruby methods (#2460)
Cfuncs that use rb_scan_args with the : entry suffer similar keyword
argument separation issues that Ruby methods suffer if the cfuncs
accept optional or variable arguments.
This makes the following changes to : handling.
* Treats as **kw, prompting keyword argument separation warnings
if called with a positional hash.
* Do not look for an option hash if empty keywords are provided.
For backwards compatibility, treat an empty keyword splat as a empty
mandatory positional hash argument, but emit a a warning, as this
behavior will be removed in Ruby 3. The argument number check
needs to be moved lower so it can correctly handle an empty
positional argument being added.
* If the last argument is nil and it is necessary to treat it as an option
hash in order to make sure all arguments are processed, continue to
treat the last argument as the option hash. Emit a warning in this case,
as this behavior will be removed in Ruby 3.
* If splitting the keyword hash into two hashes, issue a warning, as we
will not be splitting hashes in Ruby 3.
* If the keyword argument is required to fill a mandatory positional
argument, continue to do so, but emit a warning as this behavior will
be going away in Ruby 3.
* If keyword arguments are provided and the last argument is not a hash,
that indicates something wrong. This can happen if a cfunc is calling
rb_scan_args multiple times, and providing arguments that were not
passed to it from Ruby. Callers need to switch to the new
rb_scan_args_kw function, which allows passing of whether keywords
were provided.
This commit fixes all warnings caused by the changes above.
It switches some function calls to *_kw versions with appropriate
kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS
is used. If creating new arguments, RB_PASS_KEYWORDS is used if
the last argument is a hash to be treated as keywords.
In open_key_args in io.c, use rb_scan_args_kw.
In this case, the arguments provided come from another C
function, not Ruby. The last argument may or may not be a hash,
so we can't set keyword argument mode. However, if it is a
hash, we don't want to warn when treating it as keywords.
In Ruby files, make sure to appropriately use keyword splats
or literal keywords when calling Cfuncs that now issue keyword
argument separation warnings through rb_scan_args. Also, make
sure not to pass nil in place of an option hash.
Work around Kernel#warn warnings due to problems in the Rubygems
override of the method. There is an open pull request to fix
these issues in Rubygems, but part of the Rubygems tests for
their override fail on ruby-head due to rb_scan_args not
recognizing empty keyword splats, which this commit fixes.
Implementation wise, adding rb_scan_args_kw is kind of a pain,
because rb_scan_args takes a variable number of arguments.
In order to not duplicate all the code, the function internals need
to be split into two functions taking a va_list, and to avoid passing
in a ton of arguments, a single struct argument is used to handle
the variables previously local to the function.
2019-09-25 21:18:49 +03:00
Dir . children ( path , ** opts )
2019-03-27 11:26:09 +03:00
else
Make rb_scan_args handle keywords more similar to Ruby methods (#2460)
Cfuncs that use rb_scan_args with the : entry suffer similar keyword
argument separation issues that Ruby methods suffer if the cfuncs
accept optional or variable arguments.
This makes the following changes to : handling.
* Treats as **kw, prompting keyword argument separation warnings
if called with a positional hash.
* Do not look for an option hash if empty keywords are provided.
For backwards compatibility, treat an empty keyword splat as a empty
mandatory positional hash argument, but emit a a warning, as this
behavior will be removed in Ruby 3. The argument number check
needs to be moved lower so it can correctly handle an empty
positional argument being added.
* If the last argument is nil and it is necessary to treat it as an option
hash in order to make sure all arguments are processed, continue to
treat the last argument as the option hash. Emit a warning in this case,
as this behavior will be removed in Ruby 3.
* If splitting the keyword hash into two hashes, issue a warning, as we
will not be splitting hashes in Ruby 3.
* If the keyword argument is required to fill a mandatory positional
argument, continue to do so, but emit a warning as this behavior will
be going away in Ruby 3.
* If keyword arguments are provided and the last argument is not a hash,
that indicates something wrong. This can happen if a cfunc is calling
rb_scan_args multiple times, and providing arguments that were not
passed to it from Ruby. Callers need to switch to the new
rb_scan_args_kw function, which allows passing of whether keywords
were provided.
This commit fixes all warnings caused by the changes above.
It switches some function calls to *_kw versions with appropriate
kw_splat flags. If delegating arguments, RB_PASS_CALLED_KEYWORDS
is used. If creating new arguments, RB_PASS_KEYWORDS is used if
the last argument is a hash to be treated as keywords.
In open_key_args in io.c, use rb_scan_args_kw.
In this case, the arguments provided come from another C
function, not Ruby. The last argument may or may not be a hash,
so we can't set keyword argument mode. However, if it is a
hash, we don't want to warn when treating it as keywords.
In Ruby files, make sure to appropriately use keyword splats
or literal keywords when calling Cfuncs that now issue keyword
argument separation warnings through rb_scan_args. Also, make
sure not to pass nil in place of an option hash.
Work around Kernel#warn warnings due to problems in the Rubygems
override of the method. There is an open pull request to fix
these issues in Rubygems, but part of the Rubygems tests for
their override fail on ruby-head due to rb_scan_args not
recognizing empty keyword splats, which this commit fixes.
Implementation wise, adding rb_scan_args_kw is kind of a pain,
because rb_scan_args takes a variable number of arguments.
In order to not duplicate all the code, the function internals need
to be split into two functions taking a va_list, and to avoid passing
in a ton of arguments, a single struct argument is used to handle
the variables previously local to the function.
2019-09-25 21:18:49 +03:00
Dir . entries ( path ( ) , ** opts )
2019-03-27 11:26:09 +03:00
. reject { | n | n == '.' or n == '..' }
end
2019-10-18 19:36:11 +03:00
untaint = RUBY_VERSION < '2.7'
files . map { | n | Entry_ . new ( prefix ( ) , join ( rel ( ) , untaint ? n . untaint : n ) ) }
2005-05-26 02:41:32 +04:00
end
def stat
return @stat if @stat
if lstat ( ) and lstat ( ) . symlink?
@stat = File . stat ( path ( ) )
else
@stat = lstat ( )
end
@stat
end
def stat!
return @stat if @stat
if lstat! and lstat! . symlink?
@stat = File . stat ( path ( ) )
else
@stat = lstat!
end
@stat
rescue SystemCallError
nil
end
def lstat
if dereference?
@lstat || = File . stat ( path ( ) )
else
@lstat || = File . lstat ( path ( ) )
end
end
def lstat!
lstat ( )
rescue SystemCallError
nil
end
def chmod ( mode )
if symlink?
File . lchmod mode , path ( ) if have_lchmod?
else
File . chmod mode , path ( )
end
2020-01-23 09:33:42 +03:00
rescue Errno :: EOPNOTSUPP
2005-05-26 02:41:32 +04:00
end
def chown ( uid , gid )
if symlink?
File . lchown uid , gid , path ( ) if have_lchown?
else
File . chown uid , gid , path ( )
end
end
2018-03-13 11:18:03 +03:00
def link ( dest )
case
when directory?
if ! File . exist? ( dest ) and descendant_directory? ( dest , path )
raise ArgumentError , " cannot link directory %s to itself %s " % [ path , dest ]
end
begin
Dir . mkdir dest
rescue
raise unless File . directory? ( dest )
end
else
File . link path ( ) , dest
end
end
2005-05-26 02:41:32 +04:00
def copy ( dest )
2016-11-04 04:35:49 +03:00
lstat
2005-05-26 02:41:32 +04:00
case
when file?
copy_file dest
when directory?
2014-08-12 07:21:57 +04:00
if ! File . exist? ( dest ) and descendant_directory? ( dest , path )
2007-09-06 17:59:37 +04:00
raise ArgumentError , " cannot copy directory %s to itself %s " % [ path , dest ]
end
2005-05-26 02:41:32 +04:00
begin
Dir . mkdir dest
rescue
raise unless File . directory? ( dest )
end
when symlink?
File . symlink File . readlink ( path ( ) ) , dest
2019-07-10 06:41:51 +03:00
when chardev? , blockdev?
raise " cannot handle device file "
2005-05-26 02:41:32 +04:00
when socket?
2019-07-10 06:41:51 +03:00
begin
require 'socket'
rescue LoadError
raise " cannot handle socket "
else
raise " cannot handle socket " unless defined? ( UNIXServer )
end
UNIXServer . new ( dest ) . close
File . chmod lstat ( ) . mode , dest
2005-05-26 02:41:32 +04:00
when pipe?
raise " cannot handle FIFO " unless File . respond_to? ( :mkfifo )
2019-07-10 06:41:51 +03:00
File . mkfifo dest , lstat ( ) . mode
2005-05-26 02:41:32 +04:00
when door?
raise " cannot handle door: #{ path ( ) } "
else
raise " unknown file type: #{ path ( ) } "
end
end
def copy_file ( dest )
2009-09-09 17:02:04 +04:00
File . open ( path ( ) ) do | s |
2011-03-18 02:48:13 +03:00
File . open ( dest , 'wb' , s . stat . mode ) do | f |
2009-09-09 17:02:04 +04:00
IO . copy_stream ( s , f )
end
2009-06-21 05:47:25 +04:00
end
2005-05-26 02:41:32 +04:00
end
def copy_metadata ( path )
st = lstat ( )
2012-04-30 18:37:54 +04:00
if ! st . symlink?
File . utime st . atime , st . mtime , path
end
2016-06-27 04:24:46 +03:00
mode = st . mode
2005-05-26 02:41:32 +04:00
begin
2012-04-30 18:37:54 +04:00
if st . symlink?
begin
File . lchown st . uid , st . gid , path
rescue NotImplementedError
end
else
File . chown st . uid , st . gid , path
end
2016-06-28 16:14:48 +03:00
rescue Errno :: EPERM , Errno :: EACCES
2005-05-26 02:41:32 +04:00
# clear setuid/setgid
2016-06-27 04:24:46 +03:00
mode & = 01777
end
if st . symlink?
begin
File . lchmod mode , path
2020-01-23 09:33:42 +03:00
rescue NotImplementedError , Errno :: EOPNOTSUPP
2012-04-30 18:37:54 +04:00
end
2005-05-26 02:41:32 +04:00
else
2016-06-27 04:24:46 +03:00
File . chmod mode , path
2005-05-26 02:41:32 +04:00
end
end
def remove
if directory?
remove_dir1
else
remove_file
end
end
def remove_dir1
platform_support {
2012-02-25 12:08:55 +04:00
Dir . rmdir path ( ) . chomp ( ?/ )
2005-05-26 02:41:32 +04:00
}
end
def remove_file
platform_support {
File . unlink path
}
end
def platform_support
return yield unless fu_windows?
first_time_p = true
begin
yield
rescue Errno :: ENOENT
raise
rescue = > err
if first_time_p
first_time_p = false
begin
File . chmod 0700 , path ( ) # Windows does not have symlink
retry
rescue SystemCallError
end
end
raise err
end
end
def preorder_traverse
stack = [ self ]
while ent = stack . pop
yield ent
stack . concat ent . entries . reverse if ent . directory?
end
end
alias traverse preorder_traverse
def postorder_traverse
if directory?
entries ( ) . each do | ent |
ent . postorder_traverse do | e |
yield e
end
end
end
2014-08-12 08:36:31 +04:00
ensure
2005-05-26 02:41:32 +04:00
yield self
end
2013-02-02 07:54:00 +04:00
def wrap_traverse ( pre , post )
pre . call self
if directory?
entries . each do | ent |
ent . wrap_traverse pre , post
end
end
post . call self
end
2013-03-01 06:09:42 +04:00
private
2005-05-26 02:41:32 +04:00
2019-08-24 15:19:31 +03:00
@@fileutils_rb_have_lchmod = nil
2005-05-26 02:41:32 +04:00
def have_lchmod?
# This is not MT-safe, but it does not matter.
2019-08-24 15:19:31 +03:00
if @@fileutils_rb_have_lchmod == nil
@@fileutils_rb_have_lchmod = check_have_lchmod?
2005-05-26 02:41:32 +04:00
end
2019-08-24 15:19:31 +03:00
@@fileutils_rb_have_lchmod
2005-05-26 02:41:32 +04:00
end
def check_have_lchmod?
return false unless File . respond_to? ( :lchmod )
File . lchmod 0
return true
rescue NotImplementedError
return false
end
2019-08-24 15:19:31 +03:00
@@fileutils_rb_have_lchown = nil
2005-05-26 02:41:32 +04:00
def have_lchown?
# This is not MT-safe, but it does not matter.
2019-08-24 15:19:31 +03:00
if @@fileutils_rb_have_lchown == nil
@@fileutils_rb_have_lchown = check_have_lchown?
2005-05-26 02:41:32 +04:00
end
2019-08-24 15:19:31 +03:00
@@fileutils_rb_have_lchown
2005-05-26 02:41:32 +04:00
end
def check_have_lchown?
return false unless File . respond_to? ( :lchown )
File . lchown nil , nil
return true
rescue NotImplementedError
return false
end
def join ( dir , base )
return File . path ( dir ) if not base or base == '.'
return File . path ( base ) if not dir or dir == '.'
2020-06-22 12:34:24 +03:00
begin
File . join ( dir , base )
rescue EncodingError
if fu_windows?
File . join ( dir . encode ( :: Encoding :: UTF_8 ) , base . encode ( :: Encoding :: UTF_8 ) )
else
raise
end
end
2005-05-26 02:41:32 +04:00
end
2010-07-20 07:27:59 +04:00
if File :: ALT_SEPARATOR
2017-01-07 05:14:07 +03:00
DIRECTORY_TERM = " (?=[/ #{ Regexp . quote ( File :: ALT_SEPARATOR ) } ]| \\ z) "
2010-07-20 07:27:59 +04:00
else
2017-01-07 05:14:07 +03:00
DIRECTORY_TERM = " (?=/| \\ z) "
2010-07-20 07:27:59 +04:00
end
2014-08-12 07:21:57 +04:00
def descendant_directory? ( descendant , ascendant )
2016-09-11 12:09:43 +03:00
if File :: FNM_SYSCASE . nonzero?
File . expand_path ( File . dirname ( descendant ) ) . casecmp ( File . expand_path ( ascendant ) ) == 0
else
File . expand_path ( File . dirname ( descendant ) ) == File . expand_path ( ascendant )
end
2010-07-20 07:27:59 +04:00
end
2005-05-26 02:41:32 +04:00
end # class Entry_
2005-09-19 00:59:29 +04:00
def fu_list ( arg ) #:nodoc:
2004-05-07 16:04:19 +04:00
[ arg ] . flatten . map { | path | File . path ( path ) }
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_list
2002-03-26 05:03:04 +03:00
2005-09-19 00:59:29 +04:00
def fu_each_src_dest ( src , dest ) #:nodoc:
2003-11-18 08:09:20 +03:00
fu_each_src_dest0 ( src , dest ) do | s , d |
raise ArgumentError , " same file: #{ s } and #{ d } " if fu_same? ( s , d )
2014-07-11 11:51:19 +04:00
yield s , d
2003-11-18 08:09:20 +03:00
end
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_each_src_dest
2003-11-18 08:09:20 +03:00
2005-09-19 00:59:29 +04:00
def fu_each_src_dest0 ( src , dest ) #:nodoc:
2007-09-06 17:59:37 +04:00
if tmp = Array . try_convert ( src )
tmp . each do | s |
2004-04-07 06:51:05 +04:00
s = File . path ( s )
yield s , File . join ( dest , File . basename ( s ) )
2002-03-26 05:03:04 +03:00
end
else
2004-04-07 06:51:05 +04:00
src = File . path ( src )
2003-12-12 13:33:24 +03:00
if File . directory? ( dest )
2004-04-07 06:51:05 +04:00
yield src , File . join ( dest , File . basename ( src ) )
2003-12-12 13:33:24 +03:00
else
2004-04-07 06:51:05 +04:00
yield src , File . path ( dest )
2003-12-12 13:33:24 +03:00
end
2002-03-26 05:03:04 +03:00
end
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_each_src_dest0
2002-03-26 05:03:04 +03:00
2005-09-19 00:59:29 +04:00
def fu_same? ( a , b ) #:nodoc:
2010-04-12 04:19:52 +04:00
File . identical? ( a , b )
2003-11-18 08:09:20 +03:00
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_same?
2003-11-18 08:09:20 +03:00
2005-09-19 00:59:29 +04:00
def fu_output_message ( msg ) #:nodoc:
2019-07-31 22:57:21 +03:00
output = @fileutils_output if defined? ( @fileutils_output )
2020-06-12 13:00:27 +03:00
output || = $stderr
2019-07-31 22:57:21 +03:00
if defined? ( @fileutils_label )
msg = @fileutils_label + msg
end
output . puts msg
2002-03-26 05:03:04 +03:00
end
2013-03-01 06:09:42 +04:00
private_module_function :fu_output_message
2002-03-26 05:03:04 +03:00
2016-03-01 05:26:44 +03:00
# This hash table holds command options.
OPT_TABLE = { } #:nodoc: internal use only
( private_instance_methods & methods ( false ) ) . inject ( OPT_TABLE ) { | tbl , name |
( tbl [ name . to_s ] = instance_method ( name ) . parameters ) . map! { | t , n | n if t == :key } . compact!
tbl
}
2019-03-10 14:09:59 +03:00
public
2004-05-07 17:08:12 +04:00
#
2019-03-10 14:09:59 +03:00
# Returns an Array of names of high-level methods that accept any keyword
# arguments.
2004-05-07 17:08:12 +04:00
#
# p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
#
2016-02-29 11:36:28 +03:00
def self . commands
2004-05-07 17:08:12 +04:00
OPT_TABLE . keys
end
#
# Returns an Array of option names.
#
# p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
#
2016-02-29 11:36:28 +03:00
def self . options
2006-03-05 12:41:33 +03:00
OPT_TABLE . values . flatten . uniq . map { | sym | sym . to_s }
2004-05-07 17:08:12 +04:00
end
#
# Returns true if the method +mid+ have an option +opt+.
#
# p FileUtils.have_option?(:cp, :noop) #=> true
# p FileUtils.have_option?(:rm, :force) #=> true
2013-11-30 18:41:55 +04:00
# p FileUtils.have_option?(:rm, :preserve) #=> false
2004-05-07 17:08:12 +04:00
#
2016-02-29 11:36:28 +03:00
def self . have_option? ( mid , opt )
2005-05-26 02:41:32 +04:00
li = OPT_TABLE [ mid . to_s ] or raise ArgumentError , " no such method: #{ mid } "
2006-03-05 12:41:33 +03:00
li . include? ( opt )
2004-05-07 17:08:12 +04:00
end
#
# Returns an Array of option names of the method +mid+.
#
2014-01-23 09:28:36 +04:00
# p FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"]
2004-05-07 17:08:12 +04:00
#
2016-02-29 11:36:28 +03:00
def self . options_of ( mid )
2006-03-05 12:41:33 +03:00
OPT_TABLE [ mid . to_s ] . map { | sym | sym . to_s }
2004-05-07 17:08:12 +04:00
end
#
2019-03-10 14:09:59 +03:00
# Returns an Array of methods names which have the option +opt+.
2004-05-07 17:08:12 +04:00
#
2005-09-19 01:38:40 +04:00
# p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
2004-05-07 17:08:12 +04:00
#
2016-02-29 11:36:28 +03:00
def self . collect_method ( opt )
2006-03-05 12:41:33 +03:00
OPT_TABLE . keys . select { | m | OPT_TABLE [ m ] . include? ( opt ) }
2004-05-07 17:08:12 +04:00
end
2019-03-10 14:09:59 +03:00
private
LOW_METHODS = singleton_methods ( false ) - collect_method ( :noop ) . map ( & :intern ) # :nodoc:
module LowMethods # :nodoc: internal use only
2016-02-29 15:22:17 +03:00
private
def _do_nothing ( * ) end
:: FileUtils :: LOW_METHODS . map { | name | alias_method name , :_do_nothing }
2013-03-01 06:09:42 +04:00
end
2019-03-10 14:09:59 +03:00
METHODS = singleton_methods ( ) - [ :private_module_function , # :nodoc:
2013-03-01 06:09:42 +04:00
:commands , :options , :have_option? , :options_of , :collect_method ]
2009-03-06 06:56:38 +03:00
#
2013-03-01 06:09:42 +04:00
# This module has all methods of FileUtils module, but it outputs messages
# before acting. This equates to passing the <tt>:verbose</tt> flag to
# methods in FileUtils.
2009-03-06 06:56:38 +03:00
#
2013-03-01 06:09:42 +04:00
module Verbose
include FileUtils
2016-02-29 11:36:30 +03:00
names = :: FileUtils . collect_method ( :verbose )
names . each do | name |
2013-03-01 06:09:42 +04:00
module_eval ( <<-EOS, __FILE__, __LINE__ + 1)
2016-03-01 05:26:44 +03:00
def #{name}(*args, **options)
super ( * args , ** options , verbose : true )
2013-03-01 06:09:42 +04:00
end
EOS
end
2016-02-29 11:36:30 +03:00
private ( * names )
2013-03-01 06:09:42 +04:00
extend self
class << self
2016-02-29 11:36:29 +03:00
public ( * :: FileUtils :: METHODS )
2013-03-01 06:09:42 +04:00
end
end
2002-03-26 05:03:04 +03:00
2013-03-01 06:09:42 +04:00
#
# This module has all methods of FileUtils module, but never changes
# files/directories. This equates to passing the <tt>:noop</tt> flag
# to methods in FileUtils.
#
module NoWrite
include FileUtils
include LowMethods
2016-02-29 11:36:30 +03:00
names = :: FileUtils . collect_method ( :noop )
names . each do | name |
2013-03-01 06:09:42 +04:00
module_eval ( <<-EOS, __FILE__, __LINE__ + 1)
2016-03-01 05:26:44 +03:00
def #{name}(*args, **options)
super ( * args , ** options , noop : true )
2013-03-01 06:09:42 +04:00
end
EOS
end
2016-02-29 11:36:30 +03:00
private ( * names )
2013-03-01 06:09:42 +04:00
extend self
class << self
2016-02-29 11:36:29 +03:00
public ( * :: FileUtils :: METHODS )
2013-03-01 06:09:42 +04:00
end
end
2002-03-26 05:03:04 +03:00
2009-03-06 06:56:38 +03:00
#
2013-03-01 06:09:42 +04:00
# This module has all methods of FileUtils module, but never changes
# files/directories, with printing message before acting.
# This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
# to methods in FileUtils.
2009-03-06 06:56:38 +03:00
#
2013-03-01 06:09:42 +04:00
module DryRun
include FileUtils
include LowMethods
2016-02-29 11:36:30 +03:00
names = :: FileUtils . collect_method ( :noop )
names . each do | name |
2013-03-01 06:09:42 +04:00
module_eval ( <<-EOS, __FILE__, __LINE__ + 1)
2016-03-01 05:26:44 +03:00
def #{name}(*args, **options)
super ( * args , ** options , noop : true , verbose : true )
2013-03-01 06:09:42 +04:00
end
EOS
end
2016-02-29 11:36:30 +03:00
private ( * names )
2013-03-01 06:09:42 +04:00
extend self
class << self
2016-02-29 11:36:29 +03:00
public ( * :: FileUtils :: METHODS )
2013-03-01 06:09:42 +04:00
end
end
2004-02-16 19:23:07 +03:00
end