------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--                             L I B . U T I L                              --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 1992-2014, Free Software Foundation, Inc.         --
--                                                                          --
-- 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 3,  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 COPYING3.  If not, go to --
-- http://www.gnu.org/licenses for a complete copy of the license.          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

with Hostparm;
with Osint.C;  use Osint.C;
with Stringt;  use Stringt;

package body Lib.Util is

   Max_Line   : constant Natural := 2 * Hostparm.Max_Name_Length + 64;
   Max_Buffer : constant Natural := 1000 * Max_Line;

   Info_Buffer : String (1 .. Max_Buffer);
   --  Info_Buffer used to prepare lines of library output

   Info_Buffer_Len : Natural := 0;
   --  Number of characters stored in Info_Buffer

   Info_Buffer_Col : Natural := 1;
   --  Column number of next character to be written.
   --  Can be different from Info_Buffer_Len + 1 because of tab characters
   --  written by Write_Info_Tab.

   procedure Write_Info_Hex_Byte (J : Natural);
   --  Place two hex digits representing the value J (which is in the range
   --  0-255) in Info_Buffer, incrementing Info_Buffer_Len by 2. The digits
   --  are output using lower case letters.

   ---------------------
   -- Write_Info_Char --
   ---------------------

   procedure Write_Info_Char (C : Character) is
   begin
      Info_Buffer_Len := Info_Buffer_Len + 1;
      Info_Buffer (Info_Buffer_Len) := C;
      Info_Buffer_Col := Info_Buffer_Col + 1;
   end Write_Info_Char;

   --------------------------
   -- Write_Info_Char_Code --
   --------------------------

   procedure Write_Info_Char_Code (Code : Char_Code) is
   begin
      --  00 .. 7F

      if Code <= 16#7F# then
         Write_Info_Char (Character'Val (Code));

      --  80 .. FF

      elsif Code <= 16#FF# then
         Write_Info_Char ('U');
         Write_Info_Hex_Byte (Natural (Code));

      --  0100 .. FFFF

      else
         Write_Info_Char ('W');
         Write_Info_Hex_Byte (Natural (Code / 256));
         Write_Info_Hex_Byte (Natural (Code mod 256));
      end if;
   end Write_Info_Char_Code;

   --------------------
   -- Write_Info_Col --
   --------------------

   function Write_Info_Col return Positive is
   begin
      return Info_Buffer_Col;
   end Write_Info_Col;

   --------------------
   -- Write_Info_EOL --
   --------------------

   procedure Write_Info_EOL is
   begin
      if Info_Buffer_Len + Max_Line + 1 > Max_Buffer then
         Write_Info_Terminate;

      else
         --  Delete any trailing blanks

         while Info_Buffer_Len > 0
           and then Info_Buffer (Info_Buffer_Len) = ' '
         loop
            Info_Buffer_Len := Info_Buffer_Len - 1;
         end loop;

         Info_Buffer_Len := Info_Buffer_Len + 1;
         Info_Buffer (Info_Buffer_Len) := ASCII.LF;
         Info_Buffer_Col := 1;
      end if;
   end Write_Info_EOL;

   -------------------------
   -- Write_Info_Hex_Byte --
   -------------------------

   procedure Write_Info_Hex_Byte (J : Natural) is
      Hexd : constant array (0 .. 15) of Character := "0123456789abcdef";
   begin
      Write_Info_Char (Hexd (J / 16));
      Write_Info_Char (Hexd (J mod 16));
   end Write_Info_Hex_Byte;

   -------------------------
   -- Write_Info_Initiate --
   -------------------------

   procedure Write_Info_Initiate (Key : Character) renames Write_Info_Char;

   --------------------
   -- Write_Info_Int --
   --------------------

   procedure Write_Info_Int (N : Int) is
   begin
      if N >= 0 then
         Write_Info_Nat (N);

      --  Negative numbers, use Write_Info_Uint to avoid problems with largest
      --  negative number.

      else
         Write_Info_Uint (UI_From_Int (N));
      end if;
   end Write_Info_Int;

   ---------------------
   -- Write_Info_Name --
   ---------------------

   procedure Write_Info_Name (Name : Name_Id) is
   begin
      Get_Name_String (Name);
      Info_Buffer (Info_Buffer_Len + 1 .. Info_Buffer_Len + Name_Len) :=
        Name_Buffer (1 .. Name_Len);
      Info_Buffer_Len := Info_Buffer_Len + Name_Len;
      Info_Buffer_Col := Info_Buffer_Col + Name_Len;
   end Write_Info_Name;

   procedure Write_Info_Name (Name : File_Name_Type) is
   begin
      Write_Info_Name (Name_Id (Name));
   end Write_Info_Name;

   procedure Write_Info_Name (Name : Unit_Name_Type) is
   begin
      Write_Info_Name (Name_Id (Name));
   end Write_Info_Name;

   -----------------------------------
   -- Write_Info_Name_May_Be_Quoted --
   -----------------------------------

   procedure Write_Info_Name_May_Be_Quoted (Name : File_Name_Type) is
      Quoted : Boolean := False;
      Cur    : Positive;

   begin
      Get_Name_String (Name);

      --  The file/path name is quoted only if it includes spaces

      for J in 1 .. Name_Len loop
         if Name_Buffer (J) = ' ' then
            Quoted := True;
            exit;
         end if;
      end loop;

      --  Deal with quoting string if needed

      if Quoted then
         Insert_Str_In_Name_Buffer ("""", 1);
         Add_Char_To_Name_Buffer ('"');

         --  Any character '"' is doubled

         Cur := 2;
         while Cur < Name_Len loop
            if Name_Buffer (Cur) = '"' then
               Insert_Str_In_Name_Buffer ("""", Cur);
               Cur := Cur + 2;
            else
               Cur := Cur + 1;
            end if;
         end loop;
      end if;

      Info_Buffer (Info_Buffer_Len + 1 .. Info_Buffer_Len + Name_Len) :=
        Name_Buffer (1 .. Name_Len);
      Info_Buffer_Len := Info_Buffer_Len + Name_Len;
      Info_Buffer_Col := Info_Buffer_Col + Name_Len;
   end Write_Info_Name_May_Be_Quoted;

   --------------------
   -- Write_Info_Nat --
   --------------------

   procedure Write_Info_Nat (N : Nat) is
   begin
      if N > 9 then
         Write_Info_Nat (N / 10);
      end if;

      Write_Info_Char (Character'Val (N mod 10 + Character'Pos ('0')));
   end Write_Info_Nat;

   ---------------------
   -- Write_Info_Slit --
   ---------------------

   procedure Write_Info_Slit (S : String_Id) is
      C : Character;

   begin
      Write_Info_Str ("""");

      for J in 1 .. String_Length (S) loop
         C := Get_Character (Get_String_Char (S, J));

         if C in Character'Val (16#20#) .. Character'Val (16#7E#)
           and then C /= '{'
         then
            Write_Info_Char (C);

            if C = '"' then
               Write_Info_Char (C);
            end if;

         else
            Write_Info_Char ('{');
            Write_Info_Hex_Byte (Character'Pos (C));
            Write_Info_Char ('}');
         end if;
      end loop;

      Write_Info_Char ('"');
   end Write_Info_Slit;

   --------------------
   -- Write_Info_Str --
   --------------------

   procedure Write_Info_Str (Val : String) is
   begin
      Info_Buffer (Info_Buffer_Len + 1 .. Info_Buffer_Len + Val'Length)
                                                                  := Val;
      Info_Buffer_Len := Info_Buffer_Len + Val'Length;
      Info_Buffer_Col := Info_Buffer_Col + Val'Length;
   end Write_Info_Str;

   --------------------
   -- Write_Info_Tab --
   --------------------

   procedure Write_Info_Tab (Col : Positive) is
      Next_Tab : Positive;

   begin
      if Col <= Info_Buffer_Col then
         Write_Info_Str ("  ");
      else
         loop
            Next_Tab := 8 * ((Info_Buffer_Col - 1) / 8) + 8 + 1;
            exit when Col < Next_Tab;
            Write_Info_Char (ASCII.HT);
            Info_Buffer_Col := Next_Tab;
         end loop;

         while Info_Buffer_Col < Col loop
            Write_Info_Char (' ');
         end loop;
      end if;
   end Write_Info_Tab;

   --------------------------
   -- Write_Info_Terminate --
   --------------------------

   procedure Write_Info_Terminate is
   begin
      --  Delete any trailing blanks

      while Info_Buffer_Len > 0
        and then Info_Buffer (Info_Buffer_Len) = ' '
      loop
         Info_Buffer_Len := Info_Buffer_Len - 1;
      end loop;

      --  Write_Library_Info adds the EOL

      Write_Library_Info (Info_Buffer (1 .. Info_Buffer_Len));

      Info_Buffer_Len := 0;
      Info_Buffer_Col := 1;
   end Write_Info_Terminate;

   ---------------------
   -- Write_Info_Uint --
   ---------------------

   procedure Write_Info_Uint (N : Uint) is
   begin
      UI_Image (N, Decimal);
      Write_Info_Str (UI_Image_Buffer (1 .. UI_Image_Length));
   end Write_Info_Uint;

end Lib.Util;
