FSF GCC merge 02/23/03
[official-gcc.git] / gcc / ada / g-os_lib.adb
blob524c3d42892c10552642cf7f773a89d17955fb30
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T . O S _ L I B --
6 -- --
7 -- B o d y --
8 -- --
9 -- --
10 -- Copyright (C) 1995-2002 Ada Core Technologies, Inc. --
11 -- --
12 -- GNAT is free software; you can redistribute it and/or modify it under --
13 -- terms of the GNU General Public License as published by the Free Soft- --
14 -- ware Foundation; either version 2, or (at your option) any later ver- --
15 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
18 -- for more details. You should have received a copy of the GNU General --
19 -- Public License distributed with GNAT; see file COPYING. If not, write --
20 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
21 -- MA 02111-1307, USA. --
22 -- --
23 -- As a special exception, if other files instantiate generics from this --
24 -- unit, or you link this unit with other files to produce an executable, --
25 -- this unit does not by itself cause the resulting executable to be --
26 -- covered by the GNU General Public License. This exception does not --
27 -- however invalidate any other reasons why the executable file might be --
28 -- covered by the GNU Public License. --
29 -- --
30 -- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). --
31 -- --
32 ------------------------------------------------------------------------------
34 with System.Soft_Links;
35 with Unchecked_Conversion;
36 with System; use System;
38 package body GNAT.OS_Lib is
40 package SSL renames System.Soft_Links;
42 -----------------------
43 -- Local Subprograms --
44 -----------------------
46 function Args_Length (Args : Argument_List) return Natural;
47 -- Returns total number of characters needed to create a string
48 -- of all Args terminated by ASCII.NUL characters
50 function C_String_Length (S : Address) return Integer;
51 -- Returns the length of a C string. Does check for null address
52 -- (returns 0).
54 procedure Spawn_Internal
55 (Program_Name : String;
56 Args : Argument_List;
57 Result : out Integer;
58 Pid : out Process_Id;
59 Blocking : Boolean);
60 -- Internal routine to implement the two Spawn (blocking/non blocking)
61 -- routines. If Blocking is set to True then the spawn is blocking
62 -- otherwise it is non blocking. In this latter case the Pid contains
63 -- the process id number. The first three parameters are as in Spawn.
64 -- Note that Spawn_Internal normalizes the argument list before calling
65 -- the low level system spawn routines (see Normalize_Arguments). Note
66 -- that Normalize_Arguments is designed to do nothing if it is called
67 -- more than once, so calling Normalize_Arguments before calling one
68 -- of the spawn routines is fine.
70 function To_Path_String_Access
71 (Path_Addr : Address;
72 Path_Len : Integer)
73 return String_Access;
74 -- Converts a C String to an Ada String. We could do this making use of
75 -- Interfaces.C.Strings but we prefer not to import that entire package
77 -----------------
78 -- Args_Length --
79 -----------------
81 function Args_Length (Args : Argument_List) return Natural is
82 Len : Natural := 0;
84 begin
85 for J in Args'Range loop
86 Len := Len + Args (J)'Length + 1; -- One extra for ASCII.NUL
87 end loop;
89 return Len;
90 end Args_Length;
92 -----------------------------
93 -- Argument_String_To_List --
94 -----------------------------
96 function Argument_String_To_List
97 (Arg_String : String)
98 return Argument_List_Access
100 Max_Args : Integer := Arg_String'Length;
101 New_Argv : Argument_List (1 .. Max_Args);
102 New_Argc : Natural := 0;
103 Idx : Integer;
105 begin
106 Idx := Arg_String'First;
108 loop
109 declare
110 Quoted : Boolean := False;
111 Backqd : Boolean := False;
112 Old_Idx : Integer;
114 begin
115 Old_Idx := Idx;
117 loop
118 -- An unquoted space is the end of an argument
120 if not (Backqd or Quoted)
121 and then Arg_String (Idx) = ' '
122 then
123 exit;
125 -- Start of a quoted string
127 elsif not (Backqd or Quoted)
128 and then Arg_String (Idx) = '"'
129 then
130 Quoted := True;
132 -- End of a quoted string and end of an argument
134 elsif (Quoted and not Backqd)
135 and then Arg_String (Idx) = '"'
136 then
137 Idx := Idx + 1;
138 exit;
140 -- Following character is backquoted
142 elsif Arg_String (Idx) = '\' then
143 Backqd := True;
145 -- Turn off backquoting after advancing one character
147 elsif Backqd then
148 Backqd := False;
150 end if;
152 Idx := Idx + 1;
153 exit when Idx > Arg_String'Last;
154 end loop;
156 -- Found an argument
158 New_Argc := New_Argc + 1;
159 New_Argv (New_Argc) :=
160 new String'(Arg_String (Old_Idx .. Idx - 1));
162 -- Skip extraneous spaces
164 while Idx <= Arg_String'Last and then Arg_String (Idx) = ' ' loop
165 Idx := Idx + 1;
166 end loop;
167 end;
169 exit when Idx > Arg_String'Last;
170 end loop;
172 return new Argument_List'(New_Argv (1 .. New_Argc));
173 end Argument_String_To_List;
175 ---------------------
176 -- C_String_Length --
177 ---------------------
179 function C_String_Length (S : Address) return Integer is
180 function Strlen (S : Address) return Integer;
181 pragma Import (C, Strlen, "strlen");
183 begin
184 if S = Null_Address then
185 return 0;
186 else
187 return Strlen (S);
188 end if;
189 end C_String_Length;
191 -----------------
192 -- Create_File --
193 -----------------
195 function Create_File
196 (Name : C_File_Name;
197 Fmode : Mode)
198 return File_Descriptor
200 function C_Create_File
201 (Name : C_File_Name;
202 Fmode : Mode)
203 return File_Descriptor;
204 pragma Import (C, C_Create_File, "__gnat_open_create");
206 begin
207 return C_Create_File (Name, Fmode);
208 end Create_File;
210 function Create_File
211 (Name : String;
212 Fmode : Mode)
213 return File_Descriptor
215 C_Name : String (1 .. Name'Length + 1);
217 begin
218 C_Name (1 .. Name'Length) := Name;
219 C_Name (C_Name'Last) := ASCII.NUL;
220 return Create_File (C_Name (C_Name'First)'Address, Fmode);
221 end Create_File;
223 ---------------------
224 -- Create_New_File --
225 ---------------------
227 function Create_New_File
228 (Name : C_File_Name;
229 Fmode : Mode)
230 return File_Descriptor
232 function C_Create_New_File
233 (Name : C_File_Name;
234 Fmode : Mode)
235 return File_Descriptor;
236 pragma Import (C, C_Create_New_File, "__gnat_open_new");
238 begin
239 return C_Create_New_File (Name, Fmode);
240 end Create_New_File;
242 function Create_New_File
243 (Name : String;
244 Fmode : Mode)
245 return File_Descriptor
247 C_Name : String (1 .. Name'Length + 1);
249 begin
250 C_Name (1 .. Name'Length) := Name;
251 C_Name (C_Name'Last) := ASCII.NUL;
252 return Create_New_File (C_Name (C_Name'First)'Address, Fmode);
253 end Create_New_File;
255 ----------------------
256 -- Create_Temp_File --
257 ----------------------
259 procedure Create_Temp_File
260 (FD : out File_Descriptor;
261 Name : out Temp_File_Name)
263 function Open_New_Temp
264 (Name : System.Address;
265 Fmode : Mode)
266 return File_Descriptor;
267 pragma Import (C, Open_New_Temp, "__gnat_open_new_temp");
269 begin
270 FD := Open_New_Temp (Name'Address, Binary);
271 end Create_Temp_File;
273 -----------------
274 -- Delete_File --
275 -----------------
277 procedure Delete_File (Name : Address; Success : out Boolean) is
278 R : Integer;
280 function unlink (A : Address) return Integer;
281 pragma Import (C, unlink, "unlink");
283 begin
284 R := unlink (Name);
285 Success := (R = 0);
286 end Delete_File;
288 procedure Delete_File (Name : String; Success : out Boolean) is
289 C_Name : String (1 .. Name'Length + 1);
291 begin
292 C_Name (1 .. Name'Length) := Name;
293 C_Name (C_Name'Last) := ASCII.NUL;
295 Delete_File (C_Name'Address, Success);
296 end Delete_File;
298 ---------------------
299 -- File_Time_Stamp --
300 ---------------------
302 function File_Time_Stamp (FD : File_Descriptor) return OS_Time is
303 function File_Time (FD : File_Descriptor) return OS_Time;
304 pragma Import (C, File_Time, "__gnat_file_time_fd");
306 begin
307 return File_Time (FD);
308 end File_Time_Stamp;
310 function File_Time_Stamp (Name : C_File_Name) return OS_Time is
311 function File_Time (Name : Address) return OS_Time;
312 pragma Import (C, File_Time, "__gnat_file_time_name");
314 begin
315 return File_Time (Name);
316 end File_Time_Stamp;
318 function File_Time_Stamp (Name : String) return OS_Time is
319 F_Name : String (1 .. Name'Length + 1);
321 begin
322 F_Name (1 .. Name'Length) := Name;
323 F_Name (F_Name'Last) := ASCII.NUL;
324 return File_Time_Stamp (F_Name'Address);
325 end File_Time_Stamp;
327 ----------
328 -- Free --
329 ----------
331 procedure Free (Arg : in out String_List_Access) is
332 X : String_Access;
334 procedure Free_Array is new Unchecked_Deallocation
335 (Object => String_List, Name => String_List_Access);
337 begin
338 for J in Arg'Range loop
339 X := Arg (J);
340 Free (X);
341 end loop;
343 Free_Array (Arg);
344 end Free;
346 ---------------------------
347 -- Get_Debuggable_Suffix --
348 ---------------------------
350 function Get_Debuggable_Suffix return String_Access is
351 procedure Get_Suffix_Ptr (Length, Ptr : Address);
352 pragma Import (C, Get_Suffix_Ptr, "__gnat_get_debuggable_suffix_ptr");
354 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
355 pragma Import (C, Strncpy, "strncpy");
357 Suffix_Ptr : Address;
358 Suffix_Length : Integer;
359 Result : String_Access;
361 begin
362 Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
364 Result := new String (1 .. Suffix_Length);
366 if Suffix_Length > 0 then
367 Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
368 end if;
370 return Result;
371 end Get_Debuggable_Suffix;
373 ---------------------------
374 -- Get_Executable_Suffix --
375 ---------------------------
377 function Get_Executable_Suffix return String_Access is
378 procedure Get_Suffix_Ptr (Length, Ptr : Address);
379 pragma Import (C, Get_Suffix_Ptr, "__gnat_get_executable_suffix_ptr");
381 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
382 pragma Import (C, Strncpy, "strncpy");
384 Suffix_Ptr : Address;
385 Suffix_Length : Integer;
386 Result : String_Access;
388 begin
389 Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
391 Result := new String (1 .. Suffix_Length);
393 if Suffix_Length > 0 then
394 Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
395 end if;
397 return Result;
398 end Get_Executable_Suffix;
400 -----------------------
401 -- Get_Object_Suffix --
402 -----------------------
404 function Get_Object_Suffix return String_Access is
405 procedure Get_Suffix_Ptr (Length, Ptr : Address);
406 pragma Import (C, Get_Suffix_Ptr, "__gnat_get_object_suffix_ptr");
408 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
409 pragma Import (C, Strncpy, "strncpy");
411 Suffix_Ptr : Address;
412 Suffix_Length : Integer;
413 Result : String_Access;
415 begin
416 Get_Suffix_Ptr (Suffix_Length'Address, Suffix_Ptr'Address);
418 Result := new String (1 .. Suffix_Length);
420 if Suffix_Length > 0 then
421 Strncpy (Result.all'Address, Suffix_Ptr, Suffix_Length);
422 end if;
424 return Result;
425 end Get_Object_Suffix;
427 ------------
428 -- Getenv --
429 ------------
431 function Getenv (Name : String) return String_Access is
432 procedure Get_Env_Value_Ptr (Name, Length, Ptr : Address);
433 pragma Import (C, Get_Env_Value_Ptr, "__gnat_get_env_value_ptr");
435 procedure Strncpy (Astring_Addr, Cstring : Address; N : Integer);
436 pragma Import (C, Strncpy, "strncpy");
438 Env_Value_Ptr : Address;
439 Env_Value_Length : Integer;
440 F_Name : String (1 .. Name'Length + 1);
441 Result : String_Access;
443 begin
444 F_Name (1 .. Name'Length) := Name;
445 F_Name (F_Name'Last) := ASCII.NUL;
447 Get_Env_Value_Ptr
448 (F_Name'Address, Env_Value_Length'Address, Env_Value_Ptr'Address);
450 Result := new String (1 .. Env_Value_Length);
452 if Env_Value_Length > 0 then
453 Strncpy (Result.all'Address, Env_Value_Ptr, Env_Value_Length);
454 end if;
456 return Result;
457 end Getenv;
459 ------------
460 -- GM_Day --
461 ------------
463 function GM_Day (Date : OS_Time) return Day_Type is
464 Y : Year_Type;
465 Mo : Month_Type;
466 D : Day_Type;
467 H : Hour_Type;
468 Mn : Minute_Type;
469 S : Second_Type;
471 begin
472 GM_Split (Date, Y, Mo, D, H, Mn, S);
473 return D;
474 end GM_Day;
476 -------------
477 -- GM_Hour --
478 -------------
480 function GM_Hour (Date : OS_Time) return Hour_Type is
481 Y : Year_Type;
482 Mo : Month_Type;
483 D : Day_Type;
484 H : Hour_Type;
485 Mn : Minute_Type;
486 S : Second_Type;
488 begin
489 GM_Split (Date, Y, Mo, D, H, Mn, S);
490 return H;
491 end GM_Hour;
493 ---------------
494 -- GM_Minute --
495 ---------------
497 function GM_Minute (Date : OS_Time) return Minute_Type is
498 Y : Year_Type;
499 Mo : Month_Type;
500 D : Day_Type;
501 H : Hour_Type;
502 Mn : Minute_Type;
503 S : Second_Type;
505 begin
506 GM_Split (Date, Y, Mo, D, H, Mn, S);
507 return Mn;
508 end GM_Minute;
510 --------------
511 -- GM_Month --
512 --------------
514 function GM_Month (Date : OS_Time) return Month_Type is
515 Y : Year_Type;
516 Mo : Month_Type;
517 D : Day_Type;
518 H : Hour_Type;
519 Mn : Minute_Type;
520 S : Second_Type;
522 begin
523 GM_Split (Date, Y, Mo, D, H, Mn, S);
524 return Mo;
525 end GM_Month;
527 ---------------
528 -- GM_Second --
529 ---------------
531 function GM_Second (Date : OS_Time) return Second_Type is
532 Y : Year_Type;
533 Mo : Month_Type;
534 D : Day_Type;
535 H : Hour_Type;
536 Mn : Minute_Type;
537 S : Second_Type;
539 begin
540 GM_Split (Date, Y, Mo, D, H, Mn, S);
541 return S;
542 end GM_Second;
544 --------------
545 -- GM_Split --
546 --------------
548 procedure GM_Split
549 (Date : OS_Time;
550 Year : out Year_Type;
551 Month : out Month_Type;
552 Day : out Day_Type;
553 Hour : out Hour_Type;
554 Minute : out Minute_Type;
555 Second : out Second_Type)
557 procedure To_GM_Time
558 (P_Time_T, P_Year, P_Month, P_Day, P_Hours, P_Mins, P_Secs : Address);
559 pragma Import (C, To_GM_Time, "__gnat_to_gm_time");
561 T : OS_Time := Date;
562 Y : Integer;
563 Mo : Integer;
564 D : Integer;
565 H : Integer;
566 Mn : Integer;
567 S : Integer;
569 begin
570 -- Use the global lock because To_GM_Time is not thread safe.
572 Locked_Processing : begin
573 SSL.Lock_Task.all;
574 To_GM_Time
575 (T'Address, Y'Address, Mo'Address, D'Address,
576 H'Address, Mn'Address, S'Address);
577 SSL.Unlock_Task.all;
579 exception
580 when others =>
581 SSL.Unlock_Task.all;
582 raise;
583 end Locked_Processing;
585 Year := Y + 1900;
586 Month := Mo + 1;
587 Day := D;
588 Hour := H;
589 Minute := Mn;
590 Second := S;
591 end GM_Split;
593 -------------
594 -- GM_Year --
595 -------------
597 function GM_Year (Date : OS_Time) return Year_Type is
598 Y : Year_Type;
599 Mo : Month_Type;
600 D : Day_Type;
601 H : Hour_Type;
602 Mn : Minute_Type;
603 S : Second_Type;
605 begin
606 GM_Split (Date, Y, Mo, D, H, Mn, S);
607 return Y;
608 end GM_Year;
610 ----------------------
611 -- Is_Absolute_Path --
612 ----------------------
614 function Is_Absolute_Path (Name : String) return Boolean is
615 function Is_Absolute_Path (Name : Address) return Integer;
616 pragma Import (C, Is_Absolute_Path, "__gnat_is_absolute_path");
618 F_Name : String (1 .. Name'Length + 1);
620 begin
621 F_Name (1 .. Name'Length) := Name;
622 F_Name (F_Name'Last) := ASCII.NUL;
624 return Is_Absolute_Path (F_Name'Address) /= 0;
625 end Is_Absolute_Path;
627 ------------------
628 -- Is_Directory --
629 ------------------
631 function Is_Directory (Name : C_File_Name) return Boolean is
632 function Is_Directory (Name : Address) return Integer;
633 pragma Import (C, Is_Directory, "__gnat_is_directory");
635 begin
636 return Is_Directory (Name) /= 0;
637 end Is_Directory;
639 function Is_Directory (Name : String) return Boolean is
640 F_Name : String (1 .. Name'Length + 1);
642 begin
643 F_Name (1 .. Name'Length) := Name;
644 F_Name (F_Name'Last) := ASCII.NUL;
645 return Is_Directory (F_Name'Address);
646 end Is_Directory;
648 ---------------------
649 -- Is_Regular_File --
650 ---------------------
652 function Is_Regular_File (Name : C_File_Name) return Boolean is
653 function Is_Regular_File (Name : Address) return Integer;
654 pragma Import (C, Is_Regular_File, "__gnat_is_regular_file");
656 begin
657 return Is_Regular_File (Name) /= 0;
658 end Is_Regular_File;
660 function Is_Regular_File (Name : String) return Boolean is
661 F_Name : String (1 .. Name'Length + 1);
663 begin
664 F_Name (1 .. Name'Length) := Name;
665 F_Name (F_Name'Last) := ASCII.NUL;
666 return Is_Regular_File (F_Name'Address);
667 end Is_Regular_File;
669 ----------------------
670 -- Is_Writable_File --
671 ----------------------
673 function Is_Writable_File (Name : C_File_Name) return Boolean is
674 function Is_Writable_File (Name : Address) return Integer;
675 pragma Import (C, Is_Writable_File, "__gnat_is_writable_file");
677 begin
678 return Is_Writable_File (Name) /= 0;
679 end Is_Writable_File;
681 function Is_Writable_File (Name : String) return Boolean is
682 F_Name : String (1 .. Name'Length + 1);
684 begin
685 F_Name (1 .. Name'Length) := Name;
686 F_Name (F_Name'Last) := ASCII.NUL;
687 return Is_Writable_File (F_Name'Address);
688 end Is_Writable_File;
690 -------------------------
691 -- Locate_Exec_On_Path --
692 -------------------------
694 function Locate_Exec_On_Path
695 (Exec_Name : String)
696 return String_Access
698 function Locate_Exec_On_Path (C_Exec_Name : Address) return Address;
699 pragma Import (C, Locate_Exec_On_Path, "__gnat_locate_exec_on_path");
701 procedure Free (Ptr : System.Address);
702 pragma Import (C, Free, "free");
704 C_Exec_Name : String (1 .. Exec_Name'Length + 1);
705 Path_Addr : Address;
706 Path_Len : Integer;
707 Result : String_Access;
709 begin
710 C_Exec_Name (1 .. Exec_Name'Length) := Exec_Name;
711 C_Exec_Name (C_Exec_Name'Last) := ASCII.NUL;
713 Path_Addr := Locate_Exec_On_Path (C_Exec_Name'Address);
714 Path_Len := C_String_Length (Path_Addr);
716 if Path_Len = 0 then
717 return null;
719 else
720 Result := To_Path_String_Access (Path_Addr, Path_Len);
721 Free (Path_Addr);
722 return Result;
723 end if;
724 end Locate_Exec_On_Path;
726 -------------------------
727 -- Locate_Regular_File --
728 -------------------------
730 function Locate_Regular_File
731 (File_Name : C_File_Name;
732 Path : C_File_Name)
733 return String_Access
735 function Locate_Regular_File
736 (C_File_Name, Path_Val : Address) return Address;
737 pragma Import (C, Locate_Regular_File, "__gnat_locate_regular_file");
739 procedure Free (Ptr : System.Address);
740 pragma Import (C, Free, "free");
742 Path_Addr : Address;
743 Path_Len : Integer;
744 Result : String_Access;
746 begin
747 Path_Addr := Locate_Regular_File (File_Name, Path);
748 Path_Len := C_String_Length (Path_Addr);
750 if Path_Len = 0 then
751 return null;
752 else
753 Result := To_Path_String_Access (Path_Addr, Path_Len);
754 Free (Path_Addr);
755 return Result;
756 end if;
757 end Locate_Regular_File;
759 function Locate_Regular_File
760 (File_Name : String;
761 Path : String)
762 return String_Access
764 C_File_Name : String (1 .. File_Name'Length + 1);
765 C_Path : String (1 .. Path'Length + 1);
767 begin
768 C_File_Name (1 .. File_Name'Length) := File_Name;
769 C_File_Name (C_File_Name'Last) := ASCII.NUL;
771 C_Path (1 .. Path'Length) := Path;
772 C_Path (C_Path'Last) := ASCII.NUL;
774 return Locate_Regular_File (C_File_Name'Address, C_Path'Address);
775 end Locate_Regular_File;
777 ------------------------
778 -- Non_Blocking_Spawn --
779 ------------------------
781 function Non_Blocking_Spawn
782 (Program_Name : String;
783 Args : Argument_List)
784 return Process_Id
786 Junk : Integer;
787 Pid : Process_Id;
789 begin
790 Spawn_Internal (Program_Name, Args, Junk, Pid, Blocking => False);
791 return Pid;
792 end Non_Blocking_Spawn;
794 -------------------------
795 -- Normalize_Arguments --
796 -------------------------
798 procedure Normalize_Arguments (Args : in out Argument_List) is
800 procedure Quote_Argument (Arg : in out String_Access);
801 -- Add quote around argument if it contains spaces.
803 Argument_Needs_Quote : Boolean;
804 pragma Import (C, Argument_Needs_Quote, "__gnat_argument_needs_quote");
806 --------------------
807 -- Quote_Argument --
808 --------------------
810 procedure Quote_Argument (Arg : in out String_Access) is
811 Res : String (1 .. Arg'Length * 2);
812 J : Positive := 1;
813 Quote_Needed : Boolean := False;
815 begin
816 if Arg (Arg'First) /= '"' or else Arg (Arg'Last) /= '"' then
818 -- Starting quote
820 Res (J) := '"';
822 for K in Arg'Range loop
824 J := J + 1;
826 if Arg (K) = '"' then
827 Res (J) := '\';
828 J := J + 1;
829 Res (J) := '"';
831 elsif Arg (K) = ' ' then
832 Res (J) := Arg (K);
833 Quote_Needed := True;
835 else
836 Res (J) := Arg (K);
837 end if;
839 end loop;
841 if Quote_Needed then
843 -- Ending quote
845 J := J + 1;
846 Res (J) := '"';
848 declare
849 Old : String_Access := Arg;
851 begin
852 Arg := new String'(Res (1 .. J));
853 Free (Old);
854 end;
855 end if;
857 end if;
858 end Quote_Argument;
860 begin
861 if Argument_Needs_Quote then
862 for K in Args'Range loop
863 if Args (K) /= null then
864 Quote_Argument (Args (K));
865 end if;
866 end loop;
867 end if;
868 end Normalize_Arguments;
870 ------------------------
871 -- Normalize_Pathname --
872 ------------------------
874 function Normalize_Pathname
875 (Name : String;
876 Directory : String := "")
877 return String
879 Max_Path : Integer;
880 pragma Import (C, Max_Path, "__gnat_max_path_len");
881 -- Maximum length of a path name
883 procedure Get_Current_Dir
884 (Dir : System.Address;
885 Length : System.Address);
886 pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir");
888 Path_Buffer : String (1 .. Max_Path + Max_Path + 2);
889 End_Path : Natural := 0;
890 Link_Buffer : String (1 .. Max_Path + 2);
891 Status : Integer;
892 Last : Positive;
893 Start : Natural;
894 Finish : Positive;
896 Max_Iterations : constant := 500;
898 function Readlink
899 (Path : System.Address;
900 Buf : System.Address;
901 Bufsiz : Integer)
902 return Integer;
903 pragma Import (C, Readlink, "__gnat_readlink");
905 function To_Canonical_File_Spec
906 (Host_File : System.Address)
907 return System.Address;
908 pragma Import
909 (C, To_Canonical_File_Spec, "__gnat_to_canonical_file_spec");
911 The_Name : String (1 .. Name'Length + 1);
912 Canonical_File_Addr : System.Address;
913 Canonical_File_Len : Integer;
915 Need_To_Check_Drive_Letter : Boolean := False;
916 -- Set to true if Name is an absolute path that starts with "//"
918 function Strlen (S : System.Address) return Integer;
919 pragma Import (C, Strlen, "strlen");
921 function Get_Directory return String;
922 -- If Directory is not empty, return it, adding a directory separator
923 -- if not already present, otherwise return current working directory
924 -- with terminating directory separator.
926 function Final_Value (S : String) return String;
927 -- Make final adjustment to the returned string.
928 -- To compensate for non standard path name in Interix,
929 -- if S is "/x" or starts with "/x", where x is a capital
930 -- letter 'A' to 'Z', add an additional '/' at the beginning
931 -- so that the returned value starts with "//x".
933 -------------------
934 -- Get_Directory --
935 -------------------
937 function Get_Directory return String is
938 begin
939 -- Directory given, add directory separator if needed
941 if Directory'Length > 0 then
942 if Directory (Directory'Length) = Directory_Separator then
943 return Directory;
944 else
945 declare
946 Result : String (1 .. Directory'Length + 1);
948 begin
949 Result (1 .. Directory'Length) := Directory;
950 Result (Result'Length) := Directory_Separator;
951 return Result;
952 end;
953 end if;
955 -- Directory name not given, get current directory
957 else
958 declare
959 Buffer : String (1 .. Max_Path + 2);
960 Path_Len : Natural := Max_Path;
962 begin
963 Get_Current_Dir (Buffer'Address, Path_Len'Address);
965 if Buffer (Path_Len) /= Directory_Separator then
966 Path_Len := Path_Len + 1;
967 Buffer (Path_Len) := Directory_Separator;
968 end if;
970 return Buffer (1 .. Path_Len);
971 end;
972 end if;
973 end Get_Directory;
975 Reference_Dir : constant String := Get_Directory;
976 -- Current directory name specified
978 -----------------
979 -- Final_Value --
980 -----------------
982 function Final_Value (S : String) return String is
983 begin
984 -- Interix has the non standard notion of disk drive
985 -- indicated by two '/' followed by a capital letter
986 -- 'A' .. 'Z'. One of the two '/' may have been removed
987 -- by Normalize_Pathname. It has to be added again.
988 -- For other OSes, this should not make no difference.
990 if Need_To_Check_Drive_Letter
991 and then S'Length >= 2
992 and then S (S'First) = '/'
993 and then S (S'First + 1) in 'A' .. 'Z'
994 and then (S'Length = 2 or else S (S'First + 2) = '/')
995 then
996 declare
997 Result : String (1 .. S'Length + 1);
999 begin
1000 Result (1) := '/';
1001 Result (2 .. Result'Last) := S;
1002 return Result;
1003 end;
1005 else
1006 return S;
1007 end if;
1009 end Final_Value;
1011 -- Start of processing for Normalize_Pathname
1013 begin
1014 -- Special case, if name is null, then return null
1016 if Name'Length = 0 then
1017 return "";
1018 end if;
1020 -- First, convert VMS file spec to Unix file spec.
1021 -- If Name is not in VMS syntax, then this is equivalent
1022 -- to put Name at the begining of Path_Buffer.
1024 VMS_Conversion : begin
1025 The_Name (1 .. Name'Length) := Name;
1026 The_Name (The_Name'Last) := ASCII.NUL;
1028 Canonical_File_Addr := To_Canonical_File_Spec (The_Name'Address);
1029 Canonical_File_Len := Strlen (Canonical_File_Addr);
1031 -- If VMS syntax conversion has failed, return an empty string
1032 -- to indicate the failure.
1034 if Canonical_File_Len = 0 then
1035 return "";
1036 end if;
1038 declare
1039 subtype Path_String is String (1 .. Canonical_File_Len);
1040 type Path_String_Access is access Path_String;
1042 function Address_To_Access is new
1043 Unchecked_Conversion (Source => Address,
1044 Target => Path_String_Access);
1046 Path_Access : Path_String_Access :=
1047 Address_To_Access (Canonical_File_Addr);
1049 begin
1050 Path_Buffer (1 .. Canonical_File_Len) := Path_Access.all;
1051 End_Path := Canonical_File_Len;
1052 Last := 1;
1053 end;
1054 end VMS_Conversion;
1056 -- Replace all '/' by Directory Separators (this is for Windows)
1058 if Directory_Separator /= '/' then
1059 for Index in 1 .. End_Path loop
1060 if Path_Buffer (Index) = '/' then
1061 Path_Buffer (Index) := Directory_Separator;
1062 end if;
1063 end loop;
1064 end if;
1066 -- Start the conversions
1068 -- If this is not finished after Max_Iterations, give up and
1069 -- return an empty string.
1071 for J in 1 .. Max_Iterations loop
1073 -- If we don't have an absolute pathname, prepend
1074 -- the directory Reference_Dir.
1076 if Last = 1
1077 and then not Is_Absolute_Path (Path_Buffer (1 .. End_Path))
1078 then
1079 Path_Buffer
1080 (Reference_Dir'Last + 1 .. Reference_Dir'Length + End_Path) :=
1081 Path_Buffer (1 .. End_Path);
1082 End_Path := Reference_Dir'Length + End_Path;
1083 Path_Buffer (1 .. Reference_Dir'Length) := Reference_Dir;
1084 Last := Reference_Dir'Length;
1085 end if;
1087 -- If name starts with "//", we may have a drive letter on Interix
1089 if Last = 1 and then End_Path >= 3 then
1090 Need_To_Check_Drive_Letter := (Path_Buffer (1 .. 2)) = "//";
1091 end if;
1093 Start := Last + 1;
1094 Finish := Last;
1096 -- If we have traversed the full pathname, return it
1098 if Start > End_Path then
1099 return Final_Value (Path_Buffer (1 .. End_Path));
1100 end if;
1102 -- Remove duplicate directory separators
1104 while Path_Buffer (Start) = Directory_Separator loop
1105 if Start = End_Path then
1106 return Final_Value (Path_Buffer (1 .. End_Path - 1));
1108 else
1109 Path_Buffer (Start .. End_Path - 1) :=
1110 Path_Buffer (Start + 1 .. End_Path);
1111 End_Path := End_Path - 1;
1112 end if;
1113 end loop;
1115 -- Find the end of the current field: last character
1116 -- or the one preceding the next directory separator.
1118 while Finish < End_Path
1119 and then Path_Buffer (Finish + 1) /= Directory_Separator
1120 loop
1121 Finish := Finish + 1;
1122 end loop;
1124 -- Remove "." field
1126 if Start = Finish and then Path_Buffer (Start) = '.' then
1127 if Start = End_Path then
1128 if Last = 1 then
1129 return (1 => Directory_Separator);
1130 else
1131 return Path_Buffer (1 .. Last - 1);
1132 end if;
1134 else
1135 Path_Buffer (Last + 1 .. End_Path - 2) :=
1136 Path_Buffer (Last + 3 .. End_Path);
1137 End_Path := End_Path - 2;
1138 end if;
1140 -- Remove ".." fields
1142 elsif Finish = Start + 1
1143 and then Path_Buffer (Start .. Finish) = ".."
1144 then
1145 Start := Last;
1146 loop
1147 Start := Start - 1;
1148 exit when Start < 1 or else
1149 Path_Buffer (Start) = Directory_Separator;
1150 end loop;
1152 if Start <= 1 then
1153 if Finish = End_Path then
1154 return (1 => Directory_Separator);
1156 else
1157 Path_Buffer (1 .. End_Path - Finish) :=
1158 Path_Buffer (Finish + 1 .. End_Path);
1159 End_Path := End_Path - Finish;
1160 Last := 1;
1161 end if;
1163 else
1164 if Finish = End_Path then
1165 return Final_Value (Path_Buffer (1 .. Start - 1));
1167 else
1168 Path_Buffer (Start + 1 .. Start + End_Path - Finish - 1) :=
1169 Path_Buffer (Finish + 2 .. End_Path);
1170 End_Path := Start + End_Path - Finish - 1;
1171 Last := Start;
1172 end if;
1173 end if;
1175 -- Check if current field is a symbolic link
1177 else
1178 declare
1179 Saved : Character := Path_Buffer (Finish + 1);
1181 begin
1182 Path_Buffer (Finish + 1) := ASCII.NUL;
1183 Status := Readlink (Path_Buffer'Address,
1184 Link_Buffer'Address,
1185 Link_Buffer'Length);
1186 Path_Buffer (Finish + 1) := Saved;
1187 end;
1189 -- Not a symbolic link, move to the next field, if any
1191 if Status <= 0 then
1192 Last := Finish + 1;
1194 -- Replace symbolic link with its value.
1196 else
1197 if Is_Absolute_Path (Link_Buffer (1 .. Status)) then
1198 Path_Buffer (Status + 1 .. End_Path - (Finish - Status)) :=
1199 Path_Buffer (Finish + 1 .. End_Path);
1200 End_Path := End_Path - (Finish - Status);
1201 Path_Buffer (1 .. Status) := Link_Buffer (1 .. Status);
1202 Last := 1;
1204 else
1205 Path_Buffer
1206 (Last + Status + 1 .. End_Path - Finish + Last + Status) :=
1207 Path_Buffer (Finish + 1 .. End_Path);
1208 End_Path := End_Path - Finish + Last + Status;
1209 Path_Buffer (Last + 1 .. Last + Status) :=
1210 Link_Buffer (1 .. Status);
1211 end if;
1212 end if;
1213 end if;
1214 end loop;
1216 -- Too many iterations: give up
1218 -- This can happen when there is a circularity in the symbolic links:
1219 -- A is a symbolic link for B, which itself is a symbolic link, and
1220 -- the target of B or of another symbolic link target of B is A.
1221 -- In this case, we return an empty string to indicate failure to
1222 -- resolve.
1224 return "";
1225 end Normalize_Pathname;
1227 ---------------
1228 -- Open_Read --
1229 ---------------
1231 function Open_Read
1232 (Name : C_File_Name;
1233 Fmode : Mode)
1234 return File_Descriptor
1236 function C_Open_Read
1237 (Name : C_File_Name;
1238 Fmode : Mode)
1239 return File_Descriptor;
1240 pragma Import (C, C_Open_Read, "__gnat_open_read");
1242 begin
1243 return C_Open_Read (Name, Fmode);
1244 end Open_Read;
1246 function Open_Read
1247 (Name : String;
1248 Fmode : Mode)
1249 return File_Descriptor
1251 C_Name : String (1 .. Name'Length + 1);
1253 begin
1254 C_Name (1 .. Name'Length) := Name;
1255 C_Name (C_Name'Last) := ASCII.NUL;
1256 return Open_Read (C_Name (C_Name'First)'Address, Fmode);
1257 end Open_Read;
1259 ---------------------
1260 -- Open_Read_Write --
1261 ---------------------
1263 function Open_Read_Write
1264 (Name : C_File_Name;
1265 Fmode : Mode)
1266 return File_Descriptor
1268 function C_Open_Read_Write
1269 (Name : C_File_Name;
1270 Fmode : Mode)
1271 return File_Descriptor;
1272 pragma Import (C, C_Open_Read_Write, "__gnat_open_rw");
1274 begin
1275 return C_Open_Read_Write (Name, Fmode);
1276 end Open_Read_Write;
1278 function Open_Read_Write
1279 (Name : String;
1280 Fmode : Mode)
1281 return File_Descriptor
1283 C_Name : String (1 .. Name'Length + 1);
1285 begin
1286 C_Name (1 .. Name'Length) := Name;
1287 C_Name (C_Name'Last) := ASCII.NUL;
1288 return Open_Read_Write (C_Name (C_Name'First)'Address, Fmode);
1289 end Open_Read_Write;
1291 -----------------
1292 -- Rename_File --
1293 -----------------
1295 procedure Rename_File
1296 (Old_Name : C_File_Name;
1297 New_Name : C_File_Name;
1298 Success : out Boolean)
1300 function rename (From, To : Address) return Integer;
1301 pragma Import (C, rename, "rename");
1303 R : Integer;
1305 begin
1306 R := rename (Old_Name, New_Name);
1307 Success := (R = 0);
1308 end Rename_File;
1310 procedure Rename_File
1311 (Old_Name : String;
1312 New_Name : String;
1313 Success : out Boolean)
1315 C_Old_Name : String (1 .. Old_Name'Length + 1);
1316 C_New_Name : String (1 .. New_Name'Length + 1);
1318 begin
1319 C_Old_Name (1 .. Old_Name'Length) := Old_Name;
1320 C_Old_Name (C_Old_Name'Last) := ASCII.NUL;
1322 C_New_Name (1 .. New_Name'Length) := New_Name;
1323 C_New_Name (C_New_Name'Last) := ASCII.NUL;
1325 Rename_File (C_Old_Name'Address, C_New_Name'Address, Success);
1326 end Rename_File;
1328 ------------
1329 -- Setenv --
1330 ------------
1332 procedure Setenv (Name : String; Value : String) is
1333 F_Name : String (1 .. Name'Length + 1);
1334 F_Value : String (1 .. Value'Length + 1);
1336 procedure Set_Env_Value (Name, Value : System.Address);
1337 pragma Import (C, Set_Env_Value, "__gnat_set_env_value");
1339 begin
1340 F_Name (1 .. Name'Length) := Name;
1341 F_Name (F_Name'Last) := ASCII.NUL;
1343 F_Value (1 .. Value'Length) := Value;
1344 F_Value (F_Value'Last) := ASCII.NUL;
1346 Set_Env_Value (F_Name'Address, F_Value'Address);
1347 end Setenv;
1349 -----------
1350 -- Spawn --
1351 -----------
1353 function Spawn
1354 (Program_Name : String;
1355 Args : Argument_List)
1356 return Integer
1358 Junk : Process_Id;
1359 Result : Integer;
1361 begin
1362 Spawn_Internal (Program_Name, Args, Result, Junk, Blocking => True);
1363 return Result;
1364 end Spawn;
1366 procedure Spawn
1367 (Program_Name : String;
1368 Args : Argument_List;
1369 Success : out Boolean)
1371 begin
1372 Success := (Spawn (Program_Name, Args) = 0);
1373 end Spawn;
1375 --------------------
1376 -- Spawn_Internal --
1377 --------------------
1379 procedure Spawn_Internal
1380 (Program_Name : String;
1381 Args : Argument_List;
1382 Result : out Integer;
1383 Pid : out Process_Id;
1384 Blocking : Boolean)
1387 procedure Spawn (Args : Argument_List);
1388 -- Call Spawn.
1390 N_Args : Argument_List (Args'Range);
1391 -- Normalized arguments
1393 -----------
1394 -- Spawn --
1395 -----------
1397 procedure Spawn (Args : Argument_List) is
1398 type Chars is array (Positive range <>) of aliased Character;
1399 type Char_Ptr is access constant Character;
1401 Command_Len : constant Positive := Program_Name'Length + 1
1402 + Args_Length (Args);
1403 Command_Last : Natural := 0;
1404 Command : aliased Chars (1 .. Command_Len);
1405 -- Command contains all characters of the Program_Name and Args,
1406 -- all terminated by ASCII.NUL characters
1408 Arg_List_Len : constant Positive := Args'Length + 2;
1409 Arg_List_Last : Natural := 0;
1410 Arg_List : aliased array (1 .. Arg_List_Len) of Char_Ptr;
1411 -- List with pointers to NUL-terminated strings of the
1412 -- Program_Name and the Args and terminated with a null pointer.
1413 -- We rely on the default initialization for the last null pointer.
1415 procedure Add_To_Command (S : String);
1416 -- Add S and a NUL character to Command, updating Last
1418 function Portable_Spawn (Args : Address) return Integer;
1419 pragma Import (C, Portable_Spawn, "__gnat_portable_spawn");
1421 function Portable_No_Block_Spawn (Args : Address) return Process_Id;
1422 pragma Import
1423 (C, Portable_No_Block_Spawn, "__gnat_portable_no_block_spawn");
1425 --------------------
1426 -- Add_To_Command --
1427 --------------------
1429 procedure Add_To_Command (S : String) is
1430 First : constant Natural := Command_Last + 1;
1432 begin
1433 Command_Last := Command_Last + S'Length;
1435 -- Move characters one at a time, because Command has
1436 -- aliased components.
1438 for J in S'Range loop
1439 Command (First + J - S'First) := S (J);
1440 end loop;
1442 Command_Last := Command_Last + 1;
1443 Command (Command_Last) := ASCII.NUL;
1445 Arg_List_Last := Arg_List_Last + 1;
1446 Arg_List (Arg_List_Last) := Command (First)'Access;
1447 end Add_To_Command;
1449 -- Start of processing for Spawn
1451 begin
1452 Add_To_Command (Program_Name);
1454 for J in Args'Range loop
1455 Add_To_Command (Args (J).all);
1456 end loop;
1458 if Blocking then
1459 Pid := Invalid_Pid;
1460 Result := Portable_Spawn (Arg_List'Address);
1461 else
1462 Pid := Portable_No_Block_Spawn (Arg_List'Address);
1463 Result := Boolean'Pos (Pid /= Invalid_Pid);
1464 end if;
1465 end Spawn;
1467 -- Start of processing for Spawn_Internal
1469 begin
1470 -- Copy arguments into a local structure
1472 for K in N_Args'Range loop
1473 N_Args (K) := new String'(Args (K).all);
1474 end loop;
1476 -- Normalize those arguments
1478 Normalize_Arguments (N_Args);
1480 -- Call spawn using the normalized arguments
1482 Spawn (N_Args);
1484 -- Free arguments list
1486 for K in N_Args'Range loop
1487 Free (N_Args (K));
1488 end loop;
1489 end Spawn_Internal;
1491 ---------------------------
1492 -- To_Path_String_Access --
1493 ---------------------------
1495 function To_Path_String_Access
1496 (Path_Addr : Address;
1497 Path_Len : Integer)
1498 return String_Access
1500 subtype Path_String is String (1 .. Path_Len);
1501 type Path_String_Access is access Path_String;
1503 function Address_To_Access is new
1504 Unchecked_Conversion (Source => Address,
1505 Target => Path_String_Access);
1507 Path_Access : Path_String_Access := Address_To_Access (Path_Addr);
1509 Return_Val : String_Access;
1511 begin
1512 Return_Val := new String (1 .. Path_Len);
1514 for J in 1 .. Path_Len loop
1515 Return_Val (J) := Path_Access (J);
1516 end loop;
1518 return Return_Val;
1519 end To_Path_String_Access;
1521 ------------------
1522 -- Wait_Process --
1523 ------------------
1525 procedure Wait_Process (Pid : out Process_Id; Success : out Boolean) is
1526 Status : Integer;
1528 function Portable_Wait (S : Address) return Process_Id;
1529 pragma Import (C, Portable_Wait, "__gnat_portable_wait");
1531 begin
1532 Pid := Portable_Wait (Status'Address);
1533 Success := (Status = 0);
1534 end Wait_Process;
1536 end GNAT.OS_Lib;