win32-open3 0.3.1-x86-mswin32-60 → 0.3.2-x86-mswin32-60
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 +81 -75
- data/MANIFEST +10 -10
- data/README +64 -62
- data/Rakefile +98 -0
- data/doc/open3.txt +79 -79
- data/examples/win32_open3_example.rb +34 -0
- data/ext/win32/open3.c +567 -546
- data/lib/win32/open3.so +0 -0
- data/test/test_win32_open3.rb +113 -113
- data/win32-open3.gemspec +36 -0
- metadata +15 -12
@@ -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
|
-
|
8
|
-
#
|
9
|
-
#include "
|
10
|
-
|
11
|
-
#include
|
12
|
-
#include
|
13
|
-
#
|
14
|
-
|
15
|
-
#include
|
16
|
-
|
17
|
-
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
NULL,
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
NULL,
|
232
|
-
|
233
|
-
&
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
*
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
{
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
if(!
|
338
|
-
rb_raise(rb_eRuntimeError, "
|
339
|
-
ErrorDescription(GetLastError())
|
340
|
-
);
|
341
|
-
}
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
)
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
)
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
if(
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
fptr->
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
fptr->finalize = win32_pipe_finalize;
|
477
|
-
fptr->mode = modef;
|
478
|
-
fptr->pid = pid;
|
479
|
-
|
480
|
-
if(modef & FMODE_READABLE){
|
481
|
-
fptr->f =
|
482
|
-
}
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
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
|
+
}
|