IF_READ_NON_BLOCKING := function ( stream )
    local  sl, outl;
    outl := "";
    repeat
        sl := READ_IOSTREAM_NOWAIT( stream![1], 1 );
        if sl <> fail  then
            Append( outl, sl );
        fi;
    until sl = fail;
    return outl;
end;




IF_READ_UNTIL_PROMPT := function ( cas )
    local  OUT, out, prompt_arrived;

    prompt_arrived := function ( cas, OUT )
          if IsFunction( IF_PROMPTS[cas] )  then
              return IF_PROMPTS[cas]( OUT );
          else
              return ForAny( IF_PROMPTS[cas],  x -> PositionSublist( OUT, x ) <> fail );
          fi;
      end;

    OUT := "";
    repeat
#        out := ReadAll( IF_IOLP[cas] );
        out := IF_READ_NON_BLOCKING( IF_IOLP[cas] );
        Append( OUT, out );
    until prompt_arrived( cas, OUT );
    Info( InfoInterfaces, 1, "IF output: ", OUT );
    return IF_FUNC_REMOVE_PROMPT[cas]( OUT );
end;





# check the executable, and if needed try to autodetermine,
# or print an appropriate error message

IF_CHECK_EXECUTABLE := function ( cas )
    local  IsExec, to_try, exec, exec_full_path, exec_name;

    IsExec := path -> IsString( path ) and IsDirectoryPath( path ) <> true and IsExecutableFile( path ) = true;

    if IsBound( IF_EXEC_FULL_PATH[cas] ) and IsExec( IF_EXEC_FULL_PATH[cas] )  then
        return IF_EXEC_FULL_PATH[cas];
    fi;

    while true do

        if IsBound( IF_EXEC_FULL_PATH[cas] ) and IsString( IF_EXEC_FULL_PATH[cas] )  then
            exec_full_path := IF_EXEC_FULL_PATH[cas];
        else 
            exec_full_path := "/";
        fi;

        if IsBound( IF_EXEC_NAME[cas] ) and IsString( IF_EXEC_NAME[cas] )  then
            exec_name := IF_EXEC_NAME[cas];
        else 
            exec_name := "";
        fi;

        to_try := [ exec_full_path, exec_name, Filename( Directory( exec_full_path ), exec_name ),
          Filename( DirectoriesSystemPrograms(  ), exec_name ), Filename( DirectoriesSystemPrograms(  ), exec_full_path ),
          Filename( DirectoryCurrent(  ), exec_name ), Filename( DirectoryCurrent(  ), exec_full_path ) ];

        for exec  in to_try  do
            if IsExec( exec )  then
                break;
            fi;
        od;

        if IsExec( exec )  then
            break;
        fi;

        Print( "  Type 'IF_EXEC_FULL_PATH[<IF_xxx>]:=\"<path>\"; return;' where\n" );
        Print( "  <path> is the path of the interfaced CAS executable file on your system,\n" );
        Print( "  and <IF_xxx> is the conventional name of the interfaced CAS.\n" );

        if not IsBound( IF_EXEC_FULL_PATH[cas])  then
            Print( "  Now 'IF_EXEC_FULL_PATH[<IF_xxx>]' is not bound.\n" );
        elif not IsString( IF_EXEC_FULL_PATH[cas] )  then
            Print( "  Now 'IF_EXEC_FULL_PATH[<IF_xxx>]' must be a string.\n" );
	elif not IsExistingFile( IF_EXEC_FULL_PATH[cas] )  then
	    Print( "  Now '", IF_EXEC_FULL_PATH[cas], "' does not exist.\n" );
        elif IsDirectoryPath( IF_EXEC_FULL_PATH[cas] ) = true  then
            Print( "  Now 'IF_EXEC_FULL_PATH[<IF_xxx>]' must be a file, not a directory.\n" );
        elif IsExecutableFile( IF_EXEC_FULL_PATH[cas] ) <> true  then
            Print( "  Now '", IF_EXEC_FULL_PATH[cas], "' is not an executable file.\n" );
        fi;

        Error( "Executable not found!\n" );

    od;

    Info( InfoInterfaces, 1, "found executable ", exec );
    return exec;

end;




# A function for closing the interface

IF_CloseInterface := function ( cas )
    if IsBound( IF_IOLP[cas] ) and IsStream( IF_IOLP[cas] )  then
        if not IsClosedStream( IF_IOLP[cas] )  then
            WriteLine( IF_IOLP[cas], IF_QUIT_COMMAND[cas] );
            Sleep( 1 );
            CloseStream( IF_IOLP[cas] );
        else
            Info( InfoInterfaces, 1, "Interface already closed." );
        fi;
    fi;
end;




# A function for killing the interface, copy of the provious function

IF_KillInterface := function ( cas )
    if IsBound( IF_IOLP[cas] ) and IsStream( IF_IOLP[cas] )  then
        if not IsClosedStream( IF_IOLP[cas] )  then
#            WriteLine( IF_IOLP[cas], IF_QUIT_COMMAND[cas] );
#            Sleep( 1 );
            CloseStream( IF_IOLP[cas] );
        else
            Info( InfoInterfaces, 1, "Interface already closed." );
        fi;
    fi;
end;




IF_StartInterface := function ( cas )
    local  out, OUT, exec,
# modification
input, i; 

    IF_CloseInterface( cas );
    exec := IF_CHECK_EXECUTABLE( cas );

    IF_IOLP[cas] := InputOutputLocalProcess( IF_TEMP_DIRECTORY, exec, IF_EXEC_OPTIONS[cas] );
    InstallAtExit( function (  ) IF_CloseInterface( cas ); end );

#    Sleep(...);
    OUT := "";
    repeat
