with Ada.Unchecked_Conversion;
with Ada.Real_Time; use Ada.Real_Time;
with Interfaces;    use Interfaces;

with HAL.I2C;       use HAL.I2C;

package body AK8963 is

   AK9863_ADDRESS_00 : constant := 16#18#;
   AK9863_ADDRESS_01 : constant := 16#1A#;
   AK9863_ADDRESS_10 : constant := 16#1C#;
   AK9863_ADDRESS_11 : constant := 16#1E#;

   AK8963_RA_WIA     : constant := 16#00#;
--     AK8963_RA_INFO    : constant := 16#01#;
   AK8963_RA_ST1     : constant := 16#02#;
   AK8963_RA_HXL     : constant := 16#03#;
--     AK8963_RA_HXH     : constant := 16#04#;
--     AK8963_RA_HYL     : constant := 16#05#;
--     AK8963_RA_HYH     : constant := 16#06#;
--     AK8963_RA_HZL     : constant := 16#07#;
--     AK8963_RA_HZH     : constant := 16#08#;
   AK8963_RA_ST2     : constant := 16#09#;
   AK8963_RA_CNTL    : constant := 16#0A#;
--     AK8963_RA_RSV     : constant := 16#0B#;
   AK8963_RA_ASTC    : constant := 16#0C#;
--     AK8963_RA_I2CDIS  : constant := 16#0F#;
   AK8963_RA_ASAX    : constant := 16#10#;
--     AK8963_RA_ASAY    : constant := 16#11#;
--     AK8963_RA_ASAZ    : constant := 16#12#;

   --  In 16-bit output, decimal value 32760 is 4912 micro Tesla
   --  so 49.12 Gauss
   MAG_TO_GAUSS : constant := 49.12 / 32_760.0;

   AK8963_ST_X_MIN   : constant := -200.0 * MAG_TO_GAUSS;
   AK8963_ST_X_MAX   : constant := 200 * MAG_TO_GAUSS;
   AK8963_ST_Y_MIN   : constant := -200 * MAG_TO_GAUSS;
   AK8963_ST_Y_MAX   : constant := 200 * MAG_TO_GAUSS;
   AK8963_ST_Z_MIN   : constant := -3200 * MAG_TO_GAUSS;
   AK8963_ST_Z_MAX   : constant := -800 * MAG_TO_GAUSS;

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short) return Byte;

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural) return Boolean;

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Data   : Byte);

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural;
      State  : Boolean);

   --------------
   -- I2C_Read --
   --------------

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short) return Byte
   is
      Data   : I2C_Data (1 .. 1);
      Status : I2C_Status;
   begin
      Device.I2C_Port.Mem_Read
        (Device.Address,
         Reg,
         Memory_Size_8b,
         Data,
         Status);
      return Data (1);
   end I2C_Read;

   --------------
   -- I2C_Read --
   --------------

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural) return Boolean
   is
      Mask : constant Byte := 2 ** Bit;
      Data : constant Byte := I2C_Read (Device, Reg);
   begin
      return (Data and Mask) /= 0;
   end I2C_Read;

   ---------------
   -- I2C_Write --
   ---------------

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Data   : Byte)
   is
      Status : I2C_Status with Unreferenced;
   begin
      Device.I2C_Port.Mem_Write
        (Device.Address,
         Reg,
         Memory_Size_8b,
         (1 => Data),
         Status);
   end I2C_Write;

   ---------------
   -- I2C_Write --
   ---------------

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural;
      State  : Boolean)
   is
      Val  : Byte := I2C_Read (Device, Reg);
      Mask : constant Byte := 2 ** Bit;
   begin
      if State then
         Val := Val or Mask;
      else
         Val := Val and not Mask;
      end if;

      I2C_Write (Device, Reg, Val);
   end I2C_Write;

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

   procedure Initialize (Device : in out AK8963_Device)
   is
   begin
      if Device.Is_Init then
         return;
      end if;

      Device.Address :=
        (case Device.Address_Selector is
            when Add_00 => AK9863_ADDRESS_00,
            when Add_01 => AK9863_ADDRESS_01,
            when Add_10 => AK9863_ADDRESS_10,
            when Add_11 => AK9863_ADDRESS_11);
      Device.Is_Init := True;
   end Initialize;

   ---------------------
   -- Test_Connection --
   ---------------------

   function Test_Connection (Device : AK8963_Device) return Boolean
   is
      Data   : HAL.I2C.I2C_Data (1 .. 1) := (others => 0);
      Status : HAL.I2C.I2C_Status;
   begin
      Device.I2C_Port.Mem_Read
        (Device.Address,
         AK8963_RA_WIA,
         HAL.I2C.Memory_Size_8b,
         Data,
         Status);

      return Status = Ok and then Data (1) = 16#48#;
   end Test_Connection;

   ---------------
   -- Self_Test --
   ---------------

   function Self_Test (Device : in out AK8963_Device) return Boolean
   is
      Mx, My, Mz : Gauss;
      Conf_Save  : Byte;
      Retry      : Natural := 20;
      Ret        : Boolean;
      Dead       : Boolean with Unreferenced;

   begin
      if not Test_Connection (Device) then
         return False;
      end if;

      Ret := True;

      --  Save the current configuration
      Conf_Save := I2C_Read (Device, AK8963_RA_CNTL);

      --  Power_Down
      Set_Mode (Device, Power_Down);
      I2C_Write (Device, AK8963_RA_ASTC, 6, True); --  Set SELF bit to True
      Dead := Get_Overflow_Status (Device); --  Clears ST1 by reading ST2
      Set_Mode (Device, Self_Test, Mode_16bit);

      while Retry > 0 loop
         exit when Get_Data_Ready (Device);
         Retry := Retry - 1;
         delay until Clock + Milliseconds (10);
      end loop;

      if Retry = 0 then
         Ret := False;
      end if;

      Get_Heading (Device, Mx, My, Mz);
      Dead := Get_Overflow_Status (Device); --  Clears ST1 by reading ST2
      I2C_Write (Device, AK8963_RA_ASTC, 6, False); --  Set SELF bit to False
      Set_Mode (Device, Power_Down);

      if Mx < AK8963_ST_X_MIN
        or else Mx > AK8963_ST_X_MAX
        or else My < AK8963_ST_Y_MIN
        or else My > AK8963_ST_Y_MAX
        or else Mz < AK8963_ST_Z_MIN
        or else Mz > AK8963_ST_Z_MAX
      then
         Ret := False;
      end if;

      --  Set back the saved config
      I2C_Write (Device, AK8963_RA_CNTL, Conf_Save);

      return Ret;
   end Self_Test;

   --------------
   -- Set_Mode --
   --------------

   procedure Set_Mode
     (Device         : in out AK8963_Device;
      Operation_Mode : AK8963_Operation_Mode;
      Sampling_Mode  : AK8963_Sampling_Mode)
   is
      Mode   : constant Byte :=
                 AK8963_Operation_Mode'Enum_Rep (Operation_Mode)
                   or AK8963_Sampling_Mode'Enum_Rep (Sampling_Mode);
   begin
      I2C_Write (Device, AK8963_RA_CNTL, Mode);
   end Set_Mode;

   --------------
   -- Set_Mode --
   --------------

   procedure Set_Mode
     (Device         : in out AK8963_Device;
      Operation_Mode : AK8963_Operation_Mode)
   is
      Mode   : constant Byte :=
                 AK8963_Operation_Mode'Enum_Rep (Operation_Mode);
   begin
      I2C_Write (Device, AK8963_RA_CNTL, Mode);
   end Set_Mode;

   --------------------
   -- Get_Data_Ready --
   --------------------

   function Get_Data_Ready (Device : AK8963_Device) return Boolean
   is
      AK8963_ST1_DRDY_BIT : constant := 0;
   begin
      return I2C_Read (Device, AK8963_RA_ST1, AK8963_ST1_DRDY_BIT);
   end Get_Data_Ready;

   -----------------
   -- Get_Heading --
   -----------------

   procedure Get_Heading
     (Device     : AK8963_Device;
      Mx, My, Mz : out Float)
   is
      Buffer : I2C_Data (1 .. 6);
      Status : I2C_Status;
      function To_Signed is new
        Ada.Unchecked_Conversion (Unsigned_16, Integer_16);

   begin
      Mem_Read
        (Device.I2C_Port.all,
         Addr          => Device.Address,
         Mem_Addr      => AK8963_RA_HXL,
         Mem_Addr_Size => Memory_Size_8b,
         Data          => Buffer,
         Status        => Status);

      if Status /= Ok then
         Mx := 0.0;
         My := 0.0;
         Mz := 0.0;
      else
         Mx :=
           Float (To_Signed (Shift_Left (Unsigned_16 (Buffer (2)), 8)
                  or Unsigned_16 (Buffer (1)))) * MAG_TO_GAUSS;
         My :=
           Float (To_Signed (Shift_Left (Unsigned_16 (Buffer (4)), 8)
                  or Unsigned_16 (Buffer (3)))) * MAG_TO_GAUSS;
         Mz :=
           Float (To_Signed (Shift_Left (Unsigned_16 (Buffer (6)), 8)
                  or Unsigned_16 (Buffer (5)))) * MAG_TO_GAUSS;

         --  Read the sensitivity adjustment data
         Mem_Read
           (Device.I2C_Port.all,
            Addr          => Device.Address,
            Mem_Addr      => AK8963_RA_ASAX,
            Mem_Addr_Size => Memory_Size_8b,
            Data          => Buffer (1 .. 3),
            Status        => Status);

         if Status = Ok then
            Mx := Mx * ((Float (Buffer (1)) - 128.0) / 256.0 + 1.0);
            My := My * ((Float (Buffer (1)) - 128.0) / 256.0 + 1.0);
            Mz := Mz * ((Float (Buffer (1)) - 128.0) / 256.0 + 1.0);
         end if;
      end if;
   end Get_Heading;

   -------------------------
   -- Get_Overflow_Status --
   -------------------------

   function Get_Overflow_Status (Device : AK8963_Device) return Boolean
   is
      AK8963_ST2_HOFL_BIT : constant := 3;
   begin
      return I2C_Read (Device, AK8963_RA_ST2, AK8963_ST2_HOFL_BIT);
   end Get_Overflow_Status;

