OSDN Git Service

2005-03-29 Robert Dewar <dewar@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-expect.adb
index 651b620..2eed916 100644 (file)
@@ -6,9 +6,7 @@
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---                            $Revision: 1.7 $
---                                                                          --
---           Copyright (C) 2000-2001 Ada Core Technologies, Inc.            --
+--           Copyright (C) 2000-2005 Ada Core Technologies, Inc.            --
 --                                                                          --
 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
 -- terms of the  GNU General Public License as published  by the Free Soft- --
 -- however invalidate  any other reasons why  the executable file  might be --
 -- covered by the  GNU Public License.                                      --
 --                                                                          --
--- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com).   --
+-- GNAT was originally developed  by the GNAT team at  New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc.      --
 --                                                                          --
 ------------------------------------------------------------------------------
 
+with System;       use System;
+with Ada.Calendar; use Ada.Calendar;
+
 with GNAT.IO;
-with GNAT.OS_Lib;   use GNAT.OS_Lib;
-with GNAT.Regpat;   use GNAT.Regpat;
-with System;        use System;
-with Unchecked_Conversion;
+with GNAT.OS_Lib;  use GNAT.OS_Lib;
+with GNAT.Regpat;  use GNAT.Regpat;
+
 with Unchecked_Deallocation;
-with Ada.Calendar;  use Ada.Calendar;
 
 package body GNAT.Expect is
 
-   function To_Pid is new
-     Unchecked_Conversion (OS_Lib.Process_Id, Process_Id);
-
    type Array_Of_Pd is array (Positive range <>) of Process_Descriptor_Access;
 
    procedure Expect_Internal
@@ -90,36 +87,17 @@ package body GNAT.Expect is
    pragma Import (C, Dup2);
 
    procedure Kill (Pid : Process_Id; Sig_Num : Integer);
-   pragma Import (C, Kill);
+   pragma Import (C, Kill, "__gnat_kill");
 
    function Create_Pipe (Pipe : access Pipe_Type) return Integer;
    pragma Import (C, Create_Pipe, "__gnat_pipe");
 
-   function Read
-     (Fd : File_Descriptor;
-      A  : System.Address;
-      N  : Integer) return Integer;
-   pragma Import (C, Read, "read");
-   --  Read N bytes to address A from file referenced by FD. Returned value
-   --  is count of bytes actually read, which can be less than N at EOF.
-
-   procedure Close (Fd : File_Descriptor);
-   pragma Import (C, Close);
-   --  Close a file given its file descriptor.
-
-   function Write
-     (Fd : File_Descriptor;
-      A  : System.Address;
-      N  : Integer) return Integer;
-   pragma Import (C, Write, "write");
-   --  Read N bytes to address A from file referenced by FD. Returned value
-   --  is count of bytes actually read, which can be less than N at EOF.
-
    function Poll
      (Fds     : System.Address;
       Num_Fds : Integer;
       Timeout : Integer;
-      Is_Set  : System.Address) return Integer;
+      Is_Set  : System.Address)
+      return    Integer;
    pragma Import (C, Poll, "__gnat_expect_poll");
    --  Check whether there is any data waiting on the file descriptor
    --  Out_fd, and wait if there is none, at most Timeout milliseconds
@@ -128,6 +106,10 @@ package body GNAT.Expect is
    --
    --  Out_Is_Set is set to 1 if data was available, 0 otherwise.
 
+   function Waitpid (Pid : Process_Id) return Integer;
+   pragma Import (C, Waitpid, "__gnat_waitpid");
+   --  Wait for a specific process id, and return its exit code.
+
    ---------
    -- "+" --
    ---------
@@ -171,8 +153,8 @@ package body GNAT.Expect is
          if Current = null then
             Descriptor.Filters :=
               new Filter_List_Elem'
-              (Filter => Filter, Filter_On => Filter_On,
-               User_Data => User_Data, Next => null);
+               (Filter => Filter, Filter_On => Filter_On,
+                User_Data => User_Data, Next => null);
          else
             Current.Next :=
               new Filter_List_Elem'
@@ -218,10 +200,10 @@ package body GNAT.Expect is
    -- Close --
    -----------
 
