1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . O S _ L I B --
10 -- Copyright (C) 1995-2002 Ada Core Technologies, Inc. --
12 -- GNAT is free software; you can redistribute it and/or modify it under --
13 -- terms of the GNU General Public License as published by the Free Soft- --
14 -- ware Foundation; either version 2, or (at your option) any later ver- --
15 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
18 -- for more details. You should have received a copy of the GNU General --
19 -- Public License distributed with GNAT; see file COPYING. If not, write --
20 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
21 -- MA 02111-1307, USA. --
23 -- As a special exception, if other files instantiate generics from this --
24 -- unit, or you link this unit with other files to produce an executable, --
25 -- this unit does not by itself cause the resulting executable to be --
26 -- covered by the GNU General Public License. This exception does not --
27 -- however invalidate any other reasons why the executable file might be --
28 -- covered by the GNU Public License. --
30 -- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). --
32 ------------------------------------------------------------------------------
34 with System
.Soft_Links
;
35 with Unchecked_Conversion
;
36 with System
; use System
;
38 package body GNAT
.OS_Lib
is
40 package SSL
renames System
.Soft_Links
;
42 -----------------------
43 -- Local Subprograms --
44 -----------------------
46 function Args_Length
(Args
: Argument_List
) return Natural;
47 -- Returns total number of characters needed to create a string
48 -- of all Args terminated by ASCII.NUL characters
50 function C_String_Length
(S
: Address
) return Integer;
51 -- Returns the length of a C string. Does check for null address
54 procedure Spawn_Internal
55 (Program_Name
: String;
60 -- Internal routine to implement the two Spawn (blocking/non blocking)
61 -- routines. If Blocking is set to True then the spawn is blocking
62 -- otherwise it is non blocking. In this latter case the Pid contains
63 -- the process id number. The first three parameters are as in Spawn.
64 -- Note that Spawn_Internal normalizes the argument list before calling
65 -- the low level system spawn routines (see Normalize_Arguments). Note
66 -- that Normalize_Arguments is designed to do nothing if it is called
67 -- more than once, so calling Normalize_Arguments before calling one
68 -- of the spawn routines is fine.
70 function To_Path_String_Access
74 -- Converts a C String to an Ada String. We could do this making use of
75 -- Interfaces.C.Strings but we prefer not to import that entire package
81 function Args_Length
(Args
: Argument_List
) return Natural is
85 for J
in Args
'Range loop
86 Len
:= Len
+ Args
(J
)'Length + 1; -- One extra for ASCII.NUL
92 -----------------------------
93 -- Argument_String_To_List --
94 -----------------------------
96 function Argument_String_To_List
98 return Argument_List_Access
100 Max_Args
: Integer := Arg_String
'Length;
101 New_Argv
: Argument_List
(1 .. Max_Args
);
102 New_Argc
: Natural := 0;
106 Idx
:= Arg_String
'First;
110 Quoted
: Boolean := False;
111 Backqd
: Boolean := False;
118 -- An unquoted space is the end of an argument
120 if not (Backqd
or Quoted
)
121 and then Arg_String
(Idx
) = ' '
125 -- Start of a quoted string
127 elsif not (Backqd
or Quoted
)
128 and then Arg_String
(Idx
) = '"'
132 -- End of a quoted string and end of an argument
134 elsif (Quoted
and not Backqd
)
135 and then Arg_String
(Idx
) = '"'
140 -- Following character is backquoted
142 elsif Arg_String
(Idx
) = '\' then
145 -- Turn off backquoting after advancing one character
153 exit when Idx
> Arg_String
'Last;
158 New_Argc
:= New_Argc
+ 1;
159 New_Argv
(New_Argc
) :=
160 new String'(Arg_String (Old_Idx .. Idx - 1));
162 -- Skip extraneous spaces
164 while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop
169 exit when Idx > Arg_String'Last;
172 return new Argument_List'(New_Argv
(1 .. New_Argc
));
173 end Argument_String_To_List
;
175 ---------------------
176 -- C_String_Length --
177 ---------------------
179 function C_String_Length
(S
: Address
) return Integer is
180 function Strlen
(S
: Address
) return Integer;
181 pragma Import
(C
, Strlen
, "strlen");
184 if S
= Null_Address
then
198 return File_Descriptor
200 function C_Create_File
203 return File_Descriptor
;
204 pragma Import
(C
, C_Create_File
, "__gnat_open_create");
207 return C_Create_File
(Name
, Fmode
);
213 return File_Descriptor
215 C_Name
: String (1 .. Name
'Length + 1);
218 C_Name
(1 .. Name
'Length) := Name
;
219 C_Name
(C_Name
'Last) := ASCII
.NUL
;
220 return Create_File
(C_Name
(C_Name
'First)'Address, Fmode
);
223 ---------------------
224 -- Create_New_File --
225 ---------------------
227 function Create_New_File
230 return File_Descriptor
232 function C_Create_New_File
235 return File_Descriptor
;
236 pragma Import
(C
, C_Create_New_File
, "__gnat_open_new");
239 return C_Create_New_File
(Name
, Fmode
);
242 function Create_New_File
245 return File_Descriptor
247 C_Name
: String (1 .. Name
'Length + 1);
250 C_Name
(1 .. Name
'Length) := Name
;
251 C_Name
(C_Name
'Last) := ASCII
.NUL
;
252 return Create_New_File
(C_Name
(C_Name
'First)'Address, Fmode
);
255 ----------------------
256 -- Create_Temp_File --
257 ----------------------
259 procedure Create_Temp_File
260 (FD
: out File_Descriptor
;
261 Name
: out Temp_File_Name
)
263 function Open_New_Temp
264 (Name
: System
.Address
;
266 return File_Descriptor
;
267 pragma Import
(C
, Open_New_Temp
, "__gnat_open_new_temp");
270 FD
:= Open_New_Temp
(Name
'Address, Binary
);
271 end Create_Temp_File
;
277 procedure Delete_File
(Name
: Address
; Success
: out Boolean) is
280 function unlink
(A
: Address
) return Integer;
281 pragma Import
(C
, unlink
, "unlink");
288 procedure Delete_File
(Name
: String; Success
: out Boolean) is
289 C_Name
: String (1 .. Name
'Length + 1);
292 C_Name
(1 .. Name
'Length) := Name
;
293 C_Name
(C_Name
'Last) := ASCII
.NUL
;
295 Delete_File
(C_Name
'Address, Success
);
298 ---------------------
299 -- File_Time_Stamp --
300 ---------------------
302 function File_Time_Stamp
(FD
: File_Descriptor
) return OS_Time
is
303 function File_Time
(FD
: File_Descriptor
) return OS_Time
;
304 pragma Import
(C
, File_Time
, "__gnat_file_time_fd");
307 return File_Time
(FD
);
310 function File_Time_Stamp
(Name
: C_File_Name
) return OS_Time
is
311 function File_Time
(Name
: Address
) return OS_Time
;
312 pragma Import
(C
, File_Time
, "__gnat_file_time_name");
315 return File_Time
(Name
);
318 function File_Time_Stamp
(Name
: String) return OS_Time
is
319 F_Name
: String (1 .. Name
'Length + 1);
322 F_Name
(1 .. Name
'Length) := Name
;
323 F_Name
(F_Name
'Last) := ASCII
.NUL
;
324 return File_Time_Stamp
(F_Name
'Address);
331 procedure Free
(Arg
: in out String_List_Access
) is
334 procedure Free_Array
is new Unchecked_Deallocation
335 (Object
=> String_List
, Name
=> String_List_Access
);
338 for J
in Arg
'Range loop
346 ---------------------------
347 -- Get_Debuggable_Suffix --
348 ---------------------------
350 function Get_Debuggable_Suffix
return String_Access
is
351 procedure Get_Suffix_Ptr
(Length
, Ptr
: Address
);
352 pragma Import
(C
, Get_Suffix_Ptr
, "__gnat_get_debuggable_suffix_ptr");
354 procedure Strncpy
(Astring_Addr
, Cstring
: Address
; N
: Integer);
355 pragma Import
(C
, Strncpy
, "strncpy");
357 Suffix_Ptr
: Address
;
358 Suffix_Length
: Integer;
359 Result
: String_Access
;
362 Get_Suffix_Ptr
(Suffix_Length
'Address, Suffix_Ptr
'Address);
364 Result
:= new String (1 .. Suffix_Length
);
366 if Suffix_Length
> 0 then
367 Strncpy
(Result
.all'Address, Suffix_Ptr
, Suffix_Length
);
371 end Get_Debuggable_Suffix
;
373 ---------------------------
374 -- Get_Executable_Suffix --
375 ---------------------------
377 function Get_Executable_Suffix
return String_Access
is
378 procedure Get_Suffix_Ptr
(Length
, Ptr
: Address
);
379 pragma Import
(C
, Get_Suffix_Ptr
, "__gnat_get_executable_suffix_ptr");
381 procedure Strncpy
(Astring_Addr
, Cstring
: Address
; N
: Integer);
382 pragma Import
(C
, Strncpy
, "strncpy");
384 Suffix_Ptr
: Address
;
385 Suffix_Length
: Integer;
386 Result
: String_Access
;
389 Get_Suffix_Ptr
(Suffix_Length
'Address, Suffix_Ptr
'Address);
391 Result
:= new String (1 .. Suffix_Length
);
393 if Suffix_Length
> 0 then
394 Strncpy
(Result
.all'Address, Suffix_Ptr
, Suffix_Length
);
398 end Get_Executable_Suffix
;
400 -----------------------
401 -- Get_Object_Suffix --
402 -----------------------
404 function Get_Object_Suffix
return String_Access
is
405 procedure Get_Suffix_Ptr
(Length
, Ptr
: Address
);
406 pragma Import
(C
, Get_Suffix_Ptr
, "__gnat_get_object_suffix_ptr");
408 procedure Strncpy
(Astring_Addr
, Cstring
: Address
; N
: Integer);
409 pragma Import
(C
, Strncpy
, "strncpy");
411 Suffix_Ptr
: Address
;
412 Suffix_Length
: Integer;
413 Result
: String_Access
;
416 Get_Suffix_Ptr
(Suffix_Length
'Address, Suffix_Ptr
'Address);
418 Result
:= new String (1 .. Suffix_Length
);
420 if Suffix_Length
> 0 then
421 Strncpy
(Result
.all'Address, Suffix_Ptr
, Suffix_Length
);
425 end Get_Object_Suffix
;
431 function Getenv
(Name
: String) return String_Access
is
432 procedure Get_Env_Value_Ptr
(Name
, Length
, Ptr
: Address
);
433 pragma Import
(C
, Get_Env_Value_Ptr
, "__gnat_get_env_value_ptr");
435 procedure Strncpy
(Astring_Addr
, Cstring
: Address
; N
: Integer);
436 pragma Import
(C
, Strncpy
, "strncpy");
438 Env_Value_Ptr
: Address
;
439 Env_Value_Length
: Integer;
440 F_Name
: String (1 .. Name
'Length + 1);
441 Result
: String_Access
;
444 F_Name
(1 .. Name
'Length) := Name
;
445 F_Name
(F_Name
'Last) := ASCII
.NUL
;
448 (F_Name
'Address, Env_Value_Length
'Address, Env_Value_Ptr
'Address);
450 Result
:= new String (1 .. Env_Value_Length
);
452 if Env_Value_Length
> 0 then
453 Strncpy
(Result
.all'Address, Env_Value_Ptr
, Env_Value_Length
);
463 function GM_Day
(Date
: OS_Time
) return Day_Type
is
472 GM_Split
(Date
, Y
, Mo
, D
, H
, Mn
, S
);
480 function GM_Hour
(Date
: OS_Time
) return Hour_Type
is
489 GM_Split
(Date
, Y
, Mo
, D
, H
, Mn
, S
);
497 function GM_Minute
(Date
: OS_Time
) return Minute_Type
is
506 GM_Split
(Date
, Y
, Mo
, D
, H
, Mn
, S
);
514 function GM_Month
(Date
: OS_Time
) return Month_Type
is
523 GM_Split
(Date
, Y
, Mo
, D
, H
, Mn
, S
);
531 function GM_Second
(Date
: OS_Time
) return Second_Type
is
540 GM_Split
(Date
, Y
, Mo
, D
, H
, Mn
, S
);
550 Year
: out Year_Type
;
551 Month
: out Month_Type
;
553 Hour
: out Hour_Type
;
554 Minute
: out Minute_Type
;
555 Second
: out Second_Type
)
558 (P_Time_T
, P_Year
, P_Month
, P_Day
, P_Hours
, P_Mins
, P_Secs
: Address
);
559 pragma Import
(C
, To_GM_Time
, "__gnat_to_gm_time");
570 -- Use the global lock because To_GM_Time is not thread safe.
572 Locked_Processing
: begin
575 (T
'Address, Y
'Address, Mo
'Address, D
'Address,
576 H
'Address, Mn
'Address, S
'Address);
583 end Locked_Processing
;
597 function GM_Year
(Date
: OS_Time
) return Year_Type
is
606 GM_Split
(Date
, Y
, Mo
, D
, H
, Mn
, S
);
610 ----------------------
611 -- Is_Absolute_Path --
612 ----------------------
614 function Is_Absolute_Path
(Name
: String) return Boolean is
615 function Is_Absolute_Path
(Name
: Address
) return Integer;
616 pragma Import
(C
, Is_Absolute_Path
, "__gnat_is_absolute_path");
618 F_Name
: String (1 .. Name
'Length + 1);
621 F_Name
(1 .. Name
'Length) := Name
;
622 F_Name
(F_Name
'Last) := ASCII
.NUL
;
624 return Is_Absolute_Path
(F_Name
'Address) /= 0;
625 end Is_Absolute_Path
;
631 function Is_Directory
(Name
: C_File_Name
) return Boolean is
632 function Is_Directory
(Name
: Address
) return Integer;
633 pragma Import
(C
, Is_Directory
, "__gnat_is_directory");
636 return Is_Directory
(Name
) /= 0;
639 function Is_Directory
(Name
: String) return Boolean is
640 F_Name
: String (1 .. Name
'Length + 1);
643 F_Name
(1 .. Name
'Length) := Name
;
644 F_Name
(F_Name
'Last) := ASCII
.NUL
;
645 return Is_Directory
(F_Name
'Address);
648 ---------------------
649 -- Is_Regular_File --
650 ---------------------
652 function Is_Regular_File
(Name
: C_File_Name
) return Boolean is
653 function Is_Regular_File
(Name
: Address
) return Integer;
654 pragma Import
(C
, Is_Regular_File
, "__gnat_is_regular_file");
657 return Is_Regular_File
(Name
) /= 0;
660 function Is_Regular_File
(Name
: String) return Boolean is
661 F_Name
: String (1 .. Name
'Length + 1);
664 F_Name
(1 .. Name
'Length) := Name
;
665 F_Name
(F_Name
'Last) := ASCII
.NUL
;
666 return Is_Regular_File
(F_Name
'Address);
669 ----------------------
670 -- Is_Writable_File --
671 ----------------------
673 function Is_Writable_File
(Name
: C_File_Name
) return Boolean is
674 function Is_Writable_File
(Name
: Address
) return Integer;
675 pragma Import
(C
, Is_Writable_File
, "__gnat_is_writable_file");
678 return Is_Writable_File
(Name
) /= 0;
679 end Is_Writable_File
;
681 function Is_Writable_File
(Name
: String) return Boolean is
682 F_Name
: String (1 .. Name
'Length + 1);
685 F_Name
(1 .. Name
'Length) := Name
;
686 F_Name
(F_Name
'Last) := ASCII
.NUL
;
687 return Is_Writable_File
(F_Name
'Address);
688 end Is_Writable_File
;
690 -------------------------
691 -- Locate_Exec_On_Path --
692 -------------------------
694 function Locate_Exec_On_Path
698 function Locate_Exec_On_Path
(C_Exec_Name
: Address
) return Address
;
699 pragma Import
(C
, Locate_Exec_On_Path
, "__gnat_locate_exec_on_path");
701 procedure Free
(Ptr
: System
.Address
);
702 pragma Import
(C
, Free
, "free");
704 C_Exec_Name
: String (1 .. Exec_Name
'Length + 1);
707 Result
: String_Access
;
710 C_Exec_Name
(1 .. Exec_Name
'Length) := Exec_Name
;
711 C_Exec_Name
(C_Exec_Name
'Last) := ASCII
.NUL
;
713 Path_Addr
:= Locate_Exec_On_Path
(C_Exec_Name
'Address);
714 Path_Len
:= C_String_Length
(Path_Addr
);
720 Result
:= To_Path_String_Access
(Path_Addr
, Path_Len
);
724 end Locate_Exec_On_Path
;
726 -------------------------
727 -- Locate_Regular_File --
728 -------------------------
730 function Locate_Regular_File
731 (File_Name
: C_File_Name
;
735 function Locate_Regular_File
736 (C_File_Name
, Path_Val
: Address
) return Address
;
737 pragma Import
(C
, Locate_Regular_File
, "__gnat_locate_regular_file");
739 procedure Free
(Ptr
: System
.Address
);
740 pragma Import
(C
, Free
, "free");
744 Result
: String_Access
;
747 Path_Addr
:= Locate_Regular_File
(File_Name
, Path
);
748 Path_Len
:= C_String_Length
(Path_Addr
);
753 Result
:= To_Path_String_Access
(Path_Addr
, Path_Len
);
757 end Locate_Regular_File
;
759 function Locate_Regular_File
764 C_File_Name
: String (1 .. File_Name
'Length + 1);
765 C_Path
: String (1 .. Path
'Length + 1);
768 C_File_Name
(1 .. File_Name
'Length) := File_Name
;
769 C_File_Name
(C_File_Name
'Last) := ASCII
.NUL
;
771 C_Path
(1 .. Path
'Length) := Path
;
772 C_Path
(C_Path
'Last) := ASCII
.NUL
;
774 return Locate_Regular_File
(C_File_Name
'Address, C_Path
'Address);
775 end Locate_Regular_File
;
777 ------------------------
778 -- Non_Blocking_Spawn --
779 ------------------------
781 function Non_Blocking_Spawn
782 (Program_Name
: String;
783 Args
: Argument_List
)
790 Spawn_Internal
(Program_Name
, Args
, Junk
, Pid
, Blocking
=> False);
792 end Non_Blocking_Spawn
;
794 -------------------------
795 -- Normalize_Arguments --
796 -------------------------
798 procedure Normalize_Arguments
(Args
: in out Argument_List
) is
800 procedure Quote_Argument
(Arg
: in out String_Access
);
801 -- Add quote around argument if it contains spaces.
803 Argument_Needs_Quote
: Boolean;
804 pragma Import
(C
, Argument_Needs_Quote
, "__gnat_argument_needs_quote");
810 procedure Quote_Argument
(Arg
: in out String_Access
) is
811 Res
: String (1 .. Arg
'Length * 2);
813 Quote_Needed
: Boolean := False;
816 if Arg
(Arg
'First) /= '"' or else Arg
(Arg
'Last) /= '"' then
822 for K
in Arg
'Range loop
826 if Arg
(K
) = '"' then
831 elsif Arg
(K
) = ' ' then
833 Quote_Needed
:= True;
849 Old
: String_Access
:= Arg
;
852 Arg
:= new String'(Res (1 .. J));
861 if Argument_Needs_Quote then
862 for K in Args'Range loop
863 if Args (K) /= null then
864 Quote_Argument (Args (K));
868 end Normalize_Arguments;
870 ------------------------
871 -- Normalize_Pathname --
872 ------------------------
874 function Normalize_Pathname
876 Directory : String := "")
880 pragma Import (C, Max_Path, "max_path_len");
881 -- Maximum length of a path name
883 procedure Get_Current_Dir
884 (Dir : System.Address;
885 Length : System.Address);
886 pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir");
888 Path_Buffer : String (1 .. Max_Path + Max_Path + 2);
889 End_Path : Natural := 0;
890 Link_Buffer : String (1 .. Max_Path + 2);
896 Max_Iterations : constant := 500;
899 (Path : System.Address;
900 Buf : System.Address;
903 pragma Import (C, Readlink, "__gnat_readlink");
905 function To_Canonical_File_Spec
906 (Host_File : System.Address)
907 return System.Address;
909 (C, To_Canonical_File_Spec, "__gnat_to_canonical_file_spec");
911 The_Name : String (1 .. Name'Length + 1);
912 Canonical_File_Addr : System.Address;
913 Canonical_File_Len : Integer;
915 Need_To_Check_Drive_Letter : Boolean := False;
916 -- Set to true if Name is an absolute path that starts with "//"
918 function Strlen (S : System.Address) return Integer;
919 pragma Import (C, Strlen, "strlen");
921 function Get_Directory return String;
922 -- If Directory is not empty, return it, adding a directory separator
923 -- if not already present, otherwise return current working directory
924 -- with terminating directory separator.
926 function Final_Value (S : String) return String;
927 -- Make final adjustment to the returned string.
928 -- To compensate for non standard path name in Interix,
929 -- if S is "/x" or starts with "/x", where x is a capital
930 -- letter 'A
' to 'Z
', add an additional '/' at the beginning
931 -- so that the returned value starts with "//x".
937 function Get_Directory return String is
939 -- Directory given, add directory separator if needed
941 if Directory'Length > 0 then
942 if Directory (Directory'Length) = Directory_Separator then
946 Result : String (1 .. Directory'Length + 1);
949 Result (1 .. Directory'Length) := Directory;
950 Result (Result'Length) := Directory_Separator;
955 -- Directory name not given, get current directory
959 Buffer : String (1 .. Max_Path + 2);
960 Path_Len : Natural := Max_Path;
963 Get_Current_Dir (Buffer'Address, Path_Len'Address);
965 if Buffer (Path_Len) /= Directory_Separator then
966 Path_Len := Path_Len + 1;
967 Buffer (Path_Len) := Directory_Separator;
970 return Buffer (1 .. Path_Len);
975 Reference_Dir : constant String := Get_Directory;
976 -- Current directory name specified
982 function Final_Value (S : String) return String is
984 -- Interix has the non standard notion of disk drive
985 -- indicated by two '/' followed by a capital letter
986 -- 'A
' .. 'Z
'. One of the two '/' may have been removed
987 -- by Normalize_Pathname. It has to be added again.
988 -- For other OSes, this should not make no difference.
990 if Need_To_Check_Drive_Letter
991 and then S'Length >= 2
992 and then S (S'First) = '/'
993 and then S (S'First + 1) in 'A
' .. 'Z
'
994 and then (S'Length = 2 or else S (S'First + 2) = '/')
997 Result : String (1 .. S'Length + 1);
1001 Result (2 .. Result'Last) := S;
1011 -- Start of processing for Normalize_Pathname
1014 -- Special case, if name is null, then return null
1016 if Name'Length = 0 then
1020 -- First, convert VMS file spec to Unix file spec.
1021 -- If Name is not in VMS syntax, then this is equivalent
1022 -- to put Name at the begining of Path_Buffer.
1024 VMS_Conversion : begin
1025 The_Name (1 .. Name'Length) := Name;
1026 The_Name (The_Name'Last) := ASCII.NUL;
1028 Canonical_File_Addr := To_Canonical_File_Spec (The_Name'Address);
1029 Canonical_File_Len := Strlen (Canonical_File_Addr);
1031 -- If VMS syntax conversion has failed, return an empty string
1032 -- to indicate the failure.
1034 if Canonical_File_Len = 0 then
1039 subtype Path_String is String (1 .. Canonical_File_Len);
1040 type Path_String_Access is access Path_String;
1042 function Address_To_Access is new
1043 Unchecked_Conversion (Source => Address,
1044 Target => Path_String_Access);
1046 Path_Access : Path_String_Access :=
1047 Address_To_Access (Canonical_File_Addr);
1050 Path_Buffer (1 .. Canonical_File_Len) := Path_Access.all;
1051 End_Path := Canonical_File_Len;
1056 -- Replace all '/' by Directory Separators (this is for Windows)
1058 if Directory_Separator /= '/' then
1059 for Index in 1 .. End_Path loop
1060 if Path_Buffer (Index) = '/' then
1061 Path_Buffer (Index) := Directory_Separator;
1066 -- Start the conversions
1068 -- If this is not finished after Max_Iterations, give up and
1069 -- return an empty string.
1071 for J in 1 .. Max_Iterations loop
1073 -- If we don't have an absolute pathname, prepend
1074 -- the directory Reference_Dir.
1077 and then not Is_Absolute_Path (Path_Buffer (1 .. End_Path))
1080 (Reference_Dir'Last + 1 .. Reference_Dir'Length + End_Path) :=
1081 Path_Buffer (1 .. End_Path);
1082 End_Path := Reference_Dir'Length + End_Path;
1083 Path_Buffer (1 .. Reference_Dir'Length) := Reference_Dir;
1084 Last := Reference_Dir'Length;
1087 -- If name starts with "//", we may have a drive letter on Interix
1089 if Last = 1 and then End_Path >= 3 then
1090 Need_To_Check_Drive_Letter := (Path_Buffer (1 .. 2)) = "//";
1096 -- If we have traversed the full pathname, return it
1098 if Start > End_Path then
1099 return Final_Value (Path_Buffer (1 .. End_Path));
1102 -- Remove duplicate directory separators
1104 while Path_Buffer (Start) = Directory_Separator loop
1105 if Start = End_Path then
1106 return Final_Value (Path_Buffer (1 .. End_Path - 1));
1109 Path_Buffer (Start .. End_Path - 1) :=
1110 Path_Buffer (Start + 1 .. End_Path);
1111 End_Path := End_Path - 1;
1115 -- Find the end of the current field: last character
1116 -- or the one preceding the next directory separator.
1118 while Finish < End_Path
1119 and then Path_Buffer (Finish + 1) /= Directory_Separator
1121 Finish := Finish + 1;
1126 if Start = Finish and then Path_Buffer (Start) = '.' then
1127 if Start = End_Path then
1129 return (1 => Directory_Separator);
1131 return Path_Buffer (1 .. Last - 1);
1135 Path_Buffer (Last + 1 .. End_Path - 2) :=
1136 Path_Buffer (Last + 3 .. End_Path);
1137 End_Path := End_Path - 2;
1140 -- Remove ".." fields
1142 elsif Finish = Start + 1
1143 and then Path_Buffer (Start .. Finish) = ".."
1148 exit when Start < 1 or else
1149 Path_Buffer (Start) = Directory_Separator;
1153 if Finish = End_Path then
1154 return (1 => Directory_Separator);
1157 Path_Buffer (1 .. End_Path - Finish) :=
1158 Path_Buffer (Finish + 1 .. End_Path);
1159 End_Path := End_Path - Finish;
1164 if Finish = End_Path then
1165 return Final_Value (Path_Buffer (1 .. Start - 1));
1168 Path_Buffer (Start + 1 .. Start + End_Path - Finish - 1) :=
1169 Path_Buffer (Finish + 2 .. End_Path);
1170 End_Path := Start + End_Path - Finish - 1;
1175 -- Check if current field is a symbolic link
1179 Saved : Character := Path_Buffer (Finish + 1);
1182 Path_Buffer (Finish + 1) := ASCII.NUL;
1183 Status := Readlink (Path_Buffer'Address,
1184 Link_Buffer'Address,
1185 Link_Buffer'Length);
1186 Path_Buffer (Finish + 1) := Saved;
1189 -- Not a symbolic link, move to the next field, if any
1194 -- Replace symbolic link with its value.
1197 if Is_Absolute_Path (Link_Buffer (1 .. Status)) then
1198 Path_Buffer (Status + 1 .. End_Path - (Finish - Status)) :=
1199 Path_Buffer (Finish + 1 .. End_Path);
1200 End_Path := End_Path - (Finish - Status);
1201 Path_Buffer (1 .. Status) := Link_Buffer (1 .. Status);
1206 (Last + Status + 1 .. End_Path - Finish + Last + Status) :=
1207 Path_Buffer (Finish + 1 .. End_Path);
1208 End_Path := End_Path - Finish + Last + Status;
1209 Path_Buffer (Last + 1 .. Last + Status) :=
1210 Link_Buffer (1 .. Status);
1216 -- Too many iterations: give up
1218 -- This can happen when there is a circularity in the symbolic links:
1219 -- A is a symbolic link for B, which itself is a symbolic link, and
1220 -- the target of B or of another symbolic link target of B is A.
1221 -- In this case, we return an empty string to indicate failure to
1225 end Normalize_Pathname;
1232 (Name : C_File_Name;
1234 return File_Descriptor
1236 function C_Open_Read
1237 (Name : C_File_Name;
1239 return File_Descriptor;
1240 pragma Import (C, C_Open_Read, "__gnat_open_read");
1243 return C_Open_Read (Name, Fmode);
1249 return File_Descriptor
1251 C_Name : String (1 .. Name'Length + 1);
1254 C_Name (1 .. Name'Length) := Name;
1255 C_Name (C_Name'Last) := ASCII.NUL;
1256 return Open_Read (C_Name (C_Name'First)'Address, Fmode);
1259 ---------------------
1260 -- Open_Read_Write --
1261 ---------------------
1263 function Open_Read_Write
1264 (Name : C_File_Name;
1266 return File_Descriptor
1268 function C_Open_Read_Write
1269 (Name : C_File_Name;
1271 return File_Descriptor;
1272 pragma Import (C, C_Open_Read_Write, "__gnat_open_rw");
1275 return C_Open_Read_Write (Name, Fmode);
1276 end Open_Read_Write;
1278 function Open_Read_Write
1281 return File_Descriptor
1283 C_Name : String (1 .. Name'Length + 1);
1286 C_Name (1 .. Name'Length) := Name;
1287 C_Name (C_Name'Last) := ASCII.NUL;
1288 return Open_Read_Write (C_Name (C_Name'First)'Address, Fmode);
1289 end Open_Read_Write;
1295 procedure Rename_File
1296 (Old_Name : C_File_Name;
1297 New_Name : C_File_Name;
1298 Success : out Boolean)
1300 function rename (From, To : Address) return Integer;
1301 pragma Import (C, rename, "rename");
1306 R := rename (Old_Name, New_Name);
1310 procedure Rename_File
1313 Success : out Boolean)
1315 C_Old_Name : String (1 .. Old_Name'Length + 1);
1316 C_New_Name : String (1 .. New_Name'Length + 1);
1319 C_Old_Name (1 .. Old_Name'Length) := Old_Name;
1320 C_Old_Name (C_Old_Name'Last) := ASCII.NUL;
1322 C_New_Name (1 .. New_Name'Length) := New_Name;
1323 C_New_Name (C_New_Name'Last) := ASCII.NUL;
1325 Rename_File (C_Old_Name'Address, C_New_Name'Address, Success);
1332 procedure Setenv (Name : String; Value : String) is
1333 F_Name : String (1 .. Name'Length + 1);
1334 F_Value : String (1 .. Value'Length + 1);
1336 procedure Set_Env_Value (Name, Value : System.Address);
1337 pragma Import (C, Set_Env_Value, "__gnat_set_env_value");
1340 F_Name (1 .. Name'Length) := Name;
1341 F_Name (F_Name'Last) := ASCII.NUL;
1343 F_Value (1 .. Value'Length) := Value;
1344 F_Value (F_Value'Last) := ASCII.NUL;
1346 Set_Env_Value (F_Name'Address, F_Value'Address);
1354 (Program_Name : String;
1355 Args : Argument_List)
1362 Spawn_Internal (Program_Name, Args, Result, Junk, Blocking => True);
1367 (Program_Name : String;
1368 Args : Argument_List;
1369 Success : out Boolean)
1372 Success := (Spawn (Program_Name, Args) = 0);
1375 --------------------
1376 -- Spawn_Internal --
1377 --------------------
1379 procedure Spawn_Internal
1380 (Program_Name : String;
1381 Args : Argument_List;
1382 Result : out Integer;
1383 Pid : out Process_Id;
1387 procedure Spawn (Args : Argument_List);
1390 N_Args : Argument_List (Args'Range);
1391 -- Normalized arguments
1397 procedure Spawn (Args : Argument_List) is
1398 type Chars is array (Positive range <>) of aliased Character;
1399 type Char_Ptr is access constant Character;
1401 Command_Len : constant Positive := Program_Name'Length + 1
1402 + Args_Length (Args);
1403 Command_Last : Natural := 0;
1404 Command : aliased Chars (1 .. Command_Len);
1405 -- Command contains all characters of the Program_Name and Args,
1406 -- all terminated by ASCII.NUL characters
1408 Arg_List_Len : constant Positive := Args'Length + 2;
1409 Arg_List_Last : Natural := 0;
1410 Arg_List : aliased array (1 .. Arg_List_Len) of Char_Ptr;
1411 -- List with pointers to NUL-terminated strings of the
1412 -- Program_Name and the Args and terminated with a null pointer.
1413 -- We rely on the default initialization for the last null pointer.
1415 procedure Add_To_Command (S : String);
1416 -- Add S and a NUL character to Command, updating Last
1418 function Portable_Spawn (Args : Address) return Integer;
1419 pragma Import (C, Portable_Spawn, "__gnat_portable_spawn");
1421 function Portable_No_Block_Spawn (Args : Address) return Process_Id;
1423 (C, Portable_No_Block_Spawn, "__gnat_portable_no_block_spawn");
1425 --------------------
1426 -- Add_To_Command --
1427 --------------------
1429 procedure Add_To_Command (S : String) is
1430 First : constant Natural := Command_Last + 1;
1433 Command_Last := Command_Last + S'Length;
1435 -- Move characters one at a time, because Command has
1436 -- aliased components.
1438 for J in S'Range loop
1439 Command (First + J - S'First) := S (J);
1442 Command_Last := Command_Last + 1;
1443 Command (Command_Last) := ASCII.NUL;
1445 Arg_List_Last := Arg_List_Last + 1;
1446 Arg_List (Arg_List_Last) := Command (First)'Access;
1449 -- Start of processing for Spawn
1452 Add_To_Command (Program_Name);
1454 for J in Args'Range loop
1455 Add_To_Command (Args (J).all);
1460 Result := Portable_Spawn (Arg_List'Address);
1462 Pid := Portable_No_Block_Spawn (Arg_List'Address);
1463 Result := Boolean'Pos (Pid /= Invalid_Pid);
1467 -- Start of processing for Spawn_Internal
1470 -- Copy arguments into a local structure
1472 for K in N_Args'Range loop
1473 N_Args (K) := new String'(Args
(K
).all);
1476 -- Normalize those arguments
1478 Normalize_Arguments
(N_Args
);
1480 -- Call spawn using the normalized arguments
1484 -- Free arguments list
1486 for K
in N_Args
'Range loop
1491 ---------------------------
1492 -- To_Path_String_Access --
1493 ---------------------------
1495 function To_Path_String_Access
1496 (Path_Addr
: Address
;
1498 return String_Access
1500 subtype Path_String
is String (1 .. Path_Len
);
1501 type Path_String_Access
is access Path_String
;
1503 function Address_To_Access
is new
1504 Unchecked_Conversion
(Source
=> Address
,
1505 Target
=> Path_String_Access
);
1507 Path_Access
: Path_String_Access
:= Address_To_Access
(Path_Addr
);
1509 Return_Val
: String_Access
;
1512 Return_Val
:= new String (1 .. Path_Len
);
1514 for J
in 1 .. Path_Len
loop
1515 Return_Val
(J
) := Path_Access
(J
);
1519 end To_Path_String_Access
;
1525 procedure Wait_Process
(Pid
: out Process_Id
; Success
: out Boolean) is
1528 function Portable_Wait
(S
: Address
) return Process_Id
;
1529 pragma Import
(C
, Portable_Wait
, "__gnat_portable_wait");
1532 Pid
:= Portable_Wait
(Status
'Address);
1533 Success
:= (Status
= 0);