end AK8963;

with Ada.Unchecked_Conversion;
with Ada.Real_Time; use Ada.Real_Time;
with Interfaces;    use Interfaces;

with HAL.I2C;       use HAL.I2C;

package body AK8963 is

   AK9863_ADDRESS_00 : constant := 16#18#;
   AK9863_ADDRESS_01 : constant := 16#1A#;
   AK9863_ADDRESS_10 : constant := 16#1C#;
   AK9863_ADDRESS_11 : constant := 16#1E#;

   AK8963_RA_WIA     : constant := 16#00#;
--     AK8963_RA_INFO    : constant := 16#01#;
   AK8963_RA_ST1     : constant := 16#02#;
   AK8963_RA_HXL     : constant := 16#03#;
--     AK8963_RA_HXH     : constant := 16#04#;
--     AK8963_RA_HYL     : constant := 16#05#;
--     AK8963_RA_HYH     : constant := 16#06#;
--     AK8963_RA_HZL     : constant := 16#07#;
--     AK8963_RA_HZH     : constant := 16#08#;
   AK8963_RA_ST2     : constant := 16#09#;
   AK8963_RA_CNTL    : constant := 16#0A#;
--     AK8963_RA_RSV     : constant := 16#0B#;
   AK8963_RA_ASTC    : constant := 16#0C#;
--     AK8963_RA_I2CDIS  : constant := 16#0F#;
   AK8963_RA_ASAX    : constant := 16#10#;
--     AK8963_RA_ASAY    : constant := 16#11#;
--     AK8963_RA_ASAZ    : constant := 16#12#;

   --  In 16-bit output, decimal value 32760 is 4912 micro Tesla
   --  so 49.12 Gauss
   MAG_TO_GAUSS : constant := 49.12 / 32_760.0;

   AK8963_ST_X_MIN   : constant := -200.0 * MAG_TO_GAUSS;
   AK8963_ST_X_MAX   : constant := 200 * MAG_TO_GAUSS;
   AK8963_ST_Y_MIN   : constant := -200 * MAG_TO_GAUSS;
   AK8963_ST_Y_MAX   : constant := 200 * MAG_TO_GAUSS;
   AK8963_ST_Z_MIN   : constant := -3200 * MAG_TO_GAUSS;
   AK8963_ST_Z_MAX   : constant := -800 * MAG_TO_GAUSS;

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short) return Byte;

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural) return Boolean;

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Data   : Byte);

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural;
      State  : Boolean);

   --------------
   -- I2C_Read --
   --------------

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short) return Byte
   is
      Data   : I2C_Data (1 .. 1);
      Status : I2C_Status;
   begin
      Device.I2C_Port.Mem_Read
        (Device.Address,
         Reg,
         Memory_Size_8b,
         Data,
         Status);
      return Data (1);
   end I2C_Read;

   --------------
   -- I2C_Read --
   --------------

   function I2C_Read
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural) return Boolean
   is
      Mask : constant Byte := 2 ** Bit;
      Data : constant Byte := I2C_Read (Device, Reg);
   begin
      return (Data and Mask) /= 0;
   end I2C_Read;

   ---------------
   -- I2C_Write --
   ---------------

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Data   : Byte)
   is
      Status : I2C_Status with Unreferenced;
   begin
      Device.I2C_Port.Mem_Write
        (Device.Address,
         Reg,
         Memory_Size_8b,
         (1 => Data),
         Status);
   end I2C_Write;

   ---------------
   -- I2C_Write --
   ---------------

   procedure I2C_Write
     (Device : AK8963_Device;
      Reg    : Short;
      Bit    : Natural;
      State  : Boolean)
   is
      Val  : Byte := I2C_Read (Device, Reg);
      Mask : constant Byte := 2 ** Bit;
   begin
      if State then
         Val := Val or Mask;
      else
         Val := Val and not Mask;
      end if;

      I2C_Write (Device, Reg, Val);
   end I2C_Write;

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

   procedure Initialize (Device : in out AK8963_Device)
   is
   begin
      if Device.Is_Init then
         return;
      end if;

      Device.Address :=
        (case Device.Address_Selector is
            when Add_00 => AK9863_ADDRESS_00,
            when Add_01 => AK9863_ADDRESS_01,
            when Add_10 => AK9863_ADDRESS_10,
            when Add_11 => AK9863_ADDRESS_11);
      Device.Is_Init := True;
   end Initialize;

   ---------------------
   -- Test_Connection --
   ---------------------

   function Test_Connection (Device : AK8963_Device) return Boolean
   is
      Data   : HAL.I2C.I2C_Data (1 .. 1) := (others => 0);
      Status : HAL.I2C.I2C_Status;
   begin
      Device.I2C_Port.Mem_Read
        (Device.Address,
         AK8963_RA_WIA,
         HAL.I2C.Memory_Size_8b,
         Data,
         Status);

      return Status = Ok and then Data (1) = 16#48#;
   end Test_Connection;

   ---------------
   -- Self_Test --
   ---------------

   function Self_Test (Device : in out AK8963_Device) return Boolean
   is
      Mx, My, Mz : Gauss;
      Conf_Save  : Byte;
      Retry      : Natural := 20;
      Ret        : Boolean;
      Dead       : Boolean with Unreferenced;

   begin
      if not Test_Connection (Device) then
         return False;
      end if;

      Ret := True;

      --  Save the current configuration
      Conf_Save := I2C_Read (Device, AK8963_RA_CNTL);

      --  Power_Down
      Set_Mode (Device, Power_Down);
      I2C_Write (Device, AK8963_RA_ASTC, 6, True); --  Set SELF bit to True
      Dead := Get_Overflow_Status (Device); --  Clears ST1 by reading ST2
      Set_Mode (Device, Self_Test, Mode_16bit);

      while Retry > 0 loop
         exit when Get_Data_Ready (Device);
         Retry := Retry - 1;
         delay until Clock + Milliseconds (10);
      end loop;

      if Retry = 0 then
         Ret := False;
      end if;

      Get_Heading (Device, Mx, My, Mz);
      Dead := Get_Overflow_Status (Device); --  Clears ST1 by reading ST2
      I2C_Write (Device, AK8963_RA_ASTC, 6, False); --  Set SELF bit to False
      Set_Mode (Device, Power_Down);

      if Mx < AK8963_ST_X_MIN
        or else Mx > AK8963_ST_X_MAX
        or else My < AK8963_ST_Y_MIN
        or else My > AK8963_ST_Y_MAX
        or else Mz < AK8963_ST_Z_MIN
        or else Mz > AK8963_ST_Z_MAX
      then
         Ret := False;
      end if;

      --  Set back the saved config
      I2C_Write (Device, AK8963_RA_CNTL, Conf_Save);

      return Ret;
   end Self_Test;

   --------------
   -- Set_Mode --
   --------------

   procedure Set_Mode
     (Device         : in out AK8963_Device;
      Operation_Mode : AK8963_Operation_Mode;
      Sampling_Mode  : AK8963_Sampling_Mode)
   is
      Mode   : constant Byte :=
                 AK8963_Operation_Mode'Enum_Rep (Operation_Mode)
                   or AK8963_Sampling_Mode'Enum_Rep (Sampling_Mode);
   begin
      I2C_Write (Device, AK8963_RA_CNTL, Mode);
   end Set_Mode;

   --------------
   -- Set_Mode --
   --------------

   procedure Set_Mode
     (Device         : in out AK8963_Device;
      Operation_Mode : AK8963_Operation_Mode)
   is
      Mode   : constant Byte :=
                 AK8963_Operation_Mode'Enum_Rep (Operation_Mode);
   begin
      I2C_Write (Device, AK8963_RA_CNTL, Mode);
   end Set_Mode;

   --------------------
   -- Get_Data_Ready --
   --------------------

   function Get_Data_Ready (Device : AK8963_Device) return Boolean
   is
      AK8963_ST1_DRDY_BIT : constant := 0;
   begin
      return I2C_Read (Device, AK8963_RA_ST1, AK8963_ST1_DRDY_BIT);
   end Get_Data_Ready;

   -----------------
   -- Get_Heading --
   -----------------

   procedure Get_Heading
     (Device     : AK8963_Device;
      Mx, My, Mz : out Float)
   is
      Buffer : I2C_Data (1 .. 6);
      Status : I2C_Status;
      function To_Signed is new
        Ada.Unchecked_Conversion (Unsigned_16, Integer_16);

   begin
      Mem_Read
        (Device.I2C_Port.all,
         Addr          => Device.Address,
         Mem_Addr      => AK8963_RA_HXL,
         Mem_Addr_Size => Memory_Size_8b,
         Data          => Buffer,
         Status        => Status);

      if Status /= Ok then
         Mx := 0.0;
         My := 0.0;
         Mz := 0.0;
      else
         Mx :=
           Float (To_Signed (Shift_Left (Unsigned_16 (Buffer (2)), 8)
                  or Unsigned_16 (Buffer (1)))) * MAG_TO_GAUSS;
         My :=
           Float (To_Signed (Shift_Left (Unsigned_16 (Buffer (4)), 8)
                  or Unsigned_16 (Buffer (3)))) * MAG_TO_GAUSS;
         Mz :=
           Float (To_Signed (Shift_Left (Unsigned_16 (Buffer (6)), 8)
                  or Unsigned_16 (Buffer (5)))) * MAG_TO_GAUSS;

         --  Read the sensitivity adjustment data
         Mem_Read
           (Device.I2C_Port.all,
            Addr          => Device.Address,
            Mem_Addr      => AK8963_RA_ASAX,
            Mem_Addr_Size => Memory_Size_8b,
            Data          => Buffer (1 .. 3),
            Status        => Status);

         if Status = Ok then
            Mx := Mx * ((Float (Buffer (1)) - 128.0) / 256.0 + 1.0);
            My := My * ((Float (Buffer (2)) - 128.0) / 256.0 + 1.0);
            Mz := Mz * ((Float (Buffer (3)) - 128.0) / 256.0 + 1.0);
         end if;
      end if;
   end Get_Heading;

   -------------------------
   -- Get_Overflow_Status --
   -------------------------

   function Get_Overflow_Status (Device : AK8963_Device) return Boolean
   is
      AK8963_ST2_HOFL_BIT : constant := 3;
   begin
      return I2C_Read (Device, AK8963_RA_ST2, AK8963_ST2_HOFL_BIT);
   end Get_Overflow_Status;

