win32-open3 0.2.6-x86-mswin32-60

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