motomike-bnr_tools 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,125 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # system_extensions.rb
4
+ #
5
+ # Created by James Edward Gray II on 2006-06-14.
6
+ # Copyright 2006 Gray Productions. All rights reserved.
7
+ #
8
+ # This is Free Software. See LICENSE and COPYING for details.
9
+
10
+ class HighLine
11
+ module SystemExtensions
12
+ module_function
13
+
14
+ #
15
+ # This section builds character reading and terminal size functions
16
+ # to suit the proper platform we're running on. Be warned: Here be
17
+ # dragons!
18
+ #
19
+ begin
20
+ # Cygwin will look like Windows, but we want to treat it like a Posix OS:
21
+ raise LoadError, "Cygwin is a Posix OS." if RUBY_PLATFORM =~ /\bcygwin\b/i
22
+
23
+ require "Win32API" # See if we're on Windows.
24
+
25
+ CHARACTER_MODE = "Win32API" # For Debugging purposes only.
26
+
27
+ #
28
+ # Windows savvy getc().
29
+ #
30
+ # *WARNING*: This method ignores <tt>input</tt> and reads one
31
+ # character from +STDIN+!
32
+ #
33
+ def get_character( input = STDIN )
34
+ Win32API.new("crtdll", "_getch", [ ], "L").Call
35
+ end
36
+
37
+ # A Windows savvy method to fetch the console columns, and rows.
38
+ def terminal_size
39
+ m_GetStdHandle = Win32API.new( 'kernel32',
40
+ 'GetStdHandle',
41
+ ['L'],
42
+ 'L' )
43
+ m_GetConsoleScreenBufferInfo = Win32API.new(
44
+ 'kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L'
45
+ )
46
+
47
+ format = 'SSSSSssssSS'
48
+ buf = ([0] * format.size).pack(format)
49
+ stdout_handle = m_GetStdHandle.call(0xFFFFFFF5)
50
+
51
+ m_GetConsoleScreenBufferInfo.call(stdout_handle, buf)
52
+ bufx, bufy, curx, cury, wattr,
53
+ left, top, right, bottom, maxx, maxy = buf.unpack(format)
54
+ return right - left + 1, bottom - top + 1
55
+ end
56
+ rescue LoadError # If we're not on Windows try...
57
+ begin
58
+ require "termios" # Unix, first choice.
59
+
60
+ CHARACTER_MODE = "termios" # For Debugging purposes only.
61
+
62
+ #
63
+ # Unix savvy getc(). (First choice.)
64
+ #
65
+ # *WARNING*: This method requires the "termios" library!
66
+ #
67
+ def get_character( input = STDIN )
68
+ old_settings = Termios.getattr(input)
69
+
70
+ new_settings = old_settings.dup
71
+ new_settings.c_lflag &= ~(Termios::ECHO | Termios::ICANON)
72
+ new_settings.c_cc[Termios::VMIN] = 1
73
+
74
+ begin
75
+ Termios.setattr(input, Termios::TCSANOW, new_settings)
76
+ input.getc
77
+ ensure
78
+ Termios.setattr(input, Termios::TCSANOW, old_settings)
79
+ end
80
+ end
81
+ rescue LoadError # If our first choice fails, default.
82
+ CHARACTER_MODE = "stty" # For Debugging purposes only.
83
+
84
+ #
85
+ # Unix savvy getc(). (Second choice.)
86
+ #
87
+ # *WARNING*: This method requires the external "stty" program!
88
+ #
89
+ def get_character( input = STDIN )
90
+ raw_no_echo_mode
91
+
92
+ begin
93
+ input.getc
94
+ ensure
95
+ restore_mode
96
+ end
97
+ end
98
+
99
+ #
100
+ # Switched the input mode to raw and disables echo.
101
+ #
102
+ # *WARNING*: This method requires the external "stty" program!
103
+ #
104
+ def raw_no_echo_mode
105
+ @state = `stty -g`
106
+ system "stty raw -echo cbreak isig"
107
+ end
108
+
109
+ #
110
+ # Restores a previously saved input mode.
111
+ #
112
+ # *WARNING*: This method requires the external "stty" program!
113
+ #
114
+ def restore_mode
115
+ system "stty #{@state}"
116
+ end
117
+ end
118
+
119
+ # A Unix savvy method to fetch the console columns, and rows.
120
+ def terminal_size
121
+ `stty size`.split.map { |x| x.to_i }.reverse
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,393 @@
1
+ # vim: ts=2:sw=2:sts=2:et:fdm=marker
2
+ require 'fcntl'
3
+ require 'timeout'
4
+ require 'thread'
5
+
6
+ module Open4
7
+ #--{{{
8
+ VERSION = '0.9.6'
9
+ def self.version() VERSION end
10
+
11
+ class Error < ::StandardError; end
12
+
13
+ def popen4(*cmd, &b)
14
+ #--{{{
15
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
16
+
17
+ verbose = $VERBOSE
18
+ begin
19
+ $VERBOSE = nil
20
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
21
+
22
+ cid = fork {
23
+ pw.last.close
24
+ STDIN.reopen pw.first
25
+ pw.first.close
26
+
27
+ pr.first.close
28
+ STDOUT.reopen pr.last
29
+ pr.last.close
30
+
31
+ pe.first.close
32
+ STDERR.reopen pe.last
33
+ pe.last.close
34
+
35
+ STDOUT.sync = STDERR.sync = true
36
+
37
+ begin
38
+ exec(*cmd)
39
+ raise 'forty-two'
40
+ rescue Exception => e
41
+ Marshal.dump(e, ps.last)
42
+ ps.last.flush
43
+ end
44
+ ps.last.close unless (ps.last.closed?)
45
+ exit!
46
+ }
47
+ ensure
48
+ $VERBOSE = verbose
49
+ end
50
+
51
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
52
+
53
+ begin
54
+ e = Marshal.load ps.first
55
+ raise(Exception === e ? e : "unknown failure!")
56
+ rescue EOFError # If we get an EOF error, then the exec was successful
57
+ 42
58
+ ensure
59
+ ps.first.close
60
+ end
61
+
62
+ pw.last.sync = true
63
+
64
+ pi = [pw.last, pr.first, pe.first]
65
+
66
+ if b
67
+ begin
68
+ b[cid, *pi]
69
+ Process.waitpid2(cid).last
70
+ ensure
71
+ pi.each{|fd| fd.close unless fd.closed?}
72
+ end
73
+ else
74
+ [cid, pw.last, pr.first, pe.first]
75
+ end
76
+ #--}}}
77
+ end
78
+ alias open4 popen4
79
+ module_function :popen4
80
+ module_function :open4
81
+
82
+ class SpawnError < Error
83
+ #--{{{
84
+ attr 'cmd'
85
+ attr 'status'
86
+ attr 'signals'
87
+ def exitstatus
88
+ @status.exitstatus
89
+ end
90
+ def initialize cmd, status
91
+ @cmd, @status = cmd, status
92
+ @signals = {}
93
+ if status.signaled?
94
+ @signals['termsig'] = status.termsig
95
+ @signals['stopsig'] = status.stopsig
96
+ end
97
+ sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ')
98
+ super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>"
99
+ end
100
+ #--}}}
101
+ end
102
+
103
+ class ThreadEnsemble
104
+ #--{{{
105
+ attr 'threads'
106
+
107
+ def initialize cid
108
+ @cid, @threads, @argv, @done, @running = cid, [], [], Queue.new, false
109
+ @killed = false
110
+ end
111
+
112
+ def add_thread *a, &b
113
+ @running ? raise : (@argv << [a, b])
114
+ end
115
+
116
+ #
117
+ # take down process more nicely
118
+ #
119
+ def killall
120
+ c = Thread.critical
121
+ return nil if @killed
122
+ Thread.critical = true
123
+ (@threads - [Thread.current]).each{|t| t.kill rescue nil}
124
+ @killed = true
125
+ ensure
126
+ Thread.critical = c
127
+ end
128
+
129
+ def run
130
+ @running = true
131
+
132
+ begin
133
+ @argv.each do |a, b|
134
+ @threads << Thread.new(*a) do |*a|
135
+ begin
136
+ b[*a]
137
+ ensure
138
+ killall rescue nil if $!
139
+ @done.push Thread.current
140
+ end
141
+ end
142
+ end
143
+ rescue
144
+ killall
145
+ raise
146
+ ensure
147
+ all_done
148
+ end
149
+
150
+ @threads.map{|t| t.value}
151
+ end
152
+
153
+ def all_done
154
+ @threads.size.times{ @done.pop }
155
+ end
156
+ #--}}}
157
+ end
158
+
159
+ def to timeout = nil
160
+ #--{{{
161
+ Timeout.timeout(timeout){ yield }
162
+ #--}}}
163
+ end
164
+ module_function :to
165
+
166
+ def new_thread *a, &b
167
+ #--{{{
168
+ cur = Thread.current
169
+ Thread.new(*a) do |*a|
170
+ begin
171
+ b[*a]
172
+ rescue Exception => e
173
+ cur.raise e
174
+ end
175
+ end
176
+ #--}}}
177
+ end
178
+ module_function :new_thread
179
+
180
+ def getopts opts = {}
181
+ #--{{{
182
+ lambda do |*args|
183
+ keys, default, ignored = args
184
+ catch('opt') do
185
+ [keys].flatten.each do |key|
186
+ [key, key.to_s, key.to_s.intern].each do |key|
187
+ throw 'opt', opts[key] if opts.has_key?(key)
188
+ end
189
+ end
190
+ default
191
+ end
192
+ end
193
+ #--}}}
194
+ end
195
+ module_function :getopts
196
+
197
+ def relay src, dst = nil, t = nil
198
+ #--{{{
199
+ unless src.nil?
200
+ if src.respond_to? :gets
201
+ while buf = to(t){ src.gets }
202
+ dst << buf if dst
203
+ end
204
+
205
+ elsif src.respond_to? :each
206
+ q = Queue.new
207
+ th = nil
208
+
209
+ timer_set = lambda do |t|
210
+ th = new_thread{ to(t){ q.pop } }
211
+ end
212
+
213
+ timer_cancel = lambda do |t|
214
+ th.kill if th rescue nil
215
+ end
216
+
217
+ timer_set[t]
218
+ begin
219
+ src.each do |buf|
220
+ timer_cancel[t]
221
+ dst << buf if dst
222
+ timer_set[t]
223
+ end
224
+ ensure
225
+ timer_cancel[t]
226
+ end
227
+
228
+ elsif src.respond_to? :read
229
+ buf = to(t){ src.read }
230
+ dst << buf if dst
231
+
232
+ else
233
+ buf = to(t){ src.to_s }
234
+ dst << buf if dst
235
+ end
236
+ end
237
+ #--}}}
238
+ end
239
+ module_function :relay
240
+
241
+ def spawn arg, *argv
242
+ #--{{{
243
+ argv.unshift(arg)
244
+ opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
245
+ argv.flatten!
246
+ cmd = argv.join(' ')
247
+
248
+
249
+ getopt = getopts opts
250
+
251
+ ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
252
+ ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
253
+ exitstatus = getopt[ %w( exitstatus exit_status status ) ]
254
+ stdin = getopt[ %w( stdin in i 0 ) << 0 ]
255
+ stdout = getopt[ %w( stdout out o 1 ) << 1 ]
256
+ stderr = getopt[ %w( stderr err e 2 ) << 2 ]
257
+ pid = getopt[ 'pid' ]
258
+ timeout = getopt[ %w( timeout spawn_timeout ) ]
259
+ stdin_timeout = getopt[ %w( stdin_timeout ) ]
260
+ stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ]
261
+ stderr_timeout = getopt[ %w( stderr_timeout ) ]
262
+ status = getopt[ %w( status ) ]
263
+ cwd = getopt[ %w( cwd dir ) ]
264
+
265
+ exitstatus =
266
+ case exitstatus
267
+ when TrueClass, FalseClass
268
+ ignore_exit_failure = true if exitstatus
269
+ [0]
270
+ else
271
+ [*(exitstatus || 0)].map{|i| Integer i}
272
+ end
273
+
274
+ stdin ||= '' if stdin_timeout
275
+ stdout ||= '' if stdout_timeout
276
+ stderr ||= '' if stderr_timeout
277
+
278
+ started = false
279
+
280
+ status =
281
+ begin
282
+ chdir(cwd) do
283
+ Timeout::timeout(timeout) do
284
+ popen4(*argv) do |c, i, o, e|
285
+ started = true
286
+
287
+ %w( replace pid= << push update ).each do |msg|
288
+ break(pid.send(msg, c)) if pid.respond_to? msg
289
+ end
290
+
291
+ te = ThreadEnsemble.new c
292
+
293
+ te.add_thread(i, stdin) do |i, stdin|
294
+ relay stdin, i, stdin_timeout
295
+ i.close rescue nil
296
+ end
297
+
298
+ te.add_thread(o, stdout) do |o, stdout|
299
+ relay o, stdout, stdout_timeout
300
+ end
301
+
302
+ te.add_thread(e, stderr) do |o, stderr|
303
+ relay e, stderr, stderr_timeout
304
+ end
305
+
306
+ te.run
307
+ end
308
+ end
309
+ end
310
+ rescue
311
+ raise unless(not started and ignore_exec_failure)
312
+ end
313
+
314
+ raise SpawnError.new(cmd, status) unless
315
+ (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
316
+
317
+ status
318
+ #--}}}
319
+ end
320
+ module_function :spawn
321
+
322
+ def chdir cwd, &block
323
+ return(block.call Dir.pwd) unless cwd
324
+ Dir.chdir cwd, &block
325
+ end
326
+ module_function :chdir
327
+
328
+ def background arg, *argv
329
+ #--{{{
330
+ require 'thread'
331
+ q = Queue.new
332
+ opts = { 'pid' => q, :pid => q }
333
+ case argv.last
334
+ when Hash
335
+ argv.last.update opts
336
+ else
337
+ argv.push opts
338
+ end
339
+ thread = Thread.new(arg, argv){|arg, argv| spawn arg, *argv}
340
+ sc = class << thread; self; end
341
+ sc.module_eval {
342
+ define_method(:pid){ @pid ||= q.pop }
343
+ define_method(:spawn_status){ @spawn_status ||= value }
344
+ define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
345
+ }
346
+ thread
347
+ #--}}}
348
+ end
349
+ alias bg background
350
+ module_function :background
351
+ module_function :bg
352
+
353
+ def maim pid, opts = {}
354
+ #--{{{
355
+ getopt = getopts opts
356
+ sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
357
+ suspend = getopt[ 'suspend', 4 ]
358
+ pid = Integer pid
359
+ existed = false
360
+ sigs.each do |sig|
361
+ begin
362
+ Process.kill sig, pid
363
+ existed = true
364
+ rescue Errno::ESRCH
365
+ return(existed ? nil : true)
366
+ end
367
+ return true unless alive? pid
368
+ sleep suspend
369
+ return true unless alive? pid
370
+ end
371
+ return(not alive?(pid))
372
+ #--}}}
373
+ end
374
+ module_function :maim
375
+
376
+ def alive pid
377
+ #--{{{
378
+ pid = Integer pid
379
+ begin
380
+ Process.kill 0, pid
381
+ true
382
+ rescue Errno::ESRCH
383
+ false
384
+ end
385
+ #--}}}
386
+ end
387
+ alias alive? alive
388
+ module_function :alive
389
+ module_function :'alive?'
390
+ #--}}}
391
+ end
392
+
393
+ def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end