-- Version 0.1 of cp - uses read and write with tunable buffer size

-- Usage: cp_01 src dest

with Ada.Command_Line;
with Ada.Text_IO;
with POSIX.IO;
with POSIX.Permissions;
with POSIX.Process_Primitives;
with Oops;

procedure Cp is
   use POSIX;
   use POSIX.IO;
   use POSIX.Permissions;
   use type POSIX.IO_Count;

   Buffer_Size : constant Positive := 4_096;
   Copy_Mode   : constant Permission_Set := 
     (Owner_Read | Owner_Write | Group_Read | Others_Read => True,
      others                                              => False);

   Input, Output             : File_Descriptor;
   Buffer                    : IO_Buffer (1 .. Buffer_Size);
   Bytes_Read, Bytes_Written : POSIX.IO_Count;
begin
   if Ada.Command_Line.Argument_Count /= 2 then
      Ada.Text_IO.Put_Line
        (File => Ada.Text_IO.Standard_Error,
         Item => "usage: " & Ada.Command_Line.Command_Name &
                 " source destination");
      POSIX.Process_Primitives.Exit_Process (Status => 1);
   end if;

   Input := Open (Name => To_POSIX_String (Ada.Command_Line.Argument (1)),
                  Mode => Read_Only);

   Output := Open_Or_Create
     (Name        => To_POSIX_String (Ada.Command_Line.Argument (2)),
      Mode        => Write_Only,
      Permissions => Copy_Mode);

   loop
      begin
         Read (File   => Input,
               Buffer => Buffer,
               Last   => Bytes_Read);
      exception
         when Ada.Text_IO.End_Error =>
            if Bytes_Read > 0 then
               Bytes_Read := 0;
            else
               Oops (Message => "Cannot read from ",
                     Item    => Ada.Command_Line.Argument (1),
                     Error   => "End error (" & Bytes_Read'Img & ")");
            end if;
         when others =>
            raise;
      end;

      exit when Bytes_Read = 0;

      if Bytes_Read = -1 then
         Oops (Message => "Read error from ",
               Item    => Ada.Command_Line.Argument (1),
               Error   => "end of file?");
      end if;

      Write (File   => Output,
             Buffer => Buffer (Buffer'First .. Integer (Bytes_Read)),
             Last   => Bytes_Written);
      if Bytes_Written /= Bytes_Read then
         Oops (Message => "Write error to ",
               Item    => Ada.Command_Line.Argument (2),
               Error   => "disk full?");
      end if;
   end loop;

   Close (File => Input);
   Close (File => Output);
end Cp;

