1 ------------------------------------------------------------------------------
3 -- GNAT LIBRARY COMPONENTS --
5 -- G N A T . E X P E C T --
9 -- Copyright (C) 2000-2002 Ada Core Technologies, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). --
31 ------------------------------------------------------------------------------
33 with System
; use System
;
34 with Ada
.Calendar
; use Ada
.Calendar
;
37 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
38 with GNAT
.Regpat
; use GNAT
.Regpat
;
40 with Unchecked_Deallocation
;
42 package body GNAT
.Expect
is
44 type Array_Of_Pd
is array (Positive range <>) of Process_Descriptor_Access
;
46 procedure Expect_Internal
47 (Descriptors
: in out Array_Of_Pd
;
48 Result
: out Expect_Match
;
50 Full_Buffer
: Boolean);
51 -- Internal function used to read from the process Descriptor.
53 -- Three outputs are possible:
54 -- Result=Expect_Timeout, if no output was available before the timeout
56 -- Result=Expect_Full_Buffer, if Full_Buffer is True and some characters
57 -- had to be discarded from the internal buffer of Descriptor.
58 -- Result=<integer>, indicates how many characters were added to the
59 -- internal buffer. These characters are from indexes
60 -- Descriptor.Buffer_Index - Result + 1 .. Descriptor.Buffer_Index
61 -- Process_Died is raised if the process is no longer valid.
63 procedure Reinitialize_Buffer
64 (Descriptor
: in out Process_Descriptor
'Class);
65 -- Reinitialize the internal buffer.
66 -- The buffer is deleted up to the end of the last match.
68 procedure Free
is new Unchecked_Deallocation
69 (Pattern_Matcher
, Pattern_Matcher_Access
);
71 procedure Call_Filters
72 (Pid
: Process_Descriptor
'Class;
74 Filter_On
: Filter_Type
);
75 -- Call all the filters that have the appropriate type.
76 -- This function does nothing if the filters are locked
78 ------------------------------
79 -- Target dependent section --
80 ------------------------------
82 function Dup
(Fd
: File_Descriptor
) return File_Descriptor
;
83 pragma Import
(C
, Dup
);
85 procedure Dup2
(Old_Fd
, New_Fd
: File_Descriptor
);
86 pragma Import
(C
, Dup2
);
88 procedure Kill
(Pid
: Process_Id
; Sig_Num
: Integer);
89 pragma Import
(C
, Kill
);
91 function Create_Pipe
(Pipe
: access Pipe_Type
) return Integer;
92 pragma Import
(C
, Create_Pipe
, "__gnat_pipe");
95 (Fd
: File_Descriptor
;
99 pragma Import
(C
, Read
, "read");
100 -- Read N bytes to address A from file referenced by FD. Returned value
101 -- is count of bytes actually read, which can be less than N at EOF.
103 procedure Close
(Fd
: File_Descriptor
);
104 pragma Import
(C
, Close
);
105 -- Close a file given its file descriptor.
108 (Fd
: File_Descriptor
;
112 pragma Import
(C
, Write
, "write");
113 -- Read N bytes to address A from file referenced by FD. Returned value
114 -- is count of bytes actually read, which can be less than N at EOF.
117 (Fds
: System
.Address
;
120 Is_Set
: System
.Address
) return Integer;
121 pragma Import
(C
, Poll
, "__gnat_expect_poll");
122 -- Check whether there is any data waiting on the file descriptor
123 -- Out_fd, and wait if there is none, at most Timeout milliseconds
124 -- Returns -1 in case of error, 0 if the timeout expired before
125 -- data became available.
127 -- Out_Is_Set is set to 1 if data was available, 0 otherwise.
129 function Waitpid
(Pid
: Process_Id
) return Integer;
130 pragma Import
(C
, Waitpid
, "__gnat_waitpid");
131 -- Wait for a specific process id, and return its exit code.
137 function "+" (S
: String) return GNAT
.OS_Lib
.String_Access
is
139 return new String'(S);
147 (P : GNAT.Regpat.Pattern_Matcher)
148 return Pattern_Matcher_Access
151 return new GNAT.Regpat.Pattern_Matcher'(P
);
159 (Descriptor
: in out Process_Descriptor
;
160 Filter
: Filter_Function
;
161 Filter_On
: Filter_Type
:= Output
;
162 User_Data
: System
.Address
:= System
.Null_Address
;
163 After
: Boolean := False)
165 Current
: Filter_List
:= Descriptor
.Filters
;
169 while Current
/= null and then Current
.Next
/= null loop
170 Current
:= Current
.Next
;
173 if Current
= null then
174 Descriptor
.Filters
:=
175 new Filter_List_Elem
'
176 (Filter => Filter, Filter_On => Filter_On,
177 User_Data => User_Data, Next => null);
180 new Filter_List_Elem'
181 (Filter
=> Filter
, Filter_On
=> Filter_On
,
182 User_Data
=> User_Data
, Next
=> null);
186 Descriptor
.Filters
:=
187 new Filter_List_Elem
'
188 (Filter => Filter, Filter_On => Filter_On,
189 User_Data => User_Data, Next => Descriptor.Filters);
197 procedure Call_Filters
198 (Pid : Process_Descriptor'Class;
200 Filter_On : Filter_Type)
202 Current_Filter : Filter_List;
205 if Pid.Filters_Lock = 0 then
206 Current_Filter := Pid.Filters;
208 while Current_Filter /= null loop
209 if Current_Filter.Filter_On = Filter_On then
210 Current_Filter.Filter
211 (Pid, Str, Current_Filter.User_Data);
214 Current_Filter := Current_Filter.Next;
224 (Descriptor : in out Process_Descriptor;
225 Status : out Integer)
228 Close (Descriptor.Input_Fd);
230 if Descriptor.Error_Fd /= Descriptor.Output_Fd then
231 Close (Descriptor.Error_Fd);
234 Close (Descriptor.Output_Fd);
236 -- ??? Should have timeouts for different signals
237 Kill (Descriptor.Pid, 9);
239 GNAT.OS_Lib.Free (Descriptor.Buffer);
240 Descriptor.Buffer_Size := 0;
242 Status := Waitpid (Descriptor.Pid);
245 procedure Close (Descriptor : in out Process_Descriptor) is
248 Close (Descriptor, Status);
256 (Descriptor : in out Process_Descriptor;
257 Result : out Expect_Match;
259 Timeout : Integer := 10000;
260 Full_Buffer : Boolean := False)
264 Expect (Descriptor, Result, Never_Match, Timeout, Full_Buffer);
266 Expect (Descriptor, Result, Compile (Regexp), Timeout, Full_Buffer);
271 (Descriptor : in out Process_Descriptor;
272 Result : out Expect_Match;
274 Matched : out GNAT.Regpat.Match_Array;
275 Timeout : Integer := 10000;
276 Full_Buffer : Boolean := False)
279 pragma Assert (Matched'First = 0);
282 (Descriptor, Result, Never_Match, Matched, Timeout, Full_Buffer);
285 (Descriptor, Result, Compile (Regexp), Matched, Timeout,
291 (Descriptor : in out Process_Descriptor;
292 Result : out Expect_Match;
293 Regexp : GNAT.Regpat.Pattern_Matcher;
294 Timeout : Integer := 10000;
295 Full_Buffer : Boolean := False)
297 Matched : GNAT.Regpat.Match_Array (0 .. 0);
300 Expect (Descriptor, Result, Regexp, Matched, Timeout, Full_Buffer);
304 (Descriptor : in out Process_Descriptor;
305 Result : out Expect_Match;
306 Regexp : GNAT.Regpat.Pattern_Matcher;
307 Matched : out GNAT.Regpat.Match_Array;
308 Timeout : Integer := 10000;
309 Full_Buffer : Boolean := False)
312 Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
313 Try_Until : Time := Clock + Duration (Timeout) / 1000.0;
314 Timeout_Tmp : Integer := Timeout;
317 pragma Assert (Matched'First = 0);
318 Reinitialize_Buffer (Descriptor);
321 -- First, test if what is already in the buffer matches (This is
322 -- required if this package is used in multi-task mode, since one of
323 -- the tasks might have added something in the buffer, and we don't
324 -- want other tasks to wait for new input to be available before
325 -- checking the regexps).
328 (Regexp, Descriptor.Buffer (1 .. Descriptor.Buffer_Index), Matched);
330 if Descriptor.Buffer_Index >= 1 and then Matched (0).First /= 0 then
332 Descriptor.Last_Match_Start := Matched (0).First;
333 Descriptor.Last_Match_End := Matched (0).Last;
337 -- Else try to read new input
339 Expect_Internal (Descriptors, N, Timeout_Tmp, Full_Buffer);
341 if N = Expect_Timeout or else N = Expect_Full_Buffer then
346 -- Calculate the timeout for the next turn.
347 -- Note that Timeout is, from the caller's perspective, the maximum
348 -- time until a match, not the maximum time until some output is
349 -- read, and thus can not be reused as is for Expect_Internal.
351 if Timeout /= -1 then
352 Timeout_Tmp := Integer (Try_Until - Clock) * 1000;
354 if Timeout_Tmp < 0 then
355 Result := Expect_Timeout;
361 -- Even if we had the general timeout above, we have to test that the
362 -- last test we read from the external process didn't match.
365 (Regexp, Descriptor.Buffer (1 .. Descriptor.Buffer_Index), Matched);
367 if Matched (0).First /= 0 then
369 Descriptor.Last_Match_Start := Matched (0).First;
370 Descriptor.Last_Match_End := Matched (0).Last;
376 (Descriptor : in out Process_Descriptor;
377 Result : out Expect_Match;
378 Regexps : Regexp_Array;
379 Timeout : Integer := 10000;
380 Full_Buffer : Boolean := False)
382 Patterns : Compiled_Regexp_Array (Regexps'Range);
383 Matched : GNAT.Regpat.Match_Array (0 .. 0);
386 for J in Regexps'Range loop
387 Patterns (J) := new Pattern_Matcher'(Compile
(Regexps
(J
).all));
390 Expect
(Descriptor
, Result
, Patterns
, Matched
, Timeout
, Full_Buffer
);
392 for J
in Regexps
'Range loop
398 (Descriptor
: in out Process_Descriptor
;
399 Result
: out Expect_Match
;
400 Regexps
: Compiled_Regexp_Array
;
401 Timeout
: Integer := 10000;
402 Full_Buffer
: Boolean := False)
404 Matched
: GNAT
.Regpat
.Match_Array
(0 .. 0);
407 Expect
(Descriptor
, Result
, Regexps
, Matched
, Timeout
, Full_Buffer
);
411 (Result
: out Expect_Match
;
412 Regexps
: Multiprocess_Regexp_Array
;
413 Timeout
: Integer := 10000;
414 Full_Buffer
: Boolean := False)
416 Matched
: GNAT
.Regpat
.Match_Array
(0 .. 0);
419 Expect
(Result
, Regexps
, Matched
, Timeout
, Full_Buffer
);
423 (Descriptor
: in out Process_Descriptor
;
424 Result
: out Expect_Match
;
425 Regexps
: Regexp_Array
;
426 Matched
: out GNAT
.Regpat
.Match_Array
;
427 Timeout
: Integer := 10000;
428 Full_Buffer
: Boolean := False)
430 Patterns
: Compiled_Regexp_Array
(Regexps
'Range);
433 pragma Assert
(Matched
'First = 0);
435 for J
in Regexps
'Range loop
436 Patterns
(J
) := new Pattern_Matcher
'(Compile (Regexps (J).all));
439 Expect (Descriptor, Result, Patterns, Matched, Timeout, Full_Buffer);
441 for J in Regexps'Range loop
447 (Descriptor : in out Process_Descriptor;
448 Result : out Expect_Match;
449 Regexps : Compiled_Regexp_Array;
450 Matched : out GNAT.Regpat.Match_Array;
451 Timeout : Integer := 10000;
452 Full_Buffer : Boolean := False)
455 Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
458 pragma Assert (Matched'First = 0);
460 Reinitialize_Buffer (Descriptor);
463 -- First, test if what is already in the buffer matches (This is
464 -- required if this package is used in multi-task mode, since one of
465 -- the tasks might have added something in the buffer, and we don't
466 -- want other tasks to wait for new input to be available before
467 -- checking the regexps).
469 if Descriptor.Buffer /= null then
470 for J in Regexps'Range loop
473 Descriptor.Buffer (1 .. Descriptor.Buffer_Index),
476 if Matched (0) /= No_Match then
477 Result := Expect_Match (J);
478 Descriptor.Last_Match_Start := Matched (0).First;
479 Descriptor.Last_Match_End := Matched (0).Last;
485 Expect_Internal (Descriptors, N, Timeout, Full_Buffer);
487 if N = Expect_Timeout or else N = Expect_Full_Buffer then
495 (Result : out Expect_Match;
496 Regexps : Multiprocess_Regexp_Array;
497 Matched : out GNAT.Regpat.Match_Array;
498 Timeout : Integer := 10000;
499 Full_Buffer : Boolean := False)
502 Descriptors : Array_Of_Pd (Regexps'Range);
505 pragma Assert (Matched'First = 0);
507 for J in Descriptors'Range loop
508 Descriptors (J) := Regexps (J).Descriptor;
509 Reinitialize_Buffer (Regexps (J).Descriptor.all);
513 -- First, test if what is already in the buffer matches (This is
514 -- required if this package is used in multi-task mode, since one of
515 -- the tasks might have added something in the buffer, and we don't
516 -- want other tasks to wait for new input to be available before
517 -- checking the regexps).
519 for J in Regexps'Range loop
520 Match (Regexps (J).Regexp.all,
521 Regexps (J).Descriptor.Buffer
522 (1 .. Regexps (J).Descriptor.Buffer_Index),
525 if Matched (0) /= No_Match then
526 Result := Expect_Match (J);
527 Regexps (J).Descriptor.Last_Match_Start := Matched (0).First;
528 Regexps (J).Descriptor.Last_Match_End := Matched (0).Last;
533 Expect_Internal (Descriptors, N, Timeout, Full_Buffer);
535 if N = Expect_Timeout or else N = Expect_Full_Buffer then
542 ---------------------
543 -- Expect_Internal --
544 ---------------------
546 procedure Expect_Internal
547 (Descriptors : in out Array_Of_Pd;
548 Result : out Expect_Match;
550 Full_Buffer : Boolean)
552 Num_Descriptors : Integer;
553 Buffer_Size : Integer := 0;
557 type File_Descriptor_Array is
558 array (Descriptors'Range) of File_Descriptor;
559 Fds : aliased File_Descriptor_Array;
561 type Integer_Array is array (Descriptors'Range) of Integer;
562 Is_Set : aliased Integer_Array;
565 for J in Descriptors'Range loop
566 Fds (J) := Descriptors (J).Output_Fd;
568 if Descriptors (J).Buffer_Size = 0 then
569 Buffer_Size := Integer'Max (Buffer_Size, 4096);
572 Integer'Max (Buffer_Size, Descriptors (J).Buffer_Size);
577 Buffer : aliased String (1 .. Buffer_Size);
578 -- Buffer used for input. This is allocated only once, not for
579 -- every iteration of the loop
582 -- Loop until we match or we have a timeout
586 Poll (Fds'Address, Fds'Length, Timeout, Is_Set'Address);
588 case Num_Descriptors is
598 Result := Expect_Timeout;
604 for J in Descriptors'Range loop
605 if Is_Set (J) = 1 then
606 Buffer_Size := Descriptors (J).Buffer_Size;
608 if Buffer_Size = 0 then
612 N := Read (Descriptors (J).Output_Fd, Buffer'Address,
615 -- Error or End of file
618 -- ??? Note that ddd tries again up to three times
619 -- in that case. See LiterateA.C:174
623 -- If there is no limit to the buffer size
625 if Descriptors (J).Buffer_Size = 0 then
628 Tmp : String_Access := Descriptors (J).Buffer;
632 Descriptors (J).Buffer :=
633 new String (1 .. Tmp'Length + N);
634 Descriptors (J).Buffer (1 .. Tmp'Length) :=
636 Descriptors (J).Buffer
637 (Tmp'Length + 1 .. Tmp'Length + N) :=
640 Descriptors (J).Buffer_Index :=
641 Descriptors (J).Buffer'Last;
644 Descriptors (J).Buffer :=
646 Descriptors (J).Buffer.all :=
648 Descriptors (J).Buffer_Index := N;
653 -- Add what we read to the buffer
655 if Descriptors (J).Buffer_Index + N - 1 >
656 Descriptors (J).Buffer_Size
658 -- If the user wants to know when we have
659 -- read more than the buffer can contain.
662 Result := Expect_Full_Buffer;
666 -- Keep as much as possible from the buffer,
667 -- and forget old characters.
669 Descriptors (J).Buffer
670 (1 .. Descriptors (J).Buffer_Size - N) :=
671 Descriptors (J).Buffer
672 (N - Descriptors (J).Buffer_Size +
673 Descriptors (J).Buffer_Index + 1 ..
674 Descriptors (J).Buffer_Index);
675 Descriptors (J).Buffer_Index :=
676 Descriptors (J).Buffer_Size - N;
679 -- Keep what we read in the buffer.
681 Descriptors (J).Buffer
682 (Descriptors (J).Buffer_Index + 1 ..
683 Descriptors (J).Buffer_Index + N) :=
685 Descriptors (J).Buffer_Index :=
686 Descriptors (J).Buffer_Index + N;
689 -- Call each of the output filter with what we
693 (Descriptors (J).all, Buffer (1 .. N), Output);
695 Result := Expect_Match (N);
709 function Expect_Out (Descriptor : Process_Descriptor) return String is
711 return Descriptor.Buffer (1 .. Descriptor.Last_Match_End);
714 ----------------------
715 -- Expect_Out_Match --
716 ----------------------
718 function Expect_Out_Match (Descriptor : Process_Descriptor) return String is
720 return Descriptor.Buffer
721 (Descriptor.Last_Match_Start .. Descriptor.Last_Match_End);
722 end Expect_Out_Match;
729 (Descriptor : in out Process_Descriptor;
730 Timeout : Integer := 0)
732 Num_Descriptors : Integer;
734 Is_Set : aliased Integer;
735 Buffer_Size : Integer := 8192;
736 Buffer : aliased String (1 .. Buffer_Size);
739 -- Empty the current buffer
741 Descriptor.Last_Match_End := Descriptor.Buffer_Index;
742 Reinitialize_Buffer (Descriptor);
744 -- Read everything from the process to flush its output
748 Poll (Descriptor.Output_Fd'Address, 1, Timeout, Is_Set'Address);
750 case Num_Descriptors is
757 -- Timeout => End of flush
766 N := Read (Descriptor.Output_Fd, Buffer'Address,
784 function Get_Error_Fd
785 (Descriptor : Process_Descriptor)
786 return GNAT.OS_Lib.File_Descriptor
789 return Descriptor.Error_Fd;
796 function Get_Input_Fd
797 (Descriptor : Process_Descriptor)
798 return GNAT.OS_Lib.File_Descriptor
801 return Descriptor.Input_Fd;
808 function Get_Output_Fd
809 (Descriptor : Process_Descriptor)
810 return GNAT.OS_Lib.File_Descriptor
813 return Descriptor.Output_Fd;
821 (Descriptor : Process_Descriptor)
825 return Descriptor.Pid;
832 procedure Interrupt (Descriptor : in out Process_Descriptor) is
833 SIGINT : constant := 2;
836 Send_Signal (Descriptor, SIGINT);
843 procedure Lock_Filters (Descriptor : in out Process_Descriptor) is
845 Descriptor.Filters_Lock := Descriptor.Filters_Lock + 1;
848 ------------------------
849 -- Non_Blocking_Spawn --
850 ------------------------
852 procedure Non_Blocking_Spawn
853 (Descriptor : out Process_Descriptor'Class;
855 Args : GNAT.OS_Lib.Argument_List;
856 Buffer_Size : Natural := 4096;
857 Err_To_Out : Boolean := False)
861 -------------------------
862 -- Reinitialize_Buffer --
863 -------------------------
865 procedure Reinitialize_Buffer
866 (Descriptor : in out Process_Descriptor'Class)
869 if Descriptor.Buffer_Size = 0 then
871 Tmp : String_Access := Descriptor.Buffer;
876 (1 .. Descriptor.Buffer_Index - Descriptor.Last_Match_End);
879 Descriptor.Buffer.all := Tmp
880 (Descriptor.Last_Match_End + 1 .. Descriptor.Buffer_Index);
885 Descriptor.Buffer_Index := Descriptor.Buffer'Last;
889 (1 .. Descriptor.Buffer_Index - Descriptor.Last_Match_End) :=
891 (Descriptor.Last_Match_End + 1 .. Descriptor.Buffer_Index);
893 if Descriptor.Buffer_Index > Descriptor.Last_Match_End then
894 Descriptor.Buffer_Index :=
895 Descriptor.Buffer_Index - Descriptor.Last_Match_End;
897 Descriptor.Buffer_Index := 0;
901 Descriptor.Last_Match_Start := 0;
902 Descriptor.Last_Match_End := 0;
903 end Reinitialize_Buffer;
909 procedure Remove_Filter
910 (Descriptor : in out Process_Descriptor;
911 Filter : Filter_Function)
913 Previous : Filter_List := null;
914 Current : Filter_List := Descriptor.Filters;
917 while Current /= null loop
918 if Current.Filter = Filter then
919 if Previous = null then
920 Descriptor.Filters := Current.Next;
922 Previous.Next := Current.Next;
927 Current := Current.Next;
936 (Descriptor : in out Process_Descriptor;
938 Add_LF : Boolean := True;
939 Empty_Buffer : Boolean := False)
942 Full_Str : constant String := Str & ASCII.LF;
944 Result : Expect_Match;
945 Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
950 -- Force a read on the process if there is anything waiting.
952 Expect_Internal (Descriptors, Result,
953 Timeout => 0, Full_Buffer => False);
954 Descriptor.Last_Match_End := Descriptor.Buffer_Index;
958 Reinitialize_Buffer (Descriptor);
962 Last := Full_Str'Last;
964 Last := Full_Str'Last - 1;
967 Call_Filters (Descriptor, Full_Str (Full_Str'First .. Last), Input);
969 N := Write (Descriptor.Input_Fd,
971 Last - Full_Str'First + 1);
978 procedure Send_Signal
979 (Descriptor : Process_Descriptor;
983 Kill (Descriptor.Pid, Signal);
984 -- ??? Need to check process status here.
987 ---------------------------------
988 -- Set_Up_Child_Communications --
989 ---------------------------------
991 procedure Set_Up_Child_Communications
992 (Pid : in out Process_Descriptor;
993 Pipe1 : in out Pipe_Type;
994 Pipe2 : in out Pipe_Type;
995 Pipe3 : in out Pipe_Type;
997 Args : in System.Address)
999 pragma Warnings (Off, Pid);
1001 Input : File_Descriptor;
1002 Output : File_Descriptor;
1003 Error : File_Descriptor;
1006 -- Since Windows does not have a separate fork/exec, we need to
1007 -- perform the following actions:
1008 -- - save stdin, stdout, stderr
1009 -- - replace them by our pipes
1010 -- - create the child with process handle inheritance
1011 -- - revert to the previous stdin, stdout and stderr.
1013 Input := Dup (GNAT.OS_Lib.Standin);
1014 Output := Dup (GNAT.OS_Lib.Standout);
1015 Error := Dup (GNAT.OS_Lib.Standerr);
1017 -- Since we are still called from the parent process, there is no way
1018 -- currently we can cleanly close the unneeded ends of the pipes, but
1019 -- this doesn't really matter.
1020 -- We could close Pipe1.Output, Pipe2.Input, Pipe3.Input.
1022 Dup2 (Pipe1.Input, GNAT.OS_Lib.Standin);
1023 Dup2 (Pipe2.Output, GNAT.OS_Lib.Standout);
1024 Dup2 (Pipe3.Output, GNAT.OS_Lib.Standerr);
1026 Portable_Execvp (Pid.Pid'Access, Cmd & ASCII.Nul, Args);
1028 -- The following commands are not executed on Unix systems, and are
1029 -- only required for Windows systems. We are now in the parent process.
1031 -- Restore the old descriptors
1033 Dup2 (Input, GNAT.OS_Lib.Standin);
1034 Dup2 (Output, GNAT.OS_Lib.Standout);
1035 Dup2 (Error, GNAT.OS_Lib.Standerr);
1039 end Set_Up_Child_Communications;
1041 ---------------------------
1042 -- Set_Up_Communications --
1043 ---------------------------
1045 procedure Set_Up_Communications
1046 (Pid : in out Process_Descriptor;
1047 Err_To_Out : Boolean;
1048 Pipe1 : access Pipe_Type;
1049 Pipe2 : access Pipe_Type;
1050 Pipe3 : access Pipe_Type)
1055 if Create_Pipe (Pipe1) /= 0 then
1059 if Create_Pipe (Pipe2) /= 0 then
1063 Pid.Input_Fd := Pipe1.Output;
1064 Pid.Output_Fd := Pipe2.Input;
1067 Pipe3.all := Pipe2.all;
1069 if Create_Pipe (Pipe3) /= 0 then
1074 Pid.Error_Fd := Pipe3.Input;
1075 end Set_Up_Communications;
1077 ----------------------------------
1078 -- Set_Up_Parent_Communications --
1079 ----------------------------------
1081 procedure Set_Up_Parent_Communications
1082 (Pid : in out Process_Descriptor;
1083 Pipe1 : in out Pipe_Type;
1084 Pipe2 : in out Pipe_Type;
1085 Pipe3 : in out Pipe_Type)
1087 pragma Warnings (Off, Pid);
1090 Close (Pipe1.Input);
1091 Close (Pipe2.Output);
1092 Close (Pipe3.Output);
1093 end Set_Up_Parent_Communications;
1099 procedure Trace_Filter
1100 (Descriptor : Process_Descriptor'Class;
1102 User_Data : System.Address := System.Null_Address)
1104 pragma Warnings (Off, Descriptor);
1105 pragma Warnings (Off, User_Data);
1111 --------------------
1112 -- Unlock_Filters --
1113 --------------------
1115 procedure Unlock_Filters (Descriptor : in out Process_Descriptor) is
1117 if Descriptor.Filters_Lock > 0 then
1118 Descriptor.Filters_Lock := Descriptor.Filters_Lock - 1;