OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-os_lib.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                        S Y S T E M . O S _ L I B                         --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --                     Copyright (C) 1995-2012, AdaCore                     --
10 --                                                                          --
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 3,  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.                                     --
17 --                                                                          --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception,   --
20 -- version 3.1, as published by the Free Software Foundation.               --
21 --                                                                          --
22 -- You should have received a copy of the GNU General Public License and    --
23 -- a copy of the GCC Runtime Library Exception along with this program;     --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25 -- <http://www.gnu.org/licenses/>.                                          --
26 --                                                                          --
27 -- GNAT was originally developed  by the GNAT team at  New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
29 --                                                                          --
30 ------------------------------------------------------------------------------
31
32 pragma Compiler_Unit;
33
34 with Ada.Unchecked_Conversion;
35 with Ada.Unchecked_Deallocation;
36 with System; use System;
37 with System.Case_Util;
38 with System.CRTL;
39 with System.Soft_Links;
40
41 package body System.OS_Lib is
42
43    --  Imported procedures Dup and Dup2 are used in procedures Spawn and
44    --  Non_Blocking_Spawn.
45
46    function Dup (Fd : File_Descriptor) return File_Descriptor;
47    pragma Import (C, Dup, "__gnat_dup");
48
49    procedure Dup2 (Old_Fd, New_Fd : File_Descriptor);
50    pragma Import (C, Dup2, "__gnat_dup2");
51
52    On_Windows : constant Boolean := Directory_Separator = '\';
53    --  An indication that we are on Windows. Used in Normalize_Pathname, to
54    --  deal with drive letters in the beginning of absolute paths.
55
56    package SSL renames System.Soft_Links;
57
58    --  The following are used by Create_Temp_File
59
60    First_Temp_File_Name : constant String := "GNAT-TEMP-000000.TMP";
61    --  Used to initialize Current_Temp_File_Name and Temp_File_Name_Last_Digit
62
63    Current_Temp_File_Name : String := First_Temp_File_Name;
64    --  Name of the temp file last created
65
66    Temp_File_Name_Last_Digit : constant Positive :=
67                                  First_Temp_File_Name'Last - 4;
68    --  Position of the last digit in Current_Temp_File_Name
69
70    Max_Attempts : constant := 100;
71    --  The maximum number of attempts to create a new temp file
72
73    -----------------------
74    -- Local Subprograms --
75    -----------------------
76
77    function Args_Length (Args : Argument_List) return Natural;
78    --  Returns total number of characters needed to create a string of all Args
79    --  terminated by ASCII.NUL characters.
80
81    procedure Create_Temp_File_Internal
82      (FD     : out File_Descriptor;
83       Name   : out String_Access;
84       Stdout : Boolean);
85    --  Internal routine to implement two Create_Temp_File routines. If Stdout
86    --  is set to True the created descriptor is stdout-compatible, otherwise
87    --  it might not be depending on the OS (VMS is one example). The first two
88    --  parameters are as in Create_Temp_File.
89
90    function C_String_Length (S : Address) return Integer;
91    --  Returns the length of a C string. Does check for null address
92    --  (returns 0).
93
94    procedure Spawn_Internal
95      (Program_Name : String;
96       Args         : Argument_List;
97       Result       : out Integer;
98       Pid          : out Process_Id;
99       Blocking     : Boolean);
100    --  Internal routine to implement the two Spawn (blocking/non blocking)
101    --  routines. If Blocking is set to True then the spawn is blocking
102    --  otherwise it is non blocking. In this latter case the Pid contains the
103    --  process id number. The first three parameters are as in Spawn. Note that
104    --  Spawn_Internal normalizes the argument list before calling the low level
105    --  system spawn routines (see Normalize_Arguments).
106    --
107    --  Note: Normalize_Arguments is designed to do nothing if it is called more
108    --  than once, so calling Normalize_Arguments before calling one of the
109    --  spawn routines is fine.
110
111    function To_Path_String_Access
112      (Path_Addr : Address;
113       Path_Len  : Integer) return String_Access;
114    --  Converts a C String to an Ada String. We could do this making use of
115    --  Interfaces.C.Strings but we prefer not to import that entire package
116
117    ---------
118    -- "<" --
119    ---------
120
121    function "<"  (X, Y : OS_Time) return Boolean is
122    begin
123       return Long_Integer (X) < Long_Integer (Y);
124    end "<";
125
126    ----------
127    -- "<=" --
128    ----------
129
130    function "<="  (X, Y : OS_Time) return Boolean is
131    begin
132       return Long_Integer (X) <= Long_Integer (Y);
133    end "<=";
134
135    ---------
136    -- ">" --
137    ---------
138
139    function ">"  (X, Y : OS_Time) return Boolean is
140    begin
141       return Long_Integer (X) > Long_Integer (Y);
142    end ">";
143
144    ----------
145    -- ">=" --
146    ----------
147
148    function ">="  (X, Y : OS_Time) return Boolean is
149    begin
150       return Long_Integer (X) >= Long_Integer (Y);
151    end ">=";
152
153    -----------------
154    -- Args_Length --
155    -----------------
156
157    function Args_Length (Args : Argument_List) return Natural is
158       Len : Natural := 0;
159
160    begin
161       for J in Args'Range loop
162          Len := Len + Args (J)'Length + 1; --  One extra for ASCII.NUL
163       end loop;
164
165       return Len;
166    end Args_Length;
167
168    -----------------------------
169    -- Argument_String_To_List --
170    -----------------------------
171
172    function Argument_String_To_List
173      (Arg_String : String) return Argument_List_Access
174    is
175       Max_Args : constant Integer := Arg_String'Length;
176       New_Argv : Argument_List (1 .. Max_Args);
177       New_Argc : Natural := 0;
178       Idx      : Integer;
179
180    begin
181       Idx := Arg_String'First;
182
183       loop
184          exit when Idx > Arg_String'Last;
185
186          declare
187             Quoted  : Boolean := False;
188             Backqd  : Boolean := False;
189             Old_Idx : Integer;
190
191          begin
192             Old_Idx := Idx;
193
194             loop
195                --  An unquoted space is the end of an argument
196
197                if not (Backqd or Quoted)
198                  and then Arg_String (Idx) = ' '
199                then
200                   exit;
201
202                --  Start of a quoted string
203
204                elsif not (Backqd or Quoted)
205                  and then Arg_String (Idx) = '"'
206                then
207                   Quoted := True;
208
209                --  End of a quoted string and end of an argument
210
211                elsif (Quoted and not Backqd)
212                  and then Arg_String (Idx) = '"'
213                then
214                   Idx := Idx + 1;
215                   exit;
216
217                --  Following character is backquoted
218
219                elsif Arg_String (Idx) = '\' then
220                   Backqd := True;
221
222                --  Turn off backquoting after advancing one character
223
224                elsif Backqd then
225                   Backqd := False;
226
227                end if;
228
229                Idx := Idx + 1;
230                exit when Idx > Arg_String'Last;
231             end loop;
232
233             --  Found an argument
234
235             New_Argc := New_Argc + 1;
236             New_Argv (New_Argc) :=
237               new String'(Arg_String (Old_Idx .. Idx - 1));
238
239             --  Skip extraneous spaces
240
241             while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop
242                Idx := Idx + 1;
243             end loop;
244          end;
245       end loop;
246
247       return new Argument_List'(New_Argv (1 .. New_Argc));
248    end Argument_String_To_List;
249
250    ---------------------
251    -- C_String_Length --
252    ---------------------
253
254    function C_String_Length (S : Address) return Integer is
255       function Strlen (S : Address) return Integer;
256       pragma Import (C, Strlen, "strlen");
257    begin
258       if S = Null_Address then
259          return 0;
260       else
261          return Strlen (S);
262       end if;
263    end C_String_Length;
264
265    -----------
266    -- Close --
267    -----------
268
269    procedure Close (FD : File_Descriptor) is
270       procedure C_Close (FD : File_Descriptor);
271       pragma Import (C, C_Close, "close");
272    begin
273       C_Close (FD);
274    end Close;
275
276    procedure Close (FD : File_Descriptor; Status : out Boolean) is
277       function C_Close (FD : File_Descriptor) return Integer;
278       pragma Import (C, C_Close, "close");
279    begin
280       Status := (C_Close (FD) = 0);
281    end Close;
282
283    ---------------
284    -- Copy_File --
285    ---------------
286
287    procedure Copy_File
288      (Name     : String;
289       Pathname : String;
290       Success  : out Boolean;
291       Mode     : Copy_Mode := Copy;
292       Preserve : Attribute := Time_Stamps)
293    is
294       From : File_Descriptor;
295       To   : File_Descriptor;
296
297       Copy_Error : exception;
298       --  Internal exception raised to signal error in copy
299
300       function Build_Path (Dir : String; File : String) return String;
301       --  Returns pathname Dir concatenated with File adding the directory
302       --  separator only if needed.
303
304       procedure Copy (From, To : File_Descriptor);
305       --  Read data from From and place them into To. In both cases the
306       --  operations uses the current file position. Raises Constraint_Error
307       --  if a problem occurs during the copy.
308
309       procedure Copy_To (To_Name : String);
310       --  Does a straight copy from source to designated destination file
311
312       ----------------
313       -- Build_Path --
314       ----------------
315
316       function Build_Path (Dir : String; File : String) return String is
317          Res : String (1 .. Dir'Length + File'Length + 1);
318
319          Base_File_Ptr : Integer;
320          --  The base file name is File (Base_File_Ptr + 1 .. File'Last)
321
322          function Is_Dirsep (C : Character) return Boolean;
323          pragma Inline (Is_Dirsep);
324          --  Returns True if C is a directory separator. On Windows we
325          --  handle both styles of directory separator.
326
327          ---------------
328          -- Is_Dirsep --
329          ---------------
330
331          function Is_Dirsep (C : Character) return Boolean is
332          begin
333             return C = Directory_Separator or else C = '/';
334          end Is_Dirsep;
335
336       --  Start of processing for Build_Path
337
338       begin
339          --  Find base file name
340
341          Base_File_Ptr := File'Last;
342          while Base_File_Ptr >= File'First loop
343             exit when Is_Dirsep (File (Base_File_Ptr));
344             Base_File_Ptr := Base_File_Ptr - 1;
345          end loop;
346
347          declare
348             Base_File : String renames
349                           File (Base_File_Ptr + 1 .. File'Last);
350
351          begin
352             Res (1 .. Dir'Length) := Dir;
353
354             if Is_Dirsep (Dir (Dir'Last)) then
355                Res (Dir'Length + 1 .. Dir'Length + Base_File'Length) :=
356                  Base_File;
357                return Res (1 .. Dir'Length + Base_File'Length);
358
359             else
360                Res (Dir'Length + 1) := Directory_Separator;
361                Res (Dir'Length + 2 .. Dir'Length + 1 + Base_File'Length) :=
362                  Base_File;
363                return Res (1 .. Dir'Length + 1 + Base_File'Length);
364             end if;
365          end;
366       end Build_Path;
367
368       ----------
369       -- Copy --
370       ----------
371
372       procedure Copy (From, To : File_Descriptor) is
373          Buf_Size : constant := 200_000;
374          type Buf is array (1 .. Buf_Size) of Character;
375          type Buf_Ptr is access Buf;
376
377          Buffer : Buf_Ptr;
378          R      : Integer;
379          W      : Integer;
380
381          Status_From : Boolean;
382          Status_To   : Boolean;
383          --  Statuses for the calls to Close
384
385          procedure Free is new Ada.Unchecked_Deallocation (Buf, Buf_Ptr);
386
387       begin
388          --  Check for invalid descriptors, making sure that we do not
389          --  accidentally leave an open file descriptor around.
390
391          if From = Invalid_FD then
392             if To /= Invalid_FD then
393                Close (To, Status_To);
394             end if;
395
396             raise Copy_Error;
397
398          elsif To = Invalid_FD then
399             Close (From, Status_From);
400             raise Copy_Error;
401          end if;
402
403          --  Allocate the buffer on the heap
404
405          Buffer := new Buf;
406
407          loop
408             R := Read (From, Buffer (1)'Address, Buf_Size);
409
410             --  For VMS, the buffer may not be full. So, we need to try again
411             --  until there is nothing to read.
412
413             exit when R = 0;
414
415             W := Write (To, Buffer (1)'Address, R);
416
417             if W < R then
418
419                --  Problem writing data, could be a disk full. Close files
420                --  without worrying about status, since we are raising a
421                --  Copy_Error exception in any case.
422
423                Close (From, Status_From);
424                Close (To, Status_To);
425
426                Free (Buffer);
427
428                raise Copy_Error;
429             end if;
430          end loop;
431
432          Close (From, Status_From);
433          Close (To, Status_To);
434
435          Free (Buffer);
436
437          if not (Status_From and Status_To) then
438             raise Copy_Error;
439          end if;
440       end Copy;
441
442       -------------
443       -- Copy_To --
444       -------------
445
446       procedure Copy_To (To_Name : String) is
447
448          function Copy_Attributes
449            (From, To : System.Address;
450             Mode     : Integer) return Integer;
451          pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
452          --  Mode = 0 - copy only time stamps.
453          --  Mode = 1 - copy time stamps and read/write/execute attributes
454
455          C_From : String (1 .. Name'Length + 1);
456          C_To   : String (1 .. To_Name'Length + 1);
457
458       begin
459          From := Open_Read (Name, Binary);
460
461          --  Do not clobber destination file if source file could not be opened
462
463          if From /= Invalid_FD then
464             To := Create_File (To_Name, Binary);
465          end if;
466
467          Copy (From, To);
468
469          --  Copy attributes
470
471          C_From (1 .. Name'Length) := Name;
472          C_From (C_From'Last) := ASCII.NUL;
473
474          C_To (1 .. To_Name'Length) := To_Name;
475          C_To (C_To'Last) := ASCII.NUL;
476
477          case Preserve is
478
479             when Time_Stamps =>
480                if Copy_Attributes (C_From'Address, C_To'Address, 0) = -1 then
481                   raise Copy_Error;
482                end if;
483
484             when Full =>
485                if Copy_Attributes (C_From'Address, C_To'Address, 1) = -1 then
486                   raise Copy_Error;
487                end if;
488
489             when None =>
490                null;
491          end case;
492
493       end Copy_To;
494
495    --  Start of processing for Copy_File
496
497    begin
498       Success := True;
499
500       --  The source file must exist
501
502       if not Is_Regular_File (Name) then
503          raise Copy_Error;
504       end if;
505
506       --  The source file exists
507
508       case Mode is
509
510          --  Copy case, target file must not exist
511
512          when Copy =>
513
514             --  If the target file exists, we have an error
515
516             if Is_Regular_File (Pathname) then
517                raise Copy_Error;
518
519             --  Case of target is a directory
520
521             elsif Is_Directory (Pathname) then
522                declare
523                   Dest : constant String := Build_Path (Pathname, Name);
524
525                begin
526                   --  If target file exists, we have an error, else do copy
527
528                   if Is_Regular_File (Dest) then
529                      raise Copy_Error;
530                   else
531                      Copy_To (Dest);
532                   end if;
533                end;
534
535             --  Case of normal copy to file (destination does not exist)
536
537             else
538                Copy_To (Pathname);
539             end if;
540
541          --  Overwrite case (destination file may or may not exist)
542
543          when Overwrite =>
544             if Is_Directory (Pathname) then
545                Copy_To (Build_Path (Pathname, Name));
546             else
547                Copy_To (Pathname);
548             end if;
549
550          --  Append case (destination file may or may not exist)
551
552          when Append =>
553
554             --  Appending to existing file
555
556             if Is_Regular_File (Pathname) then
557
558                --  Append mode and destination file exists, append data at the
559                --  end of Pathname. But if we fail to open source file, do not
560                --  touch destination file at all.
561
562                From := Open_Read (Name, Binary);
563                if From /= Invalid_FD then
564                   To := Open_Read_Write (Pathname, Binary);
565                end if;
566
567                Lseek (To, 0, Seek_End);
568
569                Copy (From, To);
570
571             --  Appending to directory, not allowed
572
573             elsif Is_Directory (Pathname) then
574                raise Copy_Error;
575
576             --  Appending when target file does not exist
577
578             else
579                Copy_To (Pathname);
580             end if;
581       end case;
582
583    --  All error cases are caught here
584
585    exception
586       when Copy_Error =>
587          Success := False;
588    end Copy_File;
589
590    procedure Copy_File
591      (Name     : C_File_Name;
592       Pathname : C_File_Name;
593       Success  : out Boolean;
594       Mode     : Copy_Mode := Copy;
595       Preserve : Attribute := Time_Stamps)
596    is
597       Ada_Name     : String_Access :=
598                        To_Path_String_Access
599                          (Name, C_String_Length (Name));
600       Ada_Pathname : String_Access :=
601                        To_Path_String_Access
602                          (Pathname, C_String_Length (Pathname));
603    begin
604       Copy_File (Ada_Name.all, Ada_Pathname.all, Success, Mode, Preserve);
605       Free (Ada_Name);
606       Free (Ada_Pathname);
607    end Copy_File;
608
609    ----------------------
610    -- Copy_Time_Stamps --
611    ----------------------
612
613    procedure Copy_Time_Stamps (Source, Dest : String; Success : out Boolean) is
614
615       function Copy_Attributes
616         (From, To : System.Address;
617          Mode     : Integer) return Integer;
618       pragma Import (C, Copy_Attributes, "__gnat_copy_attribs");
619       --  Mode = 0 - copy only time stamps.
620       --  Mode = 1 - copy time stamps and read/write/execute attributes
621
622    begin
623       if Is_Regular_File (Source) and then Is_Writable_File (Dest) then
624          declare
625             C_Source : String (1 .. Source'Length + 1);
626             C_Dest   : String (1 .. Dest'Length + 1);
627
628          begin
629             C_Source (1 .. Source'Length) := Source;
630             C_Source (C_Source'Last)      := ASCII.NUL;
631
632             C_Dest (1 .. Dest'Length) := Dest;
633             C_Dest (C_Dest'Last)      := ASCII.NUL;
634
635             if Copy_Attributes (C_Source'Address, C_Dest'Address, 0) = -1 then
636                Success := False;
637             else
638                Success := True;
639             end if;
640          end;
641
642       else
643          Success := False;
644       end if;
645    end Copy_Time_Stamps;
646
647    procedure Copy_Time_Stamps
648      (Source, Dest : C_File_Name;
649       Success      : out Boolean)
650    is
651       Ada_Source : String_Access :=
652                      To_Path_String_Access
653                        (Source, C_String_Length (Source));
654       Ada_Dest   : String_Access :=
655                      To_Path_String_Access
656                        (Dest, C_String_Length (Dest));
657    begin
658       Copy_Time_Stamps (Ada_Source.all, Ada_Dest.all, Success);
659       Free (Ada_Source);
660       Free (Ada_Dest);
661    end Copy_Time_Stamps;
662
663    -----------------
664    -- Create_File --
665    -----------------
666
667    function Create_File
668      (Name  : C_File_Name;
669       Fmode : Mode) return File_Descriptor
670    is
671       function C_Create_File
672         (Name  : C_File_Name;
673          Fmode : Mode) return File_Descriptor;
674       pragma Import (C, C_Create_File, "__gnat_open_create");
675
676    begin
677       return C_Create_File (Name, Fmode);
678    end Create_File;
679
680    function Create_File
681      (Name  : String;
682       Fmode : Mode) return File_Descriptor
683    is
684       C_Name : String (1 .. Name'Length + 1);
685
686    begin
687       C_Name (1 .. Name'Length) := Name;
688       C_Name (C_Name'Last)      := ASCII.NUL;
689       return Create_File (C_Name (C_Name'First)'Address, Fmode);
690    end Create_File;
691
692    ---------------------
693    -- Create_New_File --
694    ---------------------
695
696    function Create_New_File
697      (Name  : C_File_Name;
698       Fmode : Mode) return File_Descriptor
699    is
700       function C_Create_New_File
701         (Name  : C_File_Name;
702          Fmode : Mode) return File_Descriptor;
703       pragma Import (C, C_Create_New_File, "__gnat_open_new");
704
705    begin
706       return C_Create_New_File (Name, Fmode);
707    end Create_New_File;
708
709    function Create_New_File
710      (Name  : String;
711       Fmode : Mode) return File_Descriptor
712    is
713       C_Name : String (1 .. Name'Length + 1);
714
715    begin
716       C_Name (1 .. Name'Length) := Name;
717       C_Name (C_Name'Last)      := ASCII.NUL;
718       return Create_New_File (C_Name (C_Name'First)'Address, Fmode);
719    end Create_New_File;
720
721    -----------------------------
722    -- Create_Output_Text_File --
723    -----------------------------
724
725    function Create_Output_Text_File (Name : String) return File_Descriptor is
726       function C_Create_File
727         (Name : C_File_Name) return File_Descriptor;
728       pragma Import (C, C_Create_File, "__gnat_create_output_file");
729
730       C_Name : String (1 .. Name'Length + 1);
731
732    begin
733       C_Name (1 .. Name'Length) := Name;
734       C_Name (C_Name'Last)      := ASCII.NUL;
735       return C_Create_File (C_Name (C_Name'First)'Address);
736    end Create_Output_Text_File;
737
738    ----------------------
739    -- Create_Temp_File --
740    ----------------------
741
742    procedure Create_Temp_File
743      (FD   : out File_Descriptor;
744       Name : out Temp_File_Name)
745    is
746       function Open_New_Temp
747         (Name  : System.Address;
748          Fmode : Mode) return File_Descriptor;
749       pragma Import (C, Open_New_Temp, "__gnat_open_new_temp");
750
751    begin
752       FD := Open_New_Temp (Name'Address, Binary);
753    end Create_Temp_File;
754
755    procedure Create_Temp_File
756      (FD   : out File_Descriptor;
757       Name : out String_Access)
758    is
759    begin
760       Create_Temp_File_Internal (FD, Name, Stdout => False);
761    end Create_Temp_File;
762
763    procedure Create_Temp_Output_File
764      (FD   : out File_Descriptor;
765       Name : out String_Access)
766    is
767    begin
768       Create_Temp_File_Internal (FD, Name, Stdout => True);
769    end Create_Temp_Output_File;
770
771    -------------------------------
772    -- Create_Temp_File_Internal --
773    -------------------------------
774
775    procedure Create_Temp_File_Internal
776      (FD        : out File_Descriptor;
777       Name      : out String_Access;
778       Stdout    : Boolean)
779    is
780       Pos      : Positive;
781       Attempts : Natural := 0;
782       Current  : String (Current_Temp_File_Name'Range);
783
784       ---------------------------------
785       -- Create_New_Output_Text_File --
786       ---------------------------------
787
788       function Create_New_Output_Text_File
789         (Name : String) return File_Descriptor;
790       --  Similar to Create_Output_Text_File, except it fails if the file
791       --  already exists. We need this behavior to ensure we don't accidentally
792       --  open a temp file that has just been created by a concurrently running
793       --  process. There is no point exposing this function, as it's generally
794       --  not particularly useful.
795
796       function Create_New_Output_Text_File
797         (Name : String) return File_Descriptor is
798          function C_Create_File
799            (Name : C_File_Name) return File_Descriptor;
800          pragma Import (C, C_Create_File, "__gnat_create_output_file_new");
801
802          C_Name : String (1 .. Name'Length + 1);
803
804       begin
805          C_Name (1 .. Name'Length) := Name;
806          C_Name (C_Name'Last)      := ASCII.NUL;
807          return C_Create_File (C_Name (C_Name'First)'Address);
808       end Create_New_Output_Text_File;
809
810    begin
811       --  Loop until a new temp file can be created
812
813       File_Loop : loop
814          Locked : begin
815             --  We need to protect global variable Current_Temp_File_Name
816             --  against concurrent access by different tasks.
817
818             SSL.Lock_Task.all;
819
820             --  Start at the last digit
821
822             Pos := Temp_File_Name_Last_Digit;
823
824             Digit_Loop :
825             loop
826                --  Increment the digit by one
827
828                case Current_Temp_File_Name (Pos) is
829                   when '0' .. '8' =>
830                      Current_Temp_File_Name (Pos) :=
831                        Character'Succ (Current_Temp_File_Name (Pos));
832                      exit Digit_Loop;
833
834                   when '9' =>
835
836                      --  For 9, set the digit to 0 and go to the previous digit
837
838                      Current_Temp_File_Name (Pos) := '0';
839                      Pos := Pos - 1;
840
841                   when others =>
842
843                      --  If it is not a digit, then there are no available
844                      --  temp file names. Return Invalid_FD. There is almost
845                      --  no chance that this code will be ever be executed,
846                      --  since it would mean that there are one million temp
847                      --  files in the same directory!
848
849                      SSL.Unlock_Task.all;
850                      FD := Invalid_FD;
851                      Name := null;
852                      exit File_Loop;
853                end case;
854             end loop Digit_Loop;
855
856             Current := Current_Temp_File_Name;
857
858             --  We can now release the lock, because we are no longer
859             --  accessing Current_Temp_File_Name.
860
861             SSL.Unlock_Task.all;
862
863          exception
864             when others =>
865                SSL.Unlock_Task.all;
866                raise;
867          end Locked;
868
869          --  Attempt to create the file
870
871          if Stdout then
872             FD := Create_New_Output_Text_File (Current);
873          else
874             FD := Create_New_File (Current, Binary);
875          end if;
876
877          if FD /= Invalid_FD then
878             Name := new String'(Current);
879             exit File_Loop;
880          end if;
881
882          if not Is_Regular_File (Current) then
883
884             --  If the file does not already exist and we are unable to create
885             --  it, we give up after Max_Attempts. Otherwise, we try again with
886             --  the next available file name.
887
888             Attempts := Attempts + 1;
889
890             if Attempts >= Max_Attempts then
891                FD := Invalid_FD;
892                Name := null;
893                exit File_Loop;
894             end if;
895          end if;
896       end loop File_Loop;
897    end Create_Temp_File_Internal;
898
899    -----------------
900    -- Delete_File --
901    -----------------
902
903    procedure Delete_File (Name : Address; Success : out Boolean) is
904       R : Integer;
905    begin
906       R := System.CRTL.unlink (Name);
907       Success := (R = 0);
908    end Delete_File;
909
910    procedure Delete_File (Name : String; Success : out Boolean) is
911       C_Name : String (1 .. Name'Length + 1);
912
913    begin
914       C_Name (1 .. Name'Length) := Name;
915       C_Name (C_Name'Last)      := ASCII.NUL;
916
917       Delete_File (C_Name'Address, Success);
918    end Delete_File;
919
920    ---------------------
921    -- File_Time_Stamp --
922    ---------------------
923
924    function File_Time_Stamp (FD : File_Descriptor) return OS_Time is
925       function File_Time (FD : File_Descriptor) return OS_Time;
926       pragma Import (C, File_Time, "__gnat_file_time_fd");
927    begin
928       return File_Time (FD);
929    end File_Time_Stamp;
930
931    function File_Time_Stamp (Name : C_File_Name) return OS_Time is
932       function File_Time (Name : Address) return OS_Time;
933       pragma Import (C, File_Time, "__gnat_file_time_name");
934    begin
935       return File_Time (Name);
936    end File_Time_Stamp;
937
938    function File_Time_Stamp (Name : String) return OS_Time is
939       F_Name : String (1 .. Name'Length + 1);
940    begin
941       F_Name (1 .. Name'Length) := Name;
942       F_Name (F_Name'Last)      := ASCII.NUL;
943       return File_Time_Stamp (F_Name'Address);
944    end File_Time_Stamp;
945
946    ---------------------------
947    -- Get_Debuggable_Suffix --
948    ---------------------------
949
950    function Get_Debuggable_Suffix return String_Access is
951       procedure Get_Suffix_Ptr (Length, Ptr : Address);
952       pragma Import (C, Get_Suffix_Ptr, "__gnat_get_debuggable_suffix_ptr");
953
954       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
955       pragma Import (C, Strncpy, "strncpy");
956
957       Suffix_Ptr    : Address;
958       Suffix_Length : Integer;
959       Result        : String_Access;
960
961    begin
962       Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
963
964       Result := new String (1 .. Suffix_Length);
965
966       if Suffix_Length > 0 then
967          Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
968       end if;
969
970       return Result;
971    end Get_Debuggable_Suffix;
972
973    ---------------------------
974    -- Get_Executable_Suffix --
975    ---------------------------
976
977    function Get_Executable_Suffix return String_Access is
978       procedure Get_Suffix_Ptr (Length, Ptr : Address);
979       pragma Import (C, Get_Suffix_Ptr, "__gnat_get_executable_suffix_ptr");
980
981       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
982       pragma Import (C, Strncpy, "strncpy");
983
984       Suffix_Ptr    : Address;
985       Suffix_Length : Integer;
986       Result        : String_Access;
987
988    begin
989       Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
990
991       Result := new String (1 .. Suffix_Length);
992
993       if Suffix_Length > 0 then
994          Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
995       end if;
996
997       return Result;
998    end Get_Executable_Suffix;
999
1000    -----------------------
1001    -- Get_Object_Suffix --
1002    -----------------------
1003
1004    function Get_Object_Suffix return String_Access is
1005       procedure Get_Suffix_Ptr (Length, Ptr : Address);
1006       pragma Import (C, Get_Suffix_Ptr, "__gnat_get_object_suffix_ptr");
1007
1008       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
1009       pragma Import (C, Strncpy, "strncpy");
1010
1011       Suffix_Ptr    : Address;
1012       Suffix_Length : Integer;
1013       Result        : String_Access;
1014
1015    begin
1016       Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
1017
1018       Result := new String (1 .. Suffix_Length);
1019
1020       if Suffix_Length > 0 then
1021          Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
1022       end if;
1023
1024       return Result;
1025    end Get_Object_Suffix;
1026
1027    ----------------------------------
1028    -- Get_Target_Debuggable_Suffix --
1029    ----------------------------------
1030
1031    function Get_Target_Debuggable_Suffix return String_Access is
1032       Target_Exec_Ext_Ptr : Address;
1033       pragma Import
1034         (C, Target_Exec_Ext_Ptr, "__gnat_target_debuggable_extension");
1035
1036       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
1037       pragma Import (C, Strncpy, "strncpy");
1038
1039       function Strlen (Cstring : Address) return Integer;
1040       pragma Import (C, Strlen, "strlen");
1041
1042       Suffix_Length : Integer;
1043       Result        : String_Access;
1044
1045    begin
1046       Suffix_Length := Strlen (Target_Exec_Ext_Ptr);
1047
1048       Result := new String (1 .. Suffix_Length);
1049
1050       if Suffix_Length > 0 then
1051          Strncpy (Result.all'Address, Target_Exec_Ext_Ptr, Suffix_Length);
1052       end if;
1053
1054       return Result;
1055    end Get_Target_Debuggable_Suffix;
1056
1057    ----------------------------------
1058    -- Get_Target_Executable_Suffix --
1059    ----------------------------------
1060
1061    function Get_Target_Executable_Suffix return String_Access is
1062       Target_Exec_Ext_Ptr : Address;
1063       pragma Import
1064         (C, Target_Exec_Ext_Ptr, "__gnat_target_executable_extension");
1065
1066       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
1067       pragma Import (C, Strncpy, "strncpy");
1068
1069       function Strlen (Cstring : Address) return Integer;
1070       pragma Import (C, Strlen, "strlen");
1071
1072       Suffix_Length : Integer;
1073       Result        : String_Access;
1074
1075    begin
1076       Suffix_Length := Strlen (Target_Exec_Ext_Ptr);
1077
1078       Result := new String (1 .. Suffix_Length);
1079
1080       if Suffix_Length > 0 then
1081          Strncpy (Result.all'Address, Target_Exec_Ext_Ptr, Suffix_Length);
1082       end if;
1083
1084       return Result;
1085    end Get_Target_Executable_Suffix;
1086
1087    ------------------------------
1088    -- Get_Target_Object_Suffix --
1089    ------------------------------
1090
1091    function Get_Target_Object_Suffix return String_Access is
1092       Target_Object_Ext_Ptr : Address;
1093       pragma Import
1094         (C, Target_Object_Ext_Ptr, "__gnat_target_object_extension");
1095
1096       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
1097       pragma Import (C, Strncpy, "strncpy");
1098
1099       function Strlen (Cstring : Address) return Integer;
1100       pragma Import (C, Strlen, "strlen");
1101
1102       Suffix_Length : Integer;
1103       Result        : String_Access;
1104
1105    begin
1106       Suffix_Length := Strlen (Target_Object_Ext_Ptr);
1107
1108       Result := new String (1 .. Suffix_Length);
1109
1110       if Suffix_Length > 0 then
1111          Strncpy (Result.all'Address, Target_Object_Ext_Ptr, Suffix_Length);
1112       end if;
1113
1114       return Result;
1115    end Get_Target_Object_Suffix;
1116
1117    ------------
1118    -- Getenv --
1119    ------------
1120
1121    function Getenv (Name : String) return String_Access is
1122       procedure Get_Env_Value_Ptr (Name, Length, Ptr : Address);
1123       pragma Import (C, Get_Env_Value_Ptr, "__gnat_getenv");
1124
1125       procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
1126       pragma Import (C, Strncpy, "strncpy");
1127
1128       Env_Value_Ptr    : aliased Address;
1129       Env_Value_Length : aliased Integer;
1130       F_Name           : aliased String (1 .. Name'Length + 1);
1131       Result           : String_Access;
1132
1133    begin
1134       F_Name (1 .. Name'Length) := Name;
1135       F_Name (F_Name'Last)      := ASCII.NUL;
1136
1137       Get_Env_Value_Ptr
1138         (F_Name'Address, Env_Value_Length'Address, Env_Value_Ptr'Address);
1139
1140       Result := new String (1 .. Env_Value_Length);
1141
1142       if Env_Value_Length > 0 then
1143          Strncpy (Result.all'Address, Env_Value_Ptr, Env_Value_Length);
1144       end if;
1145
1146       return Result;
1147    end Getenv;
1148
1149    ------------
1150    -- GM_Day --
1151    ------------
1152
1153    function GM_Day (Date : OS_Time) return Day_Type is
1154       D  : Day_Type;
1155
1156       pragma Warnings (Off);
1157       Y  : Year_Type;
1158       Mo : Month_Type;
1159       H  : Hour_Type;
1160       Mn : Minute_Type;
1161       S  : Second_Type;
1162       pragma Warnings (On);
1163
1164    begin
1165       GM_Split (Date, Y, Mo, D, H, Mn, S);
1166       return D;
1167    end GM_Day;
1168
1169    -------------
1170    -- GM_Hour --
1171    -------------
1172
1173    function GM_Hour (Date : OS_Time) return Hour_Type is
1174       H  : Hour_Type;
1175
1176       pragma Warnings (Off);
1177       Y  : Year_Type;
1178       Mo : Month_Type;
1179       D  : Day_Type;
1180       Mn : Minute_Type;
1181       S  : Second_Type;
1182       pragma Warnings (On);
1183
1184    begin
1185       GM_Split (Date, Y, Mo, D, H, Mn, S);
1186       return H;
1187    end GM_Hour;
1188
1189    ---------------
1190    -- GM_Minute --
1191    ---------------
1192
1193    function GM_Minute (Date : OS_Time) return Minute_Type is
1194       Mn : Minute_Type;
1195
1196       pragma Warnings (Off);
1197       Y  : Year_Type;
1198       Mo : Month_Type;
1199       D  : Day_Type;
1200       H  : Hour_Type;
1201       S  : Second_Type;
1202       pragma Warnings (On);
1203
1204    begin
1205       GM_Split (Date, Y, Mo, D, H, Mn, S);
1206       return Mn;
1207    end GM_Minute;
1208
1209    --------------
1210    -- GM_Month --
1211    --------------
1212
1213    function GM_Month (Date : OS_Time) return Month_Type is
1214       Mo : Month_Type;
1215
1216       pragma Warnings (Off);
1217       Y  : Year_Type;
1218       D  : Day_Type;
1219       H  : Hour_Type;
1220       Mn : Minute_Type;
1221       S  : Second_Type;
1222       pragma Warnings (On);
1223
1224    begin
1225       GM_Split (Date, Y, Mo, D, H, Mn, S);
1226       return Mo;
1227    end GM_Month;
1228
1229    ---------------
1230    -- GM_Second --
1231    ---------------
1232
1233    function GM_Second (Date : OS_Time) return Second_Type is
1234       S  : Second_Type;
1235
1236       pragma Warnings (Off);
1237       Y  : Year_Type;
1238       Mo : Month_Type;
1239       D  : Day_Type;
1240       H  : Hour_Type;
1241       Mn : Minute_Type;
1242       pragma Warnings (On);
1243
1244    begin
1245       GM_Split (Date, Y, Mo, D, H, Mn, S);
1246       return S;
1247    end GM_Second;
1248
1249    --------------
1250    -- GM_Split --
1251    --------------
1252
1253    procedure GM_Split
1254      (Date   : OS_Time;
1255       Year   : out Year_Type;
1256       Month  : out Month_Type;
1257       Day    : out Day_Type;
1258       Hour   : out Hour_Type;
1259       Minute : out Minute_Type;
1260       Second : out Second_Type)
1261    is
1262       procedure To_GM_Time
1263         (P_Time_T, P_Year, P_Month, P_Day, P_Hours, P_Mins, P_Secs : Address);
1264       pragma Import (C, To_GM_Time, "__gnat_to_gm_time");
1265
1266       T  : OS_Time := Date;
1267       Y  : Integer;
1268       Mo : Integer;
1269       D  : Integer;
1270       H  : Integer;
1271       Mn : Integer;
1272       S  : Integer;
1273
1274    begin
1275       --  Use the global lock because To_GM_Time is not thread safe
1276
1277       Locked_Processing : begin
1278          SSL.Lock_Task.all;
1279          To_GM_Time
1280            (T'Address, Y'Address, Mo'Address, D'Address,
1281             H'Address, Mn'Address, S'Address);
1282          SSL.Unlock_Task.all;
1283
1284       exception
1285          when others =>
1286             SSL.Unlock_Task.all;
1287             raise;
1288       end Locked_Processing;
1289
1290       Year   := Y + 1900;
1291       Month  := Mo + 1;
1292       Day    := D;
1293       Hour   := H;
1294       Minute := Mn;
1295       Second := S;
1296    end GM_Split;
1297
1298    -------------
1299    -- GM_Year --
1300    -------------
1301
1302    function GM_Year (Date : OS_Time) return Year_Type is
1303       Y  : Year_Type;
1304
1305       pragma Warnings (Off);
1306       Mo : Month_Type;
1307       D  : Day_Type;
1308       H  : Hour_Type;
1309       Mn : Minute_Type;
1310       S  : Second_Type;
1311       pragma Warnings (On);
1312
1313    begin
1314       GM_Split (Date, Y, Mo, D, H, Mn, S);
1315       return Y;
1316    end GM_Year;
1317
1318    ----------------------
1319    -- Is_Absolute_Path --
1320    ----------------------
1321
1322    function Is_Absolute_Path (Name : String) return Boolean is
1323       function Is_Absolute_Path
1324         (Name   : Address;
1325          Length : Integer) return Integer;
1326       pragma Import (C, Is_Absolute_Path, "__gnat_is_absolute_path");
1327    begin
1328       return Is_Absolute_Path (Name'Address, Name'Length) /= 0;
1329    end Is_Absolute_Path;
1330
1331    ------------------
1332    -- Is_Directory --
1333    ------------------
1334
1335    function Is_Directory (Name : C_File_Name) return Boolean is
1336       function Is_Directory (Name : Address) return Integer;
1337       pragma Import (C, Is_Directory, "__gnat_is_directory");
1338    begin
1339       return Is_Directory (Name) /= 0;
1340    end Is_Directory;
1341
1342    function Is_Directory (Name : String) return Boolean is
1343       F_Name : String (1 .. Name'Length + 1);
1344    begin
1345       F_Name (1 .. Name'Length) := Name;
1346       F_Name (F_Name'Last)      := ASCII.NUL;
1347       return Is_Directory (F_Name'Address);
1348    end Is_Directory;
1349
1350    ----------------------
1351    -- Is_Readable_File --
1352    ----------------------
1353
1354    function Is_Readable_File (Name : C_File_Name) return Boolean is
1355       function Is_Readable_File (Name : Address) return Integer;
1356       pragma Import (C, Is_Readable_File, "__gnat_is_readable_file");
1357    begin
1358       return Is_Readable_File (Name) /= 0;
1359    end Is_Readable_File;
1360
1361    function Is_Readable_File (Name : String) return Boolean is
1362       F_Name : String (1 .. Name'Length + 1);
1363    begin
1364       F_Name (1 .. Name'Length) := Name;
1365       F_Name (F_Name'Last)      := ASCII.NUL;
1366       return Is_Readable_File (F_Name'Address);
1367    end Is_Readable_File;
1368
1369    ------------------------
1370    -- Is_Executable_File --
1371    ------------------------
1372
1373    function Is_Executable_File (Name : C_File_Name) return Boolean is
1374       function Is_Executable_File (Name : Address) return Integer;
1375       pragma Import (C, Is_Executable_File, "__gnat_is_executable_file");
1376    begin
1377       return Is_Executable_File (Name) /= 0;
1378    end Is_Executable_File;
1379
1380    function Is_Executable_File (Name : String) return Boolean is
1381       F_Name : String (1 .. Name'Length + 1);
1382    begin
1383       F_Name (1 .. Name'Length) := Name;
1384       F_Name (F_Name'Last)      := ASCII.NUL;
1385       return Is_Executable_File (F_Name'Address);
1386    end Is_Executable_File;
1387
1388    ---------------------
1389    -- Is_Regular_File --
1390    ---------------------
1391
1392    function Is_Regular_File (Name : C_File_Name) return Boolean is
1393       function Is_Regular_File (Name : Address) return Integer;
1394       pragma Import (C, Is_Regular_File, "__gnat_is_regular_file");
1395    begin
1396       return Is_Regular_File (Name) /= 0;
1397    end Is_Regular_File;
1398
1399    function Is_Regular_File (Name : String) return Boolean is
1400       F_Name : String (1 .. Name'Length + 1);
1401    begin
1402       F_Name (1 .. Name'Length) := Name;
1403       F_Name (F_Name'Last)      := ASCII.NUL;
1404       return Is_Regular_File (F_Name'Address);
1405    end Is_Regular_File;
1406
1407    ----------------------
1408    -- Is_Symbolic_Link --
1409    ----------------------
1410
1411    function Is_Symbolic_Link (Name : C_File_Name) return Boolean is
1412       function Is_Symbolic_Link (Name : Address) return Integer;
1413       pragma Import (C, Is_Symbolic_Link, "__gnat_is_symbolic_link");
1414    begin
1415       return Is_Symbolic_Link (Name) /= 0;
1416    end Is_Symbolic_Link;
1417
1418    function Is_Symbolic_Link (Name : String) return Boolean is
1419       F_Name : String (1 .. Name'Length + 1);
1420    begin
1421       F_Name (1 .. Name'Length) := Name;
1422       F_Name (F_Name'Last)      := ASCII.NUL;
1423       return Is_Symbolic_Link (F_Name'Address);
1424    end Is_Symbolic_Link;
1425
1426    ----------------------
1427    -- Is_Writable_File --
1428    ----------------------
1429
1430    function Is_Writable_File (Name : C_File_Name) return Boolean is
1431       function Is_Writable_File (Name : Address) return Integer;
1432       pragma Import (C, Is_Writable_File, "__gnat_is_writable_file");
1433    begin
1434       return Is_Writable_File (Name) /= 0;
1435    end Is_Writable_File;
1436
1437    function Is_Writable_File (Name : String) return Boolean is
1438       F_Name : String (1 .. Name'Length + 1);
1439    begin
1440       F_Name (1 .. Name'Length) := Name;
1441       F_Name (F_Name'Last)      := ASCII.NUL;
1442       return Is_Writable_File (F_Name'Address);
1443    end Is_Writable_File;
1444
1445    -------------------------
1446    -- Locate_Exec_On_Path --
1447    -------------------------
1448
1449    function Locate_Exec_On_Path
1450      (Exec_Name : String) return String_Access
1451    is
1452       function Locate_Exec_On_Path (C_Exec_Name : Address) return Address;
1453       pragma Import (C, Locate_Exec_On_Path, "__gnat_locate_exec_on_path");
1454
1455       procedure Free (Ptr : System.Address);
1456       pragma Import (C, Free, "free");
1457
1458       C_Exec_Name  : String (1 .. Exec_Name'Length + 1);
1459       Path_Addr    : Address;
1460       Path_Len     : Integer;
1461       Result       : String_Access;
1462
1463    begin
1464       C_Exec_Name (1 .. Exec_Name'Length)   := Exec_Name;
1465       C_Exec_Name (C_Exec_Name'Last)        := ASCII.NUL;
1466
1467       Path_Addr := Locate_Exec_On_Path (C_Exec_Name'Address);
1468       Path_Len  := C_String_Length (Path_Addr);
1469
1470       if Path_Len = 0 then
1471          return null;
1472
1473       else
1474          Result := To_Path_String_Access (Path_Addr, Path_Len);
1475          Free (Path_Addr);
1476
1477          --  Always return an absolute path name
1478
1479          if not Is_Absolute_Path (Result.all) then
1480             declare
1481                Absolute_Path : constant String :=
1482                                  Normalize_Pathname (Result.all);
1483             begin
1484                Free (Result);
1485                Result := new String'(Absolute_Path);
1486             end;
1487          end if;
1488
1489          return Result;
1490       end if;
1491    end Locate_Exec_On_Path;
1492
1493    -------------------------
1494    -- Locate_Regular_File --
1495    -------------------------
1496
1497    function Locate_Regular_File
1498      (File_Name : C_File_Name;
1499       Path      : C_File_Name) return String_Access
1500    is
1501       function Locate_Regular_File
1502         (C_File_Name, Path_Val : Address) return Address;
1503       pragma Import (C, Locate_Regular_File, "__gnat_locate_regular_file");
1504
1505       procedure Free (Ptr : System.Address);
1506       pragma Import (C, Free, "free");
1507
1508       Path_Addr    : Address;
1509       Path_Len     : Integer;
1510       Result       : String_Access;
1511
1512    begin
1513       Path_Addr := Locate_Regular_File (File_Name, Path);
1514       Path_Len  := C_String_Length (Path_Addr);
1515
1516       if Path_Len = 0 then
1517          return null;
1518
1519       else
1520          Result := To_Path_String_Access (Path_Addr, Path_Len);
1521          Free (Path_Addr);
1522          return Result;
1523       end if;
1524    end Locate_Regular_File;
1525
1526    function Locate_Regular_File
1527      (File_Name : String;
1528       Path      : String) return String_Access
1529    is
1530       C_File_Name : String (1 .. File_Name'Length + 1);
1531       C_Path      : String (1 .. Path'Length + 1);
1532       Result      : String_Access;
1533
1534    begin
1535       C_File_Name (1 .. File_Name'Length)   := File_Name;
1536       C_File_Name (C_File_Name'Last)        := ASCII.NUL;
1537
1538       C_Path    (1 .. Path'Length)          := Path;
1539       C_Path    (C_Path'Last)               := ASCII.NUL;
1540
1541       Result := Locate_Regular_File (C_File_Name'Address, C_Path'Address);
1542
1543       --  Always return an absolute path name
1544
1545       if Result /= null and then not Is_Absolute_Path (Result.all) then
1546          declare
1547             Absolute_Path : constant String := Normalize_Pathname (Result.all);
1548          begin
1549             Free (Result);
1550             Result := new String'(Absolute_Path);
1551          end;
1552       end if;
1553
1554       return Result;
1555    end Locate_Regular_File;
1556
1557    ------------------------
1558    -- Non_Blocking_Spawn --
1559    ------------------------
1560
1561    function Non_Blocking_Spawn
1562      (Program_Name : String;
1563       Args         : Argument_List) return Process_Id
1564    is
1565       Pid  : Process_Id;
1566       Junk : Integer;
1567       pragma Warnings (Off, Junk);
1568    begin
1569       Spawn_Internal (Program_Name, Args, Junk, Pid, Blocking => False);
1570       return Pid;
1571    end Non_Blocking_Spawn;
1572
1573    function Non_Blocking_Spawn
1574      (Program_Name           : String;
1575       Args                   : Argument_List;
1576       Output_File_Descriptor : File_Descriptor;
1577       Err_To_Out             : Boolean := True) return Process_Id
1578    is
1579       Saved_Output : File_Descriptor;
1580       Saved_Error  : File_Descriptor := Invalid_FD; -- prevent warning
1581       Pid          : Process_Id;
1582
1583    begin
1584       if Output_File_Descriptor = Invalid_FD then
1585          return Invalid_Pid;
1586       end if;
1587
1588       --  Set standard output and, if specified, error to the temporary file
1589
1590       Saved_Output := Dup (Standout);
1591       Dup2 (Output_File_Descriptor, Standout);
1592
1593       if Err_To_Out then
1594          Saved_Error  := Dup (Standerr);
1595          Dup2 (Output_File_Descriptor, Standerr);
1596       end if;
1597
1598       --  Spawn the program
1599
1600       Pid := Non_Blocking_Spawn (Program_Name, Args);
1601
1602       --  Restore the standard output and error
1603
1604       Dup2 (Saved_Output, Standout);
1605
1606       if Err_To_Out then
1607          Dup2 (Saved_Error, Standerr);
1608       end if;
1609
1610       --  And close the saved standard output and error file descriptors
1611
1612       Close (Saved_Output);
1613
1614       if Err_To_Out then
1615          Close (Saved_Error);
1616       end if;
1617
1618       return Pid;
1619    end Non_Blocking_Spawn;
1620
1621    function Non_Blocking_Spawn
1622      (Program_Name : String;
1623       Args         : Argument_List;
1624       Output_File  : String;
1625       Err_To_Out   : Boolean := True) return Process_Id
1626    is
1627       Output_File_Descriptor : constant File_Descriptor :=
1628                                  Create_Output_Text_File (Output_File);
1629       Result : Process_Id;
1630
1631    begin
1632       --  Do not attempt to spawn if the output file could not be created
1633
1634       if Output_File_Descriptor = Invalid_FD then
1635          return Invalid_Pid;
1636
1637       else
1638          Result := Non_Blocking_Spawn
1639                      (Program_Name, Args, Output_File_Descriptor, Err_To_Out);
1640
1641          --  Close the file just created for the output, as the file descriptor
1642          --  cannot be used anywhere, being a local value. It is safe to do
1643          --  that, as the file descriptor has been duplicated to form
1644          --  standard output and error of the spawned process.
1645
1646          Close (Output_File_Descriptor);
1647
1648          return Result;
1649       end if;
1650    end Non_Blocking_Spawn;
1651
1652    -------------------------
1653    -- Normalize_Arguments --
1654    -------------------------
1655
1656    procedure Normalize_Arguments (Args : in out Argument_List) is
1657
1658       procedure Quote_Argument (Arg : in out String_Access);
1659       --  Add quote around argument if it contains spaces
1660
1661       C_Argument_Needs_Quote : Integer;
1662       pragma Import (C, C_Argument_Needs_Quote, "__gnat_argument_needs_quote");
1663       Argument_Needs_Quote : constant Boolean := C_Argument_Needs_Quote /= 0;
1664
1665       --------------------
1666       -- Quote_Argument --
1667       --------------------
1668
1669       procedure Quote_Argument (Arg : in out String_Access) is
1670          Res          : String (1 .. Arg'Length * 2);
1671          J            : Positive := 1;
1672          Quote_Needed : Boolean  := False;
1673
1674       begin
1675          if Arg (Arg'First) /= '"' or else Arg (Arg'Last) /= '"' then
1676
1677             --  Starting quote
1678
1679             Res (J) := '"';
1680
1681             for K in Arg'Range loop
1682
1683                J := J + 1;
1684
1685                if Arg (K) = '"' then
1686                   Res (J) := '\';
1687                   J := J + 1;
1688                   Res (J) := '"';
1689                   Quote_Needed := True;
1690
1691                elsif Arg (K) = ' ' then
1692                   Res (J) := Arg (K);
1693                   Quote_Needed := True;
1694
1695                else
1696                   Res (J) := Arg (K);
1697                end if;
1698             end loop;
1699
1700             if Quote_Needed then
1701
1702                --  Case of null terminated string
1703
1704                if Res (J) = ASCII.NUL then
1705
1706                   --  If the string ends with \, double it
1707
1708                   if Res (J - 1) = '\' then
1709                      Res (J) := '\';
1710                      J := J + 1;
1711                   end if;
1712
1713                   --  Put a quote just before the null at the end
1714
1715                   Res (J) := '"';
1716                   J := J + 1;
1717                   Res (J) := ASCII.NUL;
1718
1719                --  If argument is terminated by '\', then double it. Otherwise
1720                --  the ending quote will be taken as-is. This is quite strange
1721                --  spawn behavior from Windows, but this is what we see!
1722
1723                else
1724                   if Res (J) = '\' then
1725                      J := J + 1;
1726                      Res (J) := '\';
1727                   end if;
1728
1729                   --  Ending quote
1730
1731                   J := J + 1;
1732                   Res (J) := '"';
1733                end if;
1734
1735                declare
1736                   Old : String_Access := Arg;
1737
1738                begin
1739                   Arg := new String'(Res (1 .. J));
1740                   Free (Old);
1741                end;
1742             end if;
1743
1744          end if;
1745       end Quote_Argument;
1746
1747    --  Start of processing for Normalize_Arguments
1748
1749    begin
1750       if Argument_Needs_Quote then
1751          for K in Args'Range loop
1752             if Args (K) /= null and then Args (K)'Length /= 0 then
1753                Quote_Argument (Args (K));
1754             end if;
1755          end loop;
1756       end if;
1757    end Normalize_Arguments;
1758
1759    ------------------------
1760    -- Normalize_Pathname --
1761    ------------------------
1762
1763    function Normalize_Pathname
1764      (Name           : String;
1765       Directory      : String  := "";
1766       Resolve_Links  : Boolean := True;
1767       Case_Sensitive : Boolean := True) return String
1768    is
1769       Max_Path : Integer;
1770       pragma Import (C, Max_Path, "__gnat_max_path_len");
1771       --  Maximum length of a path name
1772
1773       procedure Get_Current_Dir
1774         (Dir    : System.Address;
1775          Length : System.Address);
1776       pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir");
1777
1778       Path_Buffer : String (1 .. Max_Path + Max_Path + 2);
1779       End_Path    : Natural := 0;
1780       Link_Buffer : String (1 .. Max_Path + 2);
1781       Status      : Integer;
1782       Last        : Positive;
1783       Start       : Natural;
1784       Finish      : Positive;
1785
1786       Max_Iterations : constant := 500;
1787
1788       function Get_File_Names_Case_Sensitive return Integer;
1789       pragma Import
1790         (C, Get_File_Names_Case_Sensitive,
1791          "__gnat_get_file_names_case_sensitive");
1792
1793       Fold_To_Lower_Case : constant Boolean :=
1794                              not Case_Sensitive
1795                                and then Get_File_Names_Case_Sensitive = 0;
1796
1797       function Readlink
1798         (Path   : System.Address;
1799          Buf    : System.Address;
1800          Bufsiz : Integer) return Integer;
1801       pragma Import (C, Readlink, "__gnat_readlink");
1802
1803       function To_Canonical_File_Spec
1804         (Host_File : System.Address) return System.Address;
1805       pragma Import
1806         (C, To_Canonical_File_Spec, "__gnat_to_canonical_file_spec");
1807
1808       The_Name : String (1 .. Name'Length + 1);
1809       Canonical_File_Addr : System.Address;
1810       Canonical_File_Len  : Integer;
1811
1812       function Strlen (S : System.Address) return Integer;
1813       pragma Import (C, Strlen, "strlen");
1814
1815       function Final_Value (S : String) return String;
1816       --  Make final adjustment to the returned string. This function strips
1817       --  trailing directory separators, and folds returned string to lower
1818       --  case if required.
1819
1820       function Get_Directory  (Dir : String) return String;
1821       --  If Dir is not empty, return it, adding a directory separator
1822       --  if not already present, otherwise return current working directory
1823       --  with terminating directory separator.
1824
1825       -----------------
1826       -- Final_Value --
1827       -----------------
1828
1829       function Final_Value (S : String) return String is
1830          S1 : String := S;
1831          --  We may need to fold S to lower case, so we need a variable
1832
1833          Last : Natural;
1834
1835       begin
1836          if Fold_To_Lower_Case then
1837             System.Case_Util.To_Lower (S1);
1838          end if;
1839
1840          --  Remove trailing directory separator, if any
1841
1842          Last := S1'Last;
1843
1844          if Last > 1
1845            and then (S1 (Last) = '/'
1846                        or else
1847                      S1 (Last) = Directory_Separator)
1848          then
1849             --  Special case for Windows: C:\
1850
1851             if Last = 3
1852               and then S1 (1) /= Directory_Separator
1853               and then S1 (2) = ':'
1854             then
1855                null;
1856
1857             else
1858                Last := Last - 1;
1859             end if;
1860          end if;
1861
1862          return S1 (1 .. Last);
1863       end Final_Value;
1864
1865       -------------------
1866       -- Get_Directory --
1867       -------------------
1868
1869       function Get_Directory (Dir : String) return String is
1870          Result : String (1 .. Dir'Length + 1);
1871          Length : constant Natural := Dir'Length;
1872
1873       begin
1874          --  Directory given, add directory separator if needed
1875
1876          if Length > 0 then
1877             Result (1 .. Length) := Dir;
1878
1879             --  On Windows, change all '/' to '\'
1880
1881             if On_Windows then
1882                for J in 1 .. Length loop
1883                   if Result (J) = '/' then
1884                      Result (J) := Directory_Separator;
1885                   end if;
1886                end loop;
1887             end if;
1888
1889             --  Add directory separator, if needed
1890
1891             if Result (Length) = Directory_Separator then
1892                return Result (1 .. Length);
1893             else
1894                Result (Result'Length) := Directory_Separator;
1895                return Result;
1896             end if;
1897
1898          --  Directory name not given, get current directory
1899
1900          else
1901             declare
1902                Buffer   : String (1 .. Max_Path + 2);
1903                Path_Len : Natural := Max_Path;
1904
1905             begin
1906                Get_Current_Dir (Buffer'Address, Path_Len'Address);
1907
1908                if Buffer (Path_Len) /= Directory_Separator then
1909                   Path_Len := Path_Len + 1;
1910                   Buffer (Path_Len) := Directory_Separator;
1911                end if;
1912
1913                --  By default, the drive letter on Windows is in upper case
1914
1915                if On_Windows
1916                  and then Path_Len >= 2
1917                  and then Buffer (2) = ':'
1918                then
1919                   System.Case_Util.To_Upper (Buffer (1 .. 1));
1920                end if;
1921
1922                return Buffer (1 .. Path_Len);
1923             end;
1924          end if;
1925       end Get_Directory;
1926
1927    --  Start of processing for Normalize_Pathname
1928
1929    begin
1930       --  Special case, if name is null, then return null
1931
1932       if Name'Length = 0 then
1933          return "";
1934       end if;
1935
1936       --  First, convert VMS file spec to Unix file spec.
1937       --  If Name is not in VMS syntax, then this is equivalent
1938       --  to put Name at the beginning of Path_Buffer.
1939
1940       VMS_Conversion : begin
1941          The_Name (1 .. Name'Length) := Name;
1942          The_Name (The_Name'Last) := ASCII.NUL;
1943
1944          Canonical_File_Addr := To_Canonical_File_Spec (The_Name'Address);
1945          Canonical_File_Len  := Strlen (Canonical_File_Addr);
1946
1947          --  If VMS syntax conversion has failed, return an empty string
1948          --  to indicate the failure.
1949
1950          if Canonical_File_Len = 0 then
1951             return "";
1952          end if;
1953
1954          declare
1955             subtype Path_String is String (1 .. Canonical_File_Len);
1956             type    Path_String_Access is access Path_String;
1957
1958             function Address_To_Access is new
1959                Ada.Unchecked_Conversion (Source => Address,
1960                                      Target => Path_String_Access);
1961
1962             Path_Access : constant Path_String_Access :=
1963                             Address_To_Access (Canonical_File_Addr);
1964
1965          begin
1966             Path_Buffer (1 .. Canonical_File_Len) := Path_Access.all;
1967             End_Path := Canonical_File_Len;
1968             Last := 1;
1969          end;
1970       end VMS_Conversion;
1971
1972       --  Replace all '/' by Directory Separators (this is for Windows)
1973
1974       if Directory_Separator /= '/' then
1975          for Index in 1 .. End_Path loop
1976             if Path_Buffer (Index) = '/' then
1977                Path_Buffer (Index) := Directory_Separator;
1978             end if;
1979          end loop;
1980       end if;
1981
1982       --  Resolve directory names for Windows (formerly also VMS)
1983
1984       --  On VMS, if we have a Unix path such as /temp/..., and TEMP is a
1985       --  logical name, we must not try to resolve this logical name, because
1986       --  it may have multiple equivalences and if resolved we will only
1987       --  get the first one.
1988
1989       if On_Windows then
1990
1991          --  On Windows, if we have an absolute path starting with a directory
1992          --  separator, we need to have the drive letter appended in front.
1993
1994          --  On Windows, Get_Current_Dir will return a suitable directory name
1995          --  (path starting with a drive letter on Windows). So we take this
1996          --  drive letter and prepend it to the current path.
1997
1998          if Path_Buffer (1) = Directory_Separator
1999            and then Path_Buffer (2) /= Directory_Separator
2000          then
2001             declare
2002                Cur_Dir : constant String := Get_Directory ("");
2003                --  Get the current directory to get the drive letter
2004
2005             begin
2006                if Cur_Dir'Length > 2
2007                  and then Cur_Dir (Cur_Dir'First + 1) = ':'
2008                then
2009                   Path_Buffer (3 .. End_Path + 2) :=
2010                     Path_Buffer (1 .. End_Path);
2011                   Path_Buffer (1 .. 2) :=
2012                     Cur_Dir (Cur_Dir'First .. Cur_Dir'First + 1);
2013                   End_Path := End_Path + 2;
2014                end if;
2015             end;
2016
2017          --  We have a drive letter, ensure it is upper-case
2018
2019          elsif Path_Buffer (1) in 'a' .. 'z'
2020            and then Path_Buffer (2) = ':'
2021          then
2022             System.Case_Util.To_Upper (Path_Buffer (1 .. 1));
2023          end if;
2024       end if;
2025
2026       --  On Windows, remove all double-quotes that are possibly part of the
2027       --  path but can cause problems with other methods.
2028
2029       if On_Windows then
2030          declare
2031             Index : Natural;
2032
2033          begin
2034             Index := Path_Buffer'First;
2035             for Current in Path_Buffer'First .. End_Path loop
2036                if Path_Buffer (Current) /= '"' then
2037                   Path_Buffer (Index) := Path_Buffer (Current);
2038                   Index := Index + 1;
2039                end if;
2040             end loop;
2041
2042             End_Path := Index - 1;
2043          end;
2044       end if;
2045
2046       --  Start the conversions
2047
2048       --  If this is not finished after Max_Iterations, give up and return an
2049       --  empty string.
2050
2051       for J in 1 .. Max_Iterations loop
2052
2053          --  If we don't have an absolute pathname, prepend the directory
2054          --  Reference_Dir.
2055
2056          if Last = 1
2057            and then not Is_Absolute_Path (Path_Buffer (1 .. End_Path))
2058          then
2059             declare
2060                Reference_Dir : constant String  := Get_Directory (Directory);
2061                Ref_Dir_Len   : constant Natural := Reference_Dir'Length;
2062                --  Current directory name specified and its length
2063
2064             begin
2065                Path_Buffer (Ref_Dir_Len + 1 .. Ref_Dir_Len + End_Path) :=
2066                  Path_Buffer (1 .. End_Path);
2067                End_Path := Ref_Dir_Len + End_Path;
2068                Path_Buffer (1 .. Ref_Dir_Len) := Reference_Dir;
2069                Last := Ref_Dir_Len;
2070             end;
2071          end if;
2072
2073          Start  := Last + 1;
2074          Finish := Last;
2075
2076          --  Ensure that Windows network drives are kept, e.g: \\server\drive-c
2077
2078          if Start = 2
2079            and then Directory_Separator = '\'
2080            and then Path_Buffer (1 .. 2) = "\\"
2081          then
2082             Start := 3;
2083          end if;
2084
2085          --  If we have traversed the full pathname, return it
2086
2087          if Start > End_Path then
2088             return Final_Value (Path_Buffer (1 .. End_Path));
2089          end if;
2090
2091          --  Remove duplicate directory separators
2092
2093          while Path_Buffer (Start) = Directory_Separator loop
2094             if Start = End_Path then
2095                return Final_Value (Path_Buffer (1 .. End_Path - 1));
2096
2097             else
2098                Path_Buffer (Start .. End_Path - 1) :=
2099                  Path_Buffer (Start + 1 .. End_Path);
2100                End_Path := End_Path - 1;
2101             end if;
2102          end loop;
2103
2104          --  Find the end of the current field: last character or the one
2105          --  preceding the next directory separator.
2106
2107          while Finish < End_Path
2108            and then Path_Buffer (Finish + 1) /= Directory_Separator
2109          loop
2110             Finish := Finish + 1;
2111          end loop;
2112
2113          --  Remove "." field
2114
2115          if Start = Finish and then Path_Buffer (Start) = '.' then
2116             if Start = End_Path then
2117                if Last = 1 then
2118                   return (1 => Directory_Separator);
2119                else
2120
2121                   if Fold_To_Lower_Case then
2122                      System.Case_Util.To_Lower (Path_Buffer (1 .. Last - 1));
2123                   end if;
2124
2125                   return Path_Buffer (1 .. Last - 1);
2126
2127                end if;
2128
2129             else
2130                Path_Buffer (Last + 1 .. End_Path - 2) :=
2131                  Path_Buffer (Last + 3 .. End_Path);
2132                End_Path := End_Path - 2;
2133             end if;
2134
2135          --  Remove ".." fields
2136
2137          elsif Finish = Start + 1
2138            and then Path_Buffer (Start .. Finish) = ".."
2139          then
2140             Start := Last;
2141             loop
2142                Start := Start - 1;
2143                exit when Start < 1
2144                  or else Path_Buffer (Start) = Directory_Separator;
2145             end loop;
2146
2147             if Start <= 1 then
2148                if Finish = End_Path then
2149                   return (1 => Directory_Separator);
2150
2151                else
2152                   Path_Buffer (1 .. End_Path - Finish) :=
2153                     Path_Buffer (Finish + 1 .. End_Path);
2154                   End_Path := End_Path - Finish;
2155                   Last := 1;
2156                end if;
2157
2158             else
2159                if Finish = End_Path then
2160                   return Final_Value (Path_Buffer (1 .. Start - 1));
2161
2162                else
2163                   Path_Buffer (Start + 1 .. Start + End_Path - Finish - 1) :=
2164                     Path_Buffer (Finish + 2 .. End_Path);
2165                   End_Path := Start + End_Path - Finish - 1;
2166                   Last := Start;
2167                end if;
2168             end if;
2169
2170          --  Check if current field is a symbolic link
2171
2172          elsif Resolve_Links then
2173             declare
2174                Saved : constant Character := Path_Buffer (Finish + 1);
2175
2176             begin
2177                Path_Buffer (Finish + 1) := ASCII.NUL;
2178                Status := Readlink (Path_Buffer'Address,
2179                                    Link_Buffer'Address,
2180                                    Link_Buffer'Length);
2181                Path_Buffer (Finish + 1) := Saved;
2182             end;
2183
2184             --  Not a symbolic link, move to the next field, if any
2185
2186             if Status <= 0 then
2187                Last := Finish + 1;
2188
2189             --  Replace symbolic link with its value
2190
2191             else
2192                if Is_Absolute_Path (Link_Buffer (1 .. Status)) then
2193                   Path_Buffer (Status + 1 .. End_Path - (Finish - Status)) :=
2194                   Path_Buffer (Finish + 1 .. End_Path);
2195                   End_Path := End_Path - (Finish - Status);
2196                   Path_Buffer (1 .. Status) := Link_Buffer (1 .. Status);
2197                   Last := 1;
2198
2199                else
2200                   Path_Buffer
2201                     (Last + Status + 1 .. End_Path - Finish + Last + Status) :=
2202                     Path_Buffer (Finish + 1 .. End_Path);
2203                   End_Path := End_Path - Finish + Last + Status;
2204                   Path_Buffer (Last + 1 .. Last + Status) :=
2205                     Link_Buffer (1 .. Status);
2206                end if;
2207             end if;
2208
2209          else
2210             Last := Finish + 1;
2211          end if;
2212       end loop;
2213
2214       --  Too many iterations: give up
2215
2216       --  This can happen when there is a circularity in the symbolic links: A
2217       --  is a symbolic link for B, which itself is a symbolic link, and the
2218       --  target of B or of another symbolic link target of B is A. In this
2219       --  case, we return an empty string to indicate failure to resolve.
2220
2221       return "";
2222    end Normalize_Pathname;
2223
2224    ---------------
2225    -- Open_Read --
2226    ---------------
2227
2228    function Open_Read
2229      (Name  : C_File_Name;
2230       Fmode : Mode) return File_Descriptor
2231    is
2232       function C_Open_Read
2233         (Name  : C_File_Name;
2234          Fmode : Mode) return File_Descriptor;
2235       pragma Import (C, C_Open_Read, "__gnat_open_read");
2236    begin
2237       return C_Open_Read (Name, Fmode);
2238    end Open_Read;
2239
2240    function Open_Read
2241      (Name  : String;
2242       Fmode : Mode) return File_Descriptor
2243    is
2244       C_Name : String (1 .. Name'Length + 1);
2245    begin
2246       C_Name (1 .. Name'Length) := Name;
2247       C_Name (C_Name'Last)      := ASCII.NUL;
2248       return Open_Read (C_Name (C_Name'First)'Address, Fmode);
2249    end Open_Read;
2250
2251    ---------------------
2252    -- Open_Read_Write --
2253    ---------------------
2254
2255    function Open_Read_Write
2256      (Name  : C_File_Name;
2257       Fmode : Mode) return File_Descriptor
2258    is
2259       function C_Open_Read_Write
2260         (Name  : C_File_Name;
2261          Fmode : Mode) return File_Descriptor;
2262       pragma Import (C, C_Open_Read_Write, "__gnat_open_rw");
2263    begin
2264       return C_Open_Read_Write (Name, Fmode);
2265    end Open_Read_Write;
2266
2267    function Open_Read_Write
2268      (Name  : String;
2269       Fmode : Mode) return File_Descriptor
2270    is
2271       C_Name : String (1 .. Name'Length + 1);
2272    begin
2273       C_Name (1 .. Name'Length) := Name;
2274       C_Name (C_Name'Last)      := ASCII.NUL;
2275       return Open_Read_Write (C_Name (C_Name'First)'Address, Fmode);
2276    end Open_Read_Write;
2277
2278    -------------
2279    -- OS_Exit --
2280    -------------
2281
2282    procedure OS_Exit (Status : Integer) is
2283    begin
2284       OS_Exit_Ptr (Status);
2285       raise Program_Error;
2286    end OS_Exit;
2287
2288    ---------------------
2289    -- OS_Exit_Default --
2290    ---------------------
2291
2292    procedure OS_Exit_Default (Status : Integer) is
2293       procedure GNAT_OS_Exit (Status : Integer);
2294       pragma Import (C, GNAT_OS_Exit, "__gnat_os_exit");
2295       pragma No_Return (GNAT_OS_Exit);
2296    begin
2297       GNAT_OS_Exit (Status);
2298    end OS_Exit_Default;
2299
2300    --------------------
2301    -- Pid_To_Integer --
2302    --------------------
2303
2304    function Pid_To_Integer (Pid : Process_Id) return Integer is
2305    begin
2306       return Integer (Pid);
2307    end Pid_To_Integer;
2308
2309    ----------
2310    -- Read --
2311    ----------
2312
2313    function Read
2314      (FD : File_Descriptor;
2315       A  : System.Address;
2316       N  : Integer) return Integer
2317    is
2318    begin
2319       return
2320         Integer (System.CRTL.read
2321                    (System.CRTL.int (FD),
2322                     System.CRTL.chars (A),
2323                     System.CRTL.size_t (N)));
2324    end Read;
2325
2326    -----------------
2327    -- Rename_File --
2328    -----------------
2329
2330    procedure Rename_File
2331      (Old_Name : C_File_Name;
2332       New_Name : C_File_Name;
2333       Success  : out Boolean)
2334    is
2335       function rename (From, To : Address) return Integer;
2336       pragma Import (C, rename, "__gnat_rename");
2337       R : Integer;
2338    begin
2339       R := rename (Old_Name, New_Name);
2340       Success := (R = 0);
2341    end Rename_File;
2342
2343    procedure Rename_File
2344      (Old_Name : String;
2345       New_Name : String;
2346       Success  : out Boolean)
2347    is
2348       C_Old_Name : String (1 .. Old_Name'Length + 1);
2349       C_New_Name : String (1 .. New_Name'Length + 1);
2350    begin
2351       C_Old_Name (1 .. Old_Name'Length) := Old_Name;
2352       C_Old_Name (C_Old_Name'Last)      := ASCII.NUL;
2353       C_New_Name (1 .. New_Name'Length) := New_Name;
2354       C_New_Name (C_New_Name'Last)      := ASCII.NUL;
2355       Rename_File (C_Old_Name'Address, C_New_Name'Address, Success);
2356    end Rename_File;
2357
2358    -----------------------
2359    -- Set_Close_On_Exec --
2360    -----------------------
2361
2362    procedure Set_Close_On_Exec
2363      (FD            : File_Descriptor;
2364       Close_On_Exec : Boolean;
2365       Status        : out Boolean)
2366    is
2367       function C_Set_Close_On_Exec
2368         (FD : File_Descriptor; Close_On_Exec : System.CRTL.int)
2369          return System.CRTL.int;
2370       pragma Import (C, C_Set_Close_On_Exec, "__gnat_set_close_on_exec");
2371    begin
2372       Status := C_Set_Close_On_Exec (FD, Boolean'Pos (Close_On_Exec)) = 0;
2373    end Set_Close_On_Exec;
2374
2375    --------------------
2376    -- Set_Executable --
2377    --------------------
2378
2379    procedure Set_Executable (Name : String) is
2380       procedure C_Set_Executable (Name : C_File_Name);
2381       pragma Import (C, C_Set_Executable, "__gnat_set_executable");
2382       C_Name : aliased String (Name'First .. Name'Last + 1);
2383    begin
2384       C_Name (Name'Range)  := Name;
2385       C_Name (C_Name'Last) := ASCII.NUL;
2386       C_Set_Executable (C_Name (C_Name'First)'Address);
2387    end Set_Executable;
2388
2389    ----------------------
2390    -- Set_Non_Readable --
2391    ----------------------
2392
2393    procedure Set_Non_Readable (Name : String) is
2394       procedure C_Set_Non_Readable (Name : C_File_Name);
2395       pragma Import (C, C_Set_Non_Readable, "__gnat_set_non_readable");
2396       C_Name : aliased String (Name'First .. Name'Last + 1);
2397    begin
2398       C_Name (Name'Range)  := Name;
2399       C_Name (C_Name'Last) := ASCII.NUL;
2400       C_Set_Non_Readable (C_Name (C_Name'First)'Address);
2401    end Set_Non_Readable;
2402
2403    ----------------------
2404    -- Set_Non_Writable --
2405    ----------------------
2406
2407    procedure Set_Non_Writable (Name : String) is
2408       procedure C_Set_Non_Writable (Name : C_File_Name);
2409       pragma Import (C, C_Set_Non_Writable, "__gnat_set_non_writable");
2410       C_Name : aliased String (Name'First .. Name'Last + 1);
2411    begin
2412       C_Name (Name'Range)  := Name;
2413       C_Name (C_Name'Last) := ASCII.NUL;
2414       C_Set_Non_Writable (C_Name (C_Name'First)'Address);
2415    end Set_Non_Writable;
2416
2417    ------------------
2418    -- Set_Readable --
2419    ------------------
2420
2421    procedure Set_Readable (Name : String) is
2422       procedure C_Set_Readable (Name : C_File_Name);
2423       pragma Import (C, C_Set_Readable, "__gnat_set_readable");
2424       C_Name : aliased String (Name'First .. Name'Last + 1);
2425    begin
2426       C_Name (Name'Range)  := Name;
2427       C_Name (C_Name'Last) := ASCII.NUL;
2428       C_Set_Readable (C_Name (C_Name'First)'Address);
2429    end Set_Readable;
2430
2431    --------------------
2432    -- Set_Writable --
2433    --------------------
2434
2435    procedure Set_Writable (Name : String) is
2436       procedure C_Set_Writable (Name : C_File_Name);
2437       pragma Import (C, C_Set_Writable, "__gnat_set_writable");
2438       C_Name : aliased String (Name'First .. Name'Last + 1);
2439    begin
2440       C_Name (Name'Range)  := Name;
2441       C_Name (C_Name'Last) := ASCII.NUL;
2442       C_Set_Writable (C_Name (C_Name'First)'Address);
2443    end Set_Writable;
2444
2445    ------------
2446    -- Setenv --
2447    ------------
2448
2449    procedure Setenv (Name : String; Value : String) is
2450       F_Name  : String (1 .. Name'Length + 1);
2451       F_Value : String (1 .. Value'Length + 1);
2452
2453       procedure Set_Env_Value (Name, Value : System.Address);
2454       pragma Import (C, Set_Env_Value, "__gnat_setenv");
2455
2456    begin
2457       F_Name (1 .. Name'Length) := Name;
2458       F_Name (F_Name'Last)      := ASCII.NUL;
2459
2460       F_Value (1 .. Value'Length) := Value;
2461       F_Value (F_Value'Last)      := ASCII.NUL;
2462
2463       Set_Env_Value (F_Name'Address, F_Value'Address);
2464    end Setenv;
2465
2466    -----------
2467    -- Spawn --
2468    -----------
2469
2470    function Spawn
2471      (Program_Name : String;
2472       Args         : Argument_List) return Integer
2473    is
2474       Result : Integer;
2475       Junk   : Process_Id;
2476       pragma Warnings (Off, Junk);
2477    begin
2478       Spawn_Internal (Program_Name, Args, Result, Junk, Blocking => True);
2479       return Result;
2480    end Spawn;
2481
2482    procedure Spawn
2483      (Program_Name : String;
2484       Args         : Argument_List;
2485       Success      : out Boolean)
2486    is
2487    begin
2488       Success := (Spawn (Program_Name, Args) = 0);
2489    end Spawn;
2490
2491    procedure Spawn
2492      (Program_Name           : String;
2493       Args                   : Argument_List;
2494       Output_File_Descriptor : File_Descriptor;
2495       Return_Code            : out Integer;
2496       Err_To_Out             : Boolean := True)
2497    is
2498       Saved_Output : File_Descriptor;
2499       Saved_Error  : File_Descriptor := Invalid_FD; -- prevent compiler warning
2500
2501    begin
2502       --  Set standard output and error to the temporary file
2503
2504       Saved_Output := Dup (Standout);
2505       Dup2 (Output_File_Descriptor, Standout);
2506
2507       if Err_To_Out then
2508          Saved_Error  := Dup (Standerr);
2509          Dup2 (Output_File_Descriptor, Standerr);
2510       end if;
2511
2512       --  Spawn the program
2513
2514       Return_Code := Spawn (Program_Name, Args);
2515
2516       --  Restore the standard output and error
2517
2518       Dup2 (Saved_Output, Standout);
2519
2520       if Err_To_Out then
2521          Dup2 (Saved_Error, Standerr);
2522       end if;
2523
2524       --  And close the saved standard output and error file descriptors
2525
2526       Close (Saved_Output);
2527
2528       if Err_To_Out then
2529          Close (Saved_Error);
2530       end if;
2531    end Spawn;
2532
2533    procedure Spawn
2534      (Program_Name : String;
2535       Args         : Argument_List;
2536       Output_File  : String;
2537       Success      : out Boolean;
2538       Return_Code  : out Integer;
2539       Err_To_Out   : Boolean := True)
2540    is
2541       FD : File_Descriptor;
2542
2543    begin
2544       Success := True;
2545       Return_Code := 0;
2546
2547       FD := Create_Output_Text_File (Output_File);
2548
2549       if FD = Invalid_FD then
2550          Success := False;
2551          return;
2552       end if;
2553
2554       Spawn (Program_Name, Args, FD, Return_Code, Err_To_Out);
2555
2556       Close (FD, Success);
2557    end Spawn;
2558
2559    --------------------
2560    -- Spawn_Internal --
2561    --------------------
2562
2563    procedure Spawn_Internal
2564      (Program_Name : String;
2565       Args         : Argument_List;
2566       Result       : out Integer;
2567       Pid          : out Process_Id;
2568       Blocking     : Boolean)
2569    is
2570
2571       procedure Spawn (Args : Argument_List);
2572       --  Call Spawn with given argument list
2573
2574       N_Args : Argument_List (Args'Range);
2575       --  Normalized arguments
2576
2577       -----------
2578       -- Spawn --
2579       -----------
2580
2581       procedure Spawn (Args : Argument_List) is
2582          type Chars is array (Positive range <>) of aliased Character;
2583          type Char_Ptr is access constant Character;
2584
2585          Command_Len  : constant Positive := Program_Name'Length + 1
2586                                                + Args_Length (Args);
2587          Command_Last : Natural := 0;
2588          Command      : aliased Chars (1 .. Command_Len);
2589          --  Command contains all characters of the Program_Name and Args, all
2590          --  terminated by ASCII.NUL characters.
2591
2592          Arg_List_Len  : constant Positive := Args'Length + 2;
2593          Arg_List_Last : Natural := 0;
2594          Arg_List      : aliased array (1 .. Arg_List_Len) of Char_Ptr;
2595          --  List with pointers to NUL-terminated strings of the Program_Name
2596          --  and the Args and terminated with a null pointer. We rely on the
2597          --  default initialization for the last null pointer.
2598
2599          procedure Add_To_Command (S : String);
2600          --  Add S and a NUL character to Command, updating Last
2601
2602          function Portable_Spawn (Args : Address) return Integer;
2603          pragma Import (C, Portable_Spawn, "__gnat_portable_spawn");
2604
2605          function Portable_No_Block_Spawn (Args : Address) return Process_Id;
2606          pragma Import
2607            (C, Portable_No_Block_Spawn, "__gnat_portable_no_block_spawn");
2608
2609          --------------------
2610          -- Add_To_Command --
2611          --------------------
2612
2613          procedure Add_To_Command (S : String) is
2614             First : constant Natural := Command_Last + 1;
2615
2616          begin
2617             Command_Last := Command_Last + S'Length;
2618
2619             --  Move characters one at a time, because Command has aliased
2620             --  components.
2621
2622             --  But not volatile, so why is this necessary ???
2623
2624             for J in S'Range loop
2625                Command (First + J - S'First) := S (J);
2626             end loop;
2627
2628             Command_Last := Command_Last + 1;
2629             Command (Command_Last) := ASCII.NUL;
2630
2631             Arg_List_Last := Arg_List_Last + 1;
2632             Arg_List (Arg_List_Last) := Command (First)'Access;
2633          end Add_To_Command;
2634
2635       --  Start of processing for Spawn
2636
2637       begin
2638          Add_To_Command (Program_Name);
2639
2640          for J in Args'Range loop
2641             Add_To_Command (Args (J).all);
2642          end loop;
2643
2644          if Blocking then
2645             Pid     := Invalid_Pid;
2646             Result  := Portable_Spawn (Arg_List'Address);
2647          else
2648             Pid     := Portable_No_Block_Spawn (Arg_List'Address);
2649             Result  := Boolean'Pos (Pid /= Invalid_Pid);
2650          end if;
2651       end Spawn;
2652
2653    --  Start of processing for Spawn_Internal
2654
2655    begin
2656       --  Copy arguments into a local structure
2657
2658       for K in N_Args'Range loop
2659          N_Args (K) := new String'(Args (K).all);
2660       end loop;
2661
2662       --  Normalize those arguments
2663
2664       Normalize_Arguments (N_Args);
2665
2666       --  Call spawn using the normalized arguments
2667
2668       Spawn (N_Args);
2669
2670       --  Free arguments list
2671
2672       for K in N_Args'Range loop
2673          Free (N_Args (K));
2674       end loop;
2675    end Spawn_Internal;
2676
2677    ---------------------------
2678    -- To_Path_String_Access --
2679    ---------------------------
2680
2681    function To_Path_String_Access
2682      (Path_Addr : Address;
2683       Path_Len  : Integer) return String_Access
2684    is
2685       subtype Path_String is String (1 .. Path_Len);
2686       type    Path_String_Access is access Path_String;
2687
2688       function Address_To_Access is new Ada.Unchecked_Conversion
2689         (Source => Address, Target => Path_String_Access);
2690
2691       Path_Access : constant Path_String_Access :=
2692                       Address_To_Access (Path_Addr);
2693
2694       Return_Val  : String_Access;
2695
2696    begin
2697       Return_Val := new String (1 .. Path_Len);
2698
2699       for J in 1 .. Path_Len loop
2700          Return_Val (J) := Path_Access (J);
2701       end loop;
2702
2703       return Return_Val;
2704    end To_Path_String_Access;
2705
2706    ------------------
2707    -- Wait_Process --
2708    ------------------
2709
2710    procedure Wait_Process (Pid : out Process_Id; Success : out Boolean) is
2711       Status : Integer;
2712
2713       function Portable_Wait (S : Address) return Process_Id;
2714       pragma Import (C, Portable_Wait, "__gnat_portable_wait");
2715
2716    begin
2717       Pid := Portable_Wait (Status'Address);
2718       Success := (Status = 0);
2719    end Wait_Process;
2720
2721    -----------
2722    -- Write --
2723    -----------
2724
2725    function Write
2726      (FD : File_Descriptor;
2727       A  : System.Address;
2728       N  : Integer) return Integer
2729    is
2730    begin
2731       return
2732         Integer (System.CRTL.write
2733                    (System.CRTL.int (FD),
2734                     System.CRTL.chars (A),
2735                     System.CRTL.size_t (N)));
2736    end Write;
2737
2738 end System.OS_Lib;