memfs 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +4 -0
- data/README.md +7 -1
- data/lib/memfs/dir.rb +1 -1
- data/lib/memfs/fake/directory.rb +4 -0
- data/lib/memfs/fake/entry.rb +20 -3
- data/lib/memfs/fake/file.rb +8 -0
- data/lib/memfs/fake/file/content.rb +4 -0
- data/lib/memfs/fake/symlink.rb +24 -0
- data/lib/memfs/file.rb +89 -7
- data/lib/memfs/file/stat.rb +127 -4
- data/lib/memfs/file_system.rb +0 -8
- data/lib/memfs/version.rb +1 -1
- data/spec/memfs/fake/directory_spec.rb +6 -0
- data/spec/memfs/fake/entry_spec.rb +54 -24
- data/spec/memfs/fake/file/content_spec.rb +9 -0
- data/spec/memfs/fake/file_spec.rb +22 -0
- data/spec/memfs/fake/symlink_spec.rb +70 -0
- data/spec/memfs/file/stat_spec.rb +728 -84
- data/spec/memfs/file_spec.rb +1028 -39
- data/spec/memfs/file_system_spec.rb +63 -35
- metadata +3 -5
- data/.DS_Store +0 -0
- data/lib/fileutils.rb +0 -1738
@@ -8,17 +8,6 @@ module MemFs
|
|
8
8
|
subject.mkdir '/test-dir'
|
9
9
|
end
|
10
10
|
|
11
|
-
describe '#directory?' do
|
12
|
-
it "returns true if an entry is a directory" do
|
13
|
-
expect(subject.directory?('/test-dir')).to be_true
|
14
|
-
end
|
15
|
-
|
16
|
-
it "returns false if an entry is not a directory" do
|
17
|
-
subject.touch('/some-file')
|
18
|
-
expect(subject.directory?('/some-file')).to be_false
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
11
|
describe '#chdir' do
|
23
12
|
it "changes the current working directory" do
|
24
13
|
subject.chdir '/test-dir'
|
@@ -168,22 +157,77 @@ module MemFs
|
|
168
157
|
end
|
169
158
|
|
170
159
|
describe '#find' do
|
171
|
-
|
172
|
-
|
160
|
+
context "when the entry for the given path exists" do
|
161
|
+
it "returns the entry" do
|
162
|
+
entry = subject.find('/test-dir')
|
163
|
+
expect(entry).not_to be_nil
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "when there is no entry for the given path" do
|
168
|
+
it "returns nil" do
|
169
|
+
entry = subject.find('/no-file')
|
170
|
+
expect(entry).to be_nil
|
171
|
+
end
|
173
172
|
end
|
174
173
|
|
175
|
-
|
176
|
-
|
174
|
+
context "when a part of the given path is a symlink" do
|
175
|
+
before :each do
|
176
|
+
subject.symlink('/test-dir', '/test-dir-link')
|
177
|
+
subject.symlink('/no-dir', '/test-no-link')
|
178
|
+
subject.touch('/test-dir/test-file')
|
179
|
+
end
|
180
|
+
|
181
|
+
context "and the symlink's target exists" do
|
182
|
+
it "returns the entry" do
|
183
|
+
entry = subject.find('/test-dir-link/test-file')
|
184
|
+
expect(entry).not_to be_nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context "and the symlink's target does not exist" do
|
189
|
+
it "returns nil" do
|
190
|
+
entry = subject.find('/test-no-link/test-file')
|
191
|
+
expect(entry).to be_nil
|
192
|
+
end
|
193
|
+
end
|
177
194
|
end
|
178
195
|
end
|
179
196
|
|
180
197
|
describe '#find!' do
|
181
|
-
|
182
|
-
|
198
|
+
context "when the entry for the given path exists" do
|
199
|
+
it "returns the entry" do
|
200
|
+
entry = subject.find!('/test-dir')
|
201
|
+
expect(entry).not_to be_nil
|
202
|
+
end
|
183
203
|
end
|
184
204
|
|
185
|
-
|
186
|
-
|
205
|
+
context "when there is no entry for the given path" do
|
206
|
+
it "raises an exception" do
|
207
|
+
expect { subject.find!('/no-file') }.to raise_exception
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "when a part of the given path is a symlink" do
|
212
|
+
before :each do
|
213
|
+
fs.symlink('/test-dir', '/test-dir-link')
|
214
|
+
fs.touch('/test-dir/test-file')
|
215
|
+
end
|
216
|
+
|
217
|
+
context "and the symlink's target exists" do
|
218
|
+
it "returns the entry" do
|
219
|
+
entry = subject.find!('/test-dir-link/test-file')
|
220
|
+
expect(entry).not_to be_nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context "and the symlink's target does not exist" do
|
225
|
+
it "raises an exception" do
|
226
|
+
expect {
|
227
|
+
subject.find!('/test-no-link/test-file')
|
228
|
+
}.to raise_error
|
229
|
+
end
|
230
|
+
end
|
187
231
|
end
|
188
232
|
end
|
189
233
|
|
@@ -323,22 +367,6 @@ module MemFs
|
|
323
367
|
end
|
324
368
|
end
|
325
369
|
|
326
|
-
describe '#symlink?' do
|
327
|
-
it "returns true if the entry is a symlink" do
|
328
|
-
subject.symlink('/test-file', '/test-link')
|
329
|
-
expect(subject.symlink?('/test-link')).to be_true
|
330
|
-
end
|
331
|
-
|
332
|
-
it "returns false if the entry is not a symlink" do
|
333
|
-
subject.touch('/test-file')
|
334
|
-
expect(subject.symlink?('/test-file')).to be_false
|
335
|
-
end
|
336
|
-
|
337
|
-
it "returns false if the entry doesn't exist" do
|
338
|
-
expect(subject.symlink?('/test-file')).to be_false
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
370
|
describe '#touch' do
|
343
371
|
it "creates a regular file" do
|
344
372
|
subject.touch '/some-file'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: memfs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon COURTOIS
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coveralls
|
@@ -130,7 +130,6 @@ executables: []
|
|
130
130
|
extensions: []
|
131
131
|
extra_rdoc_files: []
|
132
132
|
files:
|
133
|
-
- .DS_Store
|
134
133
|
- .gitignore
|
135
134
|
- .rspec
|
136
135
|
- .travis.yml
|
@@ -140,7 +139,6 @@ files:
|
|
140
139
|
- LICENSE.txt
|
141
140
|
- README.md
|
142
141
|
- Rakefile
|
143
|
-
- lib/fileutils.rb
|
144
142
|
- lib/memfs.rb
|
145
143
|
- lib/memfs/dir.rb
|
146
144
|
- lib/memfs/fake/directory.rb
|
@@ -189,7 +187,7 @@ rubyforge_project:
|
|
189
187
|
rubygems_version: 2.0.3
|
190
188
|
signing_key:
|
191
189
|
specification_version: 4
|
192
|
-
summary: memfs-0.0
|
190
|
+
summary: memfs-0.1.0
|
193
191
|
test_files:
|
194
192
|
- spec/fileutils_spec.rb
|
195
193
|
- spec/memfs/dir_spec.rb
|
data/.DS_Store
DELETED
Binary file
|
data/lib/fileutils.rb
DELETED
@@ -1,1738 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# = fileutils.rb
|
3
|
-
#
|
4
|
-
# Copyright (c) 2000-2007 Minero Aoki
|
5
|
-
#
|
6
|
-
# This program is free software.
|
7
|
-
# You can distribute/modify this program under the same terms of ruby.
|
8
|
-
#
|
9
|
-
# == module FileUtils
|
10
|
-
#
|
11
|
-
# Namespace for several file utility methods for copying, moving, removing, etc.
|
12
|
-
#
|
13
|
-
# === Module Functions
|
14
|
-
#
|
15
|
-
# cd(dir, options)
|
16
|
-
# cd(dir, options) {|dir| .... }
|
17
|
-
# pwd()
|
18
|
-
# mkdir(dir, options)
|
19
|
-
# mkdir(list, options)
|
20
|
-
# mkdir_p(dir, options)
|
21
|
-
# mkdir_p(list, options)
|
22
|
-
# rmdir(dir, options)
|
23
|
-
# rmdir(list, options)
|
24
|
-
# ln(old, new, options)
|
25
|
-
# ln(list, destdir, options)
|
26
|
-
# ln_s(old, new, options)
|
27
|
-
# ln_s(list, destdir, options)
|
28
|
-
# ln_sf(src, dest, options)
|
29
|
-
# cp(src, dest, options)
|
30
|
-
# cp(list, dir, options)
|
31
|
-
# cp_r(src, dest, options)
|
32
|
-
# cp_r(list, dir, options)
|
33
|
-
# mv(src, dest, options)
|
34
|
-
# mv(list, dir, options)
|
35
|
-
# rm(list, options)
|
36
|
-
# rm_r(list, options)
|
37
|
-
# rm_rf(list, options)
|
38
|
-
# install(src, dest, mode = <src's>, options)
|
39
|
-
# chmod(mode, list, options)
|
40
|
-
# chmod_R(mode, list, options)
|
41
|
-
# chown(user, group, list, options)
|
42
|
-
# chown_R(user, group, list, options)
|
43
|
-
# touch(list, options)
|
44
|
-
#
|
45
|
-
# The <tt>options</tt> parameter is a hash of options, taken from the list
|
46
|
-
# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
|
47
|
-
# <tt>:noop</tt> means that no changes are made. The other two are obvious.
|
48
|
-
# Each method documents the options that it honours.
|
49
|
-
#
|
50
|
-
# All methods that have the concept of a "source" file or directory can take
|
51
|
-
# either one file or a list of files in that argument. See the method
|
52
|
-
# documentation for examples.
|
53
|
-
#
|
54
|
-
# There are some `low level' methods, which do not accept any option:
|
55
|
-
#
|
56
|
-
# copy_entry(src, dest, preserve = false, dereference = false)
|
57
|
-
# copy_file(src, dest, preserve = false, dereference = true)
|
58
|
-
# copy_stream(srcstream, deststream)
|
59
|
-
# remove_entry(path, force = false)
|
60
|
-
# remove_entry_secure(path, force = false)
|
61
|
-
# remove_file(path, force = false)
|
62
|
-
# compare_file(path_a, path_b)
|
63
|
-
# compare_stream(stream_a, stream_b)
|
64
|
-
# uptodate?(file, cmp_list)
|
65
|
-
#
|
66
|
-
# == module FileUtils::Verbose
|
67
|
-
#
|
68
|
-
# This module has all methods of FileUtils module, but it outputs messages
|
69
|
-
# before acting. This equates to passing the <tt>:verbose</tt> flag to methods
|
70
|
-
# in FileUtils.
|
71
|
-
#
|
72
|
-
# == module FileUtils::NoWrite
|
73
|
-
#
|
74
|
-
# This module has all methods of FileUtils module, but never changes
|
75
|
-
# files/directories. This equates to passing the <tt>:noop</tt> flag to methods
|
76
|
-
# in FileUtils.
|
77
|
-
#
|
78
|
-
# == module FileUtils::DryRun
|
79
|
-
#
|
80
|
-
# This module has all methods of FileUtils module, but never changes
|
81
|
-
# files/directories. This equates to passing the <tt>:noop</tt> and
|
82
|
-
# <tt>:verbose</tt> flags to methods in FileUtils.
|
83
|
-
#
|
84
|
-
|
85
|
-
module FileUtils
|
86
|
-
@fileutils_output = $stderr
|
87
|
-
@fileutils_label = ''
|
88
|
-
extend self
|
89
|
-
|
90
|
-
#
|
91
|
-
# This module has all methods of FileUtils module, but it outputs messages
|
92
|
-
# before acting. This equates to passing the <tt>:verbose</tt> flag to
|
93
|
-
# methods in FileUtils.
|
94
|
-
#
|
95
|
-
module Verbose
|
96
|
-
include FileUtils
|
97
|
-
@fileutils_output = $stderr
|
98
|
-
@fileutils_label = ''
|
99
|
-
extend self
|
100
|
-
end
|
101
|
-
|
102
|
-
#
|
103
|
-
# This module has all methods of FileUtils module, but never changes
|
104
|
-
# files/directories. This equates to passing the <tt>:noop</tt> flag
|
105
|
-
# to methods in FileUtils.
|
106
|
-
#
|
107
|
-
module NoWrite
|
108
|
-
include FileUtils
|
109
|
-
@fileutils_output = $stderr
|
110
|
-
@fileutils_label = ''
|
111
|
-
extend self
|
112
|
-
end
|
113
|
-
|
114
|
-
#
|
115
|
-
# This module has all methods of FileUtils module, but never changes
|
116
|
-
# files/directories, with printing message before acting.
|
117
|
-
# This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag
|
118
|
-
# to methods in FileUtils.
|
119
|
-
#
|
120
|
-
module DryRun
|
121
|
-
include FileUtils
|
122
|
-
@fileutils_output = $stderr
|
123
|
-
@fileutils_label = ''
|
124
|
-
extend self
|
125
|
-
end
|
126
|
-
|
127
|
-
# This hash table holds command options.
|
128
|
-
OPT_TABLE = {} #:nodoc: internal use only
|
129
|
-
|
130
|
-
#
|
131
|
-
def self.define_command(name, *options)
|
132
|
-
OPT_TABLE[name.to_s] = options
|
133
|
-
|
134
|
-
if options.include?(:verbose)
|
135
|
-
Verbose.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
136
|
-
def #{name}(*args)
|
137
|
-
super(*fu_update_option(args, :verbose => true))
|
138
|
-
end
|
139
|
-
EOS
|
140
|
-
end
|
141
|
-
if options.include?(:noop)
|
142
|
-
NoWrite.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
143
|
-
def #{name}(*args)
|
144
|
-
super(*fu_update_option(args, :noop => true))
|
145
|
-
end
|
146
|
-
EOS
|
147
|
-
DryRun.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
148
|
-
def #{name}(*args)
|
149
|
-
super(*fu_update_option(args, :noop => true, :verbose => true))
|
150
|
-
end
|
151
|
-
EOS
|
152
|
-
else
|
153
|
-
NoWrite.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
154
|
-
def #{name}(*); end
|
155
|
-
EOS
|
156
|
-
DryRun.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
157
|
-
def #{name}(*); end
|
158
|
-
EOS
|
159
|
-
end
|
160
|
-
|
161
|
-
[self, Verbose, DryRun, NoWrite].each do |mod|
|
162
|
-
mod.module_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
163
|
-
private :#{name}
|
164
|
-
class << self; public :#{name}; end
|
165
|
-
EOS
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
class << self
|
170
|
-
private :define_command
|
171
|
-
end
|
172
|
-
|
173
|
-
public
|
174
|
-
|
175
|
-
#
|
176
|
-
# Options: (none)
|
177
|
-
#
|
178
|
-
# Returns the name of the current directory.
|
179
|
-
#
|
180
|
-
def pwd
|
181
|
-
Dir.pwd
|
182
|
-
end
|
183
|
-
|
184
|
-
alias getwd pwd
|
185
|
-
|
186
|
-
define_command('pwd')
|
187
|
-
define_command('getwd')
|
188
|
-
|
189
|
-
#
|
190
|
-
# Options: verbose
|
191
|
-
#
|
192
|
-
# Changes the current directory to the directory +dir+.
|
193
|
-
#
|
194
|
-
# If this method is called with block, resumes to the old
|
195
|
-
# working directory after the block execution finished.
|
196
|
-
#
|
197
|
-
# FileUtils.cd('/', :verbose => true) # chdir and report it
|
198
|
-
#
|
199
|
-
# FileUtils.cd('/') do # chdir
|
200
|
-
# [...] # do something
|
201
|
-
# end # return to original directory
|
202
|
-
#
|
203
|
-
def cd(dir, options = {}, &block) # :yield: dir
|
204
|
-
fu_check_options options, OPT_TABLE['cd']
|
205
|
-
fu_output_message "cd #{dir}" if options[:verbose]
|
206
|
-
Dir.chdir(dir, &block)
|
207
|
-
fu_output_message 'cd -' if options[:verbose] and block
|
208
|
-
end
|
209
|
-
|
210
|
-
alias chdir cd
|
211
|
-
|
212
|
-
define_command('cd', :verbose)
|
213
|
-
define_command('chdir', :verbose)
|
214
|
-
|
215
|
-
#
|
216
|
-
# Options: (none)
|
217
|
-
#
|
218
|
-
# Returns true if +newer+ is newer than all +old_list+.
|
219
|
-
# Non-existent files are older than any file.
|
220
|
-
#
|
221
|
-
# FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
|
222
|
-
# system 'make hello.o'
|
223
|
-
#
|
224
|
-
def uptodate?(new, old_list)
|
225
|
-
return false unless File.exist?(new)
|
226
|
-
new_time = File.mtime(new)
|
227
|
-
old_list.each do |old|
|
228
|
-
if File.exist?(old)
|
229
|
-
return false unless new_time > File.mtime(old)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
true
|
233
|
-
end
|
234
|
-
|
235
|
-
define_command('uptodate?')
|
236
|
-
|
237
|
-
#
|
238
|
-
# Options: mode noop verbose
|
239
|
-
#
|
240
|
-
# Creates one or more directories.
|
241
|
-
#
|
242
|
-
# FileUtils.mkdir 'test'
|
243
|
-
# FileUtils.mkdir %w( tmp data )
|
244
|
-
# FileUtils.mkdir 'notexist', :noop => true # Does not really create.
|
245
|
-
# FileUtils.mkdir 'tmp', :mode => 0700
|
246
|
-
#
|
247
|
-
def mkdir(list, options = {})
|
248
|
-
fu_check_options options, OPT_TABLE['mkdir']
|
249
|
-
list = fu_list(list)
|
250
|
-
fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
|
251
|
-
return if options[:noop]
|
252
|
-
|
253
|
-
list.each do |dir|
|
254
|
-
fu_mkdir dir, options[:mode]
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
define_command('mkdir', :mode, :noop, :verbose)
|
259
|
-
|
260
|
-
#
|
261
|
-
# Options: mode noop verbose
|
262
|
-
#
|
263
|
-
# Creates a directory and all its parent directories.
|
264
|
-
# For example,
|
265
|
-
#
|
266
|
-
# FileUtils.mkdir_p '/usr/local/lib/ruby'
|
267
|
-
#
|
268
|
-
# causes to make following directories, if it does not exist.
|
269
|
-
# * /usr
|
270
|
-
# * /usr/local
|
271
|
-
# * /usr/local/lib
|
272
|
-
# * /usr/local/lib/ruby
|
273
|
-
#
|
274
|
-
# You can pass several directories at a time in a list.
|
275
|
-
#
|
276
|
-
def mkdir_p(list, options = {})
|
277
|
-
fu_check_options options, OPT_TABLE['mkdir_p']
|
278
|
-
list = fu_list(list)
|
279
|
-
fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
|
280
|
-
return *list if options[:noop]
|
281
|
-
|
282
|
-
list.map {|path| path.chomp(?/) }.each do |path|
|
283
|
-
# optimize for the most common case
|
284
|
-
begin
|
285
|
-
fu_mkdir path, options[:mode]
|
286
|
-
next
|
287
|
-
rescue SystemCallError
|
288
|
-
next if File.directory?(path)
|
289
|
-
end
|
290
|
-
|
291
|
-
stack = []
|
292
|
-
until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/"
|
293
|
-
stack.push path
|
294
|
-
path = File.dirname(path)
|
295
|
-
end
|
296
|
-
stack.reverse_each do |dir|
|
297
|
-
begin
|
298
|
-
fu_mkdir dir, options[:mode]
|
299
|
-
rescue SystemCallError
|
300
|
-
raise unless File.directory?(dir)
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
return *list
|
306
|
-
end
|
307
|
-
|
308
|
-
alias mkpath mkdir_p
|
309
|
-
alias makedirs mkdir_p
|
310
|
-
|
311
|
-
define_command('mkdir_p', :mode, :noop, :verbose)
|
312
|
-
define_command('mkpath', :mode, :noop, :verbose)
|
313
|
-
define_command('makedirs', :mode, :noop, :verbose)
|
314
|
-
|
315
|
-
private
|
316
|
-
|
317
|
-
def fu_mkdir(path, mode) #:nodoc:
|
318
|
-
path = path.chomp(?/)
|
319
|
-
if mode
|
320
|
-
Dir.mkdir path, mode
|
321
|
-
File.chmod mode, path
|
322
|
-
else
|
323
|
-
Dir.mkdir path
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
public
|
328
|
-
|
329
|
-
#
|
330
|
-
# Options: noop, verbose
|
331
|
-
#
|
332
|
-
# Removes one or more directories.
|
333
|
-
#
|
334
|
-
# FileUtils.rmdir 'somedir'
|
335
|
-
# FileUtils.rmdir %w(somedir anydir otherdir)
|
336
|
-
# # Does not really remove directory; outputs message.
|
337
|
-
# FileUtils.rmdir 'somedir', :verbose => true, :noop => true
|
338
|
-
#
|
339
|
-
def rmdir(list, options = {})
|
340
|
-
fu_check_options options, OPT_TABLE['rmdir']
|
341
|
-
list = fu_list(list)
|
342
|
-
parents = options[:parents]
|
343
|
-
fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if options[:verbose]
|
344
|
-
return if options[:noop]
|
345
|
-
list.each do |dir|
|
346
|
-
begin
|
347
|
-
Dir.rmdir(dir = dir.chomp(?/))
|
348
|
-
if parents
|
349
|
-
until (parent = File.dirname(dir)) == '.' or parent == dir
|
350
|
-
Dir.rmdir(dir)
|
351
|
-
end
|
352
|
-
end
|
353
|
-
rescue Errno::ENOTEMPTY, Errno::ENOENT
|
354
|
-
end
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
define_command('rmdir', :parents, :noop, :verbose)
|
359
|
-
|
360
|
-
#
|
361
|
-
# Options: force noop verbose
|
362
|
-
#
|
363
|
-
# <b><tt>ln(old, new, options = {})</tt></b>
|
364
|
-
#
|
365
|
-
# Creates a hard link +new+ which points to +old+.
|
366
|
-
# If +new+ already exists and it is a directory, creates a link +new/old+.
|
367
|
-
# If +new+ already exists and it is not a directory, raises Errno::EEXIST.
|
368
|
-
# But if :force option is set, overwrite +new+.
|
369
|
-
#
|
370
|
-
# FileUtils.ln 'gcc', 'cc', :verbose => true
|
371
|
-
# FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
|
372
|
-
#
|
373
|
-
# <b><tt>ln(list, destdir, options = {})</tt></b>
|
374
|
-
#
|
375
|
-
# Creates several hard links in a directory, with each one pointing to the
|
376
|
-
# item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR.
|
377
|
-
#
|
378
|
-
# include FileUtils
|
379
|
-
# cd '/sbin'
|
380
|
-
# FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked.
|
381
|
-
#
|
382
|
-
def ln(src, dest, options = {})
|
383
|
-
fu_check_options options, OPT_TABLE['ln']
|
384
|
-
fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
|
385
|
-
return if options[:noop]
|
386
|
-
fu_each_src_dest0(src, dest) do |s,d|
|
387
|
-
remove_file d, true if options[:force]
|
388
|
-
File.link s, d
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
alias link ln
|
393
|
-
|
394
|
-
define_command('ln', :force, :noop, :verbose)
|
395
|
-
define_command('link', :force, :noop, :verbose)
|
396
|
-
|
397
|
-
#
|
398
|
-
# Options: force noop verbose
|
399
|
-
#
|
400
|
-
# <b><tt>ln_s(old, new, options = {})</tt></b>
|
401
|
-
#
|
402
|
-
# Creates a symbolic link +new+ which points to +old+. If +new+ already
|
403
|
-
# exists and it is a directory, creates a symbolic link +new/old+. If +new+
|
404
|
-
# already exists and it is not a directory, raises Errno::EEXIST. But if
|
405
|
-
# :force option is set, overwrite +new+.
|
406
|
-
#
|
407
|
-
# FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
|
408
|
-
# FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true
|
409
|
-
#
|
410
|
-
# <b><tt>ln_s(list, destdir, options = {})</tt></b>
|
411
|
-
#
|
412
|
-
# Creates several symbolic links in a directory, with each one pointing to the
|
413
|
-
# item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR.
|
414
|
-
#
|
415
|
-
# If +destdir+ is not a directory, raises Errno::ENOTDIR.
|
416
|
-
#
|
417
|
-
# FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin'
|
418
|
-
#
|
419
|
-
def ln_s(src, dest, options = {})
|
420
|
-
fu_check_options options, OPT_TABLE['ln_s']
|
421
|
-
fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
|
422
|
-
return if options[:noop]
|
423
|
-
fu_each_src_dest0(src, dest) do |s,d|
|
424
|
-
remove_file d, true if options[:force]
|
425
|
-
File.symlink s, d
|
426
|
-
end
|
427
|
-
end
|
428
|
-
|
429
|
-
alias symlink ln_s
|
430
|
-
|
431
|
-
define_command('ln_s', :force, :noop, :verbose)
|
432
|
-
define_command('symlink', :force, :noop, :verbose)
|
433
|
-
|
434
|
-
#
|
435
|
-
# Options: noop verbose
|
436
|
-
#
|
437
|
-
# Same as
|
438
|
-
# #ln_s(src, dest, :force => true)
|
439
|
-
#
|
440
|
-
def ln_sf(src, dest, options = {})
|
441
|
-
fu_check_options options, OPT_TABLE['ln_sf']
|
442
|
-
options = options.dup
|
443
|
-
options[:force] = true
|
444
|
-
ln_s src, dest, options
|
445
|
-
end
|
446
|
-
|
447
|
-
define_command('ln_sf', :noop, :verbose)
|
448
|
-
|
449
|
-
#
|
450
|
-
# Options: preserve noop verbose
|
451
|
-
#
|
452
|
-
# Copies a file content +src+ to +dest+. If +dest+ is a directory,
|
453
|
-
# copies +src+ to +dest/src+.
|
454
|
-
#
|
455
|
-
# If +src+ is a list of files, then +dest+ must be a directory.
|
456
|
-
#
|
457
|
-
# FileUtils.cp 'eval.c', 'eval.c.org'
|
458
|
-
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
|
459
|
-
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
|
460
|
-
# FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
|
461
|
-
#
|
462
|
-
def cp(src, dest, options = {})
|
463
|
-
fu_check_options options, OPT_TABLE['cp']
|
464
|
-
fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
|
465
|
-
return if options[:noop]
|
466
|
-
fu_each_src_dest(src, dest) do |s, d|
|
467
|
-
copy_file s, d, options[:preserve]
|
468
|
-
end
|
469
|
-
end
|
470
|
-
|
471
|
-
alias copy cp
|
472
|
-
|
473
|
-
define_command('cp', :preserve, :noop, :verbose)
|
474
|
-
define_command('copy', :preserve, :noop, :verbose)
|
475
|
-
|
476
|
-
#
|
477
|
-
# Options: preserve noop verbose dereference_root remove_destination
|
478
|
-
#
|
479
|
-
# Copies +src+ to +dest+. If +src+ is a directory, this method copies
|
480
|
-
# all its contents recursively. If +dest+ is a directory, copies
|
481
|
-
# +src+ to +dest/src+.
|
482
|
-
#
|
483
|
-
# +src+ can be a list of files.
|
484
|
-
#
|
485
|
-
# # Installing ruby library "mylib" under the site_ruby
|
486
|
-
# FileUtils.rm_r site_ruby + '/mylib', :force
|
487
|
-
# FileUtils.cp_r 'lib/', site_ruby + '/mylib'
|
488
|
-
#
|
489
|
-
# # Examples of copying several files to target directory.
|
490
|
-
# FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
|
491
|
-
# FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
|
492
|
-
#
|
493
|
-
# # If you want to copy all contents of a directory instead of the
|
494
|
-
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
|
495
|
-
# # use following code.
|
496
|
-
# FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src,
|
497
|
-
# # but this doesn't.
|
498
|
-
#
|
499
|
-
def cp_r(src, dest, options = {})
|
500
|
-
fu_check_options options, OPT_TABLE['cp_r']
|
501
|
-
fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
|
502
|
-
return if options[:noop]
|
503
|
-
options = options.dup
|
504
|
-
options[:dereference_root] = true unless options.key?(:dereference_root)
|
505
|
-
fu_each_src_dest(src, dest) do |s, d|
|
506
|
-
copy_entry s, d, options[:preserve], options[:dereference_root], options[:remove_destination]
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
define_command('cp_r', :preserve, :noop, :verbose, :dereference_root, :remove_destination)
|
511
|
-
|
512
|
-
#
|
513
|
-
# Copies a file system entry +src+ to +dest+.
|
514
|
-
# If +src+ is a directory, this method copies its contents recursively.
|
515
|
-
# This method preserves file types, c.f. symlink, directory...
|
516
|
-
# (FIFO, device files and etc. are not supported yet)
|
517
|
-
#
|
518
|
-
# Both of +src+ and +dest+ must be a path name.
|
519
|
-
# +src+ must exist, +dest+ must not exist.
|
520
|
-
#
|
521
|
-
# If +preserve+ is true, this method preserves owner, group, permissions
|
522
|
-
# and modified time.
|
523
|
-
#
|
524
|
-
# If +dereference_root+ is true, this method dereference tree root.
|
525
|
-
#
|
526
|
-
# If +remove_destination+ is true, this method removes each destination file before copy.
|
527
|
-
#
|
528
|
-
def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
|
529
|
-
Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent|
|
530
|
-
destent = Entry_.new(dest, ent.rel, false)
|
531
|
-
File.unlink destent.path if remove_destination && File.file?(destent.path)
|
532
|
-
ent.copy destent.path
|
533
|
-
end, proc do |ent|
|
534
|
-
destent = Entry_.new(dest, ent.rel, false)
|
535
|
-
ent.copy_metadata destent.path if preserve
|
536
|
-
end)
|
537
|
-
end
|
538
|
-
|
539
|
-
define_command(:copy_entry)
|
540
|
-
|
541
|
-
#
|
542
|
-
# Copies file contents of +src+ to +dest+.
|
543
|
-
# Both of +src+ and +dest+ must be a path name.
|
544
|
-
#
|
545
|
-
def copy_file(src, dest, preserve = false, dereference = true)
|
546
|
-
ent = Entry_.new(src, nil, dereference)
|
547
|
-
ent.copy_file dest
|
548
|
-
ent.copy_metadata dest if preserve
|
549
|
-
end
|
550
|
-
|
551
|
-
define_command(:copy_file)
|
552
|
-
|
553
|
-
#
|
554
|
-
# Copies stream +src+ to +dest+.
|
555
|
-
# +src+ must respond to #read(n) and
|
556
|
-
# +dest+ must respond to #write(str).
|
557
|
-
#
|
558
|
-
def copy_stream(src, dest)
|
559
|
-
IO.copy_stream(src, dest)
|
560
|
-
end
|
561
|
-
|
562
|
-
define_command(:copy_stream)
|
563
|
-
|
564
|
-
#
|
565
|
-
# Options: force noop verbose
|
566
|
-
#
|
567
|
-
# Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different
|
568
|
-
# disk partition, the file is copied then the original file is removed.
|
569
|
-
#
|
570
|
-
# FileUtils.mv 'badname.rb', 'goodname.rb'
|
571
|
-
# FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
|
572
|
-
#
|
573
|
-
# FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/'
|
574
|
-
# FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
|
575
|
-
#
|
576
|
-
def mv(src, dest, options = {})
|
577
|
-
fu_check_options options, OPT_TABLE['mv']
|
578
|
-
fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
|
579
|
-
return if options[:noop]
|
580
|
-
fu_each_src_dest(src, dest) do |s, d|
|
581
|
-
destent = Entry_.new(d, nil, true)
|
582
|
-
begin
|
583
|
-
if destent.exist?
|
584
|
-
if destent.directory?
|
585
|
-
raise Errno::EEXIST, dest
|
586
|
-
else
|
587
|
-
destent.remove_file if rename_cannot_overwrite_file?
|
588
|
-
end
|
589
|
-
end
|
590
|
-
begin
|
591
|
-
File.rename s, d
|
592
|
-
rescue Errno::EXDEV
|
593
|
-
copy_entry s, d, true
|
594
|
-
if options[:secure]
|
595
|
-
remove_entry_secure s, options[:force]
|
596
|
-
else
|
597
|
-
remove_entry s, options[:force]
|
598
|
-
end
|
599
|
-
end
|
600
|
-
rescue SystemCallError
|
601
|
-
raise unless options[:force]
|
602
|
-
end
|
603
|
-
end
|
604
|
-
end
|
605
|
-
|
606
|
-
alias move mv
|
607
|
-
|
608
|
-
define_command('mv', :force, :noop, :verbose, :secure)
|
609
|
-
define_command('move', :force, :noop, :verbose, :secure)
|
610
|
-
|
611
|
-
private
|
612
|
-
|
613
|
-
def rename_cannot_overwrite_file? #:nodoc:
|
614
|
-
/cygwin|mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
|
615
|
-
end
|
616
|
-
|
617
|
-
public
|
618
|
-
|
619
|
-
#
|
620
|
-
# Options: force noop verbose
|
621
|
-
#
|
622
|
-
# Remove file(s) specified in +list+. This method cannot remove directories.
|
623
|
-
# All StandardErrors are ignored when the :force option is set.
|
624
|
-
#
|
625
|
-
# FileUtils.rm %w( junk.txt dust.txt )
|
626
|
-
# FileUtils.rm Dir.glob('*.so')
|
627
|
-
# FileUtils.rm 'NotExistFile', :force => true # never raises exception
|
628
|
-
#
|
629
|
-
def rm(list, options = {})
|
630
|
-
fu_check_options options, OPT_TABLE['rm']
|
631
|
-
list = fu_list(list)
|
632
|
-
fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose]
|
633
|
-
return if options[:noop]
|
634
|
-
|
635
|
-
list.each do |path|
|
636
|
-
remove_file path, options[:force]
|
637
|
-
end
|
638
|
-
end
|
639
|
-
|
640
|
-
alias remove rm
|
641
|
-
|
642
|
-
define_command('rm', :force, :noop, :verbose)
|
643
|
-
define_command('remove', :force, :noop, :verbose)
|
644
|
-
|
645
|
-
#
|
646
|
-
# Options: noop verbose
|
647
|
-
#
|
648
|
-
# Equivalent to
|
649
|
-
#
|
650
|
-
# #rm(list, :force => true)
|
651
|
-
#
|
652
|
-
def rm_f(list, options = {})
|
653
|
-
fu_check_options options, OPT_TABLE['rm_f']
|
654
|
-
options = options.dup
|
655
|
-
options[:force] = true
|
656
|
-
rm list, options
|
657
|
-
end
|
658
|
-
|
659
|
-
alias safe_unlink rm_f
|
660
|
-
|
661
|
-
define_command('rm_f', :noop, :verbose)
|
662
|
-
define_command('safe_unlink', :noop, :verbose)
|
663
|
-
|
664
|
-
#
|
665
|
-
# Options: force noop verbose secure
|
666
|
-
#
|
667
|
-
# remove files +list+[0] +list+[1]... If +list+[n] is a directory,
|
668
|
-
# removes its all contents recursively. This method ignores
|
669
|
-
# StandardError when :force option is set.
|
670
|
-
#
|
671
|
-
# FileUtils.rm_r Dir.glob('/tmp/*')
|
672
|
-
# FileUtils.rm_r '/', :force => true # :-)
|
673
|
-
#
|
674
|
-
# WARNING: This method causes local vulnerability
|
675
|
-
# if one of parent directories or removing directory tree are world
|
676
|
-
# writable (including /tmp, whose permission is 1777), and the current
|
677
|
-
# process has strong privilege such as Unix super user (root), and the
|
678
|
-
# system has symbolic link. For secure removing, read the documentation
|
679
|
-
# of #remove_entry_secure carefully, and set :secure option to true.
|
680
|
-
# Default is :secure=>false.
|
681
|
-
#
|
682
|
-
# NOTE: This method calls #remove_entry_secure if :secure option is set.
|
683
|
-
# See also #remove_entry_secure.
|
684
|
-
#
|
685
|
-
def rm_r(list, options = {})
|
686
|
-
fu_check_options options, OPT_TABLE['rm_r']
|
687
|
-
# options[:secure] = true unless options.key?(:secure)
|
688
|
-
list = fu_list(list)
|
689
|
-
fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose]
|
690
|
-
return if options[:noop]
|
691
|
-
list.each do |path|
|
692
|
-
if options[:secure]
|
693
|
-
remove_entry_secure path, options[:force]
|
694
|
-
else
|
695
|
-
remove_entry path, options[:force]
|
696
|
-
end
|
697
|
-
end
|
698
|
-
end
|
699
|
-
|
700
|
-
define_command('rm_r', :force, :noop, :verbose, :secure)
|
701
|
-
|
702
|
-
#
|
703
|
-
# Options: noop verbose secure
|
704
|
-
#
|
705
|
-
# Equivalent to
|
706
|
-
#
|
707
|
-
# #rm_r(list, :force => true)
|
708
|
-
#
|
709
|
-
# WARNING: This method causes local vulnerability.
|
710
|
-
# Read the documentation of #rm_r first.
|
711
|
-
#
|
712
|
-
def rm_rf(list, options = {})
|
713
|
-
fu_check_options options, OPT_TABLE['rm_rf']
|
714
|
-
options = options.dup
|
715
|
-
options[:force] = true
|
716
|
-
rm_r list, options
|
717
|
-
end
|
718
|
-
|
719
|
-
alias rmtree rm_rf
|
720
|
-
|
721
|
-
define_command('rm_rf', :noop, :verbose, :secure)
|
722
|
-
define_command('rmtree', :noop, :verbose, :secure)
|
723
|
-
|
724
|
-
#
|
725
|
-
# This method removes a file system entry +path+. +path+ shall be a
|
726
|
-
# regular file, a directory, or something. If +path+ is a directory,
|
727
|
-
# remove it recursively. This method is required to avoid TOCTTOU
|
728
|
-
# (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
|
729
|
-
# #rm_r causes security hole when:
|
730
|
-
#
|
731
|
-
# * Parent directory is world writable (including /tmp).
|
732
|
-
# * Removing directory tree includes world writable directory.
|
733
|
-
# * The system has symbolic link.
|
734
|
-
#
|
735
|
-
# To avoid this security hole, this method applies special preprocess.
|
736
|
-
# If +path+ is a directory, this method chown(2) and chmod(2) all
|
737
|
-
# removing directories. This requires the current process is the
|
738
|
-
# owner of the removing whole directory tree, or is the super user (root).
|
739
|
-
#
|
740
|
-
# WARNING: You must ensure that *ALL* parent directories cannot be
|
741
|
-
# moved by other untrusted users. For example, parent directories
|
742
|
-
# should not be owned by untrusted users, and should not be world
|
743
|
-
# writable except when the sticky bit set.
|
744
|
-
#
|
745
|
-
# WARNING: Only the owner of the removing directory tree, or Unix super
|
746
|
-
# user (root) should invoke this method. Otherwise this method does not
|
747
|
-
# work.
|
748
|
-
#
|
749
|
-
# For details of this security vulnerability, see Perl's case:
|
750
|
-
#
|
751
|
-
# http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448
|
752
|
-
# http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452
|
753
|
-
#
|
754
|
-
# For fileutils.rb, this vulnerability is reported in [ruby-dev:26100].
|
755
|
-
#
|
756
|
-
def remove_entry_secure(path, force = false)
|
757
|
-
unless fu_have_symlink?
|
758
|
-
remove_entry path, force
|
759
|
-
return
|
760
|
-
end
|
761
|
-
fullpath = File.expand_path(path)
|
762
|
-
st = File.lstat(fullpath)
|
763
|
-
unless st.directory?
|
764
|
-
File.unlink fullpath
|
765
|
-
return
|
766
|
-
end
|
767
|
-
# is a directory.
|
768
|
-
parent_st = File.stat(File.dirname(fullpath))
|
769
|
-
unless parent_st.world_writable?
|
770
|
-
remove_entry path, force
|
771
|
-
return
|
772
|
-
end
|
773
|
-
unless parent_st.sticky?
|
774
|
-
raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})"
|
775
|
-
end
|
776
|
-
# freeze tree root
|
777
|
-
euid = Process.euid
|
778
|
-
File.open(fullpath + '/.') {|f|
|
779
|
-
unless fu_stat_identical_entry?(st, f.stat)
|
780
|
-
# symlink (TOC-to-TOU attack?)
|
781
|
-
File.unlink fullpath
|
782
|
-
return
|
783
|
-
end
|
784
|
-
f.chown euid, -1
|
785
|
-
f.chmod 0700
|
786
|
-
unless fu_stat_identical_entry?(st, File.lstat(fullpath))
|
787
|
-
# TOC-to-TOU attack?
|
788
|
-
File.unlink fullpath
|
789
|
-
return
|
790
|
-
end
|
791
|
-
}
|
792
|
-
# ---- tree root is frozen ----
|
793
|
-
root = Entry_.new(path)
|
794
|
-
root.preorder_traverse do |ent|
|
795
|
-
if ent.directory?
|
796
|
-
ent.chown euid, -1
|
797
|
-
ent.chmod 0700
|
798
|
-
end
|
799
|
-
end
|
800
|
-
root.postorder_traverse do |ent|
|
801
|
-
begin
|
802
|
-
ent.remove
|
803
|
-
rescue
|
804
|
-
raise unless force
|
805
|
-
end
|
806
|
-
end
|
807
|
-
rescue
|
808
|
-
raise unless force
|
809
|
-
end
|
810
|
-
|
811
|
-
define_command(:remove_entry_secure)
|
812
|
-
|
813
|
-
private
|
814
|
-
|
815
|
-
def fu_have_symlink? #:nodoc:
|
816
|
-
File.symlink nil, nil
|
817
|
-
rescue NotImplementedError
|
818
|
-
return false
|
819
|
-
rescue TypeError
|
820
|
-
return true
|
821
|
-
end
|
822
|
-
|
823
|
-
def fu_stat_identical_entry?(a, b) #:nodoc:
|
824
|
-
a.dev == b.dev and a.ino == b.ino
|
825
|
-
end
|
826
|
-
|
827
|
-
public
|
828
|
-
|
829
|
-
#
|
830
|
-
# This method removes a file system entry +path+.
|
831
|
-
# +path+ might be a regular file, a directory, or something.
|
832
|
-
# If +path+ is a directory, remove it recursively.
|
833
|
-
#
|
834
|
-
# See also #remove_entry_secure.
|
835
|
-
#
|
836
|
-
def remove_entry(path, force = false)
|
837
|
-
Entry_.new(path).postorder_traverse do |ent|
|
838
|
-
begin
|
839
|
-
ent.remove
|
840
|
-
rescue
|
841
|
-
raise unless force
|
842
|
-
end
|
843
|
-
end
|
844
|
-
rescue
|
845
|
-
raise unless force
|
846
|
-
end
|
847
|
-
|
848
|
-
define_command(:remove_entry)
|
849
|
-
|
850
|
-
#
|
851
|
-
# Removes a file +path+.
|
852
|
-
# This method ignores StandardError if +force+ is true.
|
853
|
-
#
|
854
|
-
def remove_file(path, force = false)
|
855
|
-
Entry_.new(path).remove_file
|
856
|
-
rescue
|
857
|
-
raise unless force
|
858
|
-
end
|
859
|
-
|
860
|
-
define_command(:remove_file)
|
861
|
-
|
862
|
-
#
|
863
|
-
# Removes a directory +dir+ and its contents recursively.
|
864
|
-
# This method ignores StandardError if +force+ is true.
|
865
|
-
#
|
866
|
-
def remove_dir(path, force = false)
|
867
|
-
remove_entry path, force # FIXME?? check if it is a directory
|
868
|
-
end
|
869
|
-
|
870
|
-
define_command(:remove_dir)
|
871
|
-
|
872
|
-
#
|
873
|
-
# Returns true if the contents of a file A and a file B are identical.
|
874
|
-
#
|
875
|
-
# FileUtils.compare_file('somefile', 'somefile') #=> true
|
876
|
-
# FileUtils.compare_file('/bin/cp', '/bin/mv') #=> maybe false
|
877
|
-
#
|
878
|
-
def compare_file(a, b)
|
879
|
-
return false unless File.size(a) == File.size(b)
|
880
|
-
File.open(a, 'rb') {|fa|
|
881
|
-
File.open(b, 'rb') {|fb|
|
882
|
-
return compare_stream(fa, fb)
|
883
|
-
}
|
884
|
-
}
|
885
|
-
end
|
886
|
-
|
887
|
-
alias identical? compare_file
|
888
|
-
alias cmp compare_file
|
889
|
-
|
890
|
-
define_command(:compare_file)
|
891
|
-
define_command(:identical?)
|
892
|
-
define_command(:cmp)
|
893
|
-
|
894
|
-
#
|
895
|
-
# Returns true if the contents of a stream +a+ and +b+ are identical.
|
896
|
-
#
|
897
|
-
def compare_stream(a, b)
|
898
|
-
bsize = fu_stream_blksize(a, b)
|
899
|
-
sa = ""
|
900
|
-
sb = ""
|
901
|
-
begin
|
902
|
-
a.read(bsize, sa)
|
903
|
-
b.read(bsize, sb)
|
904
|
-
return true if sa.empty? && sb.empty?
|
905
|
-
end while sa == sb
|
906
|
-
false
|
907
|
-
end
|
908
|
-
|
909
|
-
define_command(:compare_stream)
|
910
|
-
|
911
|
-
#
|
912
|
-
# Options: mode preserve noop verbose
|
913
|
-
#
|
914
|
-
# If +src+ is not same as +dest+, copies it and changes the permission
|
915
|
-
# mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
|
916
|
-
# This method removes destination before copy.
|
917
|
-
#
|
918
|
-
# FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
|
919
|
-
# FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
|
920
|
-
#
|
921
|
-
def install(src, dest, options = {})
|
922
|
-
fu_check_options options, OPT_TABLE['install']
|
923
|
-
fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
|
924
|
-
return if options[:noop]
|
925
|
-
fu_each_src_dest(src, dest) do |s, d, st|
|
926
|
-
unless File.exist?(d) and compare_file(s, d)
|
927
|
-
remove_file d, true
|
928
|
-
copy_file s, d
|
929
|
-
File.utime st.atime, st.mtime, d if options[:preserve]
|
930
|
-
File.chmod options[:mode], d if options[:mode]
|
931
|
-
end
|
932
|
-
end
|
933
|
-
end
|
934
|
-
|
935
|
-
define_command('install', :mode, :preserve, :noop, :verbose)
|
936
|
-
|
937
|
-
private
|
938
|
-
|
939
|
-
def user_mask(target) #:nodoc:
|
940
|
-
mask = 0
|
941
|
-
target.each_byte do |byte_chr|
|
942
|
-
case byte_chr.chr
|
943
|
-
when "u"
|
944
|
-
mask |= 04700
|
945
|
-
when "g"
|
946
|
-
mask |= 02070
|
947
|
-
when "o"
|
948
|
-
mask |= 01007
|
949
|
-
when "a"
|
950
|
-
mask |= 07777
|
951
|
-
end
|
952
|
-
end
|
953
|
-
mask
|
954
|
-
end
|
955
|
-
|
956
|
-
def mode_mask(mode, path) #:nodoc:
|
957
|
-
mask = 0
|
958
|
-
mode.each_byte do |byte_chr|
|
959
|
-
case byte_chr.chr
|
960
|
-
when "r"
|
961
|
-
mask |= 0444
|
962
|
-
when "w"
|
963
|
-
mask |= 0222
|
964
|
-
when "x"
|
965
|
-
mask |= 0111
|
966
|
-
when "X"
|
967
|
-
mask |= 0111 if FileTest::directory? path
|
968
|
-
when "s"
|
969
|
-
mask |= 06000
|
970
|
-
when "t"
|
971
|
-
mask |= 01000
|
972
|
-
end
|
973
|
-
end
|
974
|
-
mask
|
975
|
-
end
|
976
|
-
|
977
|
-
def symbolic_modes_to_i(modes, path) #:nodoc:
|
978
|
-
current_mode = (File.stat(path).mode & 07777)
|
979
|
-
modes.split(/,/).inject(0) do |mode, mode_sym|
|
980
|
-
mode_sym = "a#{mode_sym}" if mode_sym =~ %r!^[+-=]!
|
981
|
-
target, mode = mode_sym.split %r![+-=]!
|
982
|
-
user_mask = user_mask(target)
|
983
|
-
mode_mask = mode_mask(mode ? mode : "", path)
|
984
|
-
|
985
|
-
case mode_sym
|
986
|
-
when /=/
|
987
|
-
current_mode &= ~(user_mask)
|
988
|
-
current_mode |= user_mask & mode_mask
|
989
|
-
when /\+/
|
990
|
-
current_mode |= user_mask & mode_mask
|
991
|
-
when /-/
|
992
|
-
current_mode &= ~(user_mask & mode_mask)
|
993
|
-
end
|
994
|
-
end
|
995
|
-
end
|
996
|
-
|
997
|
-
def fu_mode(mode, path) #:nodoc:
|
998
|
-
mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode
|
999
|
-
end
|
1000
|
-
|
1001
|
-
def mode_to_s(mode) #:nodoc:
|
1002
|
-
mode.is_a?(String) ? mode : "%o" % mode
|
1003
|
-
end
|
1004
|
-
|
1005
|
-
public
|
1006
|
-
|
1007
|
-
#
|
1008
|
-
# Options: noop verbose
|
1009
|
-
#
|
1010
|
-
# Changes permission bits on the named files (in +list+) to the bit pattern
|
1011
|
-
# represented by +mode+.
|
1012
|
-
#
|
1013
|
-
# +mode+ is the symbolic and absolute mode can be used.
|
1014
|
-
#
|
1015
|
-
# Absolute mode is
|
1016
|
-
# FileUtils.chmod 0755, 'somecommand'
|
1017
|
-
# FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
|
1018
|
-
# FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
|
1019
|
-
#
|
1020
|
-
# Symbolic mode is
|
1021
|
-
# FileUtils.chmod "u=wrx,go=rx", 'somecommand'
|
1022
|
-
# FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
|
1023
|
-
# FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
|
1024
|
-
#
|
1025
|
-
# "a" :: is user, group, other mask.
|
1026
|
-
# "u" :: is user's mask.
|
1027
|
-
# "g" :: is group's mask.
|
1028
|
-
# "o" :: is other's mask.
|
1029
|
-
# "w" :: is write permission.
|
1030
|
-
# "r" :: is read permission.
|
1031
|
-
# "x" :: is execute permission.
|
1032
|
-
# "X" ::
|
1033
|
-
# is execute permission for directories only, must be used in conjunction with "+"
|
1034
|
-
# "s" :: is uid, gid.
|
1035
|
-
# "t" :: is sticky bit.
|
1036
|
-
# "+" :: is added to a class given the specified mode.
|
1037
|
-
# "-" :: Is removed from a given class given mode.
|
1038
|
-
# "=" :: Is the exact nature of the class will be given a specified mode.
|
1039
|
-
|
1040
|
-
def chmod(mode, list, options = {})
|
1041
|
-
fu_check_options options, OPT_TABLE['chmod']
|
1042
|
-
list = fu_list(list)
|
1043
|
-
fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if options[:verbose]
|
1044
|
-
return if options[:noop]
|
1045
|
-
list.each do |path|
|
1046
|
-
Entry_.new(path).chmod(fu_mode(mode, path))
|
1047
|
-
end
|
1048
|
-
end
|
1049
|
-
|
1050
|
-
define_command('chmod', :noop, :verbose)
|
1051
|
-
|
1052
|
-
#
|
1053
|
-
# Options: noop verbose force
|
1054
|
-
#
|
1055
|
-
# Changes permission bits on the named files (in +list+)
|
1056
|
-
# to the bit pattern represented by +mode+.
|
1057
|
-
#
|
1058
|
-
# FileUtils.chmod_R 0700, "/tmp/app.#{$$}"
|
1059
|
-
# FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}"
|
1060
|
-
#
|
1061
|
-
def chmod_R(mode, list, options = {})
|
1062
|
-
fu_check_options options, OPT_TABLE['chmod_R']
|
1063
|
-
list = fu_list(list)
|
1064
|
-
fu_output_message sprintf('chmod -R%s %s %s',
|
1065
|
-
(options[:force] ? 'f' : ''),
|
1066
|
-
mode_to_s(mode), list.join(' ')) if options[:verbose]
|
1067
|
-
return if options[:noop]
|
1068
|
-
list.each do |root|
|
1069
|
-
Entry_.new(root).traverse do |ent|
|
1070
|
-
begin
|
1071
|
-
ent.chmod(fu_mode(mode, ent.path))
|
1072
|
-
rescue
|
1073
|
-
raise unless options[:force]
|
1074
|
-
end
|
1075
|
-
end
|
1076
|
-
end
|
1077
|
-
end
|
1078
|
-
|
1079
|
-
define_command('chmod_R', :noop, :verbose, :force)
|
1080
|
-
|
1081
|
-
#
|
1082
|
-
# Options: noop verbose
|
1083
|
-
#
|
1084
|
-
# Changes owner and group on the named files (in +list+)
|
1085
|
-
# to the user +user+ and the group +group+. +user+ and +group+
|
1086
|
-
# may be an ID (Integer/String) or a name (String).
|
1087
|
-
# If +user+ or +group+ is nil, this method does not change
|
1088
|
-
# the attribute.
|
1089
|
-
#
|
1090
|
-
# FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
|
1091
|
-
# FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
|
1092
|
-
#
|
1093
|
-
def chown(user, group, list, options = {})
|
1094
|
-
fu_check_options options, OPT_TABLE['chown']
|
1095
|
-
list = fu_list(list)
|
1096
|
-
fu_output_message sprintf('chown %s%s',
|
1097
|
-
[user,group].compact.join(':') + ' ',
|
1098
|
-
list.join(' ')) if options[:verbose]
|
1099
|
-
return if options[:noop]
|
1100
|
-
uid = fu_get_uid(user)
|
1101
|
-
gid = fu_get_gid(group)
|
1102
|
-
list.each do |path|
|
1103
|
-
Entry_.new(path).chown uid, gid
|
1104
|
-
end
|
1105
|
-
end
|
1106
|
-
|
1107
|
-
define_command('chown', :noop, :verbose)
|
1108
|
-
|
1109
|
-
#
|
1110
|
-
# Options: noop verbose force
|
1111
|
-
#
|
1112
|
-
# Changes owner and group on the named files (in +list+)
|
1113
|
-
# to the user +user+ and the group +group+ recursively.
|
1114
|
-
# +user+ and +group+ may be an ID (Integer/String) or
|
1115
|
-
# a name (String). If +user+ or +group+ is nil, this
|
1116
|
-
# method does not change the attribute.
|
1117
|
-
#
|
1118
|
-
# FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
|
1119
|
-
# FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
|
1120
|
-
#
|
1121
|
-
def chown_R(user, group, list, options = {})
|
1122
|
-
fu_check_options options, OPT_TABLE['chown_R']
|
1123
|
-
list = fu_list(list)
|
1124
|
-
fu_output_message sprintf('chown -R%s %s%s',
|
1125
|
-
(options[:force] ? 'f' : ''),
|
1126
|
-
[user,group].compact.join(':') + ' ',
|
1127
|
-
list.join(' ')) if options[:verbose]
|
1128
|
-
return if options[:noop]
|
1129
|
-
uid = fu_get_uid(user)
|
1130
|
-
gid = fu_get_gid(group)
|
1131
|
-
return unless uid or gid
|
1132
|
-
list.each do |root|
|
1133
|
-
Entry_.new(root).traverse do |ent|
|
1134
|
-
begin
|
1135
|
-
ent.chown uid, gid
|
1136
|
-
rescue
|
1137
|
-
raise unless options[:force]
|
1138
|
-
end
|
1139
|
-
end
|
1140
|
-
end
|
1141
|
-
end
|
1142
|
-
|
1143
|
-
define_command('chown_R', :noop, :verbose, :force)
|
1144
|
-
|
1145
|
-
private
|
1146
|
-
|
1147
|
-
begin
|
1148
|
-
require 'etc'
|
1149
|
-
|
1150
|
-
def fu_get_uid(user) #:nodoc:
|
1151
|
-
return nil unless user
|
1152
|
-
case user
|
1153
|
-
when Integer
|
1154
|
-
user
|
1155
|
-
when /\A\d+\z/
|
1156
|
-
user.to_i
|
1157
|
-
else
|
1158
|
-
Etc.getpwnam(user).uid
|
1159
|
-
end
|
1160
|
-
end
|
1161
|
-
|
1162
|
-
def fu_get_gid(group) #:nodoc:
|
1163
|
-
return nil unless group
|
1164
|
-
case group
|
1165
|
-
when Integer
|
1166
|
-
group
|
1167
|
-
when /\A\d+\z/
|
1168
|
-
group.to_i
|
1169
|
-
else
|
1170
|
-
Etc.getgrnam(group).gid
|
1171
|
-
end
|
1172
|
-
end
|
1173
|
-
|
1174
|
-
rescue LoadError
|
1175
|
-
# need Win32 support???
|
1176
|
-
|
1177
|
-
def fu_get_uid(user) #:nodoc:
|
1178
|
-
user # FIXME
|
1179
|
-
end
|
1180
|
-
|
1181
|
-
def fu_get_gid(group) #:nodoc:
|
1182
|
-
group # FIXME
|
1183
|
-
end
|
1184
|
-
end
|
1185
|
-
|
1186
|
-
public
|
1187
|
-
|
1188
|
-
#
|
1189
|
-
# Options: noop verbose
|
1190
|
-
#
|
1191
|
-
# Updates modification time (mtime) and access time (atime) of file(s) in
|
1192
|
-
# +list+. Files are created if they don't exist.
|
1193
|
-
#
|
1194
|
-
# FileUtils.touch 'timestamp'
|
1195
|
-
# FileUtils.touch Dir.glob('*.c'); system 'make'
|
1196
|
-
#
|
1197
|
-
def touch(list, options = {})
|
1198
|
-
fu_check_options options, OPT_TABLE['touch']
|
1199
|
-
list = fu_list(list)
|
1200
|
-
created = nocreate = options[:nocreate]
|
1201
|
-
t = options[:mtime]
|
1202
|
-
if options[:verbose]
|
1203
|
-
fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}"
|
1204
|
-
end
|
1205
|
-
return if options[:noop]
|
1206
|
-
list.each do |path|
|
1207
|
-
created = nocreate
|
1208
|
-
begin
|
1209
|
-
File.utime(t, t, path)
|
1210
|
-
rescue Errno::ENOENT
|
1211
|
-
raise if created
|
1212
|
-
File.open(path, 'a') {
|
1213
|
-
;
|
1214
|
-
}
|
1215
|
-
created = true
|
1216
|
-
retry if t
|
1217
|
-
end
|
1218
|
-
end
|
1219
|
-
end
|
1220
|
-
|
1221
|
-
define_command('touch', :noop, :verbose, :mtime, :nocreate)
|
1222
|
-
|
1223
|
-
private
|
1224
|
-
|
1225
|
-
module StreamUtils_
|
1226
|
-
private
|
1227
|
-
|
1228
|
-
def fu_windows?
|
1229
|
-
/mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM
|
1230
|
-
end
|
1231
|
-
|
1232
|
-
def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
|
1233
|
-
IO.copy_stream(src, dest)
|
1234
|
-
end
|
1235
|
-
|
1236
|
-
def fu_stream_blksize(*streams)
|
1237
|
-
streams.each do |s|
|
1238
|
-
next unless s.respond_to?(:stat)
|
1239
|
-
size = fu_blksize(s.stat)
|
1240
|
-
return size if size
|
1241
|
-
end
|
1242
|
-
fu_default_blksize()
|
1243
|
-
end
|
1244
|
-
|
1245
|
-
def fu_blksize(st)
|
1246
|
-
s = st.blksize
|
1247
|
-
return nil unless s
|
1248
|
-
return nil if s == 0
|
1249
|
-
s
|
1250
|
-
end
|
1251
|
-
|
1252
|
-
def fu_default_blksize
|
1253
|
-
1024
|
1254
|
-
end
|
1255
|
-
end
|
1256
|
-
|
1257
|
-
include StreamUtils_
|
1258
|
-
extend StreamUtils_
|
1259
|
-
|
1260
|
-
class Entry_ #:nodoc: internal use only
|
1261
|
-
include StreamUtils_
|
1262
|
-
|
1263
|
-
def initialize(a, b = nil, deref = false)
|
1264
|
-
@prefix = @rel = @path = nil
|
1265
|
-
if b
|
1266
|
-
@prefix = a
|
1267
|
-
@rel = b
|
1268
|
-
else
|
1269
|
-
@path = a
|
1270
|
-
end
|
1271
|
-
@deref = deref
|
1272
|
-
@stat = nil
|
1273
|
-
@lstat = nil
|
1274
|
-
end
|
1275
|
-
|
1276
|
-
def inspect
|
1277
|
-
"\#<#{self.class} #{path()}>"
|
1278
|
-
end
|
1279
|
-
|
1280
|
-
def path
|
1281
|
-
if @path
|
1282
|
-
File.path(@path)
|
1283
|
-
else
|
1284
|
-
join(@prefix, @rel)
|
1285
|
-
end
|
1286
|
-
end
|
1287
|
-
|
1288
|
-
def prefix
|
1289
|
-
@prefix || @path
|
1290
|
-
end
|
1291
|
-
|
1292
|
-
def rel
|
1293
|
-
@rel
|
1294
|
-
end
|
1295
|
-
|
1296
|
-
def dereference?
|
1297
|
-
@deref
|
1298
|
-
end
|
1299
|
-
|
1300
|
-
def exist?
|
1301
|
-
lstat! ? true : false
|
1302
|
-
end
|
1303
|
-
|
1304
|
-
def file?
|
1305
|
-
s = lstat!
|
1306
|
-
s and s.file?
|
1307
|
-
end
|
1308
|
-
|
1309
|
-
def directory?
|
1310
|
-
s = lstat!
|
1311
|
-
s and s.directory?
|
1312
|
-
end
|
1313
|
-
|
1314
|
-
def symlink?
|
1315
|
-
s = lstat!
|
1316
|
-
s and s.symlink?
|
1317
|
-
end
|
1318
|
-
|
1319
|
-
def chardev?
|
1320
|
-
s = lstat!
|
1321
|
-
s and s.chardev?
|
1322
|
-
end
|
1323
|
-
|
1324
|
-
def blockdev?
|
1325
|
-
s = lstat!
|
1326
|
-
s and s.blockdev?
|
1327
|
-
end
|
1328
|
-
|
1329
|
-
def socket?
|
1330
|
-
s = lstat!
|
1331
|
-
s and s.socket?
|
1332
|
-
end
|
1333
|
-
|
1334
|
-
def pipe?
|
1335
|
-
s = lstat!
|
1336
|
-
s and s.pipe?
|
1337
|
-
end
|
1338
|
-
|
1339
|
-
S_IF_DOOR = 0xD000
|
1340
|
-
|
1341
|
-
def door?
|
1342
|
-
s = lstat!
|
1343
|
-
s and (s.mode & 0xF000 == S_IF_DOOR)
|
1344
|
-
end
|
1345
|
-
|
1346
|
-
def entries
|
1347
|
-
opts = {}
|
1348
|
-
opts[:encoding] = ::Encoding::UTF_8 if fu_windows?
|
1349
|
-
Dir.entries(path(), opts)\
|
1350
|
-
.reject {|n| n == '.' or n == '..' }\
|
1351
|
-
.map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) }
|
1352
|
-
end
|
1353
|
-
|
1354
|
-
def stat
|
1355
|
-
return @stat if @stat
|
1356
|
-
if lstat() and lstat().symlink?
|
1357
|
-
@stat = File.stat(path())
|
1358
|
-
else
|
1359
|
-
@stat = lstat()
|
1360
|
-
end
|
1361
|
-
@stat
|
1362
|
-
end
|
1363
|
-
|
1364
|
-
def stat!
|
1365
|
-
return @stat if @stat
|
1366
|
-
if lstat! and lstat!.symlink?
|
1367
|
-
@stat = File.stat(path())
|
1368
|
-
else
|
1369
|
-
@stat = lstat!
|
1370
|
-
end
|
1371
|
-
@stat
|
1372
|
-
rescue SystemCallError
|
1373
|
-
nil
|
1374
|
-
end
|
1375
|
-
|
1376
|
-
def lstat
|
1377
|
-
if dereference?
|
1378
|
-
@lstat ||= File.stat(path())
|
1379
|
-
else
|
1380
|
-
@lstat ||= File.lstat(path())
|
1381
|
-
end
|
1382
|
-
end
|
1383
|
-
|
1384
|
-
def lstat!
|
1385
|
-
lstat()
|
1386
|
-
rescue SystemCallError
|
1387
|
-
nil
|
1388
|
-
end
|
1389
|
-
|
1390
|
-
def chmod(mode)
|
1391
|
-
if symlink?
|
1392
|
-
File.lchmod mode, path() if have_lchmod?
|
1393
|
-
else
|
1394
|
-
File.chmod mode, path()
|
1395
|
-
end
|
1396
|
-
end
|
1397
|
-
|
1398
|
-
def chown(uid, gid)
|
1399
|
-
if symlink?
|
1400
|
-
File.lchown uid, gid, path() if have_lchown?
|
1401
|
-
else
|
1402
|
-
File.chown uid, gid, path()
|
1403
|
-
end
|
1404
|
-
end
|
1405
|
-
|
1406
|
-
def copy(dest)
|
1407
|
-
case
|
1408
|
-
when file?
|
1409
|
-
copy_file dest
|
1410
|
-
when directory?
|
1411
|
-
if !File.exist?(dest) and descendant_diretory?(dest, path)
|
1412
|
-
raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest]
|
1413
|
-
end
|
1414
|
-
begin
|
1415
|
-
Dir.mkdir dest
|
1416
|
-
rescue
|
1417
|
-
raise unless File.directory?(dest)
|
1418
|
-
end
|
1419
|
-
when symlink?
|
1420
|
-
File.symlink File.readlink(path()), dest
|
1421
|
-
when chardev?
|
1422
|
-
raise "cannot handle device file" unless File.respond_to?(:mknod)
|
1423
|
-
mknod dest, ?c, 0666, lstat().rdev
|
1424
|
-
when blockdev?
|
1425
|
-
raise "cannot handle device file" unless File.respond_to?(:mknod)
|
1426
|
-
mknod dest, ?b, 0666, lstat().rdev
|
1427
|
-
when socket?
|
1428
|
-
raise "cannot handle socket" unless File.respond_to?(:mknod)
|
1429
|
-
mknod dest, nil, lstat().mode, 0
|
1430
|
-
when pipe?
|
1431
|
-
raise "cannot handle FIFO" unless File.respond_to?(:mkfifo)
|
1432
|
-
mkfifo dest, 0666
|
1433
|
-
when door?
|
1434
|
-
raise "cannot handle door: #{path()}"
|
1435
|
-
else
|
1436
|
-
raise "unknown file type: #{path()}"
|
1437
|
-
end
|
1438
|
-
end
|
1439
|
-
|
1440
|
-
def copy_file(dest)
|
1441
|
-
File.open(path()) do |s|
|
1442
|
-
File.open(dest, 'wb', s.stat.mode) do |f|
|
1443
|
-
IO.copy_stream(s, f)
|
1444
|
-
end
|
1445
|
-
end
|
1446
|
-
end
|
1447
|
-
|
1448
|
-
def copy_metadata(path)
|
1449
|
-
st = lstat()
|
1450
|
-
if !st.symlink?
|
1451
|
-
File.utime st.atime, st.mtime, path
|
1452
|
-
end
|
1453
|
-
begin
|
1454
|
-
if st.symlink?
|
1455
|
-
begin
|
1456
|
-
File.lchown st.uid, st.gid, path
|
1457
|
-
rescue NotImplementedError
|
1458
|
-
end
|
1459
|
-
else
|
1460
|
-
File.chown st.uid, st.gid, path
|
1461
|
-
end
|
1462
|
-
rescue Errno::EPERM
|
1463
|
-
# clear setuid/setgid
|
1464
|
-
if st.symlink?
|
1465
|
-
begin
|
1466
|
-
File.lchmod st.mode & 01777, path
|
1467
|
-
rescue NotImplementedError
|
1468
|
-
end
|
1469
|
-
else
|
1470
|
-
File.chmod st.mode & 01777, path
|
1471
|
-
end
|
1472
|
-
else
|
1473
|
-
if st.symlink?
|
1474
|
-
begin
|
1475
|
-
File.lchmod st.mode, path
|
1476
|
-
rescue NotImplementedError
|
1477
|
-
end
|
1478
|
-
else
|
1479
|
-
File.chmod st.mode, path
|
1480
|
-
end
|
1481
|
-
end
|
1482
|
-
end
|
1483
|
-
|
1484
|
-
def remove
|
1485
|
-
if directory?
|
1486
|
-
remove_dir1
|
1487
|
-
else
|
1488
|
-
remove_file
|
1489
|
-
end
|
1490
|
-
end
|
1491
|
-
|
1492
|
-
def remove_dir1
|
1493
|
-
platform_support {
|
1494
|
-
Dir.rmdir path().chomp(?/)
|
1495
|
-
}
|
1496
|
-
end
|
1497
|
-
|
1498
|
-
def remove_file
|
1499
|
-
platform_support {
|
1500
|
-
File.unlink path
|
1501
|
-
}
|
1502
|
-
end
|
1503
|
-
|
1504
|
-
def platform_support
|
1505
|
-
return yield unless fu_windows?
|
1506
|
-
first_time_p = true
|
1507
|
-
begin
|
1508
|
-
yield
|
1509
|
-
rescue Errno::ENOENT
|
1510
|
-
raise
|
1511
|
-
rescue => err
|
1512
|
-
if first_time_p
|
1513
|
-
first_time_p = false
|
1514
|
-
begin
|
1515
|
-
File.chmod 0700, path() # Windows does not have symlink
|
1516
|
-
retry
|
1517
|
-
rescue SystemCallError
|
1518
|
-
end
|
1519
|
-
end
|
1520
|
-
raise err
|
1521
|
-
end
|
1522
|
-
end
|
1523
|
-
|
1524
|
-
def preorder_traverse
|
1525
|
-
stack = [self]
|
1526
|
-
while ent = stack.pop
|
1527
|
-
yield ent
|
1528
|
-
stack.concat ent.entries.reverse if ent.directory?
|
1529
|
-
end
|
1530
|
-
end
|
1531
|
-
|
1532
|
-
alias traverse preorder_traverse
|
1533
|
-
|
1534
|
-
def postorder_traverse
|
1535
|
-
if directory?
|
1536
|
-
entries().each do |ent|
|
1537
|
-
ent.postorder_traverse do |e|
|
1538
|
-
yield e
|
1539
|
-
end
|
1540
|
-
end
|
1541
|
-
end
|
1542
|
-
yield self
|
1543
|
-
end
|
1544
|
-
|
1545
|
-
def wrap_traverse(pre, post)
|
1546
|
-
pre.call self
|
1547
|
-
if directory?
|
1548
|
-
entries.each do |ent|
|
1549
|
-
ent.wrap_traverse pre, post
|
1550
|
-
end
|
1551
|
-
end
|
1552
|
-
post.call self
|
1553
|
-
end
|
1554
|
-
|
1555
|
-
private
|
1556
|
-
|
1557
|
-
$fileutils_rb_have_lchmod = nil
|
1558
|
-
|
1559
|
-
def have_lchmod?
|
1560
|
-
# This is not MT-safe, but it does not matter.
|
1561
|
-
if $fileutils_rb_have_lchmod == nil
|
1562
|
-
$fileutils_rb_have_lchmod = check_have_lchmod?
|
1563
|
-
end
|
1564
|
-
$fileutils_rb_have_lchmod
|
1565
|
-
end
|
1566
|
-
|
1567
|
-
def check_have_lchmod?
|
1568
|
-
return false unless File.respond_to?(:lchmod)
|
1569
|
-
File.lchmod 0
|
1570
|
-
return true
|
1571
|
-
rescue NotImplementedError
|
1572
|
-
return false
|
1573
|
-
end
|
1574
|
-
|
1575
|
-
$fileutils_rb_have_lchown = nil
|
1576
|
-
|
1577
|
-
def have_lchown?
|
1578
|
-
# This is not MT-safe, but it does not matter.
|
1579
|
-
if $fileutils_rb_have_lchown == nil
|
1580
|
-
$fileutils_rb_have_lchown = check_have_lchown?
|
1581
|
-
end
|
1582
|
-
$fileutils_rb_have_lchown
|
1583
|
-
end
|
1584
|
-
|
1585
|
-
def check_have_lchown?
|
1586
|
-
return false unless File.respond_to?(:lchown)
|
1587
|
-
File.lchown nil, nil
|
1588
|
-
return true
|
1589
|
-
rescue NotImplementedError
|
1590
|
-
return false
|
1591
|
-
end
|
1592
|
-
|
1593
|
-
def join(dir, base)
|
1594
|
-
return File.path(dir) if not base or base == '.'
|
1595
|
-
return File.path(base) if not dir or dir == '.'
|
1596
|
-
File.join(dir, base)
|
1597
|
-
end
|
1598
|
-
|
1599
|
-
if File::ALT_SEPARATOR
|
1600
|
-
DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)".freeze
|
1601
|
-
else
|
1602
|
-
DIRECTORY_TERM = "(?=/|\\z)".freeze
|
1603
|
-
end
|
1604
|
-
SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : ""
|
1605
|
-
|
1606
|
-
def descendant_diretory?(descendant, ascendant)
|
1607
|
-
/\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant)
|
1608
|
-
end
|
1609
|
-
end # class Entry_
|
1610
|
-
|
1611
|
-
private
|
1612
|
-
|
1613
|
-
def fu_list(arg) #:nodoc:
|
1614
|
-
[arg].flatten.map {|path| File.path(path) }
|
1615
|
-
end
|
1616
|
-
|
1617
|
-
def fu_each_src_dest(src, dest) #:nodoc:
|
1618
|
-
fu_each_src_dest0(src, dest) do |s, d|
|
1619
|
-
raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d)
|
1620
|
-
yield s, d, File.stat(s)
|
1621
|
-
end
|
1622
|
-
end
|
1623
|
-
|
1624
|
-
def fu_each_src_dest0(src, dest) #:nodoc:
|
1625
|
-
if tmp = Array.try_convert(src)
|
1626
|
-
tmp.each do |s|
|
1627
|
-
s = File.path(s)
|
1628
|
-
yield s, File.join(dest, File.basename(s))
|
1629
|
-
end
|
1630
|
-
else
|
1631
|
-
src = File.path(src)
|
1632
|
-
if File.directory?(dest)
|
1633
|
-
yield src, File.join(dest, File.basename(src))
|
1634
|
-
else
|
1635
|
-
yield src, File.path(dest)
|
1636
|
-
end
|
1637
|
-
end
|
1638
|
-
end
|
1639
|
-
|
1640
|
-
def fu_same?(a, b) #:nodoc:
|
1641
|
-
File.identical?(a, b)
|
1642
|
-
end
|
1643
|
-
|
1644
|
-
def fu_check_options(options, optdecl) #:nodoc:
|
1645
|
-
h = options.dup
|
1646
|
-
optdecl.each do |opt|
|
1647
|
-
h.delete opt
|
1648
|
-
end
|
1649
|
-
raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
|
1650
|
-
end
|
1651
|
-
|
1652
|
-
def fu_update_option(args, new) #:nodoc:
|
1653
|
-
if tmp = Hash.try_convert(args.last)
|
1654
|
-
args[-1] = tmp.dup.update(new)
|
1655
|
-
else
|
1656
|
-
args.push new
|
1657
|
-
end
|
1658
|
-
args
|
1659
|
-
end
|
1660
|
-
|
1661
|
-
def fu_output_message(msg) #:nodoc:
|
1662
|
-
@fileutils_output ||= $stderr
|
1663
|
-
@fileutils_label ||= ''
|
1664
|
-
@fileutils_output.puts @fileutils_label + msg
|
1665
|
-
end
|
1666
|
-
|
1667
|
-
#
|
1668
|
-
# Returns an Array of method names which have any options.
|
1669
|
-
#
|
1670
|
-
# p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
|
1671
|
-
#
|
1672
|
-
def FileUtils.commands
|
1673
|
-
OPT_TABLE.keys
|
1674
|
-
end
|
1675
|
-
|
1676
|
-
#
|
1677
|
-
# Returns an Array of option names.
|
1678
|
-
#
|
1679
|
-
# p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"]
|
1680
|
-
#
|
1681
|
-
def FileUtils.options
|
1682
|
-
OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s }
|
1683
|
-
end
|
1684
|
-
|
1685
|
-
#
|
1686
|
-
# Returns true if the method +mid+ have an option +opt+.
|
1687
|
-
#
|
1688
|
-
# p FileUtils.have_option?(:cp, :noop) #=> true
|
1689
|
-
# p FileUtils.have_option?(:rm, :force) #=> true
|
1690
|
-
# p FileUtils.have_option?(:rm, :perserve) #=> false
|
1691
|
-
#
|
1692
|
-
def FileUtils.have_option?(mid, opt)
|
1693
|
-
li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}"
|
1694
|
-
li.include?(opt)
|
1695
|
-
end
|
1696
|
-
|
1697
|
-
#
|
1698
|
-
# Returns an Array of option names of the method +mid+.
|
1699
|
-
#
|
1700
|
-
# p FileUtils.options(:rm) #=> ["noop", "verbose", "force"]
|
1701
|
-
#
|
1702
|
-
def FileUtils.options_of(mid)
|
1703
|
-
OPT_TABLE[mid.to_s].map {|sym| sym.to_s }
|
1704
|
-
end
|
1705
|
-
|
1706
|
-
#
|
1707
|
-
# Returns an Array of method names which have the option +opt+.
|
1708
|
-
#
|
1709
|
-
# p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
|
1710
|
-
#
|
1711
|
-
def FileUtils.collect_method(opt)
|
1712
|
-
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
|
1713
|
-
end
|
1714
|
-
|
1715
|
-
# LOW_METHODS
|
1716
|
-
#
|
1717
|
-
# :pwd, :getwd, :cd, :chdir,
|
1718
|
-
# :uptodate?, :copy_entry, :copy_file, :copy_stream, :remove_entry_secure,
|
1719
|
-
# :remove_entry, :remove_file, :remove_dir, :compare_file, :identical?,
|
1720
|
-
# :cmp, :compare_stream
|
1721
|
-
#
|
1722
|
-
# DEPRECATED - Only here for backward compatibility.
|
1723
|
-
LOW_METHODS = (commands - collect_method(:noop)).map(&:to_sym)
|
1724
|
-
|
1725
|
-
|
1726
|
-
# METHODS
|
1727
|
-
#
|
1728
|
-
# :pwd, :getwd, :cd, :chdir, :uptodate?, :mkdir, :mkdir_p, :mkpath, :makedirs,
|
1729
|
-
# :rmdir, :ln, :link, :ln_s, :symlink, :ln_sf, :cp, :copy, :cp_r, :copy_entry,
|
1730
|
-
# :copy_file, :copy_stream, :mv, :move, :rm, :remove, :rm_f, :safe_unlink,
|
1731
|
-
# :rm_r, :rm_rf, :rmtree, :remove_entry_secure, :remove_entry, :remove_file,
|
1732
|
-
# :remove_dir, :compare_file, :identical?, :cmp, :compare_stream, :install,
|
1733
|
-
# :chmod, :chmod_R, :chown, :chown_R, :touch
|
1734
|
-
#
|
1735
|
-
# DEPRECATED - Only here for backward compatibility.
|
1736
|
-
METHODS = commands.map(&:to_sym)
|
1737
|
-
|
1738
|
-
end
|