3003 lines
86 KiB
Ada
3003 lines
86 KiB
Ada
------------------------------------------------------------------------------
|
|
-- --
|
|
-- GNAT COMPILER COMPONENTS --
|
|
-- --
|
|
-- S Y S T E M . O S _ L I B --
|
|
-- --
|
|
-- B o d y --
|
|
-- --
|
|
-- Copyright (C) 1995-2015, AdaCore --
|
|
-- --
|
|
-- 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- --
|
|
-- ware Foundation; either version 3, or (at your option) any later ver- --
|
|
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
|
|
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
|
|
-- or FITNESS FOR A PARTICULAR PURPOSE. --
|
|
-- --
|
|
-- As a special exception under Section 7 of GPL version 3, you are granted --
|
|
-- additional permissions described in the GCC Runtime Library Exception, --
|
|
-- version 3.1, as published by the Free Software Foundation. --
|
|
-- --
|
|
-- You should have received a copy of the GNU General Public License and --
|
|
-- a copy of the GCC Runtime Library Exception along with this program; --
|
|
-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
|
|
-- <http://www.gnu.org/licenses/>. --
|
|
-- --
|
|
-- GNAT was originally developed by the GNAT team at New York University. --
|
|
-- Extensive contributions were provided by Ada Core Technologies Inc. --
|
|
-- --
|
|
------------------------------------------------------------------------------
|
|
|
|
pragma Compiler_Unit_Warning;
|
|
|
|
with Ada.Unchecked_Conversion;
|
|
with Ada.Unchecked_Deallocation;
|
|
with System; use System;
|
|
with System.Case_Util;
|
|
with System.CRTL;
|
|
with System.Soft_Links;
|
|
|
|
package body System.OS_Lib is
|
|
|
|
subtype size_t is CRTL.size_t;
|
|
|
|
procedure Strncpy (dest, src : System.Address; n : size_t)
|
|
renames CRTL.strncpy;
|
|
|
|
-- Imported procedures Dup and Dup2 are used in procedures Spawn and
|
|
-- Non_Blocking_Spawn.
|
|
|
|
function Dup (Fd : File_Descriptor) return File_Descriptor;
|
|
pragma Import (C, Dup, "__gnat_dup");
|
|
|
|
procedure Dup2 (Old_Fd, New_Fd : File_Descriptor);
|
|
pragma Import (C, Dup2, "__gnat_dup2");
|
|
|
|
function Copy_Attributes
|
|
(From : System.Address;
|
|
To : System.Address;
|
|
Mode : Integer) return Integer;
|
|
pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
|
|
-- Mode = 0 - copy only time stamps.
|
|
-- Mode = 1 - copy time stamps and read/write/execute attributes
|
|
-- Mode = 2 - copy read/write/execute attributes
|
|
|
|
On_Windows : constant Boolean := Directory_Separator = '\';
|
|
-- An indication that we are on Windows. Used in Normalize_Pathname, to
|
|
-- deal with drive letters in the beginning of absolute paths.
|
|
|
|
package SSL renames System.Soft_Links;
|
|
|
|
-- The following are used by Create_Temp_File
|
|
|
|
First_Temp_File_Name : constant String := "GNAT-TEMP-000000.TMP";
|
|
-- Used to initialize Current_Temp_File_Name and Temp_File_Name_Last_Digit
|
|
|
|
Current_Temp_File_Name : String := First_Temp_File_Name;
|
|
-- Name of the temp file last created
|
|
|
|
Temp_File_Name_Last_Digit : constant Positive :=
|
|
First_Temp_File_Name'Last - 4;
|
|
-- Position of the last digit in Current_Temp_File_Name
|
|
|
|
Max_Attempts : constant := 100;
|
|
-- The maximum number of attempts to create a new temp file
|
|
|
|
-----------------------
|
|
-- Local Subprograms --
|
|
-----------------------
|
|
|
|
function Args_Length (Args : Argument_List) return Natural;
|
|
-- Returns total number of characters needed to create a string of all Args
|
|
-- terminated by ASCII.NUL characters.
|
|
|
|
procedure Create_Temp_File_Internal
|
|
(FD : out File_Descriptor;
|
|
Name : out String_Access;
|
|
Stdout : Boolean);
|
|
-- Internal routine to implement two Create_Temp_File routines. If Stdout
|
|
-- is set to True the created descriptor is stdout-compatible, otherwise
|
|
-- it might not be depending on the OS. The first two parameters are as
|
|
-- in Create_Temp_File.
|
|
|
|
function C_String_Length (S : Address) return Integer;
|
|
-- Returns the length of C (null-terminated) string at S, or 0 for
|
|
-- Null_Address.
|
|
|
|
procedure Spawn_Internal
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Result : out Integer;
|
|
Pid : out Process_Id;
|
|
Blocking : Boolean);
|
|
-- Internal routine to implement the two Spawn (blocking/non blocking)
|
|
-- routines. If Blocking is set to True then the spawn is blocking
|
|
-- otherwise it is non blocking. In this latter case the Pid contains the
|
|
-- process id number. The first three parameters are as in Spawn. Note that
|
|
-- Spawn_Internal normalizes the argument list before calling the low level
|
|
-- system spawn routines (see Normalize_Arguments).
|
|
--
|
|
-- Note: Normalize_Arguments is designed to do nothing if it is called more
|
|
-- than once, so calling Normalize_Arguments before calling one of the
|
|
-- spawn routines is fine.
|
|
|
|
function To_Path_String_Access
|
|
(Path_Addr : Address;
|
|
Path_Len : Integer) return String_Access;
|
|
-- Converts a C String to an Ada String. We could do this making use of
|
|
-- Interfaces.C.Strings but we prefer not to import that entire package
|
|
|
|
---------
|
|
-- "<" --
|
|
---------
|
|
|
|
function "<" (X, Y : OS_Time) return Boolean is
|
|
begin
|
|
return Long_Integer (X) < Long_Integer (Y);
|
|
end "<";
|
|
|
|
----------
|
|
-- "<=" --
|
|
----------
|
|
|
|
function "<=" (X, Y : OS_Time) return Boolean is
|
|
begin
|
|
return Long_Integer (X) <= Long_Integer (Y);
|
|
end "<=";
|
|
|
|
---------
|
|
-- ">" --
|
|
---------
|
|
|
|
function ">" (X, Y : OS_Time) return Boolean is
|
|
begin
|
|
return Long_Integer (X) > Long_Integer (Y);
|
|
end ">";
|
|
|
|
----------
|
|
-- ">=" --
|
|
----------
|
|
|
|
function ">=" (X, Y : OS_Time) return Boolean is
|
|
begin
|
|
return Long_Integer (X) >= Long_Integer (Y);
|
|
end ">=";
|
|
|
|
-----------------
|
|
-- Args_Length --
|
|
-----------------
|
|
|
|
function Args_Length (Args : Argument_List) return Natural is
|
|
Len : Natural := 0;
|
|
|
|
begin
|
|
for J in Args'Range loop
|
|
Len := Len + Args (J)'Length + 1; -- One extra for ASCII.NUL
|
|
end loop;
|
|
|
|
return Len;
|
|
end Args_Length;
|
|
|
|
-----------------------------
|
|
-- Argument_String_To_List --
|
|
-----------------------------
|
|
|
|
function Argument_String_To_List
|
|
(Arg_String : String) return Argument_List_Access
|
|
is
|
|
Max_Args : constant Integer := Arg_String'Length;
|
|
New_Argv : Argument_List (1 .. Max_Args);
|
|
Idx : Integer;
|
|
New_Argc : Natural := 0;
|
|
|
|
Cleaned : String (1 .. Arg_String'Length);
|
|
Cleaned_Idx : Natural;
|
|
-- A cleaned up version of the argument. This function is taking
|
|
-- backslash escapes when computing the bounds for arguments. It is
|
|
-- then removing the extra backslashes from the argument.
|
|
|
|
Backslash_Is_Sep : constant Boolean := Directory_Separator = '\';
|
|
-- Whether '\' is a directory separator (as on Windows), or a way to
|
|
-- quote special characters.
|
|
|
|
begin
|
|
Idx := Arg_String'First;
|
|
|
|
loop
|
|
exit when Idx > Arg_String'Last;
|
|
|
|
declare
|
|
Backqd : Boolean := False;
|
|
Quoted : Boolean := False;
|
|
|
|
begin
|
|
Cleaned_Idx := Cleaned'First;
|
|
|
|
loop
|
|
-- An unquoted space is the end of an argument
|
|
|
|
if not (Backqd or Quoted)
|
|
and then Arg_String (Idx) = ' '
|
|
then
|
|
exit;
|
|
|
|
-- Start of a quoted string
|
|
|
|
elsif not (Backqd or Quoted)
|
|
and then Arg_String (Idx) = '"'
|
|
then
|
|
Quoted := True;
|
|
Cleaned (Cleaned_Idx) := Arg_String (Idx);
|
|
Cleaned_Idx := Cleaned_Idx + 1;
|
|
|
|
-- End of a quoted string and end of an argument
|
|
|
|
elsif (Quoted and not Backqd)
|
|
and then Arg_String (Idx) = '"'
|
|
then
|
|
Cleaned (Cleaned_Idx) := Arg_String (Idx);
|
|
Cleaned_Idx := Cleaned_Idx + 1;
|
|
Idx := Idx + 1;
|
|
exit;
|
|
|
|
-- Turn off backquoting after advancing one character
|
|
|
|
elsif Backqd then
|
|
Backqd := False;
|
|
Cleaned (Cleaned_Idx) := Arg_String (Idx);
|
|
Cleaned_Idx := Cleaned_Idx + 1;
|
|
|
|
-- Following character is backquoted
|
|
|
|
elsif not Backslash_Is_Sep and then Arg_String (Idx) = '\' then
|
|
Backqd := True;
|
|
|
|
else
|
|
Cleaned (Cleaned_Idx) := Arg_String (Idx);
|
|
Cleaned_Idx := Cleaned_Idx + 1;
|
|
end if;
|
|
|
|
Idx := Idx + 1;
|
|
exit when Idx > Arg_String'Last;
|
|
end loop;
|
|
|
|
-- Found an argument
|
|
|
|
New_Argc := New_Argc + 1;
|
|
New_Argv (New_Argc) :=
|
|
new String'(Cleaned (Cleaned'First .. Cleaned_Idx - 1));
|
|
|
|
-- Skip extraneous spaces
|
|
|
|
while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop
|
|
Idx := Idx + 1;
|
|
end loop;
|
|
end;
|
|
end loop;
|
|
|
|
return new Argument_List'(New_Argv (1 .. New_Argc));
|
|
end Argument_String_To_List;
|
|
|
|
---------------------
|
|
-- C_String_Length --
|
|
---------------------
|
|
|
|
function C_String_Length (S : Address) return Integer is
|
|
begin
|
|
if S = Null_Address then
|
|
return 0;
|
|
else
|
|
return Integer (CRTL.strlen (S));
|
|
end if;
|
|
end C_String_Length;
|
|
|
|
-----------
|
|
-- Close --
|
|
-----------
|
|
|
|
procedure Close (FD : File_Descriptor) is
|
|
use CRTL;
|
|
Discard : constant int := close (int (FD));
|
|
begin
|
|
null;
|
|
end Close;
|
|
|
|
procedure Close (FD : File_Descriptor; Status : out Boolean) is
|
|
use CRTL;
|
|
begin
|
|
Status := (close (int (FD)) = 0);
|
|
end Close;
|
|
|
|
---------------
|
|
-- Copy_File --
|
|
---------------
|
|
|
|
procedure Copy_File
|
|
(Name : String;
|
|
Pathname : String;
|
|
Success : out Boolean;
|
|
Mode : Copy_Mode := Copy;
|
|
Preserve : Attribute := Time_Stamps)
|
|
is
|
|
From : File_Descriptor;
|
|
To : File_Descriptor;
|
|
|
|
Copy_Error : exception;
|
|
-- Internal exception raised to signal error in copy
|
|
|
|
function Build_Path (Dir : String; File : String) return String;
|
|
-- Returns pathname Dir concatenated with File adding the directory
|
|
-- separator only if needed.
|
|
|
|
procedure Copy (From : File_Descriptor; To : File_Descriptor);
|
|
-- Read data from From and place them into To. In both cases the
|
|
-- operations uses the current file position. Raises Constraint_Error
|
|
-- if a problem occurs during the copy.
|
|
|
|
procedure Copy_To (To_Name : String);
|
|
-- Does a straight copy from source to designated destination file
|
|
|
|
----------------
|
|
-- Build_Path --
|
|
----------------
|
|
|
|
function Build_Path (Dir : String; File : String) return String is
|
|
function Is_Dirsep (C : Character) return Boolean;
|
|
pragma Inline (Is_Dirsep);
|
|
-- Returns True if C is a directory separator. On Windows we
|
|
-- handle both styles of directory separator.
|
|
|
|
---------------
|
|
-- Is_Dirsep --
|
|
---------------
|
|
|
|
function Is_Dirsep (C : Character) return Boolean is
|
|
begin
|
|
return C = Directory_Separator or else C = '/';
|
|
end Is_Dirsep;
|
|
|
|
-- Local variables
|
|
|
|
Base_File_Ptr : Integer;
|
|
-- The base file name is File (Base_File_Ptr + 1 .. File'Last)
|
|
|
|
Res : String (1 .. Dir'Length + File'Length + 1);
|
|
|
|
-- Start of processing for Build_Path
|
|
|
|
begin
|
|
-- Find base file name
|
|
|
|
Base_File_Ptr := File'Last;
|
|
while Base_File_Ptr >= File'First loop
|
|
exit when Is_Dirsep (File (Base_File_Ptr));
|
|
Base_File_Ptr := Base_File_Ptr - 1;
|
|
end loop;
|
|
|
|
declare
|
|
Base_File : String renames
|
|
File (Base_File_Ptr + 1 .. File'Last);
|
|
|
|
begin
|
|
Res (1 .. Dir'Length) := Dir;
|
|
|
|
if Is_Dirsep (Dir (Dir'Last)) then
|
|
Res (Dir'Length + 1 .. Dir'Length + Base_File'Length) :=
|
|
Base_File;
|
|
return Res (1 .. Dir'Length + Base_File'Length);
|
|
|
|
else
|
|
Res (Dir'Length + 1) := Directory_Separator;
|
|
Res (Dir'Length + 2 .. Dir'Length + 1 + Base_File'Length) :=
|
|
Base_File;
|
|
return Res (1 .. Dir'Length + 1 + Base_File'Length);
|
|
end if;
|
|
end;
|
|
end Build_Path;
|
|
|
|
----------
|
|
-- Copy --
|
|
----------
|
|
|
|
procedure Copy (From : File_Descriptor; To : File_Descriptor) is
|
|
Buf_Size : constant := 200_000;
|
|
type Buf is array (1 .. Buf_Size) of Character;
|
|
type Buf_Ptr is access Buf;
|
|
|
|
Buffer : Buf_Ptr;
|
|
R : Integer;
|
|
W : Integer;
|
|
|
|
Status_From : Boolean;
|
|
Status_To : Boolean;
|
|
-- Statuses for the calls to Close
|
|
|
|
procedure Free is new Ada.Unchecked_Deallocation (Buf, Buf_Ptr);
|
|
|
|
begin
|
|
-- Check for invalid descriptors, making sure that we do not
|
|
-- accidentally leave an open file descriptor around.
|
|
|
|
if From = Invalid_FD then
|
|
if To /= Invalid_FD then
|
|
Close (To, Status_To);
|
|
end if;
|
|
|
|
raise Copy_Error;
|
|
|
|
elsif To = Invalid_FD then
|
|
Close (From, Status_From);
|
|
raise Copy_Error;
|
|
end if;
|
|
|
|
-- Allocate the buffer on the heap
|
|
|
|
Buffer := new Buf;
|
|
|
|
loop
|
|
R := Read (From, Buffer (1)'Address, Buf_Size);
|
|
|
|
-- On some systems, the buffer may not be full. So, we need to try
|
|
-- again until there is nothing to read.
|
|
|
|
exit when R = 0;
|
|
|
|
W := Write (To, Buffer (1)'Address, R);
|
|
|
|
if W < R then
|
|
|
|
-- Problem writing data, could be a disk full. Close files
|
|
-- without worrying about status, since we are raising a
|
|
-- Copy_Error exception in any case.
|
|
|
|
Close (From, Status_From);
|
|
Close (To, Status_To);
|
|
|
|
Free (Buffer);
|
|
|
|
raise Copy_Error;
|
|
end if;
|
|
end loop;
|
|
|
|
Close (From, Status_From);
|
|
Close (To, Status_To);
|
|
|
|
Free (Buffer);
|
|
|
|
if not (Status_From and Status_To) then
|
|
raise Copy_Error;
|
|
end if;
|
|
end Copy;
|
|
|
|
-------------
|
|
-- Copy_To --
|
|
-------------
|
|
|
|
procedure Copy_To (To_Name : String) is
|
|
C_From : String (1 .. Name'Length + 1);
|
|
C_To : String (1 .. To_Name'Length + 1);
|
|
|
|
begin
|
|
From := Open_Read (Name, Binary);
|
|
|
|
-- Do not clobber destination file if source file could not be opened
|
|
|
|
if From /= Invalid_FD then
|
|
To := Create_File (To_Name, Binary);
|
|
end if;
|
|
|
|
Copy (From, To);
|
|
|
|
-- Copy attributes
|
|
|
|
C_From (1 .. Name'Length) := Name;
|
|
C_From (C_From'Last) := ASCII.NUL;
|
|
|
|
C_To (1 .. To_Name'Length) := To_Name;
|
|
C_To (C_To'Last) := ASCII.NUL;
|
|
|
|
case Preserve is
|
|
when Time_Stamps =>
|
|
if Copy_Attributes (C_From'Address, C_To'Address, 0) = -1 then
|
|
raise Copy_Error;
|
|
end if;
|
|
|
|
when Full =>
|
|
if Copy_Attributes (C_From'Address, C_To'Address, 1) = -1 then
|
|
raise Copy_Error;
|
|
end if;
|
|
|
|
when None =>
|
|
null;
|
|
end case;
|
|
|
|
end Copy_To;
|
|
|
|
-- Start of processing for Copy_File
|
|
|
|
begin
|
|
Success := True;
|
|
|
|
-- The source file must exist
|
|
|
|
if not Is_Regular_File (Name) then
|
|
raise Copy_Error;
|
|
end if;
|
|
|
|
-- The source file exists
|
|
|
|
case Mode is
|
|
|
|
-- Copy case, target file must not exist
|
|
|
|
when Copy =>
|
|
|
|
-- If the target file exists, we have an error
|
|
|
|
if Is_Regular_File (Pathname) then
|
|
raise Copy_Error;
|
|
|
|
-- Case of target is a directory
|
|
|
|
elsif Is_Directory (Pathname) then
|
|
declare
|
|
Dest : constant String := Build_Path (Pathname, Name);
|
|
|
|
begin
|
|
-- If target file exists, we have an error, else do copy
|
|
|
|
if Is_Regular_File (Dest) then
|
|
raise Copy_Error;
|
|
else
|
|
Copy_To (Dest);
|
|
end if;
|
|
end;
|
|
|
|
-- Case of normal copy to file (destination does not exist)
|
|
|
|
else
|
|
Copy_To (Pathname);
|
|
end if;
|
|
|
|
-- Overwrite case (destination file may or may not exist)
|
|
|
|
when Overwrite =>
|
|
if Is_Directory (Pathname) then
|
|
Copy_To (Build_Path (Pathname, Name));
|
|
else
|
|
Copy_To (Pathname);
|
|
end if;
|
|
|
|
-- Append case (destination file may or may not exist)
|
|
|
|
when Append =>
|
|
|
|
-- Appending to existing file
|
|
|
|
if Is_Regular_File (Pathname) then
|
|
|
|
-- Append mode and destination file exists, append data at the
|
|
-- end of Pathname. But if we fail to open source file, do not
|
|
-- touch destination file at all.
|
|
|
|
From := Open_Read (Name, Binary);
|
|
if From /= Invalid_FD then
|
|
To := Open_Read_Write (Pathname, Binary);
|
|
end if;
|
|
|
|
Lseek (To, 0, Seek_End);
|
|
|
|
Copy (From, To);
|
|
|
|
-- Appending to directory, not allowed
|
|
|
|
elsif Is_Directory (Pathname) then
|
|
raise Copy_Error;
|
|
|
|
-- Appending when target file does not exist
|
|
|
|
else
|
|
Copy_To (Pathname);
|
|
end if;
|
|
end case;
|
|
|
|
-- All error cases are caught here
|
|
|
|
exception
|
|
when Copy_Error =>
|
|
Success := False;
|
|
end Copy_File;
|
|
|
|
procedure Copy_File
|
|
(Name : C_File_Name;
|
|
Pathname : C_File_Name;
|
|
Success : out Boolean;
|
|
Mode : Copy_Mode := Copy;
|
|
Preserve : Attribute := Time_Stamps)
|
|
is
|
|
Ada_Name : String_Access :=
|
|
To_Path_String_Access
|
|
(Name, C_String_Length (Name));
|
|
Ada_Pathname : String_Access :=
|
|
To_Path_String_Access
|
|
(Pathname, C_String_Length (Pathname));
|
|
begin
|
|
Copy_File (Ada_Name.all, Ada_Pathname.all, Success, Mode, Preserve);
|
|
Free (Ada_Name);
|
|
Free (Ada_Pathname);
|
|
end Copy_File;
|
|
|
|
--------------------------
|
|
-- Copy_File_Attributes --
|
|
--------------------------
|
|
|
|
procedure Copy_File_Attributes
|
|
(From : String;
|
|
To : String;
|
|
Success : out Boolean;
|
|
Copy_Timestamp : Boolean := True;
|
|
Copy_Permissions : Boolean := True)
|
|
is
|
|
F : aliased String (1 .. From'Length + 1);
|
|
Mode : Integer;
|
|
T : aliased String (1 .. To'Length + 1);
|
|
|
|
begin
|
|
if Copy_Timestamp then
|
|
if Copy_Permissions then
|
|
Mode := 1;
|
|
else
|
|
Mode := 0;
|
|
end if;
|
|
else
|
|
if Copy_Permissions then
|
|
Mode := 2;
|
|
else
|
|
Success := True;
|
|
return; -- nothing to do
|
|
end if;
|
|
end if;
|
|
|
|
F (1 .. From'Length) := From;
|
|
F (F'Last) := ASCII.NUL;
|
|
|
|
T (1 .. To'Length) := To;
|
|
T (T'Last) := ASCII.NUL;
|
|
|
|
Success := Copy_Attributes (F'Address, T'Address, Mode) /= -1;
|
|
end Copy_File_Attributes;
|
|
|
|
----------------------
|
|
-- Copy_Time_Stamps --
|
|
----------------------
|
|
|
|
procedure Copy_Time_Stamps
|
|
(Source : String;
|
|
Dest : String;
|
|
Success : out Boolean)
|
|
is
|
|
begin
|
|
if Is_Regular_File (Source) and then Is_Writable_File (Dest) then
|
|
declare
|
|
C_Source : String (1 .. Source'Length + 1);
|
|
C_Dest : String (1 .. Dest'Length + 1);
|
|
|
|
begin
|
|
C_Source (1 .. Source'Length) := Source;
|
|
C_Source (C_Source'Last) := ASCII.NUL;
|
|
|
|
C_Dest (1 .. Dest'Length) := Dest;
|
|
C_Dest (C_Dest'Last) := ASCII.NUL;
|
|
|
|
if Copy_Attributes (C_Source'Address, C_Dest'Address, 0) = -1 then
|
|
Success := False;
|
|
else
|
|
Success := True;
|
|
end if;
|
|
end;
|
|
|
|
else
|
|
Success := False;
|
|
end if;
|
|
end Copy_Time_Stamps;
|
|
|
|
procedure Copy_Time_Stamps
|
|
(Source : C_File_Name;
|
|
Dest : C_File_Name;
|
|
Success : out Boolean)
|
|
is
|
|
Ada_Source : String_Access :=
|
|
To_Path_String_Access
|
|
(Source, C_String_Length (Source));
|
|
Ada_Dest : String_Access :=
|
|
To_Path_String_Access
|
|
(Dest, C_String_Length (Dest));
|
|
begin
|
|
Copy_Time_Stamps (Ada_Source.all, Ada_Dest.all, Success);
|
|
Free (Ada_Source);
|
|
Free (Ada_Dest);
|
|
end Copy_Time_Stamps;
|
|
|
|
-----------------
|
|
-- Create_File --
|
|
-----------------
|
|
|
|
function Create_File
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
function C_Create_File
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor;
|
|
pragma Import (C, C_Create_File, "__gnat_open_create");
|
|
begin
|
|
return C_Create_File (Name, Fmode);
|
|
end Create_File;
|
|
|
|
function Create_File
|
|
(Name : String;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return Create_File (C_Name (C_Name'First)'Address, Fmode);
|
|
end Create_File;
|
|
|
|
---------------------
|
|
-- Create_New_File --
|
|
---------------------
|
|
|
|
function Create_New_File
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
function C_Create_New_File
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor;
|
|
pragma Import (C, C_Create_New_File, "__gnat_open_new");
|
|
begin
|
|
return C_Create_New_File (Name, Fmode);
|
|
end Create_New_File;
|
|
|
|
function Create_New_File
|
|
(Name : String;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return Create_New_File (C_Name (C_Name'First)'Address, Fmode);
|
|
end Create_New_File;
|
|
|
|
-----------------------------
|
|
-- Create_Output_Text_File --
|
|
-----------------------------
|
|
|
|
function Create_Output_Text_File (Name : String) return File_Descriptor is
|
|
function C_Create_File (Name : C_File_Name) return File_Descriptor;
|
|
pragma Import (C, C_Create_File, "__gnat_create_output_file");
|
|
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return C_Create_File (C_Name (C_Name'First)'Address);
|
|
end Create_Output_Text_File;
|
|
|
|
----------------------
|
|
-- Create_Temp_File --
|
|
----------------------
|
|
|
|
procedure Create_Temp_File
|
|
(FD : out File_Descriptor;
|
|
Name : out Temp_File_Name)
|
|
is
|
|
function Open_New_Temp
|
|
(Name : System.Address;
|
|
Fmode : Mode) return File_Descriptor;
|
|
pragma Import (C, Open_New_Temp, "__gnat_open_new_temp");
|
|
|
|
begin
|
|
FD := Open_New_Temp (Name'Address, Binary);
|
|
end Create_Temp_File;
|
|
|
|
procedure Create_Temp_File
|
|
(FD : out File_Descriptor;
|
|
Name : out String_Access)
|
|
is
|
|
begin
|
|
Create_Temp_File_Internal (FD, Name, Stdout => False);
|
|
end Create_Temp_File;
|
|
|
|
-----------------------------
|
|
-- Create_Temp_Output_File --
|
|
-----------------------------
|
|
|
|
procedure Create_Temp_Output_File
|
|
(FD : out File_Descriptor;
|
|
Name : out String_Access)
|
|
is
|
|
begin
|
|
Create_Temp_File_Internal (FD, Name, Stdout => True);
|
|
end Create_Temp_Output_File;
|
|
|
|
-------------------------------
|
|
-- Create_Temp_File_Internal --
|
|
-------------------------------
|
|
|
|
procedure Create_Temp_File_Internal
|
|
(FD : out File_Descriptor;
|
|
Name : out String_Access;
|
|
Stdout : Boolean)
|
|
is
|
|
Pos : Positive;
|
|
Attempts : Natural := 0;
|
|
Current : String (Current_Temp_File_Name'Range);
|
|
|
|
function Create_New_Output_Text_File
|
|
(Name : String) return File_Descriptor;
|
|
-- Similar to Create_Output_Text_File, except it fails if the file
|
|
-- already exists. We need this behavior to ensure we don't accidentally
|
|
-- open a temp file that has just been created by a concurrently running
|
|
-- process. There is no point exposing this function, as it's generally
|
|
-- not particularly useful.
|
|
|
|
---------------------------------
|
|
-- Create_New_Output_Text_File --
|
|
---------------------------------
|
|
|
|
function Create_New_Output_Text_File
|
|
(Name : String) return File_Descriptor
|
|
is
|
|
function C_Create_File (Name : C_File_Name) return File_Descriptor;
|
|
pragma Import (C, C_Create_File, "__gnat_create_output_file_new");
|
|
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return C_Create_File (C_Name (C_Name'First)'Address);
|
|
end Create_New_Output_Text_File;
|
|
|
|
-- Start of processing for Create_Temp_File_Internal
|
|
|
|
begin
|
|
-- Loop until a new temp file can be created
|
|
|
|
File_Loop : loop
|
|
Locked : begin
|
|
|
|
-- We need to protect global variable Current_Temp_File_Name
|
|
-- against concurrent access by different tasks.
|
|
|
|
SSL.Lock_Task.all;
|
|
|
|
-- Start at the last digit
|
|
|
|
Pos := Temp_File_Name_Last_Digit;
|
|
|
|
Digit_Loop :
|
|
loop
|
|
-- Increment the digit by one
|
|
|
|
case Current_Temp_File_Name (Pos) is
|
|
when '0' .. '8' =>
|
|
Current_Temp_File_Name (Pos) :=
|
|
Character'Succ (Current_Temp_File_Name (Pos));
|
|
exit Digit_Loop;
|
|
|
|
when '9' =>
|
|
|
|
-- For 9, set the digit to 0 and go to the previous digit
|
|
|
|
Current_Temp_File_Name (Pos) := '0';
|
|
Pos := Pos - 1;
|
|
|
|
when others =>
|
|
|
|
-- If it is not a digit, then there are no available
|
|
-- temp file names. Return Invalid_FD. There is almost no
|
|
-- chance that this code will be ever be executed, since
|
|
-- it would mean that there are one million temp files in
|
|
-- the same directory.
|
|
|
|
SSL.Unlock_Task.all;
|
|
FD := Invalid_FD;
|
|
Name := null;
|
|
exit File_Loop;
|
|
end case;
|
|
end loop Digit_Loop;
|
|
|
|
Current := Current_Temp_File_Name;
|
|
|
|
-- We can now release the lock, because we are no longer accessing
|
|
-- Current_Temp_File_Name.
|
|
|
|
SSL.Unlock_Task.all;
|
|
|
|
exception
|
|
when others =>
|
|
SSL.Unlock_Task.all;
|
|
raise;
|
|
end Locked;
|
|
|
|
-- Attempt to create the file
|
|
|
|
if Stdout then
|
|
FD := Create_New_Output_Text_File (Current);
|
|
else
|
|
FD := Create_New_File (Current, Binary);
|
|
end if;
|
|
|
|
if FD /= Invalid_FD then
|
|
Name := new String'(Current);
|
|
exit File_Loop;
|
|
end if;
|
|
|
|
if not Is_Regular_File (Current) then
|
|
|
|
-- If the file does not already exist and we are unable to create
|
|
-- it, we give up after Max_Attempts. Otherwise, we try again with
|
|
-- the next available file name.
|
|
|
|
Attempts := Attempts + 1;
|
|
|
|
if Attempts >= Max_Attempts then
|
|
FD := Invalid_FD;
|
|
Name := null;
|
|
exit File_Loop;
|
|
end if;
|
|
end if;
|
|
end loop File_Loop;
|
|
end Create_Temp_File_Internal;
|
|
|
|
-------------------------
|
|
-- Current_Time_String --
|
|
-------------------------
|
|
|
|
function Current_Time_String return String is
|
|
subtype S23 is String (1 .. 23);
|
|
-- Holds current time in ISO 8601 format YYYY-MM-DD HH:MM:SS.SS + NUL
|
|
|
|
procedure Current_Time_String (Time : System.Address);
|
|
pragma Import (C, Current_Time_String, "__gnat_current_time_string");
|
|
-- Puts current time into Time in above ISO 8601 format
|
|
|
|
Result23 : aliased S23;
|
|
-- Current time in ISO 8601 format
|
|
|
|
begin
|
|
Current_Time_String (Result23'Address);
|
|
return Result23 (1 .. 19);
|
|
end Current_Time_String;
|
|
|
|
-----------------
|
|
-- Delete_File --
|
|
-----------------
|
|
|
|
procedure Delete_File (Name : Address; Success : out Boolean) is
|
|
R : Integer;
|
|
begin
|
|
R := System.CRTL.unlink (Name);
|
|
Success := (R = 0);
|
|
end Delete_File;
|
|
|
|
procedure Delete_File (Name : String; Success : out Boolean) is
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
Delete_File (C_Name'Address, Success);
|
|
end Delete_File;
|
|
|
|
-------------------
|
|
-- Errno_Message --
|
|
-------------------
|
|
|
|
function Errno_Message
|
|
(Err : Integer := Errno;
|
|
Default : String := "") return String
|
|
is
|
|
function strerror (errnum : Integer) return System.Address;
|
|
pragma Import (C, strerror, "strerror");
|
|
|
|
C_Msg : constant System.Address := strerror (Err);
|
|
|
|
begin
|
|
if C_Msg = Null_Address then
|
|
if Default /= "" then
|
|
return Default;
|
|
|
|
else
|
|
-- Note: for bootstrap reasons, it is impractical
|
|
-- to use Integer'Image here.
|
|
|
|
declare
|
|
Val : Integer;
|
|
First : Integer;
|
|
|
|
Buf : String (1 .. 20);
|
|
-- Buffer large enough to hold image of largest Integer values
|
|
|
|
begin
|
|
Val := abs Err;
|
|
First := Buf'Last;
|
|
loop
|
|
Buf (First) :=
|
|
Character'Val (Character'Pos ('0') + Val mod 10);
|
|
Val := Val / 10;
|
|
exit when Val = 0;
|
|
First := First - 1;
|
|
end loop;
|
|
|
|
if Err < 0 then
|
|
First := First - 1;
|
|
Buf (First) := '-';
|
|
end if;
|
|
|
|
return "errno = " & Buf (First .. Buf'Last);
|
|
end;
|
|
end if;
|
|
|
|
else
|
|
declare
|
|
Msg : String (1 .. Integer (CRTL.strlen (C_Msg)));
|
|
for Msg'Address use C_Msg;
|
|
pragma Import (Ada, Msg);
|
|
begin
|
|
return Msg;
|
|
end;
|
|
end if;
|
|
end Errno_Message;
|
|
|
|
---------------------
|
|
-- File_Time_Stamp --
|
|
---------------------
|
|
|
|
function File_Time_Stamp (FD : File_Descriptor) return OS_Time is
|
|
function File_Time (FD : File_Descriptor) return OS_Time;
|
|
pragma Import (C, File_Time, "__gnat_file_time_fd");
|
|
begin
|
|
return File_Time (FD);
|
|
end File_Time_Stamp;
|
|
|
|
function File_Time_Stamp (Name : C_File_Name) return OS_Time is
|
|
function File_Time (Name : Address) return OS_Time;
|
|
pragma Import (C, File_Time, "__gnat_file_time_name");
|
|
begin
|
|
return File_Time (Name);
|
|
end File_Time_Stamp;
|
|
|
|
function File_Time_Stamp (Name : String) return OS_Time is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return File_Time_Stamp (F_Name'Address);
|
|
end File_Time_Stamp;
|
|
|
|
---------------------------
|
|
-- Get_Debuggable_Suffix --
|
|
---------------------------
|
|
|
|
function Get_Debuggable_Suffix return String_Access is
|
|
procedure Get_Suffix_Ptr (Length, Ptr : Address);
|
|
pragma Import (C, Get_Suffix_Ptr, "__gnat_get_debuggable_suffix_ptr");
|
|
|
|
Result : String_Access;
|
|
Suffix_Length : Integer;
|
|
Suffix_Ptr : Address;
|
|
|
|
begin
|
|
Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
|
|
Result := new String (1 .. Suffix_Length);
|
|
|
|
if Suffix_Length > 0 then
|
|
Strncpy (Result.all'Address, Suffix_Ptr, size_t (Suffix_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Get_Debuggable_Suffix;
|
|
|
|
---------------------------
|
|
-- Get_Executable_Suffix --
|
|
---------------------------
|
|
|
|
function Get_Executable_Suffix return String_Access is
|
|
procedure Get_Suffix_Ptr (Length, Ptr : Address);
|
|
pragma Import (C, Get_Suffix_Ptr, "__gnat_get_executable_suffix_ptr");
|
|
|
|
Result : String_Access;
|
|
Suffix_Length : Integer;
|
|
Suffix_Ptr : Address;
|
|
|
|
begin
|
|
Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
|
|
Result := new String (1 .. Suffix_Length);
|
|
|
|
if Suffix_Length > 0 then
|
|
Strncpy (Result.all'Address, Suffix_Ptr, size_t (Suffix_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Get_Executable_Suffix;
|
|
|
|
-----------------------
|
|
-- Get_Object_Suffix --
|
|
-----------------------
|
|
|
|
function Get_Object_Suffix return String_Access is
|
|
procedure Get_Suffix_Ptr (Length, Ptr : Address);
|
|
pragma Import (C, Get_Suffix_Ptr, "__gnat_get_object_suffix_ptr");
|
|
|
|
Result : String_Access;
|
|
Suffix_Length : Integer;
|
|
Suffix_Ptr : Address;
|
|
|
|
begin
|
|
Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
|
|
Result := new String (1 .. Suffix_Length);
|
|
|
|
if Suffix_Length > 0 then
|
|
Strncpy (Result.all'Address, Suffix_Ptr, size_t (Suffix_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Get_Object_Suffix;
|
|
|
|
----------------------------------
|
|
-- Get_Target_Debuggable_Suffix --
|
|
----------------------------------
|
|
|
|
function Get_Target_Debuggable_Suffix return String_Access is
|
|
Target_Exec_Ext_Ptr : Address;
|
|
pragma Import
|
|
(C, Target_Exec_Ext_Ptr, "__gnat_target_debuggable_extension");
|
|
|
|
Result : String_Access;
|
|
Suffix_Length : Integer;
|
|
|
|
begin
|
|
Suffix_Length := Integer (CRTL.strlen (Target_Exec_Ext_Ptr));
|
|
Result := new String (1 .. Suffix_Length);
|
|
|
|
if Suffix_Length > 0 then
|
|
Strncpy
|
|
(Result.all'Address, Target_Exec_Ext_Ptr, size_t (Suffix_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Get_Target_Debuggable_Suffix;
|
|
|
|
----------------------------------
|
|
-- Get_Target_Executable_Suffix --
|
|
----------------------------------
|
|
|
|
function Get_Target_Executable_Suffix return String_Access is
|
|
Target_Exec_Ext_Ptr : Address;
|
|
pragma Import
|
|
(C, Target_Exec_Ext_Ptr, "__gnat_target_executable_extension");
|
|
|
|
Result : String_Access;
|
|
Suffix_Length : Integer;
|
|
|
|
begin
|
|
Suffix_Length := Integer (CRTL.strlen (Target_Exec_Ext_Ptr));
|
|
Result := new String (1 .. Suffix_Length);
|
|
|
|
if Suffix_Length > 0 then
|
|
Strncpy
|
|
(Result.all'Address, Target_Exec_Ext_Ptr, size_t (Suffix_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Get_Target_Executable_Suffix;
|
|
|
|
------------------------------
|
|
-- Get_Target_Object_Suffix --
|
|
------------------------------
|
|
|
|
function Get_Target_Object_Suffix return String_Access is
|
|
Target_Object_Ext_Ptr : Address;
|
|
pragma Import
|
|
(C, Target_Object_Ext_Ptr, "__gnat_target_object_extension");
|
|
|
|
Result : String_Access;
|
|
Suffix_Length : Integer;
|
|
|
|
begin
|
|
Suffix_Length := Integer (CRTL.strlen (Target_Object_Ext_Ptr));
|
|
Result := new String (1 .. Suffix_Length);
|
|
|
|
if Suffix_Length > 0 then
|
|
Strncpy
|
|
(Result.all'Address, Target_Object_Ext_Ptr, size_t (Suffix_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Get_Target_Object_Suffix;
|
|
|
|
------------
|
|
-- Getenv --
|
|
------------
|
|
|
|
function Getenv (Name : String) return String_Access is
|
|
procedure Get_Env_Value_Ptr (Name, Length, Ptr : Address);
|
|
pragma Import (C, Get_Env_Value_Ptr, "__gnat_getenv");
|
|
|
|
Env_Value_Ptr : aliased Address;
|
|
Env_Value_Length : aliased Integer;
|
|
F_Name : aliased String (1 .. Name'Length + 1);
|
|
Result : String_Access;
|
|
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
|
|
Get_Env_Value_Ptr
|
|
(F_Name'Address, Env_Value_Length'Address, Env_Value_Ptr'Address);
|
|
|
|
Result := new String (1 .. Env_Value_Length);
|
|
|
|
if Env_Value_Length > 0 then
|
|
Strncpy
|
|
(Result.all'Address, Env_Value_Ptr, size_t (Env_Value_Length));
|
|
end if;
|
|
|
|
return Result;
|
|
end Getenv;
|
|
|
|
------------
|
|
-- GM_Day --
|
|
------------
|
|
|
|
function GM_Day (Date : OS_Time) return Day_Type is
|
|
D : Day_Type;
|
|
|
|
Y : Year_Type;
|
|
Mo : Month_Type;
|
|
H : Hour_Type;
|
|
Mn : Minute_Type;
|
|
S : Second_Type;
|
|
pragma Unreferenced (Y, Mo, H, Mn, S);
|
|
|
|
begin
|
|
GM_Split (Date, Y, Mo, D, H, Mn, S);
|
|
return D;
|
|
end GM_Day;
|
|
|
|
-------------
|
|
-- GM_Hour --
|
|
-------------
|
|
|
|
function GM_Hour (Date : OS_Time) return Hour_Type is
|
|
H : Hour_Type;
|
|
|
|
Y : Year_Type;
|
|
Mo : Month_Type;
|
|
D : Day_Type;
|
|
Mn : Minute_Type;
|
|
S : Second_Type;
|
|
pragma Unreferenced (Y, Mo, D, Mn, S);
|
|
|
|
begin
|
|
GM_Split (Date, Y, Mo, D, H, Mn, S);
|
|
return H;
|
|
end GM_Hour;
|
|
|
|
---------------
|
|
-- GM_Minute --
|
|
---------------
|
|
|
|
function GM_Minute (Date : OS_Time) return Minute_Type is
|
|
Mn : Minute_Type;
|
|
|
|
Y : Year_Type;
|
|
Mo : Month_Type;
|
|
D : Day_Type;
|
|
H : Hour_Type;
|
|
S : Second_Type;
|
|
pragma Unreferenced (Y, Mo, D, H, S);
|
|
|
|
begin
|
|
GM_Split (Date, Y, Mo, D, H, Mn, S);
|
|
return Mn;
|
|
end GM_Minute;
|
|
|
|
--------------
|
|
-- GM_Month --
|
|
--------------
|
|
|
|
function GM_Month (Date : OS_Time) return Month_Type is
|
|
Mo : Month_Type;
|
|
|
|
Y : Year_Type;
|
|
D : Day_Type;
|
|
H : Hour_Type;
|
|
Mn : Minute_Type;
|
|
S : Second_Type;
|
|
pragma Unreferenced (Y, D, H, Mn, S);
|
|
|
|
begin
|
|
GM_Split (Date, Y, Mo, D, H, Mn, S);
|
|
return Mo;
|
|
end GM_Month;
|
|
|
|
---------------
|
|
-- GM_Second --
|
|
---------------
|
|
|
|
function GM_Second (Date : OS_Time) return Second_Type is
|
|
S : Second_Type;
|
|
|
|
Y : Year_Type;
|
|
Mo : Month_Type;
|
|
D : Day_Type;
|
|
H : Hour_Type;
|
|
Mn : Minute_Type;
|
|
pragma Unreferenced (Y, Mo, D, H, Mn);
|
|
|
|
begin
|
|
GM_Split (Date, Y, Mo, D, H, Mn, S);
|
|
return S;
|
|
end GM_Second;
|
|
|
|
--------------
|
|
-- GM_Split --
|
|
--------------
|
|
|
|
procedure GM_Split
|
|
(Date : OS_Time;
|
|
Year : out Year_Type;
|
|
Month : out Month_Type;
|
|
Day : out Day_Type;
|
|
Hour : out Hour_Type;
|
|
Minute : out Minute_Type;
|
|
Second : out Second_Type)
|
|
is
|
|
procedure To_GM_Time
|
|
(P_Time_T : Address;
|
|
P_Year : Address;
|
|
P_Month : Address;
|
|
P_Day : Address;
|
|
P_Hours : Address;
|
|
P_Mins : Address;
|
|
P_Secs : Address);
|
|
pragma Import (C, To_GM_Time, "__gnat_to_gm_time");
|
|
|
|
T : OS_Time := Date;
|
|
Y : Integer;
|
|
Mo : Integer;
|
|
D : Integer;
|
|
H : Integer;
|
|
Mn : Integer;
|
|
S : Integer;
|
|
|
|
begin
|
|
-- Use the global lock because To_GM_Time is not thread safe
|
|
|
|
Locked_Processing : begin
|
|
SSL.Lock_Task.all;
|
|
To_GM_Time
|
|
(P_Time_T => T'Address,
|
|
P_Year => Y'Address,
|
|
P_Month => Mo'Address,
|
|
P_Day => D'Address,
|
|
P_Hours => H'Address,
|
|
P_Mins => Mn'Address,
|
|
P_Secs => S'Address);
|
|
SSL.Unlock_Task.all;
|
|
|
|
exception
|
|
when others =>
|
|
SSL.Unlock_Task.all;
|
|
raise;
|
|
end Locked_Processing;
|
|
|
|
Year := Y + 1900;
|
|
Month := Mo + 1;
|
|
Day := D;
|
|
Hour := H;
|
|
Minute := Mn;
|
|
Second := S;
|
|
end GM_Split;
|
|
|
|
----------------
|
|
-- GM_Time_Of --
|
|
----------------
|
|
|
|
function GM_Time_Of
|
|
(Year : Year_Type;
|
|
Month : Month_Type;
|
|
Day : Day_Type;
|
|
Hour : Hour_Type;
|
|
Minute : Minute_Type;
|
|
Second : Second_Type) return OS_Time
|
|
is
|
|
procedure To_OS_Time
|
|
(P_Time_T : Address;
|
|
P_Year : Integer;
|
|
P_Month : Integer;
|
|
P_Day : Integer;
|
|
P_Hours : Integer;
|
|
P_Mins : Integer;
|
|
P_Secs : Integer);
|
|
pragma Import (C, To_OS_Time, "__gnat_to_os_time");
|
|
|
|
Result : OS_Time;
|
|
|
|
begin
|
|
To_OS_Time
|
|
(P_Time_T => Result'Address,
|
|
P_Year => Year - 1900,
|
|
P_Month => Month - 1,
|
|
P_Day => Day,
|
|
P_Hours => Hour,
|
|
P_Mins => Minute,
|
|
P_Secs => Second);
|
|
return Result;
|
|
end GM_Time_Of;
|
|
|
|
-------------
|
|
-- GM_Year --
|
|
-------------
|
|
|
|
function GM_Year (Date : OS_Time) return Year_Type is
|
|
Y : Year_Type;
|
|
|
|
Mo : Month_Type;
|
|
D : Day_Type;
|
|
H : Hour_Type;
|
|
Mn : Minute_Type;
|
|
S : Second_Type;
|
|
pragma Unreferenced (Mo, D, H, Mn, S);
|
|
|
|
begin
|
|
GM_Split (Date, Y, Mo, D, H, Mn, S);
|
|
return Y;
|
|
end GM_Year;
|
|
|
|
----------------------
|
|
-- Is_Absolute_Path --
|
|
----------------------
|
|
|
|
function Is_Absolute_Path (Name : String) return Boolean is
|
|
function Is_Absolute_Path
|
|
(Name : Address;
|
|
Length : Integer) return Integer;
|
|
pragma Import (C, Is_Absolute_Path, "__gnat_is_absolute_path");
|
|
begin
|
|
return Is_Absolute_Path (Name'Address, Name'Length) /= 0;
|
|
end Is_Absolute_Path;
|
|
|
|
------------------
|
|
-- Is_Directory --
|
|
------------------
|
|
|
|
function Is_Directory (Name : C_File_Name) return Boolean is
|
|
function Is_Directory (Name : Address) return Integer;
|
|
pragma Import (C, Is_Directory, "__gnat_is_directory");
|
|
begin
|
|
return Is_Directory (Name) /= 0;
|
|
end Is_Directory;
|
|
|
|
function Is_Directory (Name : String) return Boolean is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return Is_Directory (F_Name'Address);
|
|
end Is_Directory;
|
|
|
|
----------------------
|
|
-- Is_Readable_File --
|
|
----------------------
|
|
|
|
function Is_Readable_File (Name : C_File_Name) return Boolean is
|
|
function Is_Readable_File (Name : Address) return Integer;
|
|
pragma Import (C, Is_Readable_File, "__gnat_is_readable_file");
|
|
begin
|
|
return Is_Readable_File (Name) /= 0;
|
|
end Is_Readable_File;
|
|
|
|
function Is_Readable_File (Name : String) return Boolean is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return Is_Readable_File (F_Name'Address);
|
|
end Is_Readable_File;
|
|
|
|
------------------------
|
|
-- Is_Executable_File --
|
|
------------------------
|
|
|
|
function Is_Executable_File (Name : C_File_Name) return Boolean is
|
|
function Is_Executable_File (Name : Address) return Integer;
|
|
pragma Import (C, Is_Executable_File, "__gnat_is_executable_file");
|
|
begin
|
|
return Is_Executable_File (Name) /= 0;
|
|
end Is_Executable_File;
|
|
|
|
function Is_Executable_File (Name : String) return Boolean is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return Is_Executable_File (F_Name'Address);
|
|
end Is_Executable_File;
|
|
|
|
---------------------
|
|
-- Is_Regular_File --
|
|
---------------------
|
|
|
|
function Is_Regular_File (Name : C_File_Name) return Boolean is
|
|
function Is_Regular_File (Name : Address) return Integer;
|
|
pragma Import (C, Is_Regular_File, "__gnat_is_regular_file");
|
|
begin
|
|
return Is_Regular_File (Name) /= 0;
|
|
end Is_Regular_File;
|
|
|
|
function Is_Regular_File (Name : String) return Boolean is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return Is_Regular_File (F_Name'Address);
|
|
end Is_Regular_File;
|
|
|
|
----------------------
|
|
-- Is_Symbolic_Link --
|
|
----------------------
|
|
|
|
function Is_Symbolic_Link (Name : C_File_Name) return Boolean is
|
|
function Is_Symbolic_Link (Name : Address) return Integer;
|
|
pragma Import (C, Is_Symbolic_Link, "__gnat_is_symbolic_link");
|
|
begin
|
|
return Is_Symbolic_Link (Name) /= 0;
|
|
end Is_Symbolic_Link;
|
|
|
|
function Is_Symbolic_Link (Name : String) return Boolean is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return Is_Symbolic_Link (F_Name'Address);
|
|
end Is_Symbolic_Link;
|
|
|
|
----------------------
|
|
-- Is_Writable_File --
|
|
----------------------
|
|
|
|
function Is_Writable_File (Name : C_File_Name) return Boolean is
|
|
function Is_Writable_File (Name : Address) return Integer;
|
|
pragma Import (C, Is_Writable_File, "__gnat_is_writable_file");
|
|
begin
|
|
return Is_Writable_File (Name) /= 0;
|
|
end Is_Writable_File;
|
|
|
|
function Is_Writable_File (Name : String) return Boolean is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
return Is_Writable_File (F_Name'Address);
|
|
end Is_Writable_File;
|
|
|
|
----------
|
|
-- Kill --
|
|
----------
|
|
|
|
procedure Kill (Pid : Process_Id; Hard_Kill : Boolean := True) is
|
|
SIGKILL : constant := 9;
|
|
SIGINT : constant := 2;
|
|
|
|
procedure C_Kill (Pid : Process_Id; Sig_Num : Integer; Close : Integer);
|
|
pragma Import (C, C_Kill, "__gnat_kill");
|
|
|
|
begin
|
|
if Hard_Kill then
|
|
C_Kill (Pid, SIGKILL, 1);
|
|
else
|
|
C_Kill (Pid, SIGINT, 1);
|
|
end if;
|
|
end Kill;
|
|
|
|
-----------------------
|
|
-- Kill_Process_Tree --
|
|
-----------------------
|
|
|
|
procedure Kill_Process_Tree
|
|
(Pid : Process_Id; Hard_Kill : Boolean := True)
|
|
is
|
|
SIGKILL : constant := 9;
|
|
SIGINT : constant := 2;
|
|
|
|
procedure C_Kill_PT (Pid : Process_Id; Sig_Num : Integer);
|
|
pragma Import (C, C_Kill_PT, "__gnat_killprocesstree");
|
|
|
|
begin
|
|
if Hard_Kill then
|
|
C_Kill_PT (Pid, SIGKILL);
|
|
else
|
|
C_Kill_PT (Pid, SIGINT);
|
|
end if;
|
|
end Kill_Process_Tree;
|
|
|
|
-------------------------
|
|
-- Locate_Exec_On_Path --
|
|
-------------------------
|
|
|
|
function Locate_Exec_On_Path
|
|
(Exec_Name : String) return String_Access
|
|
is
|
|
function Locate_Exec_On_Path (C_Exec_Name : Address) return Address;
|
|
pragma Import (C, Locate_Exec_On_Path, "__gnat_locate_exec_on_path");
|
|
|
|
C_Exec_Name : String (1 .. Exec_Name'Length + 1);
|
|
Path_Addr : Address;
|
|
Path_Len : Integer;
|
|
Result : String_Access;
|
|
|
|
begin
|
|
C_Exec_Name (1 .. Exec_Name'Length) := Exec_Name;
|
|
C_Exec_Name (C_Exec_Name'Last) := ASCII.NUL;
|
|
|
|
Path_Addr := Locate_Exec_On_Path (C_Exec_Name'Address);
|
|
Path_Len := C_String_Length (Path_Addr);
|
|
|
|
if Path_Len = 0 then
|
|
return null;
|
|
|
|
else
|
|
Result := To_Path_String_Access (Path_Addr, Path_Len);
|
|
CRTL.free (Path_Addr);
|
|
|
|
-- Always return an absolute path name
|
|
|
|
if not Is_Absolute_Path (Result.all) then
|
|
declare
|
|
Absolute_Path : constant String :=
|
|
Normalize_Pathname (Result.all, Resolve_Links => False);
|
|
begin
|
|
Free (Result);
|
|
Result := new String'(Absolute_Path);
|
|
end;
|
|
end if;
|
|
|
|
return Result;
|
|
end if;
|
|
end Locate_Exec_On_Path;
|
|
|
|
-------------------------
|
|
-- Locate_Regular_File --
|
|
-------------------------
|
|
|
|
function Locate_Regular_File
|
|
(File_Name : C_File_Name;
|
|
Path : C_File_Name) return String_Access
|
|
is
|
|
function Locate_Regular_File
|
|
(C_File_Name, Path_Val : Address) return Address;
|
|
pragma Import (C, Locate_Regular_File, "__gnat_locate_regular_file");
|
|
|
|
Path_Addr : Address;
|
|
Path_Len : Integer;
|
|
Result : String_Access;
|
|
|
|
begin
|
|
Path_Addr := Locate_Regular_File (File_Name, Path);
|
|
Path_Len := C_String_Length (Path_Addr);
|
|
|
|
if Path_Len = 0 then
|
|
return null;
|
|
|
|
else
|
|
Result := To_Path_String_Access (Path_Addr, Path_Len);
|
|
CRTL.free (Path_Addr);
|
|
return Result;
|
|
end if;
|
|
end Locate_Regular_File;
|
|
|
|
function Locate_Regular_File
|
|
(File_Name : String;
|
|
Path : String) return String_Access
|
|
is
|
|
C_File_Name : String (1 .. File_Name'Length + 1);
|
|
C_Path : String (1 .. Path'Length + 1);
|
|
Result : String_Access;
|
|
|
|
begin
|
|
C_File_Name (1 .. File_Name'Length) := File_Name;
|
|
C_File_Name (C_File_Name'Last) := ASCII.NUL;
|
|
|
|
C_Path (1 .. Path'Length) := Path;
|
|
C_Path (C_Path'Last) := ASCII.NUL;
|
|
|
|
Result := Locate_Regular_File (C_File_Name'Address, C_Path'Address);
|
|
|
|
-- Always return an absolute path name
|
|
|
|
if Result /= null and then not Is_Absolute_Path (Result.all) then
|
|
declare
|
|
Absolute_Path : constant String := Normalize_Pathname (Result.all);
|
|
begin
|
|
Free (Result);
|
|
Result := new String'(Absolute_Path);
|
|
end;
|
|
end if;
|
|
|
|
return Result;
|
|
end Locate_Regular_File;
|
|
|
|
------------------------
|
|
-- Non_Blocking_Spawn --
|
|
------------------------
|
|
|
|
function Non_Blocking_Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List) return Process_Id
|
|
is
|
|
Junk : Integer;
|
|
pragma Warnings (Off, Junk);
|
|
Pid : Process_Id;
|
|
|
|
begin
|
|
Spawn_Internal (Program_Name, Args, Junk, Pid, Blocking => False);
|
|
return Pid;
|
|
end Non_Blocking_Spawn;
|
|
|
|
function Non_Blocking_Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Output_File_Descriptor : File_Descriptor;
|
|
Err_To_Out : Boolean := True) return Process_Id
|
|
is
|
|
Pid : Process_Id;
|
|
Saved_Error : File_Descriptor := Invalid_FD; -- prevent warning
|
|
Saved_Output : File_Descriptor;
|
|
|
|
begin
|
|
if Output_File_Descriptor = Invalid_FD then
|
|
return Invalid_Pid;
|
|
end if;
|
|
|
|
-- Set standard output and, if specified, error to the temporary file
|
|
|
|
Saved_Output := Dup (Standout);
|
|
Dup2 (Output_File_Descriptor, Standout);
|
|
|
|
if Err_To_Out then
|
|
Saved_Error := Dup (Standerr);
|
|
Dup2 (Output_File_Descriptor, Standerr);
|
|
end if;
|
|
|
|
-- Spawn the program
|
|
|
|
Pid := Non_Blocking_Spawn (Program_Name, Args);
|
|
|
|
-- Restore the standard output and error
|
|
|
|
Dup2 (Saved_Output, Standout);
|
|
|
|
if Err_To_Out then
|
|
Dup2 (Saved_Error, Standerr);
|
|
end if;
|
|
|
|
-- And close the saved standard output and error file descriptors
|
|
|
|
Close (Saved_Output);
|
|
|
|
if Err_To_Out then
|
|
Close (Saved_Error);
|
|
end if;
|
|
|
|
return Pid;
|
|
end Non_Blocking_Spawn;
|
|
|
|
function Non_Blocking_Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Output_File : String;
|
|
Err_To_Out : Boolean := True) return Process_Id
|
|
is
|
|
Output_File_Descriptor : constant File_Descriptor :=
|
|
Create_Output_Text_File (Output_File);
|
|
Result : Process_Id;
|
|
|
|
begin
|
|
-- Do not attempt to spawn if the output file could not be created
|
|
|
|
if Output_File_Descriptor = Invalid_FD then
|
|
return Invalid_Pid;
|
|
|
|
else
|
|
Result :=
|
|
Non_Blocking_Spawn
|
|
(Program_Name, Args, Output_File_Descriptor, Err_To_Out);
|
|
|
|
-- Close the file just created for the output, as the file descriptor
|
|
-- cannot be used anywhere, being a local value. It is safe to do
|
|
-- that, as the file descriptor has been duplicated to form
|
|
-- standard output and error of the spawned process.
|
|
|
|
Close (Output_File_Descriptor);
|
|
|
|
return Result;
|
|
end if;
|
|
end Non_Blocking_Spawn;
|
|
|
|
function Non_Blocking_Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Stdout_File : String;
|
|
Stderr_File : String) return Process_Id
|
|
is
|
|
Stderr_FD : constant File_Descriptor :=
|
|
Create_Output_Text_File (Stderr_File);
|
|
Stdout_FD : constant File_Descriptor :=
|
|
Create_Output_Text_File (Stdout_File);
|
|
|
|
Result : Process_Id;
|
|
Saved_Error : File_Descriptor;
|
|
Saved_Output : File_Descriptor;
|
|
|
|
begin
|
|
-- Do not attempt to spawn if the output files could not be created
|
|
|
|
if Stdout_FD = Invalid_FD or else Stderr_FD = Invalid_FD then
|
|
return Invalid_Pid;
|
|
end if;
|
|
|
|
-- Set standard output and error to the specified files
|
|
|
|
Saved_Output := Dup (Standout);
|
|
Dup2 (Stdout_FD, Standout);
|
|
|
|
Saved_Error := Dup (Standerr);
|
|
Dup2 (Stderr_FD, Standerr);
|
|
|
|
-- Spawn the program
|
|
|
|
Result := Non_Blocking_Spawn (Program_Name, Args);
|
|
|
|
-- Restore the standard output and error
|
|
|
|
Dup2 (Saved_Output, Standout);
|
|
Dup2 (Saved_Error, Standerr);
|
|
|
|
-- And close the saved standard output and error file descriptors
|
|
|
|
Close (Saved_Output);
|
|
Close (Saved_Error);
|
|
|
|
return Result;
|
|
end Non_Blocking_Spawn;
|
|
|
|
-------------------------
|
|
-- Normalize_Arguments --
|
|
-------------------------
|
|
|
|
procedure Normalize_Arguments (Args : in out Argument_List) is
|
|
procedure Quote_Argument (Arg : in out String_Access);
|
|
-- Add quote around argument if it contains spaces (or HT characters)
|
|
|
|
C_Argument_Needs_Quote : Integer;
|
|
pragma Import (C, C_Argument_Needs_Quote, "__gnat_argument_needs_quote");
|
|
Argument_Needs_Quote : constant Boolean := C_Argument_Needs_Quote /= 0;
|
|
|
|
--------------------
|
|
-- Quote_Argument --
|
|
--------------------
|
|
|
|
procedure Quote_Argument (Arg : in out String_Access) is
|
|
J : Positive := 1;
|
|
Quote_Needed : Boolean := False;
|
|
Res : String (1 .. Arg'Length * 2);
|
|
|
|
begin
|
|
if Arg (Arg'First) /= '"' or else Arg (Arg'Last) /= '"' then
|
|
|
|
-- Starting quote
|
|
|
|
Res (J) := '"';
|
|
|
|
for K in Arg'Range loop
|
|
|
|
J := J + 1;
|
|
|
|
if Arg (K) = '"' then
|
|
Res (J) := '\';
|
|
J := J + 1;
|
|
Res (J) := '"';
|
|
Quote_Needed := True;
|
|
|
|
elsif Arg (K) = ' ' or else Arg (K) = ASCII.HT then
|
|
Res (J) := Arg (K);
|
|
Quote_Needed := True;
|
|
|
|
else
|
|
Res (J) := Arg (K);
|
|
end if;
|
|
end loop;
|
|
|
|
if Quote_Needed then
|
|
|
|
-- Case of null terminated string
|
|
|
|
if Res (J) = ASCII.NUL then
|
|
|
|
-- If the string ends with \, double it
|
|
|
|
if Res (J - 1) = '\' then
|
|
Res (J) := '\';
|
|
J := J + 1;
|
|
end if;
|
|
|
|
-- Put a quote just before the null at the end
|
|
|
|
Res (J) := '"';
|
|
J := J + 1;
|
|
Res (J) := ASCII.NUL;
|
|
|
|
-- If argument is terminated by '\', then double it. Otherwise
|
|
-- the ending quote will be taken as-is. This is quite strange
|
|
-- spawn behavior from Windows, but this is what we see.
|
|
|
|
else
|
|
if Res (J) = '\' then
|
|
J := J + 1;
|
|
Res (J) := '\';
|
|
end if;
|
|
|
|
-- Ending quote
|
|
|
|
J := J + 1;
|
|
Res (J) := '"';
|
|
end if;
|
|
|
|
declare
|
|
Old : String_Access := Arg;
|
|
|
|
begin
|
|
Arg := new String'(Res (1 .. J));
|
|
Free (Old);
|
|
end;
|
|
end if;
|
|
|
|
end if;
|
|
end Quote_Argument;
|
|
|
|
-- Start of processing for Normalize_Arguments
|
|
|
|
begin
|
|
if Argument_Needs_Quote then
|
|
for K in Args'Range loop
|
|
if Args (K) /= null and then Args (K)'Length /= 0 then
|
|
Quote_Argument (Args (K));
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
end Normalize_Arguments;
|
|
|
|
------------------------
|
|
-- Normalize_Pathname --
|
|
------------------------
|
|
|
|
function Normalize_Pathname
|
|
(Name : String;
|
|
Directory : String := "";
|
|
Resolve_Links : Boolean := True;
|
|
Case_Sensitive : Boolean := True) return String
|
|
is
|
|
procedure Get_Current_Dir
|
|
(Dir : System.Address;
|
|
Length : System.Address);
|
|
pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir");
|
|
|
|
function Get_File_Names_Case_Sensitive return Integer;
|
|
pragma Import
|
|
(C, Get_File_Names_Case_Sensitive,
|
|
"__gnat_get_file_names_case_sensitive");
|
|
|
|
Max_Path : Integer;
|
|
pragma Import (C, Max_Path, "__gnat_max_path_len");
|
|
-- Maximum length of a path name
|
|
|
|
function Readlink
|
|
(Path : System.Address;
|
|
Buf : System.Address;
|
|
Bufsiz : Integer) return Integer;
|
|
pragma Import (C, Readlink, "__gnat_readlink");
|
|
|
|
function To_Canonical_File_Spec
|
|
(Host_File : System.Address) return System.Address;
|
|
pragma Import
|
|
(C, To_Canonical_File_Spec, "__gnat_to_canonical_file_spec");
|
|
-- Convert possible foreign file syntax to canonical form
|
|
|
|
Fold_To_Lower_Case : constant Boolean :=
|
|
not Case_Sensitive
|
|
and then Get_File_Names_Case_Sensitive = 0;
|
|
|
|
function Final_Value (S : String) return String;
|
|
-- Make final adjustment to the returned string. This function strips
|
|
-- trailing directory separators, and folds returned string to lower
|
|
-- case if required.
|
|
|
|
function Get_Directory (Dir : String) return String;
|
|
-- If Dir is not empty, return it, adding a directory separator
|
|
-- if not already present, otherwise return current working directory
|
|
-- with terminating directory separator.
|
|
|
|
-----------------
|
|
-- Final_Value --
|
|
-----------------
|
|
|
|
function Final_Value (S : String) return String is
|
|
S1 : String := S;
|
|
-- We may need to fold S to lower case, so we need a variable
|
|
|
|
Last : Natural;
|
|
|
|
begin
|
|
if Fold_To_Lower_Case then
|
|
System.Case_Util.To_Lower (S1);
|
|
end if;
|
|
|
|
-- Remove trailing directory separator, if any
|
|
|
|
Last := S1'Last;
|
|
|
|
if Last > 1
|
|
and then (S1 (Last) = '/'
|
|
or else
|
|
S1 (Last) = Directory_Separator)
|
|
then
|
|
-- Special case for Windows: C:\
|
|
|
|
if Last = 3
|
|
and then S1 (1) /= Directory_Separator
|
|
and then S1 (2) = ':'
|
|
then
|
|
null;
|
|
|
|
else
|
|
Last := Last - 1;
|
|
end if;
|
|
end if;
|
|
|
|
return S1 (1 .. Last);
|
|
end Final_Value;
|
|
|
|
-------------------
|
|
-- Get_Directory --
|
|
-------------------
|
|
|
|
function Get_Directory (Dir : String) return String is
|
|
begin
|
|
-- Directory given, add directory separator if needed
|
|
|
|
if Dir'Length > 0 then
|
|
declare
|
|
Result : String :=
|
|
Normalize_Pathname
|
|
(Dir, "", Resolve_Links, Case_Sensitive) &
|
|
Directory_Separator;
|
|
Last : Positive := Result'Last - 1;
|
|
|
|
begin
|
|
-- On Windows, change all '/' to '\'
|
|
|
|
if On_Windows then
|
|
for J in Result'First .. Last - 1 loop
|
|
if Result (J) = '/' then
|
|
Result (J) := Directory_Separator;
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
-- Include additional directory separator, if needed
|
|
|
|
if Result (Last) /= Directory_Separator then
|
|
Last := Last + 1;
|
|
end if;
|
|
|
|
return Result (Result'First .. Last);
|
|
end;
|
|
|
|
-- Directory name not given, get current directory
|
|
|
|
else
|
|
declare
|
|
Buffer : String (1 .. Max_Path + 2);
|
|
Path_Len : Natural := Max_Path;
|
|
|
|
begin
|
|
Get_Current_Dir (Buffer'Address, Path_Len'Address);
|
|
|
|
if Buffer (Path_Len) /= Directory_Separator then
|
|
Path_Len := Path_Len + 1;
|
|
Buffer (Path_Len) := Directory_Separator;
|
|
end if;
|
|
|
|
-- By default, the drive letter on Windows is in upper case
|
|
|
|
if On_Windows
|
|
and then Path_Len >= 2
|
|
and then Buffer (2) = ':'
|
|
then
|
|
System.Case_Util.To_Upper (Buffer (1 .. 1));
|
|
end if;
|
|
|
|
return Buffer (1 .. Path_Len);
|
|
end;
|
|
end if;
|
|
end Get_Directory;
|
|
|
|
-- Local variables
|
|
|
|
Max_Iterations : constant := 500;
|
|
|
|
Canonical_File_Addr : System.Address;
|
|
Canonical_File_Len : Integer;
|
|
|
|
End_Path : Natural := 0;
|
|
Finish : Positive;
|
|
Last : Positive;
|
|
Link_Buffer : String (1 .. Max_Path + 2);
|
|
Path_Buffer : String (1 .. Max_Path + Max_Path + 2);
|
|
Start : Natural;
|
|
Status : Integer;
|
|
The_Name : String (1 .. Name'Length + 1);
|
|
|
|
-- Start of processing for Normalize_Pathname
|
|
|
|
begin
|
|
-- Special case, return null if name is null, or if it is bigger than
|
|
-- the biggest name allowed.
|
|
|
|
if Name'Length = 0 or else Name'Length > Max_Path then
|
|
return "";
|
|
end if;
|
|
|
|
-- First, convert possible foreign file spec to Unix file spec. If no
|
|
-- conversion is required, all this does is put Name at the beginning
|
|
-- of Path_Buffer unchanged.
|
|
|
|
File_Name_Conversion : begin
|
|
The_Name (1 .. Name'Length) := Name;
|
|
The_Name (The_Name'Last) := ASCII.NUL;
|
|
|
|
Canonical_File_Addr := To_Canonical_File_Spec (The_Name'Address);
|
|
Canonical_File_Len := Integer (CRTL.strlen (Canonical_File_Addr));
|
|
|
|
-- If syntax conversion has failed, return an empty string to
|
|
-- indicate the failure.
|
|
|
|
if Canonical_File_Len = 0 then
|
|
return "";
|
|
end if;
|
|
|
|
declare
|
|
subtype Path_String is String (1 .. Canonical_File_Len);
|
|
Canonical_File : Path_String;
|
|
for Canonical_File'Address use Canonical_File_Addr;
|
|
pragma Import (Ada, Canonical_File);
|
|
|
|
begin
|
|
Path_Buffer (1 .. Canonical_File_Len) := Canonical_File;
|
|
End_Path := Canonical_File_Len;
|
|
Last := 1;
|
|
end;
|
|
end File_Name_Conversion;
|
|
|
|
-- Replace all '/' by Directory Separators (this is for Windows)
|
|
|
|
if Directory_Separator /= '/' then
|
|
for Index in 1 .. End_Path loop
|
|
if Path_Buffer (Index) = '/' then
|
|
Path_Buffer (Index) := Directory_Separator;
|
|
end if;
|
|
end loop;
|
|
end if;
|
|
|
|
-- Resolve directory names for Windows
|
|
|
|
if On_Windows then
|
|
|
|
-- On Windows, if we have an absolute path starting with a directory
|
|
-- separator, we need to have the drive letter appended in front.
|
|
|
|
-- On Windows, Get_Current_Dir will return a suitable directory name
|
|
-- (path starting with a drive letter on Windows). So we take this
|
|
-- drive letter and prepend it to the current path.
|
|
|
|
if Path_Buffer (1) = Directory_Separator
|
|
and then Path_Buffer (2) /= Directory_Separator
|
|
then
|
|
declare
|
|
Cur_Dir : constant String := Get_Directory ("");
|
|
-- Get the current directory to get the drive letter
|
|
|
|
begin
|
|
if Cur_Dir'Length > 2
|
|
and then Cur_Dir (Cur_Dir'First + 1) = ':'
|
|
then
|
|
Path_Buffer (3 .. End_Path + 2) :=
|
|
Path_Buffer (1 .. End_Path);
|
|
Path_Buffer (1 .. 2) :=
|
|
Cur_Dir (Cur_Dir'First .. Cur_Dir'First + 1);
|
|
End_Path := End_Path + 2;
|
|
end if;
|
|
end;
|
|
|
|
-- We have a drive letter, ensure it is upper-case
|
|
|
|
elsif Path_Buffer (1) in 'a' .. 'z'
|
|
and then Path_Buffer (2) = ':'
|
|
then
|
|
System.Case_Util.To_Upper (Path_Buffer (1 .. 1));
|
|
end if;
|
|
end if;
|
|
|
|
-- On Windows, remove all double-quotes that are possibly part of the
|
|
-- path but can cause problems with other methods.
|
|
|
|
if On_Windows then
|
|
declare
|
|
Index : Natural;
|
|
|
|
begin
|
|
Index := Path_Buffer'First;
|
|
for Current in Path_Buffer'First .. End_Path loop
|
|
if Path_Buffer (Current) /= '"' then
|
|
Path_Buffer (Index) := Path_Buffer (Current);
|
|
Index := Index + 1;
|
|
end if;
|
|
end loop;
|
|
|
|
End_Path := Index - 1;
|
|
end;
|
|
end if;
|
|
|
|
-- Start the conversions
|
|
|
|
-- If this is not finished after Max_Iterations, give up and return an
|
|
-- empty string.
|
|
|
|
for J in 1 .. Max_Iterations loop
|
|
|
|
-- If we don't have an absolute pathname, prepend the directory
|
|
-- Reference_Dir.
|
|
|
|
if Last = 1
|
|
and then not Is_Absolute_Path (Path_Buffer (1 .. End_Path))
|
|
then
|
|
declare
|
|
Reference_Dir : constant String := Get_Directory (Directory);
|
|
Ref_Dir_Len : constant Natural := Reference_Dir'Length;
|
|
-- Current directory name specified and its length
|
|
|
|
begin
|
|
Path_Buffer (Ref_Dir_Len + 1 .. Ref_Dir_Len + End_Path) :=
|
|
Path_Buffer (1 .. End_Path);
|
|
End_Path := Ref_Dir_Len + End_Path;
|
|
Path_Buffer (1 .. Ref_Dir_Len) := Reference_Dir;
|
|
Last := Ref_Dir_Len;
|
|
end;
|
|
end if;
|
|
|
|
Start := Last + 1;
|
|
Finish := Last;
|
|
|
|
-- Ensure that Windows network drives are kept, e.g: \\server\drive-c
|
|
|
|
if Start = 2
|
|
and then Directory_Separator = '\'
|
|
and then Path_Buffer (1 .. 2) = "\\"
|
|
then
|
|
Start := 3;
|
|
end if;
|
|
|
|
-- If we have traversed the full pathname, return it
|
|
|
|
if Start > End_Path then
|
|
return Final_Value (Path_Buffer (1 .. End_Path));
|
|
end if;
|
|
|
|
-- Remove duplicate directory separators
|
|
|
|
while Path_Buffer (Start) = Directory_Separator loop
|
|
if Start = End_Path then
|
|
return Final_Value (Path_Buffer (1 .. End_Path - 1));
|
|
|
|
else
|
|
Path_Buffer (Start .. End_Path - 1) :=
|
|
Path_Buffer (Start + 1 .. End_Path);
|
|
End_Path := End_Path - 1;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Find the end of the current field: last character or the one
|
|
-- preceding the next directory separator.
|
|
|
|
while Finish < End_Path
|
|
and then Path_Buffer (Finish + 1) /= Directory_Separator
|
|
loop
|
|
Finish := Finish + 1;
|
|
end loop;
|
|
|
|
-- Remove "." field
|
|
|
|
if Start = Finish and then Path_Buffer (Start) = '.' then
|
|
if Start = End_Path then
|
|
if Last = 1 then
|
|
return (1 => Directory_Separator);
|
|
else
|
|
if Fold_To_Lower_Case then
|
|
System.Case_Util.To_Lower (Path_Buffer (1 .. Last - 1));
|
|
end if;
|
|
|
|
return Path_Buffer (1 .. Last - 1);
|
|
end if;
|
|
else
|
|
Path_Buffer (Last + 1 .. End_Path - 2) :=
|
|
Path_Buffer (Last + 3 .. End_Path);
|
|
End_Path := End_Path - 2;
|
|
end if;
|
|
|
|
-- Remove ".." fields
|
|
|
|
elsif Finish = Start + 1
|
|
and then Path_Buffer (Start .. Finish) = ".."
|
|
then
|
|
Start := Last;
|
|
loop
|
|
Start := Start - 1;
|
|
exit when Start < 1
|
|
or else Path_Buffer (Start) = Directory_Separator;
|
|
end loop;
|
|
|
|
if Start <= 1 then
|
|
if Finish = End_Path then
|
|
return (1 => Directory_Separator);
|
|
|
|
else
|
|
Path_Buffer (1 .. End_Path - Finish) :=
|
|
Path_Buffer (Finish + 1 .. End_Path);
|
|
End_Path := End_Path - Finish;
|
|
Last := 1;
|
|
end if;
|
|
|
|
else
|
|
if Finish = End_Path then
|
|
return Final_Value (Path_Buffer (1 .. Start - 1));
|
|
|
|
else
|
|
Path_Buffer (Start + 1 .. Start + End_Path - Finish - 1) :=
|
|
Path_Buffer (Finish + 2 .. End_Path);
|
|
End_Path := Start + End_Path - Finish - 1;
|
|
Last := Start;
|
|
end if;
|
|
end if;
|
|
|
|
-- Check if current field is a symbolic link
|
|
|
|
elsif Resolve_Links then
|
|
declare
|
|
Saved : constant Character := Path_Buffer (Finish + 1);
|
|
|
|
begin
|
|
Path_Buffer (Finish + 1) := ASCII.NUL;
|
|
Status :=
|
|
Readlink
|
|
(Path => Path_Buffer'Address,
|
|
Buf => Link_Buffer'Address,
|
|
Bufsiz => Link_Buffer'Length);
|
|
Path_Buffer (Finish + 1) := Saved;
|
|
end;
|
|
|
|
-- Not a symbolic link, move to the next field, if any
|
|
|
|
if Status <= 0 then
|
|
Last := Finish + 1;
|
|
|
|
-- Replace symbolic link with its value
|
|
|
|
else
|
|
if Is_Absolute_Path (Link_Buffer (1 .. Status)) then
|
|
Path_Buffer (Status + 1 .. End_Path - (Finish - Status)) :=
|
|
Path_Buffer (Finish + 1 .. End_Path);
|
|
End_Path := End_Path - (Finish - Status);
|
|
Path_Buffer (1 .. Status) := Link_Buffer (1 .. Status);
|
|
Last := 1;
|
|
|
|
else
|
|
Path_Buffer
|
|
(Last + Status + 1 .. End_Path - Finish + Last + Status) :=
|
|
Path_Buffer (Finish + 1 .. End_Path);
|
|
End_Path := End_Path - Finish + Last + Status;
|
|
Path_Buffer (Last + 1 .. Last + Status) :=
|
|
Link_Buffer (1 .. Status);
|
|
end if;
|
|
end if;
|
|
|
|
else
|
|
Last := Finish + 1;
|
|
end if;
|
|
end loop;
|
|
|
|
-- Too many iterations: give up
|
|
|
|
-- This can happen when there is a circularity in the symbolic links: A
|
|
-- is a symbolic link for B, which itself is a symbolic link, and the
|
|
-- target of B or of another symbolic link target of B is A. In this
|
|
-- case, we return an empty string to indicate failure to resolve.
|
|
|
|
return "";
|
|
end Normalize_Pathname;
|
|
|
|
-----------------
|
|
-- Open_Append --
|
|
-----------------
|
|
|
|
function Open_Append
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
function C_Open_Append
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor;
|
|
pragma Import (C, C_Open_Append, "__gnat_open_append");
|
|
begin
|
|
return C_Open_Append (Name, Fmode);
|
|
end Open_Append;
|
|
|
|
function Open_Append
|
|
(Name : String;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return Open_Append (C_Name (C_Name'First)'Address, Fmode);
|
|
end Open_Append;
|
|
|
|
---------------
|
|
-- Open_Read --
|
|
---------------
|
|
|
|
function Open_Read
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
function C_Open_Read
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor;
|
|
pragma Import (C, C_Open_Read, "__gnat_open_read");
|
|
begin
|
|
return C_Open_Read (Name, Fmode);
|
|
end Open_Read;
|
|
|
|
function Open_Read
|
|
(Name : String;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return Open_Read (C_Name (C_Name'First)'Address, Fmode);
|
|
end Open_Read;
|
|
|
|
---------------------
|
|
-- Open_Read_Write --
|
|
---------------------
|
|
|
|
function Open_Read_Write
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
function C_Open_Read_Write
|
|
(Name : C_File_Name;
|
|
Fmode : Mode) return File_Descriptor;
|
|
pragma Import (C, C_Open_Read_Write, "__gnat_open_rw");
|
|
begin
|
|
return C_Open_Read_Write (Name, Fmode);
|
|
end Open_Read_Write;
|
|
|
|
function Open_Read_Write
|
|
(Name : String;
|
|
Fmode : Mode) return File_Descriptor
|
|
is
|
|
C_Name : String (1 .. Name'Length + 1);
|
|
begin
|
|
C_Name (1 .. Name'Length) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
return Open_Read_Write (C_Name (C_Name'First)'Address, Fmode);
|
|
end Open_Read_Write;
|
|
|
|
-------------
|
|
-- OS_Exit --
|
|
-------------
|
|
|
|
procedure OS_Exit (Status : Integer) is
|
|
begin
|
|
OS_Exit_Ptr (Status);
|
|
raise Program_Error;
|
|
end OS_Exit;
|
|
|
|
---------------------
|
|
-- OS_Exit_Default --
|
|
---------------------
|
|
|
|
procedure OS_Exit_Default (Status : Integer) is
|
|
procedure GNAT_OS_Exit (Status : Integer);
|
|
pragma Import (C, GNAT_OS_Exit, "__gnat_os_exit");
|
|
pragma No_Return (GNAT_OS_Exit);
|
|
begin
|
|
GNAT_OS_Exit (Status);
|
|
end OS_Exit_Default;
|
|
|
|
--------------------
|
|
-- Pid_To_Integer --
|
|
--------------------
|
|
|
|
function Pid_To_Integer (Pid : Process_Id) return Integer is
|
|
begin
|
|
return Integer (Pid);
|
|
end Pid_To_Integer;
|
|
|
|
----------
|
|
-- Read --
|
|
----------
|
|
|
|
function Read
|
|
(FD : File_Descriptor;
|
|
A : System.Address;
|
|
N : Integer) return Integer
|
|
is
|
|
begin
|
|
return
|
|
Integer (System.CRTL.read
|
|
(System.CRTL.int (FD),
|
|
System.CRTL.chars (A),
|
|
System.CRTL.size_t (N)));
|
|
end Read;
|
|
|
|
-----------------
|
|
-- Rename_File --
|
|
-----------------
|
|
|
|
procedure Rename_File
|
|
(Old_Name : C_File_Name;
|
|
New_Name : C_File_Name;
|
|
Success : out Boolean)
|
|
is
|
|
function rename (From, To : Address) return Integer;
|
|
pragma Import (C, rename, "__gnat_rename");
|
|
R : Integer;
|
|
begin
|
|
R := rename (Old_Name, New_Name);
|
|
Success := (R = 0);
|
|
end Rename_File;
|
|
|
|
procedure Rename_File
|
|
(Old_Name : String;
|
|
New_Name : String;
|
|
Success : out Boolean)
|
|
is
|
|
C_Old_Name : String (1 .. Old_Name'Length + 1);
|
|
C_New_Name : String (1 .. New_Name'Length + 1);
|
|
begin
|
|
C_Old_Name (1 .. Old_Name'Length) := Old_Name;
|
|
C_Old_Name (C_Old_Name'Last) := ASCII.NUL;
|
|
C_New_Name (1 .. New_Name'Length) := New_Name;
|
|
C_New_Name (C_New_Name'Last) := ASCII.NUL;
|
|
Rename_File (C_Old_Name'Address, C_New_Name'Address, Success);
|
|
end Rename_File;
|
|
|
|
-----------------------
|
|
-- Set_Close_On_Exec --
|
|
-----------------------
|
|
|
|
procedure Set_Close_On_Exec
|
|
(FD : File_Descriptor;
|
|
Close_On_Exec : Boolean;
|
|
Status : out Boolean)
|
|
is
|
|
function C_Set_Close_On_Exec
|
|
(FD : File_Descriptor; Close_On_Exec : System.CRTL.int)
|
|
return System.CRTL.int;
|
|
pragma Import (C, C_Set_Close_On_Exec, "__gnat_set_close_on_exec");
|
|
begin
|
|
Status := C_Set_Close_On_Exec (FD, Boolean'Pos (Close_On_Exec)) = 0;
|
|
end Set_Close_On_Exec;
|
|
|
|
--------------------
|
|
-- Set_Executable --
|
|
--------------------
|
|
|
|
procedure Set_Executable (Name : String; Mode : Positive := S_Owner) is
|
|
procedure C_Set_Executable (Name : C_File_Name; Mode : Integer);
|
|
pragma Import (C, C_Set_Executable, "__gnat_set_executable");
|
|
C_Name : aliased String (Name'First .. Name'Last + 1);
|
|
begin
|
|
C_Name (Name'Range) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
C_Set_Executable (C_Name (C_Name'First)'Address, Mode);
|
|
end Set_Executable;
|
|
|
|
-------------------------------------
|
|
-- Set_File_Last_Modify_Time_Stamp --
|
|
-------------------------------------
|
|
|
|
procedure Set_File_Last_Modify_Time_Stamp (Name : String; Time : OS_Time) is
|
|
procedure C_Set_File_Time (Name : C_File_Name; Time : OS_Time);
|
|
pragma Import (C, C_Set_File_Time, "__gnat_set_file_time_name");
|
|
C_Name : aliased String (Name'First .. Name'Last + 1);
|
|
begin
|
|
C_Name (Name'Range) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
C_Set_File_Time (C_Name'Address, Time);
|
|
end Set_File_Last_Modify_Time_Stamp;
|
|
|
|
----------------------
|
|
-- Set_Non_Readable --
|
|
----------------------
|
|
|
|
procedure Set_Non_Readable (Name : String) is
|
|
procedure C_Set_Non_Readable (Name : C_File_Name);
|
|
pragma Import (C, C_Set_Non_Readable, "__gnat_set_non_readable");
|
|
C_Name : aliased String (Name'First .. Name'Last + 1);
|
|
begin
|
|
C_Name (Name'Range) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
C_Set_Non_Readable (C_Name (C_Name'First)'Address);
|
|
end Set_Non_Readable;
|
|
|
|
----------------------
|
|
-- Set_Non_Writable --
|
|
----------------------
|
|
|
|
procedure Set_Non_Writable (Name : String) is
|
|
procedure C_Set_Non_Writable (Name : C_File_Name);
|
|
pragma Import (C, C_Set_Non_Writable, "__gnat_set_non_writable");
|
|
C_Name : aliased String (Name'First .. Name'Last + 1);
|
|
begin
|
|
C_Name (Name'Range) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
C_Set_Non_Writable (C_Name (C_Name'First)'Address);
|
|
end Set_Non_Writable;
|
|
|
|
------------------
|
|
-- Set_Readable --
|
|
------------------
|
|
|
|
procedure Set_Readable (Name : String) is
|
|
procedure C_Set_Readable (Name : C_File_Name);
|
|
pragma Import (C, C_Set_Readable, "__gnat_set_readable");
|
|
C_Name : aliased String (Name'First .. Name'Last + 1);
|
|
begin
|
|
C_Name (Name'Range) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
C_Set_Readable (C_Name (C_Name'First)'Address);
|
|
end Set_Readable;
|
|
|
|
--------------------
|
|
-- Set_Writable --
|
|
--------------------
|
|
|
|
procedure Set_Writable (Name : String) is
|
|
procedure C_Set_Writable (Name : C_File_Name);
|
|
pragma Import (C, C_Set_Writable, "__gnat_set_writable");
|
|
C_Name : aliased String (Name'First .. Name'Last + 1);
|
|
begin
|
|
C_Name (Name'Range) := Name;
|
|
C_Name (C_Name'Last) := ASCII.NUL;
|
|
C_Set_Writable (C_Name (C_Name'First)'Address);
|
|
end Set_Writable;
|
|
|
|
------------
|
|
-- Setenv --
|
|
------------
|
|
|
|
procedure Setenv (Name : String; Value : String) is
|
|
F_Name : String (1 .. Name'Length + 1);
|
|
F_Value : String (1 .. Value'Length + 1);
|
|
|
|
procedure Set_Env_Value (Name, Value : System.Address);
|
|
pragma Import (C, Set_Env_Value, "__gnat_setenv");
|
|
|
|
begin
|
|
F_Name (1 .. Name'Length) := Name;
|
|
F_Name (F_Name'Last) := ASCII.NUL;
|
|
|
|
F_Value (1 .. Value'Length) := Value;
|
|
F_Value (F_Value'Last) := ASCII.NUL;
|
|
|
|
Set_Env_Value (F_Name'Address, F_Value'Address);
|
|
end Setenv;
|
|
|
|
-----------
|
|
-- Spawn --
|
|
-----------
|
|
|
|
function Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List) return Integer
|
|
is
|
|
Junk : Process_Id;
|
|
pragma Warnings (Off, Junk);
|
|
Result : Integer;
|
|
|
|
begin
|
|
Spawn_Internal (Program_Name, Args, Result, Junk, Blocking => True);
|
|
return Result;
|
|
end Spawn;
|
|
|
|
procedure Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Success : out Boolean)
|
|
is
|
|
begin
|
|
Success := (Spawn (Program_Name, Args) = 0);
|
|
end Spawn;
|
|
|
|
procedure Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Output_File_Descriptor : File_Descriptor;
|
|
Return_Code : out Integer;
|
|
Err_To_Out : Boolean := True)
|
|
is
|
|
Saved_Error : File_Descriptor := Invalid_FD; -- prevent compiler warning
|
|
Saved_Output : File_Descriptor;
|
|
|
|
begin
|
|
-- Set standard output and error to the temporary file
|
|
|
|
Saved_Output := Dup (Standout);
|
|
Dup2 (Output_File_Descriptor, Standout);
|
|
|
|
if Err_To_Out then
|
|
Saved_Error := Dup (Standerr);
|
|
Dup2 (Output_File_Descriptor, Standerr);
|
|
end if;
|
|
|
|
-- Spawn the program
|
|
|
|
Return_Code := Spawn (Program_Name, Args);
|
|
|
|
-- Restore the standard output and error
|
|
|
|
Dup2 (Saved_Output, Standout);
|
|
|
|
if Err_To_Out then
|
|
Dup2 (Saved_Error, Standerr);
|
|
end if;
|
|
|
|
-- And close the saved standard output and error file descriptors
|
|
|
|
Close (Saved_Output);
|
|
|
|
if Err_To_Out then
|
|
Close (Saved_Error);
|
|
end if;
|
|
end Spawn;
|
|
|
|
procedure Spawn
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Output_File : String;
|
|
Success : out Boolean;
|
|
Return_Code : out Integer;
|
|
Err_To_Out : Boolean := True)
|
|
is
|
|
FD : File_Descriptor;
|
|
|
|
begin
|
|
Success := True;
|
|
Return_Code := 0;
|
|
|
|
FD := Create_Output_Text_File (Output_File);
|
|
|
|
if FD = Invalid_FD then
|
|
Success := False;
|
|
return;
|
|
end if;
|
|
|
|
Spawn (Program_Name, Args, FD, Return_Code, Err_To_Out);
|
|
|
|
Close (FD, Success);
|
|
end Spawn;
|
|
|
|
--------------------
|
|
-- Spawn_Internal --
|
|
--------------------
|
|
|
|
procedure Spawn_Internal
|
|
(Program_Name : String;
|
|
Args : Argument_List;
|
|
Result : out Integer;
|
|
Pid : out Process_Id;
|
|
Blocking : Boolean)
|
|
is
|
|
procedure Spawn (Args : Argument_List);
|
|
-- Call Spawn with given argument list
|
|
|
|
N_Args : Argument_List (Args'Range);
|
|
-- Normalized arguments
|
|
|
|
-----------
|
|
-- Spawn --
|
|
-----------
|
|
|
|
procedure Spawn (Args : Argument_List) is
|
|
type Chars is array (Positive range <>) of aliased Character;
|
|
type Char_Ptr is access constant Character;
|
|
|
|
Command_Len : constant Positive := Program_Name'Length + 1 +
|
|
Args_Length (Args);
|
|
Command_Last : Natural := 0;
|
|
Command : aliased Chars (1 .. Command_Len);
|
|
-- Command contains all characters of the Program_Name and Args, all
|
|
-- terminated by ASCII.NUL characters.
|
|
|
|
Arg_List_Len : constant Positive := Args'Length + 2;
|
|
Arg_List_Last : Natural := 0;
|
|
Arg_List : aliased array (1 .. Arg_List_Len) of Char_Ptr;
|
|
-- List with pointers to NUL-terminated strings of the Program_Name
|
|
-- and the Args and terminated with a null pointer. We rely on the
|
|
-- default initialization for the last null pointer.
|
|
|
|
procedure Add_To_Command (S : String);
|
|
-- Add S and a NUL character to Command, updating Last
|
|
|
|
function Portable_Spawn (Args : Address) return Integer;
|
|
pragma Import (C, Portable_Spawn, "__gnat_portable_spawn");
|
|
|
|
function Portable_No_Block_Spawn (Args : Address) return Process_Id;
|
|
pragma Import
|
|
(C, Portable_No_Block_Spawn, "__gnat_portable_no_block_spawn");
|
|
|
|
--------------------
|
|
-- Add_To_Command --
|
|
--------------------
|
|
|
|
procedure Add_To_Command (S : String) is
|
|
First : constant Natural := Command_Last + 1;
|
|
|
|
begin
|
|
Command_Last := Command_Last + S'Length;
|
|
|
|
-- Move characters one at a time, because Command has aliased
|
|
-- components.
|
|
|
|
-- But not volatile, so why is this necessary ???
|
|
|
|
for J in S'Range loop
|
|
Command (First + J - S'First) := S (J);
|
|
end loop;
|
|
|
|
Command_Last := Command_Last + 1;
|
|
Command (Command_Last) := ASCII.NUL;
|
|
|
|
Arg_List_Last := Arg_List_Last + 1;
|
|
Arg_List (Arg_List_Last) := Command (First)'Access;
|
|
end Add_To_Command;
|
|
|
|
-- Start of processing for Spawn
|
|
|
|
begin
|
|
Add_To_Command (Program_Name);
|
|
|
|
for J in Args'Range loop
|
|
Add_To_Command (Args (J).all);
|
|
end loop;
|
|
|
|
if Blocking then
|
|
Pid := Invalid_Pid;
|
|
Result := Portable_Spawn (Arg_List'Address);
|
|
else
|
|
Pid := Portable_No_Block_Spawn (Arg_List'Address);
|
|
Result := Boolean'Pos (Pid /= Invalid_Pid);
|
|
end if;
|
|
end Spawn;
|
|
|
|
-- Start of processing for Spawn_Internal
|
|
|
|
begin
|
|
-- Copy arguments into a local structure
|
|
|
|
for K in N_Args'Range loop
|
|
N_Args (K) := new String'(Args (K).all);
|
|
end loop;
|
|
|
|
-- Normalize those arguments
|
|
|
|
Normalize_Arguments (N_Args);
|
|
|
|
-- Call spawn using the normalized arguments
|
|
|
|
Spawn (N_Args);
|
|
|
|
-- Free arguments list
|
|
|
|
for K in N_Args'Range loop
|
|
Free (N_Args (K));
|
|
end loop;
|
|
end Spawn_Internal;
|
|
|
|
---------------------------
|
|
-- To_Path_String_Access --
|
|
---------------------------
|
|
|
|
function To_Path_String_Access
|
|
(Path_Addr : Address;
|
|
Path_Len : Integer) return String_Access
|
|
is
|
|
subtype Path_String is String (1 .. Path_Len);
|
|
type Path_String_Access is access Path_String;
|
|
|
|
function Address_To_Access is new Ada.Unchecked_Conversion
|
|
(Source => Address, Target => Path_String_Access);
|
|
|
|
Path_Access : constant Path_String_Access :=
|
|
Address_To_Access (Path_Addr);
|
|
|
|
Return_Val : String_Access;
|
|
|
|
begin
|
|
Return_Val := new String (1 .. Path_Len);
|
|
|
|
for J in 1 .. Path_Len loop
|
|
Return_Val (J) := Path_Access (J);
|
|
end loop;
|
|
|
|
return Return_Val;
|
|
end To_Path_String_Access;
|
|
|
|
------------------
|
|
-- Wait_Process --
|
|
------------------
|
|
|
|
procedure Wait_Process (Pid : out Process_Id; Success : out Boolean) is
|
|
Status : Integer;
|
|
|
|
function Portable_Wait (S : Address) return Process_Id;
|
|
pragma Import (C, Portable_Wait, "__gnat_portable_wait");
|
|
|
|
begin
|
|
Pid := Portable_Wait (Status'Address);
|
|
Success := (Status = 0);
|
|
end Wait_Process;
|
|
|
|
-----------
|
|
-- Write --
|
|
-----------
|
|
|
|
function Write
|
|
(FD : File_Descriptor;
|
|
A : System.Address;
|
|
N : Integer) return Integer
|
|
is
|
|
begin
|
|
return
|
|
Integer (System.CRTL.write
|
|
(System.CRTL.int (FD),
|
|
System.CRTL.chars (A),
|
|
System.CRTL.size_t (N)));
|
|
end Write;
|
|
|
|
end System.OS_Lib;
|