-- who4.adb - read /etc/utmp and list info therein
--          - suppresses empty records
--          - formats time nicely
--          - buffers input (using POSIX.Memory_Mapping)

with Ada.Text_IO;
with Interfaces.C;
with Interfaces.C.Strings;
with POSIX;
with POSIX.C;
with POSIX.IO;
with POSIX.Memory_Mapping;
with EUP.Strings;
with EUP.UTMP;
with System;
with System.Storage_Elements;

procedure Who is
   procedure Show (Item : in     POSIX.C.time_t) is
      use Ada.Text_IO;
      use Interfaces.C;
      use Interfaces.C.Strings;

      function ctime (timep : access POSIX.C.time_t) return chars_ptr;
      pragma Import (C, ctime);

      Buffer : aliased POSIX.C.time_t := Item;
   begin
      Put (Value (ctime (Buffer'Access)) (5 .. 16));
   end Show;

   procedure Show (Item : in     EUP.UTMP.Instance) is
      use Ada.Text_IO;
      use EUP.Strings;
      use EUP.UTMP;
   begin
      if Item.Login_Type = User_Process then
         Put (Format (Item.User, Width => 8));
         Put (" ");
         Put (Format (Item.Line, Width => 8));
         Put (" ");
         Show (Item.TV.tv_sec);
         if To_Ada_String (Item.Host)'Length > 0 then
            Put (" (" & To_Ada_String (Item.Host) & ")");
         end if;
         New_Line;
      end if;
   end Show;

   use POSIX.IO;
   use POSIX.Memory_Mapping;
   use EUP.Strings;
   use System.Storage_Elements;

   UTMP_File      : File_Descriptor;
   UTMP_File_Size : System.Storage_Elements.Storage_Offset;
   UTMP_Memory    : System.Address;
begin
   UTMP_File := Open (Name => To_POSIX_String (EUP.UTMP.File_Name),
                      Mode => Read_Only);
   UTMP_File_Size := Storage_Offset (File_Size (UTMP_File));
   UTMP_Memory := Map_Memory (Length     => UTMP_File_Size,
                              Protection => Allow_Read,
                              Mapping    => Map_Shared,
                              File       => UTMP_File,
                              Offset     => 0);

   declare
      Bit_Count    : constant Natural :=
                       Natural (UTMP_File_Size) * Storage_Element'Size;
      Record_Count : constant Natural :=
                       Bit_Count / EUP.UTMP.Instance'Size;

      Records : array (1 .. Record_Count) of EUP.UTMP.Instance;
      for Records'Address use UTMP_Memory;
   begin
      for Index in Records'Range loop
         Show (Item => Records (Index));
      end loop;
   end;

   Unmap_Memory (First  => UTMP_Memory,
                 Length => UTMP_File_Size);
   Close (File => UTMP_File);
end Who;

