win32-open3 0.3.1-x86-mswin32-60 → 0.3.2-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
data/ext/win32/open3.c CHANGED
@@ -1,546 +1,567 @@
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
- return rb_ensure(rb_yield_splat, v_port, io_close, v_port);
140
- }
141
-
142
- return v_port;
143
- }
144
-
145
- static BOOL RubyCreateProcess(char *cmdstring, HANDLE hStdin, HANDLE hStdout,
146
- HANDLE hStderr, HANDLE *hProcess, pid_t *pid, VALUE v_show_window)
147
- {
148
- PROCESS_INFORMATION piProcInfo;
149
- STARTUPINFO siStartInfo;
150
- char *s1,*s2, *s3 = " /c ";
151
- int i, x;
152
-
153
- if(i = GetEnvironmentVariable("COMSPEC", NULL, 0)){
154
- char *comshell;
155
- s1 = (char *)_alloca(i);
156
-
157
- if(!(x = GetEnvironmentVariable("COMSPEC", s1, i)))
158
- return x;
159
-
160
- /* Explicitly check if we are using COMMAND.COM. If we are
161
- * then use the w9xpopen hack.
162
- */
163
- comshell = s1 + x;
164
-
165
- while(comshell >= s1 && *comshell != '\\')
166
- --comshell;
167
-
168
- ++comshell;
169
-
170
- // Windows 95, 98 and ME are not supported
171
- if(GetVersion() < 0x80000000 && _stricmp(comshell, "command.com") != 0){
172
- x = i + strlen(s3) + strlen(cmdstring) + 1;
173
- s2 = ALLOCA_N(char, x);
174
- sprintf(s2, "%s%s%s", s1, s3, cmdstring);
175
- }
176
- else{
177
- rb_raise(rb_eRuntimeError,"not supported on this platform");
178
- }
179
- }
180
- else{
181
- // Could be an else here to try cmd.exe / command.com in the path.
182
- // Now we'll just error out..
183
- rb_raise(rb_eRuntimeError,
184
- "Cannot locate a COMSPEC environment variable to use as the shell"
185
- );
186
- return FALSE;
187
- }
188
-
189
- ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
190
-
191
- siStartInfo.cb = sizeof(STARTUPINFO);
192
- siStartInfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
193
- siStartInfo.hStdInput = hStdin;
194
- siStartInfo.hStdOutput = hStdout;
195
- siStartInfo.hStdError = hStderr;
196
- siStartInfo.wShowWindow = SW_HIDE;
197
-
198
- if(v_show_window == Qtrue)
199
- siStartInfo.wShowWindow = SW_SHOW;
200
-
201
- // Try the command first without COMSPEC
202
- if(CreateProcess(
203
- NULL,
204
- cmdstring,
205
- NULL,
206
- NULL,
207
- TRUE,
208
- 0,
209
- NULL,
210
- NULL,
211
- &siStartInfo,
212
- &piProcInfo)
213
- ){
214
- // Close the handles now so anyone waiting is woken.
215
- CloseHandle(piProcInfo.hThread);
216
-
217
- // Return process handle
218
- *hProcess = piProcInfo.hProcess;
219
- *pid = (pid_t)piProcInfo.dwProcessId;
220
- return TRUE;
221
- }
222
-
223
- // If that failed, try again with COMSPEC
224
- if(CreateProcess(
225
- NULL,
226
- s2,
227
- NULL,
228
- NULL,
229
- TRUE,
230
- CREATE_NEW_CONSOLE,
231
- NULL,
232
- NULL,
233
- &siStartInfo,
234
- &piProcInfo)
235
- ){
236
- // Close the handles now so anyone waiting is woken.
237
- CloseHandle(piProcInfo.hThread);
238
-
239
- // Return process handle
240
- *hProcess = piProcInfo.hProcess;
241
- *pid = (pid_t)piProcInfo.dwProcessId;
242
- return TRUE;
243
- }
244
-
245
- rb_raise(rb_eRuntimeError, "CreateProcess() failed: %s",
246
- ErrorDescription(GetLastError())
247
- );
248
-
249
- return FALSE;
250
- }
251
-
252
- /* Set the Process::Status. This code is based on patches by Samuel Tesla and
253
- * John-Mason Shackelford.
254
- */
255
- static void win32_set_last_status(const int status, const int pid)
256
- {
257
- // rb_last_status is defined in process.c in the main ruby.exe
258
- __declspec (dllimport) extern VALUE rb_last_status;
259
- VALUE klass = rb_path2class("Process::Status");
260
- VALUE process_status = rb_obj_alloc(klass);
261
- rb_iv_set(process_status, "status", INT2FIX(status << 8));
262
- rb_iv_set(process_status, "pid", INT2FIX(pid));
263
- rb_last_status = process_status;
264
- win32_last_status = process_status;
265
- }
266
-
267
- static void win32_pipe_finalize(OpenFile *file, int noraise)
268
- {
269
- int status;
270
-
271
- if(file->f){
272
- fclose(file->f);
273
- file->f = NULL;
274
- }
275
-
276
- if(file->f2){
277
- fclose(file->f2);
278
- file->f2 = NULL;
279
- }
280
-
281
- if(pid_handle != NULL){
282
- GetExitCodeProcess(pid_handle, &status);
283
-
284
- if(status != STILL_ACTIVE){
285
- CloseHandle(pid_handle);
286
- pid_handle = NULL;
287
- win32_set_last_status(status, file->pid);
288
- }
289
- }
290
- }
291
-
292
- // The following code is based off of KB: Q190351
293
- static VALUE ruby_popen(char *cmdstring, int mode, VALUE v_show_window)
294
- {
295
- HANDLE hChildStdinRd, hChildStdinWr, hChildStdoutRd, hChildStdoutWr,
296
- hChildStderrRd, hChildStderrWr, hChildStdinWrDup, hChildStdoutRdDup,
297
- hChildStderrRdDup, hProcess;
298
-
299
- pid_t pid;
300
-
301
- SECURITY_ATTRIBUTES saAttr;
302
- BOOL fSuccess;
303
- int fd1, fd2, fd3;
304
- FILE *f1, *f2, *f3;
305
- long file_count;
306
- VALUE port;
307
- VALUE p1,p2,p3;
308
- int modef;
309
- OpenFile *fptr;
310
- char *m1, *m2;
311
-
312
- saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
313
- saAttr.bInheritHandle = TRUE;
314
- saAttr.lpSecurityDescriptor = NULL;
315
-
316
- if(!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0)) {
317
- rb_raise(rb_eRuntimeError, "CreatePipe() failed: %s",
318
- ErrorDescription(GetLastError())
319
- );
320
- }
321
-
322
- /* Create new output read handle and the input write handle. Set
323
- * the inheritance properties to FALSE. Otherwise, the child inherits
324
- * the these handles; resulting in non-closeable handles to the pipes
325
- * being created.
326
- */
327
- fSuccess = DuplicateHandle(
328
- GetCurrentProcess(),
329
- hChildStdinWr,
330
- GetCurrentProcess(),
331
- &hChildStdinWrDup,
332
- 0,
333
- FALSE,
334
- DUPLICATE_SAME_ACCESS
335
- );
336
-
337
- if(!fSuccess){
338
- rb_raise(rb_eRuntimeError, "DuplicateHandle() failed: %s",
339
- ErrorDescription(GetLastError())
340
- );
341
- }
342
-
343
- // Close the inheritable version of ChildStdin that we're using
344
- CloseHandle(hChildStdinWr);
345
-
346
- if(!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0)) {
347
- rb_raise(rb_eRuntimeError, "CreatePipe() failed: %s",
348
- ErrorDescription(GetLastError())
349
- );
350
- }
351
-
352
- fSuccess = DuplicateHandle(
353
- GetCurrentProcess(),
354
- hChildStdoutRd,
355
- GetCurrentProcess(),
356
- &hChildStdoutRdDup,
357
- 0,
358
- FALSE,
359
- DUPLICATE_SAME_ACCESS
360
- );
361
-
362
- if(!fSuccess){
363
- rb_raise(rb_eRuntimeError, "DuplicateHandle() failed: %s",
364
- ErrorDescription(GetLastError())
365
- );
366
- }
367
-
368
- // Close the inheritable version of ChildStdout that we're using.
369
- CloseHandle(hChildStdoutRd);
370
-
371
- if(!CreatePipe(&hChildStderrRd, &hChildStderrWr, &saAttr, 0)) {
372
- rb_raise(rb_eRuntimeError, "CreatePipe() failed: %s",
373
- ErrorDescription(GetLastError())
374
- );
375
- }
376
-
377
- fSuccess = DuplicateHandle(
378
- GetCurrentProcess(),
379
- hChildStderrRd,
380
- GetCurrentProcess(),
381
- &hChildStderrRdDup,
382
- 0,
383
- FALSE,
384
- DUPLICATE_SAME_ACCESS
385
- );
386
-
387
- if (!fSuccess) {
388
- rb_raise(rb_eRuntimeError, "DuplicateHandle() failed: %s",
389
- ErrorDescription(GetLastError())
390
- );
391
- }
392
-
393
- // Close the inheritable version of ChildStdErr that we're using.
394
- CloseHandle(hChildStderrRd);
395
-
396
- if(mode & _O_TEXT){
397
- m1 = "r";
398
- m2 = "w";
399
- }
400
- else{
401
- m1 = "rb";
402
- m2 = "wb";
403
- }
404
-
405
- // Convert HANDLE's into file descriptors and, ultimately, Ruby IO objects.
406
- fd1 = _open_osfhandle((long)hChildStdinWrDup, mode);
407
- f1 = _fdopen(fd1, m2);
408
-
409
- fd2 = _open_osfhandle((long)hChildStdoutRdDup, mode);
410
- f2 = _fdopen(fd2, m1);
411
-
412
- fd3 = _open_osfhandle((long)hChildStderrRdDup, mode);
413
- f3 = _fdopen(fd3, m1);
414
-
415
- file_count = 3;
416
-
417
- if(!RubyCreateProcess(cmdstring,hChildStdinRd,hChildStdoutWr,
418
- hChildStderrWr, &hProcess, &pid, v_show_window))
419
- {
420
- return Qnil;
421
- }
422
-
423
- // I think this is only possible on Win9x, but we'll leave it here
424
- if(pid < 0){
425
- pid = -pid;
426
- }
427
-
428
- modef = rb_io_mode_flags(m2);
429
- p1 = io_alloc(rb_cIO);
430
- MakeOpenFile(p1, fptr);
431
-
432
- fptr->finalize = win32_pipe_finalize;
433
- fptr->mode = modef;
434
- fptr->pid = pid;
435
-
436
- if(modef & FMODE_READABLE){
437
- fptr->f = f1;
438
- }
439
-
440
- if(modef & FMODE_WRITABLE){
441
- if(fptr->f){
442
- fptr->f2 = f1;
443
- }
444
- else{
445
- fptr->f = f1;
446
- }
447
- fptr->mode |= FMODE_SYNC;
448
- }
449
-
450
- modef = rb_io_mode_flags(m1);
451
- p2 = io_alloc(rb_cIO);
452
- MakeOpenFile(p2, fptr);
453
-
454
- pid_handle = hProcess;
455
- fptr->finalize = win32_pipe_finalize;
456
- fptr->mode = modef;
457
- fptr->pid = pid;
458
-
459
- if(modef & FMODE_READABLE){
460
- fptr->f = f2;
461
- }
462
- if(modef & FMODE_WRITABLE){
463
- if(fptr->f){
464
- fptr->f2 = f2;
465
- }
466
- else{
467
- fptr->f = f2;
468
- }
469
- fptr->mode |= FMODE_SYNC;
470
- }
471
-
472
- modef = rb_io_mode_flags(m1);
473
- p3 = io_alloc(rb_cIO);
474
- MakeOpenFile(p3, fptr);
475
-
476
- fptr->finalize = win32_pipe_finalize;
477
- fptr->mode = modef;
478
- fptr->pid = pid;
479
-
480
- if(modef & FMODE_READABLE){
481
- fptr->f = f3;
482
- }
483
-
484
- if(modef & FMODE_WRITABLE){
485
- if(fptr->f){
486
- fptr->f2 = f3;
487
- }
488
- else{
489
- fptr->f = f3;
490
- }
491
- fptr->mode |= FMODE_SYNC;
492
- }
493
-
494
- port = rb_ary_new2(4);
495
- rb_ary_push(port,(VALUE)p1);
496
- rb_ary_push(port,(VALUE)p2);
497
- rb_ary_push(port,(VALUE)p3);
498
- rb_ary_push(port,UINT2NUM((DWORD)pid));
499
-
500
- /* Child is launched. Close the parents copy of those pipe
501
- * handles that only the child should have open. You need to
502
- * make sure that no handles to the write end of the output pipe
503
- * are maintained in this process or else the pipe will not close
504
- * when the child process exits and the ReadFile() will hang.
505
- */
506
-
507
- if(!CloseHandle(hChildStdinRd)){
508
- rb_raise(rb_eRuntimeError, "CloseHandle() failed: %s",
509
- ErrorDescription(GetLastError())
510
- );
511
- }
512
-
513
- if(!CloseHandle(hChildStdoutWr)){
514
- rb_raise(rb_eRuntimeError, "CloseHandle() failed: %s",
515
- ErrorDescription(GetLastError())
516
- );
517
- }
518
-
519
- if(!CloseHandle(hChildStderrWr)){
520
- rb_raise(rb_eRuntimeError, "CloseHandle() failed: %s",
521
- ErrorDescription(GetLastError())
522
- );
523
- }
524
-
525
- return port;
526
- }
527
-
528
- /*
529
- * Spawn an external program and retrieve the stdin, stdout and stderr IO
530
- * handles, as well as the process ID. Use in block or non-block form.
531
- */
532
- void Init_open3()
533
- {
534
- VALUE mOpen3 = rb_define_module("Open3");
535
-
536
- VALUE mOpen4 = rb_define_module("Open4");
537
-
538
- rb_define_module_function(mOpen3, "popen3", win32_popen3, -1);
539
- rb_define_module_function(mOpen4, "popen4", win32_popen3, -1);
540
-
541
- /* 0.3.1: The version of this library */
542
- rb_define_const(mOpen3, "WIN32_OPEN3_VERSION", rb_str_new2(WIN32_OPEN3_VERSION));
543
-
544
- /* 0.3.1: The version of this library */
545
- rb_define_const(mOpen4, "WIN32_OPEN3_VERSION", rb_str_new2(WIN32_OPEN3_VERSION));
546
- }
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
+ }