COVERAGE SUMMARY
FILE SUMMARY
NameExecutedRoutines%ExecutedLines%Unexecuted
/home/matt/eu/rds/include/std/pipeio.e101662.50%9416556.97%71
ROUTINE SUMMARY
RoutineExecutedLinesUnexecuted
exec_args()133438.24%21
os_execv()0120.00%12
create()81747.06%9
os_dup2()060.00%6
error()030.00%3
get_errno()030.00%3
read()121580.00%3
close()5771.43%2
error_no()020.00%2
os_fork()4666.67%2
os_pipe()81080.00%2
os_signal()020.00%2
process()4666.67%2
write()81080.00%2
exec()22100.00%0
kill()77100.00%0
LINE COVERAGE DETAIL
#Executed
1
namespace pipeio
2
3
--****
4
-- === Pipe Input/Output
5
--
6
-- ==== Notes
7
-- Due to a bug, Euphoria does not handle STDERR properly
8
-- STDERR cannot captured for Euphoria programs (other programs will work fully)
9
-- The IO functions currently work with file handles, a future version might wrap them in streams
10
-- so that they can be used directly alongside other file/socket/other-streams with a
11
-- stream_select() function.
12
--
13
14
--Includes
15
include std/dll.e
16
include std/machine.e
17
include std/error.e
18
192
ifdef WIN32 then
20
constant
21
kernel32 = open_dll("kernel32.dll"),
22
--iGetExitCodeProcess=define_c_func(kernel32,"GetExitCodeProcess",{C_UINT,C_POINTER},C_INT),
23
iCreatePipe = define_c_func(kernel32,"CreatePipe",{C_POINTER,C_POINTER,C_POINTER,C_DWORD},C_BOOL),
24
iReadFile = define_c_func(kernel32,"ReadFile",{C_UINT,C_POINTER,C_DWORD,C_POINTER,C_POINTER},C_BOOL),
25
iWriteFile = define_c_func(kernel32,"WriteFile",{C_UINT,C_POINTER,C_DWORD,C_POINTER,C_POINTER},C_BOOL),
26
iCloseHandle = define_c_func(kernel32,"CloseHandle",{C_UINT},C_BOOL),
27
iTerminateProcess=define_c_func(kernel32,"TerminateProcess",{C_UINT,C_UINT},C_BOOL),
28
iGetLastError = define_c_func(kernel32,"GetLastError",{},C_DWORD),
29
iGetStdHandle = define_c_func(kernel32,"GetStdHandle",{C_DWORD},C_UINT),
30
iSetHandleInformation = define_c_func(kernel32,"SetHandleInformation",{C_UINT,C_DWORD,C_DWORD},C_BOOL),
31
iCreateProcess = define_c_func(kernel32,"CreateProcessA",{C_POINTER,C_POINTER,C_POINTER,
32
C_POINTER,C_BOOL,C_DWORD,C_POINTER,C_POINTER,C_POINTER,C_POINTER},C_BOOL)
33
34
constant
35
-- STD_INPUT_HANDLE = -10,
36
-- STD_OUTPUT_HANDLE = -11,
37
-- STD_ERROR_HANDLE = -12,
38
-- FILE_INVALID_HANDLE = -1,
39
-- ERROR_BROKEN_PIPE = 109,
40
SA_SIZE = 12,
41
PIPE_WRITE_HANDLE = 1, PIPE_READ_HANDLE=2,
42
HANDLE_FLAG_INHERIT=1,
43
SUIdwFlags = 44,
44
SUIhStdInput = 56,
45
STARTUPINFO_SIZE = 68,
46
STARTF_USESHOWWINDOW = 1,
47
STARTF_USESTDHANDLES = 256,
48
PROCESS_INFORMATION_SIZE = 16,
49
FAIL = 0
50
51
elsedef
52
--*NIX-specific constants
53
constant
542
STDLIB = open_dll({ "libc.so", "libc.dylib", "" }),
552
PIPE = define_c_func(STDLIB, "pipe", {C_POINTER}, C_INT),
562
READ = define_c_func(STDLIB, "read", {C_INT, C_POINTER, C_INT}, C_INT),
572
WRITE = define_c_func(STDLIB, "write", {C_INT, C_POINTER, C_INT}, C_INT),
582
CLOSE = define_c_func(STDLIB, "close", {C_INT}, C_INT),
592
DUP2 = define_c_func(STDLIB, "dup2", {C_INT, C_INT}, C_INT),
602
KILL = define_c_func(STDLIB, "kill", {C_INT, C_INT}, C_INT),
612
FORK = define_c_func(STDLIB, "fork", {}, C_INT),
622
EXECV = define_c_func(STDLIB, "execv", {C_POINTER, C_POINTER}, C_INT),
632
SIGNAL = define_c_func(STDLIB, "signal", {C_INT, C_POINTER}, C_POINTER),
642
ERRNO = define_c_var( STDLIB, "errno"),
652
FAIL = -1
66
67
enum
682
os_stdin = 0, os_stdout, os_stderr,
692
os_sig_dfl = 0, os_sig_ign
70
end ifdef
71
72
--****
73
-- === Accessor Constants
74
75
public enum
76
--** Child processes standard input
772
STDIN,
78
--** Child processes standard output
792
STDOUT,
80
--** Child processes standard error
812
STDERR,
82
--** Process ID
832
PID
84
85
--
86
public enum
87
--** Set of pipes that are for the use of the parent
882
PARENT,
89
--** Set of pipes that are given to the child - should not be used by the parent
902
CHILD
91
92
--
932
atom os_errno = 0
94
95
-- Common functions
960
970
ifdef WIN32 then
98
return c_func(iGetLastError,{})
99
elsedef
1000
return peek4u(ERRNO)
101
end ifdef
102
end function
103
104
--****
105
-- === Opening/Closing
106
107
--**
108
-- Process Type
109
1102
1112
if atom(o) then
1120
return 0
113
end if
114
1152
if length(o) != 4 then
1160
return 0
117
end if
118
1192
return 1
120
end type
121
122
--**
123
-- Close handle fd
124
--
125
-- Returns:
126
-- An **integer**, 0 on success, -1 on failure
127
--
128
-- Example 1:
129
--
130
-- integer status = pipeio:close(p[STDIN])
131
--
132
--
133
13412
135
atom ret
136
13712
ifdef WIN32 then
138
ret=c_func(iCloseHandle,{fd})
139
elsedef
14012
ret=c_func(CLOSE, {fd})
141
end ifdef
142
14312
if ret = FAIL then
1440
os_errno = get_errno()
1450
return -1
146
end if
147
14812
return 0
149
end function
150
151
--**
152
-- Close pipes and kill process p with signal signal (default 15)
153
--
154
-- Comments:
155
-- Signal is ignored on Windows.
156
--
157
-- Example 1:
158
--
159
-- kill(p)
160
--
161
--
162
1632
164
atom ret
165
166
--Close the pipes
167
--If any fail its probably just because they were already closed, so that is ignored
1682
ret=close(p[STDIN])
1692
ret=close(p[STDOUT])
1702
ret=close(p[STDERR])
171
172
--Error may result, but it is usually just because the process has already ended.
1732
ifdef WIN32 then
174
--Not how to handle "signal", so its ignored on Windows for now
175
ret=c_func(iTerminateProcess,{p[PID],signal and 0})
176
elsedef
1772
ret=c_func(KILL, {p[PID], signal})
178
end ifdef
1792
end procedure
180
1816
182
sequence handles
183
atom ret
184
1856
ifdef WIN32 then
186
atom psaAttrib, phWriteToPipe, phReadFromPipe
187
188
psaAttrib = allocate(SA_SIZE+2*4)
189
poke4(psaAttrib,{SA_SIZE,0,1})
190
phWriteToPipe = psaAttrib+SA_SIZE
191
phReadFromPipe = psaAttrib+SA_SIZE+4
192
ret = c_func(iCreatePipe,{phReadFromPipe,phWriteToPipe,psaAttrib,0})
193
handles = peek4u({phWriteToPipe,2})
194
free(psaAttrib)
195
elsedef
1966
atom cmd = allocate(8)
1976
ret = c_func(PIPE,{cmd})
1986
handles = peek4u({cmd,2})
1996
free(cmd)
200
end ifdef
201
2026
if ret = FAIL then
2030
os_errno = get_errno()
2040
return -1
205
end if
206
2076
return handles
208
end function
209
210
--****
211
-- === Read/Write Process
212
213
--**
214
-- Read ##bytes## bytes from handle ##fd##
215
--
216
-- Returns:
217
-- A **sequence**, containing data, an empty sequence on EOF or an error code.
218
-- Similar to [[:get_bytes]].
219
--
220
-- Example 1:
221
--
222
-- sequence data=read(p[STDOUT],256)
223
--
224
--
225
2261
2271
if bytes=0 then return "" end if
228
2291
sequence data
2301
atom
231
ret, ReadCount,
2321
buf = allocate(bytes)
233
2341
ifdef WIN32 then
235
atom pReadCount=allocate(4)
236
ret = c_func(iReadFile,{fd,buf,bytes,pReadCount,0})
237
ReadCount=peek4u(pReadCount)
238
free(pReadCount)
239
elsedef
2401
ret = c_func(READ, {fd, buf, bytes})
2411
ReadCount=ret
242
end ifdef
243
2441
if ret = FAIL then
2450
os_errno = get_errno()
2460
free(buf)
2470
return ""
248
end if
249
2501
data=peek({buf,ReadCount})
251
2521
free(buf)
253
2541
return data
255
end function
256
257
--****
258
-- Write ##bytes## to handle ##fd##
259
--
260
-- Returns:
261
-- A **integer**, number of bytes written, or -1 on error
262
--
263
-- Example 1:
264
--
265
-- integer bytes_written = write(p[STDIN],"Hello World!")
266
--
267
--
268
2691
270
atom
271
-- fd = p[2],
2721
buf = allocate_string(str),
273
ret,WrittenCount
274
2751
ifdef WIN32 then
276
atom pWrittenCount=allocate(4)
277
ret=c_func(iWriteFile,{fd,buf,length(str),pWrittenCount,0})
278
WrittenCount=peek4u(pWrittenCount)
279
free(pWrittenCount)
280
elsedef
2811
ret = c_func(WRITE, {fd, buf, length(str)})
2821
WrittenCount=ret
283
end ifdef
284
2851
free(buf)
286
2871
if ret = FAIL then
2880
os_errno = get_errno()
2890
return -1
290
end if
291
2921
return WrittenCount
293
end function
294
2950
2960
crash(sprintf("Errno = %d", os_errno))
2970
end procedure
298
299
--**
300
-- Get error no from last call to a pipe function
301
--
302
-- Comments:
303
-- Value returned will be OS-specific, and is not always set on Windows at least
304
--
305
-- Example 1:
306
--
307
-- integer error = error_no()
308
--
309
--
3100
3110
return os_errno
312
end function
313
3142
ifdef WIN32 then
315
--WIN32-specific functions
316
function GetStdHandle(atom device)
317
return c_func(iGetStdHandle,{device})
318
end function
319
320
function SetHandleInformation(atom hObject, atom dwMask, atom dwFlags)
321
return c_func(iSetHandleInformation,{hObject,dwMask,dwFlags})
322
end function
323
324
procedure CloseAllHandles(sequence handles)
325
atom ret
326
for i = 1 to length(handles) do
327
ret=close(handles[i])
328
end for
329
end procedure
330
331
function CreateProcess(sequence CommandLine,sequence StdHandles)
332
object fnVal
333
atom pPI, pSUI, pCmdLine
334
sequence ProcInfo
335
336
pCmdLine = allocate_string(CommandLine)
337
pPI = allocate(PROCESS_INFORMATION_SIZE)
338
mem_set(pPI,0,PROCESS_INFORMATION_SIZE)
339
pSUI = allocate(STARTUPINFO_SIZE)
340
mem_set(pSUI,0,STARTUPINFO_SIZE)
341
poke4(pSUI,STARTUPINFO_SIZE)
342
poke4(pSUI+SUIdwFlags,or_bits(STARTF_USESTDHANDLES,STARTF_USESHOWWINDOW))
343
poke4(pSUI+SUIhStdInput,StdHandles)
344
fnVal = c_func(iCreateProcess,{0,pCmdLine,0,0,1,0,0,0,pSUI,pPI})
345
free(pCmdLine)
346
free(pSUI)
347
ProcInfo = peek4u({pPI,4})
348
free(pPI)
349
if not fnVal then
350
return 0
351
end if
352
return ProcInfo
353
end function -- CreateProcess()
354
355
--WIN32 version of create()
356
357
--**
358
-- Create pipes for inter-process communication
359
--
360
-- Returns:
361
-- A **handle**, process handles { {parent side pipes},{child side pipes} }
362
--
363
-- Example 1:
364
--
365
-- object p = exec("dir", create())
366
--
367
--
368
public function create()
369
atom hChildStdInRd,hChildStdOutWr, hChildStdErrWr, -- handles used by child process
370
hChildStdInWr, hChildStdOutRd,hChildStdErrRd -- handles used by parent process
371
372
object
373
StdInPipe = {},
374
StdOutPipe = {},
375
StdErrPipe = {}
376
377
object fnVal
378
379
-- capture child process std input
380
StdInPipe = os_pipe()
381
if atom(StdInPipe) then return -1 end if
382
hChildStdInRd = StdInPipe[PIPE_READ_HANDLE]
383
hChildStdInWr = StdInPipe[PIPE_WRITE_HANDLE]
384
385
386
-- capture child process std output
387
StdOutPipe = os_pipe()
388
if atom(StdOutPipe) then
389
CloseAllHandles(StdInPipe)
390
return -1
391
end if
392
hChildStdOutWr = StdOutPipe[PIPE_WRITE_HANDLE]
393
hChildStdOutRd = StdOutPipe[PIPE_READ_HANDLE]
394
395
-- capture child process std error
396
StdErrPipe = os_pipe()
397
if atom(StdErrPipe) then
398
CloseAllHandles(StdErrPipe & StdOutPipe)
399
return -1
400
end if
401
hChildStdErrWr = StdErrPipe[PIPE_WRITE_HANDLE]
402
hChildStdErrRd = StdErrPipe[PIPE_READ_HANDLE]
403
404
fnVal = SetHandleInformation(StdInPipe[PIPE_WRITE_HANDLE],HANDLE_FLAG_INHERIT,0)
405
fnVal = SetHandleInformation(StdOutPipe[PIPE_READ_HANDLE],HANDLE_FLAG_INHERIT,0)
406
fnVal = SetHandleInformation(StdErrPipe[PIPE_READ_HANDLE],HANDLE_FLAG_INHERIT,0)
407
408
return {{hChildStdInWr,hChildStdOutRd,hChildStdErrRd},
409
{hChildStdInRd,hChildStdOutWr,hChildStdErrWr}}
410
411
end function
412
413
--WIN32 version of exec()
414
415
--**
416
-- Open process with command line cmd
417
--
418
-- Returns:
419
-- A **handle**, process handles { [[:PID]], [[:STDIN]], [[:STDOUT]], [[:STDERR]] }
420
--
421
-- Example 1:
422
--
423
-- object p = exec("dir", create())
424
--
425
--
426
427
public function exec(sequence cmd, sequence pipe)
428
object fnVal
429
atom hChildStdInRd,hChildStdOutWr, hChildStdErrWr, -- handles used by child process
430
hChildStdInWr, hChildStdOutRd,hChildStdErrRd -- handles used by parent process
431
atom ret
432
433
hChildStdInWr = pipe[1][1]
434
hChildStdOutRd = pipe[1][2]
435
hChildStdErrRd = pipe[1][3]
436
hChildStdInRd = pipe[2][1]
437
hChildStdOutWr = pipe[2][2]
438
hChildStdErrWr = pipe[2][3]
439
440
atom hChildProcess
441
442
-- create child process
443
fnVal = CreateProcess(cmd,{hChildStdInRd,hChildStdOutWr,hChildStdErrWr})
444
if atom(fnVal) then
445
return -1
446
end if
447
hChildProcess = fnVal[1]
448
ret=close(fnVal[2]) -- hChildThread not needed.
449
450
ret=close(hChildStdInRd)
451
452
ret=close(hChildStdOutWr)
453
454
ret=close(hChildStdErrWr)
455
456
return {hChildStdInWr,hChildStdOutRd,hChildStdErrRd,hChildProcess}
457
458
end function
459
460
elsedef
461
462
--*NIX-specific functions
4630
4640
atom r = c_func(DUP2, {oldfd, newfd})
4650
if r = -1 then
4660
os_errno = peek4u(ERRNO)
4670
return -1
468
end if
469
4700
return r
471
end function
472
4732
4742
atom pid = c_func(FORK, {})
4752
if pid = -1 then
4760
os_errno = peek4u(ERRNO)
4770
return -1
478
end if
479
4802
return pid
481
end function
482
4830
484
atom sbuf
485
atom vbuf
486
sequence vbufseq
487
atom r
488
4890
sbuf = allocate_string(s)
4900
vbufseq = {sbuf}--http://www.cs.toronto.edu/~demke/369S.07/OS161_man/syscall/execv.html
491
4920
for i = 1 to length(v) do
4930
vbufseq &= allocate_string(v[i])
4940
end for
495
4960
vbufseq &= 0
4970
vbuf = allocate(length(vbufseq)*4)
4980
poke4(vbuf, vbufseq)
4990
r = c_func(EXECV, {sbuf, vbuf}) -- execv() should never return
5000
os_errno = peek4u(ERRNO)
5010
return -1
502
end function
503
5040
5050
return c_func(SIGNAL, {signal, handler})
506
end function
507
508
--*NIX version of create()
509
510
--See docs above in WIN32 version
5112
512
object ipipe,opipe,epipe
513
integer ret
514
515
--Create pipes
5162
ipipe=os_pipe()
5172
if atom(ipipe) then
5180
return -1
519
end if
520
5212
opipe=os_pipe()
5222
if atom(opipe) then
5230
ret=close(ipipe[1])
5240
ret=close(ipipe[2])
5250
return -1
526
end if
527
5282
epipe=os_pipe()
5292
if atom(epipe) then
5300
ret=close(ipipe[1])
5310
ret=close(ipipe[2])
5320
ret=close(opipe[1])
5330
ret=close(opipe[2])
5340
return -1
535
end if
5362
return {{ipipe[2],opipe[1],epipe[1]},{ipipe[1],opipe[2],epipe[2]}}
537
end function
538
539
--Linux takes parameters as a sequence of args,
540
--so this is wrapped in a function below to make it compatible with the Windows implementation
5412
542
atom pid
543
integer ret
544
sequence p
545
object ipipe,opipe,epipe
546
5472
ipipe = pipe[2][1] & pipe[1][1]
5482
opipe = pipe[1][2] & pipe[2][2]
5492
epipe = pipe[1][3] & pipe[2][3]
550
551
--Fork
5522
pid=os_fork()
553
5542
if pid=0 then
555
--Child process
556
557
--Not much can really be done about errors at this stage,
558
--so most are left unchecked
559
560
--Close the sides we don't need, otherwise they will be left hanging
5610
ret=close(ipipe[2])
5620
ret=close(opipe[1])
5630
ret=close(epipe[1])
564
565
--What does this do?
5660
ret=os_signal(15, os_sig_dfl)--15 = sigterm
567
568
--dup our pipe descriptors to STD*, then close them so they aren't left hanging
5690
ret=os_dup2(ipipe[1], os_stdin)
5700
ret=close(ipipe[1])
571
5720
ret=os_dup2(opipe[2], os_stdout)
5730
ret=close(opipe[2])
574
5750
ret=os_dup2(epipe[2], os_stderr)
5760
ret=close(epipe[2])
577
578
--Replace the forked child process with the process we intend to launch
5790
ret=os_execv(command,args)
580
581
--We should never reach this, so its an error no matter what happens
5820
error()
5832
elsif pid=-1 then
584
--Failed to fork
585
--Close all the descriptors
5860
ret=close(ipipe[1])
5870
ret=close(ipipe[2])
5880
ret=close(opipe[1])
5890
ret=close(opipe[2])
5900
ret=close(epipe[1])
5910
ret=close(epipe[2])
5920
return -1
593
else
594
--Parent process
595
596
--Process info
5972
p={ipipe[2], opipe[1], epipe[1], pid}
598
599
--Close the sides we don't need, otherwise they will be left hanging
6002
ret=close(ipipe[1])
6012
ret=close(opipe[2]) or ret
6022
ret=close(epipe[2]) or ret
603
604
--If any failed to close, something is wrong with them, so bail out
6052
if ret then
6060
kill(p)
6070
return -1
608
end if
609
6102
return p
611
end if
612
end function
613
614
--*NIX version of exec()
615
616
--See docs above in WIN32 version
6172
618
--*NIX needs exe and args separated,
619
--but for Windows compatibility, we need to accept a command line
620
621
--PHP's proc_open() does it this way.
622
--If there is a better way, please fix it.
623
--Need to make sure this works on all *NIX platforms
6242
return exec_args("/bin/sh",{"-c", cmd}, pipe)
625
end function
626
end ifdef