shell 0.0.1 → 0.7

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.
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # shell/builtin-command.rb -
4
+ # $Release Version: 0.7 $
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require_relative "filter"
14
+
15
+ class Shell
16
+ class BuiltInCommand < Filter
17
+ def wait?
18
+ false
19
+ end
20
+ def active?
21
+ true
22
+ end
23
+ end
24
+
25
+ class Void < BuiltInCommand
26
+ def initialize(sh, *opts)
27
+ super sh
28
+ end
29
+
30
+ def each(rs = nil)
31
+ # do nothing
32
+ end
33
+ end
34
+
35
+ class Echo < BuiltInCommand
36
+ def initialize(sh, *strings)
37
+ super sh
38
+ @strings = strings
39
+ end
40
+
41
+ def each(rs = nil)
42
+ rs = @shell.record_separator unless rs
43
+ for str in @strings
44
+ yield str + rs
45
+ end
46
+ end
47
+ end
48
+
49
+ class Cat < BuiltInCommand
50
+ def initialize(sh, *filenames)
51
+ super sh
52
+ @cat_files = filenames
53
+ end
54
+
55
+ def each(rs = nil)
56
+ if @cat_files.empty?
57
+ super
58
+ else
59
+ for src in @cat_files
60
+ @shell.foreach(src, rs){|l| yield l}
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ class Glob < BuiltInCommand
67
+ def initialize(sh, pattern)
68
+ super sh
69
+
70
+ @pattern = pattern
71
+ end
72
+
73
+ def each(rs = nil)
74
+ if @pattern[0] == ?/
75
+ @files = Dir[@pattern]
76
+ else
77
+ prefix = @shell.pwd+"/"
78
+ @files = Dir[prefix+@pattern].collect{|p| p.sub(prefix, "")}
79
+ end
80
+ rs = @shell.record_separator unless rs
81
+ for f in @files
82
+ yield f+rs
83
+ end
84
+ end
85
+ end
86
+
87
+ class AppendIO < BuiltInCommand
88
+ def initialize(sh, io, filter)
89
+ super sh
90
+ @input = filter
91
+ @io = io
92
+ end
93
+
94
+ def input=(filter)
95
+ @input.input=filter
96
+ for l in @input
97
+ @io << l
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ class AppendFile < AppendIO
104
+ def initialize(sh, to_filename, filter)
105
+ @file_name = to_filename
106
+ io = sh.open(to_filename, "a")
107
+ super(sh, io, filter)
108
+ end
109
+
110
+ def input=(filter)
111
+ begin
112
+ super
113
+ ensure
114
+ @io.close
115
+ end
116
+ end
117
+ end
118
+
119
+ class Tee < BuiltInCommand
120
+ def initialize(sh, filename)
121
+ super sh
122
+ @to_filename = filename
123
+ end
124
+
125
+ def each(rs = nil)
126
+ to = @shell.open(@to_filename, "w")
127
+ begin
128
+ super{|l| to << l; yield l}
129
+ ensure
130
+ to.close
131
+ end
132
+ end
133
+ end
134
+
135
+ class Concat < BuiltInCommand
136
+ def initialize(sh, *jobs)
137
+ super(sh)
138
+ @jobs = jobs
139
+ end
140
+
141
+ def each(rs = nil)
142
+ while job = @jobs.shift
143
+ job.each{|l| yield l}
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,668 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # shell/command-controller.rb -
4
+ # $Release Version: 0.7 $
5
+ # $Revision$
6
+ # by Keiju ISHITSUKA(keiju@ruby-lang.org)
7
+ #
8
+ # --
9
+ #
10
+ #
11
+ #
12
+
13
+ require "e2mmap"
14
+
15
+ require_relative "error"
16
+ require_relative "filter"
17
+ require_relative "system-command"
18
+ require_relative "builtin-command"
19
+
20
+ class Shell
21
+ # In order to execute a command on your OS, you need to define it as a
22
+ # Shell method.
23
+ #
24
+ # Alternatively, you can execute any command via
25
+ # Shell::CommandProcessor#system even if it is not defined.
26
+ class CommandProcessor
27
+
28
+ #
29
+ # initialize of Shell and related classes.
30
+ #
31
+ m = [:initialize, :expand_path]
32
+ if Object.methods.first.kind_of?(String)
33
+ NoDelegateMethods = m.collect{|x| x.id2name}
34
+ else
35
+ NoDelegateMethods = m
36
+ end
37
+
38
+ def self.initialize
39
+
40
+ install_builtin_commands
41
+
42
+ # define CommandProcessor#methods to Shell#methods and Filter#methods
43
+ for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
44
+ add_delegate_command_to_shell(m)
45
+ end
46
+
47
+ def self.method_added(id)
48
+ add_delegate_command_to_shell(id)
49
+ end
50
+ end
51
+
52
+ #
53
+ # include run file.
54
+ #
55
+ def self.run_config
56
+ rc = "~/.rb_shell"
57
+ begin
58
+ load File.expand_path(rc) if ENV.key?("HOME")
59
+ rescue LoadError, Errno::ENOENT
60
+ rescue
61
+ print "load error: #{rc}\n"
62
+ print $!.class, ": ", $!, "\n"
63
+ for err in $@[0, $@.size - 2]
64
+ print "\t", err, "\n"
65
+ end
66
+ end
67
+ end
68
+
69
+ def initialize(shell)
70
+ @shell = shell
71
+ @system_commands = {}
72
+ end
73
+
74
+ #
75
+ # CommandProcessor#expand_path(path)
76
+ # path: String
77
+ # return: String
78
+ # returns the absolute path for <path>
79
+ #
80
+ def expand_path(path)
81
+ @shell.expand_path(path)
82
+ end
83
+
84
+ # call-seq:
85
+ # foreach(path, record_separator) -> Enumerator
86
+ # foreach(path, record_separator) { block }
87
+ #
88
+ # See IO.foreach when +path+ is a file.
89
+ #
90
+ # See Dir.foreach when +path+ is a directory.
91
+ #
92
+ def foreach(path = nil, *rs)
93
+ path = "." unless path
94
+ path = expand_path(path)
95
+
96
+ if File.directory?(path)
97
+ Dir.foreach(path){|fn| yield fn}
98
+ else
99
+ IO.foreach(path, *rs){|l| yield l}
100
+ end
101
+ end
102
+
103
+ # call-seq:
104
+ # open(path, mode, permissions) -> Enumerator
105
+ # open(path, mode, permissions) { block }
106
+ #
107
+ # See IO.open when +path+ is a file.
108
+ #
109
+ # See Dir.open when +path+ is a directory.
110
+ #
111
+ def open(path, mode = nil, perm = 0666, &b)
112
+ path = expand_path(path)
113
+ if File.directory?(path)
114
+ Dir.open(path, &b)
115
+ else
116
+ if @shell.umask
117
+ f = File.open(path, mode, perm)
118
+ File.chmod(perm & ~@shell.umask, path)
119
+ if block_given?
120
+ f.each(&b)
121
+ end
122
+ f
123
+ else
124
+ File.open(path, mode, perm, &b)
125
+ end
126
+ end
127
+ end
128
+
129
+ # call-seq:
130
+ # unlink(path)
131
+ #
132
+ # See IO.unlink when +path+ is a file.
133
+ #
134
+ # See Dir.unlink when +path+ is a directory.
135
+ #
136
+ def unlink(path)
137
+ @shell.check_point
138
+
139
+ path = expand_path(path)
140
+ if File.directory?(path)
141
+ Dir.unlink(path)
142
+ else
143
+ IO.unlink(path)
144
+ end
145
+ Void.new(@shell)
146
+ end
147
+
148
+ # See Shell::CommandProcessor#test
149
+ alias top_level_test test
150
+ # call-seq:
151
+ # test(command, file1, file2) -> true or false
152
+ # [command, file1, file2] -> true or false
153
+ #
154
+ # Tests if the given +command+ exists in +file1+, or optionally +file2+.
155
+ #
156
+ # Example:
157
+ # sh[?e, "foo"]
158
+ # sh[:e, "foo"]
159
+ # sh["e", "foo"]
160
+ # sh[:exists?, "foo"]
161
+ # sh["exists?", "foo"]
162
+ #
163
+ def test(command, file1, file2=nil)
164
+ file1 = expand_path(file1)
165
+ file2 = expand_path(file2) if file2
166
+ command = command.id2name if command.kind_of?(Symbol)
167
+
168
+ case command
169
+ when Integer
170
+ if file2
171
+ top_level_test(command, file1, file2)
172
+ else
173
+ top_level_test(command, file1)
174
+ end
175
+ when String
176
+ if command.size == 1
177
+ if file2
178
+ top_level_test(command, file1, file2)
179
+ else
180
+ top_level_test(command, file1)
181
+ end
182
+ else
183
+ if file2
184
+ FileTest.send(command, file1, file2)
185
+ else
186
+ FileTest.send(command, file1)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ # See Shell::CommandProcessor#test
192
+ alias [] test
193
+
194
+ # call-seq:
195
+ # mkdir(path)
196
+ #
197
+ # Same as Dir.mkdir, except multiple directories are allowed.
198
+ def mkdir(*path)
199
+ @shell.check_point
200
+ notify("mkdir #{path.join(' ')}")
201
+
202
+ perm = nil
203
+ if path.last.kind_of?(Integer)
204
+ perm = path.pop
205
+ end
206
+ for dir in path
207
+ d = expand_path(dir)
208
+ if perm
209
+ Dir.mkdir(d, perm)
210
+ else
211
+ Dir.mkdir(d)
212
+ end
213
+ File.chmod(d, 0666 & ~@shell.umask) if @shell.umask
214
+ end
215
+ Void.new(@shell)
216
+ end
217
+
218
+ # call-seq:
219
+ # rmdir(path)
220
+ #
221
+ # Same as Dir.rmdir, except multiple directories are allowed.
222
+ def rmdir(*path)
223
+ @shell.check_point
224
+ notify("rmdir #{path.join(' ')}")
225
+
226
+ for dir in path
227
+ Dir.rmdir(expand_path(dir))
228
+ end
229
+ Void.new(@shell)
230
+ end
231
+
232
+ # call-seq:
233
+ # system(command, *options) -> SystemCommand
234
+ #
235
+ # Executes the given +command+ with the +options+ parameter.
236
+ #
237
+ # Example:
238
+ # print sh.system("ls", "-l")
239
+ # sh.system("ls", "-l") | sh.head > STDOUT
240
+ #
241
+ def system(command, *opts)
242
+ if opts.empty?
243
+ if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
244
+ return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
245
+ else
246
+ command, *opts = command.split(/\s+/)
247
+ end
248
+ end
249
+ SystemCommand.new(@shell, find_system_command(command), *opts)
250
+ end
251
+
252
+ # call-seq:
253
+ # rehash
254
+ #
255
+ # Clears the command hash table.
256
+ def rehash
257
+ @system_commands = {}
258
+ end
259
+
260
+ def check_point # :nodoc:
261
+ @shell.process_controller.wait_all_jobs_execution
262
+ end
263
+ alias finish_all_jobs check_point # :nodoc:
264
+
265
+ # call-seq:
266
+ # transact { block }
267
+ #
268
+ # Executes a block as self
269
+ #
270
+ # Example:
271
+ # sh.transact { system("ls", "-l") | head > STDOUT }
272
+ def transact(&block)
273
+ begin
274
+ @shell.instance_eval(&block)
275
+ ensure
276
+ check_point
277
+ end
278
+ end
279
+
280
+ # call-seq:
281
+ # out(device) { block }
282
+ #
283
+ # Calls <code>device.print</code> on the result passing the _block_ to
284
+ # #transact
285
+ def out(dev = STDOUT, &block)
286
+ dev.print transact(&block)
287
+ end
288
+
289
+ # call-seq:
290
+ # echo(*strings) -> Echo
291
+ #
292
+ # Returns a Echo object, for the given +strings+
293
+ def echo(*strings)
294
+ Echo.new(@shell, *strings)
295
+ end
296
+
297
+ # call-seq:
298
+ # cat(*filename) -> Cat
299
+ #
300
+ # Returns a Cat object, for the given +filenames+
301
+ def cat(*filenames)
302
+ Cat.new(@shell, *filenames)
303
+ end
304
+
305
+ # def sort(*filenames)
306
+ # Sort.new(self, *filenames)
307
+ # end
308
+ # call-seq:
309
+ # glob(pattern) -> Glob
310
+ #
311
+ # Returns a Glob filter object, with the given +pattern+ object
312
+ def glob(pattern)
313
+ Glob.new(@shell, pattern)
314
+ end
315
+
316
+ def append(to, filter)
317
+ case to
318
+ when String
319
+ AppendFile.new(@shell, to, filter)
320
+ when IO
321
+ AppendIO.new(@shell, to, filter)
322
+ else
323
+ Shell.Fail Error::CantApplyMethod, "append", to.class
324
+ end
325
+ end
326
+
327
+ # call-seq:
328
+ # tee(file) -> Tee
329
+ #
330
+ # Returns a Tee filter object, with the given +file+ command
331
+ def tee(file)
332
+ Tee.new(@shell, file)
333
+ end
334
+
335
+ # call-seq:
336
+ # concat(*jobs) -> Concat
337
+ #
338
+ # Returns a Concat object, for the given +jobs+
339
+ def concat(*jobs)
340
+ Concat.new(@shell, *jobs)
341
+ end
342
+
343
+ # %pwd, %cwd -> @pwd
344
+ def notify(*opts)
345
+ Shell.notify(*opts) {|mes|
346
+ yield mes if iterator?
347
+
348
+ mes.gsub!("%pwd", "#{@cwd}")
349
+ mes.gsub!("%cwd", "#{@cwd}")
350
+ }
351
+ end
352
+
353
+ #
354
+ # private functions
355
+ #
356
+ def find_system_command(command)
357
+ return command if /^\// =~ command
358
+ case path = @system_commands[command]
359
+ when String
360
+ if exists?(path)
361
+ return path
362
+ else
363
+ Shell.Fail Error::CommandNotFound, command
364
+ end
365
+ when false
366
+ Shell.Fail Error::CommandNotFound, command
367
+ end
368
+
369
+ for p in @shell.system_path
370
+ path = join(p, command)
371
+ begin
372
+ st = File.stat(path)
373
+ rescue SystemCallError
374
+ next
375
+ else
376
+ next unless st.executable? and !st.directory?
377
+ @system_commands[command] = path
378
+ return path
379
+ end
380
+ end
381
+ @system_commands[command] = false
382
+ Shell.Fail Error::CommandNotFound, command
383
+ end
384
+
385
+ # call-seq:
386
+ # def_system_command(command, path) -> Shell::SystemCommand
387
+ #
388
+ # Defines a command, registering +path+ as a Shell method for the given
389
+ # +command+.
390
+ #
391
+ # Shell::CommandProcessor.def_system_command "ls"
392
+ # #=> Defines ls.
393
+ #
394
+ # Shell::CommandProcessor.def_system_command "sys_sort", "sort"
395
+ # #=> Defines sys_sort as sort
396
+ #
397
+ def self.def_system_command(command, path = command)
398
+ begin
399
+ eval((d = %Q[def #{command}(*opts)
400
+ SystemCommand.new(@shell, '#{path}', *opts)
401
+ end]), nil, __FILE__, __LINE__ - 1)
402
+ rescue SyntaxError
403
+ Shell.notify "warn: Can't define #{command} path: #{path}."
404
+ end
405
+ Shell.notify "Define #{command} path: #{path}.", Shell.debug?
406
+ Shell.notify("Definition of #{command}: ", d,
407
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
408
+ end
409
+
410
+ # call-seq:
411
+ # undef_system_command(command) -> self
412
+ #
413
+ # Undefines a command
414
+ def self.undef_system_command(command)
415
+ command = command.id2name if command.kind_of?(Symbol)
416
+ remove_method(command)
417
+ Shell.module_eval{remove_method(command)}
418
+ Filter.module_eval{remove_method(command)}
419
+ self
420
+ end
421
+
422
+ @alias_map = {}
423
+ # Returns a list of aliased commands
424
+ def self.alias_map
425
+ @alias_map
426
+ end
427
+ # call-seq:
428
+ # alias_command(alias, command, *options) -> self
429
+ #
430
+ # Creates a command alias at the given +alias+ for the given +command+,
431
+ # passing any +options+ along with it.
432
+ #
433
+ # Shell::CommandProcessor.alias_command "lsC", "ls", "-CBF", "--show-control-chars"
434
+ # Shell::CommandProcessor.alias_command("lsC", "ls"){|*opts| ["-CBF", "--show-control-chars", *opts]}
435
+ #
436
+ def self.alias_command(ali, command, *opts)
437
+ ali = ali.id2name if ali.kind_of?(Symbol)
438
+ command = command.id2name if command.kind_of?(Symbol)
439
+ begin
440
+ if iterator?
441
+ @alias_map[ali.intern] = proc
442
+
443
+ eval((d = %Q[def #{ali}(*opts)
444
+ @shell.__send__(:#{command},
445
+ *(CommandProcessor.alias_map[:#{ali}].call *opts))
446
+ end]), nil, __FILE__, __LINE__ - 1)
447
+
448
+ else
449
+ args = opts.collect{|opt| '"' + opt + '"'}.join(",")
450
+ eval((d = %Q[def #{ali}(*opts)
451
+ @shell.__send__(:#{command}, #{args}, *opts)
452
+ end]), nil, __FILE__, __LINE__ - 1)
453
+ end
454
+ rescue SyntaxError
455
+ Shell.notify "warn: Can't alias #{ali} command: #{command}."
456
+ Shell.notify("Definition of #{ali}: ", d)
457
+ raise
458
+ end
459
+ Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
460
+ Shell.notify("Definition of #{ali}: ", d,
461
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
462
+ self
463
+ end
464
+
465
+ # call-seq:
466
+ # unalias_command(alias) -> self
467
+ #
468
+ # Unaliases the given +alias+ command.
469
+ def self.unalias_command(ali)
470
+ ali = ali.id2name if ali.kind_of?(Symbol)
471
+ @alias_map.delete ali.intern
472
+ undef_system_command(ali)
473
+ end
474
+
475
+ # :nodoc:
476
+ #
477
+ # Delegates File and FileTest methods into Shell, including the following
478
+ # commands:
479
+ #
480
+ # * Shell#blockdev?(file)
481
+ # * Shell#chardev?(file)
482
+ # * Shell#directory?(file)
483
+ # * Shell#executable?(file)
484
+ # * Shell#executable_real?(file)
485
+ # * Shell#exist?(file)/Shell#exists?(file)
486
+ # * Shell#file?(file)
487
+ # * Shell#grpowned?(file)
488
+ # * Shell#owned?(file)
489
+ # * Shell#pipe?(file)
490
+ # * Shell#readable?(file)
491
+ # * Shell#readable_real?(file)
492
+ # * Shell#setgid?(file)
493
+ # * Shell#setuid?(file)
494
+ # * Shell#size(file)/Shell#size?(file)
495
+ # * Shell#socket?(file)
496
+ # * Shell#sticky?(file)
497
+ # * Shell#symlink?(file)
498
+ # * Shell#writable?(file)
499
+ # * Shell#writable_real?(file)
500
+ # * Shell#zero?(file)
501
+ # * Shell#syscopy(filename_from, filename_to)
502
+ # * Shell#copy(filename_from, filename_to)
503
+ # * Shell#move(filename_from, filename_to)
504
+ # * Shell#compare(filename_from, filename_to)
505
+ # * Shell#safe_unlink(*filenames)
506
+ # * Shell#makedirs(*filenames)
507
+ # * Shell#install(filename_from, filename_to, mode)
508
+ #
509
+ # And also, there are some aliases for convenience:
510
+ #
511
+ # * Shell#cmp <- Shell#compare
512
+ # * Shell#mv <- Shell#move
513
+ # * Shell#cp <- Shell#copy
514
+ # * Shell#rm_f <- Shell#safe_unlink
515
+ # * Shell#mkpath <- Shell#makedirs
516
+ #
517
+ def self.def_builtin_commands(delegation_class, command_specs)
518
+ for meth, args in command_specs
519
+ arg_str = args.collect{|arg| arg.downcase}.join(", ")
520
+ call_arg_str = args.collect{
521
+ |arg|
522
+ case arg
523
+ when /^(FILENAME.*)$/
524
+ format("expand_path(%s)", $1.downcase)
525
+ when /^(\*FILENAME.*)$/
526
+ # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
527
+ $1.downcase + '.collect{|fn| expand_path(fn)}'
528
+ else
529
+ arg
530
+ end
531
+ }.join(", ")
532
+ d = %Q[def #{meth}(#{arg_str})
533
+ #{delegation_class}.#{meth}(#{call_arg_str})
534
+ end]
535
+ Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
536
+ Shell.notify("Definition of #{meth}: ", d,
537
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
538
+ eval d
539
+ end
540
+ end
541
+
542
+ # call-seq:
543
+ # install_system_commands(prefix = "sys_")
544
+ #
545
+ # Defines all commands in the Shell.default_system_path as Shell method,
546
+ # all with given +prefix+ appended to their names.
547
+ #
548
+ # Any invalid character names are converted to +_+, and errors are passed
549
+ # to Shell.notify.
550
+ #
551
+ # Methods already defined are skipped.
552
+ def self.install_system_commands(pre = "sys_")
553
+ defined_meth = {}
554
+ for m in Shell.methods
555
+ defined_meth[m] = true
556
+ end
557
+ sh = Shell.new
558
+ for path in Shell.default_system_path
559
+ next unless sh.directory? path
560
+ sh.cd path
561
+ sh.foreach do
562
+ |cn|
563
+ if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
564
+ command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
565
+ begin
566
+ def_system_command(command, sh.expand_path(cn))
567
+ rescue
568
+ Shell.notify "warn: Can't define #{command} path: #{cn}"
569
+ end
570
+ defined_meth[command] = command
571
+ end
572
+ end
573
+ end
574
+ end
575
+
576
+ def self.add_delegate_command_to_shell(id) # :nodoc:
577
+ id = id.intern if id.kind_of?(String)
578
+ name = id.id2name
579
+ if Shell.method_defined?(id)
580
+ Shell.notify "warn: override definition of Shell##{name}."
581
+ Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
582
+ Shell.module_eval "alias #{name}_org #{name}"
583
+ end
584
+ Shell.notify "method added: Shell##{name}.", Shell.debug?
585
+ Shell.module_eval(%Q[def #{name}(*args, &block)
586
+ begin
587
+ @command_processor.__send__(:#{name}, *args, &block)
588
+ rescue Exception
589
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
590
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
591
+ raise
592
+ end
593
+ end], __FILE__, __LINE__)
594
+
595
+ if Shell::Filter.method_defined?(id)
596
+ Shell.notify "warn: override definition of Shell::Filter##{name}."
597
+ Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
598
+ Filter.module_eval "alias #{name}_org #{name}"
599
+ end
600
+ Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
601
+ Filter.module_eval(%Q[def #{name}(*args, &block)
602
+ begin
603
+ self | @shell.__send__(:#{name}, *args, &block)
604
+ rescue Exception
605
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
606
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
607
+ raise
608
+ end
609
+ end], __FILE__, __LINE__)
610
+ end
611
+
612
+ # Delegates File methods into Shell, including the following commands:
613
+ #
614
+ # * Shell#atime(file)
615
+ # * Shell#basename(file, *opt)
616
+ # * Shell#chmod(mode, *files)
617
+ # * Shell#chown(owner, group, *file)
618
+ # * Shell#ctime(file)
619
+ # * Shell#delete(*file)
620
+ # * Shell#dirname(file)
621
+ # * Shell#ftype(file)
622
+ # * Shell#join(*file)
623
+ # * Shell#link(file_from, file_to)
624
+ # * Shell#lstat(file)
625
+ # * Shell#mtime(file)
626
+ # * Shell#readlink(file)
627
+ # * Shell#rename(file_from, file_to)
628
+ # * Shell#split(file)
629
+ # * Shell#stat(file)
630
+ # * Shell#symlink(file_from, file_to)
631
+ # * Shell#truncate(file, length)
632
+ # * Shell#utime(atime, mtime, *file)
633
+ #
634
+ def self.install_builtin_commands
635
+ # method related File.
636
+ # (exclude open/foreach/unlink)
637
+ normal_delegation_file_methods = [
638
+ ["atime", ["FILENAME"]],
639
+ ["basename", ["fn", "*opts"]],
640
+ ["chmod", ["mode", "*FILENAMES"]],
641
+ ["chown", ["owner", "group", "*FILENAME"]],
642
+ ["ctime", ["FILENAMES"]],
643
+ ["delete", ["*FILENAMES"]],
644
+ ["dirname", ["FILENAME"]],
645
+ ["ftype", ["FILENAME"]],
646
+ ["join", ["*items"]],
647
+ ["link", ["FILENAME_O", "FILENAME_N"]],
648
+ ["lstat", ["FILENAME"]],
649
+ ["mtime", ["FILENAME"]],
650
+ ["readlink", ["FILENAME"]],
651
+ ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
652
+ ["split", ["pathname"]],
653
+ ["stat", ["FILENAME"]],
654
+ ["symlink", ["FILENAME_O", "FILENAME_N"]],
655
+ ["truncate", ["FILENAME", "length"]],
656
+ ["utime", ["atime", "mtime", "*FILENAMES"]]]
657
+
658
+ def_builtin_commands(File, normal_delegation_file_methods)
659
+ alias_method :rm, :delete
660
+
661
+ # method related FileTest
662
+ def_builtin_commands(FileTest,
663
+ FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
664
+
665
+ end
666
+
667
+ end
668
+ end