--  smsh1.adb
--    Small shell (version 1).
--      First really useful version after prompting shell.
--      This on parses the command line into strings.
--      Uses fork, execvp, and wait, and ignores signals.

with
  Ada.Strings.Unbounded,
  Ada.Unchecked_Deallocation,
  Interfaces.C.Strings,
  POSIX.IO;

with
  C_Signals,
  EUP.Strings,
  EUP.Text_IO.Direct_Character_IO;

with Shell_Utilities;

procedure Small_Shell_1 is
   procedure Setup is
   begin
      C_Signals.Ignore (C_Signals.Signal_Interrupt);
      C_Signals.Ignore (C_Signals.Signal_Quit);
   end Setup;

   procedure Next (Command     :    out Ada.Strings.Unbounded.Unbounded_String;
                   End_Of_File :    out Boolean;
                   Prompt      : in     String) is
      use type POSIX.POSIX_Character;
      use type POSIX.IO.Open_Option_Set;
      Key : EUP.Text_IO.Direct_Character_IO.Character_Or_EOF;
   begin
      if POSIX.IO.Is_A_Terminal (POSIX.IO.Standard_Output) then
         declare
            Mode    : EUP.Text_IO.File_Mode;
            Options : POSIX.IO.Open_Option_Set;
            Written : Natural;
         begin
            POSIX.IO.Get_File_Control (File    => POSIX.IO.Standard_Output,
                                       Mode    => Mode,
                                       Options => Options);
            POSIX.IO.Set_File_Control
              (File    => POSIX.IO.Standard_Output,
               Options => Options + POSIX.IO.File_Synchronized);
            POSIX.IO.NONSTANDARD_Write
              (File   => POSIX.IO.Standard_Output,
               Buffer => EUP.Strings.To_POSIX_String (Prompt),
               Last   => Written);
            POSIX.IO.Set_File_Control (File    => POSIX.IO.Standard_Output,
                                       Options => Options);
         end;
      end if;

      Command := Ada.Strings.Unbounded.Null_Unbounded_String;
      loop
         Key := EUP.Text_IO.Direct_Character_IO.Get;
         if EUP.Text_IO.Direct_Character_IO.Is_Character (Key) then
            exit when EUP.Text_IO.Direct_Character_IO.To_POSIX_Character (Key) = POSIX.LF;
            exit when EUP.Text_IO.Direct_Character_IO.To_POSIX_Character (Key) = POSIX.CR;
            Ada.Strings.Unbounded.Append (Command, EUP.Text_IO.Direct_Character_IO.To_Character (Key));
         else
            End_Of_File := True;
            return;
         end if;
      end loop;
      End_Of_File := False;
   exception
      when others =>
         End_Of_File := True;
   end Next;

   procedure Free (Item : in out Shell_Utilities.Chars_Ptr_Array_Ptr) is
      procedure Free_List is new Ada.Unchecked_Deallocation
        (Object => Interfaces.C.Strings.Chars_Ptr_Array,
         Name   => Shell_Utilities.Chars_Ptr_Array_Ptr);
   begin
      for I in Item'Range loop
         Interfaces.C.Strings.Free (Item (I));
      end loop;
      Free_List (Item);
   end Free;

   Command_Line  : Ada.Strings.Unbounded.Unbounded_String;
   Done          : Boolean;
   Argument_List : Shell_Utilities.Chars_Ptr_Array_Ptr;
   Result        : Interfaces.C.int;
begin
   Setup;
   loop
      Next (Command     => Command_Line,
            End_Of_File => Done,
            Prompt      => "> ");
      exit when Done;
      Argument_List := Shell_Utilities.Split (Line => Command_Line);
      Result := Shell_Utilities.Execute (Argument_List);
      Free (Argument_List);
   end loop;
end Small_Shell_1;

