------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- X R _ T A B L S -- -- -- -- B o d y -- -- -- -- Copyright (C) 1998-2014, 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 Types; use Types; with Osint; with Ada.Unchecked_Conversion; with Ada.Unchecked_Deallocation; with Ada.Strings.Fixed; with Ada.Strings; with Ada.Text_IO; with Ada.Characters.Handling; use Ada.Characters.Handling; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; with GNAT.OS_Lib; use GNAT.OS_Lib; with GNAT.Directory_Operations; use GNAT.Directory_Operations; with GNAT.HTable; use GNAT.HTable; with GNAT.Heap_Sort_G; package body Xr_Tabls is type HTable_Headers is range 1 .. 10000; procedure Set_Next (E : File_Reference; Next : File_Reference); function Next (E : File_Reference) return File_Reference; function Get_Key (E : File_Reference) return Cst_String_Access; function Hash (F : Cst_String_Access) return HTable_Headers; function Equal (F1, F2 : Cst_String_Access) return Boolean; -- The five subprograms above are used to instantiate the static -- htable to store the files that should be processed. package File_HTable is new GNAT.HTable.Static_HTable (Header_Num => HTable_Headers, Element => File_Record, Elmt_Ptr => File_Reference, Null_Ptr => null, Set_Next => Set_Next, Next => Next, Key => Cst_String_Access, Get_Key => Get_Key, Hash => Hash, Equal => Equal); -- A hash table to store all the files referenced in the -- application. The keys in this htable are the name of the files -- themselves, therefore it is assumed that the source path -- doesn't contain twice the same source or ALI file name type Unvisited_Files_Record; type Unvisited_Files_Access is access Unvisited_Files_Record; type Unvisited_Files_Record is record File : File_Reference; Next : Unvisited_Files_Access; end record; -- A special list, in addition to File_HTable, that only stores -- the files that haven't been visited so far. Note that the File -- list points to some data in File_HTable, and thus should never be freed. function Next (E : Declaration_Reference) return Declaration_Reference; procedure Set_Next (E, Next : Declaration_Reference); function Get_Key (E : Declaration_Reference) return Cst_String_Access; -- The subprograms above are used to instantiate the static -- htable to store the entities that have been found in the application package Entities_HTable is new GNAT.HTable.Static_HTable (Header_Num => HTable_Headers, Element => Declaration_Record, Elmt_Ptr => Declaration_Reference, Null_Ptr => null, Set_Next => Set_Next, Next => Next, Key => Cst_String_Access, Get_Key => Get_Key, Hash => Hash, Equal => Equal); -- A hash table to store all the entities defined in the -- application. For each entity, we store a list of its reference -- locations as well. -- The keys in this htable should be created with Key_From_Ref, -- and are the file, line and column of the declaration, which are -- unique for every entity. Entities_Count : Natural := 0; -- Number of entities in Entities_HTable. This is used in the end -- when sorting the table. Longest_File_Name_In_Table : Natural := 0; Unvisited_Files : Unvisited_Files_Access := null; Directories : Project_File_Ptr; Default_Match : Boolean := False; -- The above need commenting ??? function Parse_Gnatls_Src return String; -- Return the standard source directories (taking into account the -- ADA_INCLUDE_PATH environment variable, if Osint.Add_Default_Search_Dirs -- was called first). function Parse_Gnatls_Obj return String; -- Return the standard object directories (taking into account the -- ADA_OBJECTS_PATH environment variable). function Key_From_Ref (File_Ref : File_Reference; Line : Natural; Column : Natural) return String; -- Return a key for the symbol declared at File_Ref, Line, -- Column. This key should be used for lookup in Entity_HTable function Is_Less_Than (Decl1, Decl2 : Declaration_Reference) return Boolean; -- Compare two declarations (the comparison is case-insensitive) function Is_Less_Than (Ref1, Ref2 : Reference) return Boolean; -- Compare two references procedure Store_References (Decl : Declaration_Reference; Get_Writes : Boolean := False; Get_Reads : Boolean := False; Get_Bodies : Boolean := False; Get_Declaration : Boolean := False; Arr : in out Reference_Array; Index : in out Natural); -- Store in Arr, starting at Index, all the references to Decl. The Get_* -- parameters can be used to indicate which references should be stored. -- Constraint_Error will be raised if Arr is not big enough. procedure Sort (Arr : in out Reference_Array); -- Sort an array of references (Arr'First must be 1) -------------- -- Set_Next -- -------------- procedure Set_Next (E : File_Reference; Next : File_Reference) is begin E.Next := Next; end Set_Next; procedure Set_Next (E : Declaration_Reference; Next : Declaration_Reference) is begin E.Next := Next; end Set_Next; ------------- -- Get_Key -- ------------- function Get_Key (E : File_Reference) return Cst_String_Access is begin return E.File; end Get_Key; function Get_Key (E : Declaration_Reference) return Cst_String_Access is begin return E.Key; end Get_Key; ---------- -- Hash -- ---------- function Hash (F : Cst_String_Access) return HTable_Headers is function H is new GNAT.HTable.Hash (HTable_Headers); begin return H (F.all); end Hash; ----------- -- Equal -- ----------- function Equal (F1, F2 : Cst_String_Access) return Boolean is begin return F1.all = F2.all; end Equal; ------------------ -- Key_From_Ref -- ------------------ function Key_From_Ref (File_Ref : File_Reference; Line : Natural; Column : Natural) return String is begin return File_Ref.File.all & Natural'Image (Line) & Natural'Image (Column); end Key_From_Ref; --------------------- -- Add_Declaration -- --------------------- function Add_Declaration (File_Ref : File_Reference; Symbol : String; Line : Natural; Column : Natural; Decl_Type : Character; Is_Parameter : Boolean := False; Remove_Only : Boolean := False; Symbol_Match : Boolean := True) return Declaration_Reference is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Declaration_Record, Declaration_Reference); Key : aliased constant String := Key_From_Ref (File_Ref, Line, Column); New_Decl : Declaration_Reference := Entities_HTable.Get (Key'Unchecked_Access); Is_Param : Boolean := Is_Parameter; begin -- Insert the Declaration in the table. There might already be a -- declaration in the table if the entity is a parameter, so we -- need to check that first. if New_Decl /= null and then New_Decl.Symbol_Length = 0 then Is_Param := Is_Parameter or else New_Decl.Is_Parameter; Entities_HTable.Remove (Key'Unrestricted_Access); Entities_Count := Entities_Count - 1; Free (New_Decl.Key); Unchecked_Free (New_Decl); New_Decl := null; end if; -- The declaration might also already be there for parent types. In -- this case, we should keep the entry, since some other entries are -- pointing to it. if New_Decl = null and then not Remove_Only then New_Decl := new Declaration_Record' (Symbol_Length => Symbol'Length, Symbol => Symbol, Key => new String'(Key), Decl => new Reference_Record' (File => File_Ref, Line => Line, Column => Column, Source_Line => null, Next => null), Is_Parameter => Is_Param, Decl_Type => Decl_Type, Body_Ref => null, Ref_Ref => null, Modif_Ref => null, Match => Symbol_Match and then (Default_Match or else Match (File_Ref, Line, Column)), Par_Symbol => null, Next => null); Entities_HTable.Set (New_Decl); Entities_Count := Entities_Count + 1; if New_Decl.Match then Longest_File_Name_In_Table := Natural'Max (File_Ref.File'Length, Longest_File_Name_In_Table); end if; elsif New_Decl /= null and then not New_Decl.Match then New_Decl.Match := Default_Match or else Match (File_Ref, Line, Column); New_Decl.Is_Parameter := New_Decl.Is_Parameter or Is_Param; elsif New_Decl /= null then New_Decl.Is_Parameter := New_Decl.Is_Parameter or Is_Param; end if; return New_Decl; end Add_Declaration; ---------------------- -- Add_To_Xref_File -- ---------------------- function Add_To_Xref_File (File_Name : String; Visited : Boolean := True; Emit_Warning : Boolean := False; Gnatchop_File : String := ""; Gnatchop_Offset : Integer := 0) return File_Reference is Base : aliased constant String := Base_Name (File_Name); Dir : constant String := Dir_Name (File_Name); Dir_Acc : GNAT.OS_Lib.String_Access := null; Ref : File_Reference; begin -- Do we have a directory name as well? if File_Name /= Base then Dir_Acc := new String'(Dir); end if; Ref := File_HTable.Get (Base'Unchecked_Access); if Ref = null then Ref := new File_Record' (File => new String'(Base), Dir => Dir_Acc, Lines => null, Visited => Visited, Emit_Warning => Emit_Warning, Gnatchop_File => new String'(Gnatchop_File), Gnatchop_Offset => Gnatchop_Offset, Next => null); File_HTable.Set (Ref); if not Visited then -- Keep a separate list for faster access Set_Unvisited (Ref); end if; end if; return Ref; end Add_To_Xref_File; -------------- -- Add_Line -- -------------- procedure Add_Line (File : File_Reference; Line : Natural; Column : Natural) is begin File.Lines := new Ref_In_File'(Line => Line, Column => Column, Next => File.Lines); end Add_Line; ---------------- -- Add_Parent -- ---------------- procedure Add_Parent (Declaration : in out Declaration_Reference; Symbol : String; Line : Natural; Column : Natural; File_Ref : File_Reference) is begin Declaration.Par_Symbol := Add_Declaration (File_Ref, Symbol, Line, Column, Decl_Type => ' ', Symbol_Match => False); end Add_Parent; ------------------- -- Add_Reference -- ------------------- procedure Add_Reference (Declaration : Declaration_Reference; File_Ref : File_Reference; Line : Natural; Column : Natural; Ref_Type : Character; Labels_As_Ref : Boolean) is New_Ref : Reference; New_Decl : Declaration_Reference; pragma Unreferenced (New_Decl); begin case Ref_Type is when 'b' | 'c' | 'H' | 'm' | 'o' | 'r' | 'R' | 's' | 'i' | ' ' | 'x' => null; when 'l' | 'w' => if not Labels_As_Ref then return; end if; when '=' | '<' | '>' | '^' => -- Create dummy declaration in table to report it as a parameter -- In a given ALI file, the declaration of the subprogram comes -- before the declaration of the parameter. However, it is -- possible that another ALI file has been parsed that also -- references the parameter (for instance a named parameter in -- a call), so we need to check whether there already exists a -- declaration for the parameter. New_Decl := Add_Declaration (File_Ref => File_Ref, Symbol => "", Line => Line, Column => Column, Decl_Type => ' ', Is_Parameter => True); when 'e' | 'E' | 'z' | 't' | 'p' | 'P' | 'k' | 'd' => return; when others => Ada.Text_IO.Put_Line ("Unknown reference type: " & Ref_Type); return; end case; New_Ref := new Reference_Record' (File => File_Ref, Line => Line, Column => Column, Source_Line => null, Next => null); -- We can insert the reference into the list directly, since all the -- references will appear only once in the ALI file corresponding to the -- file where they are referenced. This saves a lot of time compared to -- checking the list to check if it exists. case Ref_Type is when 'b' | 'c' => New_Ref.Next := Declaration.Body_Ref; Declaration.Body_Ref := New_Ref; when 'r' | 'R' | 's' | 'H' | 'i' | 'l' | 'o' | ' ' | 'x' | 'w' => New_Ref.Next := Declaration.Ref_Ref; Declaration.Ref_Ref := New_Ref; when 'm' => New_Ref.Next := Declaration.Modif_Ref; Declaration.Modif_Ref := New_Ref; when others => null; end case; if not Declaration.Match then Declaration.Match := Match (File_Ref, Line, Column); end if; if Declaration.Match then Longest_File_Name_In_Table := Natural'Max (File_Ref.File'Length, Longest_File_Name_In_Table); end if; end Add_Reference; ------------------- -- ALI_File_Name -- ------------------- function ALI_File_Name (Ada_File_Name : String) return String is -- ??? Should ideally be based on the naming scheme defined in -- project files. Index : constant Natural := Ada.Strings.Fixed.Index (Ada_File_Name, ".", Going => Ada.Strings.Backward); begin if Index /= 0 then return Ada_File_Name (Ada_File_Name'First .. Index) & Osint.ALI_Suffix.all; else return Ada_File_Name & "." & Osint.ALI_Suffix.all; end if; end ALI_File_Name; ------------------ -- Is_Less_Than -- ------------------ function Is_Less_Than (Ref1, Ref2 : Reference) return Boolean is begin if Ref1 = null then return False; elsif Ref2 = null then return True; end if; if Ref1.File.File.all < Ref2.File.File.all then return True; elsif Ref1.File.File.all = Ref2.File.File.all then return (Ref1.Line < Ref2.Line or else (Ref1.Line = Ref2.Line and then Ref1.Column < Ref2.Column)); end if; return False; end Is_Less_Than; ------------------ -- Is_Less_Than -- ------------------ function Is_Less_Than (Decl1, Decl2 : Declaration_Reference) return Boolean is -- We cannot store the data case-insensitive in the table, -- since we wouldn't be able to find the right casing for the -- display later on. S1 : constant String := To_Lower (Decl1.Symbol); S2 : constant String := To_Lower (Decl2.Symbol); begin if S1 < S2 then return True; elsif S1 > S2 then return False; end if; return Decl1.Key.all < Decl2.Key.all; end Is_Less_Than; ------------------------- -- Create_Project_File -- ------------------------- procedure Create_Project_File (Name : String) is Obj_Dir : Unbounded_String := Null_Unbounded_String; Src_Dir : Unbounded_String := Null_Unbounded_String; Build_Dir : GNAT.OS_Lib.String_Access := new String'(""); F : File_Descriptor; Len : Positive; File_Name : aliased String := Name & ASCII.NUL; begin -- Read the size of the file F := Open_Read (File_Name'Address, Text); -- Project file not found if F /= Invalid_FD then Len := Positive (File_Length (F)); declare Buffer : String (1 .. Len); Index : Positive := Buffer'First; Last : Positive; begin Len := Read (F, Buffer'Address, Len); Close (F); -- First, look for Build_Dir, since all the source and object -- path are relative to it. while Index <= Buffer'Last loop -- Find the end of line Last := Index; while Last <= Buffer'Last and then Buffer (Last) /= ASCII.LF and then Buffer (Last) /= ASCII.CR loop Last := Last + 1; end loop; if Index <= Buffer'Last - 9 and then Buffer (Index .. Index + 9) = "build_dir=" then Index := Index + 10; while Index <= Last and then (Buffer (Index) = ' ' or else Buffer (Index) = ASCII.HT) loop Index := Index + 1; end loop; Free (Build_Dir); Build_Dir := new String'(Buffer (Index .. Last - 1)); end if; Index := Last + 1; -- In case we had a ASCII.CR/ASCII.LF end of line, skip the -- remaining symbol if Index <= Buffer'Last and then Buffer (Index) = ASCII.LF then Index := Index + 1; end if; end loop; -- Now parse the source and object paths Index := Buffer'First; while Index <= Buffer'Last loop -- Find the end of line Last := Index; while Last <= Buffer'Last and then Buffer (Last) /= ASCII.LF and then Buffer (Last) /= ASCII.CR loop Last := Last + 1; end loop; if Index <= Buffer'Last - 7 and then Buffer (Index .. Index + 7) = "src_dir=" then Append (Src_Dir, Normalize_Pathname (Name => Ada.Strings.Fixed.Trim (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both), Directory => Build_Dir.all) & Path_Separator); elsif Index <= Buffer'Last - 7 and then Buffer (Index .. Index + 7) = "obj_dir=" then Append (Obj_Dir, Normalize_Pathname (Name => Ada.Strings.Fixed.Trim (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both), Directory => Build_Dir.all) & Path_Separator); end if; -- In case we had a ASCII.CR/ASCII.LF end of line, skip the -- remaining symbol Index := Last + 1; if Index <= Buffer'Last and then Buffer (Index) = ASCII.LF then Index := Index + 1; end if; end loop; end; end if; Osint.Add_Default_Search_Dirs; declare Src : constant String := Parse_Gnatls_Src; Obj : constant String := Parse_Gnatls_Obj; begin Directories := new Project_File' (Src_Dir_Length => Length (Src_Dir) + Src'Length, Obj_Dir_Length => Length (Obj_Dir) + Obj'Length, Src_Dir => To_String (Src_Dir) & Src, Obj_Dir => To_String (Obj_Dir) & Obj, Src_Dir_Index => 1, Obj_Dir_Index => 1, Last_Obj_Dir_Start => 0); end; Free (Build_Dir); end Create_Project_File; --------------------- -- Current_Obj_Dir -- --------------------- function Current_Obj_Dir return String is begin return Directories.Obj_Dir (Directories.Last_Obj_Dir_Start .. Directories.Obj_Dir_Index - 2); end Current_Obj_Dir; ---------------- -- Get_Column -- ---------------- function Get_Column (Decl : Declaration_Reference) return String is begin return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Column), Ada.Strings.Left); end Get_Column; function Get_Column (Ref : Reference) return String is begin return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Column), Ada.Strings.Left); end Get_Column; --------------------- -- Get_Declaration -- --------------------- function Get_Declaration (File_Ref : File_Reference; Line : Natural; Column : Natural) return Declaration_Reference is Key : aliased constant String := Key_From_Ref (File_Ref, Line, Column); begin return Entities_HTable.Get (Key'Unchecked_Access); end Get_Declaration; ---------------------- -- Get_Emit_Warning -- ---------------------- function Get_Emit_Warning (File : File_Reference) return Boolean is begin return File.Emit_Warning; end Get_Emit_Warning; -------------- -- Get_File -- -------------- function Get_File (Decl : Declaration_Reference; With_Dir : Boolean := False) return String is begin return Get_File (Decl.Decl.File, With_Dir); end Get_File; function Get_File (Ref : Reference; With_Dir : Boolean := False) return String is begin return Get_File (Ref.File, With_Dir); end Get_File; function Get_File (File : File_Reference; With_Dir : Boolean := False; Strip : Natural := 0) return String is Tmp : GNAT.OS_Lib.String_Access; function Internal_Strip (Full_Name : String) return String; -- Internal function to process the Strip parameter -------------------- -- Internal_Strip -- -------------------- function Internal_Strip (Full_Name : String) return String is Unit_End : Natural; Extension_Start : Natural; S : Natural; begin if Strip = 0 then return Full_Name; end if; -- Isolate the file extension Extension_Start := Full_Name'Last; while Extension_Start >= Full_Name'First and then Full_Name (Extension_Start) /= '.' loop Extension_Start := Extension_Start - 1; end loop; -- Strip the right number of subunit_names S := Strip; Unit_End := Extension_Start - 1; while Unit_End >= Full_Name'First and then S > 0 loop if Full_Name (Unit_End) = '-' then S := S - 1; end if; Unit_End := Unit_End - 1; end loop; if Unit_End < Full_Name'First then return ""; else return Full_Name (Full_Name'First .. Unit_End) & Full_Name (Extension_Start .. Full_Name'Last); end if; end Internal_Strip; -- Start of processing for Get_File; begin -- If we do not want the full path name if not With_Dir then return Internal_Strip (File.File.all); end if; if File.Dir = null then if Ada.Strings.Fixed.Tail (File.File.all, 3) = Osint.ALI_Suffix.all then Tmp := Locate_Regular_File (Internal_Strip (File.File.all), Directories.Obj_Dir); else Tmp := Locate_Regular_File (File.File.all, Directories.Src_Dir); end if; if Tmp = null then File.Dir := new String'(""); else File.Dir := new String'(Dir_Name (Tmp.all)); Free (Tmp); end if; end if; return Internal_Strip (File.Dir.all & File.File.all); end Get_File; ------------------ -- Get_File_Ref -- ------------------ function Get_File_Ref (Ref : Reference) return File_Reference is begin return Ref.File; end Get_File_Ref; ----------------------- -- Get_Gnatchop_File -- ----------------------- function Get_Gnatchop_File (File : File_Reference; With_Dir : Boolean := False) return String is begin if File.Gnatchop_File.all = "" then return Get_File (File, With_Dir); else return File.Gnatchop_File.all; end if; end Get_Gnatchop_File; function Get_Gnatchop_File (Ref : Reference; With_Dir : Boolean := False) return String is begin return Get_Gnatchop_File (Ref.File, With_Dir); end Get_Gnatchop_File; function Get_Gnatchop_File (Decl : Declaration_Reference; With_Dir : Boolean := False) return String is begin return Get_Gnatchop_File (Decl.Decl.File, With_Dir); end Get_Gnatchop_File; -------------- -- Get_Line -- -------------- function Get_Line (Decl : Declaration_Reference) return String is begin return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Line), Ada.Strings.Left); end Get_Line; function Get_Line (Ref : Reference) return String is begin return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Line), Ada.Strings.Left); end Get_Line; ---------------- -- Get_Parent -- ---------------- function Get_Parent (Decl : Declaration_Reference) return Declaration_Reference is begin return Decl.Par_Symbol; end Get_Parent; --------------------- -- Get_Source_Line -- --------------------- function Get_Source_Line (Ref : Reference) return String is begin if Ref.Source_Line /= null then return Ref.Source_Line.all; else return ""; end if; end Get_Source_Line; function Get_Source_Line (Decl : Declaration_Reference) return String is begin if Decl.Decl.Source_Line /= null then return Decl.Decl.Source_Line.all; else return ""; end if; end Get_Source_Line; ---------------- -- Get_Symbol -- ---------------- function Get_Symbol (Decl : Declaration_Reference) return String is begin return Decl.Symbol; end Get_Symbol; -------------- -- Get_Type -- -------------- function Get_Type (Decl : Declaration_Reference) return Character is begin return Decl.Decl_Type; end Get_Type; ---------- -- Sort -- ---------- procedure Sort (Arr : in out Reference_Array) is Tmp : Reference; function Lt (Op1, Op2 : Natural) return Boolean; procedure Move (From, To : Natural); -- See GNAT.Heap_Sort_G -------- -- Lt -- -------- function Lt (Op1, Op2 : Natural) return Boolean is begin if Op1 = 0 then return Is_Less_Than (Tmp, Arr (Op2)); elsif Op2 = 0 then return Is_Less_Than (Arr (Op1), Tmp); else return Is_Less_Than (Arr (Op1), Arr (Op2)); end if; end Lt; ---------- -- Move -- ---------- procedure Move (From, To : Natural) is begin if To = 0 then Tmp := Arr (From); elsif From = 0 then Arr (To) := Tmp; else Arr (To) := Arr (From); end if; end Move; package Ref_Sort is new GNAT.Heap_Sort_G (Move, Lt); -- Start of processing for Sort begin Ref_Sort.Sort (Arr'Last); end Sort; ----------------------- -- Grep_Source_Files -- ----------------------- procedure Grep_Source_Files is Length : Natural := 0; Decl : Declaration_Reference := Entities_HTable.Get_First; Arr : Reference_Array_Access; Index : Natural; End_Index : Natural; Current_File : File_Reference; Current_Line : Cst_String_Access; Buffer : GNAT.OS_Lib.String_Access; Ref : Reference; Line : Natural; begin -- Create a temporary array, where all references will be -- sorted by files. This way, we only have to read the source -- files once. while Decl /= null loop -- Add 1 for the declaration itself Length := Length + References_Count (Decl, True, True, True) + 1; Decl := Entities_HTable.Get_Next; end loop; Arr := new Reference_Array (1 .. Length); Index := Arr'First; Decl := Entities_HTable.Get_First; while Decl /= null loop Store_References (Decl, True, True, True, True, Arr.all, Index); Decl := Entities_HTable.Get_Next; end loop; Sort (Arr.all); -- Now traverse the whole array and find the appropriate source -- lines. for R in Arr'Range loop Ref := Arr (R); if Ref.File /= Current_File then Free (Buffer); begin Read_File (Get_File (Ref.File, With_Dir => True), Buffer); End_Index := Buffer'First - 1; Line := 0; exception when Ada.Text_IO.Name_Error | Ada.Text_IO.End_Error => Line := Natural'Last; end; Current_File := Ref.File; end if; if Ref.Line > Line then -- Do not free Current_Line, it is referenced by the last -- Ref we processed. loop Index := End_Index + 1; loop End_Index := End_Index + 1; exit when End_Index > Buffer'Last or else Buffer (End_Index) = ASCII.LF; end loop; -- Skip spaces at beginning of line while Index < End_Index and then (Buffer (Index) = ' ' or else Buffer (Index) = ASCII.HT) loop Index := Index + 1; end loop; Line := Line + 1; exit when Ref.Line = Line; end loop; Current_Line := new String'(Buffer (Index .. End_Index - 1)); end if; Ref.Source_Line := Current_Line; end loop; Free (Buffer); Free (Arr); end Grep_Source_Files; --------------- -- Read_File -- --------------- procedure Read_File (File_Name : String; Contents : out GNAT.OS_Lib.String_Access) is Name_0 : constant String := File_Name & ASCII.NUL; FD : constant File_Descriptor := Open_Read (Name_0'Address, Binary); Length : Natural; begin if FD = Invalid_FD then raise Ada.Text_IO.Name_Error; end if; -- Include room for EOF char Length := Natural (File_Length (FD)); declare Buffer : String (1 .. Length + 1); This_Read : Integer; Read_Ptr : Natural := 1; begin loop This_Read := Read (FD, A => Buffer (Read_Ptr)'Address, N => Length + 1 - Read_Ptr); Read_Ptr := Read_Ptr + Integer'Max (This_Read, 0); exit when This_Read <= 0; end loop; Buffer (Read_Ptr) := EOF; Contents := new String'(Buffer (1 .. Read_Ptr)); if Read_Ptr /= Length + 1 then raise Ada.Text_IO.End_Error; end if; Close (FD); end; end Read_File; ----------------------- -- Longest_File_Name -- ----------------------- function Longest_File_Name return Natural is begin return Longest_File_Name_In_Table; end Longest_File_Name; ----------- -- Match -- ----------- function Match (File : File_Reference; Line : Natural; Column : Natural) return Boolean is Ref : Ref_In_File_Ptr := File.Lines; begin while Ref /= null loop if (Ref.Line = 0 or else Ref.Line = Line) and then (Ref.Column = 0 or else Ref.Column = Column) then return True; end if; Ref := Ref.Next; end loop; return False; end Match; ----------- -- Match -- ----------- function Match (Decl : Declaration_Reference) return Boolean is begin return Decl.Match; end Match; ---------- -- Next -- ---------- function Next (E : File_Reference) return File_Reference is begin return E.Next; end Next; function Next (E : Declaration_Reference) return Declaration_Reference is begin return E.Next; end Next; ------------------ -- Next_Obj_Dir -- ------------------ function Next_Obj_Dir return String is First : constant Integer := Directories.Obj_Dir_Index; Last : Integer; begin Last := Directories.Obj_Dir_Index; if Last > Directories.Obj_Dir_Length then return String'(1 .. 0 => ' '); end if; while Directories.Obj_Dir (Last) /= Path_Separator loop Last := Last + 1; end loop; Directories.Obj_Dir_Index := Last + 1; Directories.Last_Obj_Dir_Start := First; return Directories.Obj_Dir (First .. Last - 1); end Next_Obj_Dir; ------------------------- -- Next_Unvisited_File -- ------------------------- function Next_Unvisited_File return File_Reference is procedure Unchecked_Free is new Ada.Unchecked_Deallocation (Unvisited_Files_Record, Unvisited_Files_Access); Ref : File_Reference; Tmp : Unvisited_Files_Access; begin if Unvisited_Files = null then return Empty_File; else Tmp := Unvisited_Files; Ref := Unvisited_Files.File; Unvisited_Files := Unvisited_Files.Next; Unchecked_Free (Tmp); return Ref; end if; end Next_Unvisited_File; ---------------------- -- Parse_Gnatls_Src -- ---------------------- function Parse_Gnatls_Src return String is Length : Natural; begin Length := 0; for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then Length := Length + 2; else Length := Length + Osint.Dir_In_Src_Search_Path (J)'Length + 1; end if; end loop; declare Result : String (1 .. Length); L : Natural; begin L := Result'First; for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then Result (L .. L + 1) := "." & Path_Separator; L := L + 2; else Result (L .. L + Osint.Dir_In_Src_Search_Path (J)'Length - 1) := Osint.Dir_In_Src_Search_Path (J).all; L := L + Osint.Dir_In_Src_Search_Path (J)'Length; Result (L) := Path_Separator; L := L + 1; end if; end loop; return Result; end; end Parse_Gnatls_Src; ---------------------- -- Parse_Gnatls_Obj -- ---------------------- function Parse_Gnatls_Obj return String is Length : Natural; begin Length := 0; for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then Length := Length + 2; else Length := Length + Osint.Dir_In_Obj_Search_Path (J)'Length + 1; end if; end loop; declare Result : String (1 .. Length); L : Natural; begin L := Result'First; for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then Result (L .. L + 1) := "." & Path_Separator; L := L + 2; else Result (L .. L + Osint.Dir_In_Obj_Search_Path (J)'Length - 1) := Osint.Dir_In_Obj_Search_Path (J).all; L := L + Osint.Dir_In_Obj_Search_Path (J)'Length; Result (L) := Path_Separator; L := L + 1; end if; end loop; return Result; end; end Parse_Gnatls_Obj; ------------------- -- Reset_Obj_Dir -- ------------------- procedure Reset_Obj_Dir is begin Directories.Obj_Dir_Index := 1; end Reset_Obj_Dir; ----------------------- -- Set_Default_Match -- ----------------------- procedure Set_Default_Match (Value : Boolean) is begin Default_Match := Value; end Set_Default_Match; ---------- -- Free -- ---------- procedure Free (Str : in out Cst_String_Access) is function Convert is new Ada.Unchecked_Conversion (Cst_String_Access, GNAT.OS_Lib.String_Access); S : GNAT.OS_Lib.String_Access := Convert (Str); begin Free (S); Str := null; end Free; --------------------- -- Reset_Directory -- --------------------- procedure Reset_Directory (File : File_Reference) is begin Free (File.Dir); end Reset_Directory; ------------------- -- Set_Unvisited -- ------------------- procedure Set_Unvisited (File_Ref : File_Reference) is F : constant String := Get_File (File_Ref, With_Dir => False); begin File_Ref.Visited := False; -- ??? Do not add a source file to the list. This is true at -- least for gnatxref, and probably for gnatfind as well if F'Length > 4 and then F (F'Last - 3 .. F'Last) = "." & Osint.ALI_Suffix.all then Unvisited_Files := new Unvisited_Files_Record' (File => File_Ref, Next => Unvisited_Files); end if; end Set_Unvisited; ---------------------- -- Get_Declarations -- ---------------------- function Get_Declarations (Sorted : Boolean := True) return Declaration_Array_Access is Arr : constant Declaration_Array_Access := new Declaration_Array (1 .. Entities_Count); Decl : Declaration_Reference := Entities_HTable.Get_First; Index : Natural := Arr'First; Tmp : Declaration_Reference; procedure Move (From : Natural; To : Natural); function Lt (Op1, Op2 : Natural) return Boolean; -- See GNAT.Heap_Sort_G -------- -- Lt -- -------- function Lt (Op1, Op2 : Natural) return Boolean is begin if Op1 = 0 then return Is_Less_Than (Tmp, Arr (Op2)); elsif Op2 = 0 then return Is_Less_Than (Arr (Op1), Tmp); else return Is_Less_Than (Arr (Op1), Arr (Op2)); end if; end Lt; ---------- -- Move -- ---------- procedure Move (From : Natural; To : Natural) is begin if To = 0 then Tmp := Arr (From); elsif From = 0 then Arr (To) := Tmp; else Arr (To) := Arr (From); end if; end Move; package Decl_Sort is new GNAT.Heap_Sort_G (Move, Lt); -- Start of processing for Get_Declarations begin while Decl /= null loop Arr (Index) := Decl; Index := Index + 1; Decl := Entities_HTable.Get_Next; end loop; if Sorted and then Arr'Length /= 0 then Decl_Sort.Sort (Entities_Count); end if; return Arr; end Get_Declarations; ---------------------- -- References_Count -- ---------------------- function References_Count (Decl : Declaration_Reference; Get_Reads : Boolean := False; Get_Writes : Boolean := False; Get_Bodies : Boolean := False) return Natural is function List_Length (E : Reference) return Natural; -- Return the number of references in E ----------------- -- List_Length -- ----------------- function List_Length (E : Reference) return Natural is L : Natural := 0; E1 : Reference := E; begin while E1 /= null loop L := L + 1; E1 := E1.Next; end loop; return L; end List_Length; Length : Natural := 0; -- Start of processing for References_Count begin if Get_Reads then Length := List_Length (Decl.Ref_Ref); end if; if Get_Writes then Length := Length + List_Length (Decl.Modif_Ref); end if; if Get_Bodies then Length := Length + List_Length (Decl.Body_Ref); end if; return Length; end References_Count; ---------------------- -- Store_References -- ---------------------- procedure Store_References (Decl : Declaration_Reference; Get_Writes : Boolean := False; Get_Reads : Boolean := False; Get_Bodies : Boolean := False; Get_Declaration : Boolean := False; Arr : in out Reference_Array; Index : in out Natural) is procedure Add (List : Reference); -- Add all the references in List to Arr --------- -- Add -- --------- procedure Add (List : Reference) is E : Reference := List; begin while E /= null loop Arr (Index) := E; Index := Index + 1; E := E.Next; end loop; end Add; -- Start of processing for Store_References begin if Get_Declaration then Add (Decl.Decl); end if; if Get_Reads then Add (Decl.Ref_Ref); end if; if Get_Writes then Add (Decl.Modif_Ref); end if; if Get_Bodies then Add (Decl.Body_Ref); end if; end Store_References; -------------------- -- Get_References -- -------------------- function Get_References (Decl : Declaration_Reference; Get_Reads : Boolean := False; Get_Writes : Boolean := False; Get_Bodies : Boolean := False) return Reference_Array_Access is Length : constant Natural := References_Count (Decl, Get_Reads, Get_Writes, Get_Bodies); Arr : constant Reference_Array_Access := new Reference_Array (1 .. Length); Index : Natural := Arr'First; begin Store_References (Decl => Decl, Get_Writes => Get_Writes, Get_Reads => Get_Reads, Get_Bodies => Get_Bodies, Get_Declaration => False, Arr => Arr.all, Index => Index); if Arr'Length /= 0 then Sort (Arr.all); end if; return Arr; end Get_References; ---------- -- Free -- ---------- procedure Free (Arr : in out Reference_Array_Access) is procedure Internal is new Ada.Unchecked_Deallocation (Reference_Array, Reference_Array_Access); begin Internal (Arr); end Free; ------------------ -- Is_Parameter -- ------------------ function Is_Parameter (Decl : Declaration_Reference) return Boolean is begin return Decl.Is_Parameter; end Is_Parameter; end Xr_Tabls;