summaryrefslogtreecommitdiff
path: root/jni/ruby/lib/shell/command-processor.rb
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/lib/shell/command-processor.rb
Fresh start
Diffstat (limited to 'jni/ruby/lib/shell/command-processor.rb')
-rw-r--r--jni/ruby/lib/shell/command-processor.rb667
1 files changed, 667 insertions, 0 deletions
diff --git a/jni/ruby/lib/shell/command-processor.rb b/jni/ruby/lib/shell/command-processor.rb
new file mode 100644
index 0000000..488ed93
--- /dev/null
+++ b/jni/ruby/lib/shell/command-processor.rb
@@ -0,0 +1,667 @@
+#
+# shell/command-controller.rb -
+# $Release Version: 0.7 $
+# $Revision: 47246 $
+# by Keiju ISHITSUKA(keiju@ruby-lang.org)
+#
+# --
+#
+#
+#
+
+require "e2mmap"
+require "thread"
+
+require "shell/error"
+require "shell/filter"
+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
+
+ #
+ # initialize of Shell and related classes.
+ #
+ 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 CommandProcessor#methods to Shell#methods and Filter#methods
+ for m in CommandProcessor.instance_methods(false) - NoDelegateMethods
+ add_delegate_command_to_shell(m)
+ end
+
+ def self.method_added(id)
+ add_delegate_command_to_shell(id)
+ end
+ end
+
+ #
+ # include run file.
+ #
+ def self.run_config
+ begin
+ load File.expand_path("~/.rb_shell") 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
+ end
+ end
+
+ def initialize(shell)
+ @shell = shell
+ @system_commands = {}
+ end
+
+ #
+ # CommandProcessor#expand_path(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 }
+ #
+ # See IO.foreach when +path+ is a file.
+ #
+ # 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}
+ else
+ IO.foreach(path, *rs){|l| yield l}
+ end
+ end
+
+ # call-seq:
+ # open(path, mode, permissions) -> Enumerator
+ # open(path, mode, permissions) { block }
+ #
+ # See IO.open when +path+ is a file.
+ #
+ # 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, &b)
+ else
+ 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
+
+ # call-seq:
+ # unlink(path)
+ #
+ # See IO.unlink when +path+ is a 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)
+ else
+ 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"]
+ #
+ def test(command, file1, file2=nil)
+ file1 = expand_path(file1)
+ file2 = expand_path(file2) if file2
+ command = command.id2name if command.kind_of?(Symbol)
+
+ case command
+ when Integer
+ 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
+ end
+ end
+ # See Shell::CommandProcessor#test
+ alias [] test
+
+ # call-seq:
+ # mkdir(path)
+ #
+ # 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
+ 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)
+ #
+ # 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))
+ 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
+ #
+ 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
+ end
+ SystemCommand.new(@shell, find_system_command(command), *opts)
+ end
+
+ # call-seq:
+ # rehash
+ #
+ # Clears the command hash table.
+ def rehash
+ @system_commands = {}
+ end
+
+ def check_point # :nodoc:
+ @shell.process_controller.wait_all_jobs_execution
+ end
+ 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)
+ ensure
+ check_point
+ end
+ end
+
+ # call-seq:
+ # out(device) { block }
+ #
+ # 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
+
+ # 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
+
+ def append(to, filter)
+ case to
+ when String
+ AppendFile.new(@shell, to, filter)
+ when IO
+ AppendIO.new(@shell, to, filter)
+ else
+ 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)
+ Shell.notify(*opts) {|mes|
+ yield mes if iterator?
+
+ mes.gsub!("%pwd", "#{@cwd}")
+ mes.gsub!("%cwd", "#{@cwd}")
+ }
+ end
+
+ #
+ # private functions
+ #
+ 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
+ when false
+ Shell.Fail Error::CommandNotFound, command
+ end
+
+ for p in @shell.system_path
+ 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.
+ #
+ # 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)
+ end]), nil, __FILE__, __LINE__ - 1)
+ rescue SyntaxError
+ 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)
+ 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)
+ Shell.module_eval{remove_method(command)}
+ Filter.module_eval{remove_method(command)}
+ self
+ end
+
+ @alias_map = {}
+ # Returns a list of aliased commands
+ def self.alias_map
+ @alias_map
+ end
+ # 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
+
+ eval((d = %Q[def #{ali}(*opts)
+ @shell.__send__(:#{command},
+ *(CommandProcessor.alias_map[:#{ali}].call *opts))
+ 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
+ rescue SyntaxError
+ 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)
+ 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
+
+ # :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
+ 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.
+ #
+ # 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
+ 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
+ end
+ end
+
+ 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 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
+ end], __FILE__, __LINE__)
+
+ if Shell::Filter.method_defined?(id)
+ 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
+ end], __FILE__, __LINE__)
+ end
+
+ # 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)
+ 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"]],
+ ["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"]]})
+
+ end
+
+ end
+end