-   procedure Close (Descriptor : in out Process_Descriptor) is
-      Success : Boolean;
-      Pid     : OS_Lib.Process_Id;
-
+   procedure Close
+     (Descriptor : in out Process_Descriptor;
+      Status     : out Integer)
+   is
    begin
       Close (Descriptor.Input_Fd);
 
@@ -231,14 +213,19 @@ package body GNAT.Expect is
 
       Close (Descriptor.Output_Fd);
 
-      --  ??? Should have timeouts for different signals, see ddd
+      --  ??? Should have timeouts for different signals
       Kill (Descriptor.Pid, 9);
 
       GNAT.OS_Lib.Free (Descriptor.Buffer);
       Descriptor.Buffer_Size := 0;
 
-      Wait_Process (Pid, Success);
-      Descriptor.Pid := To_Pid (Pid);
+      Status := Waitpid (Descriptor.Pid);
+   end Close;
+
+   procedure Close (Descriptor : in out Process_Descriptor) is
+      Status : Integer;
+   begin
+      Close (Descriptor, Status);
    end Close;
 
    ------------
@@ -303,7 +290,7 @@ package body GNAT.Expect is
    is
       N           : Expect_Match;
       Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
-      Try_Until   : Time := Clock + Duration (Timeout) / 1000.0;
+      Try_Until   : constant Time := Clock + Duration (Timeout) / 1000.0;
       Timeout_Tmp : Integer := Timeout;
 
    begin
@@ -545,7 +532,7 @@ package body GNAT.Expect is
       Num_Descriptors : Integer;
       Buffer_Size     : Integer := 0;
 
