-- Topal: GPG/GnuPG and Alpine/Pine integration -- Copyright (C) 2001--2012 Phillip J. Brooke -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License version 3 as -- published by the Free Software Foundation. -- -- 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, see . with Ada.Strings.Unbounded; with Ada.Text_IO; with GNAT.OS_Lib; with Interfaces.C; with Interfaces.C.Strings; with Misc; package body Externals is pragma Linker_Options("externals-c.o"); -- Get an environment variable. function C_Get_Env (Name : Interfaces.C.Char_Array) return Interfaces.C.Strings.Chars_Ptr; pragma Import(C, C_Get_Env, "getenv"); procedure C_Set_Env (Name : Interfaces.C.Char_Array; Value : Interfaces.C.Char_Array; Overwrite : Interfaces.C.Int); pragma Import(C, C_Set_Env, "setenv"); pragma Unreferenced(C_Set_Env); procedure C_Perror (S : in Interfaces.C.char_array); pragma Import(C, C_Perror, "perror"); function C_Errno return Interfaces.C.Int; pragma Import(C, C_Errno, "errno_wrapper"); -- Get an environment variable. function Get_Env (Name : String) return String is C_Result : constant Interfaces.C.Strings.Chars_Ptr := C_Get_Env(Interfaces.C.To_C(Name)); Result : UBS; begin Result := ToUBS(Interfaces.C.Strings.Value(C_Result)); if Ada.Strings.Unbounded.Length(Result) = 0 then raise No_Such_Environment_Variable; end if; return ToStr(Result); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Get_Env"); raise; end Get_Env; function C_Glob_Actual (Pattern : in Interfaces.C.Char_Array) return Interfaces.C.Int; pragma Import(C, C_Glob_Actual, "glob_actual"); function C_Glob_Text (Index : Interfaces.C.Int) return Interfaces.C.Strings.Chars_Ptr; pragma Import(C, C_Glob_Text, "glob_text"); procedure C_Glob_Free; pragma Import(C, C_Glob_Free, "glob_free"); function Glob (Pattern : in String) return UBS_Array is C : Natural; begin Misc.Debug("Glob: Looking for pattern `" & Pattern & "'"); C := Natural(C_Glob_Actual(Interfaces.C.To_C(Pattern))); Misc.Debug("Glob: Count (C) is " & Integer'Image(C)); declare A : UBS_Array(1..C); begin for I in 1 .. C loop A(I) := ToUBS(Interfaces.C.Strings.Value(C_Glob_Text(Interfaces.C.Int(I - 1)))); Misc.Debug("Glob: Item " & Integer'Image(I) & " is `" & ToStr(A(I)) & "'"); end loop; Misc.Debug("Glob: Free'ing..."); C_Glob_Free; Misc.Debug("Glob: A'First = " & Integer'Image(A'First)); Misc.Debug("Glob: A'Last = " & Integer'Image(A'Last)); Misc.Debug("Glob: A'Length = " & Integer'Image(A'Length)); Misc.Debug("Glob: Done"); return A; end; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Glob"); raise; end Glob; -- Execute a command via `execvp'. function C_Execvp (File : in Interfaces.C.Char_Array; Argv : in Interfaces.C.Strings.Chars_Ptr_Array) return Interfaces.C.int; pragma Import(C, C_Execvp, "execvp"); -- Our wrapper for execvp. procedure Execvp (File : in String; Argv : in UBS) is AVA : constant UBS_Array := Misc.Split_Arguments(Argv); begin -- Handoff to Execvp (B). Execvp(File, AVA); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Execvp (A)"); raise; end Execvp; -- And another wrapper for execvp. procedure Execvp (File : in String; Argv : in UBS_Array) is use type Interfaces.C.Size_T; use type Interfaces.C.Strings.Chars_Ptr; use type UBS; LI : constant Interfaces.C.Size_T := Interfaces.C.Size_T(Argv'Last); AVC : Interfaces.C.Strings.Chars_Ptr_Array(0..LI+1); -- Last item should be NULL! RV : Integer; begin Misc.Debug("Execvp (B)"); if Argv'First /= 0 then Misc.Error("Argv first element should be 0."); end if; Misc.Debug("Argv is 0 to " & Integer'Image(Argv'Last)); Misc.Debug("Argv length (number of items) is " & Integer'Image(Argv'Length)); Misc.Debug("The C items are 0 to " & Integer'Image(Integer(LI))); for I in 0 .. LI loop AVC(I) := Interfaces.C.Strings.New_String(ToStr(Argv(Integer(I)))); Misc.Debug("Argc[" & Integer'Image(Integer(I)) & "]=`" & ToStr(Argv(Integer(I))) & "'"); end loop; -- Last item should be null! AVC(LI+1) := Interfaces.C.Strings.Null_Ptr; -- Check that the executable actually exists. declare use GNAT.OS_Lib; EP : String_Access; begin EP := Locate_Exec_On_Path(File); if EP = null then -- It's not there. Misc.Error("‘" & File & "’ is either not present or not executable."); end if; -- Free it. Free(EP); end; -- Actually do the exec. RV := Integer(C_Execvp(Interfaces.C.To_C(File), AVC)); if RV = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in Execvp (B)")); end if; Misc.Debug("Execvp: Command return value " & Misc.Trim_Leading_Spaces(Integer'Image(RV))); if RV = -1 then raise Exec_Failed; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Execvp (B), File=`" & File &"'"); raise; end Execvp; -- Pipe binding. type Filedes_Array is array (Positive range 1..2) of Interfaces.C.Int; pragma Convention (C, Filedes_Array); function C_Pipe (Files : in Filedes_Array) return Interfaces.C.int; pragma Import(C, C_Pipe, "pipe"); procedure Pipe (Reading : out Integer; Writing : out Integer) is RV : Integer; Filedes : Filedes_Array; begin Filedes := (0, 1); -- Suppress that warning. RV := Integer(C_Pipe(Filedes)); if RV = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in Pipe")); end if; Misc.Debug("Pipe: Command return value " & Misc.Trim_Leading_Spaces(Integer'Image(RV))); if RV = -1 then raise Pipe_Failed; end if; Reading := Integer(Filedes(1)); Writing := Integer(Filedes(2)); Misc.Debug("Pipe created; reading is " & Integer'Image(Reading) & " writing is " & Integer'Image(Writing)); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Pipe"); raise; end Pipe; -- Fork binding. function C_Fork return Interfaces.C.int; pragma Import(C, C_Fork, "fork"); function Fork return Integer is RV : Integer; begin RV := Integer(C_Fork); if RV = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in Fork")); end if; Misc.Debug("Fork: Command return value " & Misc.Trim_Leading_Spaces(Integer'Image(RV))); if RV = -1 then raise Fork_Failed; end if; return RV; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Fork"); raise; end Fork; -- Dup2 binding function C_Dup2 (Oldfd, Newfd : Interfaces.C.Int) return Interfaces.C.int; pragma Import(C, C_Dup2, "dup2"); procedure Dup2 (Oldfd, Newfd : in Integer) is RV : Integer; begin RV := Integer(C_Dup2(Interfaces.C.Int(Oldfd), Interfaces.C.Int(Newfd))); if RV = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in Dup2")); end if; Misc.Debug("Dup2: Command return value " & Misc.Trim_Leading_Spaces(Integer'Image(RV))); if RV = -1 then raise Dup2_Failed; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Dup2"); raise; end Dup2; -- Binding for C Close. We'll need this to tidy up nicely. function C_Close (FD : Interfaces.C.Int) return Interfaces.C.int; pragma Import(C, C_Close, "close"); procedure CClose (FD : in Integer) is RV : Integer; begin RV := Integer(C_Close(Interfaces.C.Int(FD))); if RV = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in CClose")); end if; Misc.Debug("CClose: Command return value " & Misc.Trim_Leading_Spaces(Integer'Image(RV))); if RV = -1 then raise CClose_Failed; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.CClose"); raise; end CClose; -- Binding for waitpid. We return the subprocess exit code. function C_Waitpid_Wrapper (PID : Interfaces.C.Int) return Interfaces.C.int; pragma Import(C, C_Waitpid_Wrapper, "waitpid_wrapper"); function Waitpid (PID : Integer) return Integer is RV : Integer; begin RV := Integer(C_Waitpid_Wrapper(Interfaces.C.Int(PID))); if RV = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in Waitpid")); end if; Misc.Debug("Waitpid: Command return value " & Misc.Trim_Leading_Spaces(Integer'Image(RV))); if RV = -1 then raise Waitpid_Failed; end if; return RV; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Waitpid"); raise; end Waitpid; -- ForkExec: a replacement for System. function ForkExec (File : in String; Argv : in UBS_Array) return Integer is P, E : Integer; begin P := Fork; if P = 0 then -- Child. Execvp(File, Argv); return -1; else -- Parent. Wait for the child to finish. E := Waitpid(P); return E; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec"); raise; end ForkExec; function ForkExec (File : in String; Argv : in UBS) return Integer is AVA : constant UBS_Array := Misc.Split_Arguments(Argv); begin return ForkExec(File, AVA); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec (B)"); raise; end ForkExec; function C_Open_Append_Wrapper (S : Interfaces.C.Char_Array) return Interfaces.C.int; pragma Import(C, C_Open_Append_Wrapper, "open_append_wrapper"); function ForkExec_Append (File : in String; Argv : in UBS_Array; Target : in String) return Integer is T, P, E : Integer; begin P := Fork; if P = 0 then -- Child. -- Get new file handle. T := Integer(C_Open_Append_Wrapper(Interfaces.C.To_C(Target))); if T = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec_Append")); raise Open_Append_Failed; end if; Dup2(T, 1); Execvp(File, Argv); return -1; else -- Parent. Wait for the child to finish. E := Waitpid(P); return E; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec_Append"); raise; end ForkExec_Append; function C_Open_Out_Wrapper (S : Interfaces.C.Char_Array) return Interfaces.C.int; pragma Import(C, C_Open_Out_Wrapper, "open_out_wrapper"); function ForkExec_Out (File : in String; Argv : in UBS_Array; Target : in String) return Integer is T, P, E : Integer; begin P := Fork; if P = 0 then -- Child. -- Get new file handle. T := Integer(C_Open_Out_Wrapper(Interfaces.C.To_C(Target))); if T = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec_Out")); raise Open_Out_Failed; end if; Dup2(T, 1); Execvp(File, Argv); return -1; else -- Parent. Wait for the child to finish. E := Waitpid(P); return E; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec_Out"); raise; end ForkExec_Out; function ForkExec_Out (File : in String; Argv : in UBS; Target : in String) return Integer is AVA : constant UBS_Array := Misc.Split_Arguments(Argv); begin return ForkExec_Out(File, AVA, Target); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec_Out (B)"); raise; end ForkExec_Out; function C_Open_In_Wrapper (S : Interfaces.C.Char_Array) return Interfaces.C.int; pragma Import(C, C_Open_In_Wrapper, "open_in_wrapper"); function ForkExec_In (File : in String; Argv : in UBS_Array; Source : in String) return Integer is S, P, E : Integer; begin P := Fork; if P = 0 then -- Child. -- Get new file handle. S := Integer(C_Open_In_Wrapper(Interfaces.C.To_C(Source))); if S = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec_In")); raise Open_In_Failed; end if; Dup2(S, 0); Execvp(File, Argv); return -1; else -- Parent. Wait for the child to finish. E := Waitpid(P); return E; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec_In"); raise; end ForkExec_In; function ForkExec_InOut (File : in String; Argv : in UBS_Array; Source : in String; Target : in String) return Integer is S, T, P, E : Integer; begin P := Fork; if P = 0 then -- Child. -- Get new file handle. S := Integer(C_Open_In_Wrapper(Interfaces.C.To_C(Source))); if S = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec_InOut")); raise Open_In_Failed; end if; Dup2(S, 0); -- Get new file handle. T := Integer(C_Open_Out_Wrapper(Interfaces.C.To_C(Target))); if T = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec_InOut")); raise Open_Out_Failed; end if; Dup2(T, 1); Execvp(File, Argv); return -1; else -- Parent. Wait for the child to finish. E := Waitpid(P); return E; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec_InOut"); raise; end ForkExec_InOut; function ForkExec_InOut (File : in String; Argv : in UBS; Source : in String; Target : in String) return Integer is AVA : constant UBS_Array := Misc.Split_Arguments(Argv); begin return ForkExec_InOut(File, AVA, Source, Target); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec_InOut (B)"); raise; end ForkExec_InOut; procedure ForkExec2 (File1 : in String; Argv1 : in UBS_Array; Exit1 : out Integer; File2 : in String; Argv2 : in UBS_Array; Exit2 : out Integer; Merge_StdErr1 : in Boolean := False; Report : in Boolean := False) is R, W, P1, P2 : Integer; begin if Report then Ada.Text_IO.New_Line; Ada.Text_IO.Put("Executing `" & File1 & "' and `" & File2 & "' with arguments "); for I in Argv1'First .. Argv1'Last loop Ada.Text_IO.Put("`" & ToStr(Argv1(I)) & "' "); end loop; Ada.Text_IO.Put(" and "); for I in Argv2'First .. Argv2'Last loop Ada.Text_IO.Put("`" & ToStr(Argv2(I)) & "' "); end loop; Ada.Text_IO.New_Line(2); end if; Pipe(R, W); P1 := Fork; if P1 = 0 then -- child1 CClose(R); Dup2(W, 1); if Merge_StdErr1 then Dup2(W, 2); end if; Execvp(File1, Argv1); else P2 := Fork; if P2 = 0 then -- child2 CClose(W); Dup2(R, 0); Execvp(File2, Argv2); else CClose(R); CClose(W); Exit1 := Waitpid(P1); Exit2 := Waitpid(P2); end if; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec2"); raise; end ForkExec2; procedure ForkExec2 (File1 : in String; Argv1 : in UBS; Exit1 : out Integer; File2 : in String; Argv2 : in UBS; Exit2 : out Integer; Merge_StdErr1 : in Boolean := False; Report : in Boolean := False) is AVA1 : constant UBS_Array := Misc.Split_Arguments(Argv1); AVA2 : constant UBS_Array := Misc.Split_Arguments(Argv2); begin ForkExec2(File1, AVA1, Exit1, File2, AVA2, Exit2, Merge_StdErr1, Report); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec2 (B)"); raise; end ForkExec2; procedure ForkExec2_Out (File1 : in String; Argv1 : in UBS_Array; Exit1 : out Integer; File2 : in String; Argv2 : in UBS_Array; Exit2 : out Integer; Target : in String) is T, R, W, P1, P2 : Integer; begin Pipe(R, W); P1 := Fork; if P1 = 0 then -- child1 CClose(R); Dup2(W, 1); Execvp(File1, Argv1); else P2 := Fork; if P2 = 0 then -- child2 CClose(W); Dup2(R, 0); -- Get new file handle. T := Integer(C_Open_Out_Wrapper(Interfaces.C.To_C(Target))); if T = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec2_Out")); raise Open_Out_Failed; end if; Dup2(T, 1); Execvp(File2, Argv2); else CClose(R); CClose(W); Exit1 := Waitpid(P1); Exit2 := Waitpid(P2); end if; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec2_Out"); raise; end ForkExec2_Out; procedure ForkExec2_Out (File1 : in String; Argv1 : in UBS; Exit1 : out Integer; File2 : in String; Argv2 : in UBS_Array; Exit2 : out Integer; Target : in String) is AVA1 : constant UBS_Array := Misc.Split_Arguments(Argv1); begin ForkExec2_Out(File1, AVA1, Exit1, File2, Argv2, Exit2, Target); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec2_Out(B)"); raise; end ForkExec2_Out; procedure ForkExec2_InOut (File1 : in String; Argv1 : in UBS_Array; Exit1 : out Integer; File2 : in String; Argv2 : in UBS_Array; Exit2 : out Integer; Source : in String; Target : in String) is S, T, R, W, P1, P2 : Integer; begin Pipe(R, W); P1 := Fork; if P1 = 0 then -- child1 CClose(R); Dup2(W, 1); -- Get new file handle. S := Integer(C_Open_In_Wrapper(Interfaces.C.To_C(Source))); if S = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec2_InOut")); raise Open_In_Failed; end if; Dup2(S, 0); -- Get new file handle. Execvp(File1, Argv1); else P2 := Fork; if P2 = 0 then -- child2 CClose(W); Dup2(R, 0); -- Get new file handle. T := Integer(C_Open_Out_Wrapper(Interfaces.C.To_C(Target))); if T = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec2_InOut")); raise Open_Out_Failed; end if; Dup2(T, 1); Execvp(File2, Argv2); else CClose(R); CClose(W); Exit1 := Waitpid(P1); Exit2 := Waitpid(P2); end if; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec2_InOut"); raise; end ForkExec2_InOut; procedure ForkExec3_Out (File1 : in String; Argv1 : in UBS; Exit1 : out Integer; File2 : in String; Argv2 : in UBS_Array; Exit2 : out Integer; File3 : in String; Argv3 : in UBS_Array; Exit3 : out Integer; Target : in String) is T, R1, W1, R2, W2, P1, P2, P3 : Integer; AVA1 : constant UBS_Array := Misc.Split_Arguments(Argv1); begin Pipe(R1, W1); Pipe(R2, W2); P1 := Fork; if P1 = 0 then -- child1 CClose(R1); CClose(R2); CClose(W2); Dup2(W1, 1); Execvp(File1, AVA1); else P2 := Fork; if P2 = 0 then -- child2 CClose(W1); CClose(R2); Dup2(R1, 0); Dup2(W2, 1); Execvp(File2, Argv2); else P3 := Fork; if P3 = 0 then -- child3 CClose(R1); CClose(W1); CClose(W2); Dup2(R2, 0); -- Get new file handle. T := Integer(C_Open_Out_Wrapper(Interfaces.C.To_C(Target))); if T = -1 then Misc.Debug("Errno is " & Misc.Trim_Leading_Spaces(Integer'Image(Integer(C_Errno)))); C_Perror(Interfaces.C.To_C("Topal: problem in ForkExec3_Out")); raise Open_Out_Failed; end if; Dup2(T, 1); Execvp(File3, Argv3); else CClose(R1); CClose(W1); CClose(R2); CClose(W2); Exit1 := Waitpid(P1); Exit2 := Waitpid(P2); Exit3 := Waitpid(P3); end if; end if; end if; exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.ForkExec3_Out"); raise; end ForkExec3_Out; -- More wrappers. function Open_Append (S : String) return Integer is begin return Integer(C_Open_Append_Wrapper(Interfaces.C.To_C(S))); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Open_Append"); raise; end Open_Append; function Open_Out (S : String) return Integer is begin return Integer(C_Open_Out_Wrapper(Interfaces.C.To_C(S))); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Open_Out"); raise; end Open_Out; function Open_In (S : String) return Integer is begin return Integer(C_Open_In_Wrapper(Interfaces.C.To_C(S))); exception when others => Ada.Text_IO.Put_Line(Ada.Text_IO.Standard_Error, "Exception raised in Externals.Open_In"); raise; end Open_In; end Externals;