OSDN Git Service

* tree-chrec.c (avoid_arithmetics_in_type_p): New.
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-expect-vms.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT LIBRARY COMPONENTS                          --
4 --                                                                          --
5 --                          G N A T . E X P E C T                           --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                     Copyright (C) 2002-2005, AdaCore                     --
10 --                                                                          --
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,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
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.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 --  This is the VMS version
35
36 with System;        use System;
37 with Ada.Calendar;  use Ada.Calendar;
38
39 with GNAT.IO;
40 with GNAT.OS_Lib;   use GNAT.OS_Lib;
41 with GNAT.Regpat;   use GNAT.Regpat;
42
43 with Unchecked_Deallocation;
44
45 package body GNAT.Expect is
46
47    type Array_Of_Pd is array (Positive range <>) of Process_Descriptor_Access;
48
49    Save_Input  : File_Descriptor;
50    Save_Output : File_Descriptor;
51    Save_Error  : File_Descriptor;
52
53    procedure Expect_Internal
54      (Descriptors : in out Array_Of_Pd;
55       Result      : out Expect_Match;
56       Timeout     : Integer;
57       Full_Buffer : Boolean);
58    --  Internal function used to read from the process Descriptor.
59    --
60    --  Three outputs are possible:
61    --     Result=Expect_Timeout, if no output was available before the timeout
62    --        expired.
63    --     Result=Expect_Full_Buffer, if Full_Buffer is True and some characters
64    --        had to be discarded from the internal buffer of Descriptor.
65    --     Result=<integer>, indicates how many characters were added to the
66    --        internal buffer. These characters are from indexes
67    --        Descriptor.Buffer_Index - Result + 1 .. Descriptor.Buffer_Index
68    --  Process_Died is raised if the process is no longer valid.
69
70    procedure Reinitialize_Buffer
71      (Descriptor : in out Process_Descriptor'Class);
72    --  Reinitialize the internal buffer.
73    --  The buffer is deleted up to the end of the last match.
74
75    procedure Free is new Unchecked_Deallocation
76      (Pattern_Matcher, Pattern_Matcher_Access);
77
78    procedure Call_Filters
79      (Pid       : Process_Descriptor'Class;
80       Str       : String;
81       Filter_On : Filter_Type);
82    --  Call all the filters that have the appropriate type.
83    --  This function does nothing if the filters are locked
84
85    ------------------------------
86    -- Target dependent section --
87    ------------------------------
88
89    function Dup (Fd : File_Descriptor) return File_Descriptor;
90    pragma Import (C, Dup, "decc$dup");
91
92    procedure Dup2 (Old_Fd, New_Fd : File_Descriptor);
93    pragma Import (C, Dup2, "decc$dup2");
94
95    procedure Kill (Pid : Process_Id; Sig_Num : Integer);
96    pragma Import (C, Kill, "decc$kill");
97
98    function Create_Pipe (Pipe : access Pipe_Type) return Integer;
99    pragma Import (C, Create_Pipe, "__gnat_pipe");
100
101    function Poll
102      (Fds     : System.Address;
103       Num_Fds : Integer;
104       Timeout : Integer;
105       Is_Set  : System.Address) return Integer;
106    pragma Import (C, Poll, "__gnat_expect_poll");
107    --  Check whether there is any data waiting on the file descriptor
108    --  Out_fd, and wait if there is none, at most Timeout milliseconds
109    --  Returns -1 in case of error, 0 if the timeout expired before
110    --  data became available.
111    --
112    --  Out_Is_Set is set to 1 if data was available, 0 otherwise.
113
114    function Waitpid (Pid : Process_Id) return Integer;
115    pragma Import (C, Waitpid, "__gnat_waitpid");
116    --  Wait for a specific process id, and return its exit code
117
118    ---------
119    -- "+" --
120    ---------
121
122    function "+" (S : String) return GNAT.OS_Lib.String_Access is
123    begin
124       return new String'(S);
125    end "+";
126
127    ---------
128    -- "+" --
129    ---------
130
131    function "+"
132      (P : GNAT.Regpat.Pattern_Matcher) return Pattern_Matcher_Access
133    is
134    begin
135       return new GNAT.Regpat.Pattern_Matcher'(P);
136    end "+";
137
138    ----------------
139    -- Add_Filter --
140    ----------------
141
142    procedure Add_Filter
143      (Descriptor : in out Process_Descriptor;
144       Filter     : Filter_Function;
145       Filter_On  : Filter_Type := Output;
146       User_Data  : System.Address := System.Null_Address;
147       After      : Boolean := False)
148    is
149       Current : Filter_List := Descriptor.Filters;
150
151    begin
152       if After then
153          while Current /= null and then Current.Next /= null loop
154             Current := Current.Next;
155          end loop;
156
157          if Current = null then
158             Descriptor.Filters :=
159               new Filter_List_Elem'
160                (Filter => Filter, Filter_On => Filter_On,
161                 User_Data => User_Data, Next => null);
162          else
163             Current.Next :=
164               new Filter_List_Elem'
165               (Filter => Filter, Filter_On => Filter_On,
166                User_Data => User_Data, Next => null);
167          end if;
168
169       else
170          Descriptor.Filters :=
171            new Filter_List_Elem'
172              (Filter => Filter, Filter_On => Filter_On,
173               User_Data => User_Data, Next => Descriptor.Filters);
174       end if;
175    end Add_Filter;
176
177    ------------------
178    -- Call_Filters --
179    ------------------
180
181    procedure Call_Filters
182      (Pid       : Process_Descriptor'Class;
183       Str       : String;
184       Filter_On : Filter_Type)
185    is
186       Current_Filter  : Filter_List;
187
188    begin
189       if Pid.Filters_Lock = 0 then
190          Current_Filter := Pid.Filters;
191
192          while Current_Filter /= null loop
193             if Current_Filter.Filter_On = Filter_On then
194                Current_Filter.Filter
195                  (Pid, Str, Current_Filter.User_Data);
196             end if;
197
198             Current_Filter := Current_Filter.Next;
199          end loop;
200       end if;
201    end Call_Filters;
202
203    -----------
204    -- Close --
205    -----------
206
207    procedure Close
208      (Descriptor : in out Process_Descriptor;
209       Status     : out Integer)
210    is
211    begin
212       Close (Descriptor.Input_Fd);
213
214       if Descriptor.Error_Fd /= Descriptor.Output_Fd then
215          Close (Descriptor.Error_Fd);
216       end if;
217
218       Close (Descriptor.Output_Fd);
219
220       --  ??? Should have timeouts for different signals
221       Kill (Descriptor.Pid, 9);
222
223       GNAT.OS_Lib.Free (Descriptor.Buffer);
224       Descriptor.Buffer_Size := 0;
225
226       Status := Waitpid (Descriptor.Pid);
227    end Close;
228
229    procedure Close (Descriptor : in out Process_Descriptor) is
230       Status : Integer;
231    begin
232       Close (Descriptor, Status);
233    end Close;
234
235    ------------
236    -- Expect --
237    ------------
238
239    procedure Expect
240      (Descriptor  : in out Process_Descriptor;
241       Result      : out Expect_Match;
242       Regexp      : String;
243       Timeout     : Integer := 10000;
244       Full_Buffer : Boolean := False)
245    is
246    begin
247       if Regexp = "" then
248          Expect (Descriptor, Result, Never_Match, Timeout, Full_Buffer);
249       else
250          Expect (Descriptor, Result, Compile (Regexp), Timeout, Full_Buffer);
251       end if;
252    end Expect;
253
254    procedure Expect
255      (Descriptor  : in out Process_Descriptor;
256       Result      : out Expect_Match;
257       Regexp      : String;
258       Matched     : out GNAT.Regpat.Match_Array;
259       Timeout     : Integer := 10000;
260       Full_Buffer : Boolean := False)
261    is
262    begin
263       pragma Assert (Matched'First = 0);
264       if Regexp = "" then
265          Expect
266            (Descriptor, Result, Never_Match, Matched, Timeout, Full_Buffer);
267       else
268          Expect
269            (Descriptor, Result, Compile (Regexp), Matched, Timeout,
270             Full_Buffer);
271       end if;
272    end Expect;
273
274    procedure Expect
275      (Descriptor  : in out Process_Descriptor;
276       Result      : out Expect_Match;
277       Regexp      : GNAT.Regpat.Pattern_Matcher;
278       Timeout     : Integer := 10000;
279       Full_Buffer : Boolean := False)
280    is
281       Matched : GNAT.Regpat.Match_Array (0 .. 0);
282
283    begin
284       Expect (Descriptor, Result, Regexp, Matched, Timeout, Full_Buffer);
285    end Expect;
286
287    procedure Expect
288      (Descriptor  : in out Process_Descriptor;
289       Result      : out Expect_Match;
290       Regexp      : GNAT.Regpat.Pattern_Matcher;
291       Matched     : out GNAT.Regpat.Match_Array;
292       Timeout     : Integer := 10000;
293       Full_Buffer : Boolean := False)
294    is
295       N           : Expect_Match;
296       Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
297       Try_Until   : constant Time := Clock + Duration (Timeout) / 1000.0;
298       Timeout_Tmp : Integer := Timeout;
299
300    begin
301       pragma Assert (Matched'First = 0);
302       Reinitialize_Buffer (Descriptor);
303
304       loop
305          --  First, test if what is already in the buffer matches (This is
306          --  required if this package is used in multi-task mode, since one of
307          --  the tasks might have added something in the buffer, and we don't
308          --  want other tasks to wait for new input to be available before
309          --  checking the regexps).
310
311          Match
312            (Regexp, Descriptor.Buffer (1 .. Descriptor.Buffer_Index), Matched);
313
314          if Descriptor.Buffer_Index >= 1 and then Matched (0).First /= 0 then
315             Result := 1;
316             Descriptor.Last_Match_Start := Matched (0).First;
317             Descriptor.Last_Match_End := Matched (0).Last;
318             return;
319          end if;
320
321          --  Else try to read new input
322
323          Expect_Internal (Descriptors, N, Timeout_Tmp, Full_Buffer);
324
325          if N = Expect_Timeout or else N = Expect_Full_Buffer then
326             Result := N;
327             return;
328          end if;
329
330          --  Calculate the timeout for the next turn.
331          --  Note that Timeout is, from the caller's perspective, the maximum
332          --  time until a match, not the maximum time until some output is
333          --  read, and thus cannot be reused as is for Expect_Internal.
334
335          if Timeout /= -1 then
336             Timeout_Tmp := Integer (Try_Until - Clock) * 1000;
337
338             if Timeout_Tmp < 0 then
339                Result := Expect_Timeout;
340                exit;
341             end if;
342          end if;
343       end loop;
344
345       --  Even if we had the general timeout above, we have to test that the
346       --  last test we read from the external process didn't match.
347
348       Match
349         (Regexp, Descriptor.Buffer (1 .. Descriptor.Buffer_Index), Matched);
350
351       if Matched (0).First /= 0 then
352          Result := 1;
353          Descriptor.Last_Match_Start := Matched (0).First;
354          Descriptor.Last_Match_End := Matched (0).Last;
355          return;
356       end if;
357    end Expect;
358
359    procedure Expect
360      (Descriptor  : in out Process_Descriptor;
361       Result      : out Expect_Match;
362       Regexps     : Regexp_Array;
363       Timeout     : Integer := 10000;
364       Full_Buffer : Boolean := False)
365    is
366       Patterns : Compiled_Regexp_Array (Regexps'Range);
367       Matched  : GNAT.Regpat.Match_Array (0 .. 0);
368
369    begin
370       for J in Regexps'Range loop
371          Patterns (J) := new Pattern_Matcher'(Compile (Regexps (J).all));
372       end loop;
373
374       Expect (Descriptor, Result, Patterns, Matched, Timeout, Full_Buffer);
375
376       for J in Regexps'Range loop
377          Free (Patterns (J));
378       end loop;
379    end Expect;
380
381    procedure Expect
382      (Descriptor  : in out Process_Descriptor;
383       Result      : out Expect_Match;
384       Regexps     : Compiled_Regexp_Array;
385       Timeout     : Integer := 10000;
386       Full_Buffer : Boolean := False)
387    is
388       Matched : GNAT.Regpat.Match_Array (0 .. 0);
389
390    begin
391       Expect (Descriptor, Result, Regexps, Matched, Timeout, Full_Buffer);
392    end Expect;
393
394    procedure Expect
395      (Result      : out Expect_Match;
396       Regexps     : Multiprocess_Regexp_Array;
397       Timeout     : Integer := 10000;
398       Full_Buffer : Boolean := False)
399    is
400       Matched : GNAT.Regpat.Match_Array (0 .. 0);
401
402    begin
403       Expect (Result, Regexps, Matched, Timeout, Full_Buffer);
404    end Expect;
405
406    procedure Expect
407      (Descriptor  : in out Process_Descriptor;
408       Result      : out Expect_Match;
409       Regexps     : Regexp_Array;
410       Matched     : out GNAT.Regpat.Match_Array;
411       Timeout     : Integer := 10000;
412       Full_Buffer : Boolean := False)
413    is
414       Patterns : Compiled_Regexp_Array (Regexps'Range);
415
416    begin
417       pragma Assert (Matched'First = 0);
418
419       for J in Regexps'Range loop
420          Patterns (J) := new Pattern_Matcher'(Compile (Regexps (J).all));
421       end loop;
422
423       Expect (Descriptor, Result, Patterns, Matched, Timeout, Full_Buffer);
424
425       for J in Regexps'Range loop
426          Free (Patterns (J));
427       end loop;
428    end Expect;
429
430    procedure Expect
431      (Descriptor  : in out Process_Descriptor;
432       Result      : out Expect_Match;
433       Regexps     : Compiled_Regexp_Array;
434       Matched     : out GNAT.Regpat.Match_Array;
435       Timeout     : Integer := 10000;
436       Full_Buffer : Boolean := False)
437    is
438       N           : Expect_Match;
439       Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
440
441    begin
442       pragma Assert (Matched'First = 0);
443
444       Reinitialize_Buffer (Descriptor);
445
446       loop
447          --  First, test if what is already in the buffer matches (This is
448          --  required if this package is used in multi-task mode, since one of
449          --  the tasks might have added something in the buffer, and we don't
450          --  want other tasks to wait for new input to be available before
451          --  checking the regexps).
452
453          if Descriptor.Buffer /= null then
454             for J in Regexps'Range loop
455                Match
456                  (Regexps (J).all,
457                   Descriptor.Buffer (1 .. Descriptor.Buffer_Index),
458                   Matched);
459
460                if Matched (0) /= No_Match then
461                   Result := Expect_Match (J);
462                   Descriptor.Last_Match_Start := Matched (0).First;
463                   Descriptor.Last_Match_End := Matched (0).Last;
464                   return;
465                end if;
466             end loop;
467          end if;
468
469          Expect_Internal (Descriptors, N, Timeout, Full_Buffer);
470
471          if N = Expect_Timeout or else N = Expect_Full_Buffer then
472             Result := N;
473             return;
474          end if;
475       end loop;
476    end Expect;
477
478    procedure Expect
479      (Result      : out Expect_Match;
480       Regexps     : Multiprocess_Regexp_Array;
481       Matched     : out GNAT.Regpat.Match_Array;
482       Timeout     : Integer := 10000;
483       Full_Buffer : Boolean := False)
484    is
485       N           : Expect_Match;
486       Descriptors : Array_Of_Pd (Regexps'Range);
487
488    begin
489       pragma Assert (Matched'First = 0);
490
491       for J in Descriptors'Range loop
492          Descriptors (J) := Regexps (J).Descriptor;
493          Reinitialize_Buffer (Regexps (J).Descriptor.all);
494       end loop;
495
496       loop
497          --  First, test if what is already in the buffer matches (This is
498          --  required if this package is used in multi-task mode, since one of
499          --  the tasks might have added something in the buffer, and we don't
500          --  want other tasks to wait for new input to be available before
501          --  checking the regexps).
502
503          for J in Regexps'Range loop
504             Match (Regexps (J).Regexp.all,
505                    Regexps (J).Descriptor.Buffer
506                      (1 .. Regexps (J).Descriptor.Buffer_Index),
507                    Matched);
508
509             if Matched (0) /= No_Match then
510                Result := Expect_Match (J);
511                Regexps (J).Descriptor.Last_Match_Start := Matched (0).First;
512                Regexps (J).Descriptor.Last_Match_End := Matched (0).Last;
513                return;
514             end if;
515          end loop;
516
517          Expect_Internal (Descriptors, N, Timeout, Full_Buffer);
518
519          if N = Expect_Timeout or else N = Expect_Full_Buffer then
520             Result := N;
521             return;
522          end if;
523       end loop;
524    end Expect;
525
526    ---------------------
527    -- Expect_Internal --
528    ---------------------
529
530    procedure Expect_Internal
531      (Descriptors : in out Array_Of_Pd;
532       Result      : out Expect_Match;
533       Timeout     : Integer;
534       Full_Buffer : Boolean)
535    is
536       Num_Descriptors : Integer;
537       Buffer_Size     : Integer := 0;
538
539       N : Integer;
540
541       type File_Descriptor_Array is
542         array (Descriptors'Range) of File_Descriptor;
543       Fds : aliased File_Descriptor_Array;
544
545       type Integer_Array is array (Descriptors'Range) of Integer;
546       Is_Set : aliased Integer_Array;
547
548    begin
549       for J in Descriptors'Range loop
550          Fds (J) := Descriptors (J).Output_Fd;
551
552          if Descriptors (J).Buffer_Size = 0 then
553             Buffer_Size := Integer'Max (Buffer_Size, 4096);
554          else
555             Buffer_Size :=
556               Integer'Max (Buffer_Size, Descriptors (J).Buffer_Size);
557          end if;
558       end loop;
559
560       declare
561          Buffer : aliased String (1 .. Buffer_Size);
562          --  Buffer used for input. This is allocated only once, not for
563          --  every iteration of the loop
564
565       begin
566          --  Loop until we match or we have a timeout
567
568          loop
569             Num_Descriptors :=
570               Poll (Fds'Address, Fds'Length, Timeout, Is_Set'Address);
571
572             case Num_Descriptors is
573
574                --  Error?
575
576                when -1 =>
577                   raise Process_Died;
578
579                --  Timeout?
580
581                when 0  =>
582                   Result := Expect_Timeout;
583                   return;
584
585                --  Some input
586
587                when others =>
588                   for J in Descriptors'Range loop
589                      if Is_Set (J) = 1 then
590                         Buffer_Size := Descriptors (J).Buffer_Size;
591
592                         if Buffer_Size = 0 then
593                            Buffer_Size := 4096;
594                         end if;
595
596                         N := Read (Descriptors (J).Output_Fd, Buffer'Address,
597                                    Buffer_Size);
598
599                         --  Error or End of file
600
601                         if N <= 0 then
602                            --  ??? Note that ddd tries again up to three times
603                            --  in that case. See LiterateA.C:174
604                            raise Process_Died;
605
606                         else
607                            --  If there is no limit to the buffer size
608
609                            if Descriptors (J).Buffer_Size = 0 then
610
611                               declare
612                                  Tmp : String_Access := Descriptors (J).Buffer;
613
614                               begin
615                                  if Tmp /= null then
616                                     Descriptors (J).Buffer :=
617                                       new String (1 .. Tmp'Length + N);
618                                     Descriptors (J).Buffer (1 .. Tmp'Length) :=
619                                       Tmp.all;
620                                     Descriptors (J).Buffer
621                                       (Tmp'Length + 1 .. Tmp'Length + N) :=
622                                       Buffer (1 .. N);
623                                     Free (Tmp);
624                                     Descriptors (J).Buffer_Index :=
625                                       Descriptors (J).Buffer'Last;
626
627                                  else
628                                     Descriptors (J).Buffer :=
629                                       new String (1 .. N);
630                                     Descriptors (J).Buffer.all :=
631                                       Buffer (1 .. N);
632                                     Descriptors (J).Buffer_Index := N;
633                                  end if;
634                               end;
635
636                            else
637                               --  Add what we read to the buffer
638
639                               if Descriptors (J).Buffer_Index + N - 1 >
640                                 Descriptors (J).Buffer_Size
641                               then
642                                  --  If the user wants to know when we have
643                                  --  read more than the buffer can contain.
644
645                                  if Full_Buffer then
646                                     Result := Expect_Full_Buffer;
647                                     return;
648                                  end if;
649
650                                  --  Keep as much as possible from the buffer,
651                                  --  and forget old characters.
652
653                                  Descriptors (J).Buffer
654                                    (1 .. Descriptors (J).Buffer_Size - N) :=
655                                   Descriptors (J).Buffer
656                                    (N - Descriptors (J).Buffer_Size +
657                                     Descriptors (J).Buffer_Index + 1 ..
658                                     Descriptors (J).Buffer_Index);
659                                  Descriptors (J).Buffer_Index :=
660                                    Descriptors (J).Buffer_Size - N;
661                               end if;
662
663                               --  Keep what we read in the buffer
664
665                               Descriptors (J).Buffer
666                                 (Descriptors (J).Buffer_Index + 1 ..
667                                  Descriptors (J).Buffer_Index + N) :=
668                                 Buffer (1 .. N);
669                               Descriptors (J).Buffer_Index :=
670                                 Descriptors (J).Buffer_Index + N;
671                            end if;
672
673                            --  Call each of the output filter with what we
674                            --  read.
675
676                            Call_Filters
677                              (Descriptors (J).all, Buffer (1 .. N), Output);
678
679                            Result := Expect_Match (N);
680                            return;
681                         end if;
682                      end if;
683                   end loop;
684             end case;
685          end loop;
686       end;
687    end Expect_Internal;
688
689    ----------------
690    -- Expect_Out --
691    ----------------
692
693    function Expect_Out (Descriptor : Process_Descriptor) return String is
694    begin
695       return Descriptor.Buffer (1 .. Descriptor.Last_Match_End);
696    end Expect_Out;
697
698    ----------------------
699    -- Expect_Out_Match --
700    ----------------------
701
702    function Expect_Out_Match (Descriptor : Process_Descriptor) return String is
703    begin
704       return Descriptor.Buffer
705         (Descriptor.Last_Match_Start .. Descriptor.Last_Match_End);
706    end Expect_Out_Match;
707
708    -----------
709    -- Flush --
710    -----------
711
712    procedure Flush
713      (Descriptor : in out Process_Descriptor;
714       Timeout    : Integer := 0)
715    is
716       Buffer_Size     : constant Integer := 8192;
717       Num_Descriptors : Integer;
718       N               : Integer;
719       Is_Set          : aliased Integer;
720       Buffer          : aliased String (1 .. Buffer_Size);
721
722    begin
723       --  Empty the current buffer
724
725       Descriptor.Last_Match_End := Descriptor.Buffer_Index;
726       Reinitialize_Buffer (Descriptor);
727
728       --  Read everything from the process to flush its output
729
730       loop
731          Num_Descriptors :=
732            Poll (Descriptor.Output_Fd'Address, 1, Timeout, Is_Set'Address);
733
734          case Num_Descriptors is
735
736             --  Error ?
737
738             when -1 =>
739                raise Process_Died;
740
741             --  Timeout => End of flush
742
743             when 0  =>
744                return;
745
746             --  Some input
747
748             when others =>
749                if Is_Set = 1 then
750                   N := Read (Descriptor.Output_Fd, Buffer'Address,
751                              Buffer_Size);
752
753                   if N = -1 then
754                      raise Process_Died;
755                   elsif N = 0 then
756                      return;
757                   end if;
758                end if;
759          end case;
760       end loop;
761
762    end Flush;
763
764    ------------------------
765    -- Get_Command_Output --
766    ------------------------
767
768    function Get_Command_Output
769      (Command    : String;
770       Arguments  : GNAT.OS_Lib.Argument_List;
771       Input      : String;
772       Status     : access Integer;
773       Err_To_Out : Boolean := False) return String
774    is
775       use GNAT.Expect;
776
777       Process : Process_Descriptor;
778
779       Output : String_Access := new String (1 .. 1024);
780       --  Buffer used to accumulate standard output from the launched
781       --  command, expanded as necessary during execution.
782
783       Last : Integer := 0;
784       --  Index of the last used character within Output
785
786    begin
787       Non_Blocking_Spawn
788         (Process, Command, Arguments, Err_To_Out => Err_To_Out);
789
790       if Input'Length > 0 then
791          Send (Process, Input);
792       end if;
793
794       GNAT.OS_Lib.Close (Get_Input_Fd (Process));
795
796       declare
797          Result : Expect_Match;
798
799       begin
800          --  This loop runs until the call to Expect raises Process_Died
801
802          loop
803             Expect (Process, Result, ".+");
804
805             declare
806                NOutput : String_Access;
807                S       : constant String := Expect_Out (Process);
808                pragma Assert (S'Length > 0);
809
810             begin
811                --  Expand buffer if we need more space
812
813                if Last + S'Length > Output'Last then
814                   NOutput := new String (1 .. 2 * Output'Last);
815                   NOutput (Output'Range) := Output.all;
816                   Free (Output);
817
818                   --  Here if current buffer size is OK
819
820                else
821                   NOutput := Output;
822                end if;
823
824                NOutput (Last + 1 .. Last + S'Length) := S;
825                Last := Last + S'Length;
826                Output := NOutput;
827             end;
828          end loop;
829
830       exception
831          when Process_Died =>
832             Close (Process, Status.all);
833       end;
834
835       if Last = 0 then
836          return "";
837       end if;
838
839       declare
840          S : constant String := Output (1 .. Last);
841       begin
842          Free (Output);
843          return S;
844       end;
845    end Get_Command_Output;
846
847    ------------------
848    -- Get_Error_Fd --
849    ------------------
850
851    function Get_Error_Fd
852      (Descriptor : Process_Descriptor) return GNAT.OS_Lib.File_Descriptor
853    is
854    begin
855       return Descriptor.Error_Fd;
856    end Get_Error_Fd;
857
858    ------------------
859    -- Get_Input_Fd --
860    ------------------
861
862    function Get_Input_Fd
863      (Descriptor : Process_Descriptor) return GNAT.OS_Lib.File_Descriptor
864    is
865    begin
866       return Descriptor.Input_Fd;
867    end Get_Input_Fd;
868
869    -------------------
870    -- Get_Output_Fd --
871    -------------------
872
873    function Get_Output_Fd
874      (Descriptor : Process_Descriptor) return GNAT.OS_Lib.File_Descriptor
875    is
876    begin
877       return Descriptor.Output_Fd;
878    end Get_Output_Fd;
879
880    -------------
881    -- Get_Pid --
882    -------------
883
884    function Get_Pid
885      (Descriptor : Process_Descriptor) return Process_Id
886    is
887    begin
888       return Descriptor.Pid;
889    end Get_Pid;
890
891    ---------------
892    -- Interrupt --
893    ---------------
894
895    procedure Interrupt (Descriptor : in out Process_Descriptor) is
896       SIGINT : constant := 2;
897
898    begin
899       Send_Signal (Descriptor, SIGINT);
900    end Interrupt;
901
902    ------------------
903    -- Lock_Filters --
904    ------------------
905
906    procedure Lock_Filters (Descriptor : in out Process_Descriptor) is
907    begin
908       Descriptor.Filters_Lock := Descriptor.Filters_Lock + 1;
909    end Lock_Filters;
910
911    ------------------------
912    -- Non_Blocking_Spawn --
913    ------------------------
914
915    procedure Non_Blocking_Spawn
916      (Descriptor  : out Process_Descriptor'Class;
917       Command     : String;
918       Args        : GNAT.OS_Lib.Argument_List;
919       Buffer_Size : Natural := 4096;
920       Err_To_Out  : Boolean := False)
921    is separate;
922
923    -------------------------
924    -- Reinitialize_Buffer --
925    -------------------------
926
927    procedure Reinitialize_Buffer
928      (Descriptor : in out Process_Descriptor'Class)
929    is
930    begin
931       if Descriptor.Buffer_Size = 0 then
932          declare
933             Tmp : String_Access := Descriptor.Buffer;
934
935          begin
936             Descriptor.Buffer :=
937               new String
938                 (1 .. Descriptor.Buffer_Index - Descriptor.Last_Match_End);
939
940             if Tmp /= null then
941                Descriptor.Buffer.all := Tmp
942                  (Descriptor.Last_Match_End + 1 .. Descriptor.Buffer_Index);
943                Free (Tmp);
944             end if;
945          end;
946
947          Descriptor.Buffer_Index := Descriptor.Buffer'Last;
948
949       else
950          Descriptor.Buffer
951            (1 .. Descriptor.Buffer_Index - Descriptor.Last_Match_End) :=
952              Descriptor.Buffer
953                (Descriptor.Last_Match_End + 1 .. Descriptor.Buffer_Index);
954
955          if Descriptor.Buffer_Index > Descriptor.Last_Match_End then
956             Descriptor.Buffer_Index :=
957               Descriptor.Buffer_Index - Descriptor.Last_Match_End;
958          else
959             Descriptor.Buffer_Index := 0;
960          end if;
961       end if;
962
963       Descriptor.Last_Match_Start := 0;
964       Descriptor.Last_Match_End := 0;
965    end Reinitialize_Buffer;
966
967    -------------------
968    -- Remove_Filter --
969    -------------------
970
971    procedure Remove_Filter
972      (Descriptor : in out Process_Descriptor;
973       Filter     : Filter_Function)
974    is
975       Previous : Filter_List := null;
976       Current  : Filter_List := Descriptor.Filters;
977
978    begin
979       while Current /= null loop
980          if Current.Filter = Filter then
981             if Previous = null then
982                Descriptor.Filters := Current.Next;
983             else
984                Previous.Next := Current.Next;
985             end if;
986          end if;
987
988          Previous := Current;
989          Current := Current.Next;
990       end loop;
991    end Remove_Filter;
992
993    ----------
994    -- Send --
995    ----------
996
997    procedure Send
998      (Descriptor   : in out Process_Descriptor;
999       Str          : String;
1000       Add_LF       : Boolean := True;
1001       Empty_Buffer : Boolean := False)
1002    is
1003       Full_Str    : constant String := Str & ASCII.LF;
1004       Last        : Natural;
1005       Result      : Expect_Match;
1006       Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
1007
1008       Discard : Natural;
1009       pragma Unreferenced (Discard);
1010
1011    begin
1012       if Empty_Buffer then
1013
1014          --  Force a read on the process if there is anything waiting
1015
1016          Expect_Internal (Descriptors, Result,
1017                           Timeout => 0, Full_Buffer => False);
1018          Descriptor.Last_Match_End := Descriptor.Buffer_Index;
1019
1020          --  Empty the buffer
1021
1022          Reinitialize_Buffer (Descriptor);
1023       end if;
1024
1025       if Add_LF then
1026          Last := Full_Str'Last;
1027       else
1028          Last := Full_Str'Last - 1;
1029       end if;
1030
1031       Call_Filters (Descriptor, Full_Str (Full_Str'First .. Last), Input);
1032
1033       Discard := Write (Descriptor.Input_Fd,
1034                         Full_Str'Address,
1035                         Last - Full_Str'First + 1);
1036       --  Shouldn't we at least have a pragma Assert on the result ???
1037    end Send;
1038
1039    -----------------
1040    -- Send_Signal --
1041    -----------------
1042
1043    procedure Send_Signal
1044      (Descriptor : Process_Descriptor;
1045       Signal     : Integer)
1046    is
1047    begin
1048       Kill (Descriptor.Pid, Signal);
1049       --  ??? Need to check process status here
1050    end Send_Signal;
1051
1052    ---------------------------------
1053    -- Set_Up_Child_Communications --
1054    ---------------------------------
1055
1056    procedure Set_Up_Child_Communications
1057      (Pid   : in out Process_Descriptor;
1058       Pipe1 : in out Pipe_Type;
1059       Pipe2 : in out Pipe_Type;
1060       Pipe3 : in out Pipe_Type;
1061       Cmd   : in String;
1062       Args  : in System.Address)
1063    is
1064       pragma Warnings (Off, Pid);
1065
1066    begin
1067       --  Since the code between fork and exec on VMS executes
1068       --  in the context of the parent process, we need to
1069       --  perform the following actions:
1070       --    - save stdin, stdout, stderr
1071       --    - replace them by our pipes
1072       --    - create the child with process handle inheritance
1073       --    - revert to the previous stdin, stdout and stderr.
1074
1075       Save_Input  := Dup (GNAT.OS_Lib.Standin);
1076       Save_Output := Dup (GNAT.OS_Lib.Standout);
1077       Save_Error  := Dup (GNAT.OS_Lib.Standerr);
1078
1079       --  Since we are still called from the parent process, there is no way
1080       --  currently we can cleanly close the unneeded ends of the pipes, but
1081       --  this doesn't really matter.
1082       --  We could close Pipe1.Output, Pipe2.Input, Pipe3.Input.
1083
1084       Dup2 (Pipe1.Input,  GNAT.OS_Lib.Standin);
1085       Dup2 (Pipe2.Output, GNAT.OS_Lib.Standout);
1086       Dup2 (Pipe3.Output, GNAT.OS_Lib.Standerr);
1087
1088       Portable_Execvp (Pid.Pid'Access, Cmd & ASCII.Nul, Args);
1089
1090    end Set_Up_Child_Communications;
1091
1092    ---------------------------
1093    -- Set_Up_Communications --
1094    ---------------------------
1095
1096    procedure Set_Up_Communications
1097      (Pid        : in out Process_Descriptor;
1098       Err_To_Out : Boolean;
1099       Pipe1      : access Pipe_Type;
1100       Pipe2      : access Pipe_Type;
1101       Pipe3      : access Pipe_Type)
1102    is
1103    begin
1104       --  Create the pipes
1105
1106       if Create_Pipe (Pipe1) /= 0 then
1107          return;
1108       end if;
1109
1110       if Create_Pipe (Pipe2) /= 0 then
1111          return;
1112       end if;
1113
1114       Pid.Input_Fd  := Pipe1.Output;
1115       Pid.Output_Fd := Pipe2.Input;
1116
1117       if Err_To_Out then
1118          Pipe3.all := Pipe2.all;
1119       else
1120          if Create_Pipe (Pipe3) /= 0 then
1121             return;
1122          end if;
1123       end if;
1124
1125       Pid.Error_Fd := Pipe3.Input;
1126    end Set_Up_Communications;
1127
1128    ----------------------------------
1129    -- Set_Up_Parent_Communications --
1130    ----------------------------------
1131
1132    procedure Set_Up_Parent_Communications
1133      (Pid   : in out Process_Descriptor;
1134       Pipe1 : in out Pipe_Type;
1135       Pipe2 : in out Pipe_Type;
1136       Pipe3 : in out Pipe_Type)
1137    is
1138       pragma Warnings (Off, Pid);
1139
1140    begin
1141
1142       Dup2 (Save_Input,  GNAT.OS_Lib.Standin);
1143       Dup2 (Save_Output, GNAT.OS_Lib.Standout);
1144       Dup2 (Save_Error,  GNAT.OS_Lib.Standerr);
1145
1146       Close (Save_Input);
1147       Close (Save_Output);
1148       Close (Save_Error);
1149
1150       Close (Pipe1.Input);
1151       Close (Pipe2.Output);
1152       Close (Pipe3.Output);
1153    end Set_Up_Parent_Communications;
1154
1155    ------------------
1156    -- Trace_Filter --
1157    ------------------
1158
1159    procedure Trace_Filter
1160      (Descriptor : Process_Descriptor'Class;
1161       Str        : String;
1162       User_Data  : System.Address := System.Null_Address)
1163    is
1164       pragma Warnings (Off, Descriptor);
1165       pragma Warnings (Off, User_Data);
1166
1167    begin
1168       GNAT.IO.Put (Str);
1169    end Trace_Filter;
1170
1171    --------------------
1172    -- Unlock_Filters --
1173    --------------------
1174
1175    procedure Unlock_Filters (Descriptor : in out Process_Descriptor) is
1176    begin
1177       if Descriptor.Filters_Lock > 0 then
1178          Descriptor.Filters_Lock := Descriptor.Filters_Lock - 1;
1179       end if;
1180    end Unlock_Filters;
1181
1182 end GNAT.Expect;