lib/bio/command.rb in bio-1.0.0 vs lib/bio/command.rb in bio-1.1.0
- old
+ new
@@ -1,53 +1,37 @@
#
# = bio/command.rb - general methods for external command execution
#
-# Copyright:: Copyright (C) 2003-2005
+# Copyright:: Copyright (C) 2003-2006
# Naohisa Goto <ng@bioruby.org>,
# Toshiaki Katayama <k@bioruby.org>
-# License:: LGPL
+# License:: The Ruby License
#
-# $Id: command.rb,v 1.3 2005/11/04 17:36:00 k Exp $
+# $Id: command.rb,v 1.17 2007/04/05 23:35:39 trevor Exp $
#
-#--
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-#++
-#
require 'open3'
+require 'uri'
+require 'open-uri'
+require 'net/http'
module Bio
-module Command
-# = Bio::Command::Tools
+# = Bio::Command
#
-# Bio::Command::Tools is a collection of useful methods for execution
-# of external commands or web applications. Any wrapper class for
-# applications shall include this class. Note that all methods below
-# are private except for some methods.
-module Tools
+# Bio::Command is a collection of useful methods for execution
+# of external commands or web applications.
+# Any wrapper class for applications shall use this class.
+#
+# Library internal use only. Users should not directly use it.
+module Command
UNSAFE_CHARS_UNIX = /[^A-Za-z0-9\_\-\.\:\,\/\@\x1b\x80-\xfe]/n
QUOTE_CHARS_WINDOWS = /[^A-Za-z0-9\_\-\.\:\,\/\@\\]/n
UNESCAPABLE_CHARS = /[\x00-\x08\x10-\x1a\x1c-\x1f\x7f\xff]/n
- #module_function
- private
+ module_function
# Escape special characters in command line string for cmd.exe on Windows.
def escape_shell_windows(str)
str = str.to_s
raise 'cannot escape control characters' if UNESCAPABLE_CHARS =~ str
@@ -96,71 +80,258 @@
def make_command_line_unix(ary)
ary.collect { |str| escape_shell_unix(str) }.join(" ")
end
# Executes the program. Automatically select popen for Windows
- # environment and open3 for the others.
- #
- # If block is given, yield the block with input and output IO objects.
- # Note that in some platform, inn and out are the same object.
- # Please be careful to do inn.close and out.close.
- def call_command_local(cmd, query = nil, &block)
+ # environment and fork for the others.
+ # A block must be given. An IO object is passed to the block.
+ def call_command(cmd, &block)
case RUBY_PLATFORM
when /mswin32|bccwin32/
- call_command_local_popen(cmd, query, &block)
+ call_command_popen(cmd, &block)
else
- call_command_local_open3(cmd, query, &block)
+ call_command_fork(cmd, &block)
end
end
# Executes the program via IO.popen for OS which doesn't support fork.
- # If block is given, yield the block with IO objects.
- # The two objects are the same because of limitation of IO.popen.
- def call_command_local_popen(cmd, query = nil)
+ # A block must be given. An IO object is passed to the block.
+ def call_command_popen(cmd)
str = make_command_line(cmd)
IO.popen(str, "w+") do |io|
- if block_given? then
- io.sync = true
- yield io, io
+ io.sync = true
+ yield io
+ end
+ end
+
+ # Executes the program via fork (by using IO.popen("-")) and exec.
+ # A block must be given. An IO object is passed to the block.
+ #
+ # From the view point of security, this method is recommended
+ # rather than call_command_popen.
+ def call_command_fork(cmd)
+ IO.popen("-", "r+") do |io|
+ if io then
+ # parent
+ yield io
else
+ # child
+ begin
+ Kernel.exec(*cmd)
+ rescue Errno::ENOENT, Errno::EACCES
+ Process.exit!(127)
+ rescue Exception
+ end
+ Process.exit!(1)
+ end
+ end
+ end
+
+ # Executes the program via Open3.popen3
+ # A block must be given. IO objects are passed to the block.
+ #
+ # You would use this method only when you really need to get stderr.
+ def call_command_open3(cmd)
+ cmd = cmd.collect { |x| x.to_s }
+ Open3.popen3(*cmd) do |pin, pout, perr|
+ yield pin, pout, perr
+ end
+ end
+
+ # Executes the program with the query (String) given to the standard input,
+ # waits the program termination, and returns the output data printed to the
+ # standard output as a string.
+ #
+ # Automatically select popen for Windows environment and fork for the others.
+ def query_command(cmd, query = nil)
+ case RUBY_PLATFORM
+ when /mswin32|bccwin32/
+ query_command_popen(cmd, query)
+ else
+ query_command_fork(cmd, query)
+ end
+ end
+
+ # Executes the program with the query (String) given to the standard input,
+ # waits the program termination, and returns the output data printed to the
+ # standard output as a string.
+ #
+ # IO.popen is used for OS which doesn't support fork.
+ def query_command_popen(cmd, query = nil)
+ str = make_command_line(cmd)
+ IO.popen(str, "w+") do |io|
+ io.sync = true
+ io.print query if query
+ io.close_write
+ io.read
+ end
+ end
+
+ # Executes the program with the query (String) given to the standard input,
+ # waits the program termination, and returns the output data printed to the
+ # standard output as a string.
+ #
+ # Fork (by using IO.popen("-")) and exec is used to execute the program.
+ #
+ # From the view point of security, this method is recommended
+ # rather than query_popen.
+ def query_command_fork(cmd, query = nil)
+ IO.popen("-", "r+") do |io|
+ if io then
+ # parent
io.sync = true
io.print query if query
io.close_write
io.read
+ else
+ # child
+ begin
+ Kernel.exec(*cmd)
+ rescue Errno::ENOENT, Errno::EACCES
+ Process.exit!(127)
+ rescue Exception
+ end
+ Process.exit!(1)
end
end
end
- # Executes the program via Open3.popen3
- # If block is given, yield the block with input and output IO objects.
+ # Executes the program via Open3.popen3 with the query (String) given
+ # to the stain, waits the program termination, and
+ # returns the data from stdout and stderr as an array of the strings.
#
# From the view point of security, this method is recommended
# rather than exec_local_popen.
- def call_command_local_open3(cmd, query = nil)
+ def query_command_open3(cmd, query = nil)
+ errorlog = nil
cmd = cmd.collect { |x| x.to_s }
Open3.popen3(*cmd) do |pin, pout, perr|
perr.sync = true
- t = Thread.start { @errorlog = perr.read }
- if block_given? then
- yield pin, pout
- else
- begin
- pin.print query if query
- pin.close
- output = pout.read
- ensure
- t.join
- end
- output
+ t = Thread.start { errorlog = perr.read }
+ begin
+ pin.print query if query
+ pin.close
+ output = pout.read
+ ensure
+ t.join
end
+ [ output, errorlog ]
end
end
- # Shows the latest stderr of the program execution.
- # Note that this method may be thread unsafe.
- attr_reader :errorlog
- public :errorlog
+ # Same as OpenURI.open_uri(uri).read.
+ def read_uri(uri)
+ OpenURI.open_uri(uri).read
+ end
-end # module Tools
+ # Same as:
+ # Net::HTTP.start(address, port)
+ # and
+ # it uses proxy if an environment variable (same as OpenURI.open_uri)
+ # is set.
+ #
+ def start_http(address, port = 80, &block)
+ uri = URI.parse("http://#{address}:#{port}")
+ # Note: URI#find_proxy is an unofficial method defined in open-uri.rb.
+ # If the spec of open-uri.rb would be changed, we should change below.
+ if proxyuri = uri.find_proxy then
+ raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP
+ http = Net::HTTP.Proxy(proxyuri.host, proxyuri.port)
+ else
+ http = Net::HTTP
+ end
+ http.start(address, port, &block)
+ end
+
+ # Same as:
+ # Net::HTTP.new(address, port)
+ # and
+ # it uses proxy if an environment variable (same as OpenURI.open_uri)
+ # is set.
+ #
+ def new_http(address, port = 80)
+ uri = URI.parse("http://#{address}:#{port}")
+ # Note: URI#find_proxy is an unofficial method defined in open-uri.rb.
+ # If the spec of open-uri.rb would be changed, we should change below.
+ if proxyuri = uri.find_proxy then
+ raise 'Non-HTTP proxy' if proxyuri.class != URI::HTTP
+ Net::HTTP.new(address, port, proxyuri.host, proxyuri.port)
+ else
+ Net::HTTP.new(address, port)
+ end
+ end
+
+ # Same as:
+ # Net::HTTP.post_form(uri, params)
+ # and
+ # it uses proxy if an environment variable (same as OpenURI.open_uri)
+ # is set.
+ # In addition, +header+ can be set.
+ # (Note that Content-Type and Content-Length are automatically
+ # set by default.)
+ # +uri+ must be a URI object, +params+ must be a hash, and
+ # +header+ must be a hash.
+ #
+ def post_form(uri, params = nil, header = {})
+ unless uri.is_a?(URI)
+ uri = URI.parse(uri)
+ end
+
+ data = make_cgi_params(params)
+
+ hash = {
+ 'Content-Type' => 'application/x-www-form-urlencoded',
+ 'Content-Length' => data.length.to_s
+ }
+ hash.update(header)
+
+ start_http(uri.host, uri.port) do |http|
+ http.post(uri.path, data, hash)
+ end
+ end
+
+ def make_cgi_params(params)
+ data = ""
+ case params
+ when Hash
+ data = params.map do |key, val|
+ make_cgi_params_key_value(key, val)
+ end.join('&')
+ when Array
+ case params.first
+ when Hash
+ data = params.map do |hash|
+ hash.map do |key, val|
+ make_cgi_params_key_value(key, val)
+ end
+ end.join('&')
+ when Array
+ data = params.map do |key, val|
+ make_cgi_params_key_value(key, val)
+ end.join('&')
+ when String
+ data = params.map do |str|
+ URI.escape(str.strip)
+ end.join('&')
+ end
+ when String
+ data = URI.escape(params.strip)
+ end
+ return data
+ end
+
+ def make_cgi_params_key_value(key, value)
+ result = []
+ case value
+ when Array
+ value.each do |val|
+ result << [key, val].map {|x| URI.escape(x.to_s) }.join('=')
+ end
+ else
+ result << [key, value].map {|x| URI.escape(x.to_s) }.join('=')
+ end
+ return result
+ end
+
end # module Command
end # module Bio