end AK8963;

Fix the read operation for the AK8963 magnitometer.

As noted by issue #59


-----------------------------------------------------------------------
--  gperfhash -- Perfect hash Ada generator
--  Copyright (C) 2011, 2012, 2013 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Text_IO;
with Ada.Strings.Unbounded;
with Ada.Strings.Fixed;
with Ada.IO_Exceptions;
with Ada.Command_Line;

with GNAT.Command_Line;
with GNAT.Perfect_Hash_Generators;

with Util.Log.Loggers;
with Util.Files;
with Util.Strings.Vectors;
with Util.Strings.Transforms;

--  This simple utility is an Ada perfect hash generator.  Given a fixed set of keywords,
--  it generates an Ada package (spec and body) which provides a perfect hash function.
--  The perfect hash algorithm is in fact provided by GNAT Perfect_Hash_Generators package.
--
--  Usage: gperfhash [-i] [-p package] keyword-file
--
--  -i           Generate a perfect hash which ignores the case
--  -p package   Use package as the name of package (default is gphash)
--  keyword-file The file which contains the keywords, one keyword on each line
procedure Gperfhash is

   use Util.Log.Loggers;
   use Ada.Strings.Unbounded;
   use GNAT.Command_Line;
   use Ada.Text_IO;

   --  Read a keyword and add it in the keyword list.
   procedure Read_Keyword (Line : in String);

   --  Given a package name, return the file name that correspond.
   function To_File_Name (Name : in String) return String;

   procedure Generate_Keyword_Table (Into : in out Ada.Text_IO.File_Type);

   --  Generate the package specification.
   procedure Generate_Specs (Name : in String);

   --  Generate the package body.
   procedure Generate_Body (Name : in String);

   Log         : constant Logger := Create ("log", "samples/log4j.properties");

   Pkg_Name    : Unbounded_String := To_Unbounded_String ("gphash");
   Names       : Util.Strings.Vectors.Vector;

   --  When true, generate a perfect hash which ignores the case.
   Ignore_Case : Boolean := False;

   --  ------------------------------
   --  Generate the keyword table.
   --  ------------------------------
   procedure Generate_Keyword_Table (Into : in out Ada.Text_IO.File_Type) is

      Index : Integer := 0;

      procedure Print_Keyword (Pos : in Util.Strings.Vectors.Cursor);
      procedure Print_Table (Pos : in Util.Strings.Vectors.Cursor);

      --  ------------------------------
      --  Print a keyword as an Ada constant string.
      --  ------------------------------
      procedure Print_Keyword (Pos : in Util.Strings.Vectors.Cursor) is
         Name : constant String := Util.Strings.Vectors.Element (Pos);
      begin
         Put (Into, "   K_");
         Put (Into, Util.Strings.Image (Index));
         Set_Col (Into, 20);
         Put (Into, ": aliased constant String := """);
         Put (Into, Name);
         Put_Line (Into, """;");
         Index := Index + 1;
      end Print_Keyword;

      --  ------------------------------
      --  Build the keyword table.
      --  ------------------------------
      procedure Print_Table (Pos : in Util.Strings.Vectors.Cursor) is
         pragma Unreferenced (Pos);
      begin
         if Index > 0 then
            if Index mod 4 = 0 then
               Put_Line (Into, ",");
               Put (Into, "      ");
            else
               Put (Into, ", ");
            end if;
         end if;
         Put (Into, "K_");
         Put (Into, Util.Strings.Image (Index));
         Put (Into, "'Access");
         Index := Index + 1;
      end Print_Table;

   begin
      New_Line (Into);
      Put_Line (Into, "   type Name_Access is access constant String;");
      Put_Line (Into, "   type Keyword_Array is array (Natural range <>) of Name_Access;");
      Put_Line (Into, "   Keywords : constant Keyword_Array;");
      Put_Line (Into, "private");
      New_Line (Into);
      Names.Iterate (Print_Keyword'Access);
      New_Line (Into);

      Index := 0;
      Put_Line (Into, "   Keywords : constant Keyword_Array := (");
      Put (Into, "      ");
      Names.Iterate (Print_Table'Access);
      Put_Line (Into, ");");
   end Generate_Keyword_Table;

   --  ------------------------------
   --  Generate the package specification.
   --  ------------------------------
   procedure Generate_Specs (Name : in String) is
      File : Ada.Text_IO.File_Type;
      Path : constant String := To_File_Name (Name) & ".ads";
   begin
      Ada.Text_IO.Open (File => File,
                        Mode => Ada.Text_IO.Out_File,
                        Name => Path);
      Put_Line (File, "--  Generated by gperfhash");
      Put (File, "package ");
      Put (File, To_String (Pkg_Name));
      Put_Line (File, " is");
      New_Line (File);
      Put_Line (File, "   pragma Preelaborate;");
      New_Line (File);
      Put_Line (File, "   function Hash (S : String) return Natural;");
      New_Line (File);
      Put_Line (File, "   --  Returns true if the string S is a keyword.");
      Put_Line (File, "   function Is_Keyword (S : in String) return Boolean;");
      Generate_Keyword_Table (File);
      Put      (File, "end ");
      Put      (File, To_String (Pkg_Name));
      Put      (File, ";");
      New_Line (File);
      Close    (File);
   end Generate_Specs;

   --  ------------------------------
   --  Generate the package body.
   --  ------------------------------
   procedure Generate_Body (Name : in String) is

      --  Read the generated body file.
      procedure Read_Body (Line : in String);

      --  Re-generate the char position table to ignore the case.
      procedure Generate_Char_Position;

      Path  : constant String := To_File_Name (Name) & ".adb";
      File  : Ada.Text_IO.File_Type;
      Count : Natural;
      Lines : Util.Strings.Vectors.Vector;

      --  ------------------------------
      --  Read the generated body file.
      --  ------------------------------
      procedure Read_Body (Line : in String) is
      begin
         Lines.Append (Line);
      end Read_Body;

      --  ------------------------------
      --  Re-generate the char position table to ignore the case.
      --  ------------------------------
      procedure Generate_Char_Position is
         use GNAT.Perfect_Hash_Generators;

         V : Natural;
      begin
         Put (File, "     (");
         for I in 0 .. 255 loop
            if I >= Character'Pos ('a') and I <= Character'Pos ('z') then
               V := Value (Used_Character_Set, I - Character'Pos ('a') + Character'Pos ('A'));
            else
               V := GNAT.Perfect_Hash_Generators.Value (Used_Character_Set, I);
            end if;
            if I > 0 then
               if I mod 16 = 0 then
                  Put_Line (File, ",");
                  Put (File, "      ");
               else
                  Put (File, ", ");
               end if;
            end if;
            Put (File, Util.Strings.Image (V));
         end loop;
         Put_Line (File, ");");
      end Generate_Char_Position;

   begin
      Util.Files.Read_File (Path => Path, Process => Read_Body'Access);
      Count := Natural (Lines.Length);
      Ada.Text_IO.Open (File => File,
                        Mode => Ada.Text_IO.Out_File,
                        Name => Path);
      Put_Line (File, "--  Generated by gperfhash");
      if Ignore_Case then
         Put_Line (File, "with Util.Strings.Transforms;");
      end if;
      for I in 1 .. Count loop
         declare
            L : constant String := Lines.Element (I);
         begin
            --  Replace the char position table by ours.  The lower case letter are just
            --  mapped to the corresponding upper case letter.
            if Ignore_Case and I >= 6 and I <= 16 then
               if I = 6 then
                  Generate_Char_Position;
               end if;
            else
               Put_Line (File, L);
            end if;

            --  Generate the Is_Keyword function before the package end.
            if I = Count - 1 then
               Put_Line (File, "   --  Returns true if the string S is a keyword.");
               Put_Line (File, "   function Is_Keyword (S : in String) return Boolean is");
               Put_Line (File, "      H : constant Natural := Hash (S);");
               Put_Line (File, "   begin");
               if Ignore_Case then
                  Put_Line (File, "      return Keywords (H).all = "
                            & "Util.Strings.Transforms.To_Upper_Case (S);");
               else
                  Put_Line (File, "      return Keywords (H).all = S;");
               end if;
               Put_Line (File, "   end Is_Keyword;");
            end if;
         end;
      end loop;
      Close (File);
   end Generate_Body;

   --  ------------------------------
   --  Read a keyword and add it in the keyword list.
   --  ------------------------------
   procedure Read_Keyword (Line : in String) is
      use Ada.Strings;
      Word : String := Fixed.Trim (Line, Both);
   begin
      if Word'Length > 0 then
         if Ignore_Case then
            Word := Util.Strings.Transforms.To_Upper_Case (Word);
         end if;
         Names.Append (Word);
         GNAT.Perfect_Hash_Generators.Insert (Word);
      end if;
   end Read_Keyword;

   --  ------------------------------
   --  Given a package name, return the file name that correspond.
   --  ------------------------------
   function To_File_Name (Name : in String) return String is
      Result : String (Name'Range);
   begin
      for J in Name'Range loop
         if Name (J) in 'A' .. 'Z' then
            Result (J) := Character'Val (Character'Pos (Name (J))
              - Character'Pos ('A')
              + Character'Pos ('a'));

         elsif Name (J) = '.' then
            Result (J) := '-';

         else
            Result (J) := Name (J);
         end if;
      end loop;
      return Result;
   end To_File_Name;

begin
   --  Initialization is optional.  Get the log configuration by reading the property
   --  file 'samples/log4j.properties'.  The 'log.util' logger will use a DEBUG level
   --  and write the message in 'result.log'.
   Util.Log.Loggers.Initialize ("samples/log4j.properties");

   loop
      case Getopt ("h i p: package: help") is
         when ASCII.NUL =>
            exit;

         when 'i' =>
            Ignore_Case := True;

         when 'p' =>
            Pkg_Name := To_Unbounded_String (Parameter);

         when others =>
            raise GNAT.Command_Line.Invalid_Switch;
      end case;
   end loop;
   declare
      Keywords : constant String := Get_Argument;
      Pkg      : constant String := To_String (Pkg_Name);
      Count    : Natural := 0;
      K_2_V    : Float;
      V        : Natural;
      Seed     : constant Natural := 4321; --  Needed by the hash algorithm
   begin
      --  Read the keywords.
      Util.Files.Read_File (Path => Keywords, Process => Read_Keyword'Access);

      Count := Natural (Names.Length);
      if Count = 0 then
         Log.Error ("There is no keyword.");
         raise GNAT.Command_Line.Invalid_Switch;
      end if;

      --  Generate the perfect hash package.
      V := 2 * Count + 1;
      loop
         K_2_V := Float (V) / Float (Count);
         GNAT.Perfect_Hash_Generators.Initialize (Seed, K_2_V);
         begin
            GNAT.Perfect_Hash_Generators.Compute;
            exit;
         exception
            when GNAT.Perfect_Hash_Generators.Too_Many_Tries =>
               V := V + 1;
         end;
      end loop;
      GNAT.Perfect_Hash_Generators.Produce (Pkg);

      --  Override what GNAT generates to have a list of keywords and other operations.
      Generate_Specs (Pkg);
      Generate_Body (Pkg);

   exception
      when Ada.IO_Exceptions.Name_Error =>
         Log.Error ("Cannot read the keyword file.");
         Ada.Command_Line.Set_Exit_Status (1);
   end;

exception
   when GNAT.Command_Line.Invalid_Switch =>
      Log.Error ("Usage: gperfhash -i -p package keyword-file");
      Log.Error ("-i           Generate a perfect hash which ignores the case");
      Log.Error ("-p package   Use 'package' as the name of package (default is 'gphash')");
      Log.Error ("keyword-file The file which contains the keywords, one keyword on each line");
      Ada.Command_Line.Set_Exit_Status (1);
end Gperfhash;

-----------------------------------------------------------------------
--  gperfhash -- Perfect hash Ada generator
--  Copyright (C) 2011, 2012, 2013, 2018 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Text_IO;
with Ada.Strings.Unbounded;
with Ada.Strings.Fixed;
with Ada.IO_Exceptions;
with Ada.Command_Line;

with GNAT.Command_Line;
with GNAT.Perfect_Hash_Generators;

with Util.Log.Loggers;
with Util.Files;
with Util.Strings.Vectors;
with Util.Strings.Transforms;

--  This simple utility is an Ada perfect hash generator.  Given a fixed set of keywords,
--  it generates an Ada package (spec and body) which provides a perfect hash function.
--  The perfect hash algorithm is in fact provided by GNAT Perfect_Hash_Generators package.
--
--  Usage: gperfhash [-i] [-p package] keyword-file
--
--  -i           Generate a perfect hash which ignores the case
--  -p package   Use package as the name of package (default is gphash)
--  keyword-file The file which contains the keywords, one keyword on each line
procedure Gperfhash is

   use Util.Log.Loggers;
   use Ada.Strings.Unbounded;
   use GNAT.Command_Line;
   use Ada.Text_IO;

   --  Read a keyword and add it in the keyword list.
   procedure Read_Keyword (Line : in String);

   --  Given a package name, return the file name that correspond.
   function To_File_Name (Name : in String) return String;

   procedure Generate_Keyword_Table (Into : in out Ada.Text_IO.File_Type);

   --  Generate the package specification.
   procedure Generate_Specs (Name : in String);

   --  Generate the package body.
   procedure Generate_Body (Name : in String);

   Log         : constant Logger := Create ("log", "samples/log4j.properties");

   Pkg_Name    : Unbounded_String := To_Unbounded_String ("gphash");
   Names       : Util.Strings.Vectors.Vector;

   --  When true, generate a perfect hash which ignores the case.
   Ignore_Case : Boolean := False;

   --  ------------------------------
   --  Generate the keyword table.
   --  ------------------------------
   procedure Generate_Keyword_Table (Into : in out Ada.Text_IO.File_Type) is

      Index : Integer := 0;

      procedure Print_Keyword (Pos : in Util.Strings.Vectors.Cursor);
      procedure Print_Table (Pos : in Util.Strings.Vectors.Cursor);

      --  ------------------------------
      --  Print a keyword as an Ada constant string.
      --  ------------------------------
      procedure Print_Keyword (Pos : in Util.Strings.Vectors.Cursor) is
         Name : constant String := Util.Strings.Vectors.Element (Pos);
      begin
         Put (Into, "   K_");
         Put (Into, Util.Strings.Image (Index));
         Set_Col (Into, 20);
         Put (Into, ": aliased constant String := """);
         Put (Into, Name);
         Put_Line (Into, """;");
         Index := Index + 1;
      end Print_Keyword;

      --  ------------------------------
      --  Build the keyword table.
      --  ------------------------------
      procedure Print_Table (Pos : in Util.Strings.Vectors.Cursor) is
         pragma Unreferenced (Pos);
      begin
         if Index > 0 then
            if Index mod 4 = 0 then
               Put_Line (Into, ",");
               Put (Into, "      ");
            else
               Put (Into, ", ");
            end if;
         end if;
         Put (Into, "K_");
         Put (Into, Util.Strings.Image (Index));
         Put (Into, "'Access");
         Index := Index + 1;
      end Print_Table;

   begin
      New_Line (Into);
      Put_Line (Into, "   type Name_Access is access constant String;");
      Put_Line (Into, "   type Keyword_Array is array (Natural range <>) of Name_Access;");
      Put_Line (Into, "   Keywords : constant Keyword_Array;");
      Put_Line (Into, "private");
      New_Line (Into);
      Names.Iterate (Print_Keyword'Access);
      New_Line (Into);

      Index := 0;
      Put_Line (Into, "   Keywords : constant Keyword_Array := (");
      Put (Into, "      ");
      Names.Iterate (Print_Table'Access);
      Put_Line (Into, ");");
   end Generate_Keyword_Table;

   --  ------------------------------
   --  Generate the package specification.
   --  ------------------------------
   procedure Generate_Specs (Name : in String) is
      File : Ada.Text_IO.File_Type;
      Path : constant String := To_File_Name (Name) & ".ads";
   begin
      Ada.Text_IO.Open (File => File,
                        Mode => Ada.Text_IO.Out_File,
                        Name => Path);
      Put_Line (File, "--  Generated by gperfhash");
      Put (File, "package ");
      Put (File, To_String (Pkg_Name));
      Put_Line (File, " is");
      New_Line (File);
      Put_Line (File, "   pragma Preelaborate;");
      New_Line (File);
      Put_Line (File, "   function Hash (S : String) return Natural;");
      New_Line (File);
      Put_Line (File, "   --  Returns true if the string S is a keyword.");
      Put_Line (File, "   function Is_Keyword (S : in String) return Boolean;");
      Generate_Keyword_Table (File);
      Put      (File, "end ");
      Put      (File, To_String (Pkg_Name));
      Put      (File, ";");
      New_Line (File);
      Close    (File);
   end Generate_Specs;

   --  ------------------------------
   --  Generate the package body.
   --  ------------------------------
   procedure Generate_Body (Name : in String) is

      --  Read the generated body file.
      procedure Read_Body (Line : in String);

      --  Re-generate the char position table to ignore the case.
      procedure Generate_Char_Position;

      Path  : constant String := To_File_Name (Name) & ".adb";
      File  : Ada.Text_IO.File_Type;
      Count : Natural;
      Lines : Util.Strings.Vectors.Vector;

      --  ------------------------------
      --  Read the generated body file.
      --  ------------------------------
      procedure Read_Body (Line : in String) is
      begin
         Lines.Append (Line);
      end Read_Body;

      --  ------------------------------
      --  Re-generate the char position table to ignore the case.
      --  ------------------------------
      procedure Generate_Char_Position is
         use GNAT.Perfect_Hash_Generators;

         V : Natural;
      begin
         Put (File, "     (");
         for I in 0 .. 255 loop
            if I >= Character'Pos ('a') and I <= Character'Pos ('z') then
               V := Value (Used_Character_Set, I - Character'Pos ('a') + Character'Pos ('A'));
            else
               V := GNAT.Perfect_Hash_Generators.Value (Used_Character_Set, I);
            end if;
            if I > 0 then
               if I mod 16 = 0 then
                  Put_Line (File, ",");
                  Put (File, "      ");
               else
                  Put (File, ", ");
               end if;
            end if;
            Put (File, Util.Strings.Image (V));
         end loop;
         Put_Line (File, ");");
      end Generate_Char_Position;

   begin
      Util.Files.Read_File (Path => Path, Process => Read_Body'Access);
      Count := Natural (Lines.Length);
      Ada.Text_IO.Open (File => File,
                        Mode => Ada.Text_IO.Out_File,
                        Name => Path);
      Put_Line (File, "--  Generated by gperfhash");
      if Ignore_Case then
         Put_Line (File, "with Util.Strings.Transforms;");
      end if;
      for I in 1 .. Count loop
         declare
            L : constant String := Lines.Element (I);
         begin
            Put_Line (File, L);

            --  Generate the Is_Keyword function before the package end.
            if I = Count - 1 then
               Put_Line (File, "   --  Returns true if the string S is a keyword.");
               Put_Line (File, "   function Is_Keyword (S : in String) return Boolean is");
               if Ignore_Case then
                  Put_Line (File, "      K : constant String := "
                            & "Util.Strings.Transforms.To_Upper_Case (S);");
                  Put_Line (File, "      H : constant Natural := Hash (K);");
               else
                  Put_Line (File, "      H : constant Natural := Hash (S);");
               end if;
               Put_Line (File, "   begin");
               if Ignore_Case then
                  Put_Line (File, "      return Keywords (H).all = K;");
               else
                  Put_Line (File, "      return Keywords (H).all = S;");
               end if;
               Put_Line (File, "   end Is_Keyword;");
            end if;
         end;
      end loop;
      Close (File);
   end Generate_Body;

   --  ------------------------------
   --  Read a keyword and add it in the keyword list.
   --  ------------------------------
   procedure Read_Keyword (Line : in String) is
      use Ada.Strings;
      Word : String := Fixed.Trim (Line, Both);
   begin
      if Word'Length > 0 then
         if Ignore_Case then
            Word := Util.Strings.Transforms.To_Upper_Case (Word);
         end if;
         Names.Append (Word);
         GNAT.Perfect_Hash_Generators.Insert (Word);
      end if;
   end Read_Keyword;

   --  ------------------------------
   --  Given a package name, return the file name that correspond.
   --  ------------------------------
   function To_File_Name (Name : in String) return String is
      Result : String (Name'Range);
   begin
      for J in Name'Range loop
         if Name (J) in 'A' .. 'Z' then
            Result (J) := Character'Val (Character'Pos (Name (J))
              - Character'Pos ('A')
              + Character'Pos ('a'));

         elsif Name (J) = '.' then
            Result (J) := '-';

         else
            Result (J) := Name (J);
         end if;
      end loop;
      return Result;
   end To_File_Name;

begin
   --  Initialization is optional.  Get the log configuration by reading the property
   --  file 'samples/log4j.properties'.  The 'log.util' logger will use a DEBUG level
   --  and write the message in 'result.log'.
   Util.Log.Loggers.Initialize ("samples/log4j.properties");

   loop
      case Getopt ("h i p: package: help") is
         when ASCII.NUL =>
            exit;

         when 'i' =>
            Ignore_Case := True;

         when 'p' =>
            Pkg_Name := To_Unbounded_String (Parameter);

         when others =>
            raise GNAT.Command_Line.Invalid_Switch;
      end case;
   end loop;
   declare
      Keywords : constant String := Get_Argument;
      Pkg      : constant String := To_String (Pkg_Name);
      Count    : Natural := 0;
      K_2_V    : Float;
      V        : Natural;
      Seed     : constant Natural := 4321; --  Needed by the hash algorithm
   begin
      --  Read the keywords.
      Util.Files.Read_File (Path => Keywords, Process => Read_Keyword'Access);

      Count := Natural (Names.Length);
      if Count = 0 then
         Log.Error ("There is no keyword.");
         raise GNAT.Command_Line.Invalid_Switch;
      end if;

      --  Generate the perfect hash package.
      V := 2 * Count + 1;
      loop
         K_2_V := Float (V) / Float (Count);
         GNAT.Perfect_Hash_Generators.Initialize (Seed, K_2_V);
         begin
            GNAT.Perfect_Hash_Generators.Compute;
            exit;
         exception
            when GNAT.Perfect_Hash_Generators.Too_Many_Tries =>
               V := V + 1;
         end;
      end loop;
      GNAT.Perfect_Hash_Generators.Produce (Pkg);

      --  Override what GNAT generates to have a list of keywords and other operations.
      Generate_Specs (Pkg);
      Generate_Body (Pkg);

   exception
      when Ada.IO_Exceptions.Name_Error =>
         Log.Error ("Cannot read the keyword file.");
         Ada.Command_Line.Set_Exit_Status (1);
   end;

exception
   when GNAT.Command_Line.Invalid_Switch =>
      Log.Error ("Usage: gperfhash -i -p package keyword-file");
      Log.Error ("-i           Generate a perfect hash which ignores the case");
      Log.Error ("-p package   Use 'package' as the name of package (default is 'gphash')");
      Log.Error ("keyword-file The file which contains the keywords, one keyword on each line");
      Ada.Command_Line.Set_Exit_Status (1);
end Gperfhash;

Fix the perfect hash example

Fix the perfect hash example


-----------------------------------------------------------------------
--  util-dates -- Date utilities
--  Copyright (C) 2011, 2013, 2014, 2018 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
package body Util.Dates is

   --  ------------------------------
   --  Split the date into a date record (See Ada.Calendar.Formatting.Split).
   --  ------------------------------
   procedure Split (Into       : out Date_Record;
                    Date       : in Ada.Calendar.Time;
                    Time_Zone  : in Ada.Calendar.Time_Zones.Time_Offset := 0) is
      use Ada.Calendar;
      D : Ada.Calendar.Time := Date;
   begin
      Into.Date      := Date;
      Into.Time_Zone := Time_Zone;
      Ada.Calendar.Formatting.Split (Date        => Date,
                                     Year        => Into.Year,
                                     Month       => Into.Month,
                                     Day         => Into.Month_Day,
                                     Hour        => Into.Hour,
                                     Minute      => Into.Minute,
                                     Second      => Into.Second,
                                     Sub_Second  => Into.Sub_Second,
                                     Leap_Second => Into.Leap_Second,
                                     Time_Zone   => Time_Zone);

      --  The Day_Of_Week function uses the local timezone to find the week day.
      --  The wrong day is computed if the timezone is different.  If the current
      --  date is 23:30 GMT and the current system timezone is GMT+2, then the computed
      --  day of week will be the next day due to the +2 hour offset (01:30 AM).
      --  To avoid the timezone issue, we virtually force the hour to 12:00 am.
      if Into.Hour > 12 then
         D := D - Duration ((Into.Hour - 12) * 3600);
      elsif Into.Hour < 12 then
         D := D + Duration ((12 - Into.Hour) * 3600);
      end if;
      D := D - Duration ((60 - Into.Minute) * 60);
      Into.Day := Ada.Calendar.Formatting.Day_Of_Week (D);
   end Split;

   --  ------------------------------
   --  Return the date from the date record (See Ada.Calendar.Formatting.Time_Of).
   --  ------------------------------
   function Time_Of (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Date.Month_Day,
                                              Hour        => Date.Hour,
                                              Minute      => Date.Minute,
                                              Second      => Date.Second,
                                              Sub_Second  => Date.Sub_Second,
                                              Time_Zone   => Date.Time_Zone);
   end Time_Of;

   --  ------------------------------
   --  Returns true if the given year is a leap year.
   --  ------------------------------
   function Is_Leap_Year (Year : in Ada.Calendar.Year_Number) return Boolean is
   begin
      if Year mod 400 = 0 then
         return True;
      elsif Year mod 100 = 0 then
         return False;
      elsif Year mod 4 = 0 then
         return True;
      else
         return False;
      end if;
   end Is_Leap_Year;

   --  ------------------------------
   --  Returns true if both dates are on the same day.
   --  ------------------------------
   function Is_Same_Day (Date1, Date2 : in Ada.Calendar.Time) return Boolean is
      Split_Date1 : Date_Record;
      Split_Date2 : Date_Record;
   begin
      Split (Split_Date1, Date1);
      Split (Split_Date2, Date2);
      return Is_Same_Day (Split_Date1, Split_Date2);
   end Is_Same_Day;

   function Is_Same_Day (Date1, Date2 : in Date_Record) return Boolean is
   begin
      return Date1.Year = Date2.Year
        and Date1.Month = Date2.Month
        and Date1.Month_Day = Date2.Month_Day;
   end Is_Same_Day;

   --  ------------------------------
   --  Get the number of days in the given year.
   --  ------------------------------
   function Get_Day_Count (Year : in Ada.Calendar.Year_Number)
                           return Ada.Calendar.Arithmetic.Day_Count is
   begin
      if Is_Leap_Year (Year) then
         return 365;
      else
         return 366;
      end if;
   end Get_Day_Count;

   Month_Day_Count : constant array (Ada.Calendar.Month_Number)
     of Ada.Calendar.Arithmetic.Day_Count
       := (1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30,
           7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31);

   --  ------------------------------
   --  Get the number of days in the given month.
   --  ------------------------------
   function Get_Day_Count (Year  : in Ada.Calendar.Year_Number;
                           Month : in Ada.Calendar.Month_Number)
                           return Ada.Calendar.Arithmetic.Day_Count is
   begin
      if Month /= 2 then
         return Month_Day_Count (Month);
      elsif Is_Leap_Year (Year) then
         return 29;
      else
         return 28;
      end if;
   end Get_Day_Count;

   --  ------------------------------
   --  Get a time representing the given date at 00:00:00.
   --  ------------------------------
   function Get_Day_Start (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Date.Month_Day,
                                              Hour        => 0,
                                              Minute      => 0,
                                              Second      => 0,
                                              Sub_Second  => 0.0,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Day_Start;

   function Get_Day_Start (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Day_Start (D);
   end Get_Day_Start;

   --  ------------------------------
   --  Get a time representing the given date at 23:59:59.
   --  ------------------------------
   function Get_Day_End (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Date.Month_Day,
                                              Hour        => 23,
                                              Minute      => 59,
                                              Second      => 59,
                                              Sub_Second  => 0.999,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Day_End;

   function Get_Day_End (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Day_End (D);
   end Get_Day_End;

   --  ------------------------------
   --  Get a time representing the beginning of the week at 00:00:00.
   --  ------------------------------
   function Get_Week_Start (Date : in Date_Record) return Ada.Calendar.Time is
      use Ada.Calendar.Formatting;
      use Ada.Calendar.Arithmetic;

      T : constant Ada.Calendar.Time := Time_Of (Year        => Date.Year,
                                                 Month       => Date.Month,
                                                 Day         => Date.Month_Day,
                                                 Hour        => 0,
                                                 Minute      => 0,
                                                 Second      => 0,
                                                 Sub_Second  => 0.0,
                                                 Time_Zone   => Date.Time_Zone);
   begin
      if Date.Day = Ada.Calendar.Formatting.Monday then
         return T;
      else
         return T - Day_Count (Day_Name'Pos (Date.Day) - Day_Name'Pos (Monday));
      end if;
   end Get_Week_Start;

   function Get_Week_Start (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Week_Start (D);
   end Get_Week_Start;

   --  ------------------------------
   --  Get a time representing the end of the week at 23:59:99.
   --  ------------------------------
   function Get_Week_End (Date : in Date_Record) return Ada.Calendar.Time is
      use Ada.Calendar.Formatting;
      use Ada.Calendar.Arithmetic;

      T : constant Ada.Calendar.Time := Time_Of (Year        => Date.Year,
                                                 Month       => Date.Month,
                                                 Day         => Date.Month_Day,
                                                 Hour        => 23,
                                                 Minute      => 59,
                                                 Second      => 59,
                                                 Sub_Second  => 0.999,
                                                 Time_Zone   => Date.Time_Zone);
   begin
      --  End of week is 6 days + 23:59:59
      if Date.Day = Ada.Calendar.Formatting.Sunday then
         return T;
      else
         return T + Day_Count (6 - (Day_Name'Pos (Date.Day) - Day_Name'Pos (Monday)));
      end if;
   end Get_Week_End;

   function Get_Week_End (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Week_End (D);
   end Get_Week_End;

   --  ------------------------------
   --  Get a time representing the beginning of the month at 00:00:00.
   --  ------------------------------
   function Get_Month_Start (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Ada.Calendar.Day_Number'First,
                                              Hour        => 0,
                                              Minute      => 0,
                                              Second      => 0,
                                              Sub_Second  => 0.0,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Month_Start;

   function Get_Month_Start (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Month_Start (D);
   end Get_Month_Start;

   --  ------------------------------
   --  Get a time representing the end of the month at 23:59:59.
   --  ------------------------------
   function Get_Month_End (Date : in Date_Record) return Ada.Calendar.Time is
      Last_Day : constant Ada.Calendar.Day_Number
        := Ada.Calendar.Day_Number (Get_Day_Count (Date.Year, Date.Month));
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Last_Day,
                                              Hour        => 23,
                                              Minute      => 59,
                                              Second      => 59,
                                              Sub_Second  => 0.999,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Month_End;

   function Get_Month_End (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Month_End (D);
   end Get_Month_End;

end Util.Dates;

-----------------------------------------------------------------------
--  util-dates -- Date utilities
--  Copyright (C) 2011, 2013, 2014, 2018, 2020 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
package body Util.Dates is

   --  ------------------------------
   --  Split the date into a date record (See Ada.Calendar.Formatting.Split).
   --  ------------------------------
   procedure Split (Into       : out Date_Record;
                    Date       : in Ada.Calendar.Time;
                    Time_Zone  : in Ada.Calendar.Time_Zones.Time_Offset := 0) is
      use Ada.Calendar;
      D : Ada.Calendar.Time := Date;
   begin
      Into.Date      := Date;
      Into.Time_Zone := Time_Zone;
      Ada.Calendar.Formatting.Split (Date        => Date,
                                     Year        => Into.Year,
                                     Month       => Into.Month,
                                     Day         => Into.Month_Day,
                                     Hour        => Into.Hour,
                                     Minute      => Into.Minute,
                                     Second      => Into.Second,
                                     Sub_Second  => Into.Sub_Second,
                                     Leap_Second => Into.Leap_Second,
                                     Time_Zone   => Time_Zone);

      --  The Day_Of_Week function uses the local timezone to find the week day.
      --  The wrong day is computed if the timezone is different.  If the current
      --  date is 23:30 GMT and the current system timezone is GMT+2, then the computed
      --  day of week will be the next day due to the +2 hour offset (01:30 AM).
      --  To avoid the timezone issue, we virtually force the hour to 12:00 am.
      if Into.Hour > 12 then
         D := D - Duration ((Into.Hour - 12) * 3600);
      elsif Into.Hour < 12 then
         D := D + Duration ((12 - Into.Hour) * 3600);
      end if;
      D := D - Duration ((60 - Into.Minute) * 60);
      Into.Day := Ada.Calendar.Formatting.Day_Of_Week (D);
   end Split;

   --  ------------------------------
   --  Return the date from the date record (See Ada.Calendar.Formatting.Time_Of).
   --  ------------------------------
   function Time_Of (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Date.Month_Day,
                                              Hour        => Date.Hour,
                                              Minute      => Date.Minute,
                                              Second      => Date.Second,
                                              Sub_Second  => Date.Sub_Second,
                                              Time_Zone   => Date.Time_Zone);
   end Time_Of;

   --  ------------------------------
   --  Returns true if the given year is a leap year.
   --  ------------------------------
   function Is_Leap_Year (Year : in Ada.Calendar.Year_Number) return Boolean is
   begin
      if Year mod 400 = 0 then
         return True;
      elsif Year mod 100 = 0 then
         return False;
      elsif Year mod 4 = 0 then
         return True;
      else
         return False;
      end if;
   end Is_Leap_Year;

   --  ------------------------------
   --  Returns true if both dates are on the same day.
   --  ------------------------------
   function Is_Same_Day (Date1, Date2 : in Ada.Calendar.Time) return Boolean is
      Split_Date1 : Date_Record;
      Split_Date2 : Date_Record;
   begin
      Split (Split_Date1, Date1);
      Split (Split_Date2, Date2);
      return Is_Same_Day (Split_Date1, Split_Date2);
   end Is_Same_Day;

   function Is_Same_Day (Date1, Date2 : in Date_Record) return Boolean is
   begin
      return Date1.Year = Date2.Year
        and Date1.Month = Date2.Month
        and Date1.Month_Day = Date2.Month_Day;
   end Is_Same_Day;

   --  ------------------------------
   --  Get the number of days in the given year.
   --  ------------------------------
   function Get_Day_Count (Year : in Ada.Calendar.Year_Number)
                           return Ada.Calendar.Arithmetic.Day_Count is
   begin
      if Is_Leap_Year (Year) then
         return 366;
      else
         return 365;
      end if;
   end Get_Day_Count;

   Month_Day_Count : constant array (Ada.Calendar.Month_Number)
     of Ada.Calendar.Arithmetic.Day_Count
       := (1 => 31, 2 => 28, 3 => 31, 4 => 30, 5 => 31, 6 => 30,
           7 => 31, 8 => 31, 9 => 30, 10 => 31, 11 => 30, 12 => 31);

   --  ------------------------------
   --  Get the number of days in the given month.
   --  ------------------------------
   function Get_Day_Count (Year  : in Ada.Calendar.Year_Number;
                           Month : in Ada.Calendar.Month_Number)
                           return Ada.Calendar.Arithmetic.Day_Count is
   begin
      if Month /= 2 then
         return Month_Day_Count (Month);
      elsif Is_Leap_Year (Year) then
         return 29;
      else
         return 28;
      end if;
   end Get_Day_Count;

   --  ------------------------------
   --  Get a time representing the given date at 00:00:00.
   --  ------------------------------
   function Get_Day_Start (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Date.Month_Day,
                                              Hour        => 0,
                                              Minute      => 0,
                                              Second      => 0,
                                              Sub_Second  => 0.0,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Day_Start;

   function Get_Day_Start (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Day_Start (D);
   end Get_Day_Start;

   --  ------------------------------
   --  Get a time representing the given date at 23:59:59.
   --  ------------------------------
   function Get_Day_End (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Date.Month_Day,
                                              Hour        => 23,
                                              Minute      => 59,
                                              Second      => 59,
                                              Sub_Second  => 0.999,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Day_End;

   function Get_Day_End (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Day_End (D);
   end Get_Day_End;

   --  ------------------------------
   --  Get a time representing the beginning of the week at 00:00:00.
   --  ------------------------------
   function Get_Week_Start (Date : in Date_Record) return Ada.Calendar.Time is
      use Ada.Calendar.Formatting;
      use Ada.Calendar.Arithmetic;

      T : constant Ada.Calendar.Time := Time_Of (Year        => Date.Year,
                                                 Month       => Date.Month,
                                                 Day         => Date.Month_Day,
                                                 Hour        => 0,
                                                 Minute      => 0,
                                                 Second      => 0,
                                                 Sub_Second  => 0.0,
                                                 Time_Zone   => Date.Time_Zone);
   begin
      if Date.Day = Ada.Calendar.Formatting.Monday then
         return T;
      else
         return T - Day_Count (Day_Name'Pos (Date.Day) - Day_Name'Pos (Monday));
      end if;
   end Get_Week_Start;

   function Get_Week_Start (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Week_Start (D);
   end Get_Week_Start;

   --  ------------------------------
   --  Get a time representing the end of the week at 23:59:99.
   --  ------------------------------
   function Get_Week_End (Date : in Date_Record) return Ada.Calendar.Time is
      use Ada.Calendar.Formatting;
      use Ada.Calendar.Arithmetic;

      T : constant Ada.Calendar.Time := Time_Of (Year        => Date.Year,
                                                 Month       => Date.Month,
                                                 Day         => Date.Month_Day,
                                                 Hour        => 23,
                                                 Minute      => 59,
                                                 Second      => 59,
                                                 Sub_Second  => 0.999,
                                                 Time_Zone   => Date.Time_Zone);
   begin
      --  End of week is 6 days + 23:59:59
      if Date.Day = Ada.Calendar.Formatting.Sunday then
         return T;
      else
         return T + Day_Count (6 - (Day_Name'Pos (Date.Day) - Day_Name'Pos (Monday)));
      end if;
   end Get_Week_End;

   function Get_Week_End (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Week_End (D);
   end Get_Week_End;

   --  ------------------------------
   --  Get a time representing the beginning of the month at 00:00:00.
   --  ------------------------------
   function Get_Month_Start (Date : in Date_Record) return Ada.Calendar.Time is
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Ada.Calendar.Day_Number'First,
                                              Hour        => 0,
                                              Minute      => 0,
                                              Second      => 0,
                                              Sub_Second  => 0.0,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Month_Start;

   function Get_Month_Start (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Month_Start (D);
   end Get_Month_Start;

   --  ------------------------------
   --  Get a time representing the end of the month at 23:59:59.
   --  ------------------------------
   function Get_Month_End (Date : in Date_Record) return Ada.Calendar.Time is
      Last_Day : constant Ada.Calendar.Day_Number
        := Ada.Calendar.Day_Number (Get_Day_Count (Date.Year, Date.Month));
   begin
      return Ada.Calendar.Formatting.Time_Of (Year        => Date.Year,
                                              Month       => Date.Month,
                                              Day         => Last_Day,
                                              Hour        => 23,
                                              Minute      => 59,
                                              Second      => 59,
                                              Sub_Second  => 0.999,
                                              Time_Zone   => Date.Time_Zone);
   end Get_Month_End;

   function Get_Month_End (Date : in Ada.Calendar.Time) return Ada.Calendar.Time is
      D : Date_Record;
   begin
      Split (D, Date);
      return Get_Month_End (D);
   end Get_Month_End;

end Util.Dates;

Fix the Get_Day_Count function

Fix the Get_Day_Count function


-----------------------------------------------------------------------
--  facebook -- Get information about a Facebook user using the Facebook API
--  Copyright (C) 2012 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Text_IO;
with Ada.Command_Line;
with Ada.Strings.Unbounded;

with Util.Strings;
with Util.Beans.Objects;
with Util.Http.Clients;
with Util.Http.Clients.Web;
with Util.Serialize.IO.JSON;

with Mapping;

--  This example shows how to invoke a REST service, retrieve and extract a JSON content
--  into some Ada record.  It uses the Facebook Graph API which does not need any
--  authentication (ie, the public Facebook API).
procedure Facebook is

   procedure Print (P : in Mapping.Person) is
      use Ada.Strings.Unbounded;
   begin
      Ada.Text_IO.Put_Line ("Id         : " & Long_Long_Integer'Image (P.Id));
      Ada.Text_IO.Put_Line ("Name       : " & To_String (P.Name));
      Ada.Text_IO.Put_Line ("First name : " & To_String (P.First_Name));
      Ada.Text_IO.Put_Line ("Last name  : " & To_String (P.Last_Name));
      Ada.Text_IO.Put_Line ("Username   : " & To_String (P.Username));
      Ada.Text_IO.Put_Line ("Gender     : " & To_String (P.Gender));
      Ada.Text_IO.Put_Line ("Link       : " & To_String (P.Link));
   end Print;

   Count : constant Natural := Ada.Command_Line.Argument_Count;

   --  Mapping for the Person record.
   Person_Mapping  : aliased Mapping.Person_Mapper.Mapper;
begin
   if Count = 0 then
      Ada.Text_IO.Put_Line ("Usage: facebook username ...");
      Ada.Text_IO.Put_Line ("Example: facebook btaylor");
      return;
   end if;
   Person_Mapping.Add_Mapping ("id", Mapping.FIELD_ID);
   Person_Mapping.Add_Mapping ("name", Mapping.FIELD_NAME);
   Person_Mapping.Add_Mapping ("first_name", Mapping.FIELD_FIRST_NAME);
   Person_Mapping.Add_Mapping ("last_name", Mapping.FIELD_LAST_NAME);
   Person_Mapping.Add_Mapping ("link", Mapping.FIELD_LINK);
   Person_Mapping.Add_Mapping ("username", Mapping.FIELD_USER_NAME);
   Person_Mapping.Add_Mapping ("gender", Mapping.FIELD_GENDER);
   Util.Http.Clients.Web.Register;
   for I in 1 .. Count loop
      declare
         URI      : constant String := Ada.Command_Line.Argument (I);
         P        : aliased Mapping.Person;

	 procedure Get_User is
	    new Mapping.Rest_Get (Mapping.Person, Mapping.Person_Access,
				  Mapping.Person_Mapper.Set_Context);
      begin
	 Get_User ("https://graph.facebook.com/" & URI, Person_Mapping'Unchecked_Access, P'Unchecked_Access);
	 Print (P);
      end;
   end loop;
end Facebook;

-----------------------------------------------------------------------
--  facebook -- Get information about a Facebook user using the Facebook API
--  Copyright (C) 2012 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Text_IO;
with Ada.Command_Line;
with Ada.Strings.Unbounded;

with Util.Strings;
with Util.Beans.Objects;
with Util.Http.Clients;
with Util.Http.Clients.Web;
with Util.Http.Rest;
with Util.Serialize.IO.JSON;

with Mapping;

--  This example shows how to invoke a REST service, retrieve and extract a JSON content
--  into some Ada record.  It uses the Facebook Graph API which does not need any
--  authentication (ie, the public Facebook API).
procedure Facebook is

   procedure Print (P : in Mapping.Person) is
      use Ada.Strings.Unbounded;
   begin
      Ada.Text_IO.Put_Line ("Id         : " & Long_Long_Integer'Image (P.Id));
      Ada.Text_IO.Put_Line ("Name       : " & To_String (P.Name));
      Ada.Text_IO.Put_Line ("First name : " & To_String (P.First_Name));
      Ada.Text_IO.Put_Line ("Last name  : " & To_String (P.Last_Name));
      Ada.Text_IO.Put_Line ("Username   : " & To_String (P.Username));
      Ada.Text_IO.Put_Line ("Gender     : " & To_String (P.Gender));
      Ada.Text_IO.Put_Line ("Link       : " & To_String (P.Link));
   end Print;

   procedure Get_User is new Util.Http.Rest.Rest_Get (Mapping.Person_Mapper);

   Count : constant Natural := Ada.Command_Line.Argument_Count;

   --  Mapping for the Person record.
   Person_Mapping  : aliased Mapping.Person_Mapper.Mapper;
begin
   if Count = 0 then
      Ada.Text_IO.Put_Line ("Usage: facebook username ...");
      Ada.Text_IO.Put_Line ("Example: facebook btaylor");
      return;
   end if;
   Person_Mapping.Add_Mapping ("id", Mapping.FIELD_ID);
   Person_Mapping.Add_Mapping ("name", Mapping.FIELD_NAME);
   Person_Mapping.Add_Mapping ("first_name", Mapping.FIELD_FIRST_NAME);
   Person_Mapping.Add_Mapping ("last_name", Mapping.FIELD_LAST_NAME);
   Person_Mapping.Add_Mapping ("link", Mapping.FIELD_LINK);
   Person_Mapping.Add_Mapping ("username", Mapping.FIELD_USER_NAME);
   Person_Mapping.Add_Mapping ("gender", Mapping.FIELD_GENDER);
   Util.Http.Clients.Web.Register;
   for I in 1 .. Count loop
      declare
         URI      : constant String := Ada.Command_Line.Argument (I);
         P        : aliased Mapping.Person;
      begin
         Get_User ("https://graph.facebook.com/" & URI,
                   Person_Mapping'Unchecked_Access,
                   P'Unchecked_Access);
         Print (P);
      end;
   end loop;
end Facebook;

Use the generic Rest_Get operation

Use the generic Rest_Get operation


-----------------------------------------------------------------------
--  mat-symbols-targets - Symbol files management
--  Copyright (C) 2014 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Strings.Unbounded;

with Bfd.Symbols;
with Bfd.Files;

with MAT.Types;
package MAT.Symbols.Targets is

   type Target_Symbols is limited record
      File    : Bfd.Files.File_Type;
      Symbols : Bfd.Symbols.Symbol_Table;
   end record;

   --  Open the binary and load the symbols from that file.
   procedure Open (Symbols : in out Target_Symbols;
                   Path    : in String);

   --  Find the nearest source file and line for the given address.
   procedure Find_Nearest_Line (Symbols : in Target_Symbols;
                                Addr    : in MAT.Types.Target_Addr;
                                Name    : out Ada.Strings.Unbounded.Unbounded_String;
                                Func    : out Ada.Strings.Unbounded.Unbounded_String;
                                Line    : out Natural);

end MAT.Symbols.Targets;

-----------------------------------------------------------------------
--  mat-symbols-targets - Symbol files management
--  Copyright (C) 2014 Stephane Carrez
--  Written by Stephane Carrez (Stephane.Carrez@gmail.com)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------
with Ada.Strings.Unbounded;

with Bfd.Symbols;
with Bfd.Files;

with Util.Refs;

with MAT.Types;
package MAT.Symbols.Targets is

   type Target_Symbols is new Util.Refs.Ref_Entity with record
      File    : Bfd.Files.File_Type;
      Symbols : Bfd.Symbols.Symbol_Table;
   end record;
   type Target_Symbols_Access is access all Target_Symbols;

   --  Open the binary and load the symbols from that file.
   procedure Open (Symbols : in out Target_Symbols;
                   Path    : in String);

   --  Find the nearest source file and line for the given address.
   procedure Find_Nearest_Line (Symbols : in Target_Symbols;
                                Addr    : in MAT.Types.Target_Addr;
                                Name    : out Ada.Strings.Unbounded.Unbounded_String;
                                Func    : out Ada.Strings.Unbounded.Unbounded_String;
                                Line    : out Natural);

   package Target_Symbols_Refs is
     new Util.Refs.References (Target_Symbols, Target_Symbols_Access);

end MAT.Symbols.Targets;

Use a reference counter for the Target_Symbols