PageRenderTime 51ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/textmate/vendor/redcar-bundles/Support/lib/tm/process.rb

https://github.com/streuth/redcar
Ruby | 179 lines | 80 code | 27 blank | 72 comment | 8 complexity | 6b888ec7bca6d2148f3eb932f540c15a MD5 | raw file
  1. # -----------------------
  2. # TextMate::Process.run()
  3. # -----------------------
  4. # Method for opening processes under TextMate.
  5. #
  6. # # BASIC USAGE
  7. #
  8. # 1. out, err = TextMate::Process.run("svn", "commit", "-m", "A commit message")
  9. #
  10. # 'out' and 'err' are the what the process produced on stdout and stderr respectively.
  11. #
  12. # 2. TextMate::Process.run("svn", "commit", "-m", "A commit message") do |str, type|
  13. # case type
  14. # when :out
  15. # STDOUT << str
  16. # when :err
  17. # STDERR << str
  18. # end
  19. # end
  20. #
  21. # The block will be called with the output of the process as it becomes available.
  22. #
  23. # 3. TextMate::Process.run("svn", "commit", "-m", "A commit message") do |str|
  24. # STDOUT << str
  25. # end
  26. #
  27. # Similar to 2, except that the type of the output is not passed.
  28. #
  29. # # OPTIONS
  30. #
  31. # The last (non block) argument to run() can be a Hash that will augment the behaviour.
  32. # The available options are (with default values in parenthesis)
  33. #
  34. # * :interactive_input (true)
  35. #
  36. # Inject the interactive input library into the program so that any requests for
  37. # input present the user with a dialog to enter it.
  38. #
  39. # * :echo (false)
  40. #
  41. # If using interactive input, echo the user's input onto the output
  42. # (has no effect if interactive input is off).
  43. #
  44. # * :granularity (:line)
  45. #
  46. # The size of the buffer to use to read the process output. The value :line
  47. # indicates that output will be passed a line at a time. Any other non integer
  48. # value will result in an unspecified buffer size being used.
  49. #
  50. # * :input (nil)
  51. #
  52. # A string to send to the stdin of the process.
  53. #
  54. # * :env (nil)
  55. #
  56. # A hash of environment variables to set for the process.
  57. #
  58. # NOTES
  59. #
  60. # The following is not valid Ruby
  61. #
  62. # args = ["commit", "-m", "commit message"]
  63. # TextMate::Process("svn", *args, :buffer => true)
  64. #
  65. # To get around this, arguments to run() are flattened. This allows the
  66. # almost as good version
  67. #
  68. # args = ["commit", "-m", "commit message"]
  69. # TextMate::Process("svn", args, :buffer => true)
  70. #
  71. require ENV['TM_SUPPORT_PATH'] + '/lib/io'
  72. require 'fcntl'
  73. module TextMate
  74. module Process
  75. class << self
  76. TM_INTERACTIVE_INPUT_DYLIB = ENV['TM_SUPPORT_PATH'] + '/lib/tm_interactive_input.dylib'
  77. def run(*cmd, &block)
  78. cmd.flatten!
  79. options = {
  80. :interactive_input => true,
  81. :echo => false,
  82. :granularity => :line,
  83. :input => nil,
  84. :env => nil,
  85. :watch_fds => { },
  86. }
  87. options.merge! cmd.pop if cmd.last.is_a? Hash
  88. io = []
  89. 3.times { io << ::IO::pipe }
  90. # F_SETOWN = 6, ideally this would be under Fcntl::F_SETOWN
  91. io[0][0].fcntl(6, ENV['TM_PID'].to_i) if ENV.has_key? 'TM_PID'
  92. pid = fork {
  93. at_exit { exit! }
  94. STDIN.reopen(io[0][0])
  95. STDOUT.reopen(io[1][1])
  96. STDERR.reopen(io[2][1])
  97. io.flatten.each { |fd| fd.close }
  98. options[:env].each { |k,v| ENV[k] = v } unless options[:env].nil?
  99. if options[:interactive_input] and File.exists? TM_INTERACTIVE_INPUT_DYLIB
  100. dil = ENV['DYLD_INSERT_LIBRARIES']
  101. if dil.nil? or dil.empty?
  102. ENV['DYLD_INSERT_LIBRARIES'] = TM_INTERACTIVE_INPUT_DYLIB
  103. elsif not dil.include? TM_INTERACTIVE_INPUT_DYLIB
  104. ENV['DYLD_INSERT_LIBRARIES'] = "#{TM_INTERACTIVE_INPUT_DYLIB}:#{dil}"
  105. end
  106. ENV['DYLD_FORCE_FLAT_NAMESPACE'] = "1"
  107. ENV['TM_INTERACTIVE_INPUT'] = "AUTO" + ((options[:echo]) ? "|ECHO" : "")
  108. end
  109. exec(*cmd.compact)
  110. }
  111. %w[INT TERM].each do |signal|
  112. trap(signal) do
  113. begin
  114. Process.kill("KILL", pid)
  115. sleep 0.5
  116. Process.kill("TERM", pid)
  117. rescue
  118. # process doesn't exist anymore
  119. end
  120. end
  121. end
  122. [ io[0][0], io[1][1], io[2][1] ].each { |fd| fd.close }
  123. if echo_fd = ENV['TM_INTERACTIVE_INPUT_ECHO_FD']
  124. ::IO.for_fd(echo_fd.to_i).close
  125. ENV.delete('TM_INTERACTIVE_INPUT_ECHO_FD')
  126. end
  127. if options[:input].nil?
  128. io[0][1].close
  129. else
  130. Thread.new { (io[0][1] << options[:input]).close }
  131. end
  132. out = ""
  133. err = ""
  134. block ||= proc { |str, fd|
  135. case fd
  136. when :out then out << str
  137. when :err then err << str
  138. end
  139. }
  140. previous_block_size = IO.blocksize
  141. IO.blocksize = options[:granularity] if options[:granularity].is_a? Integer
  142. previous_sync = IO.sync
  143. IO.sync = true unless options[:granularity] == :line
  144. IO.exhaust(options[:watch_fds].merge(:out => io[1][0], :err => io[2][0]), &block)
  145. ::Process.waitpid(pid)
  146. IO.blocksize = previous_block_size
  147. IO.sync = previous_sync
  148. block_given? ? nil : [out,err]
  149. end
  150. end
  151. end
  152. end