aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Homebrew/cask/lib/hbc/system_command.rb
diff options
context:
space:
mode:
authorAnastasiaSulyagina2016-08-18 22:11:42 +0300
committerAnastasiaSulyagina2016-08-19 14:50:14 +0300
commite81f4ab7deeb40308f240be5ea00091fc8786d7a (patch)
treeb5418f9149de71c0f05f90cb2b39ab47f46e27b4 /Library/Homebrew/cask/lib/hbc/system_command.rb
parent5c7c9de669025bbe4cad9829be39c5cf3b31ad25 (diff)
downloadbrew-e81f4ab7deeb40308f240be5ea00091fc8786d7a.tar.bz2
init
Diffstat (limited to 'Library/Homebrew/cask/lib/hbc/system_command.rb')
-rw-r--r--Library/Homebrew/cask/lib/hbc/system_command.rb173
1 files changed, 173 insertions, 0 deletions
diff --git a/Library/Homebrew/cask/lib/hbc/system_command.rb b/Library/Homebrew/cask/lib/hbc/system_command.rb
new file mode 100644
index 000000000..6fa8a901f
--- /dev/null
+++ b/Library/Homebrew/cask/lib/hbc/system_command.rb
@@ -0,0 +1,173 @@
+require "open3"
+require "shellwords"
+
+class Hbc::SystemCommand
+ attr_reader :command
+
+ def self.run(executable, options = {})
+ new(executable, options).run!
+ end
+
+ def self.run!(command, options = {})
+ run(command, options.merge(must_succeed: true))
+ end
+
+ def run!
+ @processed_output = { stdout: "", stderr: "" }
+ odebug "Executing: #{expanded_command.utf8_inspect}"
+
+ each_output_line do |type, line|
+ case type
+ when :stdout
+ processed_output[:stdout] << line
+ ohai line.chomp if options[:print_stdout]
+ when :stderr
+ processed_output[:stderr] << line
+ ohai line.chomp if options[:print_stderr]
+ end
+ end
+
+ assert_success if options[:must_succeed]
+ result
+ end
+
+ def initialize(executable, options)
+ @executable = executable
+ @options = options
+ process_options!
+ end
+
+ private
+
+ attr_reader :executable, :options, :processed_output, :processed_status
+
+ def process_options!
+ options.assert_valid_keys :input, :print_stdout, :print_stderr, :args, :must_succeed, :sudo, :bsexec
+ sudo_prefix = %w[/usr/bin/sudo -E --]
+ bsexec_prefix = ["/bin/launchctl", "bsexec", options[:bsexec] == :startup ? "/" : options[:bsexec]]
+ @command = [executable]
+ options[:print_stderr] = true unless options.key?(:print_stderr)
+ @command.unshift(*bsexec_prefix) if options[:bsexec]
+ @command.unshift(*sudo_prefix) if options[:sudo]
+ @command.concat(options[:args]) if options.key?(:args) && !options[:args].empty?
+ @command[0] = Shellwords.shellescape(@command[0]) if @command.size == 1
+ nil
+ end
+
+ def assert_success
+ return if processed_status && processed_status.success?
+ raise Hbc::CaskCommandFailedError.new(command.utf8_inspect, processed_output[:stdout], processed_output[:stderr], processed_status)
+ end
+
+ def expanded_command
+ @expanded_command ||= command.map { |arg|
+ if arg.respond_to?(:to_path)
+ File.absolute_path(arg)
+ else
+ String(arg)
+ end
+ }
+ end
+
+ def each_output_line(&b)
+ raw_stdin, raw_stdout, raw_stderr, raw_wait_thr =
+ Open3.popen3(*expanded_command)
+
+ write_input_to(raw_stdin) if options[:input]
+ raw_stdin.close_write
+ each_line_from [raw_stdout, raw_stderr], &b
+
+ @processed_status = raw_wait_thr.value
+ end
+
+ def write_input_to(raw_stdin)
+ Array(options[:input]).each { |line| raw_stdin.puts line }
+ end
+
+ def each_line_from(sources)
+ loop do
+ readable_sources = IO.select(sources)[0]
+ readable_sources.delete_if(&:eof?).first(1).each do |source|
+ type = (source == sources[0] ? :stdout : :stderr)
+ begin
+ yield(type, source.readline_nonblock || "")
+ rescue IO::WaitReadable, EOFError
+ next
+ end
+ end
+ break if readable_sources.empty?
+ end
+ sources.each(&:close_read)
+ end
+
+ def result
+ Hbc::SystemCommand::Result.new(command,
+ processed_output[:stdout],
+ processed_output[:stderr],
+ processed_status.exitstatus)
+ end
+end
+
+class Hbc::SystemCommand::Result
+ attr_accessor :command, :stdout, :stderr, :exit_status
+
+ def initialize(command, stdout, stderr, exit_status)
+ @command = command
+ @stdout = stdout
+ @stderr = stderr
+ @exit_status = exit_status
+ end
+
+ def plist
+ @plist ||= self.class._parse_plist(@command, @stdout.dup)
+ end
+
+ def success?
+ @exit_status == 0
+ end
+
+ def merged_output
+ @merged_output ||= @stdout + @stderr
+ end
+
+ def to_s
+ @stdout
+ end
+
+ def self._warn_plist_garbage(command, garbage)
+ return true unless garbage =~ %r{\S}
+ external = File.basename(command.first)
+ lines = garbage.strip.split("\n")
+ opoo "Non-XML stdout from #{external}:"
+ $stderr.puts lines.map { |l| " #{l}" }
+ end
+
+ def self._parse_plist(command, output)
+ raise Hbc::CaskError, "Empty plist input" unless output =~ %r{\S}
+ output.sub!(%r{\A(.*?)(<\?\s*xml)}m, '\2')
+ _warn_plist_garbage(command, Regexp.last_match[1]) if Hbc.debug
+ output.sub!(%r{(<\s*/\s*plist\s*>)(.*?)\Z}m, '\1')
+ _warn_plist_garbage(command, Regexp.last_match[2])
+ xml = Plist.parse_xml(output)
+ unless xml.respond_to?(:keys) && !xml.keys.empty?
+ raise Hbc::CaskError, <<-ERRMSG
+Empty result parsing plist output from command.
+ command was:
+ #{command.utf8_inspect}
+ output we attempted to parse:
+ #{output}
+ ERRMSG
+ end
+ xml
+ rescue Plist::ParseError => e
+ raise Hbc::CaskError, <<-ERRMSG
+Error parsing plist output from command.
+ command was:
+ #{command.utf8_inspect}
+ error was:
+ #{e}
+ output we attempted to parse:
+ #{output}
+ ERRMSG
+ end
+end