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