| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T . O S _ L I B -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- $Revision$ |
| -- -- |
| -- Copyright (C) 1995-2001 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- -- |
| -- ware Foundation; either version 2, 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. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING. If not, write -- |
| -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- |
| -- MA 02111-1307, USA. -- |
| -- -- |
| -- As a special exception, if other files instantiate generics from this -- |
| -- unit, or you link this unit with other files to produce an executable, -- |
| -- this unit does not by itself cause the resulting executable to be -- |
| -- covered by the GNU General Public License. This exception does not -- |
| -- 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). -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with System.Soft_Links; |
| with Unchecked_Conversion; |
| with System; use System; |
| |
| package body GNAT.OS_Lib is |
| |
| package SSL renames System.Soft_Links; |
| |
| ----------------------- |
| -- 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 |
| |
| function C_String_Length (S : Address) return Integer; |
| -- Returns the length of a C string. Does check for null address |
| -- (returns 0). |
| |
| procedure Spawn_Internal |
| (Program_Name : String; |
| Args : Argument_List; |
| Result : out Integer; |
| Pid : out Process_Id; |
| Blocking : Boolean); |
| -- Internal routine to implement the to Spawn (blocking and 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. |
| |
| 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 |
| |
| ----------------- |
| -- 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 : Integer := Arg_String'Length; |
| New_Argv : Argument_List (1 .. Max_Args); |
| New_Argc : Natural := 0; |
| Idx : Integer; |
| |
| begin |
| Idx := Arg_String'First; |
| |
| loop |
| declare |
| Quoted : Boolean := False; |
| Backqd : Boolean := False; |
| Old_Idx : Integer; |
| |
| begin |
| Old_Idx := Idx; |
| |
| loop |
| -- A vanilla space is the end of an argument |
| |
| if not Backqd and then not Quoted |
| and then Arg_String (Idx) = ' ' |
| then |
| exit; |
| |
| -- Start of a quoted string |
| |
| elsif not Backqd and then not Quoted |
| and then Arg_String (Idx) = '"' |
| then |
| Quoted := True; |
| |
| -- End of a quoted string and end of an argument |
| |
| elsif not Backqd and then Quoted |
| and then Arg_String (Idx) = '"' |
| then |
| Idx := Idx + 1; |
| exit; |
| |
| -- Following character is backquoted |
| |
| elsif Arg_String (Idx) = '\' then |
| Backqd := True; |
| |
| -- Turn off backquoting after advancing one character |
| |
| elsif Backqd then |
| Backqd := False; |
| |
| 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'(Arg_String (Old_Idx .. Idx - 1)); |
| |
| -- Skip extraneous spaces |
| |
| while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop |
| Idx := Idx + 1; |
| end loop; |
| end; |
| |
| exit when Idx > Arg_String'Last; |
| 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 |
| function Strlen (S : Address) return Integer; |
| pragma Import (C, Strlen, "strlen"); |
| |
| begin |
| if S = Null_Address then |
| return 0; |
| else |
| return Strlen (S); |
| end if; |
| end C_String_Length; |
| |
| ----------------- |
| -- 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_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; |
| |
| ----------------- |
| -- Delete_File -- |
| ----------------- |
| |
| procedure Delete_File (Name : Address; Success : out Boolean) is |
| R : Integer; |
| |
| function unlink (A : Address) return Integer; |
| pragma Import (C, unlink, "unlink"); |
| |
| begin |
| R := 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; |
| |
| --------------------- |
| -- 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"); |
| |
| procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer); |
| pragma Import (C, Strncpy, "strncpy"); |
| |
| Suffix_Ptr : Address; |
| Suffix_Length : Integer; |
| Result : String_Access; |
| |
| 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, 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"); |
| |
| procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer); |
| pragma Import (C, Strncpy, "strncpy"); |
| |
| Suffix_Ptr : Address; |
| Suffix_Length : Integer; |
| Result : String_Access; |
| |
| 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, 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"); |
| |
| procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer); |
| pragma Import (C, Strncpy, "strncpy"); |
| |
| Suffix_Ptr : Address; |
| Suffix_Length : Integer; |
| Result : String_Access; |
| |
| 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, Suffix_Length); |
| end if; |
| |
| return Result; |
| end Get_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_get_env_value_ptr"); |
| |
| procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer); |
| pragma Import (C, Strncpy, "strncpy"); |
| |
| Env_Value_Ptr : Address; |
| Env_Value_Length : Integer; |
| F_Name : 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, Env_Value_Length); |
| end if; |
| |
| return Result; |
| end Getenv; |
| |
| ------------ |
| -- GM_Day -- |
| ------------ |
| |
| function GM_Day (Date : OS_Time) return Day_Type is |
| Y : Year_Type; |
| Mo : Month_Type; |
| D : Day_Type; |
| H : Hour_Type; |
| Mn : Minute_Type; |
| S : Second_Type; |
| |
| 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 |
| Y : Year_Type; |
| Mo : Month_Type; |
| D : Day_Type; |
| H : Hour_Type; |
| Mn : Minute_Type; |
| S : Second_Type; |
| |
| 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 |
| Y : Year_Type; |
| Mo : Month_Type; |
| D : Day_Type; |
| H : Hour_Type; |
| Mn : Minute_Type; |
| S : Second_Type; |
| |
| 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 |
| Y : Year_Type; |
| Mo : Month_Type; |
| D : Day_Type; |
| H : Hour_Type; |
| Mn : Minute_Type; |
| S : Second_Type; |
| |
| 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 |
| Y : Year_Type; |
| Mo : Month_Type; |
| D : Day_Type; |
| H : Hour_Type; |
| Mn : Minute_Type; |
| S : Second_Type; |
| |
| 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, P_Year, P_Month, P_Day, P_Hours, P_Mins, 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 |
| (T'Address, Y'Address, Mo'Address, D'Address, |
| H'Address, Mn'Address, 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_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; |
| |
| 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) return Integer; |
| pragma Import (C, Is_Absolute_Path, "__gnat_is_absolute_path"); |
| |
| F_Name : String (1 .. Name'Length + 1); |
| |
| begin |
| F_Name (1 .. Name'Length) := Name; |
| F_Name (F_Name'Last) := ASCII.NUL; |
| |
| return Is_Absolute_Path (F_Name'Address) /= 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_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_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; |
| |
| ------------------------- |
| -- 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"); |
| |
| procedure Free (Ptr : System.Address); |
| pragma Import (C, Free, "free"); |
| |
| 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); |
| Free (Path_Addr); |
| 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"); |
| |
| procedure Free (Ptr : System.Address); |
| pragma Import (C, Free, "free"); |
| |
| 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); |
| 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); |
| |
| 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; |
| |
| return Locate_Regular_File (C_File_Name'Address, C_Path'Address); |
| end Locate_Regular_File; |
| |
| ------------------------ |
| -- Non_Blocking_Spawn -- |
| ------------------------ |
| |
| function Non_Blocking_Spawn |
| (Program_Name : String; |
| Args : Argument_List) |
| return Process_Id |
| is |
| Junk : Integer; |
| Pid : Process_Id; |
| |
| begin |
| Spawn_Internal (Program_Name, Args, Junk, Pid, Blocking => False); |
| return Pid; |
| end Non_Blocking_Spawn; |
| |
| ------------------------ |
| -- Normalize_Pathname -- |
| ------------------------ |
| |
| function Normalize_Pathname |
| (Name : String; |
| Directory : String := "") |
| return String |
| is |
| Max_Path : Integer; |
| pragma Import (C, Max_Path, "max_path_len"); |
| -- Maximum length of a path name |
| |
| procedure Get_Current_Dir |
| (Dir : System.Address; |
| Length : System.Address); |
| pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir"); |
| |
| Path_Buffer : String (1 .. Max_Path + Max_Path + 2); |
| End_Path : Natural := 0; |
| Link_Buffer : String (1 .. Max_Path + 2); |
| Status : Integer; |
| Last : Positive; |
| Start : Natural; |
| Finish : Positive; |
| |
| Max_Iterations : constant := 500; |
| |
| 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"); |
| |
| The_Name : String (1 .. Name'Length + 1); |
| Canonical_File_Addr : System.Address; |
| Canonical_File_Len : Integer; |
| |
| Need_To_Check_Drive_Letter : Boolean := False; |
| -- Set to true if Name is an absolute path that starts with "//" |
| |
| function Strlen (S : System.Address) return Integer; |
| pragma Import (C, Strlen, "strlen"); |
| |
| function Get_Directory return String; |
| -- If Directory is not empty, return it, adding a directory separator |
| -- if not already present, otherwise return current working directory |
| -- with terminating directory separator. |
| |
| function Final_Value (S : String) return String; |
| -- Make final adjustment to the returned string. |
| -- To compensate for non standard path name in Interix, |
| -- if S is "/x" or starts with "/x", where x is a capital |
| -- letter 'A' to 'Z', add an additional '/' at the beginning |
| -- so that the returned value starts with "//x". |
| |
| ------------------- |
| -- Get_Directory -- |
| ------------------- |
| |
| function Get_Directory return String is |
| begin |
| -- Directory given, add directory separator if needed |
| |
| if Directory'Length > 0 then |
| if Directory (Directory'Length) = Directory_Separator then |
| return Directory; |
| else |
| declare |
| Result : String (1 .. Directory'Length + 1); |
| |
| begin |
| Result (1 .. Directory'Length) := Directory; |
| Result (Result'Length) := Directory_Separator; |
| return Result; |
| end; |
| end if; |
| |
| -- 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; |
| |
| return Buffer (1 .. Path_Len); |
| end; |
| end if; |
| end Get_Directory; |
| |
| Reference_Dir : constant String := Get_Directory; |
| -- Current directory name specified |
| |
| function Final_Value (S : String) return String is |
| begin |
| -- Interix has the non standard notion of disk drive |
| -- indicated by two '/' followed by a capital letter |
| -- 'A' .. 'Z'. One of the two '/' may have been removed |
| -- by Normalize_Pathname. It has to be added again. |
| -- For other OSes, this should not make no difference. |
| |
| if Need_To_Check_Drive_Letter |
| and then S'Length >= 2 |
| and then S (S'First) = '/' |
| and then S (S'First + 1) in 'A' .. 'Z' |
| and then (S'Length = 2 or else S (S'First + 2) = '/') |
| then |
| declare |
| Result : String (1 .. S'Length + 1); |
| |
| begin |
| Result (1) := '/'; |
| Result (2 .. Result'Last) := S; |
| return Result; |
| end; |
| |
| else |
| return S; |
| end if; |
| |
| end Final_Value; |
| |
| -- Start of processing for Normalize_Pathname |
| |
| begin |
| -- Special case, if name is null, then return null |
| |
| if Name'Length = 0 then |
| return ""; |
| end if; |
| |
| -- First, convert VMS file spec to Unix file spec. |
| -- If Name is not in VMS syntax, then this is equivalent |
| -- to put Name at the begining of Path_Buffer. |
| |
| VMS_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 := Strlen (Canonical_File_Addr); |
| |
| -- If VMS 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); |
| type Path_String_Access is access Path_String; |
| |
| function Address_To_Access is new |
| Unchecked_Conversion (Source => Address, |
| Target => Path_String_Access); |
| |
| Path_Access : Path_String_Access := |
| Address_To_Access (Canonical_File_Addr); |
| |
| begin |
| Path_Buffer (1 .. Canonical_File_Len) := Path_Access.all; |
| End_Path := Canonical_File_Len; |
| Last := 1; |
| end; |
| end VMS_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; |
| |
| -- 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 |
| Path_Buffer |
| (Reference_Dir'Last + 1 .. Reference_Dir'Length + End_Path) := |
| Path_Buffer (1 .. End_Path); |
| End_Path := Reference_Dir'Length + End_Path; |
| Path_Buffer (1 .. Reference_Dir'Length) := Reference_Dir; |
| Last := Reference_Dir'Length; |
| end if; |
| |
| -- If name starts with "//", we may have a drive letter on Interix |
| |
| if Last = 1 and then End_Path >= 3 then |
| Need_To_Check_Drive_Letter := (Path_Buffer (1 .. 2)) = "//"; |
| end if; |
| |
| Start := Last + 1; |
| Finish := Last; |
| |
| -- 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 |
| 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 |
| |
| else |
| declare |
| Saved : Character := Path_Buffer (Finish + 1); |
| |
| begin |
| Path_Buffer (Finish + 1) := ASCII.NUL; |
| Status := Readlink (Path_Buffer'Address, |
| Link_Buffer'Address, |
| 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; |
| 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_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; |
| |
| ----------------- |
| -- 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, "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; |
| |
| ------------ |
| -- 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_set_env_value"); |
| |
| 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; |
| 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; |
| |
| -------------------- |
| -- Spawn_Internal -- |
| -------------------- |
| |
| procedure Spawn_Internal |
| (Program_Name : String; |
| Args : Argument_List; |
| Result : out Integer; |
| Pid : out Process_Id; |
| Blocking : Boolean) |
| 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. |
| |
| 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_Internal |
| |
| 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_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 |
| Unchecked_Conversion (Source => Address, |
| Target => Path_String_Access); |
| |
| Path_Access : 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; |
| |
| end GNAT.OS_Lib; |