summaryrefslogtreecommitdiff
path: root/lib/shell
diff options
context:
space:
mode:
Diffstat (limited to 'lib/shell')
-rw-r--r--lib/shell/builtin-command.rb81
-rw-r--r--lib/shell/command-processor.rb727
-rw-r--r--lib/shell/error.rb12
-rw-r--r--lib/shell/filter.rb102
-rw-r--r--lib/shell/process-controller.rb349
-rw-r--r--lib/shell/system-command.rb151
-rw-r--r--lib/shell/version.rb16
7 files changed, 789 insertions, 649 deletions
diff --git a/lib/shell/builtin-command.rb b/lib/shell/builtin-command.rb
index 02025c5d42..e419a68c33 100644
--- a/lib/shell/builtin-command.rb
+++ b/lib/shell/builtin-command.rb
@@ -1,19 +1,19 @@
+# frozen_string_literal: false
#
-# shell/builtin-command.rb -
-# $Release Version: 0.6.0 $
-# $Revision: 1.1 $
-# $Date: 2001/05/17 10:02:48 $
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# shell/builtin-command.rb -
+# $Release Version: 0.7 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
+#
#
require "shell/filter"
class Shell
- class BuiltInCommand<Filter
+ class BuiltInCommand < Filter
def wait?
false
end
@@ -22,16 +22,26 @@ class Shell
end
end
+ class Void < BuiltInCommand
+ def initialize(sh, *opts)
+ super sh
+ end
+
+ def each(rs = nil)
+ # do nothing
+ end
+ end
+
class Echo < BuiltInCommand
def initialize(sh, *strings)
super sh
@strings = strings
end
-
+
def each(rs = nil)
rs = @shell.record_separator unless rs
for str in @strings
- yield str + rs
+ yield str + rs
end
end
end
@@ -44,11 +54,11 @@ class Shell
def each(rs = nil)
if @cat_files.empty?
- super
+ super
else
- for src in @cat_files
- @shell.foreach(src, rs){|l| yield l}
- end
+ for src in @cat_files
+ @shell.foreach(src, rs){|l| yield l}
+ end
end
end
end
@@ -58,39 +68,22 @@ class Shell
super sh
@pattern = pattern
- Thread.critical = true
- back = Dir.pwd
- begin
- Dir.chdir @shell.cwd
- @files = Dir[pattern]
- ensure
- Dir.chdir back
- Thread.critical = false
- end
end
def each(rs = nil)
+ if @pattern[0] == ?/
+ @files = Dir[@pattern]
+ else
+ prefix = @shell.pwd+"/"
+ @files = Dir[prefix+@pattern].collect{|p| p.sub(prefix, "")}
+ end
rs = @shell.record_separator unless rs
- for f in @files
- yield f+rs
+ for f in @files
+ yield f+rs
end
end
end
-# class Sort < Cat
-# def initialize(sh, *filenames)
-# super
-# end
-#
-# def each(rs = nil)
-# ary = []
-# super{|l| ary.push l}
-# for l in ary.sort!
-# yield l
-# end
-# end
-# end
-
class AppendIO < BuiltInCommand
def initialize(sh, io, filter)
super sh
@@ -101,7 +94,7 @@ class Shell
def input=(filter)
@input.input=filter
for l in @input
- @io << l
+ @io << l
end
end
@@ -116,9 +109,9 @@ class Shell
def input=(filter)
begin
- super
+ super
ensure
- @io.close
+ @io.close
end
end
end
@@ -132,9 +125,9 @@ class Shell
def each(rs = nil)
to = @shell.open(@to_filename, "w")
begin
- super{|l| to << l; yield l}
+ super{|l| to << l; yield l}
ensure
- to.close
+ to.close
end
end
end
@@ -147,7 +140,7 @@ class Shell
def each(rs = nil)
while job = @jobs.shift
- job.each{|l| yield l}
+ job.each{|l| yield l}
end
end
end
diff --git a/lib/shell/command-processor.rb b/lib/shell/command-processor.rb
index f08440e457..08ea5c874c 100644
--- a/lib/shell/command-processor.rb
+++ b/lib/shell/command-processor.rb
@@ -1,18 +1,16 @@
+# frozen_string_literal: false
#
-# shell/command-controller.rb -
-# $Release Version: 0.6.0 $
-# $Revision: 1.8.2.2 $
-# $Date: 2004/04/18 23:20:33 $
-# by Keiju ISHITSUKA(Nippon Rational Inc.)
+# shell/command-controller.rb -
+# $Release Version: 0.7 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
+#
#
require "e2mmap"
-require "ftools"
-require "thread"
require "shell/error"
require "shell/filter"
@@ -20,24 +18,34 @@ require "shell/system-command"
require "shell/builtin-command"
class Shell
+ # In order to execute a command on your OS, you need to define it as a
+ # Shell method.
+ #
+ # Alternatively, you can execute any command via
+ # Shell::CommandProcessor#system even if it is not defined.
class CommandProcessor
-# include Error
#
# initialize of Shell and related classes.
#
- NoDelegateMethods = ["initialize", "expand_path"]
+ m = [:initialize, :expand_path]
+ if Object.methods.first.kind_of?(String)
+ NoDelegateMethods = m.collect{|x| x.id2name}
+ else
+ NoDelegateMethods = m
+ end
+
def self.initialize
install_builtin_commands
- # define CommandProccessor#methods to Shell#methods and Filter#methods
+ # define CommandProcessor#methods to Shell#methods and Filter#methods
for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
- add_delegate_command_to_shell(m)
+ add_delegate_command_to_shell(m)
end
-
+
def self.method_added(id)
- add_delegate_command_to_shell(id)
+ add_delegate_command_to_shell(id)
end
end
@@ -45,15 +53,16 @@ class Shell
# include run file.
#
def self.run_config
+ rc = "~/.rb_shell"
begin
- load File.expand_path("~/.rb_shell") if ENV.key?("HOME")
+ load File.expand_path(rc) if ENV.key?("HOME")
rescue LoadError, Errno::ENOENT
rescue
- print "load error: #{rc}\n"
- print $!.class, ": ", $!, "\n"
- for err in $@[0, $@.size - 2]
- print "\t", err, "\n"
- end
+ print "load error: #{rc}\n"
+ print $!.class, ": ", $!, "\n"
+ for err in $@[0, $@.size - 2]
+ print "\t", err, "\n"
+ end
end
end
@@ -64,97 +73,93 @@ class Shell
#
# CommandProcessor#expand_path(path)
- # path: String
- # return: String
- # returns the absolute path for <path>
+ # path: String
+ # return: String
+ # returns the absolute path for <path>
#
def expand_path(path)
@shell.expand_path(path)
end
+ # call-seq:
+ # foreach(path, record_separator) -> Enumerator
+ # foreach(path, record_separator) { block }
#
- # File related commands
- # Shell#foreach
- # Shell#open
- # Shell#unlink
- # Shell#test
+ # See IO.foreach when +path+ is a file.
#
- # -
- #
- # CommandProcessor#foreach(path, rs)
- # path: String
- # rs: String - record separator
- # iterator
- # Same as:
- # File#foreach (when path is file)
- # Dir#foreach (when path is directory)
- # path is relative to pwd
+ # See Dir.foreach when +path+ is a directory.
#
def foreach(path = nil, *rs)
path = "." unless path
path = expand_path(path)
if File.directory?(path)
- Dir.foreach(path){|fn| yield fn}
+ Dir.foreach(path){|fn| yield fn}
else
- IO.foreach(path, *rs){|l| yield l}
+ IO.foreach(path, *rs){|l| yield l}
end
end
+ # call-seq:
+ # open(path, mode, permissions) -> Enumerator
+ # open(path, mode, permissions) { block }
#
- # CommandProcessor#open(path, mode)
- # path: String
- # mode: String
- # return: File or Dir
- # Same as:
- # File#open (when path is file)
- # Dir#open (when path is directory)
- # mode has an effect only when path is a file
+ # See IO.open when +path+ is a file.
#
- def open(path, mode)
+ # See Dir.open when +path+ is a directory.
+ #
+ def open(path, mode = nil, perm = 0666, &b)
path = expand_path(path)
if File.directory?(path)
- Dir.open(path)
+ Dir.open(path, &b)
else
- effect_umask do
- File.open(path, mode)
- end
+ if @shell.umask
+ f = File.open(path, mode, perm)
+ File.chmod(perm & ~@shell.umask, path)
+ if block_given?
+ f.each(&b)
+ end
+ f
+ else
+ File.open(path, mode, perm, &b)
+ end
end
end
- # public :open
+ # call-seq:
+ # unlink(path)
+ #
+ # See IO.unlink when +path+ is a file.
#
- # CommandProcessor#unlink(path)
- # same as:
- # Dir#unlink (when path is directory)
- # File#unlink (when path is file)
+ # See Dir.unlink when +path+ is a directory.
#
def unlink(path)
+ @shell.check_point
+
path = expand_path(path)
if File.directory?(path)
- Dir.unlink(path)
+ Dir.unlink(path)
else
- IO.unlink(path)
+ IO.unlink(path)
end
+ Void.new(@shell)
end
+ # See Shell::CommandProcessor#test
+ alias top_level_test test
+ # call-seq:
+ # test(command, file1, file2) -> true or false
+ # [command, file1, file2] -> true or false
+ #
+ # Tests if the given +command+ exists in +file1+, or optionally +file2+.
+ #
+ # Example:
+ # sh[?e, "foo"]
+ # sh[:e, "foo"]
+ # sh["e", "foo"]
+ # sh[:exists?, "foo"]
+ # sh["exists?", "foo"]
#
- # CommandProcessor#test(command, file1, file2)
- # CommandProcessor#[command, file1, file2]
- # command: char or String or Symbol
- # file1: String
- # file2: String(optional)
- # return: Boolean
- # same as:
- # test() (when command is char or length 1 string or symbol)
- # FileTest.command (others)
- # example:
- # sh[?e, "foo"]
- # sh[:e, "foo"]
- # sh["e", "foo"]
- # sh[:exists?, "foo"]
- # sh["exists?", "foo"]
- #
def test(command, file1, file2=nil)
file1 = expand_path(file1)
file2 = expand_path(file2) if file2
@@ -162,110 +167,140 @@ class Shell
case command
when Integer
- top_level_test(command, file1, file2)
+ if file2
+ top_level_test(command, file1, file2)
+ else
+ top_level_test(command, file1)
+ end
when String
- if command.size == 1
- if file2
- top_level_test(command, file1, file2)
- else
- top_level_test(command, file1)
- end
- else
- if file2
- FileTest.send(command, file1, file2)
- else
- FileTest.send(command, file1)
- end
- end
+ if command.size == 1
+ if file2
+ top_level_test(command, file1, file2)
+ else
+ top_level_test(command, file1)
+ end
+ else
+ unless FileTest.methods(false).include?(command.to_sym)
+ raise "unsupported command: #{ command }"
+ end
+ if file2
+ FileTest.send(command, file1, file2)
+ else
+ FileTest.send(command, file1)
+ end
+ end
end
end
+ # See Shell::CommandProcessor#test
alias [] test
+ # call-seq:
+ # mkdir(path)
#
- # Dir related methods
- #
- # Shell#mkdir
- # Shell#rmdir
- #
- #--
- #
- # CommandProcessor#mkdir(*path)
- # path: String
- # same as Dir.mkdir()
- #
+ # Same as Dir.mkdir, except multiple directories are allowed.
def mkdir(*path)
+ @shell.check_point
+ notify("mkdir #{path.join(' ')}")
+
+ perm = nil
+ if path.last.kind_of?(Integer)
+ perm = path.pop
+ end
for dir in path
- Dir.mkdir(expand_path(dir))
+ d = expand_path(dir)
+ if perm
+ Dir.mkdir(d, perm)
+ else
+ Dir.mkdir(d)
+ end
+ File.chmod(d, 0666 & ~@shell.umask) if @shell.umask
end
+ Void.new(@shell)
end
+ # call-seq:
+ # rmdir(path)
#
- # CommandProcessor#rmdir(*path)
- # path: String
- # same as Dir.rmdir()
- #
+ # Same as Dir.rmdir, except multiple directories are allowed.
def rmdir(*path)
+ @shell.check_point
+ notify("rmdir #{path.join(' ')}")
+
for dir in path
- Dir.rmdir(expand_path(dir))
+ Dir.rmdir(expand_path(dir))
end
+ Void.new(@shell)
end
+ # call-seq:
+ # system(command, *options) -> SystemCommand
+ #
+ # Executes the given +command+ with the +options+ parameter.
+ #
+ # Example:
+ # print sh.system("ls", "-l")
+ # sh.system("ls", "-l") | sh.head > STDOUT
#
- # CommandProcessor#system(command, *opts)
- # command: String
- # opts: String
- # return: SystemCommand
- # Same as system() function
- # example:
- # print sh.system("ls", "-l")
- # sh.system("ls", "-l") | sh.head > STDOUT
- #
def system(command, *opts)
if opts.empty?
- if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
- return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
- else
- command, *opts = command.split(/\s+/)
- end
+ if command =~ /\*|\?|\{|\}|\[|\]|<|>|\(|\)|~|&|\||\\|\$|;|'|`|"|\n/
+ return SystemCommand.new(@shell, find_system_command("sh"), "-c", command)
+ else
+ command, *opts = command.split(/\s+/)
+ end
end
SystemCommand.new(@shell, find_system_command(command), *opts)
end
+ # call-seq:
+ # rehash
#
- # ProcessCommand#rehash
- # clear command hash table.
- #
+ # Clears the command hash table.
def rehash
@system_commands = {}
end
- #
- # ProcessCommand#transact
- #
- def check_point
+ def check_point # :nodoc:
@shell.process_controller.wait_all_jobs_execution
end
- alias finish_all_jobs check_point
+ alias finish_all_jobs check_point # :nodoc:
+ # call-seq:
+ # transact { block }
+ #
+ # Executes a block as self
+ #
+ # Example:
+ # sh.transact { system("ls", "-l") | head > STDOUT }
def transact(&block)
begin
- @shell.instance_eval(&block)
+ @shell.instance_eval(&block)
ensure
- check_point
+ check_point
end
end
+ # call-seq:
+ # out(device) { block }
#
- # internal commands
- #
+ # Calls <code>device.print</code> on the result passing the _block_ to
+ # #transact
def out(dev = STDOUT, &block)
dev.print transact(&block)
end
+ # call-seq:
+ # echo(*strings) -> Echo
+ #
+ # Returns a Echo object, for the given +strings+
def echo(*strings)
Echo.new(@shell, *strings)
end
+ # call-seq:
+ # cat(*filename) -> Cat
+ #
+ # Returns a Cat object, for the given +filenames+
def cat(*filenames)
Cat.new(@shell, *filenames)
end
@@ -273,7 +308,10 @@ class Shell
# def sort(*filenames)
# Sort.new(self, *filenames)
# end
-
+ # call-seq:
+ # glob(pattern) -> Glob
+ #
+ # Returns a Glob filter object, with the given +pattern+ object
def glob(pattern)
Glob.new(@shell, pattern)
end
@@ -281,96 +319,101 @@ class Shell
def append(to, filter)
case to
when String
- AppendFile.new(@shell, to, filter)
+ AppendFile.new(@shell, to, filter)
when IO
- AppendIO.new(@shell, to, filter)
+ AppendIO.new(@shell, to, filter)
else
- Shell.Fail Error::CantApplyMethod, "append", to.class
+ Shell.Fail Error::CantApplyMethod, "append", to.class
end
end
+ # call-seq:
+ # tee(file) -> Tee
+ #
+ # Returns a Tee filter object, with the given +file+ command
def tee(file)
Tee.new(@shell, file)
end
+ # call-seq:
+ # concat(*jobs) -> Concat
+ #
+ # Returns a Concat object, for the given +jobs+
def concat(*jobs)
Concat.new(@shell, *jobs)
end
# %pwd, %cwd -> @pwd
- def notify(*opts, &block)
- Thread.exclusive do
- Shell.notify(*opts) {|mes|
- yield mes if iterator?
-
- mes.gsub!("%pwd", "#{@cwd}")
- mes.gsub!("%cwd", "#{@cwd}")
- }
- end
+ def notify(*opts)
+ Shell.notify(*opts) {|mes|
+ yield mes if iterator?
+
+ mes.gsub!("%pwd", "#{@cwd}")
+ mes.gsub!("%cwd", "#{@cwd}")
+ }
end
#
# private functions
#
- def effect_umask
- if @shell.umask
- Thread.critical = true
- save = File.umask
- begin
- yield
- ensure
- File.umask save
- Thread.critical = false
- end
- else
- yield
- end
- end
- private :effect_umask
-
def find_system_command(command)
return command if /^\// =~ command
case path = @system_commands[command]
when String
- if exists?(path)
- return path
- else
- Shell.Fail Error::CommandNotFound, command
- end
+ if exists?(path)
+ return path
+ else
+ Shell.Fail Error::CommandNotFound, command
+ end
when false
- Shell.Fail Error::CommandNotFound, command
+ Shell.Fail Error::CommandNotFound, command
end
for p in @shell.system_path
- path = join(p, command)
- if FileTest.exists?(path)
- @system_commands[command] = path
- return path
- end
+ path = join(p, command)
+ begin
+ st = File.stat(path)
+ rescue SystemCallError
+ next
+ else
+ next unless st.executable? and !st.directory?
+ @system_commands[command] = path
+ return path
+ end
end
@system_commands[command] = false
Shell.Fail Error::CommandNotFound, command
end
+ # call-seq:
+ # def_system_command(command, path) -> Shell::SystemCommand
+ #
+ # Defines a command, registering +path+ as a Shell method for the given
+ # +command+.
+ #
+ # Shell::CommandProcessor.def_system_command "ls"
+ # #=> Defines ls.
#
- # CommandProcessor.def_system_command(command, path)
- # command: String
- # path: String
- # define 'command()' method as method.
+ # Shell::CommandProcessor.def_system_command "sys_sort", "sort"
+ # #=> Defines sys_sort as sort
#
def self.def_system_command(command, path = command)
begin
- eval((d = %Q[def #{command}(*opts)
- SystemCommand.new(@shell, '#{path}', *opts)
+ eval((d = %Q[def #{command}(*opts)
+ SystemCommand.new(@shell, '#{path}', *opts)
end]), nil, __FILE__, __LINE__ - 1)
rescue SyntaxError
- Shell.notify "warn: Can't define #{command} path: #{path}."
+ Shell.notify "warn: Can't define #{command} path: #{path}."
end
Shell.notify "Define #{command} path: #{path}.", Shell.debug?
- Shell.notify("Definition of #{command}: ", d,
- Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ Shell.notify("Definition of #{command}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
end
+ # call-seq:
+ # undef_system_command(command) -> self
+ #
+ # Undefines a command
def self.undef_system_command(command)
command = command.id2name if command.kind_of?(Symbol)
remove_method(command)
@@ -379,213 +422,249 @@ class Shell
self
end
- # define command alias
- # ex)
- # def_alias_command("ls_c", "ls", "-C", "-F")
- # def_alias_command("ls_c", "ls"){|*opts| ["-C", "-F", *opts]}
- #
@alias_map = {}
+ # Returns a list of aliased commands
def self.alias_map
@alias_map
end
- def self.alias_command(ali, command, *opts, &block)
+ # call-seq:
+ # alias_command(alias, command, *options) -> self
+ #
+ # Creates a command alias at the given +alias+ for the given +command+,
+ # passing any +options+ along with it.
+ #
+ # Shell::CommandProcessor.alias_command "lsC", "ls", "-CBF", "--show-control-chars"
+ # Shell::CommandProcessor.alias_command("lsC", "ls"){|*opts| ["-CBF", "--show-control-chars", *opts]}
+ #
+ def self.alias_command(ali, command, *opts)
ali = ali.id2name if ali.kind_of?(Symbol)
command = command.id2name if command.kind_of?(Symbol)
begin
- if iterator?
- @alias_map[ali.intern] = proc
+ if iterator?
+ @alias_map[ali.intern] = proc
- eval((d = %Q[def #{ali}(*opts)
+ eval((d = %Q[def #{ali}(*opts)
@shell.__send__(:#{command},
*(CommandProcessor.alias_map[:#{ali}].call *opts))
- end]), nil, __FILE__, __LINE__ - 1)
-
- else
+ end]), nil, __FILE__, __LINE__ - 1)
+
+ else
args = opts.collect{|opt| '"' + opt + '"'}.join(",")
eval((d = %Q[def #{ali}(*opts)
@shell.__send__(:#{command}, #{args}, *opts)
end]), nil, __FILE__, __LINE__ - 1)
- end
+ end
rescue SyntaxError
- Shell.notify "warn: Can't alias #{ali} command: #{command}."
- Shell.notify("Definition of #{ali}: ", d)
- raise
+ Shell.notify "warn: Can't alias #{ali} command: #{command}."
+ Shell.notify("Definition of #{ali}: ", d)
+ raise
end
Shell.notify "Define #{ali} command: #{command}.", Shell.debug?
- Shell.notify("Definition of #{ali}: ", d,
- Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ Shell.notify("Definition of #{ali}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
self
end
-
+
+ # call-seq:
+ # unalias_command(alias) -> self
+ #
+ # Unaliases the given +alias+ command.
def self.unalias_command(ali)
ali = ali.id2name if ali.kind_of?(Symbol)
@alias_map.delete ali.intern
undef_system_command(ali)
end
-
- #
- # CommandProcessor.def_builtin_commands(delegation_class, command_specs)
- # delegation_class: Class or Module
- # command_specs: [[command_name, [argument,...]],...]
- # command_name: String
- # arguments: String
- # FILENAME?? -> expand_path(filename??)
- # *FILENAME?? -> filename??.collect{|f|expand_path(f)}.join(", ")
- # define command_name(argument,...) as
- # delegation_class.command_name(argument,...)
+
+ # :nodoc:
+ #
+ # Delegates File and FileTest methods into Shell, including the following
+ # commands:
+ #
+ # * Shell#blockdev?(file)
+ # * Shell#chardev?(file)
+ # * Shell#directory?(file)
+ # * Shell#executable?(file)
+ # * Shell#executable_real?(file)
+ # * Shell#exist?(file)/Shell#exists?(file)
+ # * Shell#file?(file)
+ # * Shell#grpowned?(file)
+ # * Shell#owned?(file)
+ # * Shell#pipe?(file)
+ # * Shell#readable?(file)
+ # * Shell#readable_real?(file)
+ # * Shell#setgid?(file)
+ # * Shell#setuid?(file)
+ # * Shell#size(file)/Shell#size?(file)
+ # * Shell#socket?(file)
+ # * Shell#sticky?(file)
+ # * Shell#symlink?(file)
+ # * Shell#writable?(file)
+ # * Shell#writable_real?(file)
+ # * Shell#zero?(file)
+ # * Shell#syscopy(filename_from, filename_to)
+ # * Shell#copy(filename_from, filename_to)
+ # * Shell#move(filename_from, filename_to)
+ # * Shell#compare(filename_from, filename_to)
+ # * Shell#safe_unlink(*filenames)
+ # * Shell#makedirs(*filenames)
+ # * Shell#install(filename_from, filename_to, mode)
+ #
+ # And also, there are some aliases for convenience:
+ #
+ # * Shell#cmp <- Shell#compare
+ # * Shell#mv <- Shell#move
+ # * Shell#cp <- Shell#copy
+ # * Shell#rm_f <- Shell#safe_unlink
+ # * Shell#mkpath <- Shell#makedirs
#
def self.def_builtin_commands(delegation_class, command_specs)
for meth, args in command_specs
- arg_str = args.collect{|arg| arg.downcase}.join(", ")
- call_arg_str = args.collect{
- |arg|
- case arg
- when /^(FILENAME.*)$/
- format("expand_path(%s)", $1.downcase)
- when /^(\*FILENAME.*)$/
- # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
- $1.downcase + '.collect{|fn| expand_path(fn)}'
- else
- arg
- end
- }.join(", ")
- d = %Q[def #{meth}(#{arg_str})
- #{delegation_class}.#{meth}(#{call_arg_str})
- end]
- Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
- Shell.notify("Definition of #{meth}: ", d,
- Shell.debug.kind_of?(Integer) && Shell.debug > 1)
- eval d
+ arg_str = args.collect{|arg| arg.downcase}.join(", ")
+ call_arg_str = args.collect{
+ |arg|
+ case arg
+ when /^(FILENAME.*)$/
+ format("expand_path(%s)", $1.downcase)
+ when /^(\*FILENAME.*)$/
+ # \*FILENAME* -> filenames.collect{|fn| expand_path(fn)}.join(", ")
+ $1.downcase + '.collect{|fn| expand_path(fn)}'
+ else
+ arg
+ end
+ }.join(", ")
+ d = %Q[def #{meth}(#{arg_str})
+ #{delegation_class}.#{meth}(#{call_arg_str})
+ end]
+ Shell.notify "Define #{meth}(#{arg_str})", Shell.debug?
+ Shell.notify("Definition of #{meth}: ", d,
+ Shell.debug.kind_of?(Integer) && Shell.debug > 1)
+ eval d
end
end
+ # call-seq:
+ # install_system_commands(prefix = "sys_")
+ #
+ # Defines all commands in the Shell.default_system_path as Shell method,
+ # all with given +prefix+ appended to their names.
#
- # CommandProcessor.install_system_commands(pre)
- # pre: String - command name prefix
- # defines every command which belongs in default_system_path via
- # CommandProcessor.command(). It doesn't define already defined
- # methods twice. By default, "pre_" is prefixes to each method
- # name. Characters that may not be used in a method name are
- # all converted to '_'. Definition errors are just ignored.
+ # Any invalid character names are converted to +_+, and errors are passed
+ # to Shell.notify.
#
+ # Methods already defined are skipped.
def self.install_system_commands(pre = "sys_")
defined_meth = {}
for m in Shell.methods
- defined_meth[m] = true
+ defined_meth[m] = true
end
sh = Shell.new
for path in Shell.default_system_path
- next unless sh.directory? path
- sh.cd path
- sh.foreach do
- |cn|
- if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
- command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
- begin
- def_system_command(command, sh.expand_path(cn))
- rescue
- Shell.notify "warn: Can't define #{command} path: #{cn}"
- end
- defined_meth[command] = command
- end
- end
+ next unless sh.directory? path
+ sh.cd path
+ sh.foreach do
+ |cn|
+ if !defined_meth[pre + cn] && sh.file?(cn) && sh.executable?(cn)
+ command = (pre + cn).gsub(/\W/, "_").sub(/^([0-9])/, '_\1')
+ begin
+ def_system_command(command, sh.expand_path(cn))
+ rescue
+ Shell.notify "warn: Can't define #{command} path: #{cn}"
+ end
+ defined_meth[command] = command
+ end
+ end
end
end
- #----------------------------------------------------------------------
- #
- # class initializing methods -
- #
- #----------------------------------------------------------------------
- def self.add_delegate_command_to_shell(id)
+ def self.add_delegate_command_to_shell(id) # :nodoc:
id = id.intern if id.kind_of?(String)
name = id.id2name
if Shell.method_defined?(id)
- Shell.notify "warn: override definnition of Shell##{name}."
- Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
- Shell.module_eval "alias #{name}_org #{name}"
+ Shell.notify "warn: override definition of Shell##{name}."
+ Shell.notify "warn: alias Shell##{name} to Shell##{name}_org.\n"
+ Shell.module_eval "alias #{name}_org #{name}"
end
Shell.notify "method added: Shell##{name}.", Shell.debug?
Shell.module_eval(%Q[def #{name}(*args, &block)
- begin
- @command_processor.__send__(:#{name}, *args, &block)
- rescue Exception
- $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
- $@.delete_if{|s| /^\\(eval\\):/ =~ s}
- raise
- end
+ begin
+ @command_processor.__send__(:#{name}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ raise
+ end
end], __FILE__, __LINE__)
if Shell::Filter.method_defined?(id)
- Shell.notify "warn: override definnition of Shell::Filter##{name}."
- Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
- Filter.module_eval "alias #{name}_org #{name}"
+ Shell.notify "warn: override definition of Shell::Filter##{name}."
+ Shell.notify "warn: alias Shell##{name} to Shell::Filter##{name}_org."
+ Filter.module_eval "alias #{name}_org #{name}"
end
Shell.notify "method added: Shell::Filter##{name}.", Shell.debug?
Filter.module_eval(%Q[def #{name}(*args, &block)
- begin
- self | @shell.__send__(:#{name}, *args, &block)
- rescue Exception
- $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
- $@.delete_if{|s| /^\\(eval\\):/ =~ s}
- raise
- end
+ begin
+ self | @shell.__send__(:#{name}, *args, &block)
+ rescue Exception
+ $@.delete_if{|s| /:in `__getobj__'$/ =~ s} #`
+ $@.delete_if{|s| /^\\(eval\\):/ =~ s}
+ raise
+ end
end], __FILE__, __LINE__)
end
- #
- # define default builtin commands
+ # Delegates File methods into Shell, including the following commands:
+ #
+ # * Shell#atime(file)
+ # * Shell#basename(file, *opt)
+ # * Shell#chmod(mode, *files)
+ # * Shell#chown(owner, group, *file)
+ # * Shell#ctime(file)
+ # * Shell#delete(*file)
+ # * Shell#dirname(file)
+ # * Shell#ftype(file)
+ # * Shell#join(*file)
+ # * Shell#link(file_from, file_to)
+ # * Shell#lstat(file)
+ # * Shell#mtime(file)
+ # * Shell#readlink(file)
+ # * Shell#rename(file_from, file_to)
+ # * Shell#split(file)
+ # * Shell#stat(file)
+ # * Shell#symlink(file_from, file_to)
+ # * Shell#truncate(file, length)
+ # * Shell#utime(atime, mtime, *file)
#
def self.install_builtin_commands
# method related File.
- # (exclude open/foreach/unlink)
+ # (exclude open/foreach/unlink)
normal_delegation_file_methods = [
- ["atime", ["FILENAME"]],
- ["basename", ["fn", "*opts"]],
- ["chmod", ["mode", "*FILENAMES"]],
- ["chown", ["owner", "group", "*FILENAME"]],
- ["ctime", ["FILENAMES"]],
- ["delete", ["*FILENAMES"]],
- ["dirname", ["FILENAME"]],
- ["ftype", ["FILENAME"]],
- ["join", ["*items"]],
- ["link", ["FILENAME_O", "FILENAME_N"]],
- ["lstat", ["FILENAME"]],
- ["mtime", ["FILENAME"]],
- ["readlink", ["FILENAME"]],
- ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
- # ["size", ["FILENAME"]],
- ["split", ["pathname"]],
- ["stat", ["FILENAME"]],
- ["symlink", ["FILENAME_O", "FILENAME_N"]],
- ["truncate", ["FILENAME", "length"]],
- ["utime", ["atime", "mtime", "*FILENAMES"]]]
+ ["atime", ["FILENAME"]],
+ ["basename", ["fn", "*opts"]],
+ ["chmod", ["mode", "*FILENAMES"]],
+ ["chown", ["owner", "group", "*FILENAME"]],
+ ["ctime", ["FILENAMES"]],
+ ["delete", ["*FILENAMES"]],
+ ["dirname", ["FILENAME"]],
+ ["ftype", ["FILENAME"]],
+ ["join", ["*items"]],
+ ["link", ["FILENAME_O", "FILENAME_N"]],
+ ["lstat", ["FILENAME"]],
+ ["mtime", ["FILENAME"]],
+ ["readlink", ["FILENAME"]],
+ ["rename", ["FILENAME_FROM", "FILENAME_TO"]],
+ ["split", ["pathname"]],
+ ["stat", ["FILENAME"]],
+ ["symlink", ["FILENAME_O", "FILENAME_N"]],
+ ["truncate", ["FILENAME", "length"]],
+ ["utime", ["atime", "mtime", "*FILENAMES"]]]
def_builtin_commands(File, normal_delegation_file_methods)
alias_method :rm, :delete
# method related FileTest
- def_builtin_commands(FileTest,
- FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
-
- # method related ftools
- normal_delegation_ftools_methods = [
- ["syscopy", ["FILENAME_FROM", "FILENAME_TO"]],
- ["copy", ["FILENAME_FROM", "FILENAME_TO"]],
- ["move", ["FILENAME_FROM", "FILENAME_TO"]],
- ["compare", ["FILENAME_FROM", "FILENAME_TO"]],
- ["safe_unlink", ["*FILENAMES"]],
- ["makedirs", ["*FILENAMES"]],
- # ["chmod", ["mode", "*FILENAMES"]],
- ["install", ["FILENAME_FROM", "FILENAME_TO", "mode"]],
- ]
- def_builtin_commands(File,
- normal_delegation_ftools_methods)
- alias_method :cmp, :compare
- alias_method :mv, :move
- alias_method :cp, :copy
- alias_method :rm_f, :safe_unlink
- alias_method :mkpath, :makedirs
+ def_builtin_commands(FileTest,
+ FileTest.singleton_methods(false).collect{|m| [m, ["FILENAME"]]})
+
end
end
diff --git a/lib/shell/error.rb b/lib/shell/error.rb
index 2f176862a8..677c424baf 100644
--- a/lib/shell/error.rb
+++ b/lib/shell/error.rb
@@ -1,13 +1,13 @@
+# frozen_string_literal: false
#
-# shell/error.rb -
-# $Release Version: 0.6.0 $
-# $Revision: 1.2 $
-# $Date: 2003/02/07 19:00:21 $
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# shell/error.rb -
+# $Release Version: 0.7 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
+#
#
require "e2mmap"
diff --git a/lib/shell/filter.rb b/lib/shell/filter.rb
index 12cc5206f8..caa976ae3e 100644
--- a/lib/shell/filter.rb
+++ b/lib/shell/filter.rb
@@ -1,27 +1,28 @@
+# frozen_string_literal: false
#
-# shell/filter.rb -
-# $Release Version: 0.6.0 $
-# $Revision: 1.3.2.1 $
-# $Date: 2004/03/21 12:21:11 $
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# shell/filter.rb -
+# $Release Version: 0.7 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
+#
#
-class Shell
+class Shell #:nodoc:
+ # Any result of command execution is a Filter.
#
- # Filter
- # A method to require
- # each()
+ # This class includes Enumerable, therefore a Filter object can use all
+ # Enumerable
+ # facilities.
#
class Filter
include Enumerable
def initialize(sh)
- @shell = sh # parent shell
- @input = nil # input filter
+ @shell = sh # parent shell
+ @input = nil # input filter
end
attr_reader :input
@@ -29,61 +30,88 @@ class Shell
def input=(filter)
@input = filter
end
-
+
+ # call-seq:
+ # each(record_separator=nil) { block }
+ #
+ # Iterates a block for each line.
def each(rs = nil)
rs = @shell.record_separator unless rs
if @input
- @input.each(rs){|l| yield l}
+ @input.each(rs){|l| yield l}
end
end
- def < (src)
+ # call-seq:
+ # < source
+ #
+ # Inputs from +source+, which is either a string of a file name or an IO
+ # object.
+ def <(src)
case src
when String
- cat = Cat.new(@shell, src)
- cat | self
+ cat = Cat.new(@shell, src)
+ cat | self
when IO
- self.input = src
- self
+ self.input = src
+ self
else
- Shell.Fail Error::CantApplyMethod, "<", to.class
+ Shell.Fail Error::CantApplyMethod, "<", src.class
end
end
- def > (to)
+ # call-seq:
+ # > source
+ #
+ # Outputs from +source+, which is either a string of a file name or an IO
+ # object.
+ def >(to)
case to
when String
- dst = @shell.open(to, "w")
- begin
- each(){|l| dst << l}
- ensure
- dst.close
- end
+ dst = @shell.open(to, "w")
+ begin
+ each(){|l| dst << l}
+ ensure
+ dst.close
+ end
when IO
- each(){|l| to << l}
+ each(){|l| to << l}
else
- Shell.Fail Error::CantApplyMethod, ">", to.class
+ Shell.Fail Error::CantApplyMethod, ">", to.class
end
self
end
- def >> (to)
+ # call-seq:
+ # >> source
+ #
+ # Appends the output to +source+, which is either a string of a file name
+ # or an IO object.
+ def >>(to)
begin
- Shell.cd(@shell.pwd).append(to, self)
+ Shell.cd(@shell.pwd).append(to, self)
rescue CantApplyMethod
- Shell.Fail Error::CantApplyMethod, ">>", to.class
+ Shell.Fail Error::CantApplyMethod, ">>", to.class
end
end
- def | (filter)
+ # call-seq:
+ # | filter
+ #
+ # Processes a pipeline.
+ def |(filter)
filter.input = self
if active?
- @shell.process_controller.start_job filter
+ @shell.process_controller.start_job filter
end
filter
end
- def + (filter)
+ # call-seq:
+ # filter1 + filter2
+ #
+ # Outputs +filter1+, and then +filter2+ using Join.new
+ def +(filter)
Join.new(@shell, self, filter)
end
@@ -101,9 +129,9 @@ class Shell
def inspect
if @shell.debug.kind_of?(Integer) && @shell.debug > 2
- super
+ super
else
- to_s
+ to_s
end
end
end
diff --git a/lib/shell/process-controller.rb b/lib/shell/process-controller.rb
index 573edb6829..d54da68cb0 100644
--- a/lib/shell/process-controller.rb
+++ b/lib/shell/process-controller.rb
@@ -1,77 +1,111 @@
+# frozen_string_literal: false
#
-# shell/process-controller.rb -
-# $Release Version: 0.6.0 $
-# $Revision: 1.3 $
-# $Date: 2003/10/16 17:47:19 $
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# shell/process-controller.rb -
+# $Release Version: 0.7 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
#
-
-require "mutex_m"
-require "monitor"
+#
+require "forwardable"
require "sync"
class Shell
class ProcessController
@ProcessControllers = {}
- @ProcessControllers.extend Mutex_m
+ @ProcessControllersMonitor = Thread::Mutex.new
+ @ProcessControllersCV = Thread::ConditionVariable.new
+
+ @BlockOutputMonitor = Thread::Mutex.new
+ @BlockOutputCV = Thread::ConditionVariable.new
+
+ class << self
+ extend Forwardable
- class<<self
+ def_delegator("@ProcessControllersMonitor",
+ "synchronize", "process_controllers_exclusive")
- def process_controllers_exclusive
- begin
- @ProcessControllers.lock unless Thread.critical
- yield
- ensure
- @ProcessControllers.unlock unless Thread.critical
- end
+ def active_process_controllers
+ process_controllers_exclusive do
+ @ProcessControllers.dup
+ end
end
def activate(pc)
- process_controllers_exclusive do
- @ProcessControllers[pc] ||= 0
- @ProcessControllers[pc] += 1
- end
+ process_controllers_exclusive do
+ @ProcessControllers[pc] ||= 0
+ @ProcessControllers[pc] += 1
+ end
end
def inactivate(pc)
- process_controllers_exclusive do
- if @ProcessControllers[pc]
- if (@ProcessControllers[pc] -= 1) == 0
- @ProcessControllers.delete(pc)
- end
- end
- end
+ process_controllers_exclusive do
+ if @ProcessControllers[pc]
+ if (@ProcessControllers[pc] -= 1) == 0
+ @ProcessControllers.delete(pc)
+ @ProcessControllersCV.signal
+ end
+ end
+ end
end
def each_active_object
- process_controllers_exclusive do
- for ref in @ProcessControllers.keys
- yield ref
- end
- end
+ process_controllers_exclusive do
+ for ref in @ProcessControllers.keys
+ yield ref
+ end
+ end
+ end
+
+ def block_output_synchronize(&b)
+ @BlockOutputMonitor.synchronize(&b)
+ end
+
+ def wait_to_finish_all_process_controllers
+ process_controllers_exclusive do
+ while !@ProcessControllers.empty?
+ Shell::notify("Process finishing, but active shell exists",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+ if Shell.debug?
+ for pc in @ProcessControllers.keys
+ Shell::notify(" Not finished jobs in "+pc.shell.to_s)
+ for com in pc.jobs
+ com.notify(" Jobs: %id")
+ end
+ end
+ end
+ @ProcessControllersCV.wait(@ProcessControllersMonitor)
+ end
+ end
end
end
+ # for shell-command complete finish at this process exit.
+ USING_AT_EXIT_WHEN_PROCESS_EXIT = true
+ at_exit do
+ wait_to_finish_all_process_controllers unless $@
+ end
+
def initialize(shell)
@shell = shell
@waiting_jobs = []
@active_jobs = []
@jobs_sync = Sync.new
- @job_monitor = Mutex.new
- @job_condition = ConditionVariable.new
+ @job_monitor = Thread::Mutex.new
+ @job_condition = Thread::ConditionVariable.new
end
+ attr_reader :shell
+
def jobs
jobs = []
@jobs_sync.synchronize(:SH) do
- jobs.concat @waiting_jobs
- jobs.concat @active_jobs
+ jobs.concat @waiting_jobs
+ jobs.concat @active_jobs
end
jobs
end
@@ -83,177 +117,192 @@ class Shell
def waiting_jobs
@waiting_jobs
end
-
+
def jobs_exist?
@jobs_sync.synchronize(:SH) do
- @active_jobs.empty? or @waiting_jobs.empty?
+ @active_jobs.empty? or @waiting_jobs.empty?
end
end
def active_jobs_exist?
@jobs_sync.synchronize(:SH) do
- @active_jobs.empty?
+ @active_jobs.empty?
end
end
def waiting_jobs_exist?
@jobs_sync.synchronize(:SH) do
- @waiting_jobs.empty?
+ @waiting_jobs.empty?
end
end
# schedule a command
def add_schedule(command)
@jobs_sync.synchronize(:EX) do
- ProcessController.activate(self)
- if @active_jobs.empty?
- start_job command
- else
- @waiting_jobs.push(command)
- end
+ ProcessController.activate(self)
+ if @active_jobs.empty?
+ start_job command
+ else
+ @waiting_jobs.push(command)
+ end
end
end
# start a job
def start_job(command = nil)
@jobs_sync.synchronize(:EX) do
- if command
- return if command.active?
- @waiting_jobs.delete command
- else
- command = @waiting_jobs.shift
- return unless command
- end
- @active_jobs.push command
- command.start
-
- # start all jobs that input from the job
- for job in @waiting_jobs
- start_job(job) if job.input == command
- end
+ if command
+ return if command.active?
+ @waiting_jobs.delete command
+ else
+ command = @waiting_jobs.shift
+
+ return unless command
+ end
+ @active_jobs.push command
+ command.start
+
+ # start all jobs that input from the job
+ for job in @waiting_jobs.dup
+ start_job(job) if job.input == command
+ end
end
end
def waiting_job?(job)
@jobs_sync.synchronize(:SH) do
- @waiting_jobs.include?(job)
+ @waiting_jobs.include?(job)
end
end
def active_job?(job)
@jobs_sync.synchronize(:SH) do
- @active_jobs.include?(job)
+ @active_jobs.include?(job)
end
end
# terminate a job
def terminate_job(command)
@jobs_sync.synchronize(:EX) do
- @active_jobs.delete command
- ProcessController.inactivate(self)
- if @active_jobs.empty?
- start_job
- end
+ @active_jobs.delete command
+ ProcessController.inactivate(self)
+ if @active_jobs.empty?
+ command.notify("start_job in terminate_job(%id)", Shell::debug?)
+ start_job
+ end
end
end
# kill a job
def kill_job(sig, command)
- @jobs_sync.synchronize(:SH) do
- if @waiting_jobs.delete command
- ProcessController.inactivate(self)
- return
- elsif @active_jobs.include?(command)
- begin
- r = command.kill(sig)
- ProcessController.inactivate(self)
- rescue
- print "Shell: Warn: $!\n" if @shell.verbose?
- return nil
- end
- @active_jobs.delete command
- r
- end
+ @jobs_sync.synchronize(:EX) do
+ if @waiting_jobs.delete command
+ ProcessController.inactivate(self)
+ return
+ elsif @active_jobs.include?(command)
+ begin
+ r = command.kill(sig)
+ ProcessController.inactivate(self)
+ rescue
+ print "Shell: Warn: $!\n" if @shell.verbose?
+ return nil
+ end
+ @active_jobs.delete command
+ r
+ end
end
end
# wait for all jobs to terminate
def wait_all_jobs_execution
@job_monitor.synchronize do
- begin
- while !jobs.empty?
- @job_condition.wait(@job_monitor)
- end
- ensure
- redo unless jobs.empty?
- end
+ begin
+ while !jobs.empty?
+ @job_condition.wait(@job_monitor)
+ for job in jobs
+ job.notify("waiting job(%id)", Shell::debug?)
+ end
+ end
+ ensure
+ redo unless jobs.empty?
+ end
end
end
# simple fork
- def sfork(command, &block)
+ def sfork(command)
pipe_me_in, pipe_peer_out = IO.pipe
pipe_peer_in, pipe_me_out = IO.pipe
- Thread.critical = true
- STDOUT.flush
- ProcessController.each_active_object do |pc|
- for jobs in pc.active_jobs
- jobs.flush
- end
+
+ pid = nil
+ pid_mutex = Thread::Mutex.new
+ pid_cv = Thread::ConditionVariable.new
+
+ Thread.start do
+ ProcessController.block_output_synchronize do
+ STDOUT.flush
+ ProcessController.each_active_object do |pc|
+ for jobs in pc.active_jobs
+ jobs.flush
+ end
+ end
+
+ pid = fork {
+ Thread.list.each do |th|
+ th.kill unless Thread.current == th
+ end
+
+ STDIN.reopen(pipe_peer_in)
+ STDOUT.reopen(pipe_peer_out)
+
+ ObjectSpace.each_object(IO) do |io|
+ if ![STDIN, STDOUT, STDERR].include?(io)
+ io.close
+ end
+ end
+
+ yield
+ }
+ end
+ pid_cv.signal
+
+ pipe_peer_in.close
+ pipe_peer_out.close
+ command.notify "job(%name:##{pid}) start", @shell.debug?
+
+ begin
+ _pid = nil
+ command.notify("job(%id) start to waiting finish.", @shell.debug?)
+ _pid = Process.waitpid(pid, nil)
+ rescue Errno::ECHILD
+ command.notify "warn: job(%id) was done already waitpid."
+ _pid = true
+ ensure
+ command.notify("Job(%id): Wait to finish when Process finished.", @shell.debug?)
+ # when the process ends, wait until the command terminates
+ if USING_AT_EXIT_WHEN_PROCESS_EXIT or _pid
+ else
+ command.notify("notice: Process finishing...",
+ "wait for Job[%id] to finish.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+ redo
+ end
+
+ @job_monitor.synchronize do
+ terminate_job(command)
+ @job_condition.signal
+ command.notify "job(%id) finish.", @shell.debug?
+ end
+ end
end
-
- pid = fork {
- Thread.critical = true
-
- Thread.list.each do |th|
- th.kill unless [Thread.main, Thread.current].include?(th)
- end
-
- STDIN.reopen(pipe_peer_in)
- STDOUT.reopen(pipe_peer_out)
-
- ObjectSpace.each_object(IO) do |io|
- if ![STDIN, STDOUT, STDERR].include?(io)
- io.close unless io.closed?
- end
- end
- yield
- }
-
- pipe_peer_in.close
- pipe_peer_out.close
- command.notify "job(%name:##{pid}) start", @shell.debug?
- Thread.critical = false
-
- th = Thread.start {
- Thread.critical = true
- begin
- _pid = nil
- command.notify("job(%id) start to waiting finish.", @shell.debug?)
- Thread.critical = false
- _pid = Process.waitpid(pid, nil)
- rescue Errno::ECHILD
- command.notify "warn: job(%id) was done already waitipd."
- _pid = true
- ensure
- # when the process ends, wait until the command termintes
- if _pid
- else
- command.notify("notice: Process finishing...",
- "wait for Job[%id] to finish.",
- "You can use Shell#transact or Shell#check_point for more safe execution.")
- redo
- end
- Thread.exclusive do
- @job_monitor.synchronize do
- terminate_job(command)
- @job_condition.signal
- command.notify "job(%id) finish.", @shell.debug?
- end
- end
- end
- }
+
+ pid_mutex.synchronize do
+ while !pid
+ pid_cv.wait(pid_mutex)
+ end
+ end
+
return pid, pipe_me_in, pipe_me_out
end
end
diff --git a/lib/shell/system-command.rb b/lib/shell/system-command.rb
index 8b1cba98c8..af22ed90d7 100644
--- a/lib/shell/system-command.rb
+++ b/lib/shell/system-command.rb
@@ -1,13 +1,13 @@
+# frozen_string_literal: false
#
-# shell/system-command.rb -
-# $Release Version: 0.6.0 $
-# $Revision: 1.2.2.1 $
-# $Date: 2004/03/21 12:21:11 $
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# shell/system-command.rb -
+# $Release Version: 0.7 $
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
+#
#
require "shell/filter"
@@ -16,13 +16,13 @@ class Shell
class SystemCommand < Filter
def initialize(sh, command, *opts)
if t = opts.find{|opt| !opt.kind_of?(String) && opt.class}
- Shell.Fail Error::TypeError, t.class, "String"
+ Shell.Fail TypeError, t.class, "String"
end
super(sh)
@command = command
@opts = opts
-
- @input_queue = Queue.new
+
+ @input_queue = Thread::Queue.new
@pid = nil
sh.process_controller.add_schedule(self)
@@ -42,17 +42,20 @@ class Shell
def input=(inp)
super
if active?
- start_export
+ start_export
end
end
def start
+ notify([@command, *@opts].join(" "))
+
@pid, @pipe_in, @pipe_out = @shell.process_controller.sfork(self) {
- Dir.chdir @shell.pwd
- exec(@command, *@opts)
+ Dir.chdir @shell.pwd
+ $0 = @command
+ exec(@command, *@opts)
}
if @input
- start_export
+ start_export
end
start_import
end
@@ -63,105 +66,93 @@ class Shell
def terminate
begin
- @pipe_in.close
+ @pipe_in.close
rescue IOError
end
begin
- @pipe_out.close
+ @pipe_out.close
rescue IOError
end
end
def kill(sig)
if @pid
- Process.kill(sig, @pid)
+ Process.kill(sig, @pid)
end
end
-
def start_import
-# Thread.critical = true
notify "Job(%id) start imp-pipe.", @shell.debug?
- rs = @shell.record_separator unless rs
_eop = true
-# Thread.critical = false
- th = Thread.start {
- Thread.critical = true
- begin
- Thread.critical = false
- while l = @pipe_in.gets
- @input_queue.push l
- end
- _eop = false
- rescue Errno::EPIPE
- _eop = false
- ensure
- if _eop
- notify("warn: Process finishing...",
- "wait for Job[%id] to finish pipe importing.",
- "You can use Shell#transact or Shell#check_point for more safe execution.")
-# Tracer.on
- Thread.current.run
- redo
- end
- Thread.exclusive do
- notify "job(%id}) close imp-pipe.", @shell.debug?
- @input_queue.push :EOF
- @pipe_in.close
- end
- end
+ Thread.start {
+ begin
+ while l = @pipe_in.gets
+ @input_queue.push l
+ end
+ _eop = false
+ rescue Errno::EPIPE
+ _eop = false
+ ensure
+ if !ProcessController::USING_AT_EXIT_WHEN_PROCESS_EXIT and _eop
+ notify("warn: Process finishing...",
+ "wait for Job[%id] to finish pipe importing.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+ redo
+ end
+ notify "job(%id}) close imp-pipe.", @shell.debug?
+ @input_queue.push :EOF
+ @pipe_in.close
+ end
}
end
def start_export
notify "job(%id) start exp-pipe.", @shell.debug?
_eop = true
- th = Thread.start{
- Thread.critical = true
- begin
- Thread.critical = false
- @input.each{|l| @pipe_out.print l}
- _eop = false
- rescue Errno::EPIPE
- _eop = false
- ensure
- if _eop
- notify("shell: warn: Process finishing...",
- "wait for Job(%id) to finish pipe exporting.",
- "You can use Shell#transact or Shell#check_point for more safe execution.")
-# Tracer.on
- redo
- end
- Thread.exclusive do
- notify "job(%id) close exp-pipe.", @shell.debug?
- @pipe_out.close
- end
- end
+ Thread.start{
+ begin
+ @input.each do |l|
+ ProcessController::block_output_synchronize do
+ @pipe_out.print l
+ end
+ end
+ _eop = false
+ rescue Errno::EPIPE, Errno::EIO
+ _eop = false
+ ensure
+ if !ProcessController::USING_AT_EXIT_WHEN_PROCESS_EXIT and _eop
+ notify("shell: warn: Process finishing...",
+ "wait for Job(%id) to finish pipe exporting.",
+ "You can use Shell#transact or Shell#check_point for more safe execution.")
+ redo
+ end
+ notify "job(%id) close exp-pipe.", @shell.debug?
+ @pipe_out.close
+ end
}
end
alias super_each each
def each(rs = nil)
while (l = @input_queue.pop) != :EOF
- yield l
+ yield l
end
end
# ex)
- # if you wish to output:
- # "shell: job(#{@command}:#{@pid}) close pipe-out."
- # then
- # mes: "job(%id) close pipe-out."
+ # if you wish to output:
+ # "shell: job(#{@command}:#{@pid}) close pipe-out."
+ # then
+ # mes: "job(%id) close pipe-out."
# yorn: Boolean(@shell.debug? or @shell.verbose?)
- def notify(*opts, &block)
- Thread.exclusive do
- @shell.notify(*opts) {|mes|
- yield mes if iterator?
-
- mes.gsub!("%id", "#{@command}:##{@pid}")
- mes.gsub!("%name", "#{@command}")
- mes.gsub!("%pid", "#{@pid}")
- }
+ def notify(*opts)
+ @shell.notify(*opts) do |mes|
+ yield mes if iterator?
+
+ mes.gsub!("%id", "#{@command}:##{@pid}")
+ mes.gsub!("%name", "#{@command}")
+ mes.gsub!("%pid", "#{@pid}")
+ mes
end
end
end
diff --git a/lib/shell/version.rb b/lib/shell/version.rb
index 46b3425afa..bb4e7dfb8e 100644
--- a/lib/shell/version.rb
+++ b/lib/shell/version.rb
@@ -1,16 +1,16 @@
+# frozen_string_literal: false
#
# version.rb - shell version definition file
-# $Release Version: 0.6.0$
-# $Revision: 1.1 $
-# $Date: 2001/05/17 10:02:48 $
-# by Keiju ISHITSUKA(Nihon Rational Software Co.,Ltd)
+# $Release Version: 0.7$
+# $Revision$
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
#
# --
#
-#
+#
#
-class Shell
- @RELEASE_VERSION = "0.6.0"
- @LAST_UPDATE_DATE = "01/03/19"
+class Shell # :nodoc:
+ @RELEASE_VERSION = "0.7"
+ @LAST_UPDATE_DATE = "07/03/20"
end