gnatfind.adb, [...]: Minor reformatting and code clean up.
[gcc.git] / gcc / ada / xr_tabls.adb
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-2009, 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 ------------------------------------------------------------------------------
25
26 with Types; use Types;
27 with Osint;
28 with Hostparm;
29
30 with Ada.Unchecked_Conversion;
31 with Ada.Unchecked_Deallocation;
32 with Ada.Strings.Fixed;
33 with Ada.Strings;
34 with Ada.Text_IO;
35 with Ada.Characters.Handling; use Ada.Characters.Handling;
36 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
37
38 with GNAT.OS_Lib; use GNAT.OS_Lib;
39 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
40 with GNAT.HTable; use GNAT.HTable;
41 with GNAT.Heap_Sort_G;
42
43 package body Xr_Tabls is
44
45 type HTable_Headers is range 1 .. 10000;
46
47 procedure Set_Next (E : File_Reference; Next : File_Reference);
48 function Next (E : File_Reference) return File_Reference;
49 function Get_Key (E : File_Reference) return Cst_String_Access;
50 function Hash (F : Cst_String_Access) return HTable_Headers;
51 function Equal (F1, F2 : Cst_String_Access) return Boolean;
52 -- The five subprograms above are used to instantiate the static
53 -- htable to store the files that should be processed.
54
55 package File_HTable is new GNAT.HTable.Static_HTable
56 (Header_Num => HTable_Headers,
57 Element => File_Record,
58 Elmt_Ptr => File_Reference,
59 Null_Ptr => null,
60 Set_Next => Set_Next,
61 Next => Next,
62 Key => Cst_String_Access,
63 Get_Key => Get_Key,
64 Hash => Hash,
65 Equal => Equal);
66 -- A hash table to store all the files referenced in the
67 -- application. The keys in this htable are the name of the files
68 -- themselves, therefore it is assumed that the source path
69 -- doesn't contain twice the same source or ALI file name
70
71 type Unvisited_Files_Record;
72 type Unvisited_Files_Access is access Unvisited_Files_Record;
73 type Unvisited_Files_Record is record
74 File : File_Reference;
75 Next : Unvisited_Files_Access;
76 end record;
77 -- A special list, in addition to File_HTable, that only stores
78 -- the files that haven't been visited so far. Note that the File
79 -- list points to some data in File_HTable, and thus should never be freed.
80
81 function Next (E : Declaration_Reference) return Declaration_Reference;
82 procedure Set_Next (E, Next : Declaration_Reference);
83 function Get_Key (E : Declaration_Reference) return Cst_String_Access;
84 -- The subprograms above are used to instantiate the static
85 -- htable to store the entities that have been found in the application
86
87 package Entities_HTable is new GNAT.HTable.Static_HTable
88 (Header_Num => HTable_Headers,
89 Element => Declaration_Record,
90 Elmt_Ptr => Declaration_Reference,
91 Null_Ptr => null,
92 Set_Next => Set_Next,
93 Next => Next,
94 Key => Cst_String_Access,
95 Get_Key => Get_Key,
96 Hash => Hash,
97 Equal => Equal);
98 -- A hash table to store all the entities defined in the
99 -- application. For each entity, we store a list of its reference
100 -- locations as well.
101 -- The keys in this htable should be created with Key_From_Ref,
102 -- and are the file, line and column of the declaration, which are
103 -- unique for every entity.
104
105 Entities_Count : Natural := 0;
106 -- Number of entities in Entities_HTable. This is used in the end
107 -- when sorting the table.
108
109 Longest_File_Name_In_Table : Natural := 0;
110 Unvisited_Files : Unvisited_Files_Access := null;
111 Directories : Project_File_Ptr;
112 Default_Match : Boolean := False;
113 -- The above need commenting ???
114
115 function Parse_Gnatls_Src return String;
116 -- Return the standard source directories (taking into account the
117 -- ADA_INCLUDE_PATH environment variable, if Osint.Add_Default_Search_Dirs
118 -- was called first).
119
120 function Parse_Gnatls_Obj return String;
121 -- Return the standard object directories (taking into account the
122 -- ADA_OBJECTS_PATH environment variable).
123
124 function Key_From_Ref
125 (File_Ref : File_Reference;
126 Line : Natural;
127 Column : Natural)
128 return String;
129 -- Return a key for the symbol declared at File_Ref, Line,
130 -- Column. This key should be used for lookup in Entity_HTable
131
132 function Is_Less_Than (Decl1, Decl2 : Declaration_Reference) return Boolean;
133 -- Compare two declarations (the comparison is case-insensitive)
134
135 function Is_Less_Than (Ref1, Ref2 : Reference) return Boolean;
136 -- Compare two references
137
138 procedure Store_References
139 (Decl : Declaration_Reference;
140 Get_Writes : Boolean := False;
141 Get_Reads : Boolean := False;
142 Get_Bodies : Boolean := False;
143 Get_Declaration : Boolean := False;
144 Arr : in out Reference_Array;
145 Index : in out Natural);
146 -- Store in Arr, starting at Index, all the references to Decl. The Get_*
147 -- parameters can be used to indicate which references should be stored.
148 -- Constraint_Error will be raised if Arr is not big enough.
149
150 procedure Sort (Arr : in out Reference_Array);
151 -- Sort an array of references (Arr'First must be 1)
152
153 --------------
154 -- Set_Next --
155 --------------
156
157 procedure Set_Next (E : File_Reference; Next : File_Reference) is
158 begin
159 E.Next := Next;
160 end Set_Next;
161
162 procedure Set_Next
163 (E : Declaration_Reference; Next : Declaration_Reference) is
164 begin
165 E.Next := Next;
166 end Set_Next;
167
168 -------------
169 -- Get_Key --
170 -------------
171
172 function Get_Key (E : File_Reference) return Cst_String_Access is
173 begin
174 return E.File;
175 end Get_Key;
176
177 function Get_Key (E : Declaration_Reference) return Cst_String_Access is
178 begin
179 return E.Key;
180 end Get_Key;
181
182 ----------
183 -- Hash --
184 ----------
185
186 function Hash (F : Cst_String_Access) return HTable_Headers is
187 function H is new GNAT.HTable.Hash (HTable_Headers);
188
189 begin
190 return H (F.all);
191 end Hash;
192
193 -----------
194 -- Equal --
195 -----------
196
197 function Equal (F1, F2 : Cst_String_Access) return Boolean is
198 begin
199 return F1.all = F2.all;
200 end Equal;
201
202 ------------------
203 -- Key_From_Ref --
204 ------------------
205
206 function Key_From_Ref
207 (File_Ref : File_Reference;
208 Line : Natural;
209 Column : Natural)
210 return String
211 is
212 begin
213 return File_Ref.File.all & Natural'Image (Line) & Natural'Image (Column);
214 end Key_From_Ref;
215
216 ---------------------
217 -- Add_Declaration --
218 ---------------------
219
220 function Add_Declaration
221 (File_Ref : File_Reference;
222 Symbol : String;
223 Line : Natural;
224 Column : Natural;
225 Decl_Type : Character;
226 Remove_Only : Boolean := False;
227 Symbol_Match : Boolean := True)
228 return Declaration_Reference
229 is
230 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
231 (Declaration_Record, Declaration_Reference);
232
233 Key : aliased constant String := Key_From_Ref (File_Ref, Line, Column);
234
235 New_Decl : Declaration_Reference :=
236 Entities_HTable.Get (Key'Unchecked_Access);
237
238 Is_Parameter : Boolean := False;
239
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.
244
245 if New_Decl /= null and then New_Decl.Symbol_Length = 0 then
246 Is_Parameter := 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;
253
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.
257
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_Parameter,
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);
283
284 Entities_HTable.Set (New_Decl);
285 Entities_Count := Entities_Count + 1;
286
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;
291
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 end if;
298
299 return New_Decl;
300 end Add_Declaration;
301
302 ----------------------
303 -- Add_To_Xref_File --
304 ----------------------
305
306 function Add_To_Xref_File
307 (File_Name : String;
308 Visited : Boolean := True;
309 Emit_Warning : Boolean := False;
310 Gnatchop_File : String := "";
311 Gnatchop_Offset : Integer := 0) return File_Reference
312 is
313 Base : aliased constant String := Base_Name (File_Name);
314 Dir : constant String := Dir_Name (File_Name);
315 Dir_Acc : GNAT.OS_Lib.String_Access := null;
316 Ref : File_Reference;
317
318 begin
319 -- Do we have a directory name as well?
320
321 if File_Name /= Base then
322 Dir_Acc := new String'(Dir);
323 end if;
324
325 Ref := File_HTable.Get (Base'Unchecked_Access);
326 if Ref = null then
327 Ref := new File_Record'
328 (File => new String'(Base),
329 Dir => Dir_Acc,
330 Lines => null,
331 Visited => Visited,
332 Emit_Warning => Emit_Warning,
333 Gnatchop_File => new String'(Gnatchop_File),
334 Gnatchop_Offset => Gnatchop_Offset,
335 Next => null);
336 File_HTable.Set (Ref);
337
338 if not Visited then
339
340 -- Keep a separate list for faster access
341
342 Set_Unvisited (Ref);
343 end if;
344 end if;
345 return Ref;
346 end Add_To_Xref_File;
347
348 --------------
349 -- Add_Line --
350 --------------
351
352 procedure Add_Line
353 (File : File_Reference;
354 Line : Natural;
355 Column : Natural)
356 is
357 begin
358 File.Lines := new Ref_In_File'(Line => Line,
359 Column => Column,
360 Next => File.Lines);
361 end Add_Line;
362
363 ----------------
364 -- Add_Parent --
365 ----------------
366
367 procedure Add_Parent
368 (Declaration : in out Declaration_Reference;
369 Symbol : String;
370 Line : Natural;
371 Column : Natural;
372 File_Ref : File_Reference)
373 is
374 begin
375 Declaration.Par_Symbol :=
376 Add_Declaration
377 (File_Ref, Symbol, Line, Column,
378 Decl_Type => ' ',
379 Symbol_Match => False);
380 end Add_Parent;
381
382 -------------------
383 -- Add_Reference --
384 -------------------
385
386 procedure Add_Reference
387 (Declaration : Declaration_Reference;
388 File_Ref : File_Reference;
389 Line : Natural;
390 Column : Natural;
391 Ref_Type : Character;
392 Labels_As_Ref : Boolean)
393 is
394 New_Ref : Reference;
395
396 begin
397 case Ref_Type is
398 when 'b' | 'c' | 'm' | 'r' | 'R' | 'i' | ' ' | 'x' =>
399 null;
400
401 when 'l' | 'w' =>
402 if not Labels_As_Ref then
403 return;
404 end if;
405
406 when '=' | '<' | '>' | '^' =>
407
408 -- Create a dummy declaration in the table to report it as a
409 -- parameter. Note that the current declaration for the subprogram
410 -- comes before the declaration of the parameter.
411
412 declare
413 Key : constant String :=
414 Key_From_Ref (File_Ref, Line, Column);
415 New_Decl : Declaration_Reference;
416
417 begin
418 New_Decl := new Declaration_Record'
419 (Symbol_Length => 0,
420 Symbol => "",
421 Key => new String'(Key),
422 Decl => null,
423 Is_Parameter => True,
424 Decl_Type => ' ',
425 Body_Ref => null,
426 Ref_Ref => null,
427 Modif_Ref => null,
428 Match => False,
429 Par_Symbol => null,
430 Next => null);
431 Entities_HTable.Set (New_Decl);
432 Entities_Count := Entities_Count + 1;
433 end;
434
435 when 'e' | 'z' | 't' | 'p' | 'P' | 'k' | 'd' =>
436 return;
437
438 when others =>
439 Ada.Text_IO.Put_Line ("Unknown reference type: " & Ref_Type);
440 return;
441 end case;
442
443 New_Ref := new Reference_Record'
444 (File => File_Ref,
445 Line => Line,
446 Column => Column,
447 Source_Line => null,
448 Next => null);
449
450 -- We can insert the reference in the list directly, since all
451 -- the references will appear only once in the ALI file
452 -- corresponding to the file where they are referenced.
453 -- This saves a lot of time compared to checking the list to check
454 -- if it exists.
455
456 case Ref_Type is
457 when 'b' | 'c' =>
458 New_Ref.Next := Declaration.Body_Ref;
459 Declaration.Body_Ref := New_Ref;
460
461 when 'r' | 'R' | 'i' | 'l' | ' ' | 'x' | 'w' =>
462 New_Ref.Next := Declaration.Ref_Ref;
463 Declaration.Ref_Ref := New_Ref;
464
465 when 'm' =>
466 New_Ref.Next := Declaration.Modif_Ref;
467 Declaration.Modif_Ref := New_Ref;
468
469 when others =>
470 null;
471 end case;
472
473 if not Declaration.Match then
474 Declaration.Match := Match (File_Ref, Line, Column);
475 end if;
476
477 if Declaration.Match then
478 Longest_File_Name_In_Table :=
479 Natural'Max (File_Ref.File'Length, Longest_File_Name_In_Table);
480 end if;
481 end Add_Reference;
482
483 -------------------
484 -- ALI_File_Name --
485 -------------------
486
487 function ALI_File_Name (Ada_File_Name : String) return String is
488
489 -- ??? Should ideally be based on the naming scheme defined in
490 -- project files.
491
492 Index : constant Natural :=
493 Ada.Strings.Fixed.Index
494 (Ada_File_Name, ".", Going => Ada.Strings.Backward);
495
496 begin
497 if Index /= 0 then
498 return Ada_File_Name (Ada_File_Name'First .. Index)
499 & Osint.ALI_Suffix.all;
500 else
501 return Ada_File_Name & "." & Osint.ALI_Suffix.all;
502 end if;
503 end ALI_File_Name;
504
505 ------------------
506 -- Is_Less_Than --
507 ------------------
508
509 function Is_Less_Than (Ref1, Ref2 : Reference) return Boolean is
510 begin
511 if Ref1 = null then
512 return False;
513 elsif Ref2 = null then
514 return True;
515 end if;
516
517 if Ref1.File.File.all < Ref2.File.File.all then
518 return True;
519
520 elsif Ref1.File.File.all = Ref2.File.File.all then
521 return (Ref1.Line < Ref2.Line
522 or else (Ref1.Line = Ref2.Line
523 and then Ref1.Column < Ref2.Column));
524 end if;
525
526 return False;
527 end Is_Less_Than;
528
529 ------------------
530 -- Is_Less_Than --
531 ------------------
532
533 function Is_Less_Than (Decl1, Decl2 : Declaration_Reference) return Boolean
534 is
535 -- We cannot store the data case-insensitive in the table,
536 -- since we wouldn't be able to find the right casing for the
537 -- display later on.
538
539 S1 : constant String := To_Lower (Decl1.Symbol);
540 S2 : constant String := To_Lower (Decl2.Symbol);
541
542 begin
543 if S1 < S2 then
544 return True;
545 elsif S1 > S2 then
546 return False;
547 end if;
548
549 return Decl1.Key.all < Decl2.Key.all;
550 end Is_Less_Than;
551
552 -------------------------
553 -- Create_Project_File --
554 -------------------------
555
556 procedure Create_Project_File (Name : String) is
557 Obj_Dir : Unbounded_String := Null_Unbounded_String;
558 Src_Dir : Unbounded_String := Null_Unbounded_String;
559 Build_Dir : GNAT.OS_Lib.String_Access := new String'("");
560
561 F : File_Descriptor;
562 Len : Positive;
563 File_Name : aliased String := Name & ASCII.NUL;
564
565 begin
566 -- Read the size of the file
567
568 F := Open_Read (File_Name'Address, Text);
569
570 -- Project file not found
571
572 if F /= Invalid_FD then
573 Len := Positive (File_Length (F));
574
575 declare
576 Buffer : String (1 .. Len);
577 Index : Positive := Buffer'First;
578 Last : Positive;
579
580 begin
581 Len := Read (F, Buffer'Address, Len);
582 Close (F);
583
584 -- First, look for Build_Dir, since all the source and object
585 -- path are relative to it.
586
587 while Index <= Buffer'Last loop
588
589 -- Find the end of line
590
591 Last := Index;
592 while Last <= Buffer'Last
593 and then Buffer (Last) /= ASCII.LF
594 and then Buffer (Last) /= ASCII.CR
595 loop
596 Last := Last + 1;
597 end loop;
598
599 if Index <= Buffer'Last - 9
600 and then Buffer (Index .. Index + 9) = "build_dir="
601 then
602 Index := Index + 10;
603 while Index <= Last
604 and then (Buffer (Index) = ' '
605 or else Buffer (Index) = ASCII.HT)
606 loop
607 Index := Index + 1;
608 end loop;
609
610 Free (Build_Dir);
611 Build_Dir := new String'(Buffer (Index .. Last - 1));
612 end if;
613
614 Index := Last + 1;
615
616 -- In case we had a ASCII.CR/ASCII.LF end of line, skip the
617 -- remaining symbol
618
619 if Index <= Buffer'Last
620 and then Buffer (Index) = ASCII.LF
621 then
622 Index := Index + 1;
623 end if;
624 end loop;
625
626 -- Now parse the source and object paths
627
628 Index := Buffer'First;
629 while Index <= Buffer'Last loop
630
631 -- Find the end of line
632
633 Last := Index;
634 while Last <= Buffer'Last
635 and then Buffer (Last) /= ASCII.LF
636 and then Buffer (Last) /= ASCII.CR
637 loop
638 Last := Last + 1;
639 end loop;
640
641 if Index <= Buffer'Last - 7
642 and then Buffer (Index .. Index + 7) = "src_dir="
643 then
644 Append (Src_Dir, Normalize_Pathname
645 (Name => Ada.Strings.Fixed.Trim
646 (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both),
647 Directory => Build_Dir.all) & Path_Separator);
648
649 elsif Index <= Buffer'Last - 7
650 and then Buffer (Index .. Index + 7) = "obj_dir="
651 then
652 Append (Obj_Dir, Normalize_Pathname
653 (Name => Ada.Strings.Fixed.Trim
654 (Buffer (Index + 8 .. Last - 1), Ada.Strings.Both),
655 Directory => Build_Dir.all) & Path_Separator);
656 end if;
657
658 -- In case we had a ASCII.CR/ASCII.LF end of line, skip the
659 -- remaining symbol
660 Index := Last + 1;
661
662 if Index <= Buffer'Last
663 and then Buffer (Index) = ASCII.LF
664 then
665 Index := Index + 1;
666 end if;
667 end loop;
668 end;
669 end if;
670
671 Osint.Add_Default_Search_Dirs;
672
673 declare
674 Src : constant String := Parse_Gnatls_Src;
675 Obj : constant String := Parse_Gnatls_Obj;
676
677 begin
678 Directories := new Project_File'
679 (Src_Dir_Length => Length (Src_Dir) + Src'Length,
680 Obj_Dir_Length => Length (Obj_Dir) + Obj'Length,
681 Src_Dir => To_String (Src_Dir) & Src,
682 Obj_Dir => To_String (Obj_Dir) & Obj,
683 Src_Dir_Index => 1,
684 Obj_Dir_Index => 1,
685 Last_Obj_Dir_Start => 0);
686 end;
687
688 Free (Build_Dir);
689 end Create_Project_File;
690
691 ---------------------
692 -- Current_Obj_Dir --
693 ---------------------
694
695 function Current_Obj_Dir return String is
696 begin
697 return Directories.Obj_Dir
698 (Directories.Last_Obj_Dir_Start .. Directories.Obj_Dir_Index - 2);
699 end Current_Obj_Dir;
700
701 ----------------
702 -- Get_Column --
703 ----------------
704
705 function Get_Column (Decl : Declaration_Reference) return String is
706 begin
707 return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Column),
708 Ada.Strings.Left);
709 end Get_Column;
710
711 function Get_Column (Ref : Reference) return String is
712 begin
713 return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Column),
714 Ada.Strings.Left);
715 end Get_Column;
716
717 ---------------------
718 -- Get_Declaration --
719 ---------------------
720
721 function Get_Declaration
722 (File_Ref : File_Reference;
723 Line : Natural;
724 Column : Natural)
725 return Declaration_Reference
726 is
727 Key : aliased constant String := Key_From_Ref (File_Ref, Line, Column);
728
729 begin
730 return Entities_HTable.Get (Key'Unchecked_Access);
731 end Get_Declaration;
732
733 ----------------------
734 -- Get_Emit_Warning --
735 ----------------------
736
737 function Get_Emit_Warning (File : File_Reference) return Boolean is
738 begin
739 return File.Emit_Warning;
740 end Get_Emit_Warning;
741
742 --------------
743 -- Get_File --
744 --------------
745
746 function Get_File
747 (Decl : Declaration_Reference;
748 With_Dir : Boolean := False) return String
749 is
750 begin
751 return Get_File (Decl.Decl.File, With_Dir);
752 end Get_File;
753
754 function Get_File
755 (Ref : Reference;
756 With_Dir : Boolean := False) return String
757 is
758 begin
759 return Get_File (Ref.File, With_Dir);
760 end Get_File;
761
762 function Get_File
763 (File : File_Reference;
764 With_Dir : Boolean := False;
765 Strip : Natural := 0) return String
766 is
767 Tmp : GNAT.OS_Lib.String_Access;
768
769 function Internal_Strip (Full_Name : String) return String;
770 -- Internal function to process the Strip parameter
771
772 --------------------
773 -- Internal_Strip --
774 --------------------
775
776 function Internal_Strip (Full_Name : String) return String is
777 Unit_End : Natural;
778 Extension_Start : Natural;
779 S : Natural;
780
781 begin
782 if Strip = 0 then
783 return Full_Name;
784 end if;
785
786 -- Isolate the file extension
787
788 Extension_Start := Full_Name'Last;
789 while Extension_Start >= Full_Name'First
790 and then Full_Name (Extension_Start) /= '.'
791 loop
792 Extension_Start := Extension_Start - 1;
793 end loop;
794
795 -- Strip the right number of subunit_names
796
797 S := Strip;
798 Unit_End := Extension_Start - 1;
799 while Unit_End >= Full_Name'First
800 and then S > 0
801 loop
802 if Full_Name (Unit_End) = '-' then
803 S := S - 1;
804 end if;
805
806 Unit_End := Unit_End - 1;
807 end loop;
808
809 if Unit_End < Full_Name'First then
810 return "";
811 else
812 return Full_Name (Full_Name'First .. Unit_End)
813 & Full_Name (Extension_Start .. Full_Name'Last);
814 end if;
815 end Internal_Strip;
816
817 -- Start of processing for Get_File;
818
819 begin
820 -- If we do not want the full path name
821
822 if not With_Dir then
823 return Internal_Strip (File.File.all);
824 end if;
825
826 if File.Dir = null then
827 if Ada.Strings.Fixed.Tail (File.File.all, 3) =
828 Osint.ALI_Suffix.all
829 then
830 Tmp := Locate_Regular_File
831 (Internal_Strip (File.File.all), Directories.Obj_Dir);
832 else
833 Tmp := Locate_Regular_File
834 (File.File.all, Directories.Src_Dir);
835 end if;
836
837 if Tmp = null then
838 File.Dir := new String'("");
839 else
840 File.Dir := new String'(Dir_Name (Tmp.all));
841 Free (Tmp);
842 end if;
843 end if;
844
845 return Internal_Strip (File.Dir.all & File.File.all);
846 end Get_File;
847
848 ------------------
849 -- Get_File_Ref --
850 ------------------
851
852 function Get_File_Ref (Ref : Reference) return File_Reference is
853 begin
854 return Ref.File;
855 end Get_File_Ref;
856
857 -----------------------
858 -- Get_Gnatchop_File --
859 -----------------------
860
861 function Get_Gnatchop_File
862 (File : File_Reference;
863 With_Dir : Boolean := False)
864 return String
865 is
866 begin
867 if File.Gnatchop_File.all = "" then
868 return Get_File (File, With_Dir);
869 else
870 return File.Gnatchop_File.all;
871 end if;
872 end Get_Gnatchop_File;
873
874 function Get_Gnatchop_File
875 (Ref : Reference;
876 With_Dir : Boolean := False)
877 return String
878 is
879 begin
880 return Get_Gnatchop_File (Ref.File, With_Dir);
881 end Get_Gnatchop_File;
882
883 function Get_Gnatchop_File
884 (Decl : Declaration_Reference;
885 With_Dir : Boolean := False)
886 return String
887 is
888 begin
889 return Get_Gnatchop_File (Decl.Decl.File, With_Dir);
890 end Get_Gnatchop_File;
891
892 --------------
893 -- Get_Line --
894 --------------
895
896 function Get_Line (Decl : Declaration_Reference) return String is
897 begin
898 return Ada.Strings.Fixed.Trim (Natural'Image (Decl.Decl.Line),
899 Ada.Strings.Left);
900 end Get_Line;
901
902 function Get_Line (Ref : Reference) return String is
903 begin
904 return Ada.Strings.Fixed.Trim (Natural'Image (Ref.Line),
905 Ada.Strings.Left);
906 end Get_Line;
907
908 ----------------
909 -- Get_Parent --
910 ----------------
911
912 function Get_Parent
913 (Decl : Declaration_Reference)
914 return Declaration_Reference
915 is
916 begin
917 return Decl.Par_Symbol;
918 end Get_Parent;
919
920 ---------------------
921 -- Get_Source_Line --
922 ---------------------
923
924 function Get_Source_Line (Ref : Reference) return String is
925 begin
926 if Ref.Source_Line /= null then
927 return Ref.Source_Line.all;
928 else
929 return "";
930 end if;
931 end Get_Source_Line;
932
933 function Get_Source_Line (Decl : Declaration_Reference) return String is
934 begin
935 if Decl.Decl.Source_Line /= null then
936 return Decl.Decl.Source_Line.all;
937 else
938 return "";
939 end if;
940 end Get_Source_Line;
941
942 ----------------
943 -- Get_Symbol --
944 ----------------
945
946 function Get_Symbol (Decl : Declaration_Reference) return String is
947 begin
948 return Decl.Symbol;
949 end Get_Symbol;
950
951 --------------
952 -- Get_Type --
953 --------------
954
955 function Get_Type (Decl : Declaration_Reference) return Character is
956 begin
957 return Decl.Decl_Type;
958 end Get_Type;
959
960 ----------
961 -- Sort --
962 ----------
963
964 procedure Sort (Arr : in out Reference_Array) is
965 Tmp : Reference;
966
967 function Lt (Op1, Op2 : Natural) return Boolean;
968 procedure Move (From, To : Natural);
969 -- See GNAT.Heap_Sort_G
970
971 --------
972 -- Lt --
973 --------
974
975 function Lt (Op1, Op2 : Natural) return Boolean is
976 begin
977 if Op1 = 0 then
978 return Is_Less_Than (Tmp, Arr (Op2));
979 elsif Op2 = 0 then
980 return Is_Less_Than (Arr (Op1), Tmp);
981 else
982 return Is_Less_Than (Arr (Op1), Arr (Op2));
983 end if;
984 end Lt;
985
986 ----------
987 -- Move --
988 ----------
989
990 procedure Move (From, To : Natural) is
991 begin
992 if To = 0 then
993 Tmp := Arr (From);
994 elsif From = 0 then
995 Arr (To) := Tmp;
996 else
997 Arr (To) := Arr (From);
998 end if;
999 end Move;
1000
1001 package Ref_Sort is new GNAT.Heap_Sort_G (Move, Lt);
1002
1003 -- Start of processing for Sort
1004
1005 begin
1006 Ref_Sort.Sort (Arr'Last);
1007 end Sort;
1008
1009 -----------------------
1010 -- Grep_Source_Files --
1011 -----------------------
1012
1013 procedure Grep_Source_Files is
1014 Length : Natural := 0;
1015 Decl : Declaration_Reference := Entities_HTable.Get_First;
1016 Arr : Reference_Array_Access;
1017 Index : Natural;
1018 End_Index : Natural;
1019 Current_File : File_Reference;
1020 Current_Line : Cst_String_Access;
1021 Buffer : GNAT.OS_Lib.String_Access;
1022 Ref : Reference;
1023 Line : Natural;
1024
1025 begin
1026 -- Create a temporary array, where all references will be
1027 -- sorted by files. This way, we only have to read the source
1028 -- files once.
1029
1030 while Decl /= null loop
1031
1032 -- Add 1 for the declaration itself
1033
1034 Length := Length + References_Count (Decl, True, True, True) + 1;
1035 Decl := Entities_HTable.Get_Next;
1036 end loop;
1037
1038 Arr := new Reference_Array (1 .. Length);
1039 Index := Arr'First;
1040
1041 Decl := Entities_HTable.Get_First;
1042 while Decl /= null loop
1043 Store_References (Decl, True, True, True, True, Arr.all, Index);
1044 Decl := Entities_HTable.Get_Next;
1045 end loop;
1046
1047 Sort (Arr.all);
1048
1049 -- Now traverse the whole array and find the appropriate source
1050 -- lines.
1051
1052 for R in Arr'Range loop
1053 Ref := Arr (R);
1054
1055 if Ref.File /= Current_File then
1056 Free (Buffer);
1057 begin
1058 Read_File (Get_File (Ref.File, With_Dir => True), Buffer);
1059 End_Index := Buffer'First - 1;
1060 Line := 0;
1061 exception
1062 when Ada.Text_IO.Name_Error | Ada.Text_IO.End_Error =>
1063 Line := Natural'Last;
1064 end;
1065 Current_File := Ref.File;
1066 end if;
1067
1068 if Ref.Line > Line then
1069
1070 -- Do not free Current_Line, it is referenced by the last
1071 -- Ref we processed.
1072
1073 loop
1074 Index := End_Index + 1;
1075
1076 loop
1077 End_Index := End_Index + 1;
1078 exit when End_Index > Buffer'Last
1079 or else Buffer (End_Index) = ASCII.LF;
1080 end loop;
1081
1082 -- Skip spaces at beginning of line
1083
1084 while Index < End_Index and then
1085 (Buffer (Index) = ' ' or else Buffer (Index) = ASCII.HT)
1086 loop
1087 Index := Index + 1;
1088 end loop;
1089
1090 Line := Line + 1;
1091 exit when Ref.Line = Line;
1092 end loop;
1093
1094 Current_Line := new String'(Buffer (Index .. End_Index - 1));
1095 end if;
1096
1097 Ref.Source_Line := Current_Line;
1098 end loop;
1099
1100 Free (Buffer);
1101 Free (Arr);
1102 end Grep_Source_Files;
1103
1104 ---------------
1105 -- Read_File --
1106 ---------------
1107
1108 procedure Read_File
1109 (File_Name : String;
1110 Contents : out GNAT.OS_Lib.String_Access)
1111 is
1112 Name_0 : constant String := File_Name & ASCII.NUL;
1113 FD : constant File_Descriptor := Open_Read (Name_0'Address, Binary);
1114 Length : Natural;
1115
1116 begin
1117 if FD = Invalid_FD then
1118 raise Ada.Text_IO.Name_Error;
1119 end if;
1120
1121 -- Include room for EOF char
1122
1123 Length := Natural (File_Length (FD));
1124
1125 declare
1126 Buffer : String (1 .. Length + 1);
1127 This_Read : Integer;
1128 Read_Ptr : Natural := 1;
1129
1130 begin
1131 loop
1132 This_Read := Read (FD,
1133 A => Buffer (Read_Ptr)'Address,
1134 N => Length + 1 - Read_Ptr);
1135 Read_Ptr := Read_Ptr + Integer'Max (This_Read, 0);
1136 exit when This_Read <= 0;
1137 end loop;
1138
1139 Buffer (Read_Ptr) := EOF;
1140 Contents := new String'(Buffer (1 .. Read_Ptr));
1141
1142 -- Things are not simple on VMS due to the plethora of file types
1143 -- and organizations. It seems clear that there shouldn't be more
1144 -- bytes read than are contained in the file though.
1145
1146 if (Hostparm.OpenVMS and then Read_Ptr > Length + 1)
1147 or else (not Hostparm.OpenVMS and then Read_Ptr /= Length + 1)
1148 then
1149 raise Ada.Text_IO.End_Error;
1150 end if;
1151
1152 Close (FD);
1153 end;
1154 end Read_File;
1155
1156 -----------------------
1157 -- Longest_File_Name --
1158 -----------------------
1159
1160 function Longest_File_Name return Natural is
1161 begin
1162 return Longest_File_Name_In_Table;
1163 end Longest_File_Name;
1164
1165 -----------
1166 -- Match --
1167 -----------
1168
1169 function Match
1170 (File : File_Reference;
1171 Line : Natural;
1172 Column : Natural)
1173 return Boolean
1174 is
1175 Ref : Ref_In_File_Ptr := File.Lines;
1176
1177 begin
1178 while Ref /= null loop
1179 if (Ref.Line = 0 or else Ref.Line = Line)
1180 and then (Ref.Column = 0 or else Ref.Column = Column)
1181 then
1182 return True;
1183 end if;
1184
1185 Ref := Ref.Next;
1186 end loop;
1187
1188 return False;
1189 end Match;
1190
1191 -----------
1192 -- Match --
1193 -----------
1194
1195 function Match (Decl : Declaration_Reference) return Boolean is
1196 begin
1197 return Decl.Match;
1198 end Match;
1199
1200 ----------
1201 -- Next --
1202 ----------
1203
1204 function Next (E : File_Reference) return File_Reference is
1205 begin
1206 return E.Next;
1207 end Next;
1208
1209 function Next (E : Declaration_Reference) return Declaration_Reference is
1210 begin
1211 return E.Next;
1212 end Next;
1213
1214 ------------------
1215 -- Next_Obj_Dir --
1216 ------------------
1217
1218 function Next_Obj_Dir return String is
1219 First : constant Integer := Directories.Obj_Dir_Index;
1220 Last : Integer;
1221
1222 begin
1223 Last := Directories.Obj_Dir_Index;
1224
1225 if Last > Directories.Obj_Dir_Length then
1226 return String'(1 .. 0 => ' ');
1227 end if;
1228
1229 while Directories.Obj_Dir (Last) /= Path_Separator loop
1230 Last := Last + 1;
1231 end loop;
1232
1233 Directories.Obj_Dir_Index := Last + 1;
1234 Directories.Last_Obj_Dir_Start := First;
1235 return Directories.Obj_Dir (First .. Last - 1);
1236 end Next_Obj_Dir;
1237
1238 -------------------------
1239 -- Next_Unvisited_File --
1240 -------------------------
1241
1242 function Next_Unvisited_File return File_Reference is
1243 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1244 (Unvisited_Files_Record, Unvisited_Files_Access);
1245
1246 Ref : File_Reference;
1247 Tmp : Unvisited_Files_Access;
1248
1249 begin
1250 if Unvisited_Files = null then
1251 return Empty_File;
1252 else
1253 Tmp := Unvisited_Files;
1254 Ref := Unvisited_Files.File;
1255 Unvisited_Files := Unvisited_Files.Next;
1256 Unchecked_Free (Tmp);
1257 return Ref;
1258 end if;
1259 end Next_Unvisited_File;
1260
1261 ----------------------
1262 -- Parse_Gnatls_Src --
1263 ----------------------
1264
1265 function Parse_Gnatls_Src return String is
1266 Length : Natural;
1267
1268 begin
1269 Length := 0;
1270 for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop
1271 if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then
1272 Length := Length + 2;
1273 else
1274 Length := Length + Osint.Dir_In_Src_Search_Path (J)'Length + 1;
1275 end if;
1276 end loop;
1277
1278 declare
1279 Result : String (1 .. Length);
1280 L : Natural;
1281
1282 begin
1283 L := Result'First;
1284 for J in 1 .. Osint.Nb_Dir_In_Src_Search_Path loop
1285 if Osint.Dir_In_Src_Search_Path (J)'Length = 0 then
1286 Result (L .. L + 1) := "." & Path_Separator;
1287 L := L + 2;
1288
1289 else
1290 Result (L .. L + Osint.Dir_In_Src_Search_Path (J)'Length - 1) :=
1291 Osint.Dir_In_Src_Search_Path (J).all;
1292 L := L + Osint.Dir_In_Src_Search_Path (J)'Length;
1293 Result (L) := Path_Separator;
1294 L := L + 1;
1295 end if;
1296 end loop;
1297
1298 return Result;
1299 end;
1300 end Parse_Gnatls_Src;
1301
1302 ----------------------
1303 -- Parse_Gnatls_Obj --
1304 ----------------------
1305
1306 function Parse_Gnatls_Obj return String is
1307 Length : Natural;
1308
1309 begin
1310 Length := 0;
1311 for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop
1312 if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then
1313 Length := Length + 2;
1314 else
1315 Length := Length + Osint.Dir_In_Obj_Search_Path (J)'Length + 1;
1316 end if;
1317 end loop;
1318
1319 declare
1320 Result : String (1 .. Length);
1321 L : Natural;
1322
1323 begin
1324 L := Result'First;
1325 for J in 1 .. Osint.Nb_Dir_In_Obj_Search_Path loop
1326 if Osint.Dir_In_Obj_Search_Path (J)'Length = 0 then
1327 Result (L .. L + 1) := "." & Path_Separator;
1328 L := L + 2;
1329 else
1330 Result (L .. L + Osint.Dir_In_Obj_Search_Path (J)'Length - 1) :=
1331 Osint.Dir_In_Obj_Search_Path (J).all;
1332 L := L + Osint.Dir_In_Obj_Search_Path (J)'Length;
1333 Result (L) := Path_Separator;
1334 L := L + 1;
1335 end if;
1336 end loop;
1337
1338 return Result;
1339 end;
1340 end Parse_Gnatls_Obj;
1341
1342 -------------------
1343 -- Reset_Obj_Dir --
1344 -------------------
1345
1346 procedure Reset_Obj_Dir is
1347 begin
1348 Directories.Obj_Dir_Index := 1;
1349 end Reset_Obj_Dir;
1350
1351 -----------------------
1352 -- Set_Default_Match --
1353 -----------------------
1354
1355 procedure Set_Default_Match (Value : Boolean) is
1356 begin
1357 Default_Match := Value;
1358 end Set_Default_Match;
1359
1360 ----------
1361 -- Free --
1362 ----------
1363
1364 procedure Free (Str : in out Cst_String_Access) is
1365 function Convert is new Ada.Unchecked_Conversion
1366 (Cst_String_Access, GNAT.OS_Lib.String_Access);
1367
1368 S : GNAT.OS_Lib.String_Access := Convert (Str);
1369
1370 begin
1371 Free (S);
1372 Str := null;
1373 end Free;
1374
1375 ---------------------
1376 -- Reset_Directory --
1377 ---------------------
1378
1379 procedure Reset_Directory (File : File_Reference) is
1380 begin
1381 Free (File.Dir);
1382 end Reset_Directory;
1383
1384 -------------------
1385 -- Set_Unvisited --
1386 -------------------
1387
1388 procedure Set_Unvisited (File_Ref : File_Reference) is
1389 F : constant String := Get_File (File_Ref, With_Dir => False);
1390
1391 begin
1392 File_Ref.Visited := False;
1393
1394 -- ??? Do not add a source file to the list. This is true at
1395 -- least for gnatxref, and probably for gnatfind as well
1396
1397 if F'Length > 4
1398 and then F (F'Last - 3 .. F'Last) = "." & Osint.ALI_Suffix.all
1399 then
1400 Unvisited_Files := new Unvisited_Files_Record'
1401 (File => File_Ref,
1402 Next => Unvisited_Files);
1403 end if;
1404 end Set_Unvisited;
1405
1406 ----------------------
1407 -- Get_Declarations --
1408 ----------------------
1409
1410 function Get_Declarations
1411 (Sorted : Boolean := True)
1412 return Declaration_Array_Access
1413 is
1414 Arr : constant Declaration_Array_Access :=
1415 new Declaration_Array (1 .. Entities_Count);
1416 Decl : Declaration_Reference := Entities_HTable.Get_First;
1417 Index : Natural := Arr'First;
1418 Tmp : Declaration_Reference;
1419
1420 procedure Move (From : Natural; To : Natural);
1421 function Lt (Op1, Op2 : Natural) return Boolean;
1422 -- See GNAT.Heap_Sort_G
1423
1424 --------
1425 -- Lt --
1426 --------
1427
1428 function Lt (Op1, Op2 : Natural) return Boolean is
1429 begin
1430 if Op1 = 0 then
1431 return Is_Less_Than (Tmp, Arr (Op2));
1432 elsif Op2 = 0 then
1433 return Is_Less_Than (Arr (Op1), Tmp);
1434 else
1435 return Is_Less_Than (Arr (Op1), Arr (Op2));
1436 end if;
1437 end Lt;
1438
1439 ----------
1440 -- Move --
1441 ----------
1442
1443 procedure Move (From : Natural; To : Natural) is
1444 begin
1445 if To = 0 then
1446 Tmp := Arr (From);
1447 elsif From = 0 then
1448 Arr (To) := Tmp;
1449 else
1450 Arr (To) := Arr (From);
1451 end if;
1452 end Move;
1453
1454 package Decl_Sort is new GNAT.Heap_Sort_G (Move, Lt);
1455
1456 -- Start of processing for Get_Declarations
1457
1458 begin
1459 while Decl /= null loop
1460 Arr (Index) := Decl;
1461 Index := Index + 1;
1462 Decl := Entities_HTable.Get_Next;
1463 end loop;
1464
1465 if Sorted and then Arr'Length /= 0 then
1466 Decl_Sort.Sort (Entities_Count);
1467 end if;
1468
1469 return Arr;
1470 end Get_Declarations;
1471
1472 ----------------------
1473 -- References_Count --
1474 ----------------------
1475
1476 function References_Count
1477 (Decl : Declaration_Reference;
1478 Get_Reads : Boolean := False;
1479 Get_Writes : Boolean := False;
1480 Get_Bodies : Boolean := False)
1481 return Natural
1482 is
1483 function List_Length (E : Reference) return Natural;
1484 -- Return the number of references in E
1485
1486 -----------------
1487 -- List_Length --
1488 -----------------
1489
1490 function List_Length (E : Reference) return Natural is
1491 L : Natural := 0;
1492 E1 : Reference := E;
1493
1494 begin
1495 while E1 /= null loop
1496 L := L + 1;
1497 E1 := E1.Next;
1498 end loop;
1499
1500 return L;
1501 end List_Length;
1502
1503 Length : Natural := 0;
1504
1505 -- Start of processing for References_Count
1506
1507 begin
1508 if Get_Reads then
1509 Length := List_Length (Decl.Ref_Ref);
1510 end if;
1511
1512 if Get_Writes then
1513 Length := Length + List_Length (Decl.Modif_Ref);
1514 end if;
1515
1516 if Get_Bodies then
1517 Length := Length + List_Length (Decl.Body_Ref);
1518 end if;
1519
1520 return Length;
1521 end References_Count;
1522
1523 ----------------------
1524 -- Store_References --
1525 ----------------------
1526
1527 procedure Store_References
1528 (Decl : Declaration_Reference;
1529 Get_Writes : Boolean := False;
1530 Get_Reads : Boolean := False;
1531 Get_Bodies : Boolean := False;
1532 Get_Declaration : Boolean := False;
1533 Arr : in out Reference_Array;
1534 Index : in out Natural)
1535 is
1536 procedure Add (List : Reference);
1537 -- Add all the references in List to Arr
1538
1539 ---------
1540 -- Add --
1541 ---------
1542
1543 procedure Add (List : Reference) is
1544 E : Reference := List;
1545 begin
1546 while E /= null loop
1547 Arr (Index) := E;
1548 Index := Index + 1;
1549 E := E.Next;
1550 end loop;
1551 end Add;
1552
1553 -- Start of processing for Store_References
1554
1555 begin
1556 if Get_Declaration then
1557 Add (Decl.Decl);
1558 end if;
1559
1560 if Get_Reads then
1561 Add (Decl.Ref_Ref);
1562 end if;
1563
1564 if Get_Writes then
1565 Add (Decl.Modif_Ref);
1566 end if;
1567
1568 if Get_Bodies then
1569 Add (Decl.Body_Ref);
1570 end if;
1571 end Store_References;
1572
1573 --------------------
1574 -- Get_References --
1575 --------------------
1576
1577 function Get_References
1578 (Decl : Declaration_Reference;
1579 Get_Reads : Boolean := False;
1580 Get_Writes : Boolean := False;
1581 Get_Bodies : Boolean := False)
1582 return Reference_Array_Access
1583 is
1584 Length : constant Natural :=
1585 References_Count (Decl, Get_Reads, Get_Writes, Get_Bodies);
1586
1587 Arr : constant Reference_Array_Access :=
1588 new Reference_Array (1 .. Length);
1589
1590 Index : Natural := Arr'First;
1591
1592 begin
1593 Store_References
1594 (Decl => Decl,
1595 Get_Writes => Get_Writes,
1596 Get_Reads => Get_Reads,
1597 Get_Bodies => Get_Bodies,
1598 Get_Declaration => False,
1599 Arr => Arr.all,
1600 Index => Index);
1601
1602 if Arr'Length /= 0 then
1603 Sort (Arr.all);
1604 end if;
1605
1606 return Arr;
1607 end Get_References;
1608
1609 ----------
1610 -- Free --
1611 ----------
1612
1613 procedure Free (Arr : in out Reference_Array_Access) is
1614 procedure Internal is new Ada.Unchecked_Deallocation
1615 (Reference_Array, Reference_Array_Access);
1616 begin
1617 Internal (Arr);
1618 end Free;
1619
1620 ------------------
1621 -- Is_Parameter --
1622 ------------------
1623
1624 function Is_Parameter (Decl : Declaration_Reference) return Boolean is
1625 begin
1626 return Decl.Is_Parameter;
1627 end Is_Parameter;
1628
1629 end Xr_Tabls;