-- Ada 2012 Implementierung von der SP2 Aufgabe "clash" -- Ich habe die POSIX.5 Ada Implementierung "Florist"[0], was auf -- Debian mit dem "libflorist2022-dev" Packet[1] installiert werden -- kann. Damit die Bibliothek auch gefunden wird, brauche ich im -- gleichem Verzeichnis eine "GNAT Project file" mit dem Namen -- "clash.gpr" und diesem Inhalt: -- with "florist"; -- project Clash is -- for Main use ("clash.adb"); -- for Object_Dir use "/tmp/build_id"; -- for Exec_Dir use "."; -- end Clash; -- Oder alternativ mit dem "compile-command" ganz unten in der Datei. -- [0] https://www.cs.fsu.edu/~baker/florist.html -- https://github.com/AdaCore/florist -- https://github.com/Blady-Com/florist -- [1] https://packages.debian.org/bookworm/libflorist2022-dev with Ada.Text_IO; with Ada.Strings; with Ada.Strings.Maps; with Ada.Strings.Bounded; with Ada.Containers.Indefinite_Ordered_Maps; with POSIX; with POSIX.Process_Primitives; with POSIX_Process_Environment; with POSIX_Process_Identification; procedure Clash is package IO renames Ada.Text_Io; package Proc renames POSIX.Process_Primitives; package Env renames POSIX_Process_Environment; package Ident renames POSIX_Process_Identification; package Command_Line is new Ada.Strings.Bounded.Generic_Bounded_Length (Max => 1_337); use Command_Line; function Pid_Less (A, B : Ident.Process_ID) return Boolean is begin return Ident.Image (A) < Ident.Image (B); end Pid_Less; package Process_Maps is new Ada.Containers.Indefinite_Ordered_Maps (Key_Type => Ident.Process_ID, "<" => Pid_Less, Element_Type => Bounded_String); Procs : Process_Maps.Map; type Pathname is access POSIX.Pathname; procedure Read_Command (Line : out Bounded_String; Args : out POSIX.POSIX_String_List; Block : out Boolean) is Index : Positive; Rest : Bounded_String; Whitespace : constant Ada.Strings.Maps.Character_Set := Ada.Strings.Maps.To_Set (Ada.Strings.Space); function Next return Boolean is Last : Natural; begin Find_Token (Rest, Whitespace, Ada.Strings.Outside, Index, Last); if Last = 0 then return False; end if; declare Token : String := Slice (Rest, Index, Last); begin if Last = Length (Rest) and Token = "&" then Block := False; else POSIX.Append (Args, POSIX.To_POSIX_String (Token)); end if; end; Rest := Bounded_Slice (Rest, Last + 1, Length (Rest)); return True; end Next; begin if IO.End_Of_File (IO.Standard_Input) then IO.Put_Line (""); -- break line Proc.Exit_Process (Proc.Exit_Status (Proc.Normal_Exit)); end if; Line := To_Bounded_String (IO.Get_Line); Rest := Line; POSIX.Make_Empty (Args); Block := True; loop exit when not Next; end loop; end Read_Command; procedure Print_Status (PID : Ident.Process_ID; Status : Proc.Termination_Status) is Line : String := (if Procs.Contains (PID) then To_String (Procs (PID)) else "???"); begin case Proc.Termination_Cause_Of (Status) is when Proc.Exited => declare Code : String := Proc.Exit_Status'Image (Proc.Exit_Status_Of (Status)); begin IO.Put_Line ("Exitstatus [" & Line & "] = " & Code); end; when others => IO.Put_Line ("No exitstatus [" & Line & "]"); end case; if Procs.Contains (PID) then Procs.Delete (PID); end if; end Print_Status; procedure Collect_Zombies is Status : Proc.Termination_Status; begin loop Proc.Wait_For_Child_Process (Status, Block => False); exit when not Proc.Status_Available (Status); Print_Status (Proc.Process_ID_Of (Status), Status); end loop; exception when E : POSIX.POSIX_Error => if not POSIX.Is_POSIX_Error (POSIX.No_Child_Process) then declare Err : POSIX.Error_Code := POSIX.Get_Error_Code; Msg : String := POSIX.Image (Err); begin IO.Put_Line (IO.Standard_Error, Msg); Proc.Exit_Process (Proc.Exit_Status (1)); end; end if; end Collect_Zombies; procedure Print_Prompt is Cwd : POSIX.Pathname := Env.Get_Working_Directory; begin IO.Put (POSIX.To_String (Cwd) & ": "); IO.Flush (IO.Standard_Output); end Print_Prompt; procedure Run_Command is Pid : Ident.Process_ID; Templ : Proc.Process_Template; Line : Bounded_String; Args : POSIX.POSIX_String_List; Status : Proc.Termination_Status; Block : Boolean; begin Read_Command (Line, Args, Block); if POSIX.Length (Args) = 0 then -- empty input return; end if; declare Path : POSIX.POSIX_String := POSIX.Value (Args, 1); Argv0 : String := POSIX.To_String (Path); begin if Argv0 = "cd" then if POSIX.Length (Args) = 2 then begin Env.Change_Working_Directory (POSIX.Value (Args, 2)); exception -- a typo shouldn't be fatal when E : POSIX.POSIX_Error => IO.Put_Line (IO.Standard_Error, POSIX.Image (POSIX.Get_Error_Code)); end; else IO.Put_Line (IO.Standard_Error, "Usage: cd "); end if; elsif Argv0 = "jobs" then for P in Procs.Iterate loop IO.Put_Line (Ident.Image (Process_Maps.Key (P)) & " " & To_String (Procs (P))); end loop; else Proc.Open_Template (Templ); Proc.Start_Process_Search (Pid, Path, Templ, Args); Proc.Close_Template (Templ); Procs.Include (Pid, Line); if Block then Proc.Wait_For_Child_Process (Status, Pid); Print_Status (Pid, Status); end if; end if; end; end Run_Command; begin loop Collect_Zombies; Print_Prompt; Run_Command; end loop; end Clash; -- Local Variables: -- tab-width: 2 -- compile-command: "gnatmake -aI/usr/share/ada/adainclude/florist -aO/usr/lib/ada/adalib/florist clash.adb -largs -lflorist" -- End: