source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/shell/command-processor.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 14.8 KB
Line 
1#
2# shell/command-controller.rb -
3# $Release Version: 0.6.0 $
4# $Revision: 11708 $
5# $Date: 2007-02-13 08:01:19 +0900 (Tue, 13 Feb 2007) $
6# by Keiju ISHITSUKA(Nippon Rational Inc.)
7#
8# --
9#
10#
11#
12
13require "e2mmap"
14require "ftools"
15require "thread"
16
17require "shell/error"
18require "shell/filter"
19require "shell/system-command"
20require "shell/builtin-command"
21
22class Shell
23 class CommandProcessor
24# include Error
25
26 #
27 # initialize of Shell and related classes.
28 #
29 NoDelegateMethods = ["initialize", "expand_path"]
30 def self.initialize
31
32 install_builtin_commands
33
34 # define CommandProccessor#methods to Shell#methods and Filter#methods
35 for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
36 add_delegate_command_to_shell(m)
37 end
38
39 def self.method_added(id)
40 add_delegate_command_to_shell(id)
41 end
42 end
43
44 #
45 # include run file.
46 #
47 def self.run_config
48 begin
49 load File.expand_path("~/.rb_shell") if ENV.key?("HOME")
50 rescue LoadError, Errno::ENOENT
51 rescue
52 print "load error: #{rc}\n"
53 print $!.class, ": ", $!, "\n"
54 for err in $@[0, [email protected] - 2]
55 print "\t", err, "\n"
56 end
57 end
58 end
59
60 def initialize(shell)
61 @shell = shell
62 @system_commands = {}
63 end
64
65 #
66 # CommandProcessor#expand_path(path)
67 # path: String
68 # return: String
69 # returns the absolute path for <path>
70 #
71 def expand_path(path)
72 @shell.expand_path(path)
73 end
74
75 #
76 # File related commands
77 # Shell#foreach
78 # Shell#open
79 # Shell#unlink
80 # Shell#test
81 #
82 # -
83 #
84 # CommandProcessor#foreach(path, rs)
85 # path: String
86 # rs: String - record separator
87 # iterator
88 # Same as:
89 # File#foreach (when path is file)
90 # Dir#foreach (when path is directory)
91 # path is relative to pwd
92 #
93 def foreach(path = nil, *rs)
94 path = "." unless path
95 path = expand_path(path)
96
97 if File.directory?(path)
98 Dir.foreach(path){|fn| yield fn}
99 else
100 IO.foreach(path, *rs){|l| yield l}
101 end
102 end
103
104 #
105 # CommandProcessor#open(path, mode)
106 # path: String
107 # mode: String
108 # return: File or Dir
109 # Same as:
110 # File#open (when path is file)
111 # Dir#open (when path is directory)
112 # mode has an effect only when path is a file
113 #
114 def open(path, mode)
115 path = expand_path(path)
116 if File.directory?(path)
117 Dir.open(path)
118 else
119 effect_umask do
120 File.open(path, mode)
121 end
122 end
123 end
124 # public :open
125
126 #
127 # CommandProcessor#unlink(path)
128 # same as:
129 # Dir#unlink (when path is directory)
130 # File#unlink (when path is file)
131 #
132 def unlink(path)
133 path = expand_path(path)
134 if File.directory?(path)
135 Dir.unlink(path)
136 else
137 IO.unlink(path)
138 end
139 end
140
141 #
142 # CommandProcessor#test(command, file1, file2)
143 # CommandProcessor#[command, file1, file2]
144 # command: char or String or Symbol
145 # file1: String
146 # file2: String(optional)
147 # return: Boolean
148 # same as:
149 # test() (when command is char or length 1 string or symbol)
150 # FileTest.command (others)
151 # example:
152 # sh[?e, "foo"]
153 # sh[:e, "foo"]
154 # sh["e", "foo"]
155 # sh[:exists?, "foo"]
156 # sh["exists?", "foo"]
157 #
158 def test(command, file1, file2=nil)
159 file1 = expand_path(file1)
160 file2 = expand_path(file2) if file2
161 command = command.id2name if command.kind_of?(Symbol)
162
163 case command
164 when Integer
165 top_level_test(command, file1, file2)
166 when String
167 if command.size == 1
168 if file2
169 top_level_test(command, file1, file2)
170 else
171 top_level_test(command, file1)
172 end
173 else
174 if file2
175 FileTest.send(command, file1, file2)
176 else
177 FileTest.send(command, file1)
178 end
179 end
180 end
181 end
182 alias [] test
183
184 #
185 # Dir related methods
186 #
187 # Shell#mkdir
188 # Shell#rmdir
189 #
190 #--
191 #
192 # CommandProcessor#mkdir(*path)
193 # path: String
194 # same as Dir.mkdir()
195 #
196 def mkdir(*path)
197 for dir in path
198 Dir.mkdir(expand_path(dir))
199 end
200 end
201
202 #
203 # CommandProcessor#rmdir(*path)
204 # path: String
205 # same as Dir.rmdir()
206 #
207 def rmdir(*path)
208 for dir in path
209 Dir.rmdir(expand_path(dir))
210 end
211 end
212
213 #
214 # CommandProcessor#system(command, *opts)
215 # command: String
216 # opts: String
217 # return: SystemCommand
218 # Same as system() function
219 # example:
220 # print sh.system("ls", "-l")
221 # sh.system("ls", "-l") | sh.head > STDOUT
222 #
223 def system(command, *opts)
224 if opts.empty?
225 if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
226 return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
227 else
228 command, *opts = command.split(/\s+/)
229 end
230 end
231 SystemCommand.new(@shell, find_system_command(command), *opts)
232 end
233
234 #
235 # ProcessCommand#rehash
236 # clear command hash table.
237 #
238 def rehash
239 @system_commands = {}
240 end
241
242 #
243 # ProcessCommand#transact
244 #
245 def check_point
246 @shell.process_controller.wait_all_jobs_execution
247 end
248 alias finish_all_jobs check_point
249
250 def transact(&block)
251 begin
252 @shell.instance_eval(&block)
253 ensure
254 check_point
255 end
256 end
257
258 #
259 # internal commands
260 #
261 def out(dev = STDOUT, &block)
262 dev.print transact(&block)
263 end
264
265 def echo(*strings)
266 Echo.new(@shell, *strings)
267 end
268
269 def cat(*filenames)
270 Cat.new(@shell, *filenames)
271 end
272
273 # def sort(*filenames)
274 # Sort.new(self, *filenames)
275 # end
276
277 def glob(pattern)
278 Glob.new(@shell, pattern)
279 end
280
281 def append(to, filter)
282 case to
283 when String
284 AppendFile.new(@shell, to, filter)
285 when IO
286 AppendIO.new(@shell, to, filter)
287 else
288 Shell.Fail Error::CantApplyMethod, "append", to.class
289 end
290 end
291
292 def tee(file)
293 Tee.new(@shell, file)
294 end
295
296 def concat(*jobs)
297 Concat.new(@shell, *jobs)
298 end
299
300 # %pwd, %cwd -> @pwd
301 def notify(*opts, &block)
302 Thread.exclusive do
303 Shell.notify(*opts) {|mes|
304 yield mes if iterator?
305
306 mes.gsub!("%pwd", "#{@cwd}")
307 mes.gsub!("%cwd", "#{@cwd}")
308 }
309 end
310 end
311
312 #
313 # private functions
314 #
315 def effect_umask
316 if @shell.umask
317 Thread.critical = true
318 save = File.umask
319 begin
320 yield
321 ensure
322 File.umask save
323 Thread.critical = false
324 end
325 else
326 yield
327 end
328 end
329 private :effect_umask
330
331 def find_system_command(command)
332 return command if /^\// =~ command
333 case path = @system_commands[command]
334 when String
335 if exists?(path)
336 return path
337 else
338 Shell.Fail Error::CommandNotFound, command
339 end
340 when false
341 Shell.Fail Error::CommandNotFound, command
342 end
343
344 for p in @shell.system_path
345 path = join(p, command)
346 if FileTest.exists?(path)
347 @system_commands[command] = path
348 return path
349 end
350 end
351 @system_commands[command] = false
352 Shell.Fail Error::CommandNotFound, command
353 end
354
355 #
356 # CommandProcessor.def_system_command(command, path)
357 # command: String
358 # path: String
359 # define 'command()' method as method.
360 #
361 def self.def_system_command(command, path = command)
362 begin
363 eval((d = %Q[def #{command}(*opts)
364 SystemCommand.new(@shell, '#{path}', *opts)
365 end]), nil, __FILE__, __LINE__ - 1)
366 rescue SyntaxError
367 Shell.notify "warn: Can't define #{command} path: #{path}."
368 end
369 Shell.notify "Define #{command} path: #{path}.", Shell.debug?
370 Shell.notify("Definition of #{command}: ", d,
371 Shell.debug.kind_of?(Integer) && Shell.debug > 1)
372 end
373
374 def self.undef_system_command(command)
375 command = command.id2name if command.kind_of?(Symbol)
376 remove_method(command)
377 Shell.module_eval{remove_method(command)}
378 Filter.module_eval{remove_method(command)}
379 self
380 end
381
382 # define command alias
383 # ex)
384 # def_alias_command("ls_c", "ls", "-C", "-F")
385 # def_alias_command("ls_c", "ls"){|*opts| ["-C", "-F", *opts]}
386 #
387 @alias_map = {}
388 def self.alias_map
389 @alias_map
390 end
391 def self.alias_command(ali, command, *opts, &block)
392 ali = ali.id2name if ali.kind_of?(Symbol)
393 command = command.id2name if command.kind_of?(Symbol)
394 begin
395 if iterator?
396 @alias_map[ali.intern] = proc
397
398 eval((d = %Q[def #{ali}(*opts)
399 @shell.__send__(:#{command},
400 *(CommandProcessor.alias_map[:#{ali}].call *opts))
401 end]), nil, __FILE__, __LINE__ - 1)
402
403 else
404 args = opts.collect{|opt| '"' + opt + '"'}.join(",")
405 eval((d = %Q[def #{ali}(*opts)
406 @shell.__send__(:#{command}, #{args}, *opts)
407 end]), nil, __FILE__, __LINE__ - 1)
408 end
409 rescue SyntaxError
410 Shell.notify "warn: Can't alias #{ali} command: #{command}."
411 Shell.notify("Definition of #{ali}: ", d)
412 raise
413 end
414 Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
415 Shell.notify("Definition of #{ali}: ", d,
416 Shell.debug.kind_of?(Integer) && Shell.debug > 1)
417 self
418 end
419
420 def self.unalias_command(ali)
421 ali = ali.id2name if ali.kind_of?(Symbol)
422 @alias_map.delete ali.intern
423 undef_system_command(ali)
424 end
425
426 #
427 # CommandProcessor.def_builtin_commands(delegation_class, command_specs)
428 # delegation_class: Class or Module
429 # command_specs: [[command_name, [argument,...]],...]
430 # command_name: String
431 # arguments: String
432 # FILENAME?? -> expand_path(filename??)
433 # *FILENAME?? -> filename??.collect{|f|expand_path(f)}.join(", ")
434 # define command_name(argument,...) as
435 # delegation_class.command_name(argument,...)
436 #
437 def self.def_builtin_commands(delegation_class, command_specs)
438 for meth, args in command_specs
439 arg_str = args.collect{|arg| arg.downcase}.join(", ")
440 call_arg_str = args.collect{
441 |arg|
442 case arg
443 when /^(FILENAME.*)$/
444 format("expand_path(%s)", $1.downcase)
445 when /^(\*FILENAME.*)$/
446 # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
447 $1.downcase + '.collect{|fn| expand_path(fn)}'
448 else
449 arg
450 end
451 }.join(", ")
452 d = %Q[def #{meth}(#{arg_str})
453 #{delegation_class}.#{meth}(#{call_arg_str})
454 end]
455 Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
456 Shell.notify("Definition of #{meth}: ", d,
457 Shell.debug.kind_of?(Integer) && Shell.debug > 1)
458 eval d
459 end
460 end
461
462 #
463 # CommandProcessor.install_system_commands(pre)
464 # pre: String - command name prefix
465 # defines every command which belongs in default_system_path via
466 # CommandProcessor.command(). It doesn't define already defined
467 # methods twice. By default, "pre_" is prefixes to each method
468 # name. Characters that may not be used in a method name are
469 # all converted to '_'. Definition errors are just ignored.
470 #
471 def self.install_system_commands(pre = "sys_")
472 defined_meth = {}
473 for m in Shell.methods
474 defined_meth[m] = true
475 end
476 sh = Shell.new
477 for path in Shell.default_system_path
478 next unless sh.directory? path
479 sh.cd path
480 sh.foreach do
481 |cn|
482 if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
483 command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
484 begin
485 def_system_command(command, sh.expand_path(cn))
486 rescue
487 Shell.notify "warn: Can't define #{command} path: #{cn}"
488 end
489 defined_meth[command] = command
490 end
491 end
492 end
493 end
494
495 #----------------------------------------------------------------------
496 #
497 # class initializing methods -
498 #
499 #----------------------------------------------------------------------
500 def self.add_delegate_command_to_shell(id)
501 id = id.intern if id.kind_of?(String)
502 name = id.id2name
503 if Shell.method_defined?(id)
504 Shell.notify "warn: override definnition of Shell##{name}."
505 Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
506 Shell.module_eval "alias #{name}_org #{name}"
507 end
508 Shell.notify "method added: Shell##{name}.", Shell.debug?
509 Shell.module_eval(%Q[def #{name}(*args, &block)
510 begin
511 @command_processor.__send__(:#{name}, *args, &block)
512 rescue Exception
513 [email protected]_if{|s| /:in `__getobj__'$/ =~ s} #`
514 [email protected]_if{|s| /^\\(eval\\):/ =~ s}
515 raise
516 end
517 end], __FILE__, __LINE__)
518
519 if Shell::Filter.method_defined?(id)
520 Shell.notify "warn: override definnition of Shell::Filter##{name}."
521 Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
522 Filter.module_eval "alias #{name}_org #{name}"
523 end
524 Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
525 Filter.module_eval(%Q[def #{name}(*args, &block)
526 begin
527 self | @shell.__send__(:#{name}, *args, &block)
528 rescue Exception
529 [email protected]_if{|s| /:in `__getobj__'$/ =~ s} #`
530 [email protected]_if{|s| /^\\(eval\\):/ =~ s}
531 raise
532 end
533 end], __FILE__, __LINE__)
534 end
535
536 #
537 # define default builtin commands
538 #
539 def self.install_builtin_commands
540 # method related File.
541 # (exclude open/foreach/unlink)
542 normal_delegation_file_methods = [
543 ["atime", ["FILENAME"]],
544 ["basename", ["fn", "*opts"]],
545 ["chmod", ["mode", "*FILENAMES"]],
546 ["chown", ["owner", "group", "*FILENAME"]],
547 ["ctime", ["FILENAMES"]],
548 ["delete", ["*FILENAMES"]],
549 ["dirname", ["FILENAME"]],
550 ["ftype", ["FILENAME"]],
551 ["join", ["*items"]],
552 ["link", ["FILENAME_O", "FILENAME_N"]],
553 ["lstat", ["FILENAME"]],
554 ["mtime", ["FILENAME"]],
555 ["readlink", ["FILENAME"]],
556 ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
557 # ["size", ["FILENAME"]],
558 ["split", ["pathname"]],
559 ["stat", ["FILENAME"]],
560 ["symlink", ["FILENAME_O", "FILENAME_N"]],
561 ["truncate", ["FILENAME", "length"]],
562 ["utime", ["atime", "mtime", "*FILENAMES"]]]
563
564 def_builtin_commands(File, normal_delegation_file_methods)
565 alias_method :rm, :delete
566
567 # method related FileTest
568 def_builtin_commands(FileTest,
569 FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
570
571 # method related ftools
572 normal_delegation_ftools_methods = [
573 ["syscopy", ["FILENAME_FROM", "FILENAME_TO"]],
574 ["copy", ["FILENAME_FROM", "FILENAME_TO"]],
575 ["move", ["FILENAME_FROM", "FILENAME_TO"]],
576 ["compare", ["FILENAME_FROM", "FILENAME_TO"]],
577 ["safe_unlink", ["*FILENAMES"]],
578 ["makedirs", ["*FILENAMES"]],
579 # ["chmod", ["mode", "*FILENAMES"]],
580 ["install", ["FILENAME_FROM", "FILENAME_TO", "mode"]],
581 ]
582 def_builtin_commands(File,
583 normal_delegation_ftools_methods)
584 alias_method :cmp, :compare
585 alias_method :mv, :move
586 alias_method :cp, :copy
587 alias_method :rm_f, :safe_unlink
588 alias_method :mkpath, :makedirs
589 end
590
591 end
592end
Note: See TracBrowser for help on using the repository browser.