fmap.adb 15.9 KB
Newer Older
1 2 3 4 5 6 7 8
------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                                 F M A P                                  --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
Arnaud Charlet committed
9
--            Copyright (C) 2001-2005, Free Software Foundation, Inc.       --
10 11 12 13 14 15 16 17 18
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNAT;  see file COPYING.  If not, write --
R. Kelley Cook committed
19 20
-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
-- Boston, MA 02110-1301, USA.                                              --
21 22
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
23
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
24 25 26
--                                                                          --
------------------------------------------------------------------------------

27 28 29 30 31
with GNAT.OS_Lib; use GNAT.OS_Lib;
with Namet;       use Namet;
with Opt;         use Opt;
with Osint;       use Osint;
with Output;      use Output;
32 33 34 35
with Table;

with Unchecked_Conversion;

36 37
with GNAT.HTable;

38 39 40 41 42 43 44 45
package body Fmap is

   subtype Big_String is String (Positive);
   type Big_String_Ptr is access all Big_String;

   function To_Big_String_Ptr is new Unchecked_Conversion
     (Source_Buffer_Ptr, Big_String_Ptr);

46 47 48 49 50 51 52
   Max_Buffer : constant := 1_500;
   Buffer : String (1 .. Max_Buffer);
   --  Used to bufferize output when writing to a new mapping file

   Buffer_Last : Natural := 0;
   --  Index of last valid character in Buffer

53 54 55 56 57
   type Mapping is record
      Uname : Unit_Name_Type;
      Fname : File_Name_Type;
   end record;

58
   package File_Mapping is new Table.Table (
59
     Table_Component_Type => Mapping,
60 61 62 63 64
     Table_Index_Type     => Int,
     Table_Low_Bound      => 0,
     Table_Initial        => 1_000,
     Table_Increment      => 1_000,
     Table_Name           => "Fmap.File_Mapping");
65
   --  Mapping table to map unit names to file names
66 67

   package Path_Mapping is new Table.Table (
68
     Table_Component_Type => Mapping,
69 70 71 72 73 74 75 76 77 78
     Table_Index_Type     => Int,
     Table_Low_Bound      => 0,
     Table_Initial        => 1_000,
     Table_Increment      => 1_000,
     Table_Name           => "Fmap.Path_Mapping");
   --  Mapping table to map file names to path names

   type Header_Num is range 0 .. 1_000;

   function Hash (F : Unit_Name_Type) return Header_Num;
79
   --  Function used to compute hash of unit name
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

   No_Entry : constant Int := -1;
   --  Signals no entry in following table

   package Unit_Hash_Table is new GNAT.HTable.Simple_HTable (
     Header_Num => Header_Num,
     Element    => Int,
     No_Element => No_Entry,
     Key        => Unit_Name_Type,
     Hash       => Hash,
     Equal      => "=");
   --  Hash table to map unit names to file names. Used in conjunction with
   --  table File_Mapping above.

   package File_Hash_Table is new GNAT.HTable.Simple_HTable (
     Header_Num => Header_Num,
     Element    => Int,
     No_Element => No_Entry,
     Key        => File_Name_Type,
     Hash       => Hash,
     Equal      => "=");
   --  Hash table to map file names to path names. Used in conjunction with
   --  table Path_Mapping above.

104 105
   Last_In_Table : Int := 0;

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
   package Forbidden_Names is new GNAT.HTable.Simple_HTable (
     Header_Num => Header_Num,
     Element    => Boolean,
     No_Element => False,
     Key        => File_Name_Type,
     Hash       => Hash,
     Equal      => "=");

   -----------------------------
   -- Add_Forbidden_File_Name --
   -----------------------------

   procedure Add_Forbidden_File_Name (Name : Name_Id) is
   begin
      Forbidden_Names.Set (Name, True);
   end Add_Forbidden_File_Name;

123 124 125
   ---------------------
   -- Add_To_File_Map --
   ---------------------
126

127
   procedure Add_To_File_Map
128 129
     (Unit_Name : Unit_Name_Type;
      File_Name : File_Name_Type;
130 131
      Path_Name : File_Name_Type)
   is
