rhodes 2.0.0.beta9 → 2.0.0.beta10

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