OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [gcc/] [ada/] [g-os_lib.adb] - Blame information for rev 20

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.