1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . O S _ L I B --
9 -- Copyright (C) 1995-2005, AdaCore --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with System.Case_Util;
36 with System.Soft_Links;
37 with Unchecked_Conversion;
38 with Unchecked_Deallocation;
39 with System; use System;
41 package body GNAT.OS_Lib is
43 -- Imported procedures Dup and Dup2 are used in procedures Spawn and
44 -- Non_Blocking_Spawn.
46 function Dup (Fd : File_Descriptor) return File_Descriptor;
47 pragma Import (C, Dup, "__gnat_dup");
49 procedure Dup2 (Old_Fd, New_Fd : File_Descriptor);
50 pragma Import (C, Dup2, "__gnat_dup2");
53 -- Note: OpenVMS should be a constant, but it cannot be, because it
54 -- prevents bootstrapping on some platforms.
56 pragma Import (Ada, OpenVMS, "system__openvms");
57 -- Needed to avoid doing useless checks when non on a VMS platform (see
58 -- Normalize_Pathname).
60 On_Windows : constant Boolean := Directory_Separator = '\';
61 -- An indication that we are on Windows. Used in Normalize_Pathname, to
62 -- deal with drive letters in the beginning of absolute paths.
64 package SSL renames System.Soft_Links;
66 -- The following are used by Create_Temp_File
68 First_Temp_File_Name : constant String := "GNAT-TEMP-000000.TMP";
69 -- Used to initialize Current_Temp_File_Name and Temp_File_Name_Last_Digit
71 Current_Temp_File_Name : String := First_Temp_File_Name;
72 -- Name of the temp file last created
74 Temp_File_Name_Last_Digit : constant Positive :=
75 First_Temp_File_Name'Last - 4;
76 -- Position of the last digit in Current_Temp_File_Name
78 Max_Attempts : constant := 100;
79 -- The maximum number of attempts to create a new temp file
81 -----------------------
82 -- Local Subprograms --
83 -----------------------
85 function Args_Length (Args : Argument_List) return Natural;
86 -- Returns total number of characters needed to create a string
87 -- of all Args terminated by ASCII.NUL characters
89 function C_String_Length (S : Address) return Integer;
90 -- Returns the length of a C string. Does check for null address
93 procedure Spawn_Internal
94 (Program_Name : String;
99 -- Internal routine to implement the two Spawn (blocking/non blocking)
100 -- routines. If Blocking is set to True then the spawn is blocking
101 -- otherwise it is non blocking. In this latter case the Pid contains the
102 -- process id number. The first three parameters are as in Spawn. Note that
103 -- Spawn_Internal normalizes the argument list before calling the low level
104 -- system spawn routines (see Normalize_Arguments).
106 -- Note: Normalize_Arguments is designed to do nothing if it is called more
107 -- than once, so calling Normalize_Arguments before calling one of the
108 -- spawn routines is fine.
110 function To_Path_String_Access
111 (Path_Addr : Address;
112 Path_Len : Integer) return String_Access;
113 -- Converts a C String to an Ada String. We could do this making use of
114 -- Interfaces.C.Strings but we prefer not to import that entire package
120 function "<" (X, Y : OS_Time) return Boolean is
122 return Long_Integer (X) < Long_Integer (Y);
129 function "<=" (X, Y : OS_Time) return Boolean is
131 return Long_Integer (X) <= Long_Integer (Y);
138 function ">" (X, Y : OS_Time) return Boolean is
140 return Long_Integer (X) > Long_Integer (Y);
147 function ">=" (X, Y : OS_Time) return Boolean is
149 return Long_Integer (X) >= Long_Integer (Y);
156 function Args_Length (Args : Argument_List) return Natural is
160 for J in Args'Range loop
161 Len := Len + Args (J)'Length + 1; -- One extra for ASCII.NUL
167 -----------------------------
168 -- Argument_String_To_List --
169 -----------------------------
171 function Argument_String_To_List
172 (Arg_String : String) return Argument_List_Access
174 Max_Args : constant Integer := Arg_String'Length;
175 New_Argv : Argument_List (1 .. Max_Args);
176 New_Argc : Natural := 0;
180 Idx := Arg_String'First;
183 exit when Idx > Arg_String'Last;
186 Quoted : Boolean := False;
187 Backqd : Boolean := False;
194 -- An unquoted space is the end of an argument
196 if not (Backqd or Quoted)
197 and then Arg_String (Idx) = ' '
201 -- Start of a quoted string
203 elsif not (Backqd or Quoted)
204 and then Arg_String (Idx) = '"'
208 -- End of a quoted string and end of an argument
210 elsif (Quoted and not Backqd)
211 and then Arg_String (Idx) = '"'
216 -- Following character is backquoted
218 elsif Arg_String (Idx) = '\' then
221 -- Turn off backquoting after advancing one character
229 exit when Idx > Arg_String'Last;
234 New_Argc := New_Argc + 1;
235 New_Argv (New_Argc) :=
236 new String'(Arg_String (Old_Idx .. Idx - 1));
238 -- Skip extraneous spaces
240 while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop
246 return new Argument_List'(New_Argv (1 .. New_Argc));
247 end Argument_String_To_List;
249 ---------------------
250 -- C_String_Length --
251 ---------------------
253 function C_String_Length (S : Address) return Integer is
254 function Strlen (S : Address) return Integer;
255 pragma Import (C, Strlen, "strlen");
257 if S = Null_Address then
268 procedure Close (FD : File_Descriptor) is
269 procedure C_Close (FD : File_Descriptor);
270 pragma Import (C, C_Close, "close");
275 procedure Close (FD : File_Descriptor; Status : out Boolean) is
276 function C_Close (FD : File_Descriptor) return Integer;
277 pragma Import (C, C_Close, "close");
279 Status := (C_Close (FD) = 0);
289 Success : out Boolean;
290 Mode : Copy_Mode := Copy;
291 Preserve : Attribute := Time_Stamps)
293 From : File_Descriptor;
294 To : File_Descriptor;
296 Copy_Error : exception;
297 -- Internal exception raised to signal error in copy
299 function Build_Path (Dir : String; File : String) return String;
300 -- Returns pathname Dir catenated with File adding the directory
301 -- separator only if needed.
303 procedure Copy (From, To : File_Descriptor);
304 -- Read data from From and place them into To. In both cases the
305 -- operations uses the current file position. Raises Constraint_Error
306 -- if a problem occurs during the copy.
308 procedure Copy_To (To_Name : String);
309 -- Does a straight copy from source to designated destination file
315 function Build_Path (Dir : String; File : String) return String is
316 Res : String (1 .. Dir'Length + File'Length + 1);
318 Base_File_Ptr : Integer;
319 -- The base file name is File (Base_File_Ptr + 1 .. File'Last)
321 function Is_Dirsep (C : Character) return Boolean;
322 pragma Inline (Is_Dirsep);
323 -- Returns True if C is a directory separator. On Windows we
324 -- handle both styles of directory separator.
330 function Is_Dirsep (C : Character) return Boolean is
332 return C = Directory_Separator or else C = '/';
335 -- Start of processing for Build_Path
338 -- Find base file name
340 Base_File_Ptr := File'Last;
341 while Base_File_Ptr >= File'First loop
342 exit when Is_Dirsep (File (Base_File_Ptr));
343 Base_File_Ptr := Base_File_Ptr - 1;
347 Base_File : String renames
348 File (Base_File_Ptr + 1 .. File'Last);
351 Res (1 .. Dir'Length) := Dir;
353 if Is_Dirsep (Dir (Dir'Last)) then
354 Res (Dir'Length + 1 .. Dir'Length + Base_File'Length) :=
356 return Res (1 .. Dir'Length + Base_File'Length);
359 Res (Dir'Length + 1) := Directory_Separator;
360 Res (Dir'Length + 2 .. Dir'Length + 1 + Base_File'Length) :=
362 return Res (1 .. Dir'Length + 1 + Base_File'Length);
371 procedure Copy (From, To : File_Descriptor) is
372 Buf_Size : constant := 200_000;
373 type Buf is array (1 .. Buf_Size) of Character;
374 type Buf_Ptr is access Buf;
380 Status_From : Boolean;
382 -- Statuses for the calls to Close
384 procedure Free is new Unchecked_Deallocation (Buf, Buf_Ptr);
387 if From = Invalid_FD or else To = Invalid_FD then
391 -- Allocate the buffer on the heap
396 R := Read (From, Buffer (1)'Address, Buf_Size);
398 -- For VMS, the buffer may not be full. So, we need to try again
399 -- until there is nothing to read.
403 W := Write (To, Buffer (1)'Address, R);
407 -- Problem writing data, could be a disk full. Close files
408 -- without worrying about status, since we are raising a
409 -- Copy_Error exception in any case.
411 Close (From, Status_From);
412 Close (To, Status_To);
420 Close (From, Status_From);
421 Close (To, Status_To);
425 if not (Status_From and Status_To) then
434 procedure Copy_To (To_Name : String) is
436 function Copy_Attributes
437 (From, To : System.Address;
438 Mode : Integer) return Integer;
439 pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
440 -- Mode = 0 - copy only time stamps.
441 -- Mode = 1 - copy time stamps and read/write/execute attributes
443 C_From : String (1 .. Name'Length + 1);
444 C_To : String (1 .. To_Name'Length + 1);
447 From := Open_Read (Name, Binary);
448 To := Create_File (To_Name, Binary);
453 C_From (1 .. Name'Length) := Name;
454 C_From (C_From'Last) := ASCII.Nul;
456 C_To (1 .. To_Name'Length) := To_Name;
457 C_To (C_To'Last) := ASCII.Nul;
462 if Copy_Attributes (C_From'Address, C_To'Address, 0) = -1 then
467 if Copy_Attributes (C_From'Address, C_To'Address, 1) = -1 then
477 -- Start of processing for Copy_File
482 -- The source file must exist
484 if not Is_Regular_File (Name) then
488 -- The source file exists
492 -- Copy case, target file must not exist
496 -- If the target file exists, we have an error
498 if Is_Regular_File (Pathname) then
501 -- Case of target is a directory
503 elsif Is_Directory (Pathname) then
505 Dest : constant String := Build_Path (Pathname, Name);
508 -- If target file exists, we have an error, else do copy
510 if Is_Regular_File (Dest) then
517 -- Case of normal copy to file (destination does not exist)
523 -- Overwrite case (destination file may or may not exist)
526 if Is_Directory (Pathname) then
527 Copy_To (Build_Path (Pathname, Name));
532 -- Append case (destination file may or may not exist)
536 -- Appending to existing file
538 if Is_Regular_File (Pathname) then
540 -- Append mode and destination file exists, append data at the
543 From := Open_Read (Name, Binary);
544 To := Open_Read_Write (Pathname, Binary);
545 Lseek (To, 0, Seek_End);
549 -- Appending to directory, not allowed
551 elsif Is_Directory (Pathname) then
554 -- Appending when target file does not exist
561 -- All error cases are caught here
570 Pathname : C_File_Name;
571 Success : out Boolean;
572 Mode : Copy_Mode := Copy;
573 Preserve : Attribute := Time_Stamps)
575 Ada_Name : String_Access :=
576 To_Path_String_Access
577 (Name, C_String_Length (Name));
579 Ada_Pathname : String_Access :=
580 To_Path_String_Access
581 (Pathname, C_String_Length (Pathname));
584 Copy_File (Ada_Name.all, Ada_Pathname.all, Success, Mode, Preserve);
589 ----------------------
590 -- Copy_Time_Stamps --
591 ----------------------
593 procedure Copy_Time_Stamps (Source, Dest : String; Success : out Boolean) is
595 function Copy_Attributes
596 (From, To : System.Address;
597 Mode : Integer) return Integer;
598 pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
599 -- Mode = 0 - copy only time stamps.
600 -- Mode = 1 - copy time stamps and read/write/execute attributes
603 if Is_Regular_File (Source) and then Is_Writable_File (Dest) then
605 C_Source : String (1 .. Source'Length + 1);
606 C_Dest : String (1 .. Dest'Length + 1);
608 C_Source (1 .. C_Source'Length) := Source;
609 C_Source (C_Source'Last) := ASCII.Nul;
611 C_Dest (1 .. C_Dest'Length) := Dest;
612 C_Dest (C_Dest'Last) := ASCII.Nul;
614 if Copy_Attributes (C_Source'Address, C_Dest'Address, 0) = -1 then
624 end Copy_Time_Stamps;
626 procedure Copy_Time_Stamps
627 (Source, Dest : C_File_Name;
628 Success : out Boolean)
630 Ada_Source : String_Access :=
631 To_Path_String_Access
632 (Source, C_String_Length (Source));
634 Ada_Dest : String_Access :=
635 To_Path_String_Access
636 (Dest, C_String_Length (Dest));
638 Copy_Time_Stamps (Ada_Source.all, Ada_Dest.all, Success);
641 end Copy_Time_Stamps;
649 Fmode : Mode) return File_Descriptor
651 function C_Create_File
653 Fmode : Mode) return File_Descriptor;
654 pragma Import (C, C_Create_File, "__gnat_open_create");
657 return C_Create_File (Name, Fmode);
662 Fmode : Mode) return File_Descriptor
664 C_Name : String (1 .. Name'Length + 1);
667 C_Name (1 .. Name'Length) := Name;
668 C_Name (C_Name'Last) := ASCII.NUL;
669 return Create_File (C_Name (C_Name'First)'Address, Fmode);
672 ---------------------
673 -- Create_New_File --
674 ---------------------
676 function Create_New_File
678 Fmode : Mode) return File_Descriptor
680 function C_Create_New_File
682 Fmode : Mode) return File_Descriptor;
683 pragma Import (C, C_Create_New_File, "__gnat_open_new");
686 return C_Create_New_File (Name, Fmode);
689 function Create_New_File
691 Fmode : Mode) return File_Descriptor
693 C_Name : String (1 .. Name'Length + 1);
696 C_Name (1 .. Name'Length) := Name;
697 C_Name (C_Name'Last) := ASCII.NUL;
698 return Create_New_File (C_Name (C_Name'First)'Address, Fmode);
701 -----------------------------
702 -- Create_Output_Text_File --
703 -----------------------------
705 function Create_Output_Text_File (Name : String) return File_Descriptor is
706 function C_Create_File
707 (Name : C_File_Name) return File_Descriptor;
708 pragma Import (C, C_Create_File, "__gnat_create_output_file");
710 C_Name : String (1 .. Name'Length + 1);
713 C_Name (1 .. Name'Length) := Name;
714 C_Name (C_Name'Last) := ASCII.NUL;
715 return C_Create_File (C_Name (C_Name'First)'Address);
716 end Create_Output_Text_File;
718 ----------------------
719 -- Create_Temp_File --
720 ----------------------
722 procedure Create_Temp_File
723 (FD : out File_Descriptor;
724 Name : out Temp_File_Name)
726 function Open_New_Temp
727 (Name : System.Address;
728 Fmode : Mode) return File_Descriptor;
729 pragma Import (C, Open_New_Temp, "__gnat_open_new_temp");
732 FD := Open_New_Temp (Name'Address, Binary);
733 end Create_Temp_File;
735 procedure Create_Temp_File
736 (FD : out File_Descriptor;
737 Name : out String_Access)
740 Attempts : Natural := 0;
741 Current : String (Current_Temp_File_Name'Range);
744 -- Loop until a new temp file can be created
748 -- We need to protect global variable Current_Temp_File_Name
749 -- against concurrent access by different tasks.
753 -- Start at the last digit
755 Pos := Temp_File_Name_Last_Digit;
759 -- Increment the digit by one
761 case Current_Temp_File_Name (Pos) is
763 Current_Temp_File_Name (Pos) :=
764 Character'Succ (Current_Temp_File_Name (Pos));
769 -- For 9, set the digit to 0 and go to the previous digit
771 Current_Temp_File_Name (Pos) := '0';
776 -- If it is not a digit, then there are no available
777 -- temp file names. Return Invalid_FD. There is almost
778 -- no that this code will be ever be executed, since
779 -- it would mean that there are one million temp files
780 -- in the same directory!
789 Current := Current_Temp_File_Name;
791 -- We can now release the lock, because we are no longer
792 -- accessing Current_Temp_File_Name.
802 -- Attempt to create the file
804 FD := Create_New_File (Current, Binary);
806 if FD /= Invalid_FD then
807 Name := new String'(Current);
811 if not Is_Regular_File (Current) then
813 -- If the file does not already exist and we are unable to create
814 -- it, we give up after Max_Attempts. Otherwise, we try again with
815 -- the next available file name.
817 Attempts := Attempts + 1;
819 if Attempts >= Max_Attempts then
826 end Create_Temp_File;
832 procedure Delete_File (Name : Address; Success : out Boolean) is
835 function unlink (A : Address) return Integer;
836 pragma Import (C, unlink, "unlink");
843 procedure Delete_File (Name : String; Success : out Boolean) is
844 C_Name : String (1 .. Name'Length + 1);
847 C_Name (1 .. Name'Length) := Name;
848 C_Name (C_Name'Last) := ASCII.NUL;
850 Delete_File (C_Name'Address, Success);
853 ---------------------
854 -- File_Time_Stamp --
855 ---------------------
857 function File_Time_Stamp (FD : File_Descriptor) return OS_Time is
858 function File_Time (FD : File_Descriptor) return OS_Time;
859 pragma Import (C, File_Time, "__gnat_file_time_fd");
861 return File_Time (FD);
864 function File_Time_Stamp (Name : C_File_Name) return OS_Time is
865 function File_Time (Name : Address) return OS_Time;
866 pragma Import (C, File_Time, "__gnat_file_time_name");
868 return File_Time (Name);
871 function File_Time_Stamp (Name : String) return OS_Time is
872 F_Name : String (1 .. Name'Length + 1);
874 F_Name (1 .. Name'Length) := Name;
875 F_Name (F_Name'Last) := ASCII.NUL;
876 return File_Time_Stamp (F_Name'Address);
879 ---------------------------
880 -- Get_Debuggable_Suffix --
881 ---------------------------
883 function Get_Debuggable_Suffix return String_Access is
884 procedure Get_Suffix_Ptr (Length, Ptr : Address);
885 pragma Import (C, Get_Suffix_Ptr, "__gnat_get_debuggable_suffix_ptr");
887 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
888 pragma Import (C, Strncpy, "strncpy");
890 Suffix_Ptr : Address;
891 Suffix_Length : Integer;
892 Result : String_Access;
895 Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
897 Result := new String (1 .. Suffix_Length);
899 if Suffix_Length > 0 then
900 Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
904 end Get_Debuggable_Suffix;
906 ---------------------------
907 -- Get_Executable_Suffix --
908 ---------------------------
910 function Get_Executable_Suffix return String_Access is
911 procedure Get_Suffix_Ptr (Length, Ptr : Address);
912 pragma Import (C, Get_Suffix_Ptr, "__gnat_get_executable_suffix_ptr");
914 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
915 pragma Import (C, Strncpy, "strncpy");
917 Suffix_Ptr : Address;
918 Suffix_Length : Integer;
919 Result : String_Access;
922 Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
924 Result := new String (1 .. Suffix_Length);
926 if Suffix_Length > 0 then
927 Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
931 end Get_Executable_Suffix;
933 -----------------------
934 -- Get_Object_Suffix --
935 -----------------------
937 function Get_Object_Suffix return String_Access is
938 procedure Get_Suffix_Ptr (Length, Ptr : Address);
939 pragma Import (C, Get_Suffix_Ptr, "__gnat_get_object_suffix_ptr");
941 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
942 pragma Import (C, Strncpy, "strncpy");
944 Suffix_Ptr : Address;
945 Suffix_Length : Integer;
946 Result : String_Access;
949 Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
951 Result := new String (1 .. Suffix_Length);
953 if Suffix_Length > 0 then
954 Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
958 end Get_Object_Suffix;
964 function Getenv (Name : String) return String_Access is
965 procedure Get_Env_Value_Ptr (Name, Length, Ptr : Address);
966 pragma Import (C, Get_Env_Value_Ptr, "__gnat_get_env_value_ptr");
968 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
969 pragma Import (C, Strncpy, "strncpy");
971 Env_Value_Ptr : aliased Address;
972 Env_Value_Length : aliased Integer;
973 F_Name : aliased String (1 .. Name'Length + 1);
974 Result : String_Access;
977 F_Name (1 .. Name'Length) := Name;
978 F_Name (F_Name'Last) := ASCII.NUL;
981 (F_Name'Address, Env_Value_Length'Address, Env_Value_Ptr'Address);
983 Result := new String (1 .. Env_Value_Length);
985 if Env_Value_Length > 0 then
986 Strncpy (Result.all'Address, Env_Value_Ptr, Env_Value_Length);
996 function GM_Day (Date : OS_Time) return Day_Type is
1005 GM_Split (Date, Y, Mo, D, H, Mn, S);
1013 function GM_Hour (Date : OS_Time) return Hour_Type is
1022 GM_Split (Date, Y, Mo, D, H, Mn, S);
1030 function GM_Minute (Date : OS_Time) return Minute_Type is
1039 GM_Split (Date, Y, Mo, D, H, Mn, S);
1047 function GM_Month (Date : OS_Time) return Month_Type is
1056 GM_Split (Date, Y, Mo, D, H, Mn, S);
1064 function GM_Second (Date : OS_Time) return Second_Type is
1073 GM_Split (Date, Y, Mo, D, H, Mn, S);
1083 Year : out Year_Type;
1084 Month : out Month_Type;
1086 Hour : out Hour_Type;
1087 Minute : out Minute_Type;
1088 Second : out Second_Type)
1090 procedure To_GM_Time
1091 (P_Time_T, P_Year, P_Month, P_Day, P_Hours, P_Mins, P_Secs : Address);
1092 pragma Import (C, To_GM_Time, "__gnat_to_gm_time");
1094 T : OS_Time := Date;
1103 -- Use the global lock because To_GM_Time is not thread safe
1105 Locked_Processing : begin
1108 (T'Address, Y'Address, Mo'Address, D'Address,
1109 H'Address, Mn'Address, S'Address);
1110 SSL.Unlock_Task.all;
1114 SSL.Unlock_Task.all;
1116 end Locked_Processing;
1130 function GM_Year (Date : OS_Time) return Year_Type is
1139 GM_Split (Date, Y, Mo, D, H, Mn, S);
1143 ----------------------
1144 -- Is_Absolute_Path --
1145 ----------------------
1147 function Is_Absolute_Path (Name : String) return Boolean is
1148 function Is_Absolute_Path
1150 Length : Integer) return Integer;
1151 pragma Import (C, Is_Absolute_Path, "__gnat_is_absolute_path");
1153 return Is_Absolute_Path (Name'Address, Name'Length) /= 0;
1154 end Is_Absolute_Path;
1160 function Is_Directory (Name : C_File_Name) return Boolean is
1161 function Is_Directory (Name : Address) return Integer;
1162 pragma Import (C, Is_Directory, "__gnat_is_directory");
1164 return Is_Directory (Name) /= 0;
1167 function Is_Directory (Name : String) return Boolean is
1168 F_Name : String (1 .. Name'Length + 1);
1170 F_Name (1 .. Name'Length) := Name;
1171 F_Name (F_Name'Last) := ASCII.NUL;
1172 return Is_Directory (F_Name'Address);
1175 ---------------------
1176 -- Is_Regular_File --
1177 ---------------------
1179 function Is_Regular_File (Name : C_File_Name) return Boolean is
1180 function Is_Regular_File (Name : Address) return Integer;
1181 pragma Import (C, Is_Regular_File, "__gnat_is_regular_file");
1183 return Is_Regular_File (Name) /= 0;
1184 end Is_Regular_File;
1186 function Is_Regular_File (Name : String) return Boolean is
1187 F_Name : String (1 .. Name'Length + 1);
1189 F_Name (1 .. Name'Length) := Name;
1190 F_Name (F_Name'Last) := ASCII.NUL;
1191 return Is_Regular_File (F_Name'Address);
1192 end Is_Regular_File;
1194 ----------------------
1195 -- Is_Readable_File --
1196 ----------------------
1198 function Is_Readable_File (Name : C_File_Name) return Boolean is
1199 function Is_Readable_File (Name : Address) return Integer;
1200 pragma Import (C, Is_Readable_File, "__gnat_is_readable_file");
1202 return Is_Readable_File (Name) /= 0;
1203 end Is_Readable_File;
1205 function Is_Readable_File (Name : String) return Boolean is
1206 F_Name : String (1 .. Name'Length + 1);
1208 F_Name (1 .. Name'Length) := Name;
1209 F_Name (F_Name'Last) := ASCII.NUL;
1210 return Is_Readable_File (F_Name'Address);
1211 end Is_Readable_File;
1213 ----------------------
1214 -- Is_Writable_File --
1215 ----------------------
1217 function Is_Writable_File (Name : C_File_Name) return Boolean is
1218 function Is_Writable_File (Name : Address) return Integer;
1219 pragma Import (C, Is_Writable_File, "__gnat_is_writable_file");
1221 return Is_Writable_File (Name) /= 0;
1222 end Is_Writable_File;
1224 function Is_Writable_File (Name : String) return Boolean is
1225 F_Name : String (1 .. Name'Length + 1);
1227 F_Name (1 .. Name'Length) := Name;
1228 F_Name (F_Name'Last) := ASCII.NUL;
1229 return Is_Writable_File (F_Name'Address);
1230 end Is_Writable_File;
1232 ----------------------
1233 -- Is_Symbolic_Link --
1234 ----------------------
1236 function Is_Symbolic_Link (Name : C_File_Name) return Boolean is
1237 function Is_Symbolic_Link (Name : Address) return Integer;
1238 pragma Import (C, Is_Symbolic_Link, "__gnat_is_symbolic_link");
1240 return Is_Symbolic_Link (Name) /= 0;
1241 end Is_Symbolic_Link;
1243 function Is_Symbolic_Link (Name : String) return Boolean is
1244 F_Name : String (1 .. Name'Length + 1);
1246 F_Name (1 .. Name'Length) := Name;
1247 F_Name (F_Name'Last) := ASCII.NUL;
1248 return Is_Symbolic_Link (F_Name'Address);
1249 end Is_Symbolic_Link;
1251 -------------------------
1252 -- Locate_Exec_On_Path --
1253 -------------------------
1255 function Locate_Exec_On_Path
1256 (Exec_Name : String) return String_Access
1258 function Locate_Exec_On_Path (C_Exec_Name : Address) return Address;
1259 pragma Import (C, Locate_Exec_On_Path, "__gnat_locate_exec_on_path");
1261 procedure Free (Ptr : System.Address);
1262 pragma Import (C, Free, "free");
1264 C_Exec_Name : String (1 .. Exec_Name'Length + 1);
1265 Path_Addr : Address;
1267 Result : String_Access;
1270 C_Exec_Name (1 .. Exec_Name'Length) := Exec_Name;
1271 C_Exec_Name (C_Exec_Name'Last) := ASCII.NUL;
1273 Path_Addr := Locate_Exec_On_Path (C_Exec_Name'Address);
1274 Path_Len := C_String_Length (Path_Addr);
1276 if Path_Len = 0 then
1280 Result := To_Path_String_Access (Path_Addr, Path_Len);
1284 end Locate_Exec_On_Path;
1286 -------------------------
1287 -- Locate_Regular_File --
1288 -------------------------
1290 function Locate_Regular_File
1291 (File_Name : C_File_Name;
1292 Path : C_File_Name) return String_Access
1294 function Locate_Regular_File
1295 (C_File_Name, Path_Val : Address) return Address;
1296 pragma Import (C, Locate_Regular_File, "__gnat_locate_regular_file");
1298 procedure Free (Ptr : System.Address);
1299 pragma Import (C, Free, "free");
1301 Path_Addr : Address;
1303 Result : String_Access;
1306 Path_Addr := Locate_Regular_File (File_Name, Path);
1307 Path_Len := C_String_Length (Path_Addr);
1309 if Path_Len = 0 then
1312 Result := To_Path_String_Access (Path_Addr, Path_Len);
1316 end Locate_Regular_File;
1318 function Locate_Regular_File
1319 (File_Name : String;
1320 Path : String) return String_Access
1322 C_File_Name : String (1 .. File_Name'Length + 1);
1323 C_Path : String (1 .. Path'Length + 1);
1326 C_File_Name (1 .. File_Name'Length) := File_Name;
1327 C_File_Name (C_File_Name'Last) := ASCII.NUL;
1329 C_Path (1 .. Path'Length) := Path;
1330 C_Path (C_Path'Last) := ASCII.NUL;
1332 return Locate_Regular_File (C_File_Name'Address, C_Path'Address);
1333 end Locate_Regular_File;
1335 ------------------------
1336 -- Non_Blocking_Spawn --
1337 ------------------------
1339 function Non_Blocking_Spawn
1340 (Program_Name : String;
1341 Args : Argument_List) return Process_Id
1347 Spawn_Internal (Program_Name, Args, Junk, Pid, Blocking => False);
1349 end Non_Blocking_Spawn;
1351 function Non_Blocking_Spawn
1352 (Program_Name : String;
1353 Args : Argument_List;
1354 Output_File_Descriptor : File_Descriptor;
1355 Err_To_Out : Boolean := True)
1358 Saved_Output : File_Descriptor;
1359 Saved_Error : File_Descriptor := Invalid_FD; -- prevent warning
1362 if Output_File_Descriptor = Invalid_FD then
1366 -- Set standard output and, if specified, error to the temporary file
1368 Saved_Output := Dup (Standout);
1369 Dup2 (Output_File_Descriptor, Standout);
1372 Saved_Error := Dup (Standerr);
1373 Dup2 (Output_File_Descriptor, Standerr);
1376 -- Spawn the program
1378 Pid := Non_Blocking_Spawn (Program_Name, Args);
1380 -- Restore the standard output and error
1382 Dup2 (Saved_Output, Standout);
1385 Dup2 (Saved_Error, Standerr);
1388 -- And close the saved standard output and error file descriptors
1390 Close (Saved_Output);
1393 Close (Saved_Error);
1397 end Non_Blocking_Spawn;
1399 function Non_Blocking_Spawn
1400 (Program_Name : String;
1401 Args : Argument_List;
1402 Output_File : String;
1403 Err_To_Out : Boolean := True) return Process_Id
1405 Output_File_Descriptor : constant File_Descriptor :=
1406 Create_Output_Text_File (Output_File);
1407 Result : Process_Id;
1410 -- Do not attempt to spawn if the output file could not be created
1412 if Output_File_Descriptor = Invalid_FD then
1416 Result := Non_Blocking_Spawn
1417 (Program_Name, Args, Output_File_Descriptor, Err_To_Out);
1419 -- Close the file just created for the output, as the file descriptor
1420 -- cannot be used anywhere, being a local value. It is safe to do
1421 -- that, as the file descriptor has been duplicated to form
1422 -- standard output and error of the spawned process.
1424 Close (Output_File_Descriptor);
1428 end Non_Blocking_Spawn;
1430 -------------------------
1431 -- Normalize_Arguments --
1432 -------------------------
1434 procedure Normalize_Arguments (Args : in out Argument_List) is
1436 procedure Quote_Argument (Arg : in out String_Access);
1437 -- Add quote around argument if it contains spaces
1439 C_Argument_Needs_Quote : Integer;
1440 pragma Import (C, C_Argument_Needs_Quote, "__gnat_argument_needs_quote");
1441 Argument_Needs_Quote : constant Boolean := C_Argument_Needs_Quote /= 0;
1443 --------------------
1444 -- Quote_Argument --
1445 --------------------
1447 procedure Quote_Argument (Arg : in out String_Access) is
1448 Res : String (1 .. Arg'Length * 2);
1450 Quote_Needed : Boolean := False;
1453 if Arg (Arg'First) /= '"' or else Arg (Arg'Last) /= '"' then
1459 for K in Arg'Range loop
1463 if Arg (K) = '"' then
1467 Quote_Needed := True;
1469 elsif Arg (K) = ' ' then
1471 Quote_Needed := True;
1479 if Quote_Needed then
1481 -- If null terminated string, put the quote before
1483 if Res (J) = ASCII.Nul then
1486 Res (J) := ASCII.Nul;
1488 -- If argument is terminated by '\', then double it. Otherwise
1489 -- the ending quote will be taken as-is. This is quite strange
1490 -- spawn behavior from Windows, but this is what we see!
1493 if Res (J) = '\' then
1505 Old : String_Access := Arg;
1508 Arg := new String'(Res (1 .. J));
1516 -- Start of processing for Normalize_Arguments
1519 if Argument_Needs_Quote then
1520 for K in Args'Range loop
1521 if Args (K) /= null and then Args (K)'Length /= 0 then
1522 Quote_Argument (Args (K));
1526 end Normalize_Arguments;
1528 ------------------------
1529 -- Normalize_Pathname --
1530 ------------------------
1532 function Normalize_Pathname
1534 Directory : String := "";
1535 Resolve_Links : Boolean := True;
1536 Case_Sensitive : Boolean := True) return String
1539 pragma Import (C, Max_Path, "__gnat_max_path_len");
1540 -- Maximum length of a path name
1542 procedure Get_Current_Dir
1543 (Dir : System.Address;
1544 Length : System.Address);
1545 pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir");
1547 function Change_Dir (Dir_Name : String) return Integer;
1548 pragma Import (C, Change_Dir, "chdir");
1550 Path_Buffer : String (1 .. Max_Path + Max_Path + 2);
1551 End_Path : Natural := 0;
1552 Link_Buffer : String (1 .. Max_Path + 2);
1558 Max_Iterations : constant := 500;
1560 function Get_File_Names_Case_Sensitive return Integer;
1562 (C, Get_File_Names_Case_Sensitive,
1563 "__gnat_get_file_names_case_sensitive");
1565 Fold_To_Lower_Case : constant Boolean :=
1567 and then Get_File_Names_Case_Sensitive = 0;
1570 (Path : System.Address;
1571 Buf : System.Address;
1572 Bufsiz : Integer) return Integer;
1573 pragma Import (C, Readlink, "__gnat_readlink");
1575 function To_Canonical_File_Spec
1576 (Host_File : System.Address) return System.Address;
1578 (C, To_Canonical_File_Spec, "__gnat_to_canonical_file_spec");
1580 The_Name : String (1 .. Name'Length + 1);
1581 Canonical_File_Addr : System.Address;
1582 Canonical_File_Len : Integer;
1584 Need_To_Check_Drive_Letter : Boolean := False;
1585 -- Set to true if Name is an absolute path that starts with "//"
1587 function Strlen (S : System.Address) return Integer;
1588 pragma Import (C, Strlen, "strlen");
1590 function Get_Directory (Dir : String) return String;
1591 -- If Dir is not empty, return it, adding a directory separator
1592 -- if not already present, otherwise return current working directory
1593 -- with terminating directory separator.
1595 function Final_Value (S : String) return String;
1596 -- Make final adjustment to the returned string.
1597 -- To compensate for non standard path name in Interix,
1598 -- if S is "/x" or starts with "/x", where x is a capital
1599 -- letter 'A' to 'Z', add an additional '/' at the beginning
1600 -- so that the returned value starts with "//x".
1606 function Get_Directory (Dir : String) return String is
1608 -- Directory given, add directory separator if needed
1610 if Dir'Length > 0 then
1611 if Dir (Dir'Length) = Directory_Separator then
1615 Result : String (1 .. Dir'Length + 1);
1618 Result (1 .. Dir'Length) := Dir;
1619 Result (Result'Length) := Directory_Separator;
1624 -- Directory name not given, get current directory
1628 Buffer : String (1 .. Max_Path + 2);
1629 Path_Len : Natural := Max_Path;
1632 Get_Current_Dir (Buffer'Address, Path_Len'Address);
1634 if Buffer (Path_Len) /= Directory_Separator then
1635 Path_Len := Path_Len + 1;
1636 Buffer (Path_Len) := Directory_Separator;
1639 -- By default, the drive letter on Windows is in upper case
1641 if On_Windows and then Path_Len >= 2 and then
1644 System.Case_Util.To_Upper (Buffer (1 .. 1));
1647 return Buffer (1 .. Path_Len);
1652 Reference_Dir : constant String := Get_Directory (Directory);
1653 -- Current directory name specified
1659 function Final_Value (S : String) return String is
1661 -- We may need to fold S to lower case, so we need a variable
1666 -- Interix has the non standard notion of disk drive
1667 -- indicated by two '/' followed by a capital letter
1668 -- 'A' .. 'Z'. One of the two '/' may have been removed
1669 -- by Normalize_Pathname. It has to be added again.
1670 -- For other OSes, this should not make no difference.
1672 if Need_To_Check_Drive_Letter
1673 and then S'Length >= 2
1674 and then S (S'First) = '/'
1675 and then S (S'First + 1) in 'A' .. 'Z'
1676 and then (S'Length = 2 or else S (S'First + 2) = '/')
1679 Result : String (1 .. S'Length + 1);
1683 Result (2 .. Result'Last) := S;
1684 Last := Result'Last;
1686 if Fold_To_Lower_Case then
1687 System.Case_Util.To_Lower (Result);
1690 -- Remove trailing directory separator, if any
1692 if Last > 1 and then
1693 (Result (Last) = '/' or else
1694 Result (Last) = Directory_Separator)
1699 return Result (1 .. Last);
1703 if Fold_To_Lower_Case then
1704 System.Case_Util.To_Lower (S1);
1707 -- Remove trailing directory separator, if any
1712 and then (S1 (Last) = '/'
1714 S1 (Last) = Directory_Separator)
1716 -- Special case for Windows: C:\
1719 and then S1 (1) /= Directory_Separator
1720 and then S1 (2) = ':'
1729 return S1 (1 .. Last);
1733 -- Start of processing for Normalize_Pathname
1736 -- Special case, if name is null, then return null
1738 if Name'Length = 0 then
1742 -- First, convert VMS file spec to Unix file spec.
1743 -- If Name is not in VMS syntax, then this is equivalent
1744 -- to put Name at the begining of Path_Buffer.
1746 VMS_Conversion : begin
1747 The_Name (1 .. Name'Length) := Name;
1748 The_Name (The_Name'Last) := ASCII.NUL;
1750 Canonical_File_Addr := To_Canonical_File_Spec (The_Name'Address);
1751 Canonical_File_Len := Strlen (Canonical_File_Addr);
1753 -- If VMS syntax conversion has failed, return an empty string
1754 -- to indicate the failure.
1756 if Canonical_File_Len = 0 then
1761 subtype Path_String is String (1 .. Canonical_File_Len);
1762 type Path_String_Access is access Path_String;
1764 function Address_To_Access is new
1765 Unchecked_Conversion (Source => Address,
1766 Target => Path_String_Access);
1768 Path_Access : constant Path_String_Access :=
1769 Address_To_Access (Canonical_File_Addr);
1772 Path_Buffer (1 .. Canonical_File_Len) := Path_Access.all;
1773 End_Path := Canonical_File_Len;
1778 -- Replace all '/' by Directory Separators (this is for Windows)
1780 if Directory_Separator /= '/' then
1781 for Index in 1 .. End_Path loop
1782 if Path_Buffer (Index) = '/' then
1783 Path_Buffer (Index) := Directory_Separator;
1788 -- Resolve directory names for VMS and Windows
1790 -- On VMS, if we have a Unix path such as /temp/..., and TEMP is a
1791 -- logical name, we need to resolve this logical name.
1793 -- On Windows, if we have an absolute path starting with a directory
1794 -- separator, we need to have the drive letter appended in front.
1796 -- For both platforms, Get_Current_Dir will return a suitable
1797 -- directory name (logical names resolved on VMS, path starting with
1798 -- a drive letter on Windows). So we find the directory, change to it,
1799 -- call Get_Current_Dir and change the directory to the returned value.
1800 -- Then, of course, we return to the previous directory.
1802 if (OpenVMS or On_Windows)
1803 and then Path_Buffer (1) = Directory_Separator
1806 Cur_Dir : String := Get_Directory ("");
1807 -- Save the current directory, so that we can change dir back to
1808 -- it. It is not a constant, because the last character (a
1809 -- directory separator) is changed to ASCII.NUL to call the C
1812 Path : String := Path_Buffer (1 .. End_Path + 1);
1813 -- Copy of the current path. One character is added that may be
1814 -- set to ASCII.NUL to call chdir.
1816 Pos : Positive := End_Path;
1817 -- Position of the last directory separator
1820 -- Value returned by chdir
1823 -- Look for the last directory separator
1825 while Path (Pos) /= Directory_Separator loop
1829 -- Get the previous character that is not a directory separator
1831 while Pos > 1 and then Path (Pos) = Directory_Separator loop
1835 -- If we are at the start of the path, take the full path.
1836 -- It may be a file in the root directory, but it may also be
1837 -- a subdirectory of the root directory.
1843 -- Add the ASCII.NUL to be able to call the C function chdir
1845 Path (Pos + 1) := ASCII.NUL;
1847 Status := Change_Dir (Path (1 .. Pos + 1));
1849 -- If Status is not zero, then we do nothing: this is a file
1850 -- path or it is not a valid directory path.
1854 New_Dir : constant String := Get_Directory ("");
1855 -- The directory path
1857 New_Path : String (1 .. New_Dir'Length + End_Path - Pos);
1858 -- The new complete path, that is built below
1861 New_Path (1 .. New_Dir'Length) := New_Dir;
1862 New_Path (New_Dir'Length + 1 .. New_Path'Last) :=
1863 Path_Buffer (Pos + 1 .. End_Path);
1864 End_Path := New_Path'Length;
1865 Path_Buffer (1 .. End_Path) := New_Path;
1868 -- Back to where we were before
1870 Cur_Dir (Cur_Dir'Last) := ASCII.NUL;
1871 Status := Change_Dir (Cur_Dir);
1876 -- Start the conversions
1878 -- If this is not finished after Max_Iterations, give up and return an
1881 for J in 1 .. Max_Iterations loop
1883 -- If we don't have an absolute pathname, prepend the directory
1887 and then not Is_Absolute_Path (Path_Buffer (1 .. End_Path))
1890 (Reference_Dir'Last + 1 .. Reference_Dir'Length + End_Path) :=
1891 Path_Buffer (1 .. End_Path);
1892 End_Path := Reference_Dir'Length + End_Path;
1893 Path_Buffer (1 .. Reference_Dir'Length) := Reference_Dir;
1894 Last := Reference_Dir'Length;
1897 -- If name starts with "//", we may have a drive letter on Interix
1899 if Last = 1 and then End_Path >= 3 then
1900 Need_To_Check_Drive_Letter := (Path_Buffer (1 .. 2)) = "//";
1906 -- Ensure that Windows network drives are kept, e.g: \\server\drive-c
1909 and then Directory_Separator = '\'
1910 and then Path_Buffer (1 .. 2) = "\\"
1915 -- If we have traversed the full pathname, return it
1917 if Start > End_Path then
1918 return Final_Value (Path_Buffer (1 .. End_Path));
1921 -- Remove duplicate directory separators
1923 while Path_Buffer (Start) = Directory_Separator loop
1924 if Start = End_Path then
1925 return Final_Value (Path_Buffer (1 .. End_Path - 1));
1928 Path_Buffer (Start .. End_Path - 1) :=
1929 Path_Buffer (Start + 1 .. End_Path);
1930 End_Path := End_Path - 1;
1934 -- Find the end of the current field: last character or the one
1935 -- preceding the next directory separator.
1937 while Finish < End_Path
1938 and then Path_Buffer (Finish + 1) /= Directory_Separator
1940 Finish := Finish + 1;
1945 if Start = Finish and then Path_Buffer (Start) = '.' then
1946 if Start = End_Path then
1948 return (1 => Directory_Separator);
1951 if Fold_To_Lower_Case then
1952 System.Case_Util.To_Lower (Path_Buffer (1 .. Last - 1));
1955 return Path_Buffer (1 .. Last - 1);
1960 Path_Buffer (Last + 1 .. End_Path - 2) :=
1961 Path_Buffer (Last + 3 .. End_Path);
1962 End_Path := End_Path - 2;
1965 -- Remove ".." fields
1967 elsif Finish = Start + 1
1968 and then Path_Buffer (Start .. Finish) = ".."
1973 exit when Start < 1 or else
1974 Path_Buffer (Start) = Directory_Separator;
1978 if Finish = End_Path then
1979 return (1 => Directory_Separator);
1982 Path_Buffer (1 .. End_Path - Finish) :=
1983 Path_Buffer (Finish + 1 .. End_Path);
1984 End_Path := End_Path - Finish;
1989 if Finish = End_Path then
1990 return Final_Value (Path_Buffer (1 .. Start - 1));
1993 Path_Buffer (Start + 1 .. Start + End_Path - Finish - 1) :=
1994 Path_Buffer (Finish + 2 .. End_Path);
1995 End_Path := Start + End_Path - Finish - 1;
2000 -- Check if current field is a symbolic link
2002 elsif Resolve_Links then
2004 Saved : constant Character := Path_Buffer (Finish + 1);
2007 Path_Buffer (Finish + 1) := ASCII.NUL;
2008 Status := Readlink (Path_Buffer'Address,
2009 Link_Buffer'Address,
2010 Link_Buffer'Length);
2011 Path_Buffer (Finish + 1) := Saved;
2014 -- Not a symbolic link, move to the next field, if any
2019 -- Replace symbolic link with its value
2022 if Is_Absolute_Path (Link_Buffer (1 .. Status)) then
2023 Path_Buffer (Status + 1 .. End_Path - (Finish - Status)) :=
2024 Path_Buffer (Finish + 1 .. End_Path);
2025 End_Path := End_Path - (Finish - Status);
2026 Path_Buffer (1 .. Status) := Link_Buffer (1 .. Status);
2031 (Last + Status + 1 .. End_Path - Finish + Last + Status) :=
2032 Path_Buffer (Finish + 1 .. End_Path);
2033 End_Path := End_Path - Finish + Last + Status;
2034 Path_Buffer (Last + 1 .. Last + Status) :=
2035 Link_Buffer (1 .. Status);
2044 -- Too many iterations: give up
2046 -- This can happen when there is a circularity in the symbolic links: A
2047 -- is a symbolic link for B, which itself is a symbolic link, and the
2048 -- target of B or of another symbolic link target of B is A. In this
2049 -- case, we return an empty string to indicate failure to resolve.
2052 end Normalize_Pathname;
2059 (Name : C_File_Name;
2060 Fmode : Mode) return File_Descriptor
2062 function C_Open_Read
2063 (Name : C_File_Name;
2064 Fmode : Mode) return File_Descriptor;
2065 pragma Import (C, C_Open_Read, "__gnat_open_read");
2067 return C_Open_Read (Name, Fmode);
2072 Fmode : Mode) return File_Descriptor
2074 C_Name : String (1 .. Name'Length + 1);
2076 C_Name (1 .. Name'Length) := Name;
2077 C_Name (C_Name'Last) := ASCII.NUL;
2078 return Open_Read (C_Name (C_Name'First)'Address, Fmode);
2081 ---------------------
2082 -- Open_Read_Write --
2083 ---------------------
2085 function Open_Read_Write
2086 (Name : C_File_Name;
2087 Fmode : Mode) return File_Descriptor
2089 function C_Open_Read_Write
2090 (Name : C_File_Name;
2091 Fmode : Mode) return File_Descriptor;
2092 pragma Import (C, C_Open_Read_Write, "__gnat_open_rw");
2094 return C_Open_Read_Write (Name, Fmode);
2095 end Open_Read_Write;
2097 function Open_Read_Write
2099 Fmode : Mode) return File_Descriptor
2101 C_Name : String (1 .. Name'Length + 1);
2103 C_Name (1 .. Name'Length) := Name;
2104 C_Name (C_Name'Last) := ASCII.NUL;
2105 return Open_Read_Write (C_Name (C_Name'First)'Address, Fmode);
2106 end Open_Read_Write;
2113 (FD : File_Descriptor;
2115 N : Integer) return Integer
2118 return Integer (System.CRTL.read
2119 (System.CRTL.int (FD), System.CRTL.chars (A), System.CRTL.int (N)));
2126 procedure Rename_File
2127 (Old_Name : C_File_Name;
2128 New_Name : C_File_Name;
2129 Success : out Boolean)
2131 function rename (From, To : Address) return Integer;
2132 pragma Import (C, rename, "rename");
2135 R := rename (Old_Name, New_Name);
2139 procedure Rename_File
2142 Success : out Boolean)
2144 C_Old_Name : String (1 .. Old_Name'Length + 1);
2145 C_New_Name : String (1 .. New_Name'Length + 1);
2147 C_Old_Name (1 .. Old_Name'Length) := Old_Name;
2148 C_Old_Name (C_Old_Name'Last) := ASCII.NUL;
2149 C_New_Name (1 .. New_Name'Length) := New_Name;
2150 C_New_Name (C_New_Name'Last) := ASCII.NUL;
2151 Rename_File (C_Old_Name'Address, C_New_Name'Address, Success);
2154 -----------------------
2155 -- Set_Close_On_Exec --
2156 -----------------------
2158 procedure Set_Close_On_Exec
2159 (FD : File_Descriptor;
2160 Close_On_Exec : Boolean;
2161 Status : out Boolean)
2163 function C_Set_Close_On_Exec
2164 (FD : File_Descriptor; Close_On_Exec : System.CRTL.int)
2165 return System.CRTL.int;
2166 pragma Import (C, C_Set_Close_On_Exec, "__gnat_set_close_on_exec");
2168 Status := C_Set_Close_On_Exec (FD, Boolean'Pos (Close_On_Exec)) = 0;
2169 end Set_Close_On_Exec;
2171 --------------------
2172 -- Set_Executable --
2173 --------------------
2175 procedure Set_Executable (Name : String) is
2176 procedure C_Set_Executable (Name : C_File_Name);
2177 pragma Import (C, C_Set_Executable, "__gnat_set_executable");
2178 C_Name : aliased String (Name'First .. Name'Last + 1);
2180 C_Name (Name'Range) := Name;
2181 C_Name (C_Name'Last) := ASCII.NUL;
2182 C_Set_Executable (C_Name (C_Name'First)'Address);
2185 --------------------
2187 --------------------
2189 procedure Set_Read_Only (Name : String) is
2190 procedure C_Set_Read_Only (Name : C_File_Name);
2191 pragma Import (C, C_Set_Read_Only, "__gnat_set_readonly");
2192 C_Name : aliased String (Name'First .. Name'Last + 1);
2194 C_Name (Name'Range) := Name;
2195 C_Name (C_Name'Last) := ASCII.NUL;
2196 C_Set_Read_Only (C_Name (C_Name'First)'Address);
2199 --------------------
2201 --------------------
2203 procedure Set_Writable (Name : String) is
2204 procedure C_Set_Writable (Name : C_File_Name);
2205 pragma Import (C, C_Set_Writable, "__gnat_set_writable");
2206 C_Name : aliased String (Name'First .. Name'Last + 1);
2208 C_Name (Name'Range) := Name;
2209 C_Name (C_Name'Last) := ASCII.NUL;
2210 C_Set_Writable (C_Name (C_Name'First)'Address);
2217 procedure Setenv (Name : String; Value : String) is
2218 F_Name : String (1 .. Name'Length + 1);
2219 F_Value : String (1 .. Value'Length + 1);
2221 procedure Set_Env_Value (Name, Value : System.Address);
2222 pragma Import (C, Set_Env_Value, "__gnat_set_env_value");
2225 F_Name (1 .. Name'Length) := Name;
2226 F_Name (F_Name'Last) := ASCII.NUL;
2228 F_Value (1 .. Value'Length) := Value;
2229 F_Value (F_Value'Last) := ASCII.NUL;
2231 Set_Env_Value (F_Name'Address, F_Value'Address);
2239 (Program_Name : String;
2240 Args : Argument_List) return Integer
2245 Spawn_Internal (Program_Name, Args, Result, Junk, Blocking => True);
2250 (Program_Name : String;
2251 Args : Argument_List;
2252 Success : out Boolean)
2255 Success := (Spawn (Program_Name, Args) = 0);
2259 (Program_Name : String;
2260 Args : Argument_List;
2261 Output_File_Descriptor : File_Descriptor;
2262 Return_Code : out Integer;
2263 Err_To_Out : Boolean := True)
2265 Saved_Output : File_Descriptor;
2266 Saved_Error : File_Descriptor := Invalid_FD; -- prevent compiler warning
2269 -- Set standard output and error to the temporary file
2271 Saved_Output := Dup (Standout);
2272 Dup2 (Output_File_Descriptor, Standout);
2275 Saved_Error := Dup (Standerr);
2276 Dup2 (Output_File_Descriptor, Standerr);
2279 -- Spawn the program
2281 Return_Code := Spawn (Program_Name, Args);
2283 -- Restore the standard output and error
2285 Dup2 (Saved_Output, Standout);
2288 Dup2 (Saved_Error, Standerr);
2291 -- And close the saved standard output and error file descriptors
2293 Close (Saved_Output);
2296 Close (Saved_Error);
2301 (Program_Name : String;
2302 Args : Argument_List;
2303 Output_File : String;
2304 Success : out Boolean;
2305 Return_Code : out Integer;
2306 Err_To_Out : Boolean := True)
2308 FD : File_Descriptor;
2314 FD := Create_Output_Text_File (Output_File);
2316 if FD = Invalid_FD then
2321 Spawn (Program_Name, Args, FD, Return_Code, Err_To_Out);
2323 Close (FD, Success);
2326 --------------------
2327 -- Spawn_Internal --
2328 --------------------
2330 procedure Spawn_Internal
2331 (Program_Name : String;
2332 Args : Argument_List;
2333 Result : out Integer;
2334 Pid : out Process_Id;
2338 procedure Spawn (Args : Argument_List);
2339 -- Call Spawn with given argument list
2341 N_Args : Argument_List (Args'Range);
2342 -- Normalized arguments
2348 procedure Spawn (Args : Argument_List) is
2349 type Chars is array (Positive range <>) of aliased Character;
2350 type Char_Ptr is access constant Character;
2352 Command_Len : constant Positive := Program_Name'Length + 1
2353 + Args_Length (Args);
2354 Command_Last : Natural := 0;
2355 Command : aliased Chars (1 .. Command_Len);
2356 -- Command contains all characters of the Program_Name and Args, all
2357 -- terminated by ASCII.NUL characters
2359 Arg_List_Len : constant Positive := Args'Length + 2;
2360 Arg_List_Last : Natural := 0;
2361 Arg_List : aliased array (1 .. Arg_List_Len) of Char_Ptr;
2362 -- List with pointers to NUL-terminated strings of the Program_Name
2363 -- and the Args and terminated with a null pointer. We rely on the
2364 -- default initialization for the last null pointer.
2366 procedure Add_To_Command (S : String);
2367 -- Add S and a NUL character to Command, updating Last
2369 function Portable_Spawn (Args : Address) return Integer;
2370 pragma Import (C, Portable_Spawn, "__gnat_portable_spawn");
2372 function Portable_No_Block_Spawn (Args : Address) return Process_Id;
2374 (C, Portable_No_Block_Spawn, "__gnat_portable_no_block_spawn");
2376 --------------------
2377 -- Add_To_Command --
2378 --------------------
2380 procedure Add_To_Command (S : String) is
2381 First : constant Natural := Command_Last + 1;
2384 Command_Last := Command_Last + S'Length;
2386 -- Move characters one at a time, because Command has aliased
2389 -- But not volatile, so why is this necessary ???
2391 for J in S'Range loop
2392 Command (First + J - S'First) := S (J);
2395 Command_Last := Command_Last + 1;
2396 Command (Command_Last) := ASCII.NUL;
2398 Arg_List_Last := Arg_List_Last + 1;
2399 Arg_List (Arg_List_Last) := Command (First)'Access;
2402 -- Start of processing for Spawn
2405 Add_To_Command (Program_Name);
2407 for J in Args'Range loop
2408 Add_To_Command (Args (J).all);
2413 Result := Portable_Spawn (Arg_List'Address);
2415 Pid := Portable_No_Block_Spawn (Arg_List'Address);
2416 Result := Boolean'Pos (Pid /= Invalid_Pid);
2420 -- Start of processing for Spawn_Internal
2423 -- Copy arguments into a local structure
2425 for K in N_Args'Range loop
2426 N_Args (K) := new String'(Args (K).all);
2429 -- Normalize those arguments
2431 Normalize_Arguments (N_Args);
2433 -- Call spawn using the normalized arguments
2437 -- Free arguments list
2439 for K in N_Args'Range loop
2444 ---------------------------
2445 -- To_Path_String_Access --
2446 ---------------------------
2448 function To_Path_String_Access
2449 (Path_Addr : Address;
2450 Path_Len : Integer) return String_Access
2452 subtype Path_String is String (1 .. Path_Len);
2453 type Path_String_Access is access Path_String;
2455 function Address_To_Access is new
2456 Unchecked_Conversion (Source => Address,
2457 Target => Path_String_Access);
2459 Path_Access : constant Path_String_Access :=
2460 Address_To_Access (Path_Addr);
2462 Return_Val : String_Access;
2465 Return_Val := new String (1 .. Path_Len);
2467 for J in 1 .. Path_Len loop
2468 Return_Val (J) := Path_Access (J);
2472 end To_Path_String_Access;
2478 procedure Wait_Process (Pid : out Process_Id; Success : out Boolean) is
2481 function Portable_Wait (S : Address) return Process_Id;
2482 pragma Import (C, Portable_Wait, "__gnat_portable_wait");
2485 Pid := Portable_Wait (Status'Address);
2486 Success := (Status = 0);
2494 (FD : File_Descriptor;
2496 N : Integer) return Integer
2499 return Integer (System.CRTL.write
2500 (System.CRTL.int (FD), System.CRTL.chars (A), System.CRTL.int (N)));