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