win32-open3 0.3.2-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,81 @@
1
+ == 0.3.2 - 23-Jan-2010
2
+ * Updated the gemspec. The required version is now any version less
3
+ than 1.9.x. This library is for the 1.8.x branch only.
4
+ * The Rakefile now handles the gem building and installation and some
5
+ existing tasks were refactored.
6
+
7
+ == 0.3.1 - 23-Jun-2009
8
+ * Updated the license to Artistic 2.0
9
+ * Set the license in the gemspec.
10
+ * Added note regarding 1.9.x in the open3.txt file.
11
+
12
+ == 0.3.0 - 22-Jun-2009
13
+ * Fixed an issue where the block form of the Open3.popen3 method did not
14
+ return the exit value.
15
+ * Fixed the version number.
16
+ * Added a test to validate the exit value in block form.
17
+ * Updated the gemspec description.
18
+
19
+ == 0.2.9 - 8-Mar-2009
20
+ * Fixed a bug within an internal function that could cause an exception.
21
+ Thanks go to Ross Bunker for the spot and patch.
22
+ * Fixed the release dates for 0.2.7 and 0.2.8 (2009, not 2008).
23
+
24
+ == 0.2.8 - 27-Feb-2009
25
+ * Fixed a potential bug where nil might be returned instead of an actual
26
+ Process::Status object. Thanks go to Benny Bach for the spot.
27
+ * The 'test' Rake task now runs the 'clean' task after the fact.
28
+ * Some updates to the README about precompiled binaries.
29
+
30
+ == 0.2.7 - 11-Jan-2009
31
+ * Fixed a bug that could cause exitstatus to return bogus information. Thanks
32
+ go to Roman Zawada for the spot.
33
+ * Added a 'build_binary_gem' Rake task.
34
+ * Updates to the gemspec, including the addition of Park Heesob as the primary
35
+ author in the gemspec.
36
+ * Renamed the test file and the example file.
37
+
38
+ == 0.2.6 - 1-June-2007
39
+ * Fixed RubyForge bug #20455 (closed IO stream). Thanks go an anonymous user
40
+ for the spot.
41
+ * Added a Rakefile with tasks for building, testing and installation.
42
+ * Minor updates to the test file.
43
+ * Some minor rdoc improvements.
44
+
45
+ == 0.2.5 - 8-Dec-2006
46
+ * Added a WIN32_OPEN3_VERSION constant.
47
+ * Fixes and updates to the gemspec.
48
+ * Some internal reorganization.
49
+ * Minor documentation updates and corrections.
50
+
51
+ == 0.2.4 - 4-Jun-2006
52
+ * Applied a patch for Windows 2000. Thanks again to John-Mason Shackelford
53
+ for the patch.
54
+ * Fixed the version number for the last release.
55
+
56
+ == 0.2.3 - 3-Jun-2006
57
+ * Now sets Process::Status when the block form is used. Thanks go to
58
+ Samuel Tesla and John-Mason Shackelford for their patches.
59
+ * Improved error messages.
60
+ * Documentation updated with regards to the block form of the method calls.
61
+ * Some internal reorganization that you don't care about.
62
+
63
+ == 0.2.2 - 10-Feb-2006
64
+ * Methods now support block form, and all handles are automatically closed
65
+ at the end of the block (via ensure).
66
+
67
+ == 0.2.1 - 27-Oct-2005
68
+ * Added a type check in the extconf.rb file and did some type mangling in
69
+ order to handle rb_pid_t in a backwards compatible way. In other words,
70
+ you need this version to work with 1.8.3 or later.
71
+
72
+ == 0.2.0 - 9-Jan-2005
73
+ * Added the Open4 module and popen4 method. Thanks go to Aslak Hellesoy for
74
+ the patch.
75
+ * Added an optional 3rd parameter - the "show" flag. See docs for details.
76
+ * Made some documentation more rdoc friendly.
77
+ * Moved the "examples" directory to the toplevel directory.
78
+ * Added some information to the README file about possible compiler problems.
79
+
80
+ == 0.1.0 - 8-Oct-2004
81
+ * Initial release
@@ -0,0 +1,11 @@
1
+ * CHANGES
2
+ * MANIFEST
3
+ * README
4
+ * Rakefile
5
+ * win32-open3.gemspec
6
+ * doc/open3.txt
7
+ * examples/open3_test.rb
8
+ * ext/extconf.rb
9
+ * ext/win32/open3.c
10
+ * ext/win32/open3.h
11
+ * test/test_win32_open3.rb
data/README ADDED
@@ -0,0 +1,64 @@
1
+ = Description
2
+ This library provides an Open3.popen3 implementation for MS Windows for
3
+ the Ruby 1.8.x branch.
4
+
5
+ = Prerequisites
6
+ Ruby 1.8.x branch only
7
+ C++ compiler (if building from source)
8
+
9
+ = Installation
10
+ gem install win32-open3 # Build and install from source
11
+ gem install win32-open3 --platform mingw # Install the binary gem
12
+
13
+ = Synopsis
14
+ require 'win32/open3'
15
+
16
+ Open3.popen3('ver'){ |io_in, io_out, io_err|
17
+ error = io_err.gets
18
+
19
+ if error
20
+ puts "Error: " + error.chomp
21
+ exit
22
+ end
23
+
24
+ puts "Result: " + io_out.readlines.last.chomp
25
+ }
26
+
27
+ = Notes
28
+ This library is not supported on Windows 95, 98, or ME.
29
+ This is a stripped down version of Park Heesob's win32_popen library.
30
+
31
+ = Developer's Notes
32
+ This is a repackaging of Heesob's win32_popen module. The purpose of the
33
+ repackaging was to create a unified API between the existing Ruby open3
34
+ library and this library.
35
+
36
+ The popen2 and posix_popen methods are not included in this release. They
37
+ were originally part of the win32_popen module, but have been left out.
38
+ They may be added back in at a later date.
39
+
40
+ There are a couple of differences in the Windows version for open3 (which
41
+ also apply to Open4.popen4) - the mode flag and the show flag. For the
42
+ mode, you can specify either 't' (text, the default) or 'b' (binary) as a
43
+ second argument. For the show flag, you can specify either true or false,
44
+ which will show the console window, or not, depending on the value you
45
+ pass. The default is false.
46
+
47
+ = Known Issues
48
+ I have noticed that this library (and others) may segfault if you are using
49
+ the latest version of the Ruby one-click installer and VC++ 7.0 or later.
50
+ This appears to be an issue between VC++ 6 (which the installer was built
51
+ with) and VC++ 7.0. Your best solution is to either upgrade your C
52
+ compiler or to rebuild Ruby from scratch rather than using the installer.
53
+
54
+ = Precompiled binaries
55
+ If you do a typical "gem install win32-open3" you will get a precompiled
56
+ binary appropriate for your platform. If you want to build from source,
57
+ go the project page and download, download the .zip file, and run the
58
+ 'install' Rake task from the command line.
59
+
60
+ = Future Plans
61
+ Replace the current implementation with a pure Ruby version, if possible.
62
+
63
+ = More Documentation
64
+ See the doc/open3.txt file for more details.
@@ -0,0 +1,98 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ include Config
6
+
7
+ make = CONFIG['MAKEFILES'].nil? ? 'nmake' : 'make'
8
+
9
+ desc 'Install the win32-open3 library'
10
+ task :install => [:build] do
11
+ Dir.chdir('ext'){
12
+ sh "#{make} install"
13
+ }
14
+ end
15
+
16
+ desc "Clean any build files for win32-open3"
17
+ task :clean do
18
+ Dir.chdir('ext') do
19
+ if File.exists?('open3.so') || File.exists?('win32/open3.so')
20
+ sh "#{make} distclean"
21
+ rm 'win32/open3.so' if File.exists?('win32/open3.so')
22
+ end
23
+ end
24
+ rm_rf 'lib'
25
+ end
26
+
27
+ desc "Build win32-open3 (but don't install it)"
28
+ task :build => [:clean] do
29
+ Dir.chdir('ext') do
30
+ ruby 'extconf.rb'
31
+ sh make
32
+ mv 'open3.so', 'win32' # For the test suite
33
+ end
34
+ end
35
+
36
+ desc "Run the sample program"
37
+ task :example => [:build] do |t|
38
+ Dir.chdir('examples'){
39
+ sh 'ruby -I../ext open3_test.rb'
40
+ }
41
+ end
42
+
43
+ namespace 'gem' do
44
+ desc 'Clean any build files and remove the .gem file'
45
+ task :clean do
46
+ Dir["*.gem"].each{ |f| File.delete(f) }
47
+ Dir.chdir('ext') do
48
+ if File.exists?('open3.so') || File.exists?('win32/open3.so')
49
+ sh "#{make} distclean"
50
+ rm 'win32/open3.so' if File.exists?('win32/open3.so')
51
+ end
52
+ end
53
+ end
54
+
55
+ desc 'Build the gem'
56
+ task :build => [:clean] do
57
+ spec = eval(IO.read('win32-open3.gemspec'))
58
+ Gem::Builder.new(spec).build
59
+ end
60
+
61
+ desc 'Install the win32-open3 library as a gem'
62
+ task :install => [:build] do
63
+ file = Dir["*.gem"].first
64
+ sh "gem install #{file}"
65
+ end
66
+
67
+ desc 'Build a binary gem'
68
+ task :binary => [:clean] do
69
+ Dir.chdir('ext') do
70
+ ruby 'extconf.rb'
71
+ sh make
72
+ end
73
+
74
+ mkdir_p 'lib/win32'
75
+ mv 'ext/open3.so', 'lib/win32'
76
+
77
+ spec = eval(IO.read('win32-open3.gemspec'))
78
+ spec.extensions = nil
79
+ spec.platform = Gem::Platform::CURRENT
80
+
81
+ spec.files = Dir['**/*'].reject{ |file|
82
+ file.include?('ext') || file.include?('git')
83
+ }
84
+
85
+ Gem::Builder.new(spec).build
86
+ end
87
+ end
88
+
89
+ Rake::TestTask.new('test') do |test|
90
+ task :test => [:build]
91
+ test.libs << 'ext'
92
+ test.warning = true
93
+ test.verbose = true
94
+ end
95
+
96
+ task :test do
97
+ Rake.application[:clean].execute
98
+ end
@@ -0,0 +1,79 @@
1
+ = Description
2
+ An open3 library for MS Windows for the Ruby 1.8.x branch.
3
+
4
+ = Synopsis
5
+ require 'win32/open3'
6
+
7
+ Open3.popen3('ver'){ |io_in, io_out, io_err|
8
+ error = io_err.gets
9
+ if error
10
+ puts "Error: " + error.chomp
11
+ exit
12
+ end
13
+ puts "Result: " + io_out.readlines.last.chomp
14
+ }
15
+
16
+ = Module functions
17
+ Open3.popen3(command, mode='t', show=false)
18
+
19
+ Open3.popen3(command, mode='t', show=false){ |io_in, io_out, io_err| ... }
20
+
21
+ Executes 'command', returning an array of three IO handles representing
22
+ STDIN, STDOUT and STDERR, respectively. In block form these IO handles
23
+ are yielded back to the block and automatically closed at the end of the
24
+ block.
25
+
26
+ You may optionally pass a mode flag of 't' (text, the default) or 'b'
27
+ (binary) to this method.
28
+
29
+ If the 'show' variable is set to true, then a console window is shown.
30
+
31
+ Open4.popen4(command, mode='t', show=false)
32
+
33
+ Open4.popen4(command, mode='t', show=false){ |io_in, io_out, io_err, pid| ... }
34
+
35
+ Executes 'command', returning an array consisting of three IO handles,
36
+ representing STDIN, STDOUT and STDERR, respectively, as well as the PID
37
+ of the newly generated process. In block form these IO handles and pid
38
+ are yielded back to the block and automatically closed at the end of the
39
+ block.
40
+
41
+ You may optionally pass a mode flag of 't' (text, the default) or 'b'
42
+ (binary) to this method.
43
+
44
+ If the 'show' variable is set to true, then a console window is shown.
45
+
46
+ = Notes
47
+ This is a remake of Park Heesob's win32_popen package. It has been reworked
48
+ in order to make the API (nearly) identical to the Open3 package currently
49
+ included in the Ruby standard library.
50
+
51
+ For now only the popen3 and popen4 methods have been included. In later
52
+ releases we will probably add the popen2 and posix_popen methods back in.
53
+
54
+ Ruby 1.9.x users will not generally need this library because of its support
55
+ for native threads. That means you can use the open3 library that ships as
56
+ part of the standard library.
57
+
58
+ = Acknowledgements
59
+ Thanks go to Samuel Tesla and John-Mason Shackelford for their patches that
60
+ enabled us to set Process::Status.
61
+
62
+ = Known Bugs
63
+ None that I know of. Please log any other bug reports on the RubyForge
64
+ project page at http://www.rubyforge.net/projects/win32utils.
65
+
66
+ = License
67
+ Artistic 2.0
68
+
69
+ = Copyright
70
+ (C) 2003-2010 Daniel J. Berger, All Rights Reserved .
71
+
72
+ = Warranty
73
+ This package is provided "as is" and without any express or
74
+ implied warranties, including, without limitation, the implied
75
+ warranties of merchantability and fitness for a particular purpose.
76
+
77
+ = Authors
78
+ Park Heesob
79
+ Daniel J. Berger
@@ -0,0 +1,34 @@
1
+ ##############################################################################
2
+ # win32_open3_example.rb
3
+ #
4
+ # Test script for the win32-open3 library. You should run this via the
5
+ # 'rake example' Rakefile task.
6
+ #
7
+ # Modify as you see fit.
8
+ ##############################################################################
9
+ require 'win32/open3'
10
+
11
+ puts "VERSION: " + Open3::WIN32_OPEN3_VERSION
12
+
13
+ cmd1 = 'ver' # valid
14
+ cmd2 = 'verb' # invalid
15
+ cmd3 = "ruby -e 'exit 1'" # valid, with explicit exit status
16
+
17
+ # Try each command as you like...
18
+ Open3.popen3(cmd3){ |io_in, io_out, io_err|
19
+ error = io_err.read
20
+ if error && error.length > 0
21
+ puts 'Error: ' + error
22
+ break
23
+ else
24
+ output = io_out.read
25
+ puts 'Output: ' + output if output
26
+ end
27
+ }
28
+
29
+ p $?.class
30
+ p $? >> 8
31
+ p $?.stopped?
32
+ p $?.exited?
33
+ p $?.exitstatus
34
+ p $?.pid
@@ -0,0 +1,567 @@
1
+ /***************************************************
2
+ * open3.c
3
+ *
4
+ * Source for the win32-open3 extension.
5
+ ***************************************************/
6
+ #include "ruby.h"
7
+
8
+ #ifdef HAVE_RUBY_IO_H
9
+ #include "ruby/io.h"
10
+ #else
11
+ #include "rubysig.h"
12
+ #include "rubyio.h"
13
+ #endif
14
+
15
+ #include "open3.h"
16
+
17
+ #include <malloc.h>
18
+ #include <io.h>
19
+ #include <string.h>
20
+ #include <fcntl.h>
21
+ #include <sys/stat.h>
22
+
23
+ // Necessary to work with Ruby 1.8.3 or later
24
+ #ifdef HAVE_TYPE_RB_PID_T
25
+ #define pid_t rb_pid_t
26
+ #endif
27
+
28
+ // Ruby 1.9.x
29
+ #ifndef RSTRING_PTR
30
+ #define RSTRING_PTR(s) (RSTRING(s)->ptr)
31
+ #endif
32
+ #ifndef RSTRING_LEN
33
+ #define RSTRING_LEN(s) (RSTRING(s)->len)
34
+ #endif
35
+
36
+ #ifndef RARRAY_PTR
37
+ #define RARRAY_PTR(a) (RARRAY(a)->ptr)
38
+ #endif
39
+ #ifndef RARRAY_LEN
40
+ #define RARRAY_LEN(a) (RARRAY(a)->len)
41
+ #endif
42
+
43
+ static VALUE win32_last_status = Qnil;
44
+ static HANDLE pid_handle = NULL;
45
+
46
+ static VALUE ruby_popen(char *, int, VALUE);
47
+
48
+ static int rb_io_mode_flags2(int mode){
49
+ int flags;
50
+
51
+ switch (mode & (O_RDONLY|O_WRONLY|O_RDWR)) {
52
+ case O_RDONLY:
53
+ flags = FMODE_READABLE;
54
+ break;
55
+ case O_WRONLY:
56
+ flags = FMODE_WRITABLE;
57
+ break;
58
+ case O_RDWR:
59
+ flags = FMODE_WRITABLE|FMODE_READABLE;
60
+ break;
61
+ }
62
+
63
+ #ifdef O_BINARY
64
+ if(mode & O_BINARY)
65
+ flags |= FMODE_BINMODE;
66
+ #endif
67
+
68
+ return flags;
69
+ }
70
+
71
+ static char* rb_io_modenum_mode(int flags, char* mode){
72
+ char *p = mode;
73
+
74
+ switch(flags & (O_RDONLY|O_WRONLY|O_RDWR)){
75
+ case O_RDONLY:
76
+ *p++ = 'r';
77
+ break;
78
+ case O_WRONLY:
79
+ *p++ = 'w';
80
+ break;
81
+ case O_RDWR:
82
+ *p++ = 'r';
83
+ *p++ = '+';
84
+ break;
85
+ }
86
+
87
+ *p++ = '\0';
88
+
89
+ #ifdef O_BINARY
90
+ if(flags & O_BINARY){
91
+ if(mode[1] == '+'){
92
+ mode[1] = 'b';
93
+ mode[2] = '+';
94
+ mode[3] = '\0';
95
+ }
96
+ else{
97
+ mode[1] = 'b'; mode[2] = '\0';
98
+ }
99
+ }
100
+ #endif
101
+ return mode;
102
+ }
103
+
104
+ /* Used to close io handle */
105
+ static VALUE io_close(VALUE val) {
106
+ int i;
107
+
108
+ for(i = 0; i < 3; i++){
109
+ if(rb_funcall(RARRAY_PTR(val)[i], rb_intern("closed?"), 0) == Qfalse)
110
+ rb_funcall(RARRAY_PTR(val)[i], rb_intern("close"), 0);
111
+ }
112
+
113
+ return Qnil;
114
+ }
115
+
116
+ /*
117
+ * call-seq:
118
+ * Open3.popen3(cmd, mode='t', show=false)
119
+ * Open3.popen3(cmd, mode='t', show=false){ |io_in, io_out, io_err| ... }
120
+ *
121
+ * Executes 'command', returning an array of three IO handles representing
122
+ * STDIN, STDOUT and STDERR, respectively. In block form these IO handles
123
+ * are yielded back to the block and automatically closed at the end of the
124
+ * block.
125
+ *
126
+ * You may optionally pass a mode flag of 't' (text, the default) or 'b'
127
+ * (binary) to this method.
128
+ *
129
+ * If the 'show' variable is set to true, then a console window is shown.
130
+ */
131
+ static VALUE win32_popen3(int argc, VALUE *argv, VALUE klass)
132
+ {
133
+ VALUE v_name, v_mode, v_port;
134
+ VALUE v_show_window = Qfalse;
135
+ char mbuf[4];
136
+ int tm = 0;
137
+ char *mode = "t";
138
+
139
+ rb_scan_args(argc, argv, "12", &v_name, &v_mode, &v_show_window);
140
+
141
+ // Mode can be either a string or a number
142
+ if(!NIL_P(v_mode)){
143
+ if(FIXNUM_P(v_mode))
144
+ mode = rb_io_modenum_mode(FIX2INT(v_mode), mbuf);
145
+ else
146
+ mode = StringValuePtr(v_mode);
147
+ }
148
+
149
+ if(*mode == 't')
150
+ tm = _O_TEXT;
151
+ else if(*mode != 'b')
152
+ rb_raise(rb_eArgError, "popen3() arg 2 must be 't' or 'b'");
153
+ else
154
+ tm = _O_BINARY;
155
+
156
+ v_port = ruby_popen(StringValuePtr(v_name), tm, v_show_window);
157
+
158
+ // Ensure handles are closed in block form
159
+ if(rb_block_given_p()){
160
+ return rb_ensure(rb_yield_splat, v_port, io_close, v_port);
161
+ }
162
+
163
+ return v_port;
164
+ }
165
+
166
+ static BOOL RubyCreateProcess(char *cmdstring, HANDLE hStdin, HANDLE hStdout,
167
+ HANDLE hStderr, HANDLE *hProcess, pid_t *pid, VALUE v_show_window)
168
+ {
169
+ PROCESS_INFORMATION piProcInfo;
170
+ STARTUPINFO siStartInfo;
171
+ char *s1,*s2, *s3 = " /c ";
172
+ int i, x;
173
+
174
+ if(i = GetEnvironmentVariable("COMSPEC", NULL, 0)){
175
+ char *comshell;
176
+ s1 = (char *)_alloca(i);
177
+
178
+ if(!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
179
+ return x;
180
+
181
+ /* Explicitly check if we are using COMMAND.COM. If we are
182
+ * then use the w9xpopen hack.
183
+ */
184
+ comshell = s1 + x;
185
+
186
+ while(comshell >= s1 && *comshell != '\\')
187
+ --comshell;
188
+
189
+ ++comshell;
190
+
191
+ // Windows 95, 98 and ME are not supported
192
+ if(GetVersion() < 0x80000000 && _stricmp(comshell, "command.com") != 0){
193
+ x = i + strlen(s3) + strlen(cmdstring) + 1;
194
+ s2 = ALLOCA_N(char, x);
195
+ sprintf(s2, "%s%s%s", s1, s3, cmdstring);
196
+ }
197
+ else{
198
+ rb_raise(rb_eRuntimeError,"not supported on this platform");
199
+ }
200
+ }
201
+ else{
202
+ // Could be an else here to try cmd.exe / command.com in the path.
203
+ // Now we'll just error out..
204
+ rb_raise(rb_eRuntimeError,
205
+ "Cannot locate a COMSPEC environment variable to use as the shell"
206
+ );
207
+ return FALSE;
208
+ }
209
+
210
+ ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
211
+
212
+ siStartInfo.cb = sizeof(STARTUPINFO);
213
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
214
+ siStartInfo.hStdInput = hStdin;
215
+ siStartInfo.hStdOutput = hStdout;
216
+ siStartInfo.hStdError = hStderr;
217
+ siStartInfo.wShowWindow = SW_HIDE;
218
+
219
+ if(v_show_window == Qtrue)
220
+ siStartInfo.wShowWindow = SW_SHOW;
221
+
222
+ // Try the command first without COMSPEC
223
+ if(CreateProcess(
224
+ NULL,
225
+ cmdstring,
226
+ NULL,
227
+ NULL,
228
+ TRUE,
229
+ 0,
230
+ NULL,
231
+ NULL,
232
+ &siStartInfo,
233
+ &piProcInfo)
234
+ ){
235
+ // Close the handles now so anyone waiting is woken.
236
+ CloseHandle(piProcInfo.hThread);
237
+
238
+ // Return process handle
239
+ *hProcess = piProcInfo.hProcess;
240
+ *pid = (pid_t)piProcInfo.dwProcessId;
241
+ return TRUE;
242
+ }
243
+
244
+ // If that failed, try again with COMSPEC
245
+ if(CreateProcess(
246
+ NULL,
247
+ s2,
248
+ NULL,
249
+ NULL,
250
+ TRUE,
251
+ CREATE_NEW_CONSOLE,
252
+ NULL,
253
+ NULL,
254
+ &siStartInfo,
255
+ &piProcInfo)
256
+ ){
257
+ // Close the handles now so anyone waiting is woken.
258
+ CloseHandle(piProcInfo.hThread);
259
+
260
+ // Return process handle
261
+ *hProcess = piProcInfo.hProcess;
262
+ *pid = (pid_t)piProcInfo.dwProcessId;
263
+ return TRUE;
264
+ }
265
+
266
+ rb_raise(rb_eRuntimeError, "CreateProcess() failed: %s",
267
+ ErrorDescription(GetLastError())
268
+ );
269
+
270
+ return FALSE;
271
+ }
272
+
273
+ /* Set the Process::Status. This code is based on patches by Samuel Tesla and
274
+ * John-Mason Shackelford.
275
+ */
276
+ static void win32_set_last_status(const int status, const int pid)
277
+ {
278
+ // rb_last_status is defined in process.c in the main ruby.exe
279
+ __declspec (dllimport) extern VALUE rb_last_status;
280
+ VALUE klass = rb_path2class("Process::Status");
281
+ VALUE process_status = rb_obj_alloc(klass);
282
+ rb_iv_set(process_status, "status", INT2FIX(status << 8));
283
+ rb_iv_set(process_status, "pid", INT2FIX(pid));
284
+ rb_last_status = process_status;
285
+ win32_last_status = process_status;
286
+ }
287
+
288
+ static void win32_pipe_finalize(OpenFile *file, int noraise)
289
+ {
290
+ DWORD status;
291
+
292
+ if(file->f){
293
+ fclose(file->f);
294
+ file->f = NULL;
295
+ }
296
+
297
+ if(file->f2){
298
+ fclose(file->f2);
299
+ file->f2 = NULL;
300
+ }
301
+
302
+ if(pid_handle != NULL){
303
+ GetExitCodeProcess(pid_handle, &status);
304
+
305
+ if(status != STILL_ACTIVE){
306
+ CloseHandle(pid_handle);
307
+ pid_handle = NULL;
308
+ win32_set_last_status(status, file->pid);
309
+ }
310
+ }
311
+ }
312
+
313
+ // The following code is based off of KB: Q190351
314
+ static VALUE ruby_popen(char *cmdstring, int mode, VALUE v_show_window)
315
+ {
316
+ HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
317
+ hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
318
+ hChildStderrRdDup, hProcess;
319
+
320
+ pid_t pid;
321
+
322
+ SECURITY_ATTRIBUTES saAttr;
323
+ BOOL fSuccess;
324
+ int fd1, fd2, fd3;
325
+ FILE *f1, *f2, *f3;
326
+ long file_count;
327
+ VALUE port;
328
+ VALUE p1,p2,p3;
329
+ int modef;
330
+ OpenFile *fptr;
331
+ char *m1, *m2;
332
+
333
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
334
+ saAttr.bInheritHandle = TRUE;
335
+ saAttr.lpSecurityDescriptor = NULL;
336
+
337
+ if(!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
338
+ rb_raise(rb_eRuntimeError, "CreatePipe() failed: %s",
339
+ ErrorDescription(GetLastError())
340
+ );
341
+ }
342
+
343
+ /* Create new output read handle and the input write handle. Set
344
+ * the inheritance properties to FALSE. Otherwise, the child inherits
345
+ * the these handles; resulting in non-closeable handles to the pipes
346
+ * being created.
347
+ */
348
+ fSuccess = DuplicateHandle(
349
+ GetCurrentProcess(),
350
+ hChildStdinWr,
351
+ GetCurrentProcess(),
352
+ &hChildStdinWrDup,
353
+ 0,
354
+ FALSE,
355
+ DUPLICATE_SAME_ACCESS
356
+ );
357
+
358
+ if(!fSuccess){
359
+ rb_raise(rb_eRuntimeError, "DuplicateHandle() failed: %s",
360
+ ErrorDescription(GetLastError())
361
+ );
362
+ }
363
+
364
+ // Close the inheritable version of ChildStdin that we're using
365
+ CloseHandle(hChildStdinWr);
366
+
367
+ if(!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
368
+ rb_raise(rb_eRuntimeError, "CreatePipe() failed: %s",
369
+ ErrorDescription(GetLastError())
370
+ );
371
+ }
372
+
373
+ fSuccess = DuplicateHandle(
374
+ GetCurrentProcess(),
375
+ hChildStdoutRd,
376
+ GetCurrentProcess(),
377
+ &hChildStdoutRdDup,
378
+ 0,
379
+ FALSE,
380
+ DUPLICATE_SAME_ACCESS
381
+ );
382
+
383
+ if(!fSuccess){
384
+ rb_raise(rb_eRuntimeError, "DuplicateHandle() failed: %s",
385
+ ErrorDescription(GetLastError())
386
+ );
387
+ }
388
+
389
+ // Close the inheritable version of ChildStdout that we're using.
390
+ CloseHandle(hChildStdoutRd);
391
+
392
+ if(!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)) {
393
+ rb_raise(rb_eRuntimeError, "CreatePipe() failed: %s",
394
+ ErrorDescription(GetLastError())
395
+ );
396
+ }
397
+
398
+ fSuccess = DuplicateHandle(
399
+ GetCurrentProcess(),
400
+ hChildStderrRd,
401
+ GetCurrentProcess(),
402
+ &hChildStderrRdDup,
403
+ 0,
404
+ FALSE,
405
+ DUPLICATE_SAME_ACCESS
406
+ );
407
+
408
+ if (!fSuccess) {
409
+ rb_raise(rb_eRuntimeError, "DuplicateHandle() failed: %s",
410
+ ErrorDescription(GetLastError())
411
+ );
412
+ }
413
+
414
+ // Close the inheritable version of ChildStdErr that we're using.
415
+ CloseHandle(hChildStderrRd);
416
+
417
+ if(mode & _O_TEXT){
418
+ m1 = "r";
419
+ m2 = "w";
420
+ }
421
+ else{
422
+ m1 = "rb";
423
+ m2 = "wb";
424
+ }
425
+
426
+ // Convert HANDLE's into file descriptors and, ultimately, Ruby IO objects.
427
+ fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
428
+ f1 = _fdopen(fd1, m2);
429
+
430
+ fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
431
+ f2 = _fdopen(fd2, m1);
432
+
433
+ fd3 = _open_osfhandle((long)hChildStderrRdDup, mode);
434
+ f3 = _fdopen(fd3, m1);
435
+
436
+ file_count = 3;
437
+
438
+ if(!RubyCreateProcess(cmdstring,hChildStdinRd,hChildStdoutWr,
439
+ hChildStderrWr, &hProcess, &pid, v_show_window))
440
+ {
441
+ return Qnil;
442
+ }
443
+
444
+ // I think this is only possible on Win9x, but we'll leave it here
445
+ if(pid < 0){
446
+ pid = -pid;
447
+ }
448
+
449
+ modef = rb_io_mode_flags(m2);
450
+ p1 = io_alloc(rb_cIO);
451
+ MakeOpenFile(p1, fptr);
452
+
453
+ fptr->finalize = win32_pipe_finalize;
454
+ fptr->mode = modef;
455
+ fptr->pid = pid;
456
+
457
+ if(modef & FMODE_READABLE){
458
+ fptr->f = f1;
459
+ }
460
+
461
+ if(modef & FMODE_WRITABLE){
462
+ if(fptr->f){
463
+ fptr->f2 = f1;
464
+ }
465
+ else{
466
+ fptr->f = f1;
467
+ }
468
+ fptr->mode |= FMODE_SYNC;
469
+ }
470
+
471
+ modef = rb_io_mode_flags(m1);
472
+ p2 = io_alloc(rb_cIO);
473
+ MakeOpenFile(p2, fptr);
474
+
475
+ pid_handle = hProcess;
476
+ fptr->finalize = win32_pipe_finalize;
477
+ fptr->mode = modef;
478
+ fptr->pid = pid;
479
+
480
+ if(modef & FMODE_READABLE){
481
+ fptr->f = f2;
482
+ }
483
+ if(modef & FMODE_WRITABLE){
484
+ if(fptr->f){
485
+ fptr->f2 = f2;
486
+ }
487
+ else{
488
+ fptr->f = f2;
489
+ }
490
+ fptr->mode |= FMODE_SYNC;
491
+ }
492
+
493
+ modef = rb_io_mode_flags(m1);
494
+ p3 = io_alloc(rb_cIO);
495
+ MakeOpenFile(p3, fptr);
496
+
497
+ fptr->finalize = win32_pipe_finalize;
498
+ fptr->mode = modef;
499
+ fptr->pid = pid;
500
+
501
+ if(modef & FMODE_READABLE){
502
+ fptr->f = f3;
503
+ }
504
+
505
+ if(modef & FMODE_WRITABLE){
506
+ if(fptr->f){
507
+ fptr->f2 = f3;
508
+ }
509
+ else{
510
+ fptr->f = f3;
511
+ }
512
+ fptr->mode |= FMODE_SYNC;
513
+ }
514
+
515
+ port = rb_ary_new2(4);
516
+ rb_ary_push(port,(VALUE)p1);
517
+ rb_ary_push(port,(VALUE)p2);
518
+ rb_ary_push(port,(VALUE)p3);
519
+ rb_ary_push(port,UINT2NUM((DWORD)pid));
520
+
521
+ /* Child is launched. Close the parents copy of those pipe
522
+ * handles that only the child should have open. You need to
523
+ * make sure that no handles to the write end of the output pipe
524
+ * are maintained in this process or else the pipe will not close
525
+ * when the child process exits and the ReadFile() will hang.
526
+ */
527
+
528
+ if(!CloseHandle(hChildStdinRd)){
529
+ rb_raise(rb_eRuntimeError, "CloseHandle() failed: %s",
530
+ ErrorDescription(GetLastError())
531
+ );
532
+ }
533
+
534
+ if(!CloseHandle(hChildStdoutWr)){
535
+ rb_raise(rb_eRuntimeError, "CloseHandle() failed: %s",
536
+ ErrorDescription(GetLastError())
537
+ );
538
+ }
539
+
540
+ if(!CloseHandle(hChildStderrWr)){
541
+ rb_raise(rb_eRuntimeError, "CloseHandle() failed: %s",
542
+ ErrorDescription(GetLastError())
543
+ );
544
+ }
545
+
546
+ return port;
547
+ }
548
+
549
+ /*
550
+ * Spawn an external program and retrieve the stdin, stdout and stderr IO
551
+ * handles, as well as the process ID. Use in block or non-block form.
552
+ */
553
+ void Init_open3()
554
+ {
555
+ VALUE mOpen3 = rb_define_module("Open3");
556
+
557
+ VALUE mOpen4 = rb_define_module("Open4");
558
+
559
+ rb_define_module_function(mOpen3, "popen3", win32_popen3, -1);
560
+ rb_define_module_function(mOpen4, "popen4", win32_popen3, -1);
561
+
562
+ /* 0.3.1: The version of this library */
563
+ rb_define_const(mOpen3, "WIN32_OPEN3_VERSION", rb_str_new2(WIN32_OPEN3_VERSION));
564
+
565
+ /* 0.3.1: The version of this library */
566
+ rb_define_const(mOpen4, "WIN32_OPEN3_VERSION", rb_str_new2(WIN32_OPEN3_VERSION));
567
+ }
Binary file
@@ -0,0 +1,113 @@
1
+ ###########################################################################
2
+ # test_win32_open3.rb
3
+ #
4
+ # Test suite for the win32-open3 library. Except for the
5
+ # 'test_open3_with_arguments' test and Open4 tests, this suite passes
6
+ # on Unix as well.
7
+ #
8
+ # You should run this test suite via the 'rake test' task.
9
+ ###########################################################################
10
+ require 'win32/open3'
11
+ require 'test/unit'
12
+
13
+ class TC_Win32_Open3 < Test::Unit::TestCase
14
+ def setup
15
+ @good_cmd = 'ver'
16
+ @bad_cmd = 'verb'
17
+ @stdin = nil
18
+ @stdout = nil
19
+ @stderr = nil
20
+ @value = nil
21
+ end
22
+
23
+ def test_open3_version
24
+ assert_equal('0.3.1', Open3::WIN32_OPEN3_VERSION)
25
+ assert_equal('0.3.1', Open4::WIN32_OPEN3_VERSION)
26
+ end
27
+
28
+ def test_open3_basic
29
+ assert_respond_to(Open3, :popen3)
30
+ assert_nothing_raised{ Open3.popen3(@good_cmd) }
31
+ assert_nothing_raised{ Open3.popen3(@bad_cmd) }
32
+ end
33
+
34
+ def test_open4_basic
35
+ assert_respond_to(Open4, :popen4)
36
+ assert_nothing_raised{ Open4.popen4(@good_cmd) }
37
+ assert_nothing_raised{ Open4.popen4(@bad_cmd) }
38
+ end
39
+
40
+ # This test would fail on other platforms
41
+ def test_open3_with_arguments
42
+ assert_nothing_raised{ Open3.popen3(@good_cmd, 't') }
43
+ assert_nothing_raised{ Open3.popen3(@bad_cmd, 't') }
44
+ assert_nothing_raised{ Open3.popen3(@good_cmd, 'b') }
45
+ assert_nothing_raised{ Open3.popen3(@bad_cmd, 'b') }
46
+ assert_nothing_raised{ Open3.popen3(@good_cmd, 't', false) }
47
+ assert_nothing_raised{ Open3.popen3(@good_cmd, 't', true) }
48
+ end
49
+
50
+ def test_open3_handles
51
+ arr = Open3.popen3(@good_cmd)
52
+ assert_kind_of(Array, arr)
53
+ assert_kind_of(IO, arr[0])
54
+ assert_kind_of(IO, arr[1])
55
+ assert_kind_of(IO, arr[2])
56
+ end
57
+
58
+ def test_open3_block
59
+ assert_nothing_raised{ Open3.popen3(@good_cmd){ |pin, pout, perr| } }
60
+ Open3.popen3(@good_cmd){ |pin, pout, perr|
61
+ assert_kind_of(IO, pin)
62
+ assert_kind_of(IO, pout)
63
+ assert_kind_of(IO, perr)
64
+ }
65
+ end
66
+
67
+ def test_open4_block
68
+ assert_nothing_raised{ Open4.popen4(@good_cmd){ |pin, pout, perr, pid| } }
69
+ Open4.popen4(@good_cmd){ |pin, pout, perr, pid|
70
+ assert_kind_of(IO, pin)
71
+ assert_kind_of(IO, pout)
72
+ assert_kind_of(IO, perr)
73
+ assert_kind_of(Fixnum, pid)
74
+ }
75
+ end
76
+
77
+ def test_open4_return_values
78
+ arr = Open4.popen4(@good_cmd)
79
+ assert_kind_of(Array,arr)
80
+ assert_kind_of(IO, arr[0])
81
+ assert_kind_of(IO, arr[1])
82
+ assert_kind_of(IO, arr[2])
83
+ assert_kind_of(Fixnum, arr[3])
84
+ end
85
+
86
+ def test_handle_good_content
87
+ fin, fout, ferr = Open3.popen3(@good_cmd)
88
+ assert_kind_of(String, fout.gets)
89
+ assert_nil(ferr.gets)
90
+ end
91
+
92
+ def test_handle_bad_content
93
+ fin, fout, ferr = Open3.popen3(@bad_cmd)
94
+ assert_kind_of(String, ferr.gets)
95
+ assert_nil(fout.gets)
96
+ end
97
+
98
+ def test_exit_status_in_block_form_popen4
99
+ assert_nothing_raised{ @value = Open3.popen3('dir'){ |x,y,z| 1 } }
100
+ assert_equal(1, @value)
101
+ end
102
+
103
+ def test_exit_status_in_block_form_popen4
104
+ assert_nothing_raised{ @value = Open4.popen4('dir'){ |x,y,z,a| 1 } }
105
+ assert_equal(1, @value)
106
+ end
107
+
108
+ def teardown
109
+ @good_cmd = nil
110
+ @bad_cmd = nil
111
+ @value = nil
112
+ end
113
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'win32-open3'
5
+ spec.version = '0.3.2'
6
+ spec.authors = ['Park Heesob', 'Daniel J. Berger']
7
+ spec.email = 'djberg96@gmail.com'
8
+ spec.homepage = 'http://www.rubyforge.org/projects/win32utils'
9
+ spec.platform = Gem::Platform::RUBY
10
+ spec.summary = 'Provides an Open3.popen3 implementation for MS Windows'
11
+ spec.test_file = 'test/test_win32_open3.rb'
12
+ spec.has_rdoc = true
13
+ spec.license = 'Artistic 2.0'
14
+ spec.extensions = ['ext/extconf.rb']
15
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
16
+
17
+ spec.extra_rdoc_files = [
18
+ 'CHANGES',
19
+ 'README',
20
+ 'MANIFEST',
21
+ 'doc/open3.txt',
22
+ 'ext/win32/open3.c'
23
+ ]
24
+
25
+ spec.rubyforge_project = 'win32utils'
26
+ spec.required_ruby_version = '< 1.9.0'
27
+
28
+ spec.description = <<-EOF
29
+ The win32-open3 library provides a working implementation of the open3
30
+ library for MS Windows. In addition, it provides the Open4 class, which
31
+ also returns pid information.
32
+
33
+ Note that this library is unnecessary with Ruby 1.9.x because of its
34
+ support for native threads.
35
+ EOF
36
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: win32-open3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.2
5
+ platform: x86-mingw32
6
+ authors:
7
+ - Park Heesob
8
+ - Daniel J. Berger
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2010-01-23 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: " The win32-open3 library provides a working implementation of the open3\n library for MS Windows. In addition, it provides the Open4 class, which\n also returns pid information.\n \n Note that this library is unnecessary with Ruby 1.9.x because of its\n support for native threads.\n"
18
+ email: djberg96@gmail.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - CHANGES
25
+ - README
26
+ - MANIFEST
27
+ - doc/open3.txt
28
+ - ext/win32/open3.c
29
+ files:
30
+ - CHANGES
31
+ - doc/open3.txt
32
+ - examples/win32_open3_example.rb
33
+ - lib/win32/open3.so
34
+ - MANIFEST
35
+ - Rakefile
36
+ - README
37
+ - test/test_win32_open3.rb
38
+ - win32-open3.gemspec
39
+ - ext/win32/open3.c
40
+ has_rdoc: true
41
+ homepage: http://www.rubyforge.org/projects/win32utils
42
+ licenses:
43
+ - Artistic 2.0
44
+ post_install_message:
45
+ rdoc_options: []
46
+
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - <
52
+ - !ruby/object:Gem::Version
53
+ version: 1.9.0
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project: win32utils
64
+ rubygems_version: 1.3.5
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Provides an Open3.popen3 implementation for MS Windows
68
+ test_files:
69
+ - test/test_win32_open3.rb