win32-open3 0.2.5-mswin32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
data/MANIFEST ADDED
@@ -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.
data/doc/open3.txt ADDED
@@ -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
data/ext/extconf.rb ADDED
@@ -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')
data/ext/open3.c ADDED
@@ -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
+ }
data/ext/open3.h ADDED
@@ -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
+ }
Binary file
data/test/tc_open3.rb ADDED
@@ -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,58 @@
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: mswin32
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/extconf.rb
35
+ - ext/open3.c
36
+ - ext/open3.h
37
+ - lib/win32/open3.so
38
+ - CHANGES
39
+ - README
40
+ - MANIFEST
41
+ test_files:
42
+ - test/tc_open3.rb
43
+ rdoc_options: []
44
+
45
+ extra_rdoc_files:
46
+ - CHANGES
47
+ - README
48
+ - MANIFEST
49
+ - doc/open3.txt
50
+ - ext/open3.c
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ requirements: []
56
+
57
+ dependencies: []
58
+