open3_backport 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.8.7@open3_backport --create
data/BSDL ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (C) 1993-2010 Yukihiro Matsumoto. All rights reserved.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions
5
+ are met:
6
+ 1. Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ 2. Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+
12
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22
+ SUCH DAMAGE.
23
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in open3_backport.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,13 @@
1
+ # See https://github.com/guard/guard#readme
2
+
3
+ guard 'bundler' do
4
+ watch('Gemfile')
5
+ watch(%r{.+\.gemspec})
6
+ end
7
+
8
+ guard 'rspec', :cli => '-c --format documentation -r ./spec/spec_helper.rb' do
9
+ watch(%r{^spec/.+_spec\.rb})
10
+ watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
11
+ watch('spec/spec_helper.rb') { "spec/" }
12
+ watch('lib/open3_backport.rb') { "spec/" }
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,65 @@
1
+ The important bits of open3_backport are pulled directly from Ruby source,
2
+ available at http://www.ruby-lang.org/. Copyrights of all other code in
3
+ the gem are assigned to the copyright owner of Ruby (Yukihiro Matsumoto).
4
+
5
+ This gem is available under the same license(s) as Ruby itself, reproduced
6
+ here for your convenience:
7
+
8
+ ======
9
+
10
+ Ruby is copyrighted free software by Yukihiro Matsumoto <matz@netlab.jp>.
11
+ You can redistribute it and/or modify it under either the terms of the
12
+ 2-clause BSDL (see the file BSDL), or the conditions below:
13
+
14
+ 1. You may make and give away verbatim copies of the source form of the
15
+ software without restriction, provided that you duplicate all of the
16
+ original copyright notices and associated disclaimers.
17
+
18
+ 2. You may modify your copy of the software in any way, provided that
19
+ you do at least ONE of the following:
20
+
21
+ a) place your modifications in the Public Domain or otherwise
22
+ make them Freely Available, such as by posting said
23
+ modifications to Usenet or an equivalent medium, or by allowing
24
+ the author to include your modifications in the software.
25
+
26
+ b) use the modified software only within your corporation or
27
+ organization.
28
+
29
+ c) give non-standard binaries non-standard names, with
30
+ instructions on where to get the original software distribution.
31
+
32
+ d) make other distribution arrangements with the author.
33
+
34
+ 3. You may distribute the software in object code or binary form,
35
+ provided that you do at least ONE of the following:
36
+
37
+ a) distribute the binaries and library files of the software,
38
+ together with instructions (in the manual page or equivalent)
39
+ on where to get the original distribution.
40
+
41
+ b) accompany the distribution with the machine-readable source of
42
+ the software.
43
+
44
+ c) give non-standard binaries non-standard names, with
45
+ instructions on where to get the original software distribution.
46
+
47
+ d) make other distribution arrangements with the author.
48
+
49
+ 4. You may modify and include the part of the software into any other
50
+ software (possibly commercial). But some files in the distribution
51
+ are not written by the author, so that they are not under these terms.
52
+
53
+ For the list of those files and their copying conditions, see the
54
+ file LEGAL.
55
+
56
+ 5. The scripts and library files supplied as input to or produced as
57
+ output from the software do not automatically fall under the
58
+ copyright of the software, but belong to whomever generated them,
59
+ and may be sold commercially, and may be aggregated with this
60
+ software.
61
+
62
+ 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
63
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
64
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
65
+ PURPOSE.
data/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # Open3Backport
2
+
3
+ Backport of Ruby 1.9's Open3 methods, for use in Ruby 1.8.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'open3_backport'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install open3_backport
18
+
19
+ ## Usage
20
+
21
+ For the most part, you can just use Open3 methods the same way you would in
22
+ Ruby 1.9. However, there is currently no support for setting environment nor
23
+ passing any of the special options that Process.spawn supports in Ruby 1.9.
24
+
25
+ Here are some examples that should work fine...
26
+
27
+ # Block form:
28
+ Open3.popen3("echo", "a") do |stdin, stdout, stderr, wait_thr|
29
+ pid = wait_thr.pid # pid of the started process.
30
+ exit_status = wait_thr.value # Process::Status object returned.
31
+ end
32
+
33
+ # Non-block form:
34
+ stdin, stdout, stderr, wait_thr = Open3.popen3("echo", "a")
35
+ pid = wait_thr[:pid] # pid of the started process.
36
+ stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
37
+ stdout.close
38
+ stderr.close
39
+ exit_status = wait_thr.value # Process::Status object returned.
40
+
41
+ Open3.popen3("echo a") {|i, o, e, t| ... }
42
+
43
+ Open3.popen3("echo", "a") {|i, o, e, t| ... }
44
+
45
+ Open3.popen2("wc -c") do |i, o, t|
46
+ i.print "answer to life the universe and everything"
47
+ i.close
48
+ p o.gets #=> "42\n"
49
+ end
50
+
51
+ Open3.popen2("bc -q") do |i, o, t|
52
+ i.puts "obase=13"
53
+ i.puts "6 * 9"
54
+ p o.gets #=> "42\n"
55
+ end
56
+
57
+ Open3.popen2("dc") do |i, o, t|
58
+ i.print "42P"
59
+ i.close
60
+ p o.read #=> "*"
61
+ end
62
+
63
+ # dot is a command of graphviz.
64
+ graph = <<'End'
65
+ digraph g {
66
+ a -> b
67
+ }
68
+ End
69
+ layouted_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
70
+
71
+ o, e, s = Open3.capture3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
72
+ p o #=> "a\n"
73
+ p e #=> "bar\nbaz\nfoo\n"
74
+ p s #=> #<Process::Status: pid 32682 exit 0>
75
+
76
+ image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
77
+ thumnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
78
+ if s.success?
79
+ STDOUT.binmode
80
+ print thumnail
81
+ end
82
+
83
+ # factor is a command for integer factorization.
84
+ o, s = Open3.capture2("factor", :stdin_data=>"42")
85
+ p o #=> "42: 2 3 7\n"
86
+
87
+ # generate x**2 graph in png using gnuplot.
88
+ gnuplot_commands = <<"End"
89
+ set terminal png
90
+ plot x**2, "-" with lines
91
+ 1 14
92
+ 2 1
93
+ 3 8
94
+ 4 5
95
+ e
96
+ End
97
+ image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
98
+
99
+ # capture make log
100
+ make_log, s = Open3.capture2e("make")
101
+
102
+
103
+ The following examples do not work yet. Pull requests are welcome!
104
+
105
+ source = "foo.c"
106
+ Open3.popen2e("gcc", "-Wall", source) do |i, oe, t|
107
+ oe.each do |line|
108
+ if /warning/ =~ line
109
+ # ...
110
+ end
111
+ end
112
+ end
113
+
114
+ Open3.pipeline_rw(["tr", "-dc", "A-Za-z"], ["wc", "-c"]) do |i, o, ts|
115
+ i.puts "All persons more than a mile high to leave the court."
116
+ i.close
117
+ p o.gets #=> "42\n"
118
+ end
119
+
120
+ Open3.pipeline_rw("sort", "cat -n") do |stdin, stdout, wait_thrs|
121
+ stdin.puts "foo"
122
+ stdin.puts "bar"
123
+ stdin.puts "baz"
124
+ stdin.close # send EOF to sort.
125
+ p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
126
+ end
127
+
128
+ Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
129
+ [{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
130
+ "logresolve") {|o, ts|
131
+ o.each_line {|line|
132
+ # ...
133
+ }
134
+ }
135
+
136
+ Open3.pipeline_r("yes", "head -10") {|o, ts|
137
+ p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
138
+ p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
139
+ p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
140
+ }
141
+
142
+ Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts|
143
+ i.puts "hello"
144
+ }
145
+
146
+ # run xeyes in 10 seconds.
147
+ Open3.pipeline_start("xeyes") {|ts|
148
+ sleep 10
149
+ t = ts[0]
150
+ Process.kill("TERM", t.pid)
151
+ p t.value #=> #<Process::Status: pid 911 SIGTERM (signal 15)>
152
+ }
153
+
154
+ # convert pdf to ps and send it to a printer.
155
+ # collect error message of pdftops and lpr.
156
+ pdf_file = "paper.pdf"
157
+ printer = "printer-name"
158
+ err_r, err_w = IO.pipe
159
+ Open3.pipeline_start(["pdftops", pdf_file, "-"],
160
+ ["lpr", "-P#{printer}"],
161
+ :err=>err_w) {|ts|
162
+ err_w.close
163
+ p err_r.read # error messages of pdftops and lpr.
164
+ }
165
+
166
+ fname = "/usr/share/man/man1/ruby.1.gz"
167
+ p Open3.pipeline(["zcat", fname], "nroff -man", "less")
168
+ #=> [#<Process::Status: pid 11817 exit 0>,
169
+ # #<Process::Status: pid 11820 exit 0>,
170
+ # #<Process::Status: pid 11828 exit 0>]
171
+
172
+ fname = "/usr/share/man/man1/ls.1.gz"
173
+ Open3.pipeline(["zcat", fname], "nroff -man", "colcrt")
174
+
175
+ # convert PDF to PS and send to a printer by lpr
176
+ pdf_file = "paper.pdf"
177
+ printer = "printer-name"
178
+ Open3.pipeline(["pdftops", pdf_file, "-"],
179
+ ["lpr", "-P#{printer}"])
180
+
181
+ # count lines
182
+ Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
183
+
184
+ # cyclic pipeline
185
+ r,w = IO.pipe
186
+ w.print "ibase=14\n10\n"
187
+ Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w)
188
+ #=> 14
189
+ # 18
190
+ # 22
191
+ # 30
192
+ # 42
193
+ # 58
194
+ # 78
195
+ # 106
196
+ # 202
197
+
198
+ ## Version History
199
+
200
+ 0.0.1 - Initial release. No support for setting environment nor
201
+ passing any of the special options that Process.spawn supports in Ruby 1.9.
202
+ These methods are not yet implemented: popen2e, pipeline, pipeline_start,
203
+ pipeline_r, pipeline_w, pipeline_rw.
204
+
205
+ ## Credits
206
+
207
+ This gem was written by Chris Johnson for Crossroads Systems, Inc. The code
208
+ and documentation was copied from the Ruby 1.9.3 stdlib, and then key method
209
+ implementations were re-written and tested to work in Ruby 1.8.7, while aiming
210
+ to maintain maximum compatibility with the Ruby 1.9.3 method interfaces. The
211
+ replacement implementation makes heavy use of the open4 gem.
212
+
213
+ ## License
214
+
215
+ The important bits of open3_backport are pulled directly from Ruby source,
216
+ available at http://www.ruby-lang.org/. Copyrights of all other code in
217
+ the gem are assigned to the copyright owner of Ruby (Yukihiro Matsumoto).
218
+
219
+ This gem is available under the same license(s) as Ruby itself (See LICENSE
220
+ file).
221
+
222
+ ## Contributing
223
+
224
+ 1. Fork it
225
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
226
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
227
+ 4. Push to the branch (`git push origin my-new-feature`)
228
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ Dir['tasks/*.rake'].each { |task| load task }
@@ -0,0 +1,6 @@
1
+ if RUBY_VERSION < "1.9"
2
+ require 'rubygems' rescue nil
3
+ require 'open4'
4
+ require "open3_backport/version"
5
+ require "open3_backport/open3"
6
+ end
@@ -0,0 +1,676 @@
1
+ #
2
+ # = open3.rb: Popen, but with stderr, too
3
+ #
4
+ # Author:: Yukihiro Matsumoto, backport by Chris Johnson
5
+ # Documentation:: Konrad Meyer, backport by Chris Johnson
6
+ #
7
+ # Open3 gives you access to stdin, stdout, and stderr when running other
8
+ # programs.
9
+ #
10
+
11
+ #
12
+ # Open3 grants you access to stdin, stdout, stderr and a thread to wait the
13
+ # child process when running another program.
14
+ #
15
+ # - Open3.popen3 : pipes for stdin, stdout, stderr
16
+ # - Open3.popen2 : pipes for stdin, stdout
17
+ # - Open3.popen2e : pipes for stdin, merged stdout and stderr
18
+ # - Open3.capture3 : give a string for stdin. get strings for stdout, stderr
19
+ # - Open3.capture2 : give a string for stdin. get a string for stdout
20
+ # - Open3.capture2e : give a string for stdin. get a string for merged stdout and stderr
21
+ # - Open3.pipeline_rw : pipes for first stdin and last stdout of a pipeline
22
+ # - Open3.pipeline_r : pipe for last stdout of a pipeline
23
+ # - Open3.pipeline_w : pipe for first stdin of a pipeline
24
+ # - Open3.pipeline_start : a pipeline
25
+ # - Open3.pipeline : run a pipline and wait
26
+ #
27
+
28
+ module Open3
29
+
30
+ def detach(pid) # :nodoc:
31
+ thread = Process.detach(pid)
32
+ thread[:pid] = pid
33
+ def thread.pid
34
+ self[:pid]
35
+ end
36
+ thread
37
+ end
38
+ module_function :detach
39
+ class << self
40
+ private :detach
41
+ end
42
+
43
+ #
44
+ # Open stdin, stdout, and stderr streams and start external executable.
45
+ # In addition, a thread for waiting the started process is noticed.
46
+ # The thread has a pid method and thread variable :pid which is the pid of
47
+ # the started process.
48
+ #
49
+ # Block form:
50
+ #
51
+ # Open3.popen3(cmd...) {|stdin, stdout, stderr, wait_thr|
52
+ # pid = wait_thr.pid # pid of the started process.
53
+ # ...
54
+ # exit_status = wait_thr.value # Process::Status object returned.
55
+ # }
56
+ #
57
+ # Non-block form:
58
+ #
59
+ # stdin, stdout, stderr, wait_thr = Open3.popen3(cmd...)
60
+ # pid = wait_thr[:pid] # pid of the started process.
61
+ # ...
62
+ # stdin.close # stdin, stdout and stderr should be closed explicitly in this form.
63
+ # stdout.close
64
+ # stderr.close
65
+ # exit_status = wait_thr.value # Process::Status object returned.
66
+ #
67
+ # So a commandline string and list of argument strings can be accepted as follows.
68
+ #
69
+ # Open3.popen3("echo", "a") {|i, o, e, t| ... }
70
+ #
71
+ # wait_thr.value waits the termination of the process.
72
+ # The block form also waits the process when it returns.
73
+ #
74
+ # Closing stdin, stdout and stderr does not wait the process.
75
+ #
76
+ def popen3(*cmd)
77
+ if block_given?
78
+ begin
79
+ pid, stdin, stdout, stderr = Open4::popen4(*cmd)
80
+ wait_thr = detach(pid)
81
+ stdin.sync = true
82
+ return yield(stdin, stdout, stderr, wait_thr)
83
+ ensure
84
+ stdin.close unless stdin.nil? || stdin.closed?
85
+ stdout.close unless stdout.nil? || stdout.closed?
86
+ stderr.close unless stderr.nil? || stderr.closed?
87
+ wait_thr.value unless wait_thr.nil?
88
+ end
89
+ else
90
+ pid, stdin, stdout, stderr = Open4::popen4(*cmd)
91
+ stdin.sync = true
92
+ return [stdin, stdout, stderr, detach(pid)]
93
+ end
94
+ end
95
+ module_function :popen3
96
+
97
+ # Open3.popen2 is similer to Open3.popen3 except it doesn't make a pipe for
98
+ # the standard error stream.
99
+ #
100
+ # Block form:
101
+ #
102
+ # Open3.popen2(cmd...) {|stdin, stdout, wait_thr|
103
+ # pid = wait_thr.pid # pid of the started process.
104
+ # ...
105
+ # exit_status = wait_thr.value # Process::Status object returned.
106
+ # }
107
+ #
108
+ # Non-block form:
109
+ #
110
+ # stdin, stdout, wait_thr = Open3.popen2(cmd...)
111
+ # ...
112
+ # stdin.close # stdin and stdout should be closed explicitly in this form.
113
+ # stdout.close
114
+ #
115
+ # Example:
116
+ #
117
+ # Open3.popen2("wc -c") {|i,o,t|
118
+ # i.print "answer to life the universe and everything"
119
+ # i.close
120
+ # p o.gets #=> "42\n"
121
+ # }
122
+ #
123
+ # Open3.popen2("bc -q") {|i,o,t|
124
+ # i.puts "obase=13"
125
+ # i.puts "6 * 9"
126
+ # p o.gets #=> "42\n"
127
+ # }
128
+ #
129
+ # Open3.popen2("dc") {|i,o,t|
130
+ # i.print "42P"
131
+ # i.close
132
+ # p o.read #=> "*"
133
+ # }
134
+ #
135
+ def popen2(*cmd)
136
+ if block_given?
137
+ popen3(*cmd) do |i, o, e, t|
138
+ e.close
139
+ yield(i, o, t)
140
+ end
141
+ else
142
+ i, o, e, t = popen3(*cmd)
143
+ e.close
144
+ return [i, o, t]
145
+ end
146
+ end
147
+ module_function :popen2
148
+
149
+ # Open3.popen2e is similer to Open3.popen3 except it merges
150
+ # the standard output stream and the standard error stream.
151
+ #
152
+ # Block form:
153
+ #
154
+ # Open3.popen2e(cmd...) {|stdin, stdout_and_stderr, wait_thr|
155
+ # pid = wait_thr.pid # pid of the started process.
156
+ # ...
157
+ # exit_status = wait_thr.value # Process::Status object returned.
158
+ # }
159
+ #
160
+ # Non-block form:
161
+ #
162
+ # stdin, stdout_and_stderr, wait_thr = Open3.popen2e(cmd...)
163
+ # ...
164
+ # stdin.close # stdin and stdout_and_stderr should be closed explicitly in this form.
165
+ # stdout_and_stderr.close
166
+ #
167
+ # Example:
168
+ # # check gcc warnings
169
+ # source = "foo.c"
170
+ # Open3.popen2e("gcc", "-Wall", source) {|i,oe,t|
171
+ # oe.each {|line|
172
+ # if /warning/ =~ line
173
+ # ...
174
+ # end
175
+ # }
176
+ # }
177
+ #
178
+ def popen2e(*cmd)
179
+ if block_given?
180
+ popen3(*cmd) do |i, o, e, t|
181
+ yield(i, merged_read_stream(o, e), t)
182
+ end
183
+ else
184
+ i, o, e, t = popen3(*cmd)
185
+ return [i, merged_read_stream(o, e), t]
186
+ end
187
+ end
188
+ module_function :popen2e
189
+
190
+ def merged_read_stream(*streams) # :nodoc:
191
+ raise NotImplementedError
192
+ end
193
+
194
+ # Open3.capture3 captures the standard output and the standard error of a command.
195
+ #
196
+ # stdout_str, stderr_str, status = Open3.capture3(cmd... [, opts])
197
+ #
198
+ # The cmd arguments are passed to Open3.popen3.
199
+ #
200
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
201
+ #
202
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
203
+ #
204
+ # Example:
205
+ #
206
+ # # dot is a command of graphviz.
207
+ # graph = <<'End'
208
+ # digraph g {
209
+ # a -> b
210
+ # }
211
+ # End
212
+ # layouted_graph, dot_log = Open3.capture3("dot -v", :stdin_data=>graph)
213
+ #
214
+ # o, e, s = Open3.capture3("echo a; sort >&2", :stdin_data=>"foo\nbar\nbaz\n")
215
+ # p o #=> "a\n"
216
+ # p e #=> "bar\nbaz\nfoo\n"
217
+ # p s #=> #<Process::Status: pid 32682 exit 0>
218
+ #
219
+ # # generate a thumnail image using the convert command of ImageMagick.
220
+ # # However, if the image stored really in a file,
221
+ # # system("convert", "-thumbnail", "80", "png:#{filename}", "png:-") is better
222
+ # # because memory consumption.
223
+ # # But if the image is stored in a DB or generated by gnuplot Open3.capture2 example,
224
+ # # Open3.capture3 is considerable.
225
+ # #
226
+ # image = File.read("/usr/share/openclipart/png/animals/mammals/sheep-md-v0.1.png", :binmode=>true)
227
+ # thumnail, err, s = Open3.capture3("convert -thumbnail 80 png:- png:-", :stdin_data=>image, :binmode=>true)
228
+ # if s.success?
229
+ # STDOUT.binmode; print thumnail
230
+ # end
231
+ #
232
+ def capture3(*cmd)
233
+ if Hash === cmd.last
234
+ opts = cmd.pop.dup
235
+ else
236
+ opts = {}
237
+ end
238
+
239
+ binmode = opts[:binmode]
240
+ i_data = (opts[:stdin_data] || '').to_s
241
+ o_data = ''
242
+ e_data = ''
243
+
244
+ popen3(*cmd) do |i, o, e, t|
245
+ if binmode
246
+ i.binmode
247
+ o.binmode
248
+ e.binmode
249
+ end
250
+
251
+ i_complete = i_data.empty?
252
+ o_complete = false
253
+ e_complete = false
254
+
255
+ until i_complete && o_complete && e_complete
256
+ i_blocked = false
257
+ o_blocked = false
258
+ e_blocked = false
259
+
260
+ unless i_complete
261
+ begin
262
+ bytes_written = i.write_nonblock(i_data)
263
+ if bytes_written == i_data.length
264
+ i.close
265
+ i_complete = true
266
+ else
267
+ i_data = i_data[bytes_written .. -1]
268
+ end
269
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
270
+ i_blocked = true
271
+ end
272
+ end
273
+
274
+ unless o_complete
275
+ begin
276
+ o_data << o.read_nonblock(CAPTURE_BUFFER_SIZE)
277
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
278
+ o_blocked = true
279
+ rescue EOFError
280
+ raise unless i_complete
281
+ o.close
282
+ o_complete = true
283
+ end
284
+ end
285
+
286
+ unless e_complete
287
+ begin
288
+ e_data << e.read_nonblock(CAPTURE_BUFFER_SIZE)
289
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
290
+ e_blocked = true
291
+ rescue EOFError
292
+ raise unless i_complete
293
+ e.close
294
+ e_complete = true
295
+ end
296
+ end
297
+
298
+ if i_blocked && o_blocked && e_blocked
299
+ IO.select([o, e], [i], [o,e,i])
300
+ end
301
+ end
302
+ return [o_data, e_data, t.value]
303
+ end
304
+ end
305
+ module_function :capture3
306
+
307
+ # Open3.capture2 captures the standard output of a command.
308
+ #
309
+ # stdout_str, status = Open3.capture2(cmd... [, opts])
310
+ #
311
+ # The cmd arguments are passed to Open3.popen3.
312
+ #
313
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
314
+ #
315
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
316
+ #
317
+ # Example:
318
+ #
319
+ # # factor is a command for integer factorization.
320
+ # o, s = Open3.capture2("factor", :stdin_data=>"42")
321
+ # p o #=> "42: 2 3 7\n"
322
+ #
323
+ # # generate x**2 graph in png using gnuplot.
324
+ # gnuplot_commands = <<"End"
325
+ # set terminal png
326
+ # plot x**2, "-" with lines
327
+ # 1 14
328
+ # 2 1
329
+ # 3 8
330
+ # 4 5
331
+ # e
332
+ # End
333
+ # image, s = Open3.capture2("gnuplot", :stdin_data=>gnuplot_commands, :binmode=>true)
334
+ #
335
+ def capture2(*cmd)
336
+ if Hash === cmd.last
337
+ opts = cmd.pop.dup
338
+ else
339
+ opts = {}
340
+ end
341
+
342
+ binmode = opts[:binmode]
343
+ i_data = (opts[:stdin_data] || '').to_s
344
+ o_data = ''
345
+
346
+ popen3(*cmd) do |i, o, e, t|
347
+ e.close
348
+ if binmode
349
+ i.binmode
350
+ o.binmode
351
+ e.binmode
352
+ end
353
+
354
+ i_complete = i_data.empty?
355
+ o_complete = false
356
+
357
+ until i_complete && o_complete
358
+ i_blocked = false
359
+ o_blocked = false
360
+
361
+ unless i_complete
362
+ begin
363
+ bytes_written = i.write_nonblock(i_data)
364
+ if bytes_written == i_data.length
365
+ i.close
366
+ i_complete = true
367
+ else
368
+ i_data = i_data[bytes_written .. -1]
369
+ end
370
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
371
+ i_blocked = true
372
+ end
373
+ end
374
+
375
+ unless o_complete
376
+ begin
377
+ o_data << o.read_nonblock(CAPTURE_BUFFER_SIZE)
378
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
379
+ o_blocked = true
380
+ rescue EOFError
381
+ raise unless i_complete
382
+ o.close
383
+ o_complete = true
384
+ end
385
+ end
386
+
387
+ if i_blocked && o_blocked && e_blocked
388
+ IO.select([o], [i], [o,i])
389
+ end
390
+ end
391
+ return [o_data, t.value]
392
+ end
393
+ end
394
+ module_function :capture2
395
+
396
+ # Open3.capture2e captures the standard output and the standard error of a command.
397
+ #
398
+ # stdout_and_stderr_str, status = Open3.capture2e(cmd... [, opts])
399
+ #
400
+ # The cmd arguments are passed to Open3.popen3.
401
+ #
402
+ # If opts[:stdin_data] is specified, it is sent to the command's standard input.
403
+ #
404
+ # If opts[:binmode] is true, internal pipes are set to binary mode.
405
+ #
406
+ # Example:
407
+ #
408
+ # # capture make log
409
+ # make_log, s = Open3.capture2e("make")
410
+ #
411
+ def capture2e(*cmd)
412
+ if Hash === cmd.last
413
+ opts = cmd.pop.dup
414
+ else
415
+ opts = {}
416
+ end
417
+
418
+ binmode = opts[:binmode]
419
+ i_data = (opts[:stdin_data] || '').to_s
420
+ oe_data = ''
421
+
422
+ popen3(*cmd) do |i, o, e, t|
423
+ if binmode
424
+ i.binmode
425
+ o.binmode
426
+ e.binmode
427
+ end
428
+
429
+ i_complete = i_data.empty?
430
+ o_complete = false
431
+ e_complete = false
432
+
433
+ until i_complete && o_complete && e_complete
434
+ i_blocked = false
435
+ o_blocked = false
436
+ e_blocked = false
437
+
438
+ unless i_complete
439
+ begin
440
+ bytes_written = i.write_nonblock(i_data)
441
+ if bytes_written == i_data.length
442
+ i.close
443
+ i_complete = true
444
+ else
445
+ i_data = i_data[bytes_written .. -1]
446
+ end
447
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
448
+ i_blocked = true
449
+ end
450
+ end
451
+
452
+ unless o_complete
453
+ begin
454
+ oe_data << o.read_nonblock(CAPTURE_BUFFER_SIZE)
455
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
456
+ o_blocked = true
457
+ rescue EOFError
458
+ raise unless i_complete
459
+ o.close
460
+ o_complete = true
461
+ end
462
+ end
463
+
464
+ unless e_complete
465
+ begin
466
+ oe_data << e.read_nonblock(CAPTURE_BUFFER_SIZE)
467
+ rescue Errno::EWOULDBLOCK, Errno::EAGAIN
468
+ e_blocked = true
469
+ rescue EOFError
470
+ raise unless i_complete
471
+ e.close
472
+ e_complete = true
473
+ end
474
+ end
475
+
476
+ if i_blocked && o_blocked && e_blocked
477
+ IO.select([o, e], [i], [o,e,i])
478
+ end
479
+ end
480
+ return [oe_data, t.value]
481
+ end
482
+ end
483
+ module_function :capture2e
484
+
485
+ CAPTURE_BUFFER_SIZE = 65536
486
+
487
+ # Open3.pipeline_rw starts a list of commands as a pipeline with pipes
488
+ # which connects stdin of the first command and stdout of the last command.
489
+ #
490
+ # Open3.pipeline_rw(cmd1, cmd2, ...) {|first_stdin, last_stdout, wait_threads|
491
+ # ...
492
+ # }
493
+ #
494
+ # first_stdin, last_stdout, wait_threads = Open3.pipeline_rw(cmd1, cmd2, ...)
495
+ # ...
496
+ # first_stdin.close
497
+ # last_stdout.close
498
+ #
499
+ # Each cmd is a string or an array.
500
+ # If it is an array, the first element is the command name, and the remaining
501
+ # elements are arguments passed (without parsing) to the command.
502
+ #
503
+ # Example:
504
+ #
505
+ # Open3.pipeline_rw(["tr", "-dc", "A-Za-z"], ["wc", "-c"]) {|i,o,ts|
506
+ # i.puts "All persons more than a mile high to leave the court."
507
+ # i.close
508
+ # p o.gets #=> "42\n"
509
+ # }
510
+ #
511
+ # Open3.pipeline_rw("sort", "cat -n") {|stdin, stdout, wait_thrs|
512
+ # stdin.puts "foo"
513
+ # stdin.puts "bar"
514
+ # stdin.puts "baz"
515
+ # stdin.close # send EOF to sort.
516
+ # p stdout.read #=> " 1\tbar\n 2\tbaz\n 3\tfoo\n"
517
+ # }
518
+ def pipeline_rw(*cmds, &block)
519
+ raise NotImplementedError
520
+ end
521
+ module_function :pipeline_rw
522
+
523
+ # Open3.pipeline_r starts a list of commands as a pipeline with a pipe
524
+ # which connects stdout of the last command.
525
+ #
526
+ # Open3.pipeline_r(cmd1, cmd2, ...) {|last_stdout, wait_threads|
527
+ # ...
528
+ # }
529
+ #
530
+ # last_stdout, wait_threads = Open3.pipeline_r(cmd1, cmd2, ...)
531
+ # ...
532
+ # last_stdout.close
533
+ #
534
+ # Each cmd is a string or an array.
535
+ # If it is an array, the first element is the command name, and the remaining
536
+ # elements are arguments passed (without parsing) to the command.
537
+ #
538
+ # Example:
539
+ #
540
+ # Open3.pipeline_r("zcat /var/log/apache2/access.log.*.gz",
541
+ # [{"LANG"=>"C"}, "grep", "GET /favicon.ico"],
542
+ # "logresolve") {|o, ts|
543
+ # o.each_line {|line|
544
+ # ...
545
+ # }
546
+ # }
547
+ #
548
+ # Open3.pipeline_r("yes", "head -10") {|o, ts|
549
+ # p o.read #=> "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"
550
+ # p ts[0].value #=> #<Process::Status: pid 24910 SIGPIPE (signal 13)>
551
+ # p ts[1].value #=> #<Process::Status: pid 24913 exit 0>
552
+ # }
553
+ #
554
+ def pipeline_r(*cmds, &block)
555
+ raise NotImplementedError
556
+ end
557
+ module_function :pipeline_r
558
+
559
+ # Open3.pipeline_w starts a list of commands as a pipeline with a pipe
560
+ # which connects stdin of the first command.
561
+ #
562
+ # Open3.pipeline_w(cmd1, cmd2, ...) {|first_stdin, wait_threads|
563
+ # ...
564
+ # }
565
+ #
566
+ # first_stdin, wait_threads = Open3.pipeline_w(cmd1, cmd2, ...)
567
+ # ...
568
+ # first_stdin.close
569
+ #
570
+ # Each cmd is a string or an array.
571
+ # If it is an array, the first element is the command name, and the remaining
572
+ # elements are arguments passed (without parsing) to the command.
573
+ #
574
+ # Example:
575
+ #
576
+ # Open3.pipeline_w("bzip2 -c", :out=>"/tmp/hello.bz2") {|i, ts|
577
+ # i.puts "hello"
578
+ # }
579
+ #
580
+ def pipeline_w(*cmds, &block)
581
+ raise NotImplementedError
582
+ end
583
+ module_function :pipeline_w
584
+
585
+ # Open3.pipeline_start starts a list of commands as a pipeline.
586
+ # No pipe made for stdin of the first command and
587
+ # stdout of the last command.
588
+ #
589
+ # Open3.pipeline_start(cmd1, cmd2, ...) {|wait_threads|
590
+ # ...
591
+ # }
592
+ #
593
+ # wait_threads = Open3.pipeline_start(cmd1, cmd2, ...)
594
+ # ...
595
+ #
596
+ # Each cmd is a string or an array.
597
+ # If it is an array, the first element is the command name, and the remaining
598
+ # elements are arguments passed (without parsing) to the command.
599
+ #
600
+ # Example:
601
+ #
602
+ # # run xeyes in 10 seconds.
603
+ # Open3.pipeline_start("xeyes") {|ts|
604
+ # sleep 10
605
+ # t = ts[0]
606
+ # Process.kill("TERM", t.pid)
607
+ # p t.value #=> #<Process::Status: pid 911 SIGTERM (signal 15)>
608
+ # }
609
+ #
610
+ # # convert pdf to ps and send it to a printer.
611
+ # # collect error message of pdftops and lpr.
612
+ # pdf_file = "paper.pdf"
613
+ # printer = "printer-name"
614
+ # err_r, err_w = IO.pipe
615
+ # Open3.pipeline_start(["pdftops", pdf_file, "-"],
616
+ # ["lpr", "-P#{printer}"],
617
+ # :err=>err_w) {|ts|
618
+ # err_w.close
619
+ # p err_r.read # error messages of pdftops and lpr.
620
+ # }
621
+ #
622
+ def pipeline_start(*cmds, &block)
623
+ raise NotImplementedError
624
+ end
625
+ module_function :pipeline_start
626
+
627
+ # Open3.pipeline starts a list of commands as a pipeline.
628
+ # It waits the finish of the commands.
629
+ # No pipe made for stdin of the first command and
630
+ # stdout of the last command.
631
+ #
632
+ # status_list = Open3.pipeline(cmd1, cmd2, ...)
633
+ #
634
+ # Each cmd is a string or an array.
635
+ # If it is an array, the first element is the command name, and the remaining
636
+ # elements are arguments passed (without parsing) to the command.
637
+ #
638
+ # Example:
639
+ #
640
+ # fname = "/usr/share/man/man1/ruby.1.gz"
641
+ # p Open3.pipeline(["zcat", fname], "nroff -man", "less")
642
+ # #=> [#<Process::Status: pid 11817 exit 0>,
643
+ # # #<Process::Status: pid 11820 exit 0>,
644
+ # # #<Process::Status: pid 11828 exit 0>]
645
+ #
646
+ # fname = "/usr/share/man/man1/ls.1.gz"
647
+ # Open3.pipeline(["zcat", fname], "nroff -man", "colcrt")
648
+ #
649
+ # # convert PDF to PS and send to a printer by lpr
650
+ # pdf_file = "paper.pdf"
651
+ # printer = "printer-name"
652
+ # Open3.pipeline(["pdftops", pdf_file, "-"],
653
+ # ["lpr", "-P#{printer}"])
654
+ #
655
+ # # count lines
656
+ # Open3.pipeline("sort", "uniq -c", :in=>"names.txt", :out=>"count")
657
+ #
658
+ # # cyclic pipeline
659
+ # r,w = IO.pipe
660
+ # w.print "ibase=14\n10\n"
661
+ # Open3.pipeline("bc", "tee /dev/tty", :in=>r, :out=>w)
662
+ # #=> 14
663
+ # # 18
664
+ # # 22
665
+ # # 30
666
+ # # 42
667
+ # # 58
668
+ # # 78
669
+ # # 106
670
+ # # 202
671
+ #
672
+ def pipeline(*cmds)
673
+ raise NotImplementedError
674
+ end
675
+ module_function :pipeline
676
+ end