-- main is main file of main project written with Ada. -- Main is Copyright (C) 2025 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 := 2026-10-11 17:08:47 ;  -- Version := 0.48.0a ;  with GNAT.Directory_Operations.Iteration; with Gnat.Regexp; with Gnat.Directory_Operations; with Ada.Strings.Fixed; use Ada.Strings; use Gnat.Directory_Operations; with Gnat.Command_Line; with Text_Io; use Text_Io; with System.Os_Constants; use System.Os_Constants; with Ada.Characters.Handling; use Ada.Characters; with Gnat.Os_Lib; use Gnat; with Ada.Wide_Text_Io; use Ada; with System.Os_Constants; use System.Os_Constants; with Gnat.Command_Line; use Gnat.Command_Line; with Ada.Characters.Handling; use Ada.Characters.Handling; package body M.Tools is function Integer_Image (Value : in Integer) return String is begin if Value < 0 then return '-' & Integer'Image(Value)(Fixed.Index_Non_Blank(Integer'Image(Value)(2..Integer'Image(Value)'Last))..Integer'Image(Value)'Last); else return Integer'Image(Value)(Fixed.Index_Non_Blank(Integer'Image(Value))..Integer'Image(Value)'Last); end if; end Integer_Image; function Spawn(Line : in String; Filename : in String) return M.Results.Result_Record is Command : Gnat.Command_Line.Command_Line; Spawn_Result : M.Results.Result_Record(M.Results.Spawn); 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; PID : Process_Id; begin case Target_Os is when Windows => Home := Getenv("HOMEPATH"); when others => Home := Getenv("HOME"); end case; -- Out_Filename := new String ' (Home.all & Directory_Separator & Filename);  Out_Filename := new String ' (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 => M.Results.Spawn, Wlines => null, Count => 0, Initialized => False, Process_Id => Invalid_Pid, Program_Name => M.Results."+"(To_Wide_String(Arguments_List(1).all)), Args => Buffer_Args, Output_File => M.Results."+"(To_Wide_String(Out_Filename.all)), Success => False, Return_Code => 0, Err_To_Out => True ); else Spawn_Result := (Name => M.Results.Spawn, Wlines => null, Count => 0, Initialized => False, Process_Id => Invalid_Pid, Program_Name => M.Results."+"(To_Wide_String(Arguments_List(1).all)), Args => new Argument_List ' (1..0 => new String ' ("" & Character'Val(0))), Output_File => M.Results."+"(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.Process_id := Os_Lib.Non_Blocking_Spawn(Program_Name => Exec_Path.all, Args => Spawn_Result.Args.all, Output_File => To_String(M.Results."-"(Spawn_Result.Output_File)), --Success => Spawn_Result.Success, --Return_Code => Spawn_Result.Return_Code, 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 M.Results.U_Array(1..1); Wide_Text_Io.Open(File, Wide_Text_Io.In_File, To_String(M.Results."-"(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 : M.Results.U_Array_Access; begin if M.Results."/="(Spawn_Result.Wlines, null) then if Initialized then Buffer := new M.Results.U_Array(1..(Spawn_Result.Wlines'Length)+1); Buffer(Buffer'First..Buffer'Last-1) := Spawn_Result.Wlines.all; Buffer(Buffer'Last) := (M.Results."+"(Line)); else Buffer := new M.Results.U_array(1..1); Buffer(Buffer'Last) := (M.Results."+"(Line)); Initialized := True; end if; end if; M.Results.U_Array_Free(Spawn_Result.Wlines); Spawn_Result.Wlines := new M.Results.U_Array ' (Buffer.all); M.Results.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); Spawn_Result.Return_Code := Gnat.Os_Lib.Errno; -- if Spawn_Result.Success then  -- Spawn_Result.Return_Code := Gnat.Os_Lib.Errno; -- end if; else Spawn_Result.Wlines := new M.Results.U_Array ' (1 => (M.Results."+"(To_Wide_String(Arguments_List(1).all) & " : command not found."))); Spawn_Result.Return_Code := 127; end if; else Spawn_Result.Wlines := new M.Results.U_Array ' (1 => (M.Results."+"(To_Wide_String(Arguments_List(1).all) & " : command not found."))); Spawn_Result.Return_Code := 127; end if; else Spawn_Result.Wlines := new M.Results.U_Array ' (1 => (M.Results."+"(To_Wide_String(Arguments_List(1).all) & " : command not found."))); Spawn_Result.Return_Code := 127; end if; return Spawn_Result; end Spawn; function Delete_Local_Prefix(Filename : in String) return String is begin if Filename'Length > 2 then if Filename(Filename'First..Filename'First+1) = Local_Prefix then return Filename(Filename'First+2..Filename'Last); else return Filename; end if; else return Filename; end if; end Delete_Local_Prefix; 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; function Search_Regexp (Path : in String; Pattern : in String) return String is Full_Line : String_Access := new String ' (""); procedure Action (Filename : in String; Index : in Positive; Verax : in out Boolean) is Buffer : String_Access := new String ' (""); Tmp : String_Access := new String ' (""); Is_Out : Boolean := True; Regular_Exp : Gnat.Regexp.Regexp; begin --Text_Io.Put_line("File : "& Filename & "; Pattern : " & Pattern);  Regular_Exp := Gnat.Regexp.Compile("*" & Pattern, True, True); if Gnat.Regexp.Match(Filename, Regular_Exp) then if Fixed.Index(Filename, " ") /= 0 then for I in Filename'First..Filename'Last-1 loop if Filename(I) = Character'Val(32) then Buffer := new String ' (Tmp.all & " "); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); elsif Filename(I) = '/' then if Fixed.Index(Filename, " ", I) > I and Fixed.Index(Filename, "/", I+1) > Fixed.Index(Filename, " ", I) then if Is_Out then Buffer := new String ' (Tmp.all & Filename(I) & """"); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); Is_Out := False; else Buffer := new String ' (Tmp.all & Filename(I)); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); end if; elsif Fixed.Index(Filename, " ", I) > I and Fixed.Index(Filename, "/", I+1) = 0 then if Is_Out then Buffer := new String ' (Tmp.all & Filename(I) & """"); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); Is_Out := False; else Buffer := new String ' (Tmp.all & Filename(I)); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); end if; else Buffer := new String ' (Tmp.all & Filename(I)); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); end if; else Buffer := new String ' (Tmp.all & Filename(I)); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); end if; end loop; Buffer := new String ' (Tmp.all & Filename(Filename'Last)); if not Is_Out then Buffer := new String ' (Tmp.all & """"); Free(Tmp); Tmp := new String ' (Buffer.all); Free(Buffer); end if; --Text_Io.Put_Line("Matched :" & Full_Line.all); if Full_Line'Length > 0 then Buffer := new String ' (Full_Line.all & ' ' & "" & Delete_Local_Prefix(Tmp.all) & ""); Free(Full_Line); Full_Line := new String ' (Buffer.all); Free(Buffer); else Buffer := new String ' (Full_Line.all & "" & Delete_Local_Prefix(Tmp.all) & ""); Free(Full_Line); Full_Line := new String ' (Buffer.all); Free(Buffer); end if; --Text_Io.Put_Line("Matche :" & Tmp.all); else --Text_Io.Put_Line("Matched :" & filename); if Full_Line'Length > 0 then Buffer := new String ' (Full_Line.all & ' ' & Delete_Local_Prefix(filename)); Free(Full_Line); Full_Line := new String ' (Buffer.all); Free(Buffer); else Buffer := new String ' (Full_Line.all & "" & Delete_Local_Prefix(filename) & ""); Free(Full_Line); Full_Line := new String ' (Buffer.all); Free(Buffer); end if; --Text_Io.Put_Line("Matche :" & filename); end if; end if; Verax := False; end Action; procedure File_Search is new Iteration.Wildcard_iterator(Action); begin --Text_Io.Put_Line("pattern for : " & Expand_Path(Pattern)); if Expand_Path(Pattern) /= "" then File_Search(Path); end if; if Full_Line'Length > 0 then declare Result : constant String := Full_Line.all; begin Free(Full_Line); return Result; end; else return Expand_Path(Pattern); end if; exception when others => if Full_Line'Length > 0 then declare Result : constant String := Full_Line.all; begin Free(Full_Line); return Result; end; else return Expand_Path(Pattern); end if; end Search_Regexp; function Expand_filename (Line : in String) return String is Absolute_Path : constant Character := '/'; procedure Recursive_Expand (Line : in String; Result : in out String_Access) is Start, Stop : Natural := 0; begin if Line'Length > 1 then if Start = 0 then Start := Line'First; end if; Stop := Fixed.Index(Line, " ", Start+1); if Stop /= 0 then if Line(Start) /= Absolute_Path then Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Stop-1)), Line(Start..Stop-1))); else Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Stop-1)), Line(Start..Stop-1))); end if; Recursive_Expand(Line(Stop+1..Line'last), Result); else if Line(Start) /= Absolute_Path then Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'last)), Line(Start..Line'Last))); else Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'last)), Line(Start..Line'Last))); end if; end if; else if Start = 0 then Start := Line'First; end if; if Line(Start) /= Absolute_Path then Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'Last)), Line(Start..Line'Last))); else Result := new String ' (Result.all & ' ' & Search_Regexp(Expand_Path(Line(Start..Line'last)), Line(Start..Line'Last))); end if; end if; end Recursive_Expand; Full_Set : String_Access := new String ' (""); begin Recursive_Expand(Line(Fixed.Index_Non_Blank(Line)..Fixed.Index_Non_Blank(Line, Backward)), Full_Set); if Full_Set'Length > 0 then declare Result : constant String := Full_Set.all(Fixed.Index_Non_Blank(Full_Set.all)..Fixed.Index_Non_Blank(Full_Set.all, Backward)); begin Free(Full_Set); return Result; end; else return ""; end if; end Expand_Filename; procedure Change_directory (Line : in String;Old_Pwd : in out String_Access) is Command : Gnat.Command_Line.Command_Line; Args_List : Argument_List_Access; Buffer : String_Access; begin Gnat.Command_Line.Set_Command_Line(Command, Line); Gnat.Command_Line.Build(Command, Args_List, False); if Args_List'Last > 2 then return; elsif Args_List'Last = 2 then if Args_List(2) /= null and then Args_List(2).all /= "" then if Args_List(2).all = "-" then if Old_Pwd'Length /= 0 then buffer := new String ' (Get_Current_Dir); Change_Dir(Old_Pwd.all); Free(Old_Pwd); Old_Pwd := new String ' (Buffer.all); Free(Buffer); else Put_Line("no old_pwd."); end if; else Free(Old_Pwd); Old_Pwd := new String ' (Get_Current_Dir); Change_Dir(Args_List(2).all); end if; end if; else case Target_Os is when Windows => Free(Old_Pwd); Old_Pwd := new String ' (Get_Current_Dir); Change_Dir(Getenv("HOMEPATH").all); when Other_OS => Free(Old_Pwd); Old_Pwd := new String ' (Get_Current_Dir); Change_Dir(Getenv("HOME").all); end case; end if; end Change_Directory; use Gnat.Regexp; procedure Find_At_Path (Path : in String; Pattern : in String; Result : in M.Results.Wide_Result_Access) is Reg_Exp : constant Gnat.Regexp.Regexp := Gnat.Regexp.Compile(Pattern, True, True); procedure Find_Action (Item : String; Index : Positive; Quit : in out Boolean) is Buffer : M.Results.U_array_Access; begin if Match(Item, Reg_Exp) then Buffer := new M.Results.u_Array(1..(Result.Wlines'Length)+1); Buffer(Buffer'First..Buffer'Last-1) := Result.Wlines.all; Buffer(Buffer'Last) := (M.Results."+"(Handling.To_Wide_String(Item))); M.Results.U_Array_free(Result.Wlines); Result.Wlines := new M.Results.U_Array ' (Buffer.all); Quit := False; end if; end Find_Action; procedure Wildcard_Iterator is new GNAT.Directory_Operations.Iteration.Wildcard_Iterator(Find_Action); begin Wildcard_Iterator(Path); end Find_At_Path; procedure Completion (Line : in String; Full_Command : out Wide_String_Access; Result : out M.Results.Wide_Result_Access) is Top : constant Natural := Fixed.Index_Non_Blank(Line); Bot : constant Natural := Fixed.Index_Non_Blank(Line, Backward); Blank_Index : constant Natural := Fixed.Index(Line(Top..Bot), " ", Backward); begin Result := new M.Results.spawn_Result_Record; Result.Wlines := new M.Results.U_Array (1..1); if Blank_Index < Bot then declare Path : constant String := Line(Blank_Index+1..Bot); Directory_Index : constant Natural := Fixed.Index(Path, "/", Backward); begin if Directory_Index > 0 and Directory_Index < Path'Last then declare Directory : constant String := Path(Path'First..Directory_Index); begin Find_At_Path(Directory, "*" & Path(Directory_Index+1..Path'Last) & "*", Result); end; elsif Directory_Index > 0 then declare Directory : constant String := Path(Path'First..Directory_Index); begin Find_At_Path(Directory, "*", Result); end; else begin Find_At_Path(Get_Current_Dir, "*" & Path & "*", Result); end; end if; end; else begin Find_At_Path(Get_Current_Dir, "*", Result); end; end if; if Result.Wlines'Length = 2 then Full_Command := new Wide_String ' (Handling.To_Wide_String(Line(Line'First..Blank_Index)) & Handling.To_Wide_String(Delete_Local_Prefix(Handling.To_String((M.Results."-"(Result.Wlines(2))))))); end if; end Completion; use Adam.Results; function Shell_Spawn(Line : in String) return Adam.Results.Spawn_Result_Type is Command : Gnat.Command_Line.Command_Line; Spawn_Result : Adam.Results.Spawn_Result_Type; Buffer_args, Arguments_List : Os_Lib.Argument_List_Access; File : Wide_Text_Io.File_Type; Home : constant Os_Lib.String_Access := Getenv("HOME"); Out_Filename : constant String := Home.all & "/.out_file.txt"; Deleted : Boolean := False; Exec_Path : Os_Lib.String_Access; Initialized : Boolean := False; GnuShell_Filename : constant String := Home.all & "/.buffer.txt"; GnuShell_File : Wide_Text_Io.File_Type; begin 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 := ( Process_Id => Invalid_Pid, Program_Name => +To_Wide_String(Arguments_List(3).all), Args => Buffer_Args, Output_File => +To_Wide_String(Out_Filename), Success => False, Return_Code => 0, Err_To_Out => True, File_content => null ); else Spawn_Result := ( Process_Id => Invalid_Pid, Program_Name => +To_Wide_String(Arguments_List(3).all), Args => new Argument_List ' (1..0 => new String ' ("" & Character'Val(0))), Output_File => +To_Wide_String(Out_Filename), Success => False, Return_Code => 0, Err_To_Out => True, File_content => null ); 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.File_Content := new Variadic_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, Form => "WCEM=8"); exception when others => Wide_Text_Io.Create(Gnushell_File, Wide_Text_Io.Out_File, Gnushell_Filename, 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, 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 : Variadic_Access; begin if Spawn_Result.File_Content /= null then if Initialized then Buffer := new Variadic_Array(1..(Spawn_Result.File_Content'Length)+1); Buffer(Buffer'First..Buffer'Last-1) := Spawn_Result.File_Content.all; Buffer(Buffer'Last) := (+(Line)); else Buffer := new Variadic_Array(1..1); Buffer(Buffer'Last) := (+(Line)); Initialized := True; end if; end if; Spawn_Result.File_Content := new Variadic_Array ' (Buffer.all); end; end loop; Wide_Text_Io.Close(Gnushell_File); Delete_File (Name => To_String((-Spawn_Result.Output_File)), Success => deleted); --Wide_Text_Io.Put_Line("Deleted file : "& (-Spawn_Result.Output_File)); if not Spawn_Result.Success then Spawn_Result.Return_Code := Gnat.Os_Lib.Errno; end if; else Spawn_Result.File_Content := new Variadic_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found."))); Spawn_Result.Return_Code := 127; end if; else Spawn_Result.File_Content := new Variadic_Array ' (1 => (+(To_Wide_String(Arguments_List(1).all) & " : command not found."))); Spawn_Result.Return_Code := 127; end if; else Spawn_Result.File_Content := new Variadic_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 M.Tools ;