--  Loading of source files.
--  Copyright (C) 2002, 2003, 2004, 2005 Tristan Gingold
--
--  This program 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 of the License, or
--  (at your option) any later version.
--
--  This program 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 this program.  If not, see <gnu.org/licenses>.
with Types; use Types;
with Tables;
with Dyn_Tables;
with Vhdl.Types;

--  Source file handling

package Files_Map is

   --  Range for Tab_Stop.
   subtype Tab_Stop_Range is Natural range 1 .. 120;

   --  Tab width: a tab character jumps to the next column that is one plus a
   --  multiple of this witdh (if columns are numbered from 1).
   Tab_Stop : Tab_Stop_Range := 8;

   --  Create the path from DIRECTORY and NAME:
   --  If NAME is an absolute pathname, then return NAME.
   --  Otherwise, return the concatenation of DIRECTORY and NAME.
   function Get_Pathname (Directory : Name_Id; Name : Name_Id) return String;

   --  Return an entry for a filename.  Null_Identifier for DIRECTORY means
   --  current directory.
   --  Load the filename if necessary.
   --  Return No_Source_File_Entry if the file does not exist.
   function Read_Source_File (Directory : Name_Id; Name : Name_Id)
                              return Source_File_Entry;

   --  Same as Read_Source_File, but first normalize DIRECTORY and NAME.
   function Read_Source_File_Normalize (Directory : Name_Id; Name: Name_Id)
                                       return Source_File_Entry;

   --  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;

   --  Reserve an entry, but do not read any file.
   --  The length should includes the two terminal EOT.
   function Reserve_Source_File
     (Directory : Name_Id; Name : Name_Id; Length : Source_Ptr)
     return Source_File_Entry;

   --  Each file in memory has two terminal EOT.
   EOT : constant Character := Character'Val (4);

   --  From the extension of FILENAME, extract the language.
   --  Return Language_Unknown is not known.
   function Find_Language (Filename : String) return Language_Type;

   --  Create an empty Source_File for a virtual file name.  Used for implicit,
   --  command-line and std.standard library.
   function Create_Virtual_Source_File (Name : Name_Id)
                                       return Source_File_Entry;

   --  Create a Source_File for a possible virtual file NAME using CONTENT
   --  as content of the file.  The file must not already exist.
   function Create_Source_File_From_String (Name : Name_Id; Content : String)
                                           return Source_File_Entry;

   --  Create a pseudo source file from REF for instance INST (created at
   --  location LOC).  The content of this file is the same as REF, but with
   --  new locations so that it is possible to retrieve the instance from
   --  the new locations.
   function Create_Instance_Source_File (Ref : Source_File_Entry;
                                         Loc : Location_Type;
                                         Inst : Vhdl.Types.Vhdl_Node)
                                        return Source_File_Entry;

   --  Unload last source file.  Works only with the last one.  Must be
   --  carefully used as the corresponding locations will be reused.
   procedure Unload_Last_Source_File (File : Source_File_Entry);

   --  Mark FILE as unavailable: clear the name and directory.
   --  This is needed before creating a new source file with the same name.
   procedure Discard_Source_File (File : Source_File_Entry);

   --  Free resources used by FILE, but keep the entry.
   --  (It could be recycled for files that could fit - not implemented).
   procedure Free_Source_File (File : Source_File_Entry);

   --  Relocate location LOC (which must be in the reference of INST_FILE)
   --  for instance INST_FILE.
   function Instance_Relocate (Inst_File : Source_File_Entry;
                               Loc : Location_Type) return Location_Type;

   --  If LOC is a location of an instance (in a file created by
   --  create_instance_source_file), return the location where the instance
   --  has been created.  Otherwise, return No_Location.
   function Location_Instance_To_Location (Loc : Location_Type)
                                          return Location_Type;

   --  If POS points to the start of the gap of FILE, it will be updated
   --  to the next character after the gap.
   procedure Skip_Gap (File : Source_File_Entry; Pos : in out Source_Ptr);

   --  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;

   --  Likewise but return a pointer.  To be used only from non-Ada code.
   function Get_File_Buffer (File : Source_File_Entry) return File_Buffer_Ptr;

   --  Set the length of the file (which is less than the size of the
   --  file buffer).
   --  Set also append two EOT at the end of the file.
   procedure Set_File_Length (File : Source_File_Entry; Length : Source_Ptr);

   --  Get the position of the first EOT character.
   function Get_File_Length (File : Source_File_Entry) return Source_Ptr;

   --  Get the length of the content; this is the file length minus the gap,
   --  if the gap is before the end.
   function Get_Content_Length (File : Source_File_Entry) return Source_Ptr;

   --  Get the length of the buffer, which always includes the gap and the
   --  two terminal EOT.
   function Get_Buffer_Length (File : Source_File_Entry) return Source_Ptr;

   --  Return the name of the file.
   function Get_File_Name (File : Source_File_Entry) return Name_Id;

   --  Return the directory of the file.
   function Get_Directory_Name (File : Source_File_Entry) return Name_Id;

   --  Return the entry of the last known file.
   --  This allow the user to create a table of Source_File_Entry.
   function Get_Last_Source_File_Entry return Source_File_Entry;

   --  Time stamp handling.
   function Is_Eq (L : Time_Stamp_Id; R : Time_Stamp_Id) return Boolean;
   function Is_Gt (L : Time_Stamp_Id; R : Time_Stamp_Id) return Boolean;
   function Get_Time_Stamp_String (Ts : Time_Stamp_Id) return String;

   --  Return the checksum of the content of FILE.
   function Get_File_Checksum (File : Source_File_Entry)
                              return File_Checksum_Id;

   --  True if two file checksums are identical.
   function Is_Eq (L, R : File_Checksum_Id) return Boolean;

   --  String image of CHECKSUM.
   function Get_File_Checksum_String (Checksum : File_Checksum_Id)
                                     return String;

   --  Return the current date of the system.
   function Get_Os_Time_Stamp return Time_Stamp_Id;

   --  Return the home directory (current directory).
   function Get_Home_Directory return Name_Id;

   --  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 : Positive; Pos : Source_Ptr);

   --  Convert LOCATION to a source file.  Return No_Source_File_Entry if
   --  LOCATION is incorrect.
   function Location_To_File (Location : Location_Type)
                             return Source_File_Entry;

   --  Convert LOCATION and FILE to a position (offset) into the source file.
   function Location_File_To_Pos
     (Location : Location_Type; File : Source_File_Entry) return Source_Ptr;

   --  Convert LOCATION and FILE to a line number.
   function Location_File_To_Line
     (Location : Location_Type; File : Source_File_Entry) return Positive;

   --  Get the offset in the line LINE of LOC.
   function Location_File_Line_To_Offset
     (Loc : Location_Type; File : Source_File_Entry; Line : Positive)
     return Natural;

   --  Get logical column (with HT expanded) from LOC, FILE and LINE.
   function Location_File_Line_To_Col
     (Loc : Location_Type; File : Source_File_Entry; Line : Positive)
     return Natural;

   --  Convert LOCATION into a source file FILE and an offset POS in the
   --  file.
   procedure Location_To_File_Pos (Location : Location_Type;
                                   File : out Source_File_Entry;
                                   Pos : out Source_Ptr);

   --  Convert a FILE and an offset POS in the file into a location.
   function File_Pos_To_Location (File : Source_File_Entry; Pos : Source_Ptr)
                                 return Location_Type;

   --  Convert a FILE into a location.
   function File_To_Location (File : Source_File_Entry) return Location_Type;

   --  Convert a FILE+LINE into a position.
   --  Return Source_Ptr_Bad in case of error (LINE out of bounds).
   function File_Line_To_Position (File : Source_File_Entry; Line : Positive)
                                  return Source_Ptr;

   --  Translate LOCATION into coordinate (physical position).
   --  FILE identifies the filename.
   --  LINE_POS is the offset in the file of the first character of the line,
   --  LINE is the line number (first line is 1),
   --  OFFSET is the offset of the location in the line (first character is 0,
   --     a tabulation is one character),
   procedure Location_To_Coord (Location : Location_Type;
                                File : out Source_File_Entry;
                                Line_Pos : out Source_Ptr;
                                Line : out Positive;
                                Offset : out Natural);

   --  Convert FILE and POS to coordinate.
   procedure File_Pos_To_Coord (File : Source_File_Entry;
                                Pos : Source_Ptr;
                                Line_Pos : out Source_Ptr;
                                Line : out Positive;
                                Offset : out Natural);

   --  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 the
   --  tab stop.
   --  The first character is at position 1 and at offset 0.
   function Coord_To_Col (File : Source_File_Entry;
                          Line_Pos : Source_Ptr;
                          Offset : Natural) return Natural;

   --  Translate coordinate into logical position.
   --  NAME is the name of the file,
   --  COL is the column (first character is 1, tabulation are at every 8
   --    positions).
   procedure Coord_To_Position (File : Source_File_Entry;
                                Line_Pos : Source_Ptr;
                                Offset : Natural;
                                Name : out Name_Id;
                                Col : out Natural);

   --  Translate LOCATION to NAME, LINE and COL.
   --  It is like to two procedures above.
   procedure Location_To_Position (Location : Location_Type;
                                   Name : out Name_Id;
                                   Line : out Positive;
                                   Col : out Natural);

   --  Return the line LINE from FILE (without end of line).  The line is
   --  expanded: tabs are replaced by spaces according to Tab_Stop.  This
   --  function is slow.
   function Extract_Expanded_Line (File : Source_File_Entry;
                                   Line : Positive) return String;

   --  Return the image of LOC using the "FILENAME:LINE:COL" format or
   --  "LINE:COL" format if FILENAME is false;
   function Image (Loc : Location_Type; Filename : Boolean := True)
                  return String;

   --  Initialize.
   procedure Initialize;

   --  Free all memory.
   procedure Finalize;

   --  Debug procedures.

   --  Disp info about all source files
   procedure Debug_Source_Files;
   procedure Debug_Source_File (File : Source_File_Entry);