132 133 134
   begin
      File_Mapping.Increment_Last;
      Unit_Hash_Table.Set (Unit_Name, File_Mapping.Last);
135 136
      File_Mapping.Table (File_Mapping.Last) :=
        (Uname => Unit_Name, Fname => File_Name);
137 138
      Path_Mapping.Increment_Last;
      File_Hash_Table.Set (File_Name, Path_Mapping.Last);
139 140
      Path_Mapping.Table (Path_Mapping.Last) :=
        (Uname => Unit_Name, Fname => Path_Name);
141
   end Add_To_File_Map;
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

   ----------
   -- Hash --
   ----------

   function Hash (F : Unit_Name_Type) return Header_Num is
   begin
      return Header_Num (Int (F) rem Header_Num'Range_Length);
   end Hash;

   ----------------
   -- Initialize --
   ----------------

   procedure Initialize (File_Name : String) is
      Src : Source_Buffer_Ptr;
      Hi  : Source_Ptr;
      BS  : Big_String_Ptr;
      SP  : String_Ptr;

162 163
      First : Positive := 1;
      Last  : Natural  := 0;
164 165 166 167 168

      Uname : Unit_Name_Type;
      Fname : Name_Id;
      Pname : Name_Id;

169 170
      The_Mapping : Mapping;

171
      procedure Empty_Tables (Warning : Boolean := True);
172 173
      --  Remove all entries in case of incorrect mapping file

174 175 176
      function Find_Name return Name_Id;
      --  Return Error_Name for "/", otherwise call Name_Find

177 178 179 180 181 182 183 184 185 186 187
      procedure Get_Line;
      --  Get a line from the mapping file

      procedure Report_Truncated;
      --  Report a warning when the mapping file is truncated
      --  (number of lines is not a multiple of 3).

      ------------------
      -- Empty_Tables --
      ------------------

188
      procedure Empty_Tables (Warning : Boolean := True) is
189
      begin
190 191 192 193 194 195
         if Warning then
            Write_Str ("mapping file """);
            Write_Str (File_Name);
            Write_Line (""" is not taken into account");
         end if;

196 197 198 199
         Unit_Hash_Table.Reset;
         File_Hash_Table.Reset;
         Path_Mapping.Set_Last (0);
         File_Mapping.Set_Last (0);
200
         Last_In_Table := 0;
201 202 203 204 205 206 207 208
      end Empty_Tables;

      --------------
      -- Get_Line --
      --------------

      procedure Get_Line is
         use ASCII;
209

210
      begin
211
         First := Last + 1;
212 213

         --  If not at the end of file, skip the end of line
214

215 216
         while First < SP'Last
           and then (SP (First) = CR
217 218
                      or else SP (First) = LF
                      or else SP (First) = EOF)
219
         loop
220
            First := First + 1;
221 222
         end loop;

223
         --  If not at the end of file, find the end of this new line
224

225 226
         if First < SP'Last and then SP (First) /= EOF then
            Last := First;
227

228 229 230 231
            while Last < SP'Last
              and then SP (Last + 1) /= CR
              and then SP (Last + 1) /= LF
              and then SP (Last + 1) /= EOF
232
            loop
233
               Last := Last + 1;
234 235 236 237 238
            end loop;

         end if;
      end Get_Line;

239 240 241 242 243 244 245 246 247 248 249 250 251 252
      ---------------
      -- Find_Name --
      ---------------

      function Find_Name return Name_Id is
      begin
         if Name_Buffer (1 .. Name_Len) = "/" then
            return Error_Name;

         else
            return Name_Find;
         end if;
      end Find_Name;

253 254 255 256 257 258
      ----------------------
      -- Report_Truncated --
      ----------------------

      procedure Report_Truncated is
      begin
259 260 261
         Write_Str ("warning: mapping file """);
         Write_Str (File_Name);
         Write_Line (""" is truncated");
262 263
      end Report_Truncated;

264
   --  Start of procedure Initialize
265 266

   begin
267
      Empty_Tables (Warning => False);
268 269 270 271 272
      Name_Len := File_Name'Length;
      Name_Buffer (1 .. Name_Len) := File_Name;
      Read_Source_File (Name_Enter, 0, Hi, Src, Config);

      if Src = null then
273 274 275
         Write_Str ("warning: could not read mapping file """);
         Write_Str (File_Name);
         Write_Line ("""");
276 277 278 279 280 281 282 283 284 285 286 287

      else
         BS := To_Big_String_Ptr (Src);
         SP := BS (1 .. Natural (Hi))'Unrestricted_Access;

         loop
            --  Get the unit name

            Get_Line;

            --  Exit if end of file has been reached

288
            exit when First > Last;
289

290 291 292 293 294
            if (Last < First + 2) or else (SP (Last - 1) /= '%')
              or else (SP (Last) /= 's' and then SP (Last) /= 'b')
            then
               Write_Str ("warning: mapping file """);
               Write_Str (File_Name);
Arnaud Charlet committed
295
               Write_Line (""" is incorrectly formatted");
296 297 298 299
               Empty_Tables;
               return;
            end if;

300 301
            Name_Len := Last - First + 1;
            Name_Buffer (1 .. Name_Len) := SP (First .. Last);
302
            Uname := Find_Name;
303 304 305 306 307 308 309

            --  Get the file name

            Get_Line;

            --  If end of line has been reached, file is truncated

310
            if First > Last then
311 312 313 314 315
               Report_Truncated;
               Empty_Tables;
               return;
            end if;

316 317
            Name_Len := Last - First + 1;
            Name_Buffer (1 .. Name_Len) := SP (First .. Last);
318 319
            Canonical_Case_File_Name (Name_Buffer (1 .. Name_Len));
            Fname := Find_Name;
320 321 322 323 324 325 326

            --  Get the path name

            Get_Line;

            --  If end of line has been reached, file is truncated

327
            if First > Last then
328 329 330 331 332
               Report_Truncated;
               Empty_Tables;
               return;
            end if;

333 334
            Name_Len := Last - First + 1;
            Name_Buffer (1 .. Name_Len) := SP (First .. Last);
335
            Pname := Find_Name;
336 337 338 339

            --  Check for duplicate entries

            if Unit_Hash_Table.Get (Uname) /= No_Entry then
340 341 342 343 344 345 346 347
               Write_Str ("warning: duplicate entry """);
               Write_Str (Get_Name_String (Uname));
               Write_Str (""" in mapping file """);
               Write_Str (File_Name);
               Write_Line ("""");
               The_Mapping := File_Mapping.Table (Unit_Hash_Table.Get (Uname));
               Write_Line (Get_Name_String (The_Mapping.Uname));
               Write_Line (Get_Name_String (The_Mapping.Fname));
348 349 350 351 352
               Empty_Tables;
               return;
            end if;

            if File_Hash_Table.Get (Fname) /= No_Entry then
353 354 355 356 357 358 359 360
               Write_Str ("warning: duplicate entry """);
               Write_Str (Get_Name_String (Fname));
               Write_Str (""" in mapping file """);
               Write_Str (File_Name);
               Write_Line ("""");
               The_Mapping := Path_Mapping.Table (File_Hash_Table.Get (Fname));
               Write_Line (Get_Name_String (The_Mapping.Uname));
               Write_Line (Get_Name_String (The_Mapping.Fname));
361 362 363 364 365 366
               Empty_Tables;
               return;
            end if;

            --  Add the mappings for this unit name

367
            Add_To_File_Map (Uname, Fname, Pname);
368 369
         end loop;
      end if;
370 371 372 373 374

      --  Record the length of the two mapping tables

      Last_In_Table := File_Mapping.Last;

375 376
   end Initialize;

377 378 379 380 381 382 383 384 385 386 387
   ----------------------
   -- Mapped_File_Name --
   ----------------------

   function Mapped_File_Name (Unit : Unit_Name_Type) return File_Name_Type is
      The_Index : constant Int := Unit_Hash_Table.Get (Unit);

   begin
      if The_Index = No_Entry then
         return No_File;
      else
388
         return File_Mapping.Table (The_Index).Fname;
389 390 391 392 393 394
      end if;
   end Mapped_File_Name;

   ----------------------
   -- Mapped_Path_Name --
   ----------------------
395

396
   function Mapped_Path_Name (File : File_Name_Type) return File_Name_Type is
397
      Index : Int := No_Entry;
398

399
   begin
400 401 402 403
      if Forbidden_Names.Get (File) then
         return Error_Name;
      end if;

404 405 406 407 408
      Index := File_Hash_Table.Get (File);

      if Index = No_Entry then
         return No_File;
      else
409
         return Path_Mapping.Table (Index).Fname;
410
      end if;
411
   end Mapped_Path_Name;
412

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
   --------------------------------
   -- Remove_Forbidden_File_Name --
   --------------------------------

   procedure Remove_Forbidden_File_Name (Name : Name_Id) is
   begin
      Forbidden_Names.Set (Name, False);
   end Remove_Forbidden_File_Name;

   ------------------
   -- Reset_Tables --
   ------------------

   procedure Reset_Tables is
   begin
      File_Mapping.Init;
      Path_Mapping.Init;
      Unit_Hash_Table.Reset;
      File_Hash_Table.Reset;
      Forbidden_Names.Reset;
      Last_In_Table := 0;
   end Reset_Tables;

436 437 438 439 440
   -------------------------
   -- Update_Mapping_File --
   -------------------------

   procedure Update_Mapping_File (File_Name : String) is
441 442 443 444 445
      File    : File_Descriptor;
      N_Bytes : Integer;

      Status : Boolean;
      --  For the call to Close
446 447 448 449 450 451 452 453 454 455 456 457

      procedure Put_Line (Name : Name_Id);
      --  Put Name as a line in the Mapping File

      --------------
      -- Put_Line --
      --------------

      procedure Put_Line (Name : Name_Id) is
      begin
         Get_Name_String (Name);

458 459 460 461 462 463 464 465 466 467
         --  If the Buffer is full, write it to the file

         if Buffer_Last + Name_Len + 1 > Buffer'Last then
            N_Bytes := Write (File, Buffer (1)'Address, Buffer_Last);

            if N_Bytes < Buffer_Last then
               Fail ("disk full");
            end if;

            Buffer_Last := 0;
468 469
         end if;

470 471 472 473 474 475
         --  Add the line to the Buffer

         Buffer (Buffer_Last + 1 .. Buffer_Last + Name_Len) :=
           Name_Buffer (1 .. Name_Len);
         Buffer_Last := Buffer_Last + Name_Len + 1;
         Buffer (Buffer_Last) := ASCII.LF;
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
      end Put_Line;

   --  Start of Update_Mapping_File

   begin

      --  Only Update if there are new entries in the mappings

      if Last_In_Table < File_Mapping.Last then

         --  If the tables have been emptied, recreate the file.
         --  Otherwise, append to it.

         if Last_In_Table = 0 then
            declare
               Discard : Boolean;

            begin
               Delete_File (File_Name, Discard);
            end;

            File := Create_File (File_Name, Binary);

         else
            File := Open_Read_Write (Name => File_Name, Fmode => Binary);
         end if;

         if File /= Invalid_FD then
            if Last_In_Table > 0 then
               Lseek (File, 0, Seek_End);
            end if;

            for Unit in Last_In_Table + 1 .. File_Mapping.Last loop
               Put_Line (File_Mapping.Table (Unit).Uname);
               Put_Line (File_Mapping.Table (Unit).Fname);
               Put_Line (Path_Mapping.Table (Unit).Fname);
            end loop;

514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
            --  Before closing the file, write the buffer to the file.
            --  It is guaranteed that the Buffer is not empty, because
            --  Put_Line has been called at least 3 times, and after
            --  a call to Put_Line, the Buffer is not empty.

            N_Bytes := Write (File, Buffer (1)'Address, Buffer_Last);

            if N_Bytes < Buffer_Last then
               Fail ("disk full");
            end if;

            Close (File, Status);

            if not Status then
               Fail ("disk full");
            end if;
530 531 532 533 534 535 536 537 538 539

         elsif not Quiet_Output then
            Write_Str ("warning: could not open mapping file """);
            Write_Str (File_Name);
            Write_Line (""" for update");
         end if;

      end if;
   end Update_Mapping_File;

540
end Fmap;