win32-open3 0.2.5

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