-- elhoim is full object organizer with editor and command interpreter.
-- Elhoim is Copyright (C) 2023 Manuel De Girardi ; 
--
--   This program is free software; you can redistribute it and/or modify
--   it under the terms of the GNU General Public License as published by
--   the Free Software Foundation; either version 2 of the License, or
--   (at your option) any later version.
--
--   This program is distributed in the hope that it will be useful,
--   but WITHOUT 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
--   along with this program; if not, write to the Free Software
--   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
--
-- Date := "2023-05-26 17:40:38"
-- Version := "0.6.6b"
with Ada.Wide_Text_Io;
with Ada.Characters.Handling;
use Ada.Characters.Handling;
use Ada;

with Gnat.Command_Line;
use Gnat.Command_Line;
with Gnat.Os_Lib;
use Gnat;

with System.Os_Constants;

package body El.Operat is
   
   package W_Io renames Ada.Wide_Text_Io;
   
   
   function Normalize_Quoted_Argument(Arg : in String) return String is
      Local : String(1..Arg'Length) := Arg;
      Local_Index : Natural := 0;
   begin
      for I in Arg'First .. Arg'Last loop 
	 if Arg(i) /= '"' then
	    Local(Local_Index + 1) := Arg(I);
	    Local_Index := Local_Index + 1;
	 end if;
      end loop;
      if Local_Index /= 0 then
	 return Local(Local'First..Local_index);
      else
	 return Arg;
      end if;
   end Normalize_Quoted_Argument;
	
   use Os_Lib;
   use type U_String;
   
   function Spawn(Line : in String; Filename : in String) return Spawn_Result_Record is
      Command : Gnat.Command_Line.Command_Line;
            
      Spawn_Result : Spawn_Result_Record;
	        
      Buffer_args, Arguments_List : Os_Lib.Argument_List_Access;                  
      
      File : Wide_Text_Io.File_Type;      
      Deleted : Boolean := False;
      
      Exec_Path : Os_Lib.String_Access;
      Initialized : Boolean := False;
      
      
      Home : Os_Lib.String_Access;
      
      Out_Filename : String_Access;
      
   begin
      case System.Os_Constants.Target_Os is
	 when System.Os_Constants.Windows =>	    
	    Home := Getenv("HOMEPATH");
	 when others =>
	    Home := Getenv("HOME");
      end case;
      Out_Filename := new String ' (Home.all & Directory_Separator & Filename); 
	    
      Set_Command_Line(Command, Line);
      
      Gnat.Command_Line.Build(Command, Arguments_List, False);
      
      if Arguments_List(1) /= null and then Arguments_List(1).all /= "" then
         
         
         if Arguments_List'length > 1 then
            
            Buffer_Args := new Argument_List(1..Arguments_List'length - 1);
            
            for I in 1..Arguments_List'length - 1 loop
	       
               Buffer_Args(I) := new String ' (Normalize_Quoted_argument(Arguments_List(I+1).all));
	       
            end loop;            
            
            Spawn_Result := (Name         => Spawn,
			     Wlines       => null,
			     Count        => 0,
			     Initialized  => False,
                             Process_Id   => Invalid_Pid,			  
                             Program_Name => +To_Wide_String(Arguments_List(1).all),
                             Args          => Buffer_Args,
                             Output_File  => +To_Wide_String(Out_Filename.all),
                             Success      => False,
                             Return_Code  => 0,
                             Err_To_Out   => True
                             
                            );
            
            
            
            
         else
            
            
            Spawn_Result := (Name         => Spawn,
			     Wlines       => null,
			     Count        => 0,
			     Initialized  => False,
                             Process_Id   => Invalid_Pid,                             
                             Program_Name => +To_Wide_String(Arguments_List(1).all),
                             Args          => new Argument_List  ' (1..0 => new String ' ("" & Character'Val(0))),
                             Output_File  => +To_Wide_String(Out_Filename.all),
                             Success      => False,
                             Return_Code  => 0,
                             Err_To_Out   => True
                               
                            );
         end if;
         
         
         
         if Locate_Exec_On_Path(Arguments_List(1).all) /= null then
            Exec_Path := new String ' (Locate_Exec_On_Path(Arguments_List(1).all).all);         
                              
	    
            if Is_Executable_File(Exec_Path.all) then                                             
               
               
	       Os_Lib.Spawn(Program_Name => Exec_Path.all,
			    Args         => Spawn_Result.Args.all,                           
			    Output_File  => To_String(-Spawn_Result.Output_File),
			    Success      => Spawn_Result.Success,
			    Return_Code  => Spawn_Result.Return_Code,
			    Err_To_Out   => Spawn_Result.Err_To_out
			   );
               
               
               Spawn_Result.Wlines := new U_Array(1..1);
               
               Wide_Text_Io.Open(File, Wide_Text_Io.In_File, To_String(-Spawn_Result.Output_File), Form => "WCEM=8");

               while not Wide_Text_Io.End_Of_File(File) loop

                  declare
                     
                     Line : constant Wide_String := Wide_Text_Io.Get_Line(File);
                     Buffer : U_Array_Access;
                  begin
		     if Spawn_Result.Wlines /= null then
			
			if Initialized then
			   Buffer := new U_Array(1..(Spawn_Result.Wlines'Length)+1);
			   Buffer(Buffer'First..Buffer'Last-1) := Spawn_Result.Wlines.all;
			   Buffer(Buffer'Last) := (+(Line));
			else
			   Buffer := new U_Array(1..1);
			   Buffer(Buffer'Last) := (+(Line));
			   Initialized := True;
			end if;

                        

                     end if;
                     U_Array_Free(Spawn_Result.Wlines);
                     Spawn_Result.Wlines := new U_Array ' (Buffer.all);
                     U_Array_Free(Buffer);
                     
                  end;
                  
               end loop;
               
               Wide_Text_Io.Close(File);
	       
	       Spawn_Result.Success := True;
	       
               Delete_File (Name => To_String((-Spawn_Result.Output_File)), Success => deleted);
               
               if not Spawn_Result.Success then      
                  Spawn_Result.Return_Code := Gnat.Os_Lib.Errno;
               end if;
            else
               Spawn_Result.Wlines := new U_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found.")));               
               Spawn_Result.Return_Code := 127;
            end if;
         else            
            Spawn_Result.Wlines := new U_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found.")));               
            Spawn_Result.Return_Code := 127;
         end if;         
      else         
         Spawn_Result.Wlines := new U_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found.")));                        
         Spawn_Result.Return_Code := 127;
      end if;
      
      return Spawn_Result;
   end Spawn;    
   
   
   function Shell_Spawn(Line : in String; Filename : in String) return Spawn_Result_Record is
      
      Command : Gnat.Command_Line.Command_Line;
            
      Spawn_Result : Spawn_Result_Record;
	        
      Buffer_args, Arguments_List : Os_Lib.Argument_List_Access;                  
      
      File : Wide_Text_Io.File_Type;      
      
      Home : Os_Lib.String_Access;
      Out_Filename : String_Access;
      
      Deleted : Boolean := False;
      
      Exec_Path : Os_Lib.String_Access;
      Initialized : Boolean := False;
      
      
      
      GnuShell_Filename : String_Access;
      
      GnuShell_File : Wide_Text_Io.File_Type;      
      
   begin
      case System.Os_Constants.Target_Os is
	 when System.Os_Constants.Windows =>	    
	    Home := Getenv("%HOMEPATH%");
	 when others =>
	    Home := Getenv("HOME");
      end case;
      Out_Filename:= new String ' (Home.all & Directory_Separator & ".tmp_file.txt");
      Gnushell_Filename := new String ' (Home.all & Directory_Separator & Filename);
      Set_Command_Line(Command, Line);
      
      Gnat.Command_Line.Build(Command, Arguments_List, False);
      
      if Arguments_List(1) /= null and then Arguments_List(1).all /= "" then         
         
         if Arguments_List'length > 1 then
            
            Buffer_Args := new Argument_List(1..Arguments_List'length - 1);
            
            for I in 1..Arguments_List'length - 1 loop
	       
               Buffer_Args(I) := new String ' (Normalize_Quoted_argument(Arguments_List(I+1).all));
	       
            end loop;            
            
            Spawn_Result := (Spawn,
                             Process_Id   => Invalid_Pid,			  
                             Program_Name => +To_Wide_String(Arguments_List(1).all),
                             Args          => Buffer_Args,
                             Output_File  => +To_Wide_String(Out_Filename.all),
                             Success      => False,
                             Return_Code  => 0,
                             Err_To_Out   => True,
                             Wlines      => null,
			     Count       => 0,
			     Initialized => False
                            );
            
            
         else
            
            
            Spawn_Result := (Spawn,
                             Process_Id   => Invalid_Pid,                             
                             Program_Name => +To_Wide_String(Arguments_List(1).all),
                             Args          => new Argument_List  ' (1..0 => new String ' ("" & Character'Val(0))),
                             Output_File  => +To_Wide_String(Out_Filename.all),
                             Success      => False,
                             Return_Code  => 0,
                             Err_To_Out   => True,
                             Wlines      => null,
			     Count       => 0,
			     Initialized => False
                               
                            );
         end if;
         
         
         
         if Locate_Exec_On_Path(Arguments_List(1).all) /= null then
            Exec_Path := new String ' (Locate_Exec_On_Path(Arguments_List(1).all).all);         
                              
            
            if Is_Executable_File(Exec_Path.all) then
	       
               
               
               Spawn_Result.Process_Id := 
                 Non_Blocking_Spawn(Program_Name => Exec_Path.all,
                                    Args         => Spawn_Result.Args.all,                           
                                    Output_File  => To_String((-Spawn_Result.Output_File)),
                                    Err_To_Out   => Spawn_Result.Err_To_out
                                   );
               
               if Spawn_Result.Process_Id /= Gnat.Os_Lib.Invalid_Pid then
                  
                  Gnat.Os_Lib.Wait_Process(Spawn_Result.Process_Id, Spawn_Result.Success);
                  
               end if;               
               
               Spawn_Result.Wlines := new U_Array(1..1);
               
	       
               Wide_Text_Io.Open(File, Wide_Text_Io.In_File, To_String(-Spawn_Result.Output_File), Form => "WCEM=8");
	       
	       begin
	       	  Wide_Text_Io.Open(Gnushell_File, Wide_Text_Io.Append_File, Gnushell_Filename.all, Form => "WCEM=8");
	       exception
	       	  when others =>
	       	     Wide_Text_Io.Create(Gnushell_File, Wide_Text_Io.Out_File, Gnushell_Filename.all, Form => "WCEM=8");
	       end;
	       while not Wide_Text_Io.End_Of_File(File) loop

                  declare
                     
                     Line : constant Wide_String := Wide_Text_Io.Get_Line(File);
                     
                  begin
                     Wide_Text_Io.Put_Line(Gnushell_File, Line);
                     
                  end;
                  
               end loop;
	       Wide_Text_Io.Close(File);
	       Wide_Text_Io.Close(Gnushell_File);
	       Wide_Text_Io.Open(Gnushell_File, Wide_Text_Io.In_File, Gnushell_Filename.all, Form => "WCEM=8");
               while not Wide_Text_Io.End_Of_File(Gnushell_File) loop

                  declare
                     
                     Line : constant Wide_String := Wide_Text_Io.Get_Line(GnuShell_File);
                     Buffer : U_Array_Access;
                  begin
		     if Spawn_Result.Wlines /= null then
			
			if Initialized then
			   Buffer := new U_Array(1..(Spawn_Result.Wlines'Length)+1);
			   Buffer(Buffer'First..Buffer'Last-1) := Spawn_Result.Wlines.all;
			   Buffer(Buffer'Last) := (+(Line));
			else
			   Buffer := new U_Array(1..1);
			   Buffer(Buffer'Last) := (+(Line));
			   Initialized := True;
			end if;
                        

                     end if;
                     
                     Spawn_Result.Wlines := new U_Array ' (Buffer.all);
                     
                     
                  end;
                  
               end loop;
               Wide_Text_Io.Close(Gnushell_File);	 
               
               Delete_File (Name => To_String((-Spawn_Result.Output_File)), Success => deleted);	       
               if not Spawn_Result.Success then      
                  Spawn_Result.Return_Code := Gnat.Os_Lib.Errno;
               end if;
            else
               Spawn_Result.Wlines := new U_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found.")));               
               Spawn_Result.Return_Code := 127;
            end if;
         else            
            Spawn_Result.Wlines := new U_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found.")));               
            Spawn_Result.Return_Code := 127;
         end if;         
      else         
         Spawn_Result.Wlines := new U_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found.")));                        
         Spawn_Result.Return_Code := 127;
      end if;
      
      return Spawn_Result;
   end Shell_Spawn;    

end El.Operat ;