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