OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [gcc/] [ada/] [g-expect-vms.adb] - Blame information for rev 717

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.