OSDN Git Service

* 1aexcept.adb, 1aexcept.ads, 1ic.ads, 1ssecsta.adb,
[pf3gnuchains/gcc-fork.git] / gcc / ada / xref_lib.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                              X R E F _ L I B                             --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1998-2002 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 -- Extensive contributions were provided by Ada Core Technologies Inc.   --
23 --                                                                          --
24 ------------------------------------------------------------------------------
25
26 with Osint;
27 with Output;                 use Output;
28 with Types;                  use Types;
29 with Unchecked_Deallocation;
30
31 with Ada.Strings.Fixed; use Ada.Strings.Fixed;
32
33 with GNAT.Command_Line; use GNAT.Command_Line;
34 with GNAT.IO_Aux;       use GNAT.IO_Aux;
35
36 package body Xref_Lib is
37
38    Type_Position : constant := 50;
39    --  Column for label identifying type of entity
40
41    ---------------------
42    -- Local Variables --
43    ---------------------
44
45    Pipe : constant Character := '|';
46    --  First character on xref lines in the .ali file
47
48    EOF : constant Character := ASCII.SUB;
49    --  Special character to signal end of file. Not required in input file,
50    --  but should be properly treated if present. See also Read_File.
51
52    No_Xref_Information : exception;
53    --  Exception raised when there is no cross-referencing information in
54    --  the .ali files
55
56    subtype File_Offset is Natural;
57
58    procedure Read_File
59      (FD       : File_Descriptor;
60       Contents : out String_Access;
61       Success  : out Boolean);
62    --  Reads file associated with FS into the newly allocated
63    --  string Contents. An EOF character will be added to the
64    --  returned Contents to simplify parsing.
65    --  [VMS] Success is true iff the number of bytes read is less than or
66    --   equal to the file size.
67    --  [Other] Success is true iff the number of bytes read is equal to
68    --   the file size.
69
70    procedure Parse_EOL (Source : access String; Ptr : in out Positive);
71    --  On return Source (Ptr) is the first character of the next line
72    --  or EOF. Source.all must be terminated by EOF.
73
74    procedure Parse_Identifier_Info
75      (Pattern       : Search_Pattern;
76       File          : in out ALI_File;
77       Local_Symbols : Boolean;
78       Der_Info      : Boolean := False;
79       Type_Tree     : Boolean := False;
80       Wide_Search   : Boolean := True);
81    --  Output the file and the line where the identifier was referenced,
82    --  If Local_Symbols is False then only the publicly visible symbols
83    --  will be processed
84
85    procedure Parse_Token
86      (Source    : access String;
87       Ptr       : in out Positive;
88       Token_Ptr : out Positive);
89    --  Skips any separators and stores the start of the token in Token_Ptr.
90    --  Then stores the position of the next separator in Ptr.
91    --  On return Source (Token_Ptr .. Ptr - 1) is the token.
92    --  Separators are space and ASCII.HT.
93    --  Parse_Token will never skip to the next line.
94
95    procedure Parse_Number
96      (Source : access String;
97       Ptr    : in out Positive;
98       Number : out Natural);
99    --  Skips any separators and parses Source upto the first character that
100    --  is not a decimal digit. Returns value of parsed digits or 0 if none.
101
102    procedure Parse_X_Filename (File : in out ALI_File);
103    --  Reads and processes "X..." lines in the ALI file
104    --  and updates the File.X_File information.
105
106    ----------------
107    -- Add_Entity --
108    ----------------
109
110    procedure Add_Entity
111      (Pattern : in out Search_Pattern;
112       Entity  : String;
113       Glob    : Boolean := False)
114    is
115       File_Start   : Natural;
116       Line_Start   : Natural;
117       Col_Start    : Natural;
118       Line_Num     : Natural := 0;
119       Col_Num      : Natural := 0;
120       File_Ref     : File_Reference := Empty_File;
121       File_Existed : Boolean;
122       Has_Pattern  : Boolean := False;
123
124    begin
125       --  Find the end of the first item in Entity (pattern or file?)
126       --  If there is no ':', we only have a pattern
127
128       File_Start := Index (Entity, ":");
129       if File_Start = 0 then
130
131          --  If the regular expression is invalid, just consider it as a string
132
133          begin
134             Pattern.Entity := Compile (Entity, Glob, False);
135             Pattern.Initialized := True;
136
137          exception
138             when Error_In_Regexp =>
139
140                --  The basic idea is to insert a \ before every character
141
142                declare
143                   Tmp_Regexp : String (1 .. 2 * Entity'Length);
144                   Index      : Positive := 1;
145
146                begin
147                   for J in Entity'Range loop
148                      Tmp_Regexp (Index) := '\';
149                      Tmp_Regexp (Index + 1) := Entity (J);
150                      Index := Index + 2;
151                   end loop;
152
153                   Pattern.Entity := Compile (Tmp_Regexp, True, False);
154                   Pattern.Initialized := True;
155                end;
156          end;
157
158          Set_Default_Match (True);
159          return;
160       end if;
161
162       --  If there is a dot in the pattern, then it is a file name
163
164       if (Glob and then
165              Index (Entity (Entity'First .. File_Start - 1), ".") /= 0)
166                or else
167                 (not Glob
168                    and then Index (Entity (Entity'First .. File_Start - 1),
169                                    "\.") /= 0)
170       then
171          Pattern.Entity := Compile (".*", False);
172          Pattern.Initialized := True;
173          File_Start     := Entity'First;
174
175       else
176          --  If the regular expression is invalid,
177          --  just consider it as a string
178
179          begin
180             Pattern.Entity :=
181               Compile (Entity (Entity'First .. File_Start - 1), Glob, False);
182             Pattern.Initialized := True;
183
184          exception
185             when Error_In_Regexp =>
186
187                --  The basic idea is to insert a \ before every character
188
189                declare
190                   Tmp_Regexp : String (1 .. 2 * (File_Start - Entity'First));
191                   Index      : Positive := 1;
192
193                begin
194                   for J in Entity'First .. File_Start - 1 loop
195                      Tmp_Regexp (Index) := '\';
196                      Tmp_Regexp (Index + 1) := Entity (J);
197                      Index := Index + 2;
198                   end loop;
199
200                   Pattern.Entity := Compile (Tmp_Regexp, True, False);
201                   Pattern.Initialized := True;
202                end;
203          end;
204
205          File_Start  := File_Start + 1;
206          Has_Pattern := True;
207       end if;
208
209       --  Parse the file name
210
211       Line_Start := Index (Entity (File_Start .. Entity'Last), ":");
212
213       --  Check if it was a disk:\directory item (for NT and OS/2)
214
215       if File_Start = Line_Start - 1
216         and then Line_Start < Entity'Last
217         and then Entity (Line_Start + 1) = '\'
218       then
219          Line_Start := Index (Entity (Line_Start + 1 .. Entity'Last), ":");
220       end if;
221
222       if Line_Start = 0 then
223          Line_Start := Entity'Length + 1;
224
225       elsif Line_Start /= Entity'Last then
226          Col_Start := Index (Entity (Line_Start + 1 .. Entity'Last), ":");
227
228          if Col_Start = 0 then
229             Col_Start := Entity'Last + 1;
230          end if;
231
232          if Col_Start > Line_Start + 1 then
233             begin
234                Line_Num := Natural'Value
235                  (Entity (Line_Start + 1 .. Col_Start - 1));
236
237             exception
238                when Constraint_Error =>
239                   raise Invalid_Argument;
240             end;
241          end if;
242
243          if Col_Start < Entity'Last then
244             begin
245                Col_Num := Natural'Value (Entity
246                                          (Col_Start + 1 .. Entity'Last));
247
248             exception
249                when Constraint_Error => raise Invalid_Argument;
250             end;
251          end if;
252       end if;
253
254       Add_To_Xref_File (Entity (File_Start .. Line_Start - 1),
255                 File_Existed,
256                 File_Ref,
257                 Visited => True);
258       Add_Line (File_Ref, Line_Num, Col_Num);
259       Add_To_Xref_File
260         (ALI_File_Name (Entity (File_Start .. Line_Start - 1)),
261          File_Existed, File_Ref,
262          Visited => False,
263          Emit_Warning => True);
264    end Add_Entity;
265
266    -------------------
267    -- Add_Xref_File --
268    -------------------
269
270    procedure Add_Xref_File (File : String) is
271       File_Ref     : File_Reference := Empty_File;
272       File_Existed : Boolean;
273       Iterator     : Expansion_Iterator;
274
275       procedure Add_Xref_File_Internal (File : String);
276       --  Do the actual addition of the file
277
278       ----------------------------
279       -- Add_Xref_File_Internal --
280       ----------------------------
281
282       procedure Add_Xref_File_Internal (File : String) is
283       begin
284          --  Case where we have an ALI file, accept it even though this is
285          --  not official usage, since the intention is obvious
286
287          if Tail (File, 4) = ".ali" then
288             Add_To_Xref_File
289               (File,
290                File_Existed,
291                File_Ref,
292                Visited => False,
293                Emit_Warning => True);
294
295          --  Normal non-ali file case
296
297          else
298             Add_To_Xref_File
299               (File,
300                File_Existed,
301                File_Ref,
302                Visited => True);
303
304             Add_To_Xref_File
305               (ALI_File_Name (File),
306                File_Existed,
307                File_Ref,
308                Visited => False,
309                Emit_Warning => True);
310          end if;
311       end Add_Xref_File_Internal;
312
313    --  Start of processing for Add_Xref_File
314
315    begin
316       --  Check if we need to do the expansion
317
318       if Ada.Strings.Fixed.Index (File, "*") /= 0
319         or else Ada.Strings.Fixed.Index (File, "?") /= 0
320       then
321          Start_Expansion (Iterator, File);
322
323          loop
324             declare
325                S : constant String := Expansion (Iterator);
326
327             begin
328                exit when S'Length = 0;
329                Add_Xref_File_Internal (S);
330             end;
331          end loop;
332
333       else
334          Add_Xref_File_Internal (File);
335       end if;
336    end Add_Xref_File;
337
338    -----------------------
339    -- Current_Xref_File --
340    -----------------------
341
342    function Current_Xref_File (File : ALI_File) return File_Reference is
343    begin
344       return File.X_File;
345    end Current_Xref_File;
346
347    --------------------------
348    -- Default_Project_File --
349    --------------------------
350
351    function Default_Project_File
352      (Dir_Name : String)
353       return     String
354    is
355       My_Dir  : Dir_Type;
356       Dir_Ent : File_Name_String;
357       Last    : Natural;
358
359    begin
360       Open (My_Dir, Dir_Name);
361
362       loop
363          Read (My_Dir, Dir_Ent, Last);
364          exit when Last = 0;
365
366          if Tail (Dir_Ent (1 .. Last), 4) = ".adp" then
367
368             --  The first project file found is the good one.
369
370             Close (My_Dir);
371             return Dir_Ent (1 .. Last);
372          end if;
373       end loop;
374
375       Close (My_Dir);
376       return String'(1 .. 0 => ' ');
377
378    exception
379       when Directory_Error => return String'(1 .. 0 => ' ');
380    end Default_Project_File;
381
382    ---------------
383    -- File_Name --
384    ---------------
385
386    function File_Name
387      (File : ALI_File;
388       Num  : Positive)
389       return File_Reference
390    is
391    begin
392       return File.Dep.Table (Num);
393    end File_Name;
394
395    --------------------
396    -- Find_ALI_Files --
397    --------------------
398
399    procedure Find_ALI_Files is
400       My_Dir       : Rec_DIR;
401       Dir_Ent      : File_Name_String;
402       Last         : Natural;
403       File_Existed : Boolean;
404       File_Ref     : File_Reference;
405
406       function Open_Next_Dir return Boolean;
407       --  Tries to open the next object directory, and return False if
408       --  the directory cannot be opened.
409
410       -------------------
411       -- Open_Next_Dir --
412       -------------------
413
414       function Open_Next_Dir return Boolean is
415       begin
416          --  Until we are able to open a new directory
417
418          loop
419             declare
420                Obj_Dir : constant String := Next_Obj_Dir;
421
422             begin
423                --  If there was no more Obj_Dir line
424
425                if Obj_Dir'Length = 0 then
426                   return False;
427                end if;
428
429                Open (My_Dir.Dir, Obj_Dir);
430                exit;
431
432             exception
433                --  Could not open the directory
434
435                when Directory_Error => null;
436             end;
437          end loop;
438
439          return True;
440       end Open_Next_Dir;
441
442    --  Start of processing for Find_ALI_Files
443
444    begin
445       if Open_Next_Dir then
446          loop
447             Read (My_Dir.Dir, Dir_Ent, Last);
448
449             if Last = 0 then
450                Close (My_Dir.Dir);
451
452                if not Open_Next_Dir then
453                   return;
454                end if;
455
456             elsif Last > 4 and then Dir_Ent (Last - 3 .. Last) = ".ali" then
457                Add_To_Xref_File
458                  (Dir_Ent (1 .. Last),
459                   File_Existed,
460                   File_Ref,
461                   Visited => False);
462                Set_Directory (File_Ref, Current_Obj_Dir);
463             end if;
464          end loop;
465       end if;
466    end Find_ALI_Files;
467
468    -------------------
469    -- Get_Full_Type --
470    -------------------
471
472    function Get_Full_Type (Abbrev : Character) return String is
473    begin
474       case Abbrev is
475          when 'A' => return "array type";
476          when 'B' => return "boolean type";
477          when 'C' => return "class-wide type";
478          when 'D' => return "decimal type";
479          when 'E' => return "enumeration type";
480          when 'F' => return "float type";
481          when 'I' => return "integer type";
482          when 'M' => return "modular type";
483          when 'O' => return "fixed type";
484          when 'P' => return "access type";
485          when 'R' => return "record type";
486          when 'S' => return "string type";
487          when 'T' => return "task type";
488          when 'W' => return "protected type";
489
490          when 'a' => return "array type";
491          when 'b' => return "boolean object";
492          when 'c' => return "class-wide object";
493          when 'd' => return "decimal object";
494          when 'e' => return "enumeration object";
495          when 'f' => return "float object";
496          when 'i' => return "integer object";
497          when 'm' => return "modular object";
498          when 'o' => return "fixed object";
499          when 'p' => return "access object";
500          when 'r' => return "record object";
501          when 's' => return "string object";
502          when 't' => return "task object";
503          when 'w' => return "protected object";
504
505          when 'K' => return "package";
506          when 'k' => return "generic package";
507          when 'L' => return "statement label";
508          when 'l' => return "loop label";
509          when 'N' => return "named number";
510          when 'n' => return "enumeration literal";
511          when 'q' => return "block label";
512          when 'U' => return "procedure";
513          when 'u' => return "generic procedure";
514          when 'V' => return "function";
515          when 'v' => return "generic function";
516          when 'X' => return "exception";
517          when 'Y' => return "entry";
518
519          --  The above should be the only possibilities, but for a
520          --  tool like this we don't want to bomb if we find something
521          --  else, so just return ??? when we have an unknown Abbrev value
522
523          when others =>
524             return "???";
525       end case;
526    end Get_Full_Type;
527
528    -----------
529    -- Match --
530    -----------
531
532    function Match
533      (Pattern : Search_Pattern;
534       Symbol  : String)
535       return    Boolean
536    is
537    begin
538       --  Get the entity name
539
540       return Match (Symbol, Pattern.Entity);
541    end Match;
542
543    ----------
544    -- Open --
545    ----------
546
547    procedure Open
548      (Name         : String;
549       File         : out ALI_File;
550       Dependencies : Boolean := False)
551    is
552       Name_0           : constant String := Name & ASCII.NUL;
553       Num_Dependencies : Natural := 0;
554       File_Existed     : Boolean;
555       File_Ref         : File_Reference;
556       FD               : File_Descriptor;
557       Success          : Boolean := False;
558       Ali              : String_Access renames File.Buffer;
559       Token            : Positive;
560       Ptr              : Positive;
561       File_Start       : Positive;
562       File_End         : Positive;
563       Gnatchop_Offset  : Integer;
564       Gnatchop_Name    : Positive;
565
566    begin
567       if File.Buffer /= null then
568          Free (File.Buffer);
569       end if;
570
571       Init (File.Dep);
572
573       FD := Open_Read (Name_0'Address, Binary);
574
575       if FD = Invalid_FD then
576          raise No_Xref_Information;
577       end if;
578
579       Read_File (FD, Ali, Success);
580       Close (FD);
581
582       Ptr := Ali'First;
583
584       --  Read all the lines possibly processing with-clauses and dependency
585       --  information and exit on finding the first Xref line.
586       --  A fall-through of the loop means that there is no xref information
587       --  which is an error condition.
588
589       while Ali (Ptr) /= EOF loop
590
591          if Ali (Ptr) = 'D' then
592             --  Found dependency information. Format looks like:
593             --  D source-name time-stamp checksum [subunit-name] \
594             --    [line:file-name]
595
596             --  Skip the D and parse the filename
597
598             Ptr := Ptr + 1;
599             Parse_Token (Ali, Ptr, Token);
600             File_Start := Token;
601             File_End := Ptr - 1;
602
603             Num_Dependencies := Num_Dependencies + 1;
604             Set_Last (File.Dep, Num_Dependencies);
605
606             Parse_Token (Ali, Ptr, Token); --  Skip time-stamp
607             Parse_Token (Ali, Ptr, Token); --  Skip checksum
608             Parse_Token (Ali, Ptr, Token); --  Read next entity on the line
609
610             if not (Ali (Token) in '0' .. '9') then
611                Parse_Token (Ali, Ptr, Token); --  Was a subunit name
612             end if;
613
614             --  Did we have a gnatchop-ed file with a pragma Source_Reference ?
615             Gnatchop_Offset := 0;
616
617             if Ali (Token) in '0' .. '9' then
618                Gnatchop_Name := Token;
619                while Ali (Gnatchop_Name) /= ':' loop
620                   Gnatchop_Name := Gnatchop_Name + 1;
621                end loop;
622                Gnatchop_Offset :=
623                  2 - Natural'Value (Ali (Token .. Gnatchop_Name - 1));
624                Token := Gnatchop_Name + 1;
625             end if;
626
627             Add_To_Xref_File
628               (Ali (File_Start .. File_End),
629                File_Existed,
630                File.Dep.Table (Num_Dependencies),
631                Gnatchop_File => Ali (Token .. Ptr - 1),
632                Gnatchop_Offset => Gnatchop_Offset);
633
634          elsif Dependencies and then Ali (Ptr) = 'W' then
635             --  Found with-clause information. Format looks like:
636             --     "W debug%s               debug.adb               debug.ali"
637
638             --  Skip the W and parse the .ali filename (3rd token)
639
640             Parse_Token (Ali, Ptr, Token);
641             Parse_Token (Ali, Ptr, Token);
642             Parse_Token (Ali, Ptr, Token);
643
644             Add_To_Xref_File
645               (Ali (Token .. Ptr - 1),
646                File_Existed,
647                File_Ref,
648                Visited => False);
649
650          elsif Ali (Ptr) = 'X' then
651             --  Found a cross-referencing line - stop processing
652
653             File.Current_Line := Ptr;
654             File.Xref_Line    := Ptr;
655             return;
656          end if;
657
658          Parse_EOL (Ali, Ptr);
659       end loop;
660
661       raise No_Xref_Information;
662    end Open;
663
664    ---------------
665    -- Parse_EOL --
666    ---------------
667
668    procedure Parse_EOL (Source : access String; Ptr : in out Positive) is
669    begin
670       --  Skip to end of line
671
672       while Source (Ptr) /= ASCII.CR and then Source (Ptr) /= ASCII.LF
673         and then Source (Ptr) /= EOF
674       loop
675          Ptr := Ptr + 1;
676       end loop;
677
678       if Source (Ptr) /= EOF then
679          Ptr := Ptr + 1;      -- skip CR or LF
680       end if;
681
682       --  Skip past CR/LF or LF/CR combination
683
684       if (Source (Ptr) = ASCII.CR or else Source (Ptr) = ASCII.LF)
685          and then Source (Ptr) /= Source (Ptr - 1)
686       then
687          Ptr := Ptr + 1;
688       end if;
689    end Parse_EOL;
690
691    ---------------------------
692    -- Parse_Identifier_Info --
693    ---------------------------
694
695    procedure Parse_Identifier_Info
696      (Pattern       : Search_Pattern;
697       File          : in out ALI_File;
698       Local_Symbols : Boolean;
699       Der_Info      : Boolean := False;
700       Type_Tree     : Boolean := False;
701       Wide_Search   : Boolean := True)
702    is
703       Ptr      : Positive renames File.Current_Line;
704       Ali      : String_Access renames File.Buffer;
705
706       E_Line   : Natural;   --  Line number of current entity
707       E_Col    : Natural;   --  Column number of current entity
708       E_Type   : Character; --  Type of current entity
709       E_Name   : Positive;  --  Pointer to begin of entity name
710       E_Global : Boolean;   --  True iff entity is global
711
712       R_Line   : Natural;   --  Line number of current reference
713       R_Col    : Natural;   --  Column number of current reference
714       R_Type   : Character; --  Type of current reference
715
716       Decl_Ref : Declaration_Reference;
717       File_Ref : File_Reference := Current_Xref_File (File);
718
719       function Get_Symbol_Name (Eun, Line, Col : Natural) return String;
720       --  Returns the symbol name for the entity defined at the specified
721       --  line and column in the dependent unit number Eun. For this we need
722       --  to parse the ali file again because the parent entity is not in
723       --  the declaration table if it did not match the search pattern.
724
725       ---------------------
726       -- Get_Symbol_Name --
727       ---------------------
728
729       function Get_Symbol_Name (Eun, Line, Col : Natural) return String is
730          Ptr    : Positive := 1;
731          E_Eun  : Positive;   --  Unit number of current entity
732          E_Line : Natural;    --  Line number of current entity
733          E_Col  : Natural;    --  Column number of current entity
734          E_Name : Positive;   --  Pointer to begin of entity name
735          E_Type : Character;  --  Type of current entity
736
737          procedure Skip_Line;
738          --  skip current line and continuation line
739
740          procedure Skip_Line is
741          begin
742             loop
743                Parse_EOL (Ali, Ptr);
744                exit when Ali (Ptr) /= '.';
745             end loop;
746          end Skip_Line;
747
748       --  Start of processing for Get_Symbol_Name
749
750       begin
751          --  Look for the X lines corresponding to unit Eun
752
753          loop
754             if Ali (Ptr) = 'X' then
755                Ptr := Ptr + 1;
756                Parse_Number (Ali, Ptr, E_Eun);
757                exit when E_Eun = Eun;
758             end if;
759
760             Skip_Line;
761          end loop;
762
763          --  Here we are in the right Ali section, we now look for the entity
764          --  declared at position (Line, Col).
765
766          loop
767             Parse_Number (Ali, Ptr, E_Line);
768             E_Type := Ali (Ptr);
769             Ptr := Ptr + 1;
770             Parse_Number (Ali, Ptr, E_Col);
771             Ptr := Ptr + 1;
772
773             if Line = E_Line and then Col = E_Col then
774                Parse_Token (Ali, Ptr, E_Name);
775                return Ali (E_Name .. Ptr - 1);
776             end if;
777
778             Skip_Line;
779          end loop;
780
781          --  We were not able to find the symbol, this should not happend but
782          --  since we don't want to stop here we return a string of three
783          --  question marks as the symbol name.
784
785          return "???";
786       end Get_Symbol_Name;
787
788    --  Start of processing for Parse_Identifier_Info
789
790    begin
791       --  The identifier info looks like:
792       --     "38U9*Debug 12|36r6 36r19"
793
794       --  Extract the line, column and entity name information
795
796       Parse_Number (Ali, Ptr, E_Line);
797
798       if Ali (Ptr) > ' ' then
799          E_Type := Ali (Ptr);
800          Ptr := Ptr + 1;
801       end if;
802
803       Parse_Number (Ali, Ptr, E_Col);
804
805       E_Global := False;
806       if Ali (Ptr) >= ' ' then
807          E_Global := (Ali (Ptr) = '*');
808          Ptr := Ptr + 1;
809       end if;
810
811       Parse_Token (Ali, Ptr, E_Name);
812
813       --  Exit if the symbol does not match
814       --  or if we have a local symbol and we do not want it
815
816       if (not Local_Symbols and not E_Global)
817         or else (Pattern.Initialized
818                   and then not Match (Pattern, Ali (E_Name .. Ptr - 1)))
819         or else (E_Name >= Ptr)
820       then
821          --  Skip rest of this line and all continuation lines
822
823          loop
824             Parse_EOL (Ali, Ptr);
825             exit when Ali (Ptr) /= '.';
826          end loop;
827          return;
828       end if;
829
830       --  Insert the declaration in the table
831
832       Decl_Ref := Add_Declaration
833         (File.X_File, Ali (E_Name .. Ptr - 1), E_Line, E_Col, E_Type);
834
835       if Ali (Ptr) = '<'
836         or else Ali (Ptr) = '('
837         or else Ali (Ptr) = '{'
838       then
839
840          --  Here we have a type derivation information. The format is
841          --  <3|12I45> which means that the current entity is derived from the
842          --  type defined in unit number 3, line 12 column 45. The pipe and
843          --  unit number is optional. It is specified only if the parent type
844          --  is not defined in the current unit.
845
846          --  We could also have something like
847          --  16I9*I<integer>
848          --  that indicates that I derives from the predefined type integer.
849
850          Ptr := Ptr + 1;
851
852          if Ali (Ptr) in '0' .. '9' then
853             Parse_Derived_Info : declare
854                P_Line   : Natural;          --  parent entity line
855                P_Column : Natural;          --  parent entity column
856                P_Type   : Character;        --  parent entity type
857                P_Eun    : Positive;         --  parent entity file number
858
859             begin
860                Parse_Number (Ali, Ptr, P_Line);
861
862                --  If we have a pipe then the first number was the unit number
863
864                if Ali (Ptr) = '|' then
865                   P_Eun := P_Line;
866                   Ptr := Ptr + 1;
867
868                   --  Now we have the line number
869
870                   Parse_Number (Ali, Ptr, P_Line);
871
872                else
873                   --  We don't have a unit number specified, so we set P_Eun to
874                   --  the current unit.
875
876                   for K in Dependencies_Tables.First .. Last (File.Dep) loop
877                      P_Eun := K;
878                      exit when File.Dep.Table (K) = File_Ref;
879                   end loop;
880                end if;
881
882                --  Then parse the type and column number
883
884                P_Type := Ali (Ptr);
885                Ptr := Ptr + 1;
886                Parse_Number (Ali, Ptr, P_Column);
887
888                --  Skip '>', or ')' or '>'
889
890                Ptr := Ptr + 1;
891
892                --  The derived info is needed only is the derived info mode is
893                --  on or if we want to output the type hierarchy
894
895                if Der_Info or else Type_Tree then
896                   Add_Parent
897                     (Decl_Ref,
898                      Get_Symbol_Name (P_Eun, P_Line, P_Column),
899                      P_Line,
900                      P_Column,
901                      File.Dep.Table (P_Eun));
902                end if;
903
904                if Type_Tree then
905                   Search_Parent_Tree : declare
906                      Pattern         : Search_Pattern;  --  Parent type pattern
907                      File_Pos_Backup : Positive;
908
909                   begin
910                      Add_Entity
911                        (Pattern,
912                         Get_Symbol_Name (P_Eun, P_Line, P_Column)
913                         & ':' & Get_Gnatchop_File (File.Dep.Table (P_Eun))
914                         & ':' & Get_Line (Get_Parent (Decl_Ref))
915                         & ':' & Get_Column (Get_Parent (Decl_Ref)),
916                      False);
917
918                      --  No default match is needed to look for the parent type
919                      --  since we are using the fully qualified symbol name:
920                      --  symbol:file:line:column
921
922                      Set_Default_Match (False);
923
924                      --  The parent type is defined in the same unit as the
925                      --  derived type. So we want to revisit the unit.
926
927                      File_Pos_Backup   := File.Current_Line;
928
929                      if File.Dep.Table (P_Eun) = File_Ref then
930
931                         --  set file pointer at the start of the xref lines
932
933                         File.Current_Line := File.Xref_Line;
934
935                         Revisit_ALI_File : declare
936                            File_Existed : Boolean;
937                            File_Ref     : File_Reference;
938
939                         begin
940                            Add_To_Xref_File
941                              (ALI_File_Name
942                               (Get_File (File.Dep.Table (P_Eun))),
943                               File_Existed,
944                               File_Ref,
945                               Visited => False);
946                            Set_Unvisited (File_Ref);
947                         end Revisit_ALI_File;
948                      end if;
949
950                      Search (Pattern,
951                              Local_Symbols, False, False, Der_Info, Type_Tree);
952
953                      File.Current_Line := File_Pos_Backup;
954                   end Search_Parent_Tree;
955                end if;
956             end Parse_Derived_Info;
957
958          else
959             while Ali (Ptr) /= '>'
960               and then Ali (Ptr) /= ')'
961               and then Ali (Ptr) /= '}'
962             loop
963                Ptr := Ptr + 1;
964             end loop;
965             Ptr := Ptr + 1;
966          end if;
967
968       elsif Ali (Ptr) = '=' then
969          declare
970             P_Line, P_Column : Natural;
971          begin
972             Ptr := Ptr + 1;
973             Parse_Number (Ali, Ptr, P_Line);
974             Ptr := Ptr + 1;
975             Parse_Number (Ali, Ptr, P_Column);
976          end;
977       end if;
978
979       --  To find the body, we will have to parse the file too
980
981       if Wide_Search then
982          declare
983             File_Existed : Boolean;
984             File_Ref     : File_Reference;
985             File_Name    : constant String :=
986                              Get_Gnatchop_File (File.X_File);
987
988          begin
989             Add_To_Xref_File
990               (ALI_File_Name (File_Name), File_Existed, File_Ref, False);
991          end;
992       end if;
993
994       --  Parse references to this entity.
995       --  Ptr points to next reference with leading blanks
996
997       loop
998          --  Process references on current line
999
1000          while Ali (Ptr) = ' ' or Ali (Ptr) = ASCII.HT loop
1001
1002             --  For every reference read the line, type and column,
1003             --  optionally preceded by a file number and a pipe symbol.
1004
1005             Parse_Number (Ali, Ptr, R_Line);
1006
1007             if Ali (Ptr) = Pipe then
1008                Ptr := Ptr + 1;
1009                File_Ref := File_Name (File, R_Line);
1010
1011                Parse_Number (Ali, Ptr, R_Line);
1012             end if;
1013
1014             if Ali (Ptr) > ' ' then
1015                R_Type := Ali (Ptr);
1016                Ptr := Ptr + 1;
1017             end if;
1018
1019             Parse_Number (Ali, Ptr, R_Col);
1020
1021             --  Insert the reference or body in the table
1022
1023             Add_Reference (Decl_Ref, File_Ref, R_Line, R_Col, R_Type);
1024
1025             --  Skip generic information, if any
1026
1027             if Ali (Ptr) = '[' then
1028                declare
1029                   Num_Nested : Integer := 1;
1030                begin
1031                   Ptr := Ptr + 1;
1032                   while Num_Nested /= 0 loop
1033                      if Ali (Ptr) = ']' then
1034                         Num_Nested := Num_Nested - 1;
1035                      elsif Ali (Ptr) = '[' then
1036                         Num_Nested := Num_Nested + 1;
1037                      end if;
1038                      Ptr := Ptr + 1;
1039                   end loop;
1040                end;
1041             end if;
1042
1043          end loop;
1044
1045          Parse_EOL (Ali, Ptr);
1046
1047          --   Loop until new line is no continuation line
1048
1049          exit when Ali (Ptr) /= '.';
1050          Ptr := Ptr + 1;
1051       end loop;
1052    end Parse_Identifier_Info;
1053
1054    ------------------
1055    -- Parse_Number --
1056    ------------------
1057
1058    procedure Parse_Number
1059      (Source    : access String;
1060       Ptr       : in out Positive;
1061       Number    : out Natural)
1062    is
1063    begin
1064       --  Skip separators
1065
1066       while Source (Ptr) = ' ' or else Source (Ptr) = ASCII.HT loop
1067          Ptr := Ptr + 1;
1068       end loop;
1069
1070       Number := 0;
1071       while Source (Ptr) in '0' .. '9' loop
1072          Number := 10 * Number
1073            + (Character'Pos (Source (Ptr)) - Character'Pos ('0'));
1074          Ptr := Ptr + 1;
1075       end loop;
1076    end Parse_Number;
1077
1078    -----------------
1079    -- Parse_Token --
1080    -----------------
1081
1082    procedure Parse_Token
1083      (Source    : access String;
1084       Ptr       : in out Positive;
1085       Token_Ptr : out Positive)
1086    is
1087       In_Quotes : Boolean := False;
1088
1089    begin
1090       --  Skip separators
1091
1092       while Source (Ptr) = ' ' or else Source (Ptr) = ASCII.HT loop
1093          Ptr := Ptr + 1;
1094       end loop;
1095
1096       Token_Ptr := Ptr;
1097
1098       --  Find end-of-token
1099
1100       while (In_Quotes or else
1101                not (Source (Ptr) = ' '
1102                     or else Source (Ptr) = ASCII.HT
1103                     or else Source (Ptr) = '<'
1104                     or else Source (Ptr) = '{'
1105                     or else Source (Ptr) = '='
1106                     or else Source (Ptr) = '('))
1107         and then Source (Ptr) >= ' '
1108       loop
1109          if Source (Ptr) = '"' then
1110             In_Quotes := not In_Quotes;
1111          end if;
1112
1113          Ptr := Ptr + 1;
1114       end loop;
1115    end Parse_Token;
1116
1117    ----------------------
1118    -- Parse_X_Filename --
1119    ----------------------
1120
1121    procedure Parse_X_Filename (File : in out ALI_File) is
1122       Ali     : String_Access renames File.Buffer;
1123       Ptr     : Positive renames File.Current_Line;
1124       File_Nr : Natural;
1125
1126    begin
1127       while Ali (Ptr) = 'X' loop
1128
1129          --  The current line is the start of a new Xref file section,
1130          --  whose format looks like:
1131
1132          --     " X 1 debug.ads"
1133
1134          --  Skip the X and read the file number for the new X_File
1135
1136          Ptr := Ptr + 1;
1137          Parse_Number (Ali, Ptr, File_Nr);
1138
1139          if File_Nr > 0 then
1140             File.X_File := File.Dep.Table (File_Nr);
1141          end if;
1142
1143          Parse_EOL (Ali, Ptr);
1144       end loop;
1145
1146    end Parse_X_Filename;
1147
1148    --------------------
1149    -- Print_Gnatfind --
1150    --------------------
1151
1152    procedure Print_Gnatfind
1153      (References     : Boolean;
1154       Full_Path_Name : Boolean)
1155    is
1156       Decl : Declaration_Reference := First_Declaration;
1157       Ref1 : Reference;
1158       Ref2 : Reference;
1159
1160       procedure Print_Ref
1161         (Ref : Reference;
1162          Msg : String := "      ");
1163       --  Print a reference, according to the extended tag of the output
1164
1165       ---------------
1166       -- Print_Ref --
1167       ---------------
1168
1169       procedure Print_Ref
1170         (Ref : Reference;
1171          Msg : String := "      ")
1172       is
1173          Buffer : constant String :=
1174            Osint.To_Host_File_Spec
1175              (Get_Gnatchop_File (Ref, Full_Path_Name)).all
1176            & ":" & Get_Line (Ref)
1177            & ":" & Get_Column (Ref)
1178            & ": ";
1179          Num_Blanks : Integer := Longest_File_Name + 10 - Buffer'Length;
1180
1181       begin
1182          Num_Blanks := Integer'Max (0, Num_Blanks);
1183          Write_Line
1184            (Buffer
1185             & String'(1 .. Num_Blanks => ' ')
1186             & Msg & " " & Get_Symbol (Decl));
1187          if Get_Source_Line (Ref)'Length /= 0 then
1188             Write_Line ("   " & Get_Source_Line (Ref));
1189          end if;
1190       end Print_Ref;
1191
1192    --  Start of processing for Print_Gnatfind
1193
1194    begin
1195       while Decl /= Empty_Declaration loop
1196          if Match (Decl) then
1197
1198             --  Output the declaration
1199
1200             declare
1201                Parent : constant Declaration_Reference := Get_Parent (Decl);
1202                Buffer : constant String :=
1203                  Osint.To_Host_File_Spec
1204                    (Get_Gnatchop_File (Decl, Full_Path_Name)).all
1205                  & ":" & Get_Line (Decl)
1206                  & ":" & Get_Column (Decl)
1207                  & ": ";
1208                Num_Blanks : Integer := Longest_File_Name + 10 - Buffer'Length;
1209
1210             begin
1211                Num_Blanks := Integer'Max (0, Num_Blanks);
1212                Write_Line
1213                  (Buffer & String'(1 .. Num_Blanks => ' ')
1214                   & "(spec) " & Get_Symbol (Decl));
1215
1216                if Parent /= Empty_Declaration then
1217                   Write_Line
1218                     (Buffer & String'(1 .. Num_Blanks => ' ')
1219                      & "   derived from " & Get_Symbol (Parent)
1220                      & " ("
1221                      & Osint.To_Host_File_Spec (Get_Gnatchop_File (Parent)).all
1222                      & ':' & Get_Line (Parent)
1223                      & ':' & Get_Column (Parent) & ')');
1224                end if;
1225             end;
1226
1227             if Get_Source_Line (Decl)'Length /= 0 then
1228                Write_Line ("   " & Get_Source_Line (Decl));
1229             end if;
1230
1231             --  Output the body (sorted)
1232
1233             Ref1 := First_Body (Decl);
1234             while Ref1 /= Empty_Reference loop
1235                Print_Ref (Ref1, "(body)");
1236                Ref1 := Next (Ref1);
1237             end loop;
1238
1239             if References then
1240                Ref1 := First_Modif (Decl);
1241                Ref2 := First_Reference (Decl);
1242                while Ref1 /= Empty_Reference
1243                  or else Ref2 /= Empty_Reference
1244                loop
1245                   if Compare (Ref1, Ref2) = LessThan then
1246                      Print_Ref (Ref1);
1247                      Ref1 := Next (Ref1);
1248                   else
1249                      Print_Ref (Ref2);
1250                      Ref2 := Next (Ref2);
1251                   end if;
1252                end loop;
1253             end if;
1254          end if;
1255
1256          Decl := Next (Decl);
1257       end loop;
1258    end Print_Gnatfind;
1259
1260    ------------------
1261    -- Print_Unused --
1262    ------------------
1263
1264    procedure Print_Unused (Full_Path_Name : in Boolean) is
1265       Decl : Declaration_Reference := First_Declaration;
1266       Ref  : Reference;
1267
1268    begin
1269       while Decl /= Empty_Declaration loop
1270          if First_Modif (Decl) = Empty_Reference
1271            and then First_Reference (Decl) = Empty_Reference
1272          then
1273             Write_Str (Get_Symbol (Decl)
1274                       & " "
1275                       & Get_Type (Decl)
1276                       & " "
1277                       & Osint.To_Host_File_Spec
1278                          (Get_Gnatchop_File (Decl, Full_Path_Name)).all
1279                       & ':'
1280                       & Get_Line (Decl)
1281                       & ':'
1282                       & Get_Column (Decl));
1283
1284             --  Print the body if any
1285
1286             Ref := First_Body (Decl);
1287
1288             if Ref /= Empty_Reference then
1289                Write_Line (' '
1290                           & Osint.To_Host_File_Spec
1291                              (Get_Gnatchop_File (Ref, Full_Path_Name)).all
1292                           & ':' & Get_Line (Ref)
1293                           & ':' & Get_Column (Ref));
1294             else
1295                Write_Eol;
1296             end if;
1297          end if;
1298
1299          Decl := Next (Decl);
1300       end loop;
1301    end Print_Unused;
1302
1303    --------------
1304    -- Print_Vi --
1305    --------------
1306
1307    procedure Print_Vi (Full_Path_Name : in Boolean) is
1308       Tab  : constant Character := ASCII.HT;
1309       Decl : Declaration_Reference := First_Declaration;
1310       Ref  : Reference;
1311
1312    begin
1313       while Decl /= Empty_Declaration loop
1314          Write_Line (Get_Symbol (Decl) & Tab
1315                             & Get_File (Decl, Full_Path_Name) & Tab
1316                             & Get_Line (Decl));
1317
1318          --  Print the body if any
1319
1320          Ref := First_Body (Decl);
1321
1322          if Ref /= Empty_Reference then
1323             Write_Line (Get_Symbol (Decl) & Tab
1324                                & Get_File (Ref, Full_Path_Name)
1325                                & Tab
1326                                & Get_Line (Ref));
1327          end if;
1328
1329          --  Print the modifications
1330
1331          Ref := First_Modif (Decl);
1332
1333          while Ref /= Empty_Reference loop
1334             Write_Line (Get_Symbol (Decl) & Tab
1335                                & Get_File (Ref, Full_Path_Name)
1336                                & Tab
1337                                & Get_Line (Ref));
1338             Ref := Next (Ref);
1339          end loop;
1340
1341          Decl := Next (Decl);
1342       end loop;
1343    end Print_Vi;
1344
1345    ----------------
1346    -- Print_Xref --
1347    ----------------
1348
1349    procedure Print_Xref (Full_Path_Name : in Boolean) is
1350       Decl : Declaration_Reference := First_Declaration;
1351       Ref  : Reference;
1352       File : File_Reference;
1353
1354       Margin : constant := 10;
1355       --  Column where file names start
1356
1357       procedure New_Line80;
1358       --  Go to start of new line
1359
1360       procedure Print80 (S : in String);
1361       --  Print the text, respecting the 80 columns rule.
1362
1363       procedure Print_Ref (Line, Column : String);
1364       --  The beginning of the output is aligned on a column multiple of 9
1365
1366       ----------------
1367       -- New_Line80 --
1368       ----------------
1369
1370       procedure New_Line80 is
1371       begin
1372          Write_Eol;
1373          Write_Str (String'(1 .. Margin - 1 => ' '));
1374       end New_Line80;
1375
1376       -------------
1377       -- Print80 --
1378       -------------
1379
1380       procedure Print80 (S : in String) is
1381          Align : Natural := Margin - (Integer (Column) mod Margin);
1382       begin
1383          if Align = Margin then
1384             Align := 0;
1385          end if;
1386
1387          Write_Str (String'(1 .. Align => ' ') & S);
1388       end Print80;
1389
1390       ---------------
1391       -- Print_Ref --
1392       ---------------
1393
1394       procedure Print_Ref (Line, Column : String) is
1395          Line_Align : constant Integer := 4 - Line'Length;
1396
1397          S : constant String := String'(1 .. Line_Align => ' ')
1398                                   & Line & ':' & Column;
1399
1400          Align : Natural := Margin - (Integer (Output.Column) mod Margin);
1401
1402       begin
1403          if Align = Margin then
1404             Align := 0;
1405          end if;
1406
1407          if Integer (Output.Column) + Align + S'Length > 79 then
1408             New_Line80;
1409             Align := 0;
1410          end if;
1411
1412          Write_Str (String'(1 .. Align => ' ') & S);
1413       end Print_Ref;
1414
1415    --  Start of processing for Print_Xref
1416
1417    begin
1418       while Decl /= Empty_Declaration loop
1419          Write_Str (Get_Symbol (Decl));
1420
1421          while Column < Type_Position loop
1422             Write_Char (' ');
1423          end loop;
1424
1425          Write_Line (Get_Full_Type (Get_Type (Decl)));
1426
1427          Write_Parent_Info : declare
1428             Parent : constant Declaration_Reference := Get_Parent (Decl);
1429          begin
1430             if Parent /= Empty_Declaration then
1431                Write_Str ("  Ptype: ");
1432                Print80
1433                  (Osint.To_Host_File_Spec (Get_Gnatchop_File (Parent)).all);
1434                Print_Ref (Get_Line (Parent), Get_Column (Parent));
1435                Print80 ("  " & Get_Symbol (Parent));
1436                Write_Eol;
1437             end if;
1438          end Write_Parent_Info;
1439
1440          Write_Str ("  Decl:  ");
1441          Print80
1442            (Osint.To_Host_File_Spec
1443              (Get_Gnatchop_File (Decl, Full_Path_Name)).all & ' ');
1444          Print_Ref (Get_Line (Decl), Get_Column (Decl));
1445
1446          --  Print the body if any
1447
1448          Ref := First_Body (Decl);
1449
1450          if Ref /= Empty_Reference then
1451             Write_Eol;
1452             Write_Str ("  Body:  ");
1453             Print80
1454               (Osint.To_Host_File_Spec
1455                 (Get_Gnatchop_File (Ref, Full_Path_Name)).all & ' ');
1456             Print_Ref (Get_Line (Ref), Get_Column (Ref));
1457          end if;
1458
1459          --  Print the modifications if any
1460
1461          Ref := First_Modif (Decl);
1462
1463          if Ref /= Empty_Reference then
1464             Write_Eol;
1465             Write_Str ("  Modi:  ");
1466          end if;
1467
1468          File := Empty_File;
1469
1470          while Ref /= Empty_Reference loop
1471             if Get_File_Ref (Ref) /= File then
1472                if File /= Empty_File then
1473                   New_Line80;
1474                end if;
1475
1476                File := Get_File_Ref (Ref);
1477                Write_Str
1478                  (Get_Gnatchop_File (Ref, Full_Path_Name) & ' ');
1479                Print_Ref (Get_Line (Ref), Get_Column (Ref));
1480
1481             else
1482                Print_Ref (Get_Line (Ref), Get_Column (Ref));
1483             end if;
1484
1485             Ref := Next (Ref);
1486          end loop;
1487
1488          --  Print the references
1489
1490          Ref := First_Reference (Decl);
1491
1492          if Ref /= Empty_Reference then
1493             Write_Eol;
1494             Write_Str ("  Ref:   ");
1495          end if;
1496
1497          File := Empty_File;
1498
1499          while Ref /= Empty_Reference loop
1500             if Get_File_Ref (Ref) /= File then
1501                if File /= Empty_File then
1502                   New_Line80;
1503                end if;
1504
1505                File := Get_File_Ref (Ref);
1506                Write_Str
1507                  (Osint.To_Host_File_Spec
1508                    (Get_Gnatchop_File (Ref, Full_Path_Name)).all & ' ');
1509                Print_Ref (Get_Line (Ref), Get_Column (Ref));
1510
1511             else
1512                Print_Ref (Get_Line (Ref), Get_Column (Ref));
1513             end if;
1514
1515             Ref := Next (Ref);
1516          end loop;
1517
1518          Write_Eol;
1519          Decl := Next (Decl);
1520       end loop;
1521    end Print_Xref;
1522
1523    ---------------
1524    -- Read_File --
1525    ---------------
1526
1527    procedure Read_File
1528      (FD       : File_Descriptor;
1529       Contents : out String_Access;
1530       Success  : out Boolean)
1531    is
1532       Length : constant File_Offset := File_Offset (File_Length (FD));
1533       --  Include room for EOF char
1534
1535       Buffer : String (1 .. Length + 1);
1536
1537       This_Read : Integer;
1538       Read_Ptr  : File_Offset := 1;
1539
1540    begin
1541
1542       loop
1543          This_Read := Read (FD,
1544            A => Buffer (Read_Ptr)'Address,
1545            N => Length + 1 - Read_Ptr);
1546          Read_Ptr := Read_Ptr + Integer'Max (This_Read, 0);
1547          exit when This_Read <= 0;
1548       end loop;
1549
1550       Buffer (Read_Ptr) := EOF;
1551       Contents := new String'(Buffer (1 .. Read_Ptr));
1552
1553       --  Things aren't simple on VMS due to the plethora of file types
1554       --  and organizations. It seems clear that there shouldn't be more
1555       --  bytes read than are contained in the file though.
1556
1557       if Hostparm.OpenVMS then
1558          Success := Read_Ptr <= Length + 1;
1559       else
1560          Success := Read_Ptr = Length + 1;
1561       end if;
1562    end Read_File;
1563
1564    ------------
1565    -- Search --
1566    ------------
1567
1568    procedure Search
1569      (Pattern       : Search_Pattern;
1570       Local_Symbols : Boolean;
1571       Wide_Search   : Boolean;
1572       Read_Only     : Boolean;
1573       Der_Info      : Boolean;
1574       Type_Tree     : Boolean)
1575    is
1576       type String_Access is access String;
1577       procedure Free is new Unchecked_Deallocation (String, String_Access);
1578
1579       ALIfile    : ALI_File;
1580       File_Ref   : File_Reference;
1581       Strip_Num  : Natural := 0;
1582       Ali_Name   : String_Access;
1583
1584    begin
1585       --  If we want all the .ali files, then find them
1586
1587       if Wide_Search then
1588          Find_ALI_Files;
1589       end if;
1590
1591       loop
1592          --  Get the next unread ali file
1593
1594          File_Ref := Next_Unvisited_File;
1595
1596          exit when File_Ref = Empty_File;
1597
1598          --  Find the ALI file to use. Most of the time, it will be the unit
1599          --  name, with a different extension. However, when dealing with
1600          --  separates the ALI file is in fact the parent's ALI file (and this
1601          --  is recursive, in case the parent itself is a separate).
1602
1603          Strip_Num := 0;
1604          loop
1605             Free (Ali_Name);
1606             Ali_Name := new String'
1607               (Get_File (File_Ref, With_Dir => True, Strip => Strip_Num));
1608
1609             --  Striped too many things...
1610             if Ali_Name.all = "" then
1611                if Get_Emit_Warning (File_Ref) then
1612                   Set_Standard_Error;
1613                   Write_Line
1614                     ("warning : file " & Get_File (File_Ref, With_Dir => True)
1615                      & " not found");
1616                   Set_Standard_Output;
1617                end if;
1618                Free (Ali_Name);
1619                exit;
1620
1621                --  If not found, try the parent's ALI file (this is needed for
1622                --  separate units and subprograms).
1623             elsif not File_Exists (Ali_Name.all) then
1624                Strip_Num := Strip_Num + 1;
1625
1626                --  Else we finally found it
1627             else
1628                exit;
1629             end if;
1630          end loop;
1631
1632          --  Now that we have a file name, parse it to find any reference to
1633          --  the entity.
1634
1635          if Ali_Name /= null
1636            and then (Read_Only or else Is_Writable_File (Ali_Name.all))
1637          then
1638             begin
1639                Open (Ali_Name.all, ALIfile);
1640                while ALIfile.Buffer (ALIfile.Current_Line) /= EOF loop
1641                   Parse_X_Filename (ALIfile);
1642                   Parse_Identifier_Info (Pattern, ALIfile, Local_Symbols,
1643                      Der_Info, Type_Tree, Wide_Search);
1644                end loop;
1645
1646             exception
1647                when No_Xref_Information   =>
1648                   if Get_Emit_Warning (File_Ref) then
1649                      Set_Standard_Error;
1650                      Write_Line
1651                        ("warning : No cross-referencing information in  "
1652                         & Ali_Name.all);
1653                      Set_Standard_Output;
1654                   end if;
1655             end;
1656          end if;
1657       end loop;
1658
1659       Free (Ali_Name);
1660    end Search;
1661
1662    -----------------
1663    -- Search_Xref --
1664    -----------------
1665
1666    procedure Search_Xref
1667      (Local_Symbols : Boolean;
1668       Read_Only     : Boolean;
1669       Der_Info      : Boolean)
1670    is
1671       ALIfile    : ALI_File;
1672       File_Ref   : File_Reference;
1673       Null_Pattern : Search_Pattern;
1674    begin
1675       loop
1676          --  Find the next unvisited file
1677
1678          File_Ref := Next_Unvisited_File;
1679          exit when File_Ref = Empty_File;
1680
1681          --  Search the object directories for the .ali file
1682
1683          if Read_Only
1684            or else Is_Writable_File (Get_File (File_Ref, With_Dir => True))
1685          then
1686             begin
1687                Open (Get_File (File_Ref, With_Dir => True), ALIfile, True);
1688
1689                while ALIfile.Buffer (ALIfile.Current_Line) /= EOF loop
1690                   Parse_X_Filename (ALIfile);
1691                   Parse_Identifier_Info
1692                     (Null_Pattern, ALIfile, Local_Symbols, Der_Info);
1693                end loop;
1694
1695             exception
1696                when No_Xref_Information =>  null;
1697             end;
1698          end if;
1699       end loop;
1700    end Search_Xref;
1701
1702 end Xref_Lib;