| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- O S I N T -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1992-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Alloc; |
| with Debug; |
| with Fmap; use Fmap; |
| with Gnatvsn; use Gnatvsn; |
| with Hostparm; |
| with Opt; use Opt; |
| with Output; use Output; |
| with Sdefault; use Sdefault; |
| with Table; |
| with Targparm; use Targparm; |
| |
| with Ada.Unchecked_Conversion; |
| |
| pragma Warnings (Off); |
| -- This package is used also by gnatcoll |
| with System.Case_Util; use System.Case_Util; |
| with System.CRTL; |
| pragma Warnings (On); |
| |
| with GNAT.HTable; |
| |
| package body Osint is |
| |
| use type CRTL.size_t; |
| |
| Running_Program : Program_Type := Unspecified; |
| -- Set by Set_Program to indicate which of Compiler, Binder, etc is |
| -- running. |
| |
| Program_Set : Boolean := False; |
| -- True if Set_Program has been called; used to detect duplicate calls. |
| |
| Std_Prefix : String_Ptr; |
| -- Standard prefix, computed dynamically the first time Relocate_Path |
| -- is called, and cached for subsequent calls. |
| |
| Empty : aliased String := ""; |
| No_Dir : constant String_Ptr := Empty'Access; |
| -- Used in Locate_File as a fake directory when Name is already an |
| -- absolute path. |
| |
| ------------------------------------- |
| -- Use of Name_Find and Name_Enter -- |
| ------------------------------------- |
| |
| -- This package creates a number of source, ALI and object file names |
| -- that are used to locate the actual file and for the purpose of message |
| -- construction. These names need not be accessible by Name_Find, and can |
| -- be therefore created by using routine Name_Enter. The files in question |
| -- are file names with a prefix directory (i.e., the files not in the |
| -- current directory). File names without a prefix directory are entered |
| -- with Name_Find because special values might be attached to the various |
| -- Info fields of the corresponding name table entry. |
| |
| ----------------------- |
| -- Local Subprograms -- |
| ----------------------- |
| |
| function Append_Suffix_To_File_Name |
| (Name : File_Name_Type; |
| Suffix : String) return File_Name_Type; |
| -- Appends Suffix to Name and returns the new name |
| |
| function OS_Time_To_GNAT_Time (T : OS_Time) return Time_Stamp_Type; |
| -- Convert OS format time to GNAT format time stamp. If T is Invalid_Time, |
| -- then returns Empty_Time_Stamp. |
| -- Round to even seconds on Windows before conversion. |
| -- Windows ALI files had timestamps rounded to even seconds historically. |
| -- The rounding was originally done in GM_Split. Now that GM_Split no |
| -- longer does it, we are rounding it here only for ALI files. |
| |
| function Executable_Prefix return String_Ptr; |
| -- Returns the name of the root directory where the executable is stored. |
| -- The executable must be located in a directory called "bin", or under |
| -- root/lib/gcc-lib/..., or under root/libexec/gcc/... For example, if |
| -- executable is stored in directory "/foo/bar/bin", this routine returns |
| -- "/foo/bar/". Return "" if location is not recognized as described above. |
| |
| function File_Names_Equal (File1, File2 : String) return Boolean; |
| -- Compare File1 and File2 taking into account the case insensitivity |
| -- of the OS. |
| |
| function Update_Path (Path : String_Ptr) return String_Ptr; |
| -- Update the specified path to replace the prefix with the location where |
| -- GNAT is installed. See the file prefix.c in GCC for details. |
| |
| procedure Locate_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Dir : Natural; |
| Name : String; |
| Found : out File_Name_Type; |
| Attr : access File_Attributes); |
| -- See if the file N whose name is Name exists in directory Dir. Dir is an |
| -- index into the Lib_Search_Directories table if T = Library. Otherwise |
| -- if T = Source, Dir is an index into the Src_Search_Directories table. |
| -- Returns the File_Name_Type of the full file name if file found, or |
| -- No_File if not found. |
| -- |
| -- On exit, Found is set to the file that was found, and Attr to a cache of |
| -- its attributes (at least those that have been computed so far). Reusing |
| -- the cache will save some system calls. |
| -- |
| -- Attr is always reset in this call to Unknown_Attributes, even in case of |
| -- failure |
| |
| procedure Find_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Found : out File_Name_Type; |
| Attr : access File_Attributes; |
| Full_Name : Boolean := False); |
| -- A version of Find_File that also returns a cache of the file attributes |
| -- for later reuse |
| |
| procedure Smart_Find_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Found : out File_Name_Type; |
| Attr : out File_Attributes); |
| -- A version of Smart_Find_File that also returns a cache of the file |
| -- attributes for later reuse |
| |
| function C_String_Length (S : Address) return CRTL.size_t; |
| -- Returns length of a C string (zero for a null address) |
| |
| function To_Path_String_Access |
| (Path_Addr : Address; |
| Path_Len : CRTL.size_t) return String_Access; |
| -- Converts a C String to an Ada String. We don't use a more general |
| -- purpose facility, because we are dealing with low-level types like |
| -- Address. Caller must free result. |
| |
| function Include_Dir_Default_Prefix return String_Access; |
| -- Same as exported version, except returns a String_Access |
| |
| ------------------------------ |
| -- Other Local Declarations -- |
| ------------------------------ |
| |
| EOL : constant Character := ASCII.LF; |
| -- End of line character |
| |
| Number_File_Names : Nat := 0; |
| -- Number of file names found on command line and placed in File_Names |
| |
| Look_In_Primary_Directory_For_Current_Main : Boolean := False; |
| -- When this variable is True, Find_File only looks in Primary_Directory |
| -- for the Current_Main file. This variable is always set to True for the |
| -- compiler. It is also True for gnatmake, when the source name given on |
| -- the command line has directory information. |
| |
| Current_Full_Source_Name : File_Name_Type := No_File; |
| Current_Full_Source_Stamp : Time_Stamp_Type := Empty_Time_Stamp; |
| Current_Full_Lib_Name : File_Name_Type := No_File; |
| Current_Full_Lib_Stamp : Time_Stamp_Type := Empty_Time_Stamp; |
| Current_Full_Obj_Name : File_Name_Type := No_File; |
| Current_Full_Obj_Stamp : Time_Stamp_Type := Empty_Time_Stamp; |
| -- Respectively full name (with directory info) and time stamp of the |
| -- latest source, library and object files opened by Read_Source_File and |
| -- Read_Library_Info. |
| |
| package File_Name_Chars is new Table.Table ( |
| Table_Component_Type => Character, |
| Table_Index_Type => Int, |
| Table_Low_Bound => 1, |
| Table_Initial => Alloc.File_Name_Chars_Initial, |
| Table_Increment => Alloc.File_Name_Chars_Increment, |
| Table_Name => "File_Name_Chars"); |
| -- Table to store text to be printed by Dump_Source_File_Names |
| |
| The_Include_Dir_Default_Prefix : String_Access := null; |
| -- Value returned by Include_Dir_Default_Prefix. We don't initialize it |
| -- here, because that causes an elaboration cycle with Sdefault; we |
| -- initialize it lazily instead. |
| |
| ------------------ |
| -- Search Paths -- |
| ------------------ |
| |
| Primary_Directory : constant := 0; |
| -- This is index in the tables created below for the first directory to |
| -- search in for source or library information files. This is the directory |
| -- containing the latest main input file (a source file for the compiler or |
| -- a library file for the binder). |
| |
| package Src_Search_Directories is new Table.Table ( |
| Table_Component_Type => String_Ptr, |
| Table_Index_Type => Integer, |
| Table_Low_Bound => Primary_Directory, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Osint.Src_Search_Directories"); |
| -- Table of names of directories in which to search for source (Compiler) |
| -- files. This table is filled in the order in which the directories are |
| -- to be searched, and then used in that order. |
| |
| package Lib_Search_Directories is new Table.Table ( |
| Table_Component_Type => String_Ptr, |
| Table_Index_Type => Integer, |
| Table_Low_Bound => Primary_Directory, |
| Table_Initial => 10, |
| Table_Increment => 100, |
| Table_Name => "Osint.Lib_Search_Directories"); |
| -- Table of names of directories in which to search for library (Binder) |
| -- files. This table is filled in the order in which the directories are |
| -- to be searched and then used in that order. The reason for having two |
| -- distinct tables is that we need them both in gnatmake. |
| |
| --------------------- |
| -- File Hash Table -- |
| --------------------- |
| |
| -- The file hash table is provided to free the programmer from any |
| -- efficiency concern when retrieving full file names or time stamps of |
| -- source files. If the programmer calls Source_File_Data (Cache => True) |
| -- he is guaranteed that the price to retrieve the full name (i.e. with |
| -- directory info) or time stamp of the file will be payed only once, the |
| -- first time the full name is actually searched (or the first time the |
| -- time stamp is actually retrieved). This is achieved by employing a hash |
| -- table that stores as a key the File_Name_Type of the file and associates |
| -- to that File_Name_Type the full file name and time stamp of the file. |
| |
| File_Cache_Enabled : Boolean := False; |
| -- Set to true if you want the enable the file data caching mechanism |
| |
| type File_Hash_Num is range 0 .. 1020; |
| |
| function File_Hash (F : File_Name_Type) return File_Hash_Num; |
| -- Compute hash index for use by Simple_HTable |
| |
| type File_Info_Cache is record |
| File : File_Name_Type; |
| Attr : aliased File_Attributes; |
| end record; |
| |
| No_File_Info_Cache : constant File_Info_Cache := (No_File, (others => 0)); |
| |
| package File_Name_Hash_Table is new GNAT.HTable.Simple_HTable ( |
| Header_Num => File_Hash_Num, |
| Element => File_Info_Cache, |
| No_Element => No_File_Info_Cache, |
| Key => File_Name_Type, |
| Hash => File_Hash, |
| Equal => "="); |
| |
| function Smart_Find_File |
| (N : File_Name_Type; |
| T : File_Type) return File_Name_Type; |
| -- Exactly like Find_File except that if File_Cache_Enabled is True this |
| -- routine looks first in the hash table to see if the full name of the |
| -- file is already available. |
| |
| function Smart_File_Stamp |
| (N : File_Name_Type; |
| T : File_Type) return Time_Stamp_Type; |
| -- Takes the same parameter as the routine above (N is a file name without |
| -- any prefix directory information) and behaves like File_Stamp except |
| -- that if File_Cache_Enabled is True this routine looks first in the hash |
| -- table to see if the file stamp of the file is already available. |
| |
| ----------------------------- |
| -- Add_Default_Search_Dirs -- |
| ----------------------------- |
| |
| procedure Add_Default_Search_Dirs is |
| Search_Dir : String_Access; |
| Search_Path : String_Access; |
| Path_File_Name : String_Access; |
| |
| procedure Add_Search_Dir |
| (Search_Dir : String; |
| Additional_Source_Dir : Boolean); |
| procedure Add_Search_Dir |
| (Search_Dir : String_Access; |
| Additional_Source_Dir : Boolean); |
| -- Add a source search dir or a library search dir, depending on the |
| -- value of Additional_Source_Dir. |
| |
| procedure Get_Dirs_From_File (Additional_Source_Dir : Boolean); |
| -- Open a path file and read the directory to search, one per line |
| |
| function Get_Libraries_From_Registry return String_Ptr; |
| -- On Windows systems, get the list of installed standard libraries |
| -- from the registry key: |
| -- |
| -- HKEY_LOCAL_MACHINE\SOFTWARE\Ada Core Technologies\ |
| -- GNAT\Standard Libraries |
| -- Return an empty string on other systems. |
| -- |
| -- Note that this is an undocumented legacy feature, and that it |
| -- works only when using the default runtime library (i.e. no --RTS= |
| -- command line switch). |
| |
| -------------------- |
| -- Add_Search_Dir -- |
| -------------------- |
| |
| procedure Add_Search_Dir |
| (Search_Dir : String; |
| Additional_Source_Dir : Boolean) |
| is |
| begin |
| if Additional_Source_Dir then |
| Add_Src_Search_Dir (Search_Dir); |
| else |
| Add_Lib_Search_Dir (Search_Dir); |
| end if; |
| end Add_Search_Dir; |
| |
| procedure Add_Search_Dir |
| (Search_Dir : String_Access; |
| Additional_Source_Dir : Boolean) |
| is |
| begin |
| if Additional_Source_Dir then |
| Add_Src_Search_Dir (Search_Dir.all); |
| else |
| Add_Lib_Search_Dir (Search_Dir.all); |
| end if; |
| end Add_Search_Dir; |
| |
| ------------------------ |
| -- Get_Dirs_From_File -- |
| ------------------------ |
| |
| procedure Get_Dirs_From_File (Additional_Source_Dir : Boolean) is |
| File_FD : File_Descriptor; |
| Buffer : constant String := Path_File_Name.all & ASCII.NUL; |
| Len : Natural; |
| Actual_Len : Natural; |
| S : String_Access; |
| Curr : Natural; |
| First : Natural; |
| Ch : Character; |
| |
| Status : Boolean; |
| pragma Warnings (Off, Status); |
| -- For the call to Close where status is ignored |
| |
| begin |
| File_FD := Open_Read (Buffer'Address, Binary); |
| |
| -- If we cannot open the file, we ignore it, we don't fail |
| |
| if File_FD = Invalid_FD then |
| return; |
| end if; |
| |
| Len := Integer (File_Length (File_FD)); |
| |
| S := new String (1 .. Len); |
| |
| -- Read the file. Note that the loop is probably not necessary any |
| -- more since the whole file is read in at once on all targets. But |
| -- it is harmless and might be needed in future. |
| |
| Curr := 1; |
| Actual_Len := Len; |
| while Curr <= Len and then Actual_Len /= 0 loop |
| Actual_Len := Read (File_FD, S (Curr)'Address, Len); |
| Curr := Curr + Actual_Len; |
| end loop; |
| |
| -- We are done with the file, so we close it (ignore any error on |
| -- the close, since we have successfully read the file). |
| |
| Close (File_FD, Status); |
| |
| -- Now, we read line by line |
| |
| First := 1; |
| Curr := 0; |
| while Curr < Len loop |
| Ch := S (Curr + 1); |
| |
| if Ch = ASCII.CR or else Ch = ASCII.LF |
| or else Ch = ASCII.FF or else Ch = ASCII.VT |
| then |
| if First <= Curr then |
| Add_Search_Dir (S (First .. Curr), Additional_Source_Dir); |
| end if; |
| |
| First := Curr + 2; |
| end if; |
| |
| Curr := Curr + 1; |
| end loop; |
| |
| -- Last line is a special case, if the file does not end with |
| -- an end of line mark. |
| |
| if First <= S'Last then |
| Add_Search_Dir (S (First .. S'Last), Additional_Source_Dir); |
| end if; |
| end Get_Dirs_From_File; |
| |
| --------------------------------- |
| -- Get_Libraries_From_Registry -- |
| --------------------------------- |
| |
| function Get_Libraries_From_Registry return String_Ptr is |
| function C_Get_Libraries_From_Registry return Address; |
| pragma Import (C, C_Get_Libraries_From_Registry, |
| "__gnat_get_libraries_from_registry"); |
| |
| Result_Ptr : Address; |
| Result_Length : CRTL.size_t; |
| Out_String : String_Ptr; |
| |
| begin |
| Result_Ptr := C_Get_Libraries_From_Registry; |
| Result_Length := CRTL.strlen (Result_Ptr); |
| |
| Out_String := new String (1 .. Integer (Result_Length)); |
| CRTL.strncpy (Out_String.all'Address, Result_Ptr, Result_Length); |
| |
| CRTL.free (Result_Ptr); |
| |
| return Out_String; |
| end Get_Libraries_From_Registry; |
| |
| -- Start of processing for Add_Default_Search_Dirs |
| |
| begin |
| -- If there was a -gnateO switch, add all object directories from the |
| -- file given in argument to the library search list. |
| |
| if Object_Path_File_Name /= null then |
| Path_File_Name := String_Access (Object_Path_File_Name); |
| pragma Assert (Path_File_Name'Length > 0); |
| Get_Dirs_From_File (Additional_Source_Dir => False); |
| end if; |
| |
| -- After the locations specified on the command line, the next places |
| -- to look for files are the directories specified by the appropriate |
| -- environment variable. Get this value, extract the directory names |
| -- and store in the tables. |
| |
| -- Check for eventual project path file env vars |
| |
| Path_File_Name := Getenv (Project_Include_Path_File); |
| |
| if Path_File_Name'Length > 0 then |
| Get_Dirs_From_File (Additional_Source_Dir => True); |
| end if; |
| |
| Path_File_Name := Getenv (Project_Objects_Path_File); |
| |
| if Path_File_Name'Length > 0 then |
| Get_Dirs_From_File (Additional_Source_Dir => False); |
| end if; |
| |
| -- Put path name in canonical form |
| |
| for Additional_Source_Dir in False .. True loop |
| if Additional_Source_Dir then |
| Search_Path := Getenv (Ada_Include_Path); |
| |
| else |
| Search_Path := Getenv (Ada_Objects_Path); |
| |
| end if; |
| |
| Get_Next_Dir_In_Path_Init (Search_Path); |
| loop |
| Search_Dir := Get_Next_Dir_In_Path (Search_Path); |
| exit when Search_Dir = null; |
| Add_Search_Dir (Search_Dir, Additional_Source_Dir); |
| end loop; |
| end loop; |
| |
| -- For the compiler, if --RTS= was specified, add the runtime |
| -- directories. |
| |
| if RTS_Src_Path_Name /= null and then RTS_Lib_Path_Name /= null then |
| Add_Search_Dirs (RTS_Src_Path_Name, Include); |
| Add_Search_Dirs (RTS_Lib_Path_Name, Objects); |
| |
| else |
| if not Opt.No_Stdinc then |
| |
| -- For WIN32 systems, look for any system libraries defined in |
| -- the registry. These are added to both source and object |
| -- directories. |
| |
| Search_Path := String_Access (Get_Libraries_From_Registry); |
| |
| Get_Next_Dir_In_Path_Init (Search_Path); |
| loop |
| Search_Dir := Get_Next_Dir_In_Path (Search_Path); |
| exit when Search_Dir = null; |
| Add_Search_Dir (Search_Dir, False); |
| Add_Search_Dir (Search_Dir, True); |
| end loop; |
| |
| -- The last place to look are the defaults |
| |
| Search_Path := |
| Read_Default_Search_Dirs |
| (String_Access (Update_Path (Search_Dir_Prefix)), |
| Include_Search_File, |
| String_Access (Update_Path (Include_Dir_Default_Name))); |
| |
| Get_Next_Dir_In_Path_Init (Search_Path); |
| loop |
| Search_Dir := Get_Next_Dir_In_Path (Search_Path); |
| exit when Search_Dir = null; |
| Add_Search_Dir (Search_Dir, True); |
| end loop; |
| end if; |
| |
| -- Even when -nostdlib is used, we still want to have visibility on |
| -- the run-time object directory, as it is used by gnatbind to find |
| -- the run-time ALI files in "real" ZFP set up. |
| |
| if not Opt.RTS_Switch then |
| Search_Path := |
| Read_Default_Search_Dirs |
| (String_Access (Update_Path (Search_Dir_Prefix)), |
| Objects_Search_File, |
| String_Access (Update_Path (Object_Dir_Default_Name))); |
| |
| Get_Next_Dir_In_Path_Init (Search_Path); |
| loop |
| Search_Dir := Get_Next_Dir_In_Path (Search_Path); |
| exit when Search_Dir = null; |
| Add_Search_Dir (Search_Dir, False); |
| end loop; |
| end if; |
| end if; |
| end Add_Default_Search_Dirs; |
| |
| -------------- |
| -- Add_File -- |
| -------------- |
| |
| procedure Add_File (File_Name : String; Index : Int := No_Index) is |
| begin |
| Number_File_Names := Number_File_Names + 1; |
| |
| -- As Add_File may be called for mains specified inside a project file, |
| -- File_Names may be too short and needs to be extended. |
| |
| if Number_File_Names > File_Names'Last then |
| File_Names := new File_Name_Array'(File_Names.all & File_Names.all); |
| File_Indexes := |
| new File_Index_Array'(File_Indexes.all & File_Indexes.all); |
| end if; |
| |
| File_Names (Number_File_Names) := new String'(File_Name); |
| File_Indexes (Number_File_Names) := Index; |
| end Add_File; |
| |
| ------------------------ |
| -- Add_Lib_Search_Dir -- |
| ------------------------ |
| |
| procedure Add_Lib_Search_Dir (Dir : String) is |
| begin |
| if Dir'Length = 0 then |
| Fail ("missing library directory name"); |
| end if; |
| |
| declare |
| Norm : String_Ptr := Normalize_Directory_Name (Dir); |
| |
| begin |
| -- Do nothing if the directory is already in the list. This saves |
| -- system calls and avoid unneeded work |
| |
| for D in Lib_Search_Directories.First .. |
| Lib_Search_Directories.Last |
| loop |
| if Lib_Search_Directories.Table (D).all = Norm.all then |
| Free (Norm); |
| return; |
| end if; |
| end loop; |
| |
| Lib_Search_Directories.Increment_Last; |
| Lib_Search_Directories.Table (Lib_Search_Directories.Last) := Norm; |
| end; |
| end Add_Lib_Search_Dir; |
| |
| --------------------- |
| -- Add_Search_Dirs -- |
| --------------------- |
| |
| procedure Add_Search_Dirs |
| (Search_Path : String_Ptr; |
| Path_Type : Search_File_Type) |
| is |
| Current_Search_Path : String_Access; |
| |
| begin |
| Get_Next_Dir_In_Path_Init (String_Access (Search_Path)); |
| loop |
| Current_Search_Path := |
| Get_Next_Dir_In_Path (String_Access (Search_Path)); |
| exit when Current_Search_Path = null; |
| |
| if Path_Type = Include then |
| Add_Src_Search_Dir (Current_Search_Path.all); |
| else |
| Add_Lib_Search_Dir (Current_Search_Path.all); |
| end if; |
| end loop; |
| end Add_Search_Dirs; |
| |
| ------------------------ |
| -- Add_Src_Search_Dir -- |
| ------------------------ |
| |
| procedure Add_Src_Search_Dir (Dir : String) is |
| begin |
| if Dir'Length = 0 then |
| Fail ("missing source directory name"); |
| end if; |
| |
| Src_Search_Directories.Increment_Last; |
| Src_Search_Directories.Table (Src_Search_Directories.Last) := |
| Normalize_Directory_Name (Dir); |
| end Add_Src_Search_Dir; |
| |
| -------------------------------- |
| -- Append_Suffix_To_File_Name -- |
| -------------------------------- |
| |
| function Append_Suffix_To_File_Name |
| (Name : File_Name_Type; |
| Suffix : String) return File_Name_Type |
| is |
| begin |
| Get_Name_String (Name); |
| Name_Buffer (Name_Len + 1 .. Name_Len + Suffix'Length) := Suffix; |
| Name_Len := Name_Len + Suffix'Length; |
| return Name_Find; |
| end Append_Suffix_To_File_Name; |
| |
| --------------------- |
| -- C_String_Length -- |
| --------------------- |
| |
| function C_String_Length (S : Address) return CRTL.size_t is |
| begin |
| if S = Null_Address then |
| return 0; |
| else |
| return CRTL.strlen (S); |
| end if; |
| end C_String_Length; |
| |
| ------------------------------ |
| -- Canonical_Case_File_Name -- |
| ------------------------------ |
| |
| procedure Canonical_Case_File_Name (S : in out String) is |
| begin |
| if not File_Names_Case_Sensitive then |
| To_Lower (S); |
| end if; |
| end Canonical_Case_File_Name; |
| |
| --------------------------------- |
| -- Canonical_Case_Env_Var_Name -- |
| --------------------------------- |
| |
| procedure Canonical_Case_Env_Var_Name (S : in out String) is |
| begin |
| if not Env_Vars_Case_Sensitive then |
| To_Lower (S); |
| end if; |
| end Canonical_Case_Env_Var_Name; |
| |
| --------------------------- |
| -- Create_File_And_Check -- |
| --------------------------- |
| |
| procedure Create_File_And_Check |
| (Fdesc : out File_Descriptor; |
| Fmode : Mode) |
| is |
| begin |
| Output_File_Name := Name_Enter; |
| Fdesc := Create_File (Name_Buffer'Address, Fmode); |
| |
| if Fdesc = Invalid_FD then |
| Fail ("Cannot create: " & Name_Buffer (1 .. Name_Len)); |
| end if; |
| end Create_File_And_Check; |
| |
| ----------------------------------- |
| -- Open_File_To_Append_And_Check -- |
| ----------------------------------- |
| |
| procedure Open_File_To_Append_And_Check |
| (Fdesc : out File_Descriptor; |
| Fmode : Mode) |
| is |
| begin |
| Output_File_Name := Name_Enter; |
| Fdesc := Open_Append (Name_Buffer'Address, Fmode); |
| |
| if Fdesc = Invalid_FD then |
| Fail ("Cannot create: " & Name_Buffer (1 .. Name_Len)); |
| end if; |
| end Open_File_To_Append_And_Check; |
| |
| ------------------------ |
| -- Current_File_Index -- |
| ------------------------ |
| |
| function Current_File_Index return Int is |
| begin |
| return File_Indexes (Current_File_Name_Index); |
| end Current_File_Index; |
| |
| -------------------------------- |
| -- Current_Library_File_Stamp -- |
| -------------------------------- |
| |
| function Current_Library_File_Stamp return Time_Stamp_Type is |
| begin |
| return Current_Full_Lib_Stamp; |
| end Current_Library_File_Stamp; |
| |
| ------------------------------- |
| -- Current_Object_File_Stamp -- |
| ------------------------------- |
| |
| function Current_Object_File_Stamp return Time_Stamp_Type is |
| begin |
| return Current_Full_Obj_Stamp; |
| end Current_Object_File_Stamp; |
| |
| ------------------------------- |
| -- Current_Source_File_Stamp -- |
| ------------------------------- |
| |
| function Current_Source_File_Stamp return Time_Stamp_Type is |
| begin |
| return Current_Full_Source_Stamp; |
| end Current_Source_File_Stamp; |
| |
| ---------------------------- |
| -- Dir_In_Obj_Search_Path -- |
| ---------------------------- |
| |
| function Dir_In_Obj_Search_Path (Position : Natural) return String_Ptr is |
| begin |
| if Opt.Look_In_Primary_Dir then |
| return |
| Lib_Search_Directories.Table (Primary_Directory + Position - 1); |
| else |
| return Lib_Search_Directories.Table (Primary_Directory + Position); |
| end if; |
| end Dir_In_Obj_Search_Path; |
| |
| ---------------------------- |
| -- Dir_In_Src_Search_Path -- |
| ---------------------------- |
| |
| function Dir_In_Src_Search_Path (Position : Natural) return String_Ptr is |
| begin |
| if Opt.Look_In_Primary_Dir then |
| return |
| Src_Search_Directories.Table (Primary_Directory + Position - 1); |
| else |
| return Src_Search_Directories.Table (Primary_Directory + Position); |
| end if; |
| end Dir_In_Src_Search_Path; |
| |
| ----------------------------------------- |
| -- Dump_Command_Line_Source_File_Names -- |
| ----------------------------------------- |
| |
| procedure Dump_Command_Line_Source_File_Names is |
| begin |
| for J in 1 .. Number_Of_Files loop |
| Write_Str (File_Names (J).all & " "); |
| end loop; |
| end Dump_Command_Line_Source_File_Names; |
| |
| ---------------------------- |
| -- Dump_Source_File_Names -- |
| ---------------------------- |
| |
| procedure Dump_Source_File_Names is |
| subtype Rng is Int range File_Name_Chars.First .. File_Name_Chars.Last; |
| begin |
| Write_Str (String (File_Name_Chars.Table (Rng))); |
| end Dump_Source_File_Names; |
| |
| --------------------- |
| -- Executable_Name -- |
| --------------------- |
| |
| function Executable_Name |
| (Name : File_Name_Type; |
| Only_If_No_Suffix : Boolean := False) return File_Name_Type |
| is |
| Exec_Suffix : String_Access; |
| Add_Suffix : Boolean; |
| |
| begin |
| if Name = No_File then |
| return No_File; |
| end if; |
| |
| if Executable_Extension_On_Target = No_Name then |
| Exec_Suffix := Get_Target_Executable_Suffix; |
| else |
| Get_Name_String (Executable_Extension_On_Target); |
| Exec_Suffix := new String'(Name_Buffer (1 .. Name_Len)); |
| end if; |
| |
| if Exec_Suffix'Length /= 0 then |
| Get_Name_String (Name); |
| |
| Add_Suffix := True; |
| if Only_If_No_Suffix then |
| for J in reverse 1 .. Name_Len loop |
| if Name_Buffer (J) = '.' then |
| Add_Suffix := False; |
| exit; |
| |
| elsif Is_Directory_Separator (Name_Buffer (J)) then |
| exit; |
| end if; |
| end loop; |
| end if; |
| |
| if Add_Suffix then |
| -- If Executable doesn't end with the executable suffix, add it |
| |
| if Name_Len <= Exec_Suffix'Length |
| or else not |
| File_Names_Equal |
| (Name_Buffer |
| (Name_Len - Exec_Suffix'Length + 1 .. Name_Len), |
| Exec_Suffix.all) |
| then |
| Name_Buffer |
| (Name_Len + 1 .. Name_Len + Exec_Suffix'Length) := |
| Exec_Suffix.all; |
| Name_Len := Name_Len + Exec_Suffix'Length; |
| Free (Exec_Suffix); |
| return Name_Find; |
| end if; |
| end if; |
| end if; |
| |
| Free (Exec_Suffix); |
| return Name; |
| end Executable_Name; |
| |
| function Executable_Name |
| (Name : String; |
| Only_If_No_Suffix : Boolean := False) return String |
| is |
| Exec_Suffix : String_Access; |
| Add_Suffix : Boolean; |
| |
| begin |
| if Executable_Extension_On_Target = No_Name then |
| Exec_Suffix := Get_Target_Executable_Suffix; |
| else |
| Get_Name_String (Executable_Extension_On_Target); |
| Exec_Suffix := new String'(Name_Buffer (1 .. Name_Len)); |
| end if; |
| |
| if Exec_Suffix'Length = 0 then |
| Free (Exec_Suffix); |
| return Name; |
| |
| else |
| declare |
| Suffix : constant String := Exec_Suffix.all; |
| |
| begin |
| Free (Exec_Suffix); |
| Add_Suffix := True; |
| |
| if Only_If_No_Suffix then |
| for J in reverse Name'Range loop |
| if Name (J) = '.' then |
| Add_Suffix := False; |
| exit; |
| |
| elsif Is_Directory_Separator (Name (J)) then |
| exit; |
| end if; |
| end loop; |
| end if; |
| |
| if Add_Suffix and then |
| (Name'Length <= Suffix'Length |
| or else not |
| File_Names_Equal |
| (Name (Name'Last - Suffix'Length + 1 .. Name'Last), |
| Suffix)) |
| then |
| declare |
| Result : String (1 .. Name'Length + Suffix'Length); |
| begin |
| Result (1 .. Name'Length) := Name; |
| Result (Name'Length + 1 .. Result'Last) := Suffix; |
| return Result; |
| end; |
| else |
| return Name; |
| end if; |
| end; |
| end if; |
| end Executable_Name; |
| |
| ----------------------- |
| -- Executable_Prefix -- |
| ----------------------- |
| |
| function Executable_Prefix return String_Ptr is |
| |
| function Get_Install_Dir (Exec : String) return String_Ptr; |
| -- S is the executable name preceded by the absolute or relative |
| -- path, e.g. "c:\usr\bin\gcc.exe" or "..\bin\gcc". |
| |
| --------------------- |
| -- Get_Install_Dir -- |
| --------------------- |
| |
| function Get_Install_Dir (Exec : String) return String_Ptr is |
| Full_Path : constant String := Normalize_Pathname (Exec); |
| -- Use the full path, so that we find "lib" or "bin", even when |
| -- the tool has been invoked with a relative path, as in |
| -- "./gnatls -v" invoked in the GNAT bin directory. |
| |
| begin |
| for J in reverse Full_Path'Range loop |
| if Is_Directory_Separator (Full_Path (J)) then |
| if J < Full_Path'Last - 5 then |
| if (To_Lower (Full_Path (J + 1)) = 'l' |
| and then To_Lower (Full_Path (J + 2)) = 'i' |
| and then To_Lower (Full_Path (J + 3)) = 'b') |
| or else |
| (To_Lower (Full_Path (J + 1)) = 'b' |
| and then To_Lower (Full_Path (J + 2)) = 'i' |
| and then To_Lower (Full_Path (J + 3)) = 'n') |
| then |
| return new String'(Full_Path (Full_Path'First .. J)); |
| end if; |
| end if; |
| end if; |
| end loop; |
| |
| return new String'(""); |
| end Get_Install_Dir; |
| |
| -- Start of processing for Executable_Prefix |
| |
| begin |
| if Exec_Name = null then |
| Exec_Name := new String (1 .. Len_Arg (0)); |
| Osint.Fill_Arg (Exec_Name (1)'Address, 0); |
| end if; |
| |
| -- First determine if a path prefix was placed in front of the |
| -- executable name. |
| |
| for J in reverse Exec_Name'Range loop |
| if Is_Directory_Separator (Exec_Name (J)) then |
| return Get_Install_Dir (Exec_Name.all); |
| end if; |
| end loop; |
| |
| -- If we come here, the user has typed the executable name with no |
| -- directory prefix. |
| |
| return Get_Install_Dir (Locate_Exec_On_Path (Exec_Name.all).all); |
| end Executable_Prefix; |
| |
| ------------------ |
| -- Exit_Program -- |
| ------------------ |
| |
| procedure Exit_Program (Exit_Code : Exit_Code_Type) is |
| begin |
| -- The program will exit with the following status: |
| |
| -- 0 if the object file has been generated (with or without warnings) |
| -- 1 if recompilation was not needed (smart recompilation) |
| -- 2 if gnat1 has been killed by a signal (detected by GCC) |
| -- 4 for a fatal error |
| -- 5 if there were errors |
| -- 6 if no code has been generated (spec) |
| |
| -- Note that exit code 3 is not used and must not be used as this is |
| -- the code returned by a program aborted via C abort() routine on |
| -- Windows. GCC checks for that case and thinks that the child process |
| -- has been aborted. This code (exit code 3) used to be the code used |
| -- for E_No_Code, but E_No_Code was changed to 6 for this reason. |
| |
| case Exit_Code is |
| when E_Success => OS_Exit (0); |
| when E_Warnings => OS_Exit (0); |
| when E_No_Compile => OS_Exit (1); |
| when E_Fatal => OS_Exit (4); |
| when E_Errors => OS_Exit (5); |
| when E_No_Code => OS_Exit (6); |
| when E_Abort => OS_Abort; |
| end case; |
| end Exit_Program; |
| |
| ---------- |
| -- Fail -- |
| ---------- |
| |
| procedure Fail (S : String) is |
| begin |
| -- We use Output in case there is a special output set up. In this case |
| -- Set_Standard_Error will have no immediate effect. |
| |
| Set_Standard_Error; |
| Osint.Write_Program_Name; |
| Write_Str (": "); |
| Write_Str (S); |
| Write_Eol; |
| |
| Exit_Program (E_Fatal); |
| end Fail; |
| |
| ---------------------- |
| -- File_Names_Equal -- |
| ---------------------- |
| |
| function File_Names_Equal (File1, File2 : String) return Boolean is |
| |
| function To_Lower (A : String) return String; |
| -- For bootstrap reasons, we cannot use To_Lower function from |
| -- System.Case_Util. |
| |
| -------------- |
| -- To_Lower -- |
| -------------- |
| |
| function To_Lower (A : String) return String is |
| Result : String := A; |
| begin |
| To_Lower (Result); |
| return Result; |
| end To_Lower; |
| |
| -- Start of processing for File_Names_Equal |
| |
| begin |
| if File_Names_Case_Sensitive then |
| return File1 = File2; |
| else |
| return To_Lower (File1) = To_Lower (File2); |
| end if; |
| end File_Names_Equal; |
| |
| --------------- |
| -- File_Hash -- |
| --------------- |
| |
| function File_Hash (F : File_Name_Type) return File_Hash_Num is |
| begin |
| return File_Hash_Num (Int (F) mod File_Hash_Num'Range_Length); |
| end File_Hash; |
| |
| ----------------- |
| -- File_Length -- |
| ----------------- |
| |
| function File_Length |
| (Name : C_File_Name; |
| Attr : access File_Attributes) return Long_Integer |
| is |
| function Internal |
| (F : Integer; |
| N : C_File_Name; |
| A : System.Address) return CRTL.int64; |
| pragma Import (C, Internal, "__gnat_file_length_attr"); |
| |
| begin |
| -- The conversion from int64 to Long_Integer is ok here as this |
| -- routine is only to be used by the compiler and we do not expect |
| -- a unit to be larger than a 32bit integer. |
| |
| return Long_Integer (Internal (-1, Name, Attr.all'Address)); |
| end File_Length; |
| |
| --------------------- |
| -- File_Time_Stamp -- |
| --------------------- |
| |
| function File_Time_Stamp |
| (Name : C_File_Name; |
| Attr : access File_Attributes) return OS_Time |
| is |
| function Internal (N : C_File_Name; A : System.Address) return OS_Time; |
| pragma Import (C, Internal, "__gnat_file_time_name_attr"); |
| begin |
| return Internal (Name, Attr.all'Address); |
| end File_Time_Stamp; |
| |
| function File_Time_Stamp |
| (Name : Path_Name_Type; |
| Attr : access File_Attributes) return Time_Stamp_Type |
| is |
| begin |
| if Name = No_Path then |
| return Empty_Time_Stamp; |
| end if; |
| |
| Get_Name_String (Name); |
| Name_Buffer (Name_Len + 1) := ASCII.NUL; |
| return OS_Time_To_GNAT_Time |
| (File_Time_Stamp (Name_Buffer'Address, Attr)); |
| end File_Time_Stamp; |
| |
| ---------------- |
| -- File_Stamp -- |
| ---------------- |
| |
| function File_Stamp (Name : File_Name_Type) return Time_Stamp_Type is |
| begin |
| if Name = No_File then |
| return Empty_Time_Stamp; |
| end if; |
| |
| Get_Name_String (Name); |
| |
| -- File_Time_Stamp will always return Invalid_Time if the file does |
| -- not exist, and OS_Time_To_GNAT_Time will convert this value to |
| -- Empty_Time_Stamp. Therefore we do not need to first test whether |
| -- the file actually exists, which saves a system call. |
| |
| return OS_Time_To_GNAT_Time |
| (File_Time_Stamp (Name_Buffer (1 .. Name_Len))); |
| end File_Stamp; |
| |
| function File_Stamp (Name : Path_Name_Type) return Time_Stamp_Type is |
| begin |
| return File_Stamp (File_Name_Type (Name)); |
| end File_Stamp; |
| |
| --------------- |
| -- Find_File -- |
| --------------- |
| |
| function Find_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Full_Name : Boolean := False) return File_Name_Type |
| is |
| Attr : aliased File_Attributes; |
| Found : File_Name_Type; |
| begin |
| Find_File (N, T, Found, Attr'Access, Full_Name); |
| return Found; |
| end Find_File; |
| |
| --------------- |
| -- Find_File -- |
| --------------- |
| |
| procedure Find_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Found : out File_Name_Type; |
| Attr : access File_Attributes; |
| Full_Name : Boolean := False) |
| is |
| begin |
| Get_Name_String (N); |
| |
| declare |
| File_Name : String renames Name_Buffer (1 .. Name_Len); |
| File : File_Name_Type := No_File; |
| Last_Dir : Natural; |
| |
| begin |
| -- If we are looking for a config file, look only in the current |
| -- directory, i.e. return input argument unchanged. Also look only in |
| -- the current directory if we are looking for a .dg file (happens in |
| -- -gnatD mode). |
| |
| if T = Config |
| or else (Debug_Generated_Code |
| and then Name_Len > 3 |
| and then Name_Buffer (Name_Len - 2 .. Name_Len) = ".dg") |
| then |
| Found := N; |
| Attr.all := Unknown_Attributes; |
| |
| if T = Config then |
| if Full_Name then |
| declare |
| Full_Path : constant String := |
| Normalize_Pathname (Get_Name_String (N)); |
| Full_Size : constant Natural := Full_Path'Length; |
| |
| begin |
| Name_Buffer (1 .. Full_Size) := Full_Path; |
| Name_Len := Full_Size; |
| Found := Name_Find; |
| end; |
| end if; |
| |
| -- Check that it is a file, not a directory |
| |
| if not Is_Regular_File (Get_Name_String (Found)) then |
| Found := No_File; |
| end if; |
| end if; |
| |
| return; |
| |
| -- If we are trying to find the current main file just look in the |
| -- directory where the user said it was. |
| |
| elsif Look_In_Primary_Directory_For_Current_Main |
| and then Current_Main = N |
| then |
| Locate_File (N, T, Primary_Directory, File_Name, Found, Attr); |
| return; |
| |
| -- Otherwise do standard search for source file |
| |
| else |
| -- Check the mapping of this file name |
| |
| File := Mapped_Path_Name (N); |
| |
| -- If the file name is mapped to a path name, return the |
| -- corresponding path name |
| |
| if File /= No_File then |
| |
| -- For locally removed file, Error_Name is returned; then |
| -- return No_File, indicating the file is not a source. |
| |
| if File = Error_File_Name then |
| Found := No_File; |
| else |
| Found := File; |
| end if; |
| |
| Attr.all := Unknown_Attributes; |
| return; |
| end if; |
| |
| -- First place to look is in the primary directory (i.e. the same |
| -- directory as the source) unless this has been disabled with -I- |
| |
| if Opt.Look_In_Primary_Dir then |
| Locate_File (N, T, Primary_Directory, File_Name, Found, Attr); |
| |
| if Found /= No_File then |
| return; |
| end if; |
| end if; |
| |
| -- Finally look in directories specified with switches -I/-aI/-aO |
| |
| if T = Library then |
| Last_Dir := Lib_Search_Directories.Last; |
| else |
| Last_Dir := Src_Search_Directories.Last; |
| end if; |
| |
| for D in Primary_Directory + 1 .. Last_Dir loop |
| Locate_File (N, T, D, File_Name, Found, Attr); |
| |
| if Found /= No_File then |
| return; |
| end if; |
| end loop; |
| |
| Attr.all := Unknown_Attributes; |
| Found := No_File; |
| end if; |
| end; |
| end Find_File; |
| |
| ----------------------- |
| -- Find_Program_Name -- |
| ----------------------- |
| |
| procedure Find_Program_Name is |
| Command_Name : String (1 .. Len_Arg (0)); |
| Cindex1 : Integer := Command_Name'First; |
| Cindex2 : Integer := Command_Name'Last; |
| |
| begin |
| Fill_Arg (Command_Name'Address, 0); |
| |
| if Command_Name = "" then |
| Name_Len := 0; |
| return; |
| end if; |
| |
| -- The program name might be specified by a full path name. However, |
| -- we don't want to print that all out in an error message, so the |
| -- path might need to be stripped away. |
| |
| for J in reverse Cindex1 .. Cindex2 loop |
| if Is_Directory_Separator (Command_Name (J)) then |
| Cindex1 := J + 1; |
| exit; |
| end if; |
| end loop; |
| |
| -- Command_Name(Cindex1 .. Cindex2) is now the equivalent of the |
| -- POSIX command "basename argv[0]" |
| |
| -- Strip off any executable extension (usually nothing or .exe) |
| -- but formally reported by autoconf in the variable EXEEXT |
| |
| if Cindex2 - Cindex1 >= 4 then |
| if To_Lower (Command_Name (Cindex2 - 3)) = '.' |
| and then To_Lower (Command_Name (Cindex2 - 2)) = 'e' |
| and then To_Lower (Command_Name (Cindex2 - 1)) = 'x' |
| and then To_Lower (Command_Name (Cindex2)) = 'e' |
| then |
| Cindex2 := Cindex2 - 4; |
| end if; |
| end if; |
| |
| Name_Len := Cindex2 - Cindex1 + 1; |
| Name_Buffer (1 .. Name_Len) := Command_Name (Cindex1 .. Cindex2); |
| end Find_Program_Name; |
| |
| ------------------------ |
| -- Full_Lib_File_Name -- |
| ------------------------ |
| |
| procedure Full_Lib_File_Name |
| (N : File_Name_Type; |
| Lib_File : out File_Name_Type; |
| Attr : out File_Attributes) |
| is |
| begin |
| Smart_Find_File (N, Library, Lib_File, Attr); |
| end Full_Lib_File_Name; |
| |
| ------------------------ |
| -- Full_Lib_File_Name -- |
| ------------------------ |
| |
| function Full_Lib_File_Name (N : File_Name_Type) return File_Name_Type is |
| Attr : File_Attributes; |
| File : File_Name_Type; |
| begin |
| Full_Lib_File_Name (N, File, Attr); |
| return File; |
| end Full_Lib_File_Name; |
| |
| ---------------------------- |
| -- Full_Library_Info_Name -- |
| ---------------------------- |
| |
| function Full_Library_Info_Name return File_Name_Type is |
| begin |
| return Current_Full_Lib_Name; |
| end Full_Library_Info_Name; |
| |
| --------------------------- |
| -- Full_Object_File_Name -- |
| --------------------------- |
| |
| function Full_Object_File_Name return File_Name_Type is |
| begin |
| return Current_Full_Obj_Name; |
| end Full_Object_File_Name; |
| |
| ---------------------- |
| -- Full_Source_Name -- |
| ---------------------- |
| |
| function Full_Source_Name return File_Name_Type is |
| begin |
| return Current_Full_Source_Name; |
| end Full_Source_Name; |
| |
| ---------------------- |
| -- Full_Source_Name -- |
| ---------------------- |
| |
| function Full_Source_Name (N : File_Name_Type) return File_Name_Type is |
| begin |
| return Smart_Find_File (N, Source); |
| end Full_Source_Name; |
| |
| ---------------------- |
| -- Full_Source_Name -- |
| ---------------------- |
| |
| procedure Full_Source_Name |
| (N : File_Name_Type; |
| Full_File : out File_Name_Type; |
| Attr : access File_Attributes) is |
| begin |
| Smart_Find_File (N, Source, Full_File, Attr.all); |
| end Full_Source_Name; |
| |
| ------------------- |
| -- Get_Directory -- |
| ------------------- |
| |
| function Get_Directory (Name : File_Name_Type) return File_Name_Type is |
| begin |
| Get_Name_String (Name); |
| |
| for J in reverse 1 .. Name_Len loop |
| if Is_Directory_Separator (Name_Buffer (J)) then |
| Name_Len := J; |
| return Name_Find; |
| end if; |
| end loop; |
| |
| Name_Len := Hostparm.Normalized_CWD'Length; |
| Name_Buffer (1 .. Name_Len) := Hostparm.Normalized_CWD; |
| return Name_Find; |
| end Get_Directory; |
| |
| ------------------------------ |
| -- Get_First_Main_File_Name -- |
| ------------------------------ |
| |
| function Get_First_Main_File_Name return String is |
| begin |
| return File_Names (1).all; |
| end Get_First_Main_File_Name; |
| |
| -------------------------- |
| -- Get_Next_Dir_In_Path -- |
| -------------------------- |
| |
| Search_Path_Pos : Integer; |
| -- Keeps track of current position in search path. Initialized by the |
| -- call to Get_Next_Dir_In_Path_Init, updated by Get_Next_Dir_In_Path. |
| |
| function Get_Next_Dir_In_Path |
| (Search_Path : String_Access) return String_Access |
| is |
| Lower_Bound : Positive := Search_Path_Pos; |
| Upper_Bound : Positive; |
| |
| begin |
| loop |
| while Lower_Bound <= Search_Path'Last |
| and then Search_Path.all (Lower_Bound) = Path_Separator |
| loop |
| Lower_Bound := Lower_Bound + 1; |
| end loop; |
| |
| exit when Lower_Bound > Search_Path'Last; |
| |
| Upper_Bound := Lower_Bound; |
| while Upper_Bound <= Search_Path'Last |
| and then Search_Path.all (Upper_Bound) /= Path_Separator |
| loop |
| Upper_Bound := Upper_Bound + 1; |
| end loop; |
| |
| Search_Path_Pos := Upper_Bound; |
| return new String'(Search_Path.all (Lower_Bound .. Upper_Bound - 1)); |
| end loop; |
| |
| return null; |
| end Get_Next_Dir_In_Path; |
| |
| ------------------------------- |
| -- Get_Next_Dir_In_Path_Init -- |
| ------------------------------- |
| |
| procedure Get_Next_Dir_In_Path_Init (Search_Path : String_Access) is |
| begin |
| Search_Path_Pos := Search_Path'First; |
| end Get_Next_Dir_In_Path_Init; |
| |
| -------------------------------------- |
| -- Get_Primary_Src_Search_Directory -- |
| -------------------------------------- |
| |
| function Get_Primary_Src_Search_Directory return String_Ptr is |
| begin |
| return Src_Search_Directories.Table (Primary_Directory); |
| end Get_Primary_Src_Search_Directory; |
| |
| ------------------------ |
| -- Get_RTS_Search_Dir -- |
| ------------------------ |
| |
| function Get_RTS_Search_Dir |
| (Search_Dir : String; |
| File_Type : Search_File_Type) return String_Ptr |
| is |
| procedure Get_Current_Dir |
| (Dir : System.Address; |
| Length : System.Address); |
| pragma Import (C, Get_Current_Dir, "__gnat_get_current_dir"); |
| |
| Max_Path : Integer; |
| pragma Import (C, Max_Path, "__gnat_max_path_len"); |
| -- Maximum length of a path name |
| |
| Current_Dir : String_Ptr; |
| Default_Search_Dir : String_Access; |
| Default_Suffix_Dir : String_Access; |
| Local_Search_Dir : String_Access; |
| Norm_Search_Dir : String_Access; |
| Result_Search_Dir : String_Access; |
| Search_File : String_Access; |
| Temp_String : String_Ptr; |
| |
| begin |
| -- Add a directory separator at the end of the directory if necessary |
| -- so that we can directly append a file to the directory |
| |
| if not Is_Directory_Separator (Search_Dir (Search_Dir'Last)) then |
| Local_Search_Dir := |
| new String'(Search_Dir & String'(1 => Directory_Separator)); |
| else |
| Local_Search_Dir := new String'(Search_Dir); |
| end if; |
| |
| if File_Type = Include then |
| Search_File := Include_Search_File; |
| Default_Suffix_Dir := new String'("adainclude"); |
| else |
| Search_File := Objects_Search_File; |
| Default_Suffix_Dir := new String'("adalib"); |
| end if; |
| |
| Norm_Search_Dir := Local_Search_Dir; |
| |
| if Is_Absolute_Path (Norm_Search_Dir.all) then |
| |
| -- We first verify if there is a directory Include_Search_Dir |
| -- containing default search directories |
| |
| Result_Search_Dir := |
| Read_Default_Search_Dirs (Norm_Search_Dir, Search_File, null); |
| Default_Search_Dir := |
| new String'(Norm_Search_Dir.all & Default_Suffix_Dir.all); |
| Free (Norm_Search_Dir); |
| |
| if Result_Search_Dir /= null then |
| return String_Ptr (Result_Search_Dir); |
| elsif Is_Directory (Default_Search_Dir.all) then |
| return String_Ptr (Default_Search_Dir); |
| else |
| return null; |
| end if; |
| |
| -- Search in the current directory |
| |
| else |
| -- Get the current directory |
| |
| declare |
| Buffer : String (1 .. Max_Path + 2); |
| Path_Len : Natural := Max_Path; |
| |
| begin |
| Get_Current_Dir (Buffer'Address, Path_Len'Address); |
| |
| if Path_Len = 0 then |
| raise Program_Error; |
| end if; |
| |
| if not Is_Directory_Separator (Buffer (Path_Len)) then |
| Path_Len := Path_Len + 1; |
| Buffer (Path_Len) := Directory_Separator; |
| end if; |
| |
| Current_Dir := new String'(Buffer (1 .. Path_Len)); |
| end; |
| |
| Norm_Search_Dir := |
| new String'(Current_Dir.all & Local_Search_Dir.all); |
| |
| Result_Search_Dir := |
| Read_Default_Search_Dirs (Norm_Search_Dir, Search_File, null); |
| |
| Default_Search_Dir := |
| new String'(Norm_Search_Dir.all & Default_Suffix_Dir.all); |
| |
| Free (Norm_Search_Dir); |
| |
| if Result_Search_Dir /= null then |
| return String_Ptr (Result_Search_Dir); |
| |
| elsif Is_Directory (Default_Search_Dir.all) then |
| return String_Ptr (Default_Search_Dir); |
| |
| else |
| -- Search in Search_Dir_Prefix/Search_Dir |
| |
| Norm_Search_Dir := |
| new String' |
| (Update_Path (Search_Dir_Prefix).all & Local_Search_Dir.all); |
| |
| Result_Search_Dir := |
| Read_Default_Search_Dirs (Norm_Search_Dir, Search_File, null); |
| |
| Default_Search_Dir := |
| new String'(Norm_Search_Dir.all & Default_Suffix_Dir.all); |
| |
| Free (Norm_Search_Dir); |
| |
| if Result_Search_Dir /= null then |
| return String_Ptr (Result_Search_Dir); |
| |
| elsif Is_Directory (Default_Search_Dir.all) then |
| return String_Ptr (Default_Search_Dir); |
| |
| else |
| -- We finally search in Search_Dir_Prefix/rts-Search_Dir |
| |
| Temp_String := |
| new String'(Update_Path (Search_Dir_Prefix).all & "rts-"); |
| |
| Norm_Search_Dir := |
| new String'(Temp_String.all & Local_Search_Dir.all); |
| |
| Result_Search_Dir := |
| Read_Default_Search_Dirs (Norm_Search_Dir, Search_File, null); |
| |
| Default_Search_Dir := |
| new String'(Norm_Search_Dir.all & Default_Suffix_Dir.all); |
| Free (Norm_Search_Dir); |
| |
| if Result_Search_Dir /= null then |
| return String_Ptr (Result_Search_Dir); |
| |
| elsif Is_Directory (Default_Search_Dir.all) then |
| return String_Ptr (Default_Search_Dir); |
| |
| else |
| return null; |
| end if; |
| end if; |
| end if; |
| end if; |
| end Get_RTS_Search_Dir; |
| |
| -------------------------------- |
| -- Include_Dir_Default_Prefix -- |
| -------------------------------- |
| |
| function Include_Dir_Default_Prefix return String_Access is |
| begin |
| if The_Include_Dir_Default_Prefix = null then |
| The_Include_Dir_Default_Prefix := |
| String_Access (Update_Path (Include_Dir_Default_Name)); |
| end if; |
| |
| return The_Include_Dir_Default_Prefix; |
| end Include_Dir_Default_Prefix; |
| |
| function Include_Dir_Default_Prefix return String is |
| begin |
| return Include_Dir_Default_Prefix.all; |
| end Include_Dir_Default_Prefix; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize is |
| begin |
| Number_File_Names := 0; |
| Current_File_Name_Index := 0; |
| |
| Src_Search_Directories.Init; |
| Lib_Search_Directories.Init; |
| |
| -- Start off by setting all suppress options, to False. The special |
| -- overflow fields are set to Not_Set (they will be set by -gnatp, or |
| -- by -gnato, or, if neither of these appear, in Adjust_Global_Switches |
| -- in Gnat1drv). |
| |
| Suppress_Options := ((others => False), Not_Set, Not_Set); |
| |
| -- Reserve the first slot in the search paths table. This is the |
| -- directory of the main source file or main library file and is filled |
| -- in by each call to Next_Main_Source/Next_Main_Lib_File with the |
| -- directory specified for this main source or library file. This is the |
| -- directory which is searched first by default. This default search is |
| -- inhibited by the option -I- for both source and library files. |
| |
| Src_Search_Directories.Set_Last (Primary_Directory); |
| Src_Search_Directories.Table (Primary_Directory) := new String'(""); |
| |
| Lib_Search_Directories.Set_Last (Primary_Directory); |
| Lib_Search_Directories.Table (Primary_Directory) := new String'(""); |
| end Initialize; |
| |
| ------------------ |
| -- Is_Directory -- |
| ------------------ |
| |
| function Is_Directory |
| (Name : C_File_Name; Attr : access File_Attributes) return Boolean |
| is |
| function Internal (N : C_File_Name; A : System.Address) return Integer; |
| pragma Import (C, Internal, "__gnat_is_directory_attr"); |
| begin |
| return Internal (Name, Attr.all'Address) /= 0; |
| end Is_Directory; |
| |
| ---------------------------- |
| -- Is_Directory_Separator -- |
| ---------------------------- |
| |
| function Is_Directory_Separator (C : Character) return Boolean is |
| begin |
| -- In addition to the default directory_separator allow the '/' to |
| -- act as separator since this is allowed in MS-DOS and Windows. |
| |
| return C = Directory_Separator or else C = '/'; |
| end Is_Directory_Separator; |
| |
| ------------------------- |
| -- Is_Readonly_Library -- |
| ------------------------- |
| |
| function Is_Readonly_Library (File : File_Name_Type) return Boolean is |
| begin |
| Get_Name_String (File); |
| |
| pragma Assert (Name_Buffer (Name_Len - 3 .. Name_Len) = ".ali"); |
| |
| return not Is_Writable_File (Name_Buffer (1 .. Name_Len)); |
| end Is_Readonly_Library; |
| |
| ------------------------ |
| -- Is_Executable_File -- |
| ------------------------ |
| |
| function Is_Executable_File |
| (Name : C_File_Name; Attr : access File_Attributes) return Boolean |
| is |
| function Internal (N : C_File_Name; A : System.Address) return Integer; |
| pragma Import (C, Internal, "__gnat_is_executable_file_attr"); |
| begin |
| return Internal (Name, Attr.all'Address) /= 0; |
| end Is_Executable_File; |
| |
| ---------------------- |
| -- Is_Readable_File -- |
| ---------------------- |
| |
| function Is_Readable_File |
| (Name : C_File_Name; Attr : access File_Attributes) return Boolean |
| is |
| function Internal (N : C_File_Name; A : System.Address) return Integer; |
| pragma Import (C, Internal, "__gnat_is_readable_file_attr"); |
| begin |
| return Internal (Name, Attr.all'Address) /= 0; |
| end Is_Readable_File; |
| |
| --------------------- |
| -- Is_Regular_File -- |
| --------------------- |
| |
| function Is_Regular_File |
| (Name : C_File_Name; Attr : access File_Attributes) return Boolean |
| is |
| function Internal (N : C_File_Name; A : System.Address) return Integer; |
| pragma Import (C, Internal, "__gnat_is_regular_file_attr"); |
| begin |
| return Internal (Name, Attr.all'Address) /= 0; |
| end Is_Regular_File; |
| |
| ---------------------- |
| -- Is_Symbolic_Link -- |
| ---------------------- |
| |
| function Is_Symbolic_Link |
| (Name : C_File_Name; Attr : access File_Attributes) return Boolean |
| is |
| function Internal (N : C_File_Name; A : System.Address) return Integer; |
| pragma Import (C, Internal, "__gnat_is_symbolic_link_attr"); |
| begin |
| return Internal (Name, Attr.all'Address) /= 0; |
| end Is_Symbolic_Link; |
| |
| ---------------------- |
| -- Is_Writable_File -- |
| ---------------------- |
| |
| function Is_Writable_File |
| (Name : C_File_Name; Attr : access File_Attributes) return Boolean |
| is |
| function Internal (N : C_File_Name; A : System.Address) return Integer; |
| pragma Import (C, Internal, "__gnat_is_writable_file_attr"); |
| begin |
| return Internal (Name, Attr.all'Address) /= 0; |
| end Is_Writable_File; |
| |
| ------------------- |
| -- Lib_File_Name -- |
| ------------------- |
| |
| function Lib_File_Name |
| (Source_File : File_Name_Type; |
| Munit_Index : Nat := 0) return File_Name_Type |
| is |
| begin |
| Get_Name_String (Source_File); |
| |
| for J in reverse 2 .. Name_Len loop |
| if Name_Buffer (J) = '.' then |
| Name_Len := J - 1; |
| exit; |
| end if; |
| end loop; |
| |
| if Munit_Index /= 0 then |
| Add_Char_To_Name_Buffer (Multi_Unit_Index_Character); |
| Add_Nat_To_Name_Buffer (Munit_Index); |
| end if; |
| |
| Add_Char_To_Name_Buffer ('.'); |
| Add_Str_To_Name_Buffer (ALI_Suffix.all); |
| return Name_Find; |
| end Lib_File_Name; |
| |
| ----------------- |
| -- Locate_File -- |
| ----------------- |
| |
| procedure Locate_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Dir : Natural; |
| Name : String; |
| Found : out File_Name_Type; |
| Attr : access File_Attributes) |
| is |
| Dir_Name : String_Ptr; |
| |
| begin |
| -- If Name is already an absolute path, do not look for a directory |
| |
| if Is_Absolute_Path (Name) then |
| Dir_Name := No_Dir; |
| |
| elsif T = Library then |
| Dir_Name := Lib_Search_Directories.Table (Dir); |
| |
| else |
| pragma Assert (T /= Config); |
| Dir_Name := Src_Search_Directories.Table (Dir); |
| end if; |
| |
| declare |
| Full_Name : |
| constant String (1 .. Dir_Name'Length + Name'Length + 1) := |
| Dir_Name.all & Name & ASCII.NUL; |
| -- Use explicit bounds, because Dir_Name might be a substring whose |
| -- 'First is not 1. |
| |
| begin |
| Attr.all := Unknown_Attributes; |
| |
| if not Is_Regular_File (Full_Name'Address, Attr) then |
| Found := No_File; |
| |
| else |
| -- If the file is in the current directory then return N itself |
| |
| if Dir_Name'Length = 0 then |
| Found := N; |
| else |
| Found := |
| Name_Find (Full_Name (Full_Name'First .. Full_Name'Last - 1)); |
| end if; |
| end if; |
| end; |
| end Locate_File; |
| |
| ------------------------------- |
| -- Matching_Full_Source_Name -- |
| ------------------------------- |
| |
| function Matching_Full_Source_Name |
| (N : File_Name_Type; |
| T : Time_Stamp_Type) return File_Name_Type |
| is |
| begin |
| Get_Name_String (N); |
| |
| declare |
| File_Name : constant String := Name_Buffer (1 .. Name_Len); |
| File : File_Name_Type := No_File; |
| Attr : aliased File_Attributes; |
| Last_Dir : Natural; |
| |
| begin |
| if Opt.Look_In_Primary_Dir then |
| Locate_File |
| (N, Source, Primary_Directory, File_Name, File, |
| Attr'Unchecked_Access); |
| |
| if File /= No_File and then T = File_Stamp (N) then |
| return File; |
| end if; |
| end if; |
| |
| Last_Dir := Src_Search_Directories.Last; |
| |
| for D in Primary_Directory + 1 .. Last_Dir loop |
| Locate_File (N, Source, D, File_Name, File, Attr'Unchecked_Access); |
| |
| if File /= No_File and then T = File_Stamp (File) then |
| return File; |
| end if; |
| end loop; |
| |
| return No_File; |
| end; |
| end Matching_Full_Source_Name; |
| |
| ---------------- |
| -- More_Files -- |
| ---------------- |
| |
| function More_Files return Boolean is |
| begin |
| return (Current_File_Name_Index < Number_File_Names); |
| end More_Files; |
| |
| ------------------------------- |
| -- Nb_Dir_In_Obj_Search_Path -- |
| ------------------------------- |
| |
| function Nb_Dir_In_Obj_Search_Path return Natural is |
| begin |
| if Opt.Look_In_Primary_Dir then |
| return Lib_Search_Directories.Last - Primary_Directory + 1; |
| else |
| return Lib_Search_Directories.Last - Primary_Directory; |
| end if; |
| end Nb_Dir_In_Obj_Search_Path; |
| |
| ------------------------------- |
| -- Nb_Dir_In_Src_Search_Path -- |
| ------------------------------- |
| |
| function Nb_Dir_In_Src_Search_Path return Natural is |
| begin |
| if Opt.Look_In_Primary_Dir then |
| return Src_Search_Directories.Last - Primary_Directory + 1; |
| else |
| return Src_Search_Directories.Last - Primary_Directory; |
| end if; |
| end Nb_Dir_In_Src_Search_Path; |
| |
| -------------------- |
| -- Next_Main_File -- |
| -------------------- |
| |
| function Next_Main_File return File_Name_Type is |
| File_Name : String_Ptr; |
| Dir_Name : String_Ptr; |
| Fptr : Natural; |
| |
| begin |
| pragma Assert (More_Files); |
| |
| Current_File_Name_Index := Current_File_Name_Index + 1; |
| |
| -- Get the file and directory name |
| |
| File_Name := File_Names (Current_File_Name_Index); |
| Fptr := File_Name'First; |
| |
| for J in reverse File_Name'Range loop |
| if Is_Directory_Separator (File_Name (J)) then |
| if J = File_Name'Last then |
| Fail ("File name missing"); |
| end if; |
| |
| Fptr := J + 1; |
| exit; |
| end if; |
| end loop; |
| |
| -- Save name of directory in which main unit resides for use in |
| -- locating other units |
| |
| Dir_Name := new String'(File_Name (File_Name'First .. Fptr - 1)); |
| |
| case Running_Program is |
| when Compiler => |
| Src_Search_Directories.Table (Primary_Directory) := Dir_Name; |
| Look_In_Primary_Directory_For_Current_Main := True; |
| |
| when Make => |
| Src_Search_Directories.Table (Primary_Directory) := Dir_Name; |
| |
| if Fptr > File_Name'First then |
| Look_In_Primary_Directory_For_Current_Main := True; |
| end if; |
| |
| when Binder |
| | Gnatls |
| => |
| Dir_Name := Normalize_Directory_Name (Dir_Name.all); |
| Lib_Search_Directories.Table (Primary_Directory) := Dir_Name; |
| |
| when Unspecified => |
| null; |
| end case; |
| |
| Name_Len := File_Name'Last - Fptr + 1; |
| Name_Buffer (1 .. Name_Len) := File_Name (Fptr .. File_Name'Last); |
| Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len)); |
| Current_Main := Name_Find; |
| |
| -- In the gnatmake case, the main file may have not have the |
| -- extension. Try ".adb" first then ".ads" |
| |
| if Running_Program = Make then |
| declare |
| Orig_Main : constant File_Name_Type := Current_Main; |
| |
| begin |
| if Strip_Suffix (Orig_Main) = Orig_Main then |
| Current_Main := |
| Append_Suffix_To_File_Name (Orig_Main, ".adb"); |
| |
| if Full_Source_Name (Current_Main) = No_File then |
| Current_Main := |
| Append_Suffix_To_File_Name (Orig_Main, ".ads"); |
| |
| if Full_Source_Name (Current_Main) = No_File then |
| Current_Main := Orig_Main; |
| end if; |
| end if; |
| end if; |
| end; |
| end if; |
| |
| return Current_Main; |
| end Next_Main_File; |
| |
| ------------------------------ |
| -- Normalize_Directory_Name -- |
| ------------------------------ |
| |
| function Normalize_Directory_Name (Directory : String) return String_Ptr is |
| |
| function Is_Quoted (Path : String) return Boolean; |
| pragma Inline (Is_Quoted); |
| -- Returns true if Path is quoted (either double or single quotes) |
| |
| --------------- |
| -- Is_Quoted -- |
| --------------- |
| |
| function Is_Quoted (Path : String) return Boolean is |
| First : constant Character := Path (Path'First); |
| Last : constant Character := Path (Path'Last); |
| |
| begin |
| if (First = ''' and then Last = ''') |
| or else |
| (First = '"' and then Last = '"') |
| then |
| return True; |
| else |
| return False; |
| end if; |
| end Is_Quoted; |
| |
| Result : String_Ptr; |
| |
| -- Start of processing for Normalize_Directory_Name |
| |
| begin |
| if Directory'Length = 0 then |
| Result := new String'(Hostparm.Normalized_CWD); |
| |
| elsif Is_Directory_Separator (Directory (Directory'Last)) then |
| Result := new String'(Directory); |
| |
| elsif Is_Quoted (Directory) then |
| |
| -- This is a quoted string, it certainly means that the directory |
| -- contains some spaces for example. We can safely remove the quotes |
| -- here as the OS_Lib.Normalize_Arguments will be called before any |
| -- spawn routines. This ensure that quotes will be added when needed. |
| |
| Result := new String (1 .. Directory'Length - 1); |
| Result (1 .. Directory'Length - 2) := |
| Directory (Directory'First + 1 .. Directory'Last - 1); |
| Result (Result'Last) := Directory_Separator; |
| |
| else |
| Result := new String (1 .. Directory'Length + 1); |
| Result (1 .. Directory'Length) := Directory; |
| Result (Directory'Length + 1) := Directory_Separator; |
| end if; |
| |
| return Result; |
| end Normalize_Directory_Name; |
| |
| --------------------- |
| -- Number_Of_Files -- |
| --------------------- |
| |
| function Number_Of_Files return Nat is |
| begin |
| return Number_File_Names; |
| end Number_Of_Files; |
| |
| ------------------------------- |
| -- Object_Dir_Default_Prefix -- |
| ------------------------------- |
| |
| function Object_Dir_Default_Prefix return String is |
| Object_Dir : String_Access := |
| String_Access (Update_Path (Object_Dir_Default_Name)); |
| |
| begin |
| if Object_Dir = null then |
| return ""; |
| |
| else |
| declare |
| Result : constant String := Object_Dir.all; |
| begin |
| Free (Object_Dir); |
| return Result; |
| end; |
| end if; |
| end Object_Dir_Default_Prefix; |
| |
| ---------------------- |
| -- Object_File_Name -- |
| ---------------------- |
| |
| function Object_File_Name (N : File_Name_Type) return File_Name_Type is |
| begin |
| if N = No_File then |
| return No_File; |
| end if; |
| |
| Get_Name_String (N); |
| Name_Len := Name_Len - ALI_Suffix'Length - 1; |
| |
| for J in Target_Object_Suffix'Range loop |
| Name_Len := Name_Len + 1; |
| Name_Buffer (Name_Len) := Target_Object_Suffix (J); |
| end loop; |
| |
| return Name_Enter; |
| end Object_File_Name; |
| |
| ------------------------------- |
| -- OS_Exit_Through_Exception -- |
| ------------------------------- |
| |
| procedure OS_Exit_Through_Exception (Status : Integer) is |
| begin |
| Current_Exit_Status := Status; |
| raise Types.Terminate_Program; |
| end OS_Exit_Through_Exception; |
| |
| -------------------------- |
| -- OS_Time_To_GNAT_Time -- |
| -------------------------- |
| |
| function OS_Time_To_GNAT_Time (T : OS_Time) return Time_Stamp_Type is |
| GNAT_Time : Time_Stamp_Type; |
| |
| type Underlying_OS_Time is |
| range -(2 ** 63) .. +(2 ** 63 - 1); |
| -- Underlying_OS_Time is a redeclaration of OS_Time to allow integer |
| -- manipulation. Remove this in favor of To_Ada/To_C once newer |
| -- GNAT releases are available with these functions. |
| |
| function To_Int is |
| new Ada.Unchecked_Conversion (OS_Time, Underlying_OS_Time); |
| function From_Int is |
| new Ada.Unchecked_Conversion (Underlying_OS_Time, OS_Time); |
| |
| TI : Underlying_OS_Time := To_Int (T); |
| Y : Year_Type; |
| Mo : Month_Type; |
| D : Day_Type; |
| H : Hour_Type; |
| Mn : Minute_Type; |
| S : Second_Type; |
| |
| begin |
| if T = Invalid_Time then |
| return Empty_Time_Stamp; |
| end if; |
| |
| if On_Windows and then TI mod 2 > 0 then |
| -- Windows ALI files had timestamps rounded to even seconds |
| -- historically. The rounding was originally done in GM_Split. |
| -- Now that GM_Split no longer does it, we are rounding it here |
| -- only for ALI files. |
| |
| TI := TI + 1; |
| end if; |
| |
| GM_Split (From_Int (TI), Y, Mo, D, H, Mn, S); |
| |
| Make_Time_Stamp |
| (Year => Nat (Y), |
| Month => Nat (Mo), |
| Day => Nat (D), |
| Hour => Nat (H), |
| Minutes => Nat (Mn), |
| Seconds => Nat (S), |
| TS => GNAT_Time); |
| |
| return GNAT_Time; |
| end OS_Time_To_GNAT_Time; |
| |
| ----------------- |
| -- Prep_Suffix -- |
| ----------------- |
| |
| function Prep_Suffix return String is |
| begin |
| return ".prep"; |
| end Prep_Suffix; |
| |
| ------------------ |
| -- Program_Name -- |
| ------------------ |
| |
| function Program_Name (Nam : String; Prog : String) return String_Access is |
| End_Of_Prefix : Natural := 0; |
| Start_Of_Prefix : Positive := 1; |
| Start_Of_Suffix : Positive; |
| |
| begin |
| -- Get the name of the current program being executed |
| |
| Find_Program_Name; |
| |
| -- Find the target prefix if any, for the cross compilation case. |
| -- For instance in "powerpc-elf-gcc" the target prefix is |
| -- "powerpc-elf-" |
| -- Ditto for suffix, e.g. in "gcc-4.1", the suffix is "-4.1" |
| |
| for J in reverse 1 .. Name_Len loop |
| if Is_Directory_Separator (Name_Buffer (J)) |
| or else Name_Buffer (J) = ':' |
| then |
| Start_Of_Prefix := J + 1; |
| exit; |
| end if; |
| end loop; |
| |
| -- Find End_Of_Prefix |
| |
| for J in Start_Of_Prefix .. Name_Len - Prog'Length + 1 loop |
| if Name_Buffer (J .. J + Prog'Length - 1) = Prog then |
| End_Of_Prefix := J - 1; |
| exit; |
| end if; |
| end loop; |
| |
| Start_Of_Suffix := End_Of_Prefix + Prog'Length + 1; |
| |
| -- Create the new program name |
| |
| return new String' |
| (Name_Buffer (Start_Of_Prefix .. End_Of_Prefix) |
| & Nam |
| & Name_Buffer (Start_Of_Suffix .. Name_Len)); |
| end Program_Name; |
| |
| ------------------------------ |
| -- Read_Default_Search_Dirs -- |
| ------------------------------ |
| |
| function Read_Default_Search_Dirs |
| (Search_Dir_Prefix : String_Access; |
| Search_File : String_Access; |
| Search_Dir_Default_Name : String_Access) return String_Access |
| is |
| Prefix_Len : constant Integer := Search_Dir_Prefix.all'Length; |
| Buffer : String (1 .. Prefix_Len + Search_File.all'Length + 1); |
| File_FD : File_Descriptor; |
| S, S1 : String_Access; |
| Len : Integer; |
| Curr : Integer; |
| Actual_Len : Integer; |
| J1 : Integer; |
| |
| Prev_Was_Separator : Boolean; |
| Nb_Relative_Dir : Integer; |
| |
| function Is_Relative (S : String; K : Positive) return Boolean; |
| pragma Inline (Is_Relative); |
| -- Returns True if a relative directory specification is found |
| -- in S at position K, False otherwise. |
| |
| ----------------- |
| -- Is_Relative -- |
| ----------------- |
| |
| function Is_Relative (S : String; K : Positive) return Boolean is |
| begin |
| return not Is_Absolute_Path (S (K .. S'Last)); |
| end Is_Relative; |
| |
| -- Start of processing for Read_Default_Search_Dirs |
| |
| begin |
| -- Construct a C compatible character string buffer |
| |
| Buffer (1 .. Search_Dir_Prefix.all'Length) |
| := Search_Dir_Prefix.all; |
| Buffer (Search_Dir_Prefix.all'Length + 1 .. Buffer'Last - 1) |
| := Search_File.all; |
| Buffer (Buffer'Last) := ASCII.NUL; |
| |
| File_FD := Open_Read (Buffer'Address, Binary); |
| if File_FD = Invalid_FD then |
| return Search_Dir_Default_Name; |
| end if; |
| |
| Len := Integer (File_Length (File_FD)); |
| |
| -- An extra character for a trailing Path_Separator is allocated |
| |
| S := new String (1 .. Len + 1); |
| S (Len + 1) := Path_Separator; |
| |
| -- Read the file. Note that the loop is probably not necessary since the |
| -- whole file is read at once but the loop is harmless and that way we |
| -- are sure to accommodate systems where this is not the case. |
| |
| Curr := 1; |
| Actual_Len := Len; |
| while Actual_Len /= 0 loop |
| Actual_Len := Read (File_FD, S (Curr)'Address, Len); |
| Curr := Curr + Actual_Len; |
| end loop; |
| |
| -- Process the file, dealing with path separators |
| |
| Prev_Was_Separator := True; |
| Nb_Relative_Dir := 0; |
| for J in 1 .. Len loop |
| |
| -- Treat any EOL character as a path separator. Note that we do |
| -- not treat space as a path separator (we used to treat space as a |
| -- path separator in an earlier version). That way space can appear |
| -- as a legitimate character in a path name. |
| |
| if S (J) = ASCII.LF or else S (J) = ASCII.CR then |
| S (J) := Path_Separator; |
| end if; |
| |
| -- Test for explicit path separator (or control char as above) |
| |
| if S (J) = Path_Separator then |
| Prev_Was_Separator := True; |
| |
| -- If not path separator, register use of relative directory |
| |
| else |
| if Prev_Was_Separator and then Is_Relative (S.all, J) then |
| Nb_Relative_Dir := Nb_Relative_Dir + 1; |
| end if; |
| |
| Prev_Was_Separator := False; |
| end if; |
| end loop; |
| |
| if Nb_Relative_Dir = 0 then |
| return S; |
| end if; |
| |
| -- Add the Search_Dir_Prefix to all relative paths |
| |
| S1 := new String (1 .. S'Length + Nb_Relative_Dir * Prefix_Len); |
| J1 := 1; |
| Prev_Was_Separator := True; |
| for J in 1 .. Len + 1 loop |
| if S (J) = Path_Separator then |
| Prev_Was_Separator := True; |
| |
| else |
| if Prev_Was_Separator and then Is_Relative (S.all, J) then |
| S1 (J1 .. J1 + Prefix_Len - 1) := Search_Dir_Prefix.all; |
| J1 := J1 + Prefix_Len; |
| end if; |
| |
| Prev_Was_Separator := False; |
| end if; |
| S1 (J1) := S (J); |
| J1 := J1 + 1; |
| end loop; |
| |
| Free (S); |
| return S1; |
| end Read_Default_Search_Dirs; |
| |
| ----------------------- |
| -- Read_Library_Info -- |
| ----------------------- |
| |
| function Read_Library_Info |
| (Lib_File : File_Name_Type; |
| Fatal_Err : Boolean := False) return Text_Buffer_Ptr |
| is |
| File : File_Name_Type; |
| Attr : aliased File_Attributes; |
| begin |
| Find_File (Lib_File, Library, File, Attr'Access); |
| return Read_Library_Info_From_Full |
| (Full_Lib_File => File, |
| Lib_File_Attr => Attr'Access, |
| Fatal_Err => Fatal_Err); |
| end Read_Library_Info; |
| |
| --------------------------------- |
| -- Read_Library_Info_From_Full -- |
| --------------------------------- |
| |
| function Read_Library_Info_From_Full |
| (Full_Lib_File : File_Name_Type; |
| Lib_File_Attr : access File_Attributes; |
| Fatal_Err : Boolean := False) return Text_Buffer_Ptr |
| is |
| Lib_FD : File_Descriptor; |
| -- The file descriptor for the current library file. A negative value |
| -- indicates failure to open the specified source file. |
| |
| Len : Integer; |
| -- Length of source file text (ALI). If it doesn't fit in an integer |
| -- we're probably stuck anyway (>2 gigs of source seems a lot, and |
| -- there are other places in the compiler that make this assumption). |
| |
| Text : Text_Buffer_Ptr; |
| -- Allocated text buffer |
| |
| Status : Boolean; |
| pragma Warnings (Off, Status); |
| -- For the calls to Close |
| |
| begin |
| Current_Full_Lib_Name := Full_Lib_File; |
| Current_Full_Obj_Name := Object_File_Name (Current_Full_Lib_Name); |
| |
| if Current_Full_Lib_Name = No_File then |
| if Fatal_Err then |
| Fail ("Cannot find: " & Name_Buffer (1 .. Name_Len)); |
| else |
| Current_Full_Obj_Stamp := Empty_Time_Stamp; |
| return null; |
| end if; |
| end if; |
| |
| Get_Name_String (Current_Full_Lib_Name); |
| Name_Buffer (Name_Len + 1) := ASCII.NUL; |
| |
| -- Open the library FD, note that we open in binary mode, because as |
| -- documented in the spec, the caller is expected to handle either |
| -- DOS or Unix mode files, and there is no point in wasting time on |
| -- text translation when it is not required. |
| |
| Lib_FD := Open_Read (Name_Buffer'Address, Binary); |
| |
| if Lib_FD = Invalid_FD then |
| if Fatal_Err then |
| Fail ("Cannot open: " & Name_Buffer (1 .. Name_Len)); |
| else |
| Current_Full_Obj_Stamp := Empty_Time_Stamp; |
| return null; |
| end if; |
| end if; |
| |
| -- Compute the length of the file (potentially also preparing other data |
| -- like the timestamp and whether the file is read-only, for future use) |
| |
| Len := Integer (File_Length (Name_Buffer'Address, Lib_File_Attr)); |
| |
| -- Check for object file consistency if requested |
| |
| if Opt.Check_Object_Consistency then |
| -- On most systems, this does not result in an extra system call |
| |
| Current_Full_Lib_Stamp := |
| OS_Time_To_GNAT_Time |
| (File_Time_Stamp (Name_Buffer'Address, Lib_File_Attr)); |
| |
| -- ??? One system call here |
| |
| Current_Full_Obj_Stamp := File_Stamp (Current_Full_Obj_Name); |
| |
| if Current_Full_Obj_Stamp (1) = ' ' then |
| |
| -- When the library is readonly always assume object is consistent |
| -- The call to Is_Writable_File only results in a system call on |
| -- some systems, but in most cases it has already been computed as |
| -- part of the call to File_Length above. |
| |
| Get_Name_String (Current_Full_Lib_Name); |
| Name_Buffer (Name_Len + 1) := ASCII.NUL; |
| |
| if not Is_Writable_File (Name_Buffer'Address, Lib_File_Attr) then |
| Current_Full_Obj_Stamp := Current_Full_Lib_Stamp; |
| |
| elsif Fatal_Err then |
| Get_Name_String (Current_Full_Obj_Name); |
| Close (Lib_FD, Status); |
| |
| -- No need to check the status, we fail anyway |
| |
| Fail ("Cannot find: " & Name_Buffer (1 .. Name_Len)); |
| |
| else |
| Current_Full_Obj_Stamp := Empty_Time_Stamp; |
| Close (Lib_FD, Status); |
| |
| -- No need to check the status, we return null anyway |
| |
| return null; |
| end if; |
| |
| elsif Current_Full_Obj_Stamp < Current_Full_Lib_Stamp then |
| Close (Lib_FD, Status); |
| |
| -- No need to check the status, we return null anyway |
| |
| return null; |
| end if; |
| end if; |
| |
| -- Read data from the file |
| |
| declare |
| Actual_Len : Integer; |
| |
| Lo : constant Text_Ptr := 0; |
| -- Low bound for allocated text buffer |
| |
| Hi : Text_Ptr := Text_Ptr (Len); |
| -- High bound for allocated text buffer. Note length is Len + 1 |
| -- which allows for extra EOF character at the end of the buffer. |
| |
| begin |
| -- Allocate text buffer. Note extra character at end for EOF |
| |
| Text := new Text_Buffer (Lo .. Hi); |
| |
| -- Some systems have file types that require one read per line, |
| -- so read until we get the Len bytes or until there are no more |
| -- characters. |
| |
| Hi := Lo; |
| loop |
| Actual_Len := Read (Lib_FD, Text (Hi)'Address, Len); |
| Hi := Hi + Text_Ptr (Actual_Len); |
| exit when Actual_Len = Len or else Actual_Len <= 0; |
| end loop; |
| |
| Text (Hi) := EOF; |
| end; |
| |
| -- Read is complete, close file and we are done |
| |
| Close (Lib_FD, Status); |
| -- The status should never be False. But, if it is, what can we do? |
| -- So, we don't test it. |
| |
| return Text; |
| |
| end Read_Library_Info_From_Full; |
| |
| ---------------------- |
| -- Read_Source_File -- |
| ---------------------- |
| |
| procedure Read_Source_File |
| (N : File_Name_Type; |
| Lo : Source_Ptr; |
| Hi : out Source_Ptr; |
| Src : out Source_Buffer_Ptr; |
| FD : out File_Descriptor; |
| T : File_Type := Source) |
| is |
| Len : Integer; |
| -- Length of file, assume no more than 2 gigabytes of source |
| |
| Actual_Len : Integer; |
| |
| Status : Boolean; |
| pragma Warnings (Off, Status); |
| -- For the call to Close |
| |
| begin |
| Current_Full_Source_Name := Find_File (N, T, Full_Name => True); |
| Current_Full_Source_Stamp := File_Stamp (Current_Full_Source_Name); |
| |
| if Current_Full_Source_Name = No_File then |
| |
| -- If we were trying to access the main file and we could not find |
| -- it, we have an error. |
| |
| if N = Current_Main then |
| Get_Name_String (N); |
| Fail ("Cannot find: " & Name_Buffer (1 .. Name_Len)); |
| end if; |
| |
| FD := Null_FD; |
| Src := null; |
| Hi := No_Location; |
| return; |
| end if; |
| |
| Get_Name_String (Current_Full_Source_Name); |
| Name_Buffer (Name_Len + 1) := ASCII.NUL; |
| |
| -- Open the source FD, note that we open in binary mode, because as |
| -- documented in the spec, the caller is expected to handle either |
| -- DOS or Unix mode files, and there is no point in wasting time on |
| -- text translation when it is not required. |
| |
| FD := Open_Read (Name_Buffer'Address, Binary); |
| |
| if FD = Invalid_FD then |
| Src := null; |
| Hi := No_Location; |
| return; |
| end if; |
| |
| -- If it's a Source file, print out the file name, if requested, and if |
| -- it's not part of the runtimes, store it in File_Name_Chars. We don't |
| -- want to print non-Source files, like GNAT-TEMP-000001.TMP used to |
| -- pass information from gprbuild to gcc. We don't want to save runtime |
| -- file names, because we don't want users to send them in bug reports. |
| |
| if T = Source then |
| declare |
| Name : String renames Name_Buffer (1 .. Name_Len); |
| Inc : String renames Include_Dir_Default_Prefix.all; |
| |
| Part_Of_Runtimes : constant Boolean := |
| Inc /= "" |
| and then Inc'Length < Name_Len |
| and then Name_Buffer (1 .. Inc'Length) = Inc; |
| |
| begin |
| if Debug.Debug_Flag_Dot_N then |
| Write_Line (Name); |
| end if; |
| |
| if not Part_Of_Runtimes then |
| File_Name_Chars.Append_All (File_Name_Chars.Table_Type (Name)); |
| File_Name_Chars.Append (ASCII.LF); |
| end if; |
| end; |
| end if; |
| |
| -- Prepare to read data from the file |
| |
| Len := Integer (File_Length (FD)); |
| |
| -- Set Hi so that length is one more than the physical length, |
| -- allowing for the extra EOF character at the end of the buffer |
| |
| Hi := Lo + Source_Ptr (Len); |
| |
| -- Do the actual read operation |
| |
| declare |
| Var_Ptr : constant Source_Buffer_Ptr_Var := |
| new Source_Buffer (Lo .. Hi); |
| -- Allocate source buffer, allowing extra character at end for EOF |
| begin |
| -- Some systems have file types that require one read per line, |
| -- so read until we get the Len bytes or until there are no more |
| -- characters. |
| |
| Hi := Lo; |
| loop |
| Actual_Len := Read (FD, Var_Ptr (Hi)'Address, Len); |
| Hi := Hi + Source_Ptr (Actual_Len); |
| exit when Actual_Len = Len or else Actual_Len <= 0; |
| end loop; |
| |
| Var_Ptr (Hi) := EOF; |
| Src := Var_Ptr.all'Access; |
| end; |
| |
| -- Read is complete, get time stamp and close file and we are done |
| |
| Close (FD, Status); |
| |
| -- The status should never be False. But, if it is, what can we do? |
| -- So, we don't test it. |
| |
| -- ???We don't really need to return Hi anymore; We could get rid of |
| -- it. We could also make this into a function. |
| |
| pragma Assert (Hi = Src'Last); |
| end Read_Source_File; |
| |
| ------------------- |
| -- Relocate_Path -- |
| ------------------- |
| |
| function Relocate_Path |
| (Prefix : String; |
| Path : String) return String_Ptr |
| is |
| S : String_Ptr; |
| |
| procedure set_std_prefix (S : String; Len : Integer); |
| pragma Import (C, set_std_prefix); |
| |
| begin |
| if Std_Prefix = null then |
| Std_Prefix := String_Ptr (Getenv ("GNSA_ROOT")); |
| |
| if Std_Prefix.all = "" then |
| Std_Prefix := Executable_Prefix; |
| |
| elsif not Is_Directory_Separator (Std_Prefix (Std_Prefix'Last)) then |
| |
| -- The remainder of this function assumes that Std_Prefix |
| -- terminates with a dir separator, so we force this here. |
| |
| declare |
| Old_Prefix : String_Ptr := Std_Prefix; |
| begin |
| Std_Prefix := new String (1 .. Old_Prefix'Length + 1); |
| Std_Prefix (1 .. Old_Prefix'Length) := Old_Prefix.all; |
| Std_Prefix (Old_Prefix'Length + 1) := Directory_Separator; |
| Free (Old_Prefix); |
| end; |
| end if; |
| |
| if Std_Prefix.all /= "" then |
| |
| -- Remove trailing directory separator when calling set_std_prefix |
| |
| set_std_prefix (Std_Prefix.all, Std_Prefix'Length - 1); |
| end if; |
| end if; |
| |
| if Path'Last >= Prefix'Last and then Path (Prefix'Range) = Prefix then |
| if Std_Prefix.all /= "" then |
| S := new String |
| (1 .. Std_Prefix'Length + Path'Last - Prefix'Last); |
| S (1 .. Std_Prefix'Length) := Std_Prefix.all; |
| S (Std_Prefix'Length + 1 .. S'Last) := |
| Path (Prefix'Last + 1 .. Path'Last); |
| return S; |
| end if; |
| end if; |
| |
| return new String'(Path); |
| end Relocate_Path; |
| |
| ----------------- |
| -- Set_Program -- |
| ----------------- |
| |
| procedure Set_Program (P : Program_Type) is |
| begin |
| if Program_Set then |
| Fail ("Set_Program called twice"); |
| end if; |
| |
| Program_Set := True; |
| Running_Program := P; |
| end Set_Program; |
| |
| ---------------- |
| -- Shared_Lib -- |
| ---------------- |
| |
| function Shared_Lib (Name : String) return String is |
| Library : String (1 .. Name'Length + Library_Version'Length + 3); |
| -- 3 = 2 for "-l" + 1 for "-" before lib version |
| |
| begin |
| Library (1 .. 2) := "-l"; |
| Library (3 .. 2 + Name'Length) := Name; |
| Library (3 + Name'Length) := '-'; |
| Library (4 + Name'Length .. Library'Last) := Library_Version; |
| return Library; |
| end Shared_Lib; |
| |
| ---------------------- |
| -- Smart_File_Stamp -- |
| ---------------------- |
| |
| function Smart_File_Stamp |
| (N : File_Name_Type; |
| T : File_Type) return Time_Stamp_Type |
| is |
| File : File_Name_Type; |
| Attr : aliased File_Attributes; |
| |
| begin |
| if not File_Cache_Enabled then |
| Find_File (N, T, File, Attr'Access); |
| else |
| Smart_Find_File (N, T, File, Attr); |
| end if; |
| |
| if File = No_File then |
| return Empty_Time_Stamp; |
| else |
| Get_Name_String (File); |
| Name_Buffer (Name_Len + 1) := ASCII.NUL; |
| return |
| OS_Time_To_GNAT_Time |
| (File_Time_Stamp (Name_Buffer'Address, Attr'Access)); |
| end if; |
| end Smart_File_Stamp; |
| |
| --------------------- |
| -- Smart_Find_File -- |
| --------------------- |
| |
| function Smart_Find_File |
| (N : File_Name_Type; |
| T : File_Type) return File_Name_Type |
| is |
| File : File_Name_Type; |
| Attr : File_Attributes; |
| begin |
| Smart_Find_File (N, T, File, Attr); |
| return File; |
| end Smart_Find_File; |
| |
| --------------------- |
| -- Smart_Find_File -- |
| --------------------- |
| |
| procedure Smart_Find_File |
| (N : File_Name_Type; |
| T : File_Type; |
| Found : out File_Name_Type; |
| Attr : out File_Attributes) |
| is |
| Info : File_Info_Cache; |
| |
| begin |
| if not File_Cache_Enabled then |
| Find_File (N, T, Info.File, Info.Attr'Access); |
| |
| else |
| Info := File_Name_Hash_Table.Get (N); |
| |
| if Info.File = No_File then |
| Find_File (N, T, Info.File, Info.Attr'Access); |
| File_Name_Hash_Table.Set (N, Info); |
| end if; |
| end if; |
| |
| Found := Info.File; |
| Attr := Info.Attr; |
| end Smart_Find_File; |
| |
| ---------------------- |
| -- Source_File_Data -- |
| ---------------------- |
| |
| procedure Source_File_Data (Cache : Boolean) is |
| begin |
| File_Cache_Enabled := Cache; |
| end Source_File_Data; |
| |
| ----------------------- |
| -- Source_File_Stamp -- |
| ----------------------- |
| |
| function Source_File_Stamp (N : File_Name_Type) return Time_Stamp_Type is |
| begin |
| return Smart_File_Stamp (N, Source); |
| end Source_File_Stamp; |
| |
| --------------------- |
| -- Strip_Directory -- |
| --------------------- |
| |
| function Strip_Directory (Name : File_Name_Type) return File_Name_Type is |
| begin |
| Get_Name_String (Name); |
| |
| for J in reverse 1 .. Name_Len - 1 loop |
| |
| -- If we find the last directory separator |
| |
| if Is_Directory_Separator (Name_Buffer (J)) then |
| |
| -- Return part of Name that follows this last directory separator |
| |
| Name_Buffer (1 .. Name_Len - J) := Name_Buffer (J + 1 .. Name_Len); |
| Name_Len := Name_Len - J; |
| return Name_Find; |
| end if; |
| end loop; |
| |
| -- There were no directory separator, just return Name |
| |
| return Name; |
| end Strip_Directory; |
| |
| ------------------ |
| -- Strip_Suffix -- |
| ------------------ |
| |
| function Strip_Suffix (Name : File_Name_Type) return File_Name_Type is |
| begin |
| Get_Name_String (Name); |
| |
| for J in reverse 2 .. Name_Len loop |
| |
| -- If we found the last '.', return part of Name that precedes it |
| |
| if Name_Buffer (J) = '.' then |
| Name_Len := J - 1; |
| return Name_Enter; |
| end if; |
| end loop; |
| |
| return Name; |
| end Strip_Suffix; |
| |
| --------------------------- |
| -- To_Canonical_File_List -- |
| --------------------------- |
| |
| function To_Canonical_File_List |
| (Wildcard_Host_File : String; |
| Only_Dirs : Boolean) return String_Access_List_Access |
| is |
| function To_Canonical_File_List_Init |
| (Host_File : Address; |
| Only_Dirs : Integer) return Integer; |
| pragma Import (C, To_Canonical_File_List_Init, |
| "__gnat_to_canonical_file_list_init"); |
| |
| function To_Canonical_File_List_Next return Address; |
| pragma Import (C, To_Canonical_File_List_Next, |
| "__gnat_to_canonical_file_list_next"); |
| |
| procedure To_Canonical_File_List_Free; |
| pragma Import (C, To_Canonical_File_List_Free, |
| "__gnat_to_canonical_file_list_free"); |
| |
| Num_Files : Integer; |
| C_Wildcard_Host_File : String (1 .. Wildcard_Host_File'Length + 1); |
| |
| begin |
| C_Wildcard_Host_File (1 .. Wildcard_Host_File'Length) := |
| Wildcard_Host_File; |
| C_Wildcard_Host_File (C_Wildcard_Host_File'Last) := ASCII.NUL; |
| |
| -- Do the expansion and say how many there are |
| |
| Num_Files := To_Canonical_File_List_Init |
| (C_Wildcard_Host_File'Address, Boolean'Pos (Only_Dirs)); |
| |
| declare |
| Canonical_File_List : String_Access_List (1 .. Num_Files); |
| Canonical_File_Addr : Address; |
| Canonical_File_Len : CRTL.size_t; |
| |
| begin |
| -- Retrieve the expanded directory names and build the list |
| |
| for J in 1 .. Num_Files loop |
| Canonical_File_Addr := To_Canonical_File_List_Next; |
| Canonical_File_Len := C_String_Length (Canonical_File_Addr); |
| Canonical_File_List (J) := To_Path_String_Access |
| (Canonical_File_Addr, Canonical_File_Len); |
| end loop; |
| |
| -- Free up the storage |
| |
| To_Canonical_File_List_Free; |
| |
| return new String_Access_List'(Canonical_File_List); |
| end; |
| end To_Canonical_File_List; |
| |
| ---------------------- |
| -- To_Host_Dir_Spec -- |
| ---------------------- |
| |
| function To_Host_Dir_Spec |
| (Canonical_Dir : String; |
| Prefix_Style : Boolean) return String_Access |
| is |
| function To_Host_Dir_Spec |
| (Canonical_Dir : Address; |
| Prefix_Flag : Integer) return Address; |
| pragma Import (C, To_Host_Dir_Spec, "__gnat_to_host_dir_spec"); |
| |
| C_Canonical_Dir : String (1 .. Canonical_Dir'Length + 1); |
| Host_Dir_Addr : Address; |
| Host_Dir_Len : CRTL.size_t; |
| |
| begin |
| C_Canonical_Dir (1 .. Canonical_Dir'Length) := Canonical_Dir; |
| C_Canonical_Dir (C_Canonical_Dir'Last) := ASCII.NUL; |
| |
| if Prefix_Style then |
| Host_Dir_Addr := To_Host_Dir_Spec (C_Canonical_Dir'Address, 1); |
| else |
| Host_Dir_Addr := To_Host_Dir_Spec (C_Canonical_Dir'Address, 0); |
| end if; |
| Host_Dir_Len := C_String_Length (Host_Dir_Addr); |
| |
| if Host_Dir_Len = 0 then |
| return null; |
| else |
| return To_Path_String_Access (Host_Dir_Addr, Host_Dir_Len); |
| end if; |
| end To_Host_Dir_Spec; |
| |
| ----------------------- |
| -- To_Host_File_Spec -- |
| ----------------------- |
| |
| function To_Host_File_Spec |
| (Canonical_File : String) return String_Access |
| is |
| function To_Host_File_Spec (Canonical_File : Address) return Address; |
| pragma Import (C, To_Host_File_Spec, "__gnat_to_host_file_spec"); |
| |
| C_Canonical_File : String (1 .. Canonical_File'Length + 1); |
| Host_File_Addr : Address; |
| Host_File_Len : CRTL.size_t; |
| |
| begin |
| C_Canonical_File (1 .. Canonical_File'Length) := Canonical_File; |
| C_Canonical_File (C_Canonical_File'Last) := ASCII.NUL; |
| |
| Host_File_Addr := To_Host_File_Spec (C_Canonical_File'Address); |
| Host_File_Len := C_String_Length (Host_File_Addr); |
| |
| if Host_File_Len = 0 then |
| return null; |
| else |
| return To_Path_String_Access |
| (Host_File_Addr, Host_File_Len); |
| end if; |
| end To_Host_File_Spec; |
| |
| --------------------------- |
| -- To_Path_String_Access -- |
| --------------------------- |
| |
| function To_Path_String_Access |
| (Path_Addr : Address; |
| Path_Len : CRTL.size_t) return String_Access |
| is |
| subtype Path_String is String (1 .. Integer (Path_Len)); |
| type Path_String_Access is access Path_String; |
| |
| function Address_To_Access is new |
| Ada.Unchecked_Conversion (Source => Address, |
| Target => Path_String_Access); |
| |
| Path_Access : constant Path_String_Access := |
| Address_To_Access (Path_Addr); |
| |
| Return_Val : String_Access; |
| |
| begin |
| Return_Val := new String (1 .. Integer (Path_Len)); |
| |
| for J in 1 .. Integer (Path_Len) loop |
| Return_Val (J) := Path_Access (J); |
| end loop; |
| |
| return Return_Val; |
| end To_Path_String_Access; |
| |
| ----------------- |
| -- Update_Path -- |
| ----------------- |
| |
| function Update_Path (Path : String_Ptr) return String_Ptr is |
| |
| function C_Update_Path (Path, Component : Address) return Address; |
| pragma Import (C, C_Update_Path, "update_path"); |
| |
| In_Length : constant Integer := Path'Length; |
| In_String : String (1 .. In_Length + 1); |
| Component_Name : aliased String := "GCC" & ASCII.NUL; |
| Result_Ptr : Address; |
| Result_Length : CRTL.size_t; |
| Out_String : String_Ptr; |
| |
| begin |
| In_String (1 .. In_Length) := Path.all; |
| In_String (In_Length + 1) := ASCII.NUL; |
| Result_Ptr := C_Update_Path (In_String'Address, Component_Name'Address); |
| Result_Length := CRTL.strlen (Result_Ptr); |
| |
| Out_String := new String (1 .. Integer (Result_Length)); |
| CRTL.strncpy (Out_String.all'Address, Result_Ptr, Result_Length); |
| return Out_String; |
| end Update_Path; |
| |
| ---------------- |
| -- Write_Info -- |
| ---------------- |
| |
| procedure Write_Info (Info : String) is |
| begin |
| Write_With_Check (Info'Address, Info'Length); |
| Write_With_Check (EOL'Address, 1); |
| end Write_Info; |
| |
| ------------------------ |
| -- Write_Program_Name -- |
| ------------------------ |
| |
| procedure Write_Program_Name is |
| Save_Buffer : constant String (1 .. Name_Len) := |
| Name_Buffer (1 .. Name_Len); |
| |
| begin |
| Find_Program_Name; |
| |
| -- Convert the name to lower case so error messages are the same on |
| -- all systems. |
| |
| for J in 1 .. Name_Len loop |
| if Name_Buffer (J) in 'A' .. 'Z' then |
| Name_Buffer (J) := |
| Character'Val (Character'Pos (Name_Buffer (J)) + 32); |
| end if; |
| end loop; |
| |
| Write_Str (Name_Buffer (1 .. Name_Len)); |
| |
| -- Restore Name_Buffer which was clobbered by the call to |
| -- Find_Program_Name |
| |
| Name_Len := Save_Buffer'Last; |
| Name_Buffer (1 .. Name_Len) := Save_Buffer; |
| end Write_Program_Name; |
| |
| ---------------------- |
| -- Write_With_Check -- |
| ---------------------- |
| |
| procedure Write_With_Check (A : Address; N : Integer) is |
| Ignore : Boolean; |
| begin |
| if N = Write (Output_FD, A, N) then |
| return; |
| else |
| Write_Str ("error: disk full writing "); |
| Write_Name_Decoded (Output_File_Name); |
| Write_Eol; |
| Name_Len := Name_Len + 1; |
| Name_Buffer (Name_Len) := ASCII.NUL; |
| Delete_File (Name_Buffer'Address, Ignore); |
| Exit_Program (E_Fatal); |
| end if; |
| end Write_With_Check; |
| |
| ---------------------------- |
| -- Package Initialization -- |
| ---------------------------- |
| |
| procedure Reset_File_Attributes (Attr : System.Address); |
| pragma Import (C, Reset_File_Attributes, "__gnat_reset_attributes"); |
| |
| begin |
| Initialization : declare |
| |
| function Get_Default_Identifier_Character_Set return Character; |
| pragma Import (C, Get_Default_Identifier_Character_Set, |
| "__gnat_get_default_identifier_character_set"); |
| -- Function to determine the default identifier character set, |
| -- which is system dependent. See Opt package spec for a list of |
| -- the possible character codes and their interpretations. |
| |
| function Get_Maximum_File_Name_Length return Int; |
| pragma Import (C, Get_Maximum_File_Name_Length, |
| "__gnat_get_maximum_file_name_length"); |
| -- Function to get maximum file name length for system |
| |
| Sizeof_File_Attributes : Integer; |
| pragma Import (C, Sizeof_File_Attributes, |
| "__gnat_size_of_file_attributes"); |
| |
| begin |
| pragma Assert (Sizeof_File_Attributes <= File_Attributes_Size); |
| |
| Reset_File_Attributes (Unknown_Attributes'Address); |
| |
| Identifier_Character_Set := Get_Default_Identifier_Character_Set; |
| Maximum_File_Name_Length := Get_Maximum_File_Name_Length; |
| |
| -- Following should be removed by having above function return |
| -- Integer'Last as indication of no maximum instead of -1 ??? |
| |
| if Maximum_File_Name_Length = -1 then |
| Maximum_File_Name_Length := Int'Last; |
| end if; |
| |
| Src_Search_Directories.Set_Last (Primary_Directory); |
| Src_Search_Directories.Table (Primary_Directory) := new String'(""); |
| |
| Lib_Search_Directories.Set_Last (Primary_Directory); |
| Lib_Search_Directories.Table (Primary_Directory) := new String'(""); |
| |
| Osint.Initialize; |
| end Initialization; |
| |
| end Osint; |