2016-10-26 François Dumont <fdumont@gcc.gnu.org>
[official-gcc.git] / gcc / ada / xr_tabls.adb
blob0b97c121da22e5f1e6486b467023e524e73b2070
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- X R _ T A B L S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1998-2014, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Types; use Types;
27 with Osint;
29 with Ada.Unchecked_Conversion;
30 with Ada.Unchecked_Deallocation;
31 with Ada.Strings.Fixed;
32 with Ada.Strings;
33 with Ada.Text_IO;
34 with Ada.Characters.Handling; use Ada.Characters.Handling;
35 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
37 with GNAT.OS_Lib; use GNAT.OS_Lib;
38 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
39 with GNAT.HTable; use GNAT.HTable;
40 with GNAT.Heap_Sort_G;
42 package body Xr_Tabls is
44 type HTable_Headers is range 1 .. 10000;
46 procedure Set_Next (E : File_Reference; Next : File_Reference);
47 function Next (E : File_Reference) return File_Reference;
48 function Get_Key (E : File_Reference) return Cst_String_Access;
49 function Hash (F : Cst_String_Access) return HTable_Headers;
50 function Equal (F1, F2 : Cst_String_Access) return Boolean;
51 -- The five subprograms above are used to instantiate the static
52 -- htable to store the files that should be processed.
54 package File_HTable is new GNAT.HTable.Static_HTable
55 (Header_Num => HTable_Headers,
56 Element => File_Record,
57 Elmt_Ptr => File_Reference,
58 Null_Ptr => null,
59 Set_Next => Set_Next,
60 Next => Next,
61 Key => Cst_String_Access,
62 Get_Key => Get_Key,
63 Hash => Hash,
64 Equal => Equal);
65 -- A hash table to store all the files referenced in the
66 -- application. The keys in this htable are the name of the files
67 -- themselves, therefore it is assumed that the source path
68 -- doesn't contain twice the same source or ALI file name
70 type Unvisited_Files_Record;
71 type Unvisited_Files_Access is access Unvisited_Files_Record;
72 type Unvisited_Files_Record is record
73 File : File_Reference;
74 Next : Unvisited_Files_Access;
75 end record;
76 -- A special list, in addition to File_HTable, that only stores
77 -- the files that haven't been visited so far. Note that the File
78 -- list points to some data in File_HTable, and thus should never be freed.
80 function Next (E : Declaration_Reference) return Declaration_Reference;
81 procedure Set_Next (E, Next : Declaration_Reference);
82 function Get_Key (E : Declaration_Reference) return Cst_String_Access;
83 -- The subprograms above are used to instantiate the static
84 -- htable to store the entities that have been found in the application
86 package Entities_HTable is new GNAT.HTable.Static_HTable
87 (Header_Num => HTable_Headers,
88 Element => Declaration_Record,
89 Elmt_Ptr => Declaration_Reference,
90 Null_Ptr => null,
91 Set_Next => Set_Next,
92 Next => Next,
93 Key => Cst_String_Access,
94 Get_Key => Get_Key,
95 Hash => Hash,
96 Equal => Equal);
97 -- A hash table to store all the entities defined in the
98 -- application. For each entity, we store a list of its reference
99 -- locations as well.
100 -- The keys in this htable should be created with Key_From_Ref,
101 -- and are the file, line and column of the declaration, which are
102 -- unique for every entity.
104 Entities_Count : Natural := 0;
105 -- Number of entities in Entities_HTable. This is used in the end
106 -- when sorting the table.
108 Longest_File_Name_In_Table : Natural := 0;
109 Unvisited_Files : Unvisited_Files_Access := null;
110 Directories : Project_File_Ptr;
111 Default_Match : Boolean := False;
112 -- The above need commenting ???
114 function Parse_Gnatls_Src return String;
115 -- Return the standard source directories (taking into account the
116 -- ADA_INCLUDE_PATH environment variable, if Osint.Add_Default_Search_Dirs
117 -- was called first).
119 function Parse_Gnatls_Obj return String;
120 -- Return the standard object directories (taking into account the
121 -- ADA_OBJECTS_PATH environment variable).
123 function Key_From_Ref
124 (File_Ref : File_Reference;
125 Line : Natural;
126 Column : Natural)
127 return String;
128 -- Return a key for the symbol declared at File_Ref, Line,
129 -- Column. This key should be used for lookup in Entity_HTable
131 function Is_Less_Than (Decl1, Decl2 : Declaration_Reference) return Boolean;
132 -- Compare two declarations (the comparison is case-insensitive)
134 function Is_Less_Than (Ref1, Ref2 : Reference) return Boolean;
135 -- Compare two references
137 procedure Store_References
138 (Decl : Declaration_Reference;
139 Get_Writes : Boolean := False;
140 Get_Reads : Boolean := False;
141 Get_Bodies : Boolean := False;
142 Get_Declaration : Boolean := False;
143 Arr : in out Reference_Array;
144 Index : in out Natural);
145 -- Store in Arr, starting at Index, all the references to Decl. The Get_*
146 -- parameters can be used to indicate which references should be stored.
147 -- Constraint_Error will be raised if Arr is not big enough.
149 procedure Sort (Arr : in out Reference_Array);
150 -- Sort an array of references (Arr'First must be 1)
152 --------------
153 -- Set_Next --
154 --------------
156 procedure Set_Next (E : File_Reference; Next : File_Reference) is
157 begin
158 E.Next := Next;
159 end Set_Next;
161 procedure Set_Next
162 (E : Declaration_Reference; Next : Declaration_Reference) is
163 begin
164 E.Next := Next;
165 end Set_Next;
167 -------------
168 -- Get_Key --
169 -------------
171 function Get_Key (E : File_Reference) return Cst_String_Access is
172 begin
173 return E.File;
174 end Get_Key;
176 function Get_Key (E : Declaration_Reference) return Cst_String_Access is
177 begin
178 return E.Key;
179 end Get_Key;
181 ----------
182 -- Hash --
183 ----------
185 function Hash (F : Cst_String_Access) return HTable_Headers is
186 function H is new GNAT.HTable.Hash (HTable_Headers);
188 begin
189 return H (F.all);
190 end Hash;
192 -----------
193 -- Equal --
194 -----------
196 function Equal (F1, F2 : Cst_String_Access) return Boolean is
197 begin
198 return F1.all = F2.all;
199 end Equal;
201 ------------------
202 -- Key_From_Ref --
203 ------------------
205 function Key_From_Ref
206 (File_Ref : File_Reference;
207 Line : Natural;
208 Column : Natural)
209 return String
211 begin
212 return File_Ref.File.all & Natural'Image (Line) & Natural'Image (Column);
213 end Key_From_Ref;
215 ---------------------
216 -- Add_Declaration --
217 ---------------------
219 function Add_Declaration
220 (File_Ref : File_Reference;
221 Symbol : String;
222 Line : Natural;
223 Column : Natural;
224 Decl_Type : Character;
225 Is_Parameter : Boolean := False;
226 Remove_Only : Boolean := False;
227 Symbol_Match : Boolean := True)
228 return Declaration_Reference
230 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
231 (Declaration_Record, Declaration_Reference);
233 Key : aliased constant String := Key_From_Ref (File_Ref, Line, Column);
235 New_Decl : Declaration_Reference :=
236 Entities_HTable.Get (Key'Unchecked_Access);
238 Is_Param : Boolean := Is_Parameter;
240 begin
241 -- Insert the Declaration in the table. There might already be a
242 -- declaration in the table if the entity is a parameter, so we
243 -- need to check that first.
245 if New_Decl /= null and then New_Decl.Symbol_Length = 0 then
246 Is_Param := Is_Parameter or else New_Decl.Is_Parameter;
247 Entities_HTable.Remove (Key'Unrestricted_Access);
248 Entities_Count := Entities_Count - 1;
249 Free (New_Decl.Key);
250 Unchecked_Free (New_Decl);
251 New_Decl := null;
252 end if;
254 -- The declaration might also already be there for parent types. In
255 -- this case, we should keep the entry, since some other entries are
256 -- pointing to it.
258 if New_Decl = null
259 and then not Remove_Only
260 then
261 New_Decl :=
262 new Declaration_Record'
263 (Symbol_Length => Symbol'Length,
264 Symbol => Symbol,
265 Key => new String'(Key),
266 Decl => new Reference_Record'
267 (File => File_Ref,
268 Line => Line,
269 Column => Column,
270 Source_Line => null,
271 Next => null),
272 Is_Parameter => Is_Param,
273 Decl_Type => Decl_Type,
274 Body_Ref => null,
275 Ref_Ref => null,
276 Modif_Ref => null,
277 Match => Symbol_Match
278 and then
279 (Default_Match
280 or else Match (File_Ref, Line, Column)),
281 Par_Symbol => null,
282 Next => null);
284 Entities_HTable.Set (New_Decl);
285 Entities_Count := Entities_Count + 1;
287 if New_Decl.Match then
288 Longest_File_Name_In_Table :=
289 Natural'Max (File_Ref.File'Length, Longest_File_Name_In_Table);
290 end if;
292 elsif New_Decl /= null
293 and then not New_Decl.Match
294 then
295 New_Decl.Match := Default_Match
296 or else Match (File_Ref, Line, Column);
297 New_Decl.Is_Parameter := New_Decl.Is_Parameter or Is_Param;
299 elsif New_Decl /= null then
300 New_Decl.Is_Parameter := New_Decl.Is_Parameter or Is_Param;
301 end if;
303 return New_Decl;
304 end Add_Declaration;
306 ----------------------
307 -- Add_To_Xref_File --
308 ----------------------
310 function Add_To_Xref_File
311 (File_Name : String;
312 Visited : Boolean := True;
313 Emit_Warning : Boolean := False;
314 Gnatchop_File : String := "";
315 Gnatchop_Offset : Integer := 0) return File_Reference
317 Base : aliased constant String := Base_Name (File_Name);
318 Dir : constant String := Dir_Name (File_Name);
319 Dir_Acc : GNAT.OS_Lib.String_Access := null;
320 Ref : File_Reference;
322 begin
323 -- Do we have a directory name as well?
325 if File_Name /= Base then
326 Dir_Acc := new String'(Dir);
327 end if;
329 Ref := File_HTable.Get (Base'Unchecked_Access);
330 if Ref = null then
331 Ref := new File_Record'
332 (File => new String'(Base),
333 Dir => Dir_Acc,
334 Lines => null,
335 Visited => Visited,
336 Emit_Warning => Emit_Warning,
337 Gnatchop_File => new String'(Gnatchop_File),
338 Gnatchop_Offset => Gnatchop_Offset,
339 Next => null);
340 File_HTable.Set (Ref);
342 if not Visited then
344 -- Keep a separate list for faster access
346 Set_Unvisited (Ref);
347 end if;
348 end if;
349 return Ref;
350 end Add_To_Xref_File;
352 --------------
353 -- Add_Line --
354 --------------
356 procedure Add_Line
357 (File : File_Reference;
358 Line : Natural;
359 Column : Natural)
361 begin
362 File.Lines := new Ref_In_File'(Line => Line,
363 Column => Column,
364 Next => File.Lines);
365 end Add_Line;
367 ----------------
368 -- Add_Parent --
369 ----------------
371 procedure Add_Parent
372 (Declaration : in out Declaration_Reference;
373 Symbol : String;
374 Line : Natural;
375 Column : Natural;
376 File_Ref : File_Reference)
378 begin
379 Declaration.Par_Symbol :=
380 Add_Declaration
381 (File_Ref, Symbol, Line, Column,
382 Decl_Type => ' ',
383 Symbol_Match => False);
384 end Add_Parent;
386 -------------------
387 -- Add_Reference --
388 -------------------
390 procedure Add_Reference
391 (Declaration : Declaration_Reference;
392 File_Ref : File_Reference;
393 Line : Natural;
394 Column : Natural;
395 Ref_Type : Character;
396 Labels_As_Ref : Boolean)
398 New_Ref : Reference;
399 New_Decl : Declaration_Reference;
400 pragma Unreferenced (New_Decl);
402 begin
403 case Ref_Type is
404 when 'b' | 'c' | 'H' | 'm' | 'o' | 'r' | 'R' |
405 's' | 'i' | ' ' | 'x' =>
406 null;
408 when 'l' | 'w' =>
409 if not Labels_As_Ref then
410 return;
411 end if;
413 when '=' | '<' | '>' | '^' =>
415 -- Create dummy declaration in table to report it as a parameter
417 -- In a given ALI file, the declaration of the subprogram comes
418 -- before the declaration of the parameter. However, it is
419 -- possible that another ALI file has been parsed that also
420 -- references the parameter (for instance a named parameter in
421 -- a call), so we need to check whether there already exists a
422 -- declaration for the parameter.
424 New_Decl :=
425 Add_Declaration
426 (File_Ref => File_Ref,
427 Symbol => "",
428 Line => Line,
429 Column => Column,
430 Decl_Type => ' ',
431 Is_Parameter => True);
433 when 'e' | 'E' | 'z' | 't' | 'p' | 'P' | 'k' | 'd' =>
434 return;
436 when others =>
437 Ada.Text_IO.Put_Line ("Unknown reference type: " & Ref_Type);
438 return;
439 end case;
441 New_Ref := new Reference_Record'
442 (File => File_Ref,
443 Line => Line,
444 Column => Column,
445 Source_Line => null,
446 Next => null);
448 -- We can insert the reference into the list directly, since all the
449 -- references will appear only once in the ALI file corresponding to the
450 -- file where they are referenced. This saves a lot of time compared to
451 -- checking the list to check if it exists.
453 case Ref_Type is
454 when 'b' | 'c' =>
455 New_Ref.Next := Declaration.Body_Ref;
456 Declaration.Body_Ref := New_Ref;
458 when 'r' | 'R' | 's' | 'H' | 'i' | 'l' | 'o' | ' ' | 'x' | 'w' =>
459 New_Ref.Next := Declaration.Ref_Ref;
460 Declaration.Ref_Ref := New_Ref;
462 when 'm' =>
463 New_Ref.Next := Declaration.Modif_Ref;
464 Declaration.Modif_Ref := New_Ref;
466 when others =>
467 null;
468 end case;
470 if not Declaration.Match then
471 Declaration.Match := Match (File_Ref, Line, Column);
472 end if;
474 if Declaration.Match then
475 Longest_File_Name_In_Table :=
476 Natural'Max (File_Ref.File'Length, Longest_File_Name_In_Table);
477 end if;
478 end Add_Reference;
480 -------------------
481 -- ALI_File_Name --
482 -------------------
484 function ALI_File_Name (Ada_File_Name : String) return String is
486 -- ??? Should ideally be based on the naming scheme defined in
487 -- project files.
489 Index : constant Natural :=
490 Ada.Strings.Fixed.Index
491 (Ada_File_Name, ".", Going => Ada.Strings.Backward);
493 begin
494 if Index /= 0 then
495 return Ada_File_Name (Ada_File_Name'First .. Index)
496 & Osint.ALI_Suffix.all;
497 else
498 return Ada_File_Name & "." & Osint.ALI_Suffix.all;
499 end if;
500 end ALI_File_Name;
502 ------------------
503 -- Is_Less_Than --
504 ------------------
506 function Is_Less_Than (Ref1, Ref2 : Reference) return Boolean is
507 begin
508 if Ref1 = null then
509 return False;
510 elsif Ref2 = null then
511 return True;
512 end if;
514 if Ref1.File.File.all < Ref2.File.File.all then
515 return True;
517 elsif Ref1.File.File.all = Ref2.File.File.all then
518 return (Ref1.Line < Ref2.Line
519 or else (Ref1.Line = Ref2.Line
520 and then Ref1.Column < Ref2.Column));
521 end if;
523 return False;
524 end Is_Less_Than;
526 ------------------
527 -- Is_Less_Than --
528 ------------------
530 function Is_Less_Than (Decl1, Decl2 : Declaration_Reference) return Boolean
532 -- We cannot store the data case-insensitive in the table,
533 -- since we wouldn't be able to find the right casing for the
534 -- display later on.
536 S1 : constant String := To_Lower (Decl1.Symbol);
537 S2 : constant String := To_Lower (Decl2.Symbol);
539 begin
540 if S1 < S2 then
541 return True;
542 elsif S1 > S2 then
543 return False;
544 end if;
546 return Decl1.Key.all < Decl2.Key.all;
547 end Is_Less_Than;
549 -------------------------
550 -- Create_Project_File --
551 -------------------------
553 procedure Create_Project_File (Name : String) is
554 Obj_Dir : Unbounded_String := Null_Unbounded_String;
555 Src_Dir : Unbounded_String := Null_Unbounded_String;
556 Build_Dir : GNAT.OS_Lib.String_Access := new String'("");
558 F : File_Descriptor;
559 Len : Positive;
560 File_Name : aliased String := Name & ASCII.NUL;
562 begin
563 -- Read the size of the file
565 F := Open_Read (File_Name'Address, Text);
567 -- Project file not found
569 if F /= Invalid_FD then
570 Len := Positive (File_Length (F));
572 declare
573 Buffer : String (1 .. Len);
574 Index : Positive := Buffer'First;
575 Last : Positive;
577 begin
578 Len := Read (F, Buffer'Address, Len);
579 Close (F);
581 -- First, look for Build_Dir, since all the source and object
582 -- path are relative to it.
584 while Index <= Buffer'Last loop
586 -- Find the end of line
588 Last := Index;
589 while Last <= Buffer'Last
590 and then Buffer (Last) /= ASCII.LF
591 and then Buffer (Last) /= ASCII.CR
592 loop
593 Last := Last + 1;
594 end loop;
596 if Index <= Buffer'Last - 9
597 and then Buffer (Index .. Index + 9) = "build_dir="
598 then
599 Index := Index + 10;
600 while Index <= Last
601 and then (Buffer (Index) = ' '
602 or else Buffer (Index) = ASCII.HT)
603 loop
604 Index := Index + 1;
605 end loop;
607 Free (Build_Dir);
608 Build_Dir := new String'(Buffer (Index .. Last - 1));
609 end if;
611 Index := Last + 1;
613 -- In case we had a ASCII.CR/ASCII.LF end of line, skip the
614 -- remaining symbol
616 if Index <= Buffer'Last
617 and then Buffer (Index) = ASCII.LF
618 then
619 Index := Index + 1;
620 end if;
621 end loop;
623 -- Now parse the source and object paths
625 Index := Buffer'First;
626 while Index <= Buffer'Last loop
628 -- Find the end of line
630 Last := Index;
631 while Last <= Buffer'Last
632 and then Buffer (Last) /= ASCII.LF
633 and then Buffer (Last) /= ASCII.CR
634 loop
635 Last := Last + 1;
636 end loop;
638 if Index <= Buffer'Last - 7
639 and then Buffer (Index .. Index + 7) = "src_dir="
640 then
641 Append (Src_Dir, Normalize_Pathname
642 (Name => Ada.Strings.Fixed.Trim
643 (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both),
644 Directory => Build_Dir.all) & Path_Separator);
646 elsif Index <= Buffer'Last - 7
647 and then Buffer (Index .. Index + 7) = "obj_dir="
648 then
649 Append (Obj_Dir, Normalize_Pathname
650 (Name => Ada.Strings.Fixed.Trim
651 (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both),
652 Directory => Build_Dir.all) & Path_Separator);
653 end if;
655 -- In case we had a ASCII.CR/ASCII.LF end of line, skip the
656 -- remaining symbol
657 Index := Last + 1;
659 if Index <= Buffer'Last
660 and then Buffer (Index) = ASCII.LF
661 then
662 Index := Index + 1;
663 end if;
664 end loop;
665 end;
666 end if;
668 Osint.Add_Default_Search_Dirs;
670 declare
671 Src : constant String := Parse_Gnatls_Src;
672 Obj : constant String := Parse_Gnatls_Obj;
674 begin
675 Directories := new Project_File'
676 (Src_Dir_Length => Length (Src_Dir) + Src'Length,
677 Obj_Dir_Length => Length (Obj_Dir) + Obj'Length,
678 Src_Dir => To_String (Src_Dir) & Src,
679 Obj_Dir => To_String (Obj_Dir) & Obj,
680 Src_Dir_Index => 1,
681 Obj_Dir_Index => 1,
682 Last_Obj_Dir_Start => 0);
683 end;
685 Free (Build_Dir);
686 end Create_Project_File;
688 ---------------------
689 -- Current_Obj_Dir --
690 ---------------------
692 function Current_Obj_Dir return String is
693 begin
694 return Directories.Obj_Dir
695 (Directories.Last_Obj_Dir_Start .. Directories.Obj_Dir_Index - 2);
696 end Current_Obj_Dir;
698 ----------------
699 -- Get_Column --
700 ----------------
702 function Get_Column (Decl : Declaration_Reference) return String is
703 begin
704 return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Column),
705 Ada.Strings.Left);
706 end Get_Column;
708 function Get_Column (Ref : Reference) return String is
709 begin
710 return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Column),
711 Ada.Strings.Left);
712 end Get_Column;
714 ---------------------
715 -- Get_Declaration --
716 ---------------------
718 function Get_Declaration
719 (File_Ref : File_Reference;
720 Line : Natural;
721 Column : Natural)
722 return Declaration_Reference
724 Key : aliased constant String := Key_From_Ref (File_Ref, Line, Column);
726 begin
727 return Entities_HTable.Get (Key'Unchecked_Access);
728 end Get_Declaration;
730 ----------------------
731 -- Get_Emit_Warning --
732 ----------------------
734 function Get_Emit_Warning (File : File_Reference) return Boolean is
735 begin
736 return File.Emit_Warning;
737 end Get_Emit_Warning;
739 --------------
740 -- Get_File --
741 --------------
743 function Get_File
744 (Decl : Declaration_Reference;
745 With_Dir : Boolean := False) return String
747 begin
748 return Get_File (Decl.Decl.File, With_Dir);
749 end Get_File;
751 function Get_File
752 (Ref : Reference;
753 With_Dir : Boolean := False) return String
755 begin
756 return Get_File (Ref.File, With_Dir);
757 end Get_File;
759 function Get_File
760 (File : File_Reference;
761 With_Dir : Boolean := False;
762 Strip : Natural := 0) return String
764 Tmp : GNAT.OS_Lib.String_Access;
766 function Internal_Strip (Full_Name : String) return String;
767 -- Internal function to process the Strip parameter
769 --------------------
770 -- Internal_Strip --
771 --------------------
773 function Internal_Strip (Full_Name : String) return String is
774 Unit_End : Natural;
775 Extension_Start : Natural;
776 S : Natural;
778 begin
779 if Strip = 0 then
780 return Full_Name;
781 end if;
783 -- Isolate the file extension
785 Extension_Start := Full_Name'Last;
786 while Extension_Start >= Full_Name'First
787 and then Full_Name (Extension_Start) /= '.'
788 loop
789 Extension_Start := Extension_Start - 1;
790 end loop;
792 -- Strip the right number of subunit_names
794 S := Strip;
795 Unit_End := Extension_Start - 1;
796 while Unit_End >= Full_Name'First
797 and then S > 0
798 loop
799 if Full_Name (Unit_End) = '-' then
800 S := S - 1;
801 end if;
803 Unit_End := Unit_End - 1;
804 end loop;
806 if Unit_End < Full_Name'First then
807 return "";
808 else
809 return Full_Name (Full_Name'First .. Unit_End)
810 & Full_Name (Extension_Start .. Full_Name'Last);
811 end if;
812 end Internal_Strip;
814 -- Start of processing for Get_File;
816 begin
817 -- If we do not want the full path name
819 if not With_Dir then
820 return Internal_Strip (File.File.all);
821 end if;
823 if File.Dir = null then
824 if Ada.Strings.Fixed.Tail (File.File.all, 3) =
825 Osint.ALI_Suffix.all
826 then
827 Tmp := Locate_Regular_File
828 (Internal_Strip (File.File.all), Directories.Obj_Dir);
829 else
830 Tmp := Locate_Regular_File
831 (File.File.all, Directories.Src_Dir);
832 end if;
834 if Tmp = null then
835 File.Dir := new String'("");
836 else
837 File.Dir := new String'(Dir_Name (Tmp.all));
838 Free (Tmp);
839 end if;
840 end if;
842 return Internal_Strip (File.Dir.all & File.File.all);
843 end Get_File;
845 ------------------
846 -- Get_File_Ref --
847 ------------------
849 function Get_File_Ref (Ref : Reference) return File_Reference is
850 begin
851 return Ref.File;
852 end Get_File_Ref;
854 -----------------------
855 -- Get_Gnatchop_File --
856 -----------------------
858 function Get_Gnatchop_File
859 (File : File_Reference;
860 With_Dir : Boolean := False)
861 return String
863 begin
864 if File.Gnatchop_File.all = "" then
865 return Get_File (File, With_Dir);
866 else
867 return File.Gnatchop_File.all;
868 end if;
869 end Get_Gnatchop_File;
871 function Get_Gnatchop_File
872 (Ref : Reference;
873 With_Dir : Boolean := False)
874 return String
876 begin
877 return Get_Gnatchop_File (Ref.File, With_Dir);
878 end Get_Gnatchop_File;
880 function Get_Gnatchop_File
881 (Decl : Declaration_Reference;
882 With_Dir : Boolean := False)
883 return String
885 begin
886 return Get_Gnatchop_File (Decl.Decl.File, With_Dir);
887 end Get_Gnatchop_File;
889 --------------
890 -- Get_Line --
891 --------------
893 function Get_Line (Decl : Declaration_Reference) return String is
894 begin
895 return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Line),
896 Ada.Strings.Left);
897 end Get_Line;
899 function Get_Line (Ref : Reference) return String is
900 begin
901 return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Line),
902 Ada.Strings.Left);
903 end Get_Line;
905 ----------------
906 -- Get_Parent --
907 ----------------
909 function Get_Parent
910 (Decl : Declaration_Reference)
911 return Declaration_Reference
913 begin
914 return Decl.Par_Symbol;
915 end Get_Parent;
917 ---------------------
918 -- Get_Source_Line --
919 ---------------------
921 function Get_Source_Line (Ref : Reference) return String is
922 begin
923 if Ref.Source_Line /= null then
924 return Ref.Source_Line.all;
925 else
926 return "";
927 end if;
928 end Get_Source_Line;
930 function Get_Source_Line (Decl : Declaration_Reference) return String is
931 begin
932 if Decl.Decl.Source_Line /= null then
933 return Decl.Decl.Source_Line.all;
934 else
935 return "";
936 end if;
937 end Get_Source_Line;
939 ----------------
940 -- Get_Symbol --
941 ----------------
943 function Get_Symbol (Decl : Declaration_Reference) return String is
944 begin
945 return Decl.Symbol;
946 end Get_Symbol;
948 --------------
949 -- Get_Type --
950 --------------
952 function Get_Type (Decl : Declaration_Reference) return Character is
953 begin
954 return Decl.Decl_Type;
955 end Get_Type;
957 ----------
958 -- Sort --
959 ----------
961 procedure Sort (Arr : in out Reference_Array) is
962 Tmp : Reference;
964 function Lt (Op1, Op2 : Natural) return Boolean;
965 procedure Move (From, To : Natural);
966 -- See GNAT.Heap_Sort_G
968 --------
969 -- Lt --
970 --------
972 function Lt (Op1, Op2 : Natural) return Boolean is
973 begin
974 if Op1 = 0 then
975 return Is_Less_Than (Tmp, Arr (Op2));
976 elsif Op2 = 0 then
977 return Is_Less_Than (Arr (Op1), Tmp);
978 else
979 return Is_Less_Than (Arr (Op1), Arr (Op2));
980 end if;
981 end Lt;
983 ----------
984 -- Move --
985 ----------
987 procedure Move (From, To : Natural) is
988 begin
989 if To = 0 then
990 Tmp := Arr (From);
991 elsif From = 0 then
992 Arr (To) := Tmp;
993 else
994 Arr (To) := Arr (From);
995 end if;
996 end Move;
998 package Ref_Sort is new GNAT.Heap_Sort_G (Move, Lt);
1000 -- Start of processing for Sort
1002 begin
1003 Ref_Sort.Sort (Arr'Last);
1004 end Sort;
1006 -----------------------
1007 -- Grep_Source_Files --
1008 -----------------------
1010 procedure Grep_Source_Files is
1011 Length : Natural := 0;
1012 Decl : Declaration_Reference := Entities_HTable.Get_First;
1013 Arr : Reference_Array_Access;
1014 Index : Natural;
1015 End_Index : Natural;
1016 Current_File : File_Reference;
1017 Current_Line : Cst_String_Access;
1018 Buffer : GNAT.OS_Lib.String_Access;
1019 Ref : Reference;
1020 Line : Natural;
1022 begin
1023 -- Create a temporary array, where all references will be
1024 -- sorted by files. This way, we only have to read the source
1025 -- files once.
1027 while Decl /= null loop
1029 -- Add 1 for the declaration itself
1031 Length := Length + References_Count (Decl, True, True, True) + 1;
1032 Decl := Entities_HTable.Get_Next;
1033 end loop;
1035 Arr := new Reference_Array (1 .. Length);
1036 Index := Arr'First;
1038 Decl := Entities_HTable.Get_First;
1039 while Decl /= null loop
1040 Store_References (Decl, True, True, True, True, Arr.all, Index);
1041 Decl := Entities_HTable.Get_Next;
1042 end loop;
1044 Sort (Arr.all);
1046 -- Now traverse the whole array and find the appropriate source
1047 -- lines.
1049 for R in Arr'Range loop
1050 Ref := Arr (R);
1052 if Ref.File /= Current_File then
1053 Free (Buffer);
1054 begin
1055 Read_File (Get_File (Ref.File, With_Dir => True), Buffer);
1056 End_Index := Buffer'First - 1;
1057 Line := 0;
1058 exception
1059 when Ada.Text_IO.Name_Error | Ada.Text_IO.End_Error =>
1060 Line := Natural'Last;
1061 end;
1062 Current_File := Ref.File;
1063 end if;
1065 if Ref.Line > Line then
1067 -- Do not free Current_Line, it is referenced by the last
1068 -- Ref we processed.
1070 loop
1071 Index := End_Index + 1;
1073 loop
1074 End_Index := End_Index + 1;
1075 exit when End_Index > Buffer'Last
1076 or else Buffer (End_Index) = ASCII.LF;
1077 end loop;
1079 -- Skip spaces at beginning of line
1081 while Index < End_Index and then
1082 (Buffer (Index) = ' ' or else Buffer (Index) = ASCII.HT)
1083 loop
1084 Index := Index + 1;
1085 end loop;
1087 Line := Line + 1;
1088 exit when Ref.Line = Line;
1089 end loop;
1091 Current_Line := new String'(Buffer (Index .. End_Index - 1));
1092 end if;
1094 Ref.Source_Line := Current_Line;
1095 end loop;
1097 Free (Buffer);
1098 Free (Arr);
1099 end Grep_Source_Files;
1101 ---------------
1102 -- Read_File --
1103 ---------------
1105 procedure Read_File
1106 (File_Name : String;
1107 Contents : out GNAT.OS_Lib.String_Access)
1109 Name_0 : constant String := File_Name & ASCII.NUL;
1110 FD : constant File_Descriptor := Open_Read (Name_0'Address, Binary);
1111 Length : Natural;
1113 begin
1114 if FD = Invalid_FD then
1115 raise Ada.Text_IO.Name_Error;
1116 end if;
1118 -- Include room for EOF char
1120 Length := Natural (File_Length (FD));
1122 declare
1123 Buffer : String (1 .. Length + 1);
1124 This_Read : Integer;
1125 Read_Ptr : Natural := 1;
1127 begin
1128 loop
1129 This_Read := Read (FD,
1130 A => Buffer (Read_Ptr)'Address,
1131 N => Length + 1 - Read_Ptr);
1132 Read_Ptr := Read_Ptr + Integer'Max (This_Read, 0);
1133 exit when This_Read <= 0;
1134 end loop;
1136 Buffer (Read_Ptr) := EOF;
1137 Contents := new String'(Buffer (1 .. Read_Ptr));
1139 if Read_Ptr /= Length + 1 then
1140 raise Ada.Text_IO.End_Error;
1141 end if;
1143 Close (FD);
1144 end;
1145 end Read_File;
1147 -----------------------
1148 -- Longest_File_Name --
1149 -----------------------
1151 function Longest_File_Name return Natural is
1152 begin
1153 return Longest_File_Name_In_Table;
1154 end Longest_File_Name;
1156 -----------
1157 -- Match --
1158 -----------
1160 function Match
1161 (File : File_Reference;
1162 Line : Natural;
1163 Column : Natural)
1164 return Boolean
1166 Ref : Ref_In_File_Ptr := File.Lines;
1168 begin
1169 while Ref /= null loop
1170 if (Ref.Line = 0 or else Ref.Line = Line)
1171 and then (Ref.Column = 0 or else Ref.Column = Column)
1172 then
1173 return True;
1174 end if;
1176 Ref := Ref.Next;
1177 end loop;
1179 return False;
1180 end Match;
1182 -----------
1183 -- Match --
1184 -----------
1186 function Match (Decl : Declaration_Reference) return Boolean is
1187 begin
1188 return Decl.Match;
1189 end Match;
1191 ----------
1192 -- Next --
1193 ----------
1195 function Next (E : File_Reference) return File_Reference is
1196 begin
1197 return E.Next;
1198 end Next;
1200 function Next (E : Declaration_Reference) return Declaration_Reference is
1201 begin
1202 return E.Next;
1203 end Next;
1205 ------------------
1206 -- Next_Obj_Dir --
1207 ------------------
1209 function Next_Obj_Dir return String is
1210 First : constant Integer := Directories.Obj_Dir_Index;
1211 Last : Integer;
1213 begin
1214 Last := Directories.Obj_Dir_Index;
1216 if Last > Directories.Obj_Dir_Length then
1217 return String'(1 .. 0 => ' ');
1218 end if;
1220 while Directories.Obj_Dir (Last) /= Path_Separator loop
1221 Last := Last + 1;
1222 end loop;
1224 Directories.Obj_Dir_Index := Last + 1;
1225 Directories.Last_Obj_Dir_Start := First;
1226 return Directories.Obj_Dir (First .. Last - 1);
1227 end Next_Obj_Dir;
1229 -------------------------
1230 -- Next_Unvisited_File --
1231 -------------------------
1233 function Next_Unvisited_File return File_Reference is
1234 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1235 (Unvisited_Files_Record, Unvisited_Files_Access);
1237 Ref : File_Reference;
1238 Tmp : Unvisited_Files_Access;
1240 begin
1241 if Unvisited_Files = null then
1242 return Empty_File;
1243 else
1244 Tmp := Unvisited_Files;
1245 Ref := Unvisited_Files.File;
1246 Unvisited_Files := Unvisited_Files.Next;
1247 Unchecked_Free (Tmp);
1248 return Ref;
1249 end if;
1250 end Next_Unvisited_File;
1252 ----------------------
1253 -- Parse_Gnatls_Src --
1254 ----------------------
1256 function Parse_Gnatls_Src return String is
1257 Length : Natural;
1259 begin
1260 Length := 0;
1261 for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop
1262 if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then
1263 Length := Length + 2;
1264 else
1265 Length := Length + Osint.Dir_In_Src_Search_Path (J)'Length + 1;
1266 end if;
1267 end loop;
1269 declare
1270 Result : String (1 .. Length);
1271 L : Natural;
1273 begin
1274 L := Result'First;
1275 for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop
1276 if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then
1277 Result (L .. L + 1) := "." & Path_Separator;
1278 L := L + 2;
1280 else
1281 Result (L .. L + Osint.Dir_In_Src_Search_Path (J)'Length - 1) :=
1282 Osint.Dir_In_Src_Search_Path (J).all;
1283 L := L + Osint.Dir_In_Src_Search_Path (J)'Length;
1284 Result (L) := Path_Separator;
1285 L := L + 1;
1286 end if;
1287 end loop;
1289 return Result;
1290 end;
1291 end Parse_Gnatls_Src;
1293 ----------------------
1294 -- Parse_Gnatls_Obj --
1295 ----------------------
1297 function Parse_Gnatls_Obj return String is
1298 Length : Natural;
1300 begin
1301 Length := 0;
1302 for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop
1303 if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then
1304 Length := Length + 2;
1305 else
1306 Length := Length + Osint.Dir_In_Obj_Search_Path (J)'Length + 1;
1307 end if;
1308 end loop;
1310 declare
1311 Result : String (1 .. Length);
1312 L : Natural;
1314 begin
1315 L := Result'First;
1316 for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop
1317 if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then
1318 Result (L .. L + 1) := "." & Path_Separator;
1319 L := L + 2;
1320 else
1321 Result (L .. L + Osint.Dir_In_Obj_Search_Path (J)'Length - 1) :=
1322 Osint.Dir_In_Obj_Search_Path (J).all;
1323 L := L + Osint.Dir_In_Obj_Search_Path (J)'Length;
1324 Result (L) := Path_Separator;
1325 L := L + 1;
1326 end if;
1327 end loop;
1329 return Result;
1330 end;
1331 end Parse_Gnatls_Obj;
1333 -------------------
1334 -- Reset_Obj_Dir --
1335 -------------------
1337 procedure Reset_Obj_Dir is
1338 begin
1339 Directories.Obj_Dir_Index := 1;
1340 end Reset_Obj_Dir;
1342 -----------------------
1343 -- Set_Default_Match --
1344 -----------------------
1346 procedure Set_Default_Match (Value : Boolean) is
1347 begin
1348 Default_Match := Value;
1349 end Set_Default_Match;
1351 ----------
1352 -- Free --
1353 ----------
1355 procedure Free (Str : in out Cst_String_Access) is
1356 function Convert is new Ada.Unchecked_Conversion
1357 (Cst_String_Access, GNAT.OS_Lib.String_Access);
1359 S : GNAT.OS_Lib.String_Access := Convert (Str);
1361 begin
1362 Free (S);
1363 Str := null;
1364 end Free;
1366 ---------------------
1367 -- Reset_Directory --
1368 ---------------------
1370 procedure Reset_Directory (File : File_Reference) is
1371 begin
1372 Free (File.Dir);
1373 end Reset_Directory;
1375 -------------------
1376 -- Set_Unvisited --
1377 -------------------
1379 procedure Set_Unvisited (File_Ref : File_Reference) is
1380 F : constant String := Get_File (File_Ref, With_Dir => False);
1382 begin
1383 File_Ref.Visited := False;
1385 -- ??? Do not add a source file to the list. This is true at
1386 -- least for gnatxref, and probably for gnatfind as well
1388 if F'Length > 4
1389 and then F (F'Last - 3 .. F'Last) = "." & Osint.ALI_Suffix.all
1390 then
1391 Unvisited_Files := new Unvisited_Files_Record'
1392 (File => File_Ref,
1393 Next => Unvisited_Files);
1394 end if;
1395 end Set_Unvisited;
1397 ----------------------
1398 -- Get_Declarations --
1399 ----------------------
1401 function Get_Declarations
1402 (Sorted : Boolean := True)
1403 return Declaration_Array_Access
1405 Arr : constant Declaration_Array_Access :=
1406 new Declaration_Array (1 .. Entities_Count);
1407 Decl : Declaration_Reference := Entities_HTable.Get_First;
1408 Index : Natural := Arr'First;
1409 Tmp : Declaration_Reference;
1411 procedure Move (From : Natural; To : Natural);
1412 function Lt (Op1, Op2 : Natural) return Boolean;
1413 -- See GNAT.Heap_Sort_G
1415 --------
1416 -- Lt --
1417 --------
1419 function Lt (Op1, Op2 : Natural) return Boolean is
1420 begin
1421 if Op1 = 0 then
1422 return Is_Less_Than (Tmp, Arr (Op2));
1423 elsif Op2 = 0 then
1424 return Is_Less_Than (Arr (Op1), Tmp);
1425 else
1426 return Is_Less_Than (Arr (Op1), Arr (Op2));
1427 end if;
1428 end Lt;
1430 ----------
1431 -- Move --
1432 ----------
1434 procedure Move (From : Natural; To : Natural) is
1435 begin
1436 if To = 0 then
1437 Tmp := Arr (From);
1438 elsif From = 0 then
1439 Arr (To) := Tmp;
1440 else
1441 Arr (To) := Arr (From);
1442 end if;
1443 end Move;
1445 package Decl_Sort is new GNAT.Heap_Sort_G (Move, Lt);
1447 -- Start of processing for Get_Declarations
1449 begin
1450 while Decl /= null loop
1451 Arr (Index) := Decl;
1452 Index := Index + 1;
1453 Decl := Entities_HTable.Get_Next;
1454 end loop;
1456 if Sorted and then Arr'Length /= 0 then
1457 Decl_Sort.Sort (Entities_Count);
1458 end if;
1460 return Arr;
1461 end Get_Declarations;
1463 ----------------------
1464 -- References_Count --
1465 ----------------------
1467 function References_Count
1468 (Decl : Declaration_Reference;
1469 Get_Reads : Boolean := False;
1470 Get_Writes : Boolean := False;
1471 Get_Bodies : Boolean := False)
1472 return Natural
1474 function List_Length (E : Reference) return Natural;
1475 -- Return the number of references in E
1477 -----------------
1478 -- List_Length --
1479 -----------------
1481 function List_Length (E : Reference) return Natural is
1482 L : Natural := 0;
1483 E1 : Reference := E;
1485 begin
1486 while E1 /= null loop
1487 L := L + 1;
1488 E1 := E1.Next;
1489 end loop;
1491 return L;
1492 end List_Length;
1494 Length : Natural := 0;
1496 -- Start of processing for References_Count
1498 begin
1499 if Get_Reads then
1500 Length := List_Length (Decl.Ref_Ref);
1501 end if;
1503 if Get_Writes then
1504 Length := Length + List_Length (Decl.Modif_Ref);
1505 end if;
1507 if Get_Bodies then
1508 Length := Length + List_Length (Decl.Body_Ref);
1509 end if;
1511 return Length;
1512 end References_Count;
1514 ----------------------
1515 -- Store_References --
1516 ----------------------
1518 procedure Store_References
1519 (Decl : Declaration_Reference;
1520 Get_Writes : Boolean := False;
1521 Get_Reads : Boolean := False;
1522 Get_Bodies : Boolean := False;
1523 Get_Declaration : Boolean := False;
1524 Arr : in out Reference_Array;
1525 Index : in out Natural)
1527 procedure Add (List : Reference);
1528 -- Add all the references in List to Arr
1530 ---------
1531 -- Add --
1532 ---------
1534 procedure Add (List : Reference) is
1535 E : Reference := List;
1536 begin
1537 while E /= null loop
1538 Arr (Index) := E;
1539 Index := Index + 1;
1540 E := E.Next;
1541 end loop;
1542 end Add;
1544 -- Start of processing for Store_References
1546 begin
1547 if Get_Declaration then
1548 Add (Decl.Decl);
1549 end if;
1551 if Get_Reads then
1552 Add (Decl.Ref_Ref);
1553 end if;
1555 if Get_Writes then
1556 Add (Decl.Modif_Ref);
1557 end if;
1559 if Get_Bodies then
1560 Add (Decl.Body_Ref);
1561 end if;
1562 end Store_References;
1564 --------------------
1565 -- Get_References --
1566 --------------------
1568 function Get_References
1569 (Decl : Declaration_Reference;
1570 Get_Reads : Boolean := False;
1571 Get_Writes : Boolean := False;
1572 Get_Bodies : Boolean := False)
1573 return Reference_Array_Access
1575 Length : constant Natural :=
1576 References_Count (Decl, Get_Reads, Get_Writes, Get_Bodies);
1578 Arr : constant Reference_Array_Access :=
1579 new Reference_Array (1 .. Length);
1581 Index : Natural := Arr'First;
1583 begin
1584 Store_References
1585 (Decl => Decl,
1586 Get_Writes => Get_Writes,
1587 Get_Reads => Get_Reads,
1588 Get_Bodies => Get_Bodies,
1589 Get_Declaration => False,
1590 Arr => Arr.all,
1591 Index => Index);
1593 if Arr'Length /= 0 then
1594 Sort (Arr.all);
1595 end if;
1597 return Arr;
1598 end Get_References;
1600 ----------
1601 -- Free --
1602 ----------
1604 procedure Free (Arr : in out Reference_Array_Access) is
1605 procedure Internal is new Ada.Unchecked_Deallocation
1606 (Reference_Array, Reference_Array_Access);
1607 begin
1608 Internal (Arr);
1609 end Free;
1611 ------------------
1612 -- Is_Parameter --
1613 ------------------
1615 function Is_Parameter (Decl : Declaration_Reference) return Boolean is
1616 begin
1617 return Decl.Is_Parameter;
1618 end Is_Parameter;
1620 end Xr_Tabls;