private
   Lines_Table_Init : Natural := 64;

   package Lines_Tables is new Dyn_Tables
     (Table_Component_Type => Source_Ptr,
      Table_Index_Type => Natural,
      Table_Low_Bound => 1);

   --  There are several kinds of source file.
   type Source_File_Kind is
     (
      --  A *real* source file, read from the filesystem.
      Source_File_File,

      --  A virtual source file, created from a string.
      Source_File_String,

      --  A duplicated source file (there is no copy however), created by
      --  an instantiation.
      Source_File_Instance
     );

   --  Data associed with a file.
   type Source_File_Record (Kind : Source_File_Kind := Source_File_File) 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;

      --  Position of the EOT character after the file.  Also the length of
      --  the file + 1, unless there is a gap.
      File_Length : Source_Ptr;

      Checksum : File_Checksum_Id;

      case Kind is
         when Source_File_File =>
            --  Line table

            Lines : Lines_Tables.Instance;

            --  Cache for line table.
            Cache_Line : Positive;
            Cache_Pos : Source_Ptr;

            --  Gap
            Gap_Start : Source_Ptr;
            Gap_Last : Source_Ptr;

         when Source_File_String =>
            --  There is only one line.
            null;

         when Source_File_Instance =>
            --  The instance was created from REF.
            Ref : Source_File_Entry;
            --  The ultimate non-instance is BASE.
            Base : Source_File_Entry;

            Instance_Loc : Location_Type;
      end case;
   end record;

   package Source_Files is new Tables
     (Table_Index_Type => Source_File_Entry,
      Table_Component_Type => Source_File_Record,
      Table_Low_Bound => No_Source_File_Entry + 1,
      Table_Initial => 16);

end Files_Map;