-      N               : Integer;
+      N : Integer;
 
       type File_Descriptor_Array is
         array (Descriptors'Range) of File_Descriptor;
@@ -722,10 +709,10 @@ package body GNAT.Expect is
      (Descriptor : in out Process_Descriptor;
       Timeout    : Integer := 0)
    is
+      Buffer_Size     : constant Integer := 8192;
       Num_Descriptors : Integer;
       N               : Integer;
       Is_Set          : aliased Integer;
-      Buffer_Size     : Integer := 8192;
       Buffer          : aliased String (1 .. Buffer_Size);
 
    begin
@@ -775,9 +762,7 @@ package body GNAT.Expect is
    ------------------
 
    function Get_Error_Fd
-     (Descriptor : Process_Descriptor)
-      return       GNAT.OS_Lib.File_Descriptor
-   is
+     (Descriptor : Process_Descriptor) return GNAT.OS_Lib.File_Descriptor is
    begin
       return Descriptor.Error_Fd;
    end Get_Error_Fd;
@@ -787,9 +772,7 @@ package body GNAT.Expect is
    ------------------
 
    function Get_Input_Fd
-     (Descriptor : Process_Descriptor)
-      return       GNAT.OS_Lib.File_Descriptor
-   is
+     (Descriptor : Process_Descriptor) return GNAT.OS_Lib.File_Descriptor is
    begin
       return Descriptor.Input_Fd;
    end Get_Input_Fd;
@@ -799,9 +782,7 @@ package body GNAT.Expect is
    -------------------
 
    function Get_Output_Fd
-     (Descriptor : Process_Descriptor)
-      return       GNAT.OS_Lib.File_Descriptor
-   is
+     (Descriptor : Process_Descriptor) return GNAT.OS_Lib.File_Descriptor is
    begin
       return Descriptor.Output_Fd;
    end Get_Output_Fd;
@@ -811,9 +792,7 @@ package body GNAT.Expect is
    -------------
 
    function Get_Pid
-     (Descriptor : Process_Descriptor)
-      return       Process_Id
-   is
+     (Descriptor : Process_Descriptor) return Process_Id is
    begin
       return Descriptor.Pid;
    end Get_Pid;
@@ -851,15 +830,16 @@ package body GNAT.Expect is
    is
       function Fork return Process_Id;
       pragma Import (C, Fork, "__gnat_expect_fork");
-      --  Starts a new process if possible.
-      --  See the Unix command fork for more information. On systems that
-      --  don't support this capability (Windows...), this command does
-      --  nothing, and Fork will return Null_Pid.
+      --  Starts a new process if possible. See the Unix command fork for more
+      --  information. On systems that do not support this capability (such as
+      --  Windows...), this command does nothing, and Fork will return
+      --  Null_Pid.
 
       Pipe1, Pipe2, Pipe3 : aliased Pipe_Type;
 
-      Arg      : String_Access;
-      Arg_List : aliased array (1 .. Args'Length + 2) of System.Address;
+      Arg        : String_Access;
+      Arg_List   : String_List (1 .. Args'Length + 2);
+      C_Arg_List : aliased array (1 .. Args'Length + 2) of System.Address;
 
       Command_With_Path : String_Access;
 
@@ -869,6 +849,12 @@ package body GNAT.Expect is
       Set_Up_Communications
         (Descriptor, Err_To_Out, Pipe1'Access, Pipe2'Access, Pipe3'Access);
 
+      Command_With_Path := Locate_Exec_On_Path (Command);
+
+      if Command_With_Path = null then
+         raise Invalid_Process;
+      end if;
+
       --  Fork a new process
 
       Descriptor.Pid := Fork;
@@ -877,37 +863,49 @@ package body GNAT.Expect is
       --  process).
 
       if Descriptor.Pid = Null_Pid then
-
-         Command_With_Path := Locate_Exec_On_Path (Command);
-
          --  Prepare an array of arguments to pass to C
-         Arg   := new String (1 .. Command_With_Path'Length + 1);
+
+         Arg := new String (1 .. Command_With_Path'Length + 1);
          Arg (1 .. Command_With_Path'Length) := Command_With_Path.all;
-         Arg (Arg'Last)        := ASCII.Nul;
-         Arg_List (1)          := Arg.all'Address;
+         Arg (Arg'Last)        := ASCII.NUL;
+         Arg_List (1)          := Arg;
 
          for J in Args'Range loop
             Arg                     := new String (1 .. Args (J)'Length + 1);
             Arg (1 .. Args (J)'Length)  := Args (J).all;
-            Arg (Arg'Last)              := ASCII.Nul;
-            Arg_List (J + 2 - Args'First) := Arg.all'Address;
+            Arg (Arg'Last)              := ASCII.NUL;
+            Arg_List (J + 2 - Args'First)   := Arg.all'Access;
          end loop;
 
-         Arg_List (Arg_List'Last) := System.Null_Address;
+         Arg_List (Arg_List'Last) := null;
+
+         --  Make sure all arguments are compatible with OS conventions
+
+         Normalize_Arguments (Arg_List);
+
+         --  Prepare low-level argument list from the normalized arguments
+
+         for K in Arg_List'Range loop
+            if Arg_List (K) /= null then
+               C_Arg_List (K) := Arg_List (K).all'Address;
+            else
+               C_Arg_List (K) := System.Null_Address;
+            end if;
+         end loop;
 
          --  This does not return on Unix systems
 
          Set_Up_Child_Communications
            (Descriptor, Pipe1, Pipe2, Pipe3, Command_With_Path.all,
-            Arg_List'Address);
-
-         Free (Command_With_Path);
+            C_Arg_List'Address);
       end if;
 
+      Free (Command_With_Path);
+
       --  Did we have an error when spawning the child ?
 
       if Descriptor.Pid < Null_Pid then
-         null;
+         raise Invalid_Process;
       else
          --  We are now in the parent process
 
@@ -998,17 +996,19 @@ package body GNAT.Expect is
    ----------
 
    procedure Send
-     (Descriptor : in out Process_Descriptor;
-      Str        : String;
-      Add_LF     : Boolean := True;
+     (Descriptor   : in out Process_Descriptor;
+      Str          : String;
+      Add_LF       : Boolean := True;
       Empty_Buffer : Boolean := False)
    is
-      N           : Natural;
       Full_Str    : constant String := Str & ASCII.LF;
       Last        : Natural;
       Result      : Expect_Match;
       Descriptors : Array_Of_Pd := (1 => Descriptor'Unrestricted_Access);
 
+      Dummy : Natural;
+      pragma Unreferenced (Dummy);
+
    begin
       if Empty_Buffer then
 
@@ -1031,9 +1031,10 @@ package body GNAT.Expect is
 
       Call_Filters (Descriptor, Full_Str (Full_Str'First .. Last), Input);
 
-      N := Write (Descriptor.Input_Fd,
-                  Full_Str'Address,
-                  Last - Full_Str'First + 1);
+      Dummy :=
+        Write (Descriptor.Input_Fd,
+               Full_Str'Address,
+               Last - Full_Str'First + 1);
    end Send;
 
    -----------------
@@ -1061,7 +1062,11 @@ package body GNAT.Expect is
       Cmd   : in String;
       Args  : in System.Address)
    is
-      Input, Output, Error : File_Descriptor;
+      pragma Warnings (Off, Pid);
+
+      Input  : File_Descriptor;
+      Output : File_Descriptor;
+      Error  : File_Descriptor;
 
    begin
       --  Since Windows does not have a separate fork/exec, we need to
@@ -1084,7 +1089,7 @@ package body GNAT.Expect is
       Dup2 (Pipe2.Output, GNAT.OS_Lib.Standout);
       Dup2 (Pipe3.Output, GNAT.OS_Lib.Standerr);
 
-      Portable_Execvp (Cmd & ASCII.Nul, Args);
+      Portable_Execvp (Pid.Pid'Access, Cmd & ASCII.Nul, Args);
 
       --  The following commands are not executed on Unix systems, and are
       --  only required for Windows systems. We are now in the parent process.
@@ -1093,7 +1098,7 @@ package body GNAT.Expect is
 
       Dup2 (Input,  GNAT.OS_Lib.Standin);
       Dup2 (Output, GNAT.OS_Lib.Standout);
-      Dup2 (Error, GNAT.OS_Lib.Standerr);
+      Dup2 (Error,  GNAT.OS_Lib.Standerr);
       Close (Input);
       Close (Output);
       Close (Error);
@@ -1108,7 +1113,10 @@ package body GNAT.Expect is
       Err_To_Out : Boolean;
       Pipe1      : access Pipe_Type;
       Pipe2      : access Pipe_Type;
-      Pipe3      : access Pipe_Type) is
+      Pipe3      : access Pipe_Type)
+   is
+      Status : Boolean;
+
    begin
       --  Create the pipes
 
@@ -1120,18 +1128,36 @@ package body GNAT.Expect is
          return;
       end if;
 
+      --  Record the 'parent' end of the two pipes in Pid:
+      --    Child stdin  is connected to the 'write' end of Pipe1;
+      --    Child stdout is connected to the 'read'  end of Pipe2.
+      --  We do not want these descriptors to remain open in the child
+      --  process, so we mark them close-on-exec/non-inheritable.
+
       Pid.Input_Fd  := Pipe1.Output;
+      Set_Close_On_Exec (Pipe1.Output, True, Status);
       Pid.Output_Fd := Pipe2.Input;
+      Set_Close_On_Exec (Pipe2.Input, True, Status);
 
       if Err_To_Out then
+
+         --  Reuse the standard output pipe for standard error
+
          Pipe3.all := Pipe2.all;
       else
+
+         --  Create a separate pipe for standard error
+
          if Create_Pipe (Pipe3) /= 0 then
             return;
          end if;
       end if;
 
+      --  As above, we record the proper fd for the child's
+      --  standard error stream.
+
       Pid.Error_Fd := Pipe3.Input;
+      Set_Close_On_Exec (Pipe3.Input, True, Status);
    end Set_Up_Communications;
 
    ----------------------------------
@@ -1144,6 +1170,8 @@ package body GNAT.Expect is
       Pipe2 : in out Pipe_Type;
       Pipe3 : in out Pipe_Type)
    is
+      pragma Warnings (Off, Pid);
+
    begin
       Close (Pipe1.Input);
       Close (Pipe2.Output);
@@ -1159,6 +1187,9 @@ package body GNAT.Expect is
       Str        : String;
       User_Data  : System.Address := System.Null_Address)
    is
+      pragma Warnings (Off, Descriptor);
+      pragma Warnings (Off, User_Data);
+
    begin
       GNAT.IO.Put (Str);
    end Trace_Filter;