#        out := ReadAll( IF_IOLP[cas] );
        out := IF_READ_NON_BLOCKING( IF_IOLP[cas] );
        Append( OUT, out );
    until PositionSublist( OUT, IF_FIRST_PROMPT[cas] ) <> fail;
    Info( InfoInterfaces, 1, "IF output: ", OUT );

    if IsBound( IF_FIRST_COMMAND[cas] ) then
        Info( InfoInterfaces, 1, "IF input: ", IF_FIRST_COMMAND[cas] );
# begin modification
input := SplitString( IF_FIRST_COMMAND[cas], '\n' );
for i in [1..Length(input)] do
#        WriteLine(IF_IOLP[cas], IF_FIRST_COMMAND[cas]);
        WriteLine(IF_IOLP[cas], input[i]);
        if IF_IS_THERE_PROMPT[cas]  then
            OUT := IF_READ_UNTIL_PROMPT(cas);
        fi;
od;
# end modification
    fi;

end;




IF_Type := function ( cas, obj )
    local  i;
    for i  in RecNames( IF_DATA_TYPES[cas] )  do
        if IsBound( IF_DATA_TYPES[cas].(i)[2] ) and IF_DATA_TYPES[cas].(i)[2]( obj )  then
            return i;
        fi;
    od;
    return fail;
end;




IF_ConvertFromGap := function ( cas, obj )
    local  type, i;
    type := IF_Type( cas, obj );

    if type in RecNames( IF_DATA_TYPES[cas] ) and IsBound( IF_DATA_TYPES[cas].(type)[3])  then
        return IF_DATA_TYPES[cas].(type)[3]( obj );
    fi;

    return String( obj );
    return fail;
end;



_EVALSTRINGTMP := 0;
IF_EvalString := function ( s )
    local  a, f, res;
    a := "_EVALSTRINGTMP:=";
    Append( a, s );
    Add( a, ';' );
    Unbind( _EVALSTRINGTMP );
    f := InputTextString( a );
    Read( f );
    if not IsBound( _EVALSTRINGTMP)  then
        res := s;
    else
        res := _EVALSTRINGTMP;
    fi;
    Unbind( _EVALSTRINGTMP );
    return res;
end;
Unbind(_EVALSTRINGTMP);



IF_ConvertToGap := function ( cas, obj, type, name )
    local  i;

    if type in RecNames( IF_DATA_TYPES[cas] ) and IsBound( IF_DATA_TYPES[cas].(type)[4])  then
        if NumberArgumentsFunction( IF_DATA_TYPES[cas].(type)[4] ) = 2 then 
            return IF_DATA_TYPES[cas].(type)[4]( obj, name );
        else
            return IF_DATA_TYPES[cas].(type)[4]( obj );
        fi;
    fi;

    return IF_EvalString( obj );
    return obj;
end;




IF_GET_CAS_OUTPUT := function( cas, name )

    local type, input, out, brackets;

    if IsBound ( IF_BRACKETS[cas] ) then
        brackets := IF_BRACKETS[cas];
    else
        brackets := [ "(", ")" ];
    fi;

    # get the output type
    if IsFunction( IF_FUNC_TYPE[cas] ) then 
        type := IF_FUNC_TYPE[cas]();
    else
        input := Concatenation( IF_FUNC_TYPE[cas], brackets[1], name , brackets[2], IF_END_COMMAND[cas] );
        Info( InfoInterfaces, 1, "IF input: ", input );
        WriteLine( IF_IOLP[cas], input );
        type := IF_READ_UNTIL_PROMPT( cas );
        if IF_TYPE_IS_QUOTED[cas]  then
            type := IF_EvalString( type );
        fi;
    fi;
    Info( InfoInterfaces, 1, "IF type: ", type );

    # get the output
    input := Concatenation( IF_FUNC_DISPLAY[cas], brackets[1], name , brackets[2], IF_END_COMMAND[cas] );
    Info( InfoInterfaces, 1, "IF input: ", input );
    WriteLine( IF_IOLP[cas], input );
    out := IF_READ_UNTIL_PROMPT( cas );

    return IF_ConvertToGap( cas, out, type, name );
end;




IF_Interface := function ( cas, command, arguments )
    local  length, obj, i, input, brackets;

    # start the interface if needed
    if not IsBound( IF_IOLP[cas] ) or not IsStream( IF_IOLP[cas] ) or IsClosedStream( IF_IOLP[cas] )  then
        IF_StartInterface(cas);
    fi;

    if IsBound ( IF_BRACKETS[cas] ) then
        brackets := IF_BRACKETS[cas];
    else
        brackets := [ "(", ")" ];
    fi;

    # send the input
    input := Concatenation( "GapObj ", IF_ASSIGN[cas], " ", command, brackets[1], " " );
    if IsString( arguments ) and IsStringRep( arguments )  then
        Append( input, arguments );
    else
        length := Length( arguments );
        for i  in [ 1 .. length ]  do
            obj := IF_ConvertFromGap( cas, arguments[i] );
            Append( input, obj );
            if i < length  then
                Append( input, ", " );
            fi;
        od;
    fi;
    Append( input, " " );
    Append( input, brackets[2] );

    Append( input, IF_END_COMMAND[cas] );
    Info( InfoInterfaces, 1, "IF input: ", input );
# begin modification
input := SplitString( input, '\n' );
for i in [1..Length(input)] do
    WriteLine( IF_IOLP[cas], input[i] );

    if IF_IS_THERE_PROMPT[cas]  then
        IF_READ_UNTIL_PROMPT( cas );
    fi;
od;
# end modification


    return IF_GET_CAS_OUTPUT( cas, "GapObj" );

end;



