aboutsummaryrefslogtreecommitdiffstats
path: root/src/files_map.adb
diff options
context:
space:
mode:
Diffstat (limited to 'src/files_map.adb')
-rw-r--r--src/files_map.adb857
1 files changed, 857 insertions, 0 deletions
diff --git a/src/files_map.adb b/src/files_map.adb
new file mode 100644
index 000000000..f4927e8db
--- /dev/null
+++ b/src/files_map.adb
@@ -0,0 +1,857 @@
+-- Loading of source files.
+-- Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold
+--
+-- GHDL is free software; you can redistribute it and/or modify it under
+-- the terms of the GNU General Public License as published by the Free
+-- Software Foundation; either version 2, or (at your option) any later
+-- version.
+--
+-- GHDL is distributed in the hope that it will be useful, but WITHOUT 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
+-- along with GHDL; see the file COPYING. If not, write to the Free
+-- Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+-- 02111-1307, USA.
+with Interfaces.C;
+with Ada.Characters.Latin_1;
+with Ada.Text_IO; use Ada.Text_IO;
+with Ada.Unchecked_Deallocation;
+with GNAT.Table;
+with GNAT.OS_Lib;
+with GNAT.Directory_Operations;
+with Name_Table; use Name_Table;
+with Str_Table;
+with Ada.Calendar;
+with Ada.Calendar.Time_Zones;
+
+package body Files_Map is
+
+ -- Check validity of FILE.
+ -- Raise an exception in case of error.
+ procedure Check_File (File: in Source_File_Entry);
+
+ type Lines_Table_Type is array (Positive) of Source_Ptr;
+ type Lines_Table_Ptr is access all Lines_Table_Type;
+
+ -- Data associed with a file.
+ type Source_File_Record is record
+ -- All location between first and last belong to this file.
+ First_Location : Location_Type;
+ Last_Location : Location_Type;
+
+ -- The name_id that identify this file.
+ -- FIXME: what about file aliasing (links) ?
+ File_Name: Name_Id;
+
+ Directory : Name_Id;
+
+ -- The buffer containing the file.
+ Source: File_Buffer_Acc;
+
+ -- Length of the file, which is also the length of the buffer.
+ File_Length: Natural;
+
+ Time_Stamp: Time_Stamp_Id;
+
+ -- Current number of line in Lines_Table.
+ Nbr_Lines: Natural;
+
+ Lines_Table: Lines_Table_Ptr;
+
+ -- Current size of Lines_Table.
+ Lines_Table_Max: Natural;
+
+ -- Cache.
+ Cache_Line : Natural;
+ Cache_Pos : Source_Ptr;
+ end record;
+
+ -- Next location to use.
+ Next_Location : Location_Type := Location_Nil + 1;
+
+ package Source_Files is new GNAT.Table
+ (Table_Index_Type => Source_File_Entry,
+ Table_Component_Type => Source_File_Record,
+ Table_Low_Bound => No_Source_File_Entry + 1,
+ Table_Initial => 16,
+ Table_Increment => 100);
+
+ function Get_Last_Source_File_Entry return Source_File_Entry is
+ begin
+ return Source_Files.Last;
+ end Get_Last_Source_File_Entry;
+
+ Home_Dir : Name_Id := Null_Identifier;
+
+ function Get_Home_Directory return Name_Id is
+ begin
+ if Home_Dir = Null_Identifier then
+ GNAT.Directory_Operations.Get_Current_Dir (Name_Buffer, Name_Length);
+ Home_Dir := Get_Identifier;
+ end if;
+ return Home_Dir;
+ end Get_Home_Directory;
+
+ procedure Location_To_File_Pos (Location : Location_Type;
+ File : out Source_File_Entry;
+ Pos : out Source_Ptr)
+ is
+ begin
+ -- FIXME: use a cache
+ -- FIXME: dicotomy
+ for I in Source_Files.First .. Source_Files.Last loop
+ declare
+ F : Source_File_Record renames Source_Files.Table (I);
+ begin
+ if Location >= F.First_Location
+ and then Location <= F.Last_Location
+ then
+ File := I;
+ Pos := Source_Ptr (Location - F.First_Location);
+ return;
+ end if;
+ end;
+ end loop;
+ -- File not found, location must be bad...
+ raise Internal_Error;
+ end Location_To_File_Pos;
+
+ function File_Pos_To_Location (File : Source_File_Entry; Pos : Source_Ptr)
+ return Location_Type
+ is
+ begin
+ if Source_Files.Table (File).Source = null then
+ raise Internal_Error;
+ else
+ return Source_Files.Table (File).First_Location + Location_Type (Pos);
+ end if;
+ end File_Pos_To_Location;
+
+ function Source_File_To_Location (File : Source_File_Entry)
+ return Location_Type
+ is
+ begin
+ return Source_Files.Table (File).First_Location;
+ end Source_File_To_Location;
+
+ procedure Reallocate_Lines_Table
+ (File: in out Source_File_Record; New_Size: Natural) is
+ use Interfaces.C;
+
+ function realloc
+ (memblock : Lines_Table_Ptr;
+ size : size_t)
+ return Lines_Table_Ptr;
+ pragma Import (C, realloc);
+
+ function malloc
+ (size : size_t)
+ return Lines_Table_Ptr;
+ pragma Import (C, malloc);
+
+ New_Table: Lines_Table_Ptr;
+ New_Byte_Size : size_t;
+ begin
+ New_Byte_Size :=
+ size_t(New_Size *
+ Lines_Table_Type'Component_Size / System.Storage_Unit);
+ if File.Lines_Table = null then
+ New_Table := malloc (New_Byte_Size);
+ else
+ New_Table := realloc (File.Lines_Table, New_Byte_Size);
+ end if;
+ if New_Table = null then
+ raise Storage_Error;
+ else
+ File.Lines_Table := New_Table;
+ File.Lines_Table (File.Lines_Table_Max + 1 .. New_Size) :=
+ (others => Source_Ptr_Bad);
+ File.Lines_Table_Max := New_Size;
+ end if;
+ end Reallocate_Lines_Table;
+
+ -- Add a new entry in the lines_table.
+ -- The new entry must be the next one after the last entry.
+ procedure File_Add_Line_Number
+ (File: Source_File_Entry; Line: Natural; Pos: Source_Ptr) is
+ Source_File: Source_File_Record renames Source_Files.Table (File);
+ begin
+ -- Just check File is not out of bounds.
+ if File > Source_Files.Last then
+ raise Internal_Error;
+ end if;
+
+ if Line = 1 then
+ -- The position of the first line is well-known.
+ if Pos /= Source_Ptr_Org then
+ raise Internal_Error;
+ end if;
+ else
+ -- The position of a non first line is not the well-known value.
+ if Pos <= Source_Ptr_Org then
+ raise Internal_Error;
+ end if;
+ -- Take care of scan backtracking.
+ if Line <= Source_File.Nbr_Lines then
+ if Source_File.Lines_Table (Line) = Source_Ptr_Bad then
+ Source_File.Lines_Table (Line) := Pos;
+ elsif Pos /= Source_File.Lines_Table (Line) then
+ Put_Line ("file" & Source_File_Entry'Image (File)
+ & " for line" & Natural'Image (Line)
+ & " pos =" & Source_Ptr'Image (Pos)
+ & ", lines_table = "
+ & Source_Ptr'Image (Source_File.Lines_Table (Line)));
+ raise Internal_Error;
+ end if;
+ return;
+ end if;
+ -- The new entry must just follow the last entry.
+-- if Line /= Source_File.Nbr_Lines + 1 then
+-- raise Internal_Error;
+-- end if;
+ end if;
+ if Line > Source_File.Lines_Table_Max then
+ Reallocate_Lines_Table (Source_File, (Line / 128 + 1) * 128);
+ end if;
+ Source_File.Lines_Table (Line) := Pos;
+ if Line > Source_File.Nbr_Lines then
+ Source_File.Nbr_Lines := Line;
+ end if;
+ -- Source_File.Nbr_Lines := Source_File.Nbr_Lines + 1;
+ if False then
+ Put_Line ("file" & Source_File_Entry'Image (File)
+ & " line" & Natural'Image (Line)
+ & " at position" & Source_Ptr'Image (Pos));
+ end if;
+ end File_Add_Line_Number;
+
+ -- Convert a physical column to a logical column.
+ -- A physical column is the offset in byte from the first byte of the line.
+ -- A logical column is the position of the character when displayed.
+ -- A HT (tabulation) moves the cursor to the next position multiple of 8.
+ -- The first character is at position 1 and at offset 0.
+ procedure Coord_To_Position
+ (File : Source_File_Entry;
+ Line_Pos : Source_Ptr;
+ Offset : Natural;
+ Name : out Name_Id;
+ Col : out Natural)
+ is
+ Source_File: Source_File_Record renames Source_Files.Table (File);
+ Res : Positive := 1;
+ begin
+ Name := Source_File.File_Name;
+ for I in Line_Pos .. Line_Pos + Source_Ptr (Offset) - 1 loop
+ if Source_File.Source (I) = Ada.Characters.Latin_1.HT then
+ Res := Res + 8 - Res mod 8;
+ else
+ Res := Res + 1;
+ end if;
+ end loop;
+ Col := Res;
+ end Coord_To_Position;
+
+ -- Should only be called by Location_To_Coord.
+ function Location_To_Line
+ (Source_File : Source_File_Record; Pos : Source_Ptr)
+ return Natural
+ is
+ Low, Hi, Mid : Natural;
+ Mid1 : Natural;
+ Lines_Table : constant Lines_Table_Ptr := Source_File.Lines_Table;
+ begin
+ -- Look in the cache.
+ if Pos >= Source_File.Cache_Pos then
+ Low := Source_File.Cache_Line;
+ Hi := Source_File.Nbr_Lines;
+ else
+ Low := 1;
+ Hi := Source_File.Cache_Line;
+ end if;
+
+ loop
+ << Again >> null;
+ Mid := (Hi + Low) / 2;
+ if Lines_Table (Mid) = Source_Ptr_Bad then
+ -- There is a hole: no position for this line.
+ -- Set MID1 to a line which has a position.
+ -- Try downward.
+ Mid1 := Mid;
+ while Lines_Table (Mid1) = Source_Ptr_Bad loop
+ -- Note: Low may have no line.
+ exit when Mid1 = Low;
+ Mid1 := Mid1 - 1;
+ end loop;
+ if Mid1 /= Low then
+ -- Mid1 has a line.
+ if Pos < Lines_Table (Mid1) then
+ Hi := Mid1;
+ goto Again;
+ end if;
+ if Pos > Lines_Table (Mid1) then
+ Low := Mid1;
+ goto Again;
+ end if;
+ -- Found, handled just below.
+ else
+ -- Failed (downward is LOW): try upward.
+ Mid1 := Mid;
+ while Lines_Table (Mid1) = Source_Ptr_Bad loop
+ Mid1 := Mid1 + 1;
+ end loop;
+ if Mid1 = Hi then
+ -- Failed: no lines between LOW and HI.
+ if Pos >= Lines_Table (Hi) then
+ Mid1 := Hi;
+ else
+ Mid1 := Low;
+ end if;
+ return Mid1;
+ end if;
+ -- Mid1 has a line.
+ if Pos < Lines_Table (Mid1) then
+ Hi := Mid1;
+ goto Again;
+ end if;
+ if Pos > Lines_Table (Mid1) then
+ Low := Mid1;
+ goto Again;
+ end if;
+ end if;
+ Mid := Mid1;
+ end if;
+ if Pos >= Lines_Table (Mid) then
+ if Mid = Source_File.Nbr_Lines
+ or else Pos < Lines_Table (Mid + 1)
+ or else Pos = Lines_Table (Mid)
+ or else (Hi <= Mid + 1
+ and Lines_Table (Mid + 1) = Source_Ptr_Bad)
+ then
+ return Mid;
+ end if;
+ end if;
+ if Pos < Lines_Table (Mid) then
+ Hi := Mid - 1;
+ else
+ if Lines_Table (Mid + 1) /= Source_Ptr_Bad then
+ Low := Mid + 1;
+ else
+ Low := Mid;
+ end if;
+ end if;
+ end loop;
+ end Location_To_Line;
+
+ procedure Location_To_Coord
+ (Source_File : in out Source_File_Record;
+ Pos : Source_Ptr;
+ Line_Pos : out Source_Ptr;
+ Line : out Natural;
+ Offset : out Natural)
+ is
+ Line_P : Source_Ptr;
+ Line_Threshold : constant Natural := 4;
+ Low, Hi : Natural;
+ begin
+ -- Look in the cache.
+ if Pos >= Source_File.Cache_Pos then
+ Low := Source_File.Cache_Line;
+ Hi := Source_File.Nbr_Lines;
+
+ -- Maybe adjust the threshold.
+ -- Quick look.
+ if Pos - Source_File.Cache_Pos <= 120
+ and then Low + Line_Threshold <= Hi
+ then
+ for I in 1 .. Line_Threshold loop
+ Line_P := Source_File.Lines_Table (Low + I);
+ if Line_P > Pos then
+ Line := Low + I - 1;
+ goto Found;
+ else
+ exit when Line_P = Source_Ptr_Bad;
+ end if;
+ end loop;
+ end if;
+ end if;
+
+ Line := Location_To_Line (Source_File, Pos);
+
+ << Found >> null;
+
+ Line_Pos := Source_File.Lines_Table (Line);
+ Offset := Natural (Pos - Source_File.Lines_Table (Line));
+
+ -- Update cache.
+ Source_File.Cache_Pos := Pos;
+ Source_File.Cache_Line := Line;
+ end Location_To_Coord;
+
+ procedure Location_To_Position
+ (Location : Location_Type;
+ Name : out Name_Id;
+ Line : out Natural;
+ Col : out Natural)
+ is
+ File : Source_File_Entry;
+ Line_Pos : Source_Ptr;
+ Offset : Natural;
+ begin
+ Location_To_Coord (Location, File, Line_Pos, Line, Offset);
+ Coord_To_Position (File, Line_Pos, Offset, Name, Col);
+ end Location_To_Position;
+
+ procedure Location_To_Coord
+ (Location : Location_Type;
+ File : out Source_File_Entry;
+ Line_Pos : out Source_Ptr;
+ Line : out Natural;
+ Offset : out Natural)
+ is
+ Pos : Source_Ptr;
+ begin
+ Location_To_File_Pos (Location, File, Pos);
+ Location_To_Coord (Source_Files.Table (File), Pos,
+ Line_Pos, Line, Offset);
+ end Location_To_Coord;
+
+ -- Convert the first digit of VAL into a character (base 10).
+ function Digit_To_Char (Val: Natural) return Character is
+ begin
+ return Character'Val (Character'Pos ('0') + Val mod 10);
+ end Digit_To_Char;
+
+ -- Format: YYYYMMDDHHmmsscc
+ -- Y: year, M: month, D: day, H: hour, m: minute, s: second, cc:100th sec
+ function Os_Time_To_Time_Stamp_Id (Time: GNAT.OS_Lib.OS_Time)
+ return Time_Stamp_Id
+ is
+ use GNAT.OS_Lib;
+ use Str_Table;
+ Res: Time_Stamp_Id;
+ Year: Year_Type;
+ Month: Month_Type;
+ Day: Day_Type;
+ Hour: Hour_Type;
+ Minute: Minute_Type;
+ Second: Second_Type;
+ begin
+ GM_Split (Time, Year, Month, Day, Hour, Minute, Second);
+ Res := Time_Stamp_Id (Start);
+ Append (Digit_To_Char (Year / 1000));
+ Append (Digit_To_Char (Year / 100));
+ Append (Digit_To_Char (Year / 10));
+ Append (Digit_To_Char (Year / 1));
+ Append (Digit_To_Char (Month / 10));
+ Append (Digit_To_Char (Month / 1));
+ Append (Digit_To_Char (Day / 10));
+ Append (Digit_To_Char (Day / 1));
+ Append (Digit_To_Char (Hour / 10));
+ Append (Digit_To_Char (Hour / 1));
+ Append (Digit_To_Char (Minute / 10));
+ Append (Digit_To_Char (Minute / 1));
+ Append (Digit_To_Char (Second / 10));
+ Append (Digit_To_Char (Second / 1));
+ Append ('.');
+ Append ('0');
+ Append ('0');
+ Append ('0');
+ Finish;
+ return Res;
+ end Os_Time_To_Time_Stamp_Id;
+
+ function Get_File_Time_Stamp (Filename : System.Address)
+ return Time_Stamp_Id
+ is
+ use GNAT.OS_Lib;
+ Fd : File_Descriptor;
+ Res : Time_Stamp_Id;
+ begin
+ Fd := Open_Read (Filename, Binary);
+ if Fd = Invalid_FD then
+ return Null_Time_Stamp;
+ end if;
+ Res := Os_Time_To_Time_Stamp_Id (File_Time_Stamp (Fd));
+ Close (Fd);
+ return Res;
+ end Get_File_Time_Stamp;
+
+ function Get_File_Time_Stamp (FD : GNAT.OS_Lib.File_Descriptor)
+ return Time_Stamp_Id
+ is
+ begin
+ return Os_Time_To_Time_Stamp_Id (GNAT.OS_Lib.File_Time_Stamp (FD));
+ end Get_File_Time_Stamp;
+
+ function Get_Os_Time_Stamp return Time_Stamp_Id
+ is
+ use Ada.Calendar;
+ use Ada.Calendar.Time_Zones;
+ use Str_Table;
+
+ Now : constant Time := Clock;
+ Now_UTC : constant Time := Now - Duration (UTC_Time_Offset (Now) * 60);
+ Year : Year_Number;
+ Month : Month_Number;
+ Day : Day_Number;
+ Sec : Day_Duration;
+ S : Integer;
+ S1 : Integer;
+ M : Integer;
+ Res: Time_Stamp_Id;
+ begin
+ -- Use UTC time (like file time stamp).
+ Split (Now_UTC, Year, Month, Day, Sec);
+
+ Res := Time_Stamp_Id (Start);
+ Append (Digit_To_Char (Year / 1000));
+ Append (Digit_To_Char (Year / 100));
+ Append (Digit_To_Char (Year / 10));
+ Append (Digit_To_Char (Year / 1));
+ Append (Digit_To_Char (Month / 10));
+ Append (Digit_To_Char (Month / 1));
+ Append (Digit_To_Char (Day / 10));
+ Append (Digit_To_Char (Day / 1));
+ S := Integer (Sec);
+ if Day_Duration (S) > Sec then
+ -- We need a truncation.
+ S := S - 1;
+ end if;
+ S1 := S / 3600;
+ Append (Digit_To_Char (S1 / 10));
+ Append (Digit_To_Char (S1));
+ S1 := (S / 60) mod 60;
+ Append (Digit_To_Char (S1 / 10));
+ Append (Digit_To_Char (S1));
+ S1 := S mod 60;
+ Append (Digit_To_Char (S1 / 10));
+ Append (Digit_To_Char (S1));
+
+ Append ('.');
+ Sec := Sec - Day_Duration (S);
+ M := Integer (Sec * 1000);
+ if M = 1000 then
+ -- We need truncation.
+ M := 999;
+ end if;
+ Append (Digit_To_Char (M / 100));
+ Append (Digit_To_Char (M / 10));
+ Append (Digit_To_Char (M));
+ Finish;
+ return Res;
+ end Get_Os_Time_Stamp;
+
+ function Get_Pathname (Directory : Name_Id;
+ Name: Name_Id;
+ Add_Nul : Boolean)
+ return String
+ is
+ L : Natural;
+ begin
+ Image (Name);
+ if not GNAT.OS_Lib.Is_Absolute_Path (Name_Buffer (1 .. Name_Length)) then
+ L := Name_Length;
+ Image (Directory);
+ Name_Buffer (Name_Length + 1 .. Name_Length + L) := Image (Name);
+ Name_Length := Name_Length + L;
+ end if;
+ if Add_Nul then
+ Name_Length := Name_Length + 1;
+ Name_Buffer (Name_Length) := Character'Val (0);
+ end if;
+ return Name_Buffer (1 .. Name_Length);
+ end Get_Pathname;
+
+ -- Find a source_file by DIRECTORY and NAME.
+ -- Return NO_SOURCE_FILE_ENTRY if not already opened.
+ function Find_Source_File (Directory : Name_Id; Name: Name_Id)
+ return Source_File_Entry
+ is
+ begin
+ for I in Source_Files.First .. Source_Files.Last loop
+ if Source_Files.Table (I).File_Name = Name
+ and then Source_Files.Table (I).Directory = Directory
+ then
+ return I;
+ end if;
+ end loop;
+ return No_Source_File_Entry;
+ end Find_Source_File;
+
+ -- Return an entry for a filename.
+ -- The file is not loaded.
+ function Create_Source_File_Entry (Directory : Name_Id; Name: Name_Id)
+ return Source_File_Entry
+ is
+ Res: Source_File_Entry;
+ begin
+ if Find_Source_File (Directory, Name) /= No_Source_File_Entry then
+ raise Internal_Error;
+ end if;
+
+ -- Create a new entry.
+ Res := Source_Files.Allocate;
+ Source_Files.Table (Res) := (First_Location => Next_Location,
+ Last_Location => Next_Location,
+ File_Name => Name,
+ Directory => Directory,
+ Time_Stamp => Null_Time_Stamp,
+ Source => null,
+ File_Length => 0,
+ Nbr_Lines => 0,
+ Lines_Table_Max => 0,
+ Lines_Table => null,
+ Cache_Pos => Source_Ptr_Org,
+ Cache_Line => 1);
+ File_Add_Line_Number (Res, 1, Source_Ptr_Org);
+ return Res;
+ end Create_Source_File_Entry;
+
+ function Create_Source_File_From_String (Name: Name_Id; Content : String)
+ return Source_File_Entry
+ is
+ Res : Source_File_Entry;
+ Buffer: File_Buffer_Acc;
+ Len : constant Source_Ptr := Source_Ptr (Content'Length);
+ begin
+ Res := Create_Source_File_Entry (Null_Identifier, Name);
+
+ Buffer := new File_Buffer
+ (Source_Ptr_Org .. Source_Ptr_Org + Len + 1);
+
+ Buffer (Source_Ptr_Org .. Source_Ptr_Org + Len - 1) :=
+ File_Buffer (Content);
+ Buffer (Source_Ptr_Org + Len) := EOT;
+ Buffer (Source_Ptr_Org + Len + 1) := EOT;
+
+ Source_Files.Table (Res).Last_Location :=
+ Next_Location + Location_Type (Len) + 1;
+ Next_Location := Source_Files.Table (Res).Last_Location + 1;
+ Source_Files.Table (Res).Source := Buffer;
+ Source_Files.Table (Res).File_Length := Natural (Len);
+
+ return Res;
+ end Create_Source_File_From_String;
+
+ function Create_Virtual_Source_File (Name: Name_Id)
+ return Source_File_Entry
+ is
+ begin
+ return Create_Source_File_From_String (Name, "");
+ end Create_Virtual_Source_File;
+
+ -- Return an entry for a filename.
+ -- Load the filename if necessary.
+ function Load_Source_File (Directory : Name_Id; Name: Name_Id)
+ return Source_File_Entry
+ is
+ use GNAT.OS_Lib;
+ Fd: File_Descriptor;
+
+ Res: Source_File_Entry;
+
+ Length: Source_Ptr;
+ Buffer: File_Buffer_Acc;
+ begin
+ -- If the file is already loaded, nothing to do!
+ Res := Find_Source_File (Directory, Name);
+ if Res /= No_Source_File_Entry then
+ if Source_Files.Table (Res).Source = null then
+ raise Internal_Error;
+ end if;
+ return Res;
+ end if;
+
+ -- Open the file (punt on non regular files).
+ declare
+ Filename : String := Get_Pathname (Directory, Name, True);
+ begin
+ if not Is_Regular_File(Filename) then
+ return No_Source_File_Entry;
+ end if;
+ Fd := Open_Read (Filename'Address, Binary);
+ if Fd = Invalid_FD then
+ return No_Source_File_Entry;
+ end if;
+ end;
+
+ Res := Create_Source_File_Entry (Directory, Name);
+
+ Source_Files.Table (Res).Time_Stamp := Get_File_Time_Stamp (Fd);
+
+ Length := Source_Ptr (File_Length (Fd));
+
+ Buffer :=
+ new File_Buffer (Source_Ptr_Org .. Source_Ptr_Org + Length + 1);
+
+ if Read (Fd, Buffer (Source_Ptr_Org)'Address, Integer (Length))
+ /= Integer (Length)
+ then
+ Close (Fd);
+ raise Internal_Error;
+ end if;
+ Buffer (Length) := EOT;
+ Buffer (Length + 1) := EOT;
+
+ if Source_Files.Table (Res).First_Location /= Next_Location then
+ -- Load_Source_File call must follow its Create_Source_File.
+ raise Internal_Error;
+ end if;
+
+ Source_Files.Table (Res).Last_Location :=
+ Next_Location + Location_Type (Length) + 1;
+ Next_Location := Source_Files.Table (Res).Last_Location + 1;
+ Source_Files.Table (Res).Source := Buffer;
+ Source_Files.Table (Res).File_Length := Integer (Length);
+
+ Close (Fd);
+
+ return Res;
+ end Load_Source_File;
+
+ -- Check validity of FILE.
+ -- Raise an exception in case of error.
+ procedure Check_File (File: in Source_File_Entry) is
+ begin
+ if File > Source_Files.Last then
+ raise Internal_Error;
+ end if;
+ end Check_File;
+
+ -- Return a buffer (access to the contents of the file) for a file entry.
+ function Get_File_Source (File: Source_File_Entry)
+ return File_Buffer_Acc is
+ begin
+ Check_File (File);
+ return Source_Files.Table (File).Source;
+ end Get_File_Source;
+
+ -- Return the length of the file (which is the size of the file buffer).
+ function Get_File_Length (File: Source_File_Entry) return Source_Ptr is
+ begin
+ Check_File (File);
+ return Source_Ptr (Source_Files.Table (File).File_Length);
+ end Get_File_Length;
+
+ -- Return the name of the file.
+ function Get_File_Name (File: Source_File_Entry) return Name_Id is
+ begin
+ Check_File (File);
+ return Source_Files.Table (File).File_Name;
+ end Get_File_Name;
+
+ -- Return the date of the file (last modification date) as a string.
+ function Get_File_Time_Stamp (File: Source_File_Entry)
+ return Time_Stamp_Id is
+ begin
+ Check_File (File);
+ return Source_Files.Table (File).Time_Stamp;
+ end Get_File_Time_Stamp;
+
+ function Get_Source_File_Directory (File : Source_File_Entry)
+ return Name_Id is
+ begin
+ Check_File (File);
+ return Source_Files.Table (File).Directory;
+ end Get_Source_File_Directory;
+
+ function Line_To_Position (File : Source_File_Entry; Line : Natural)
+ return Source_Ptr
+ is
+ begin
+ Check_File (File);
+ if Line > Source_Files.Table (File).Nbr_Lines then
+ return Source_Ptr_Bad;
+ else
+ return Source_Files.Table (File).Lines_Table (Line);
+ end if;
+ end Line_To_Position;
+
+ function Is_Eq (L : Time_Stamp_Id; R : Time_Stamp_Id) return Boolean
+ is
+ use Str_Table;
+ L_Str : constant String_Fat_Acc := Get_String_Fat_Acc (String_Id (L));
+ R_Str : constant String_Fat_Acc := Get_String_Fat_Acc (String_Id (R));
+ begin
+ return L_Str (1 .. Time_Stamp_String'Length)
+ = R_Str (1 .. Time_Stamp_String'Length);
+ end Is_Eq;
+
+ function Is_Gt (L : Time_Stamp_Id; R : Time_Stamp_Id) return Boolean
+ is
+ use Str_Table;
+ L_Str : constant String_Fat_Acc := Get_String_Fat_Acc (String_Id (L));
+ R_Str : constant String_Fat_Acc := Get_String_Fat_Acc (String_Id (R));
+ begin
+ return L_Str (1 .. Time_Stamp_String'Length)
+ > R_Str (1 .. Time_Stamp_String'Length);
+ end Is_Gt;
+
+ function Get_Time_Stamp_String (Ts : Time_Stamp_Id) return String is
+ begin
+ if Ts = Null_Time_Stamp then
+ return "NULL_TS";
+ else
+ return String (Str_Table.Get_String_Fat_Acc (String_Id (Ts))
+ (1 .. Time_Stamp_String'Length));
+ end if;
+ end Get_Time_Stamp_String;
+
+ -- Debug procedures.
+ procedure Debug_Source_Lines (File: Source_File_Entry);
+ pragma Unreferenced (Debug_Source_Lines);
+
+ procedure Debug_Source_File;
+ pragma Unreferenced (Debug_Source_File);
+
+ -- Disp sources lines of a file.
+ procedure Debug_Source_Lines (File: Source_File_Entry) is
+ Source_File: Source_File_Record renames Source_Files.Table (File);
+ begin
+ Check_File (File);
+ for I in Positive'First .. Source_File.Nbr_Lines loop
+ Put_Line ("line" & Natural'Image (I) & " at offset"
+ & Source_Ptr'Image (Source_File.Lines_Table (I)));
+ end loop;
+ end Debug_Source_Lines;
+
+ procedure Debug_Source_File is
+ begin
+ for I in Source_Files.First .. Source_Files.Last loop
+ declare
+ F : Source_File_Record renames Source_Files.Table(I);
+ begin
+ Put ("file" & Source_File_Entry'Image (I));
+ Put (" name: " & Image (F.File_Name));
+ Put (" dir:" & Image (F.Directory));
+ Put (" length:" & Natural'Image (F.File_Length));
+ New_Line;
+ if F.Time_Stamp /= Null_Time_Stamp then
+ Put (" time_stamp: " & Get_Time_Stamp_String (F.Time_Stamp));
+ end if;
+ Put (" nbr lines:" & Natural'Image (F.Nbr_Lines));
+ Put (" lines_table_max:" & Natural'Image (F.Lines_Table_Max));
+ New_Line;
+ end;
+ end loop;
+ end Debug_Source_File;
+
+ procedure Initialize
+ is
+ procedure free (Ptr : Lines_Table_Ptr);
+ pragma Import (C, free);
+
+ procedure Free is new Ada.Unchecked_Deallocation
+ (File_Buffer, File_Buffer_Acc);
+ begin
+ for I in Source_Files.First .. Source_Files.Last loop
+ free (Source_Files.Table (I).Lines_Table);
+ Free (Source_Files.Table (I).Source);
+ end loop;
+ Source_Files.Free;
+ Source_Files.Init;
+ end Initialize;
+end Files_Map;