motomike-bnr_tools 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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