%%
%% %CopyrightBegin%
%%
  Copyright Ericsson AB 1996 - 2021 . All Rights Reserved .
%%
  Licensed under the Apache License , Version 2.0 ( the " License " ) ;
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     -2.0
%%
%% Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an " AS IS " BASIS ,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(io).

-export([put_chars/1,put_chars/2,nl/0,nl/1,
	 get_chars/2,get_chars/3,get_line/1,get_line/2,
	 get_password/0, get_password/1,
	 setopts/1, setopts/2, getopts/0, getopts/1]).
-export([write/1,write/2,read/1,read/2,read/3,read/4]).
-export([columns/0,columns/1,rows/0,rows/1]).
-export([fwrite/1,fwrite/2,fwrite/3,fread/2,fread/3,
	 format/1,format/2,format/3]).
-export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3,scan_erl_exprs/4,
	 scan_erl_form/1,scan_erl_form/2,scan_erl_form/3,scan_erl_form/4,
	 parse_erl_exprs/1,parse_erl_exprs/2,parse_erl_exprs/3,
         parse_erl_exprs/4,parse_erl_form/1,parse_erl_form/2,
         parse_erl_form/3,parse_erl_form/4]).
-export([request/1,request/2,requests/1,requests/2]).
%% Implemented in native code
-export([printable_range/0]).

-export_type([device/0, format/0, server_no_data/0]).

%%-------------------------------------------------------------------------

-type device() :: atom() | pid().
-type prompt() :: atom() | unicode:chardata().

  ErrorDescription is whatever the I / O - server sends .
-type server_no_data() :: {'error', ErrorDescription :: term()} | 'eof'.

-type location() :: erl_anno:location().

%%-------------------------------------------------------------------------
%% Needs to be inlined for error_info to be correct
-compile({inline,[o_request/2]}).
o_request(Function, OrigArgs) ->
    {Io, Request} =
        if
            Function =:= format; Function =:= fwrite ->
                case OrigArgs of
                    [Format] ->
                        {default_output(), {format, Format, []}};
                    [Format, Args] ->
                        {default_output(), {format, Format, Args}};
                    [D, Format, Args] ->
                        {D, {format, Format, Args}}
                end;
            Function =:= put_chars ->
                case OrigArgs of
                    [Chars] ->
                        {default_output(), {put_chars, unicode, Chars}};
                    [D, Chars] ->
                        {D, {put_chars, unicode, Chars}};
                    [D, Encoding, Chars] ->
                        {D, {put_chars, Encoding, Chars}}
                end;
            Function =:= nl ->
                case OrigArgs of
                    [] ->
                        {default_output(), nl};
                    [D] ->
                        {D, nl}
                end;
            Function =:= write ->
                case OrigArgs of
                    [Term] ->
                        {default_output(), {write, Term}};
                    [D, Term] ->
                        {D, {write, Term}}
                end
        end,
    ErrorRef = make_ref(),
    case request(Io, Request, ErrorRef) of
        {ErrorRef, Reason} ->
            %% We differentiate between errors that are created by this module
            erlang:error(conv_reason(Reason), OrigArgs,
                         [{error_info,#{cause => {?MODULE, Reason},
                                        module => erl_stdlib_errors}}]);
        {error, Reason} ->
            %% and the errors we get from the Device
            erlang:error(conv_reason(Reason), OrigArgs,
                         [{error_info,#{cause => {device, Reason},
                                        module => erl_stdlib_errors}}]);
        Other ->
            Other
    end.

%%
%% User interface.
%%

%% Request what the user considers printable characters
-spec printable_range() -> 'unicode' | 'latin1'.
printable_range() ->
    erlang:nif_error(undefined).

%% Put chars takes mixed *unicode* list from R13 onwards.
-spec put_chars(CharData) -> 'ok' when
      CharData :: unicode:chardata().

put_chars(Chars) ->
    o_request(?FUNCTION_NAME, [Chars]).

-spec put_chars(IoDevice, CharData) -> 'ok' when
      IoDevice :: device(),
      CharData :: unicode:chardata().

put_chars(Io, Chars) ->
    o_request(?FUNCTION_NAME, [Io, Chars]).

-spec nl() -> 'ok'.

nl() ->
    o_request(?FUNCTION_NAME, []).

-spec nl(IoDevice) -> 'ok' when
      IoDevice :: device().

nl(Io) ->
    o_request(?FUNCTION_NAME, [Io]).

-spec columns() -> {'ok', pos_integer()} | {'error', 'enotsup'}.

columns() ->
    columns(default_output()).

-spec columns(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when
      IoDevice :: device().

columns(Io) ->
    case request(Io, {get_geometry,columns}) of
	N  when is_integer(N), N > 0 ->
	    {ok,N};
	_ ->
	    {error,enotsup}
    end.
	    
-spec rows() -> {'ok', pos_integer()} | {'error', 'enotsup'}.

rows() ->
    rows(default_output()).

-spec rows(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when
      IoDevice :: device().

rows(Io) ->
    case request(Io,{get_geometry,rows}) of
	N  when is_integer(N), N > 0 ->
	    {ok,N};
	_ ->
	    {error,enotsup}
    end.    

-spec get_chars(Prompt, Count) -> Data | server_no_data() when
      Prompt :: prompt(),
      Count :: non_neg_integer(),
      Data :: string() | unicode:unicode_binary().

get_chars(Prompt, N) ->
    get_chars(default_input(), Prompt, N).

-spec get_chars(IoDevice, Prompt, Count) -> Data | server_no_data() when
      IoDevice :: device(),
      Prompt :: prompt(),
      Count :: non_neg_integer(),
      Data :: string() | unicode:unicode_binary().

get_chars(Io, Prompt, N) when is_integer(N), N >= 0 ->
    request(Io, {get_chars,unicode,Prompt,N}).

-spec get_line(Prompt) -> Data | server_no_data() when
      Prompt :: prompt(),
      Data :: string() | unicode:unicode_binary().

get_line(Prompt) ->
    get_line(default_input(), Prompt).

-spec get_line(IoDevice, Prompt) -> Data | server_no_data() when
      IoDevice :: device(),
      Prompt :: prompt(),
      Data :: string() | unicode:unicode_binary().

get_line(Io, Prompt) ->
    request(Io, {get_line,unicode,Prompt}).

get_password() ->
    get_password(default_input()).

get_password(Io) ->
    request(Io, {get_password,unicode}).

-type encoding()   :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | 'utf32'
                    | {'utf16', 'big' | 'little'} | {'utf32','big' | 'little'}.
-type expand_fun() :: fun((term()) -> {'yes'|'no', string(), [string(), ...]}).
-type opt_pair()   :: {'binary', boolean()}
                    | {'echo', boolean()}
                    | {'expand_fun', expand_fun()}
                    | {'encoding', encoding()}.

-spec getopts() -> [opt_pair()] | {'error', Reason} when
      Reason :: term().

getopts() ->
    getopts(default_input()).

-spec getopts(IoDevice) -> [opt_pair()] | {'error', Reason} when
      IoDevice :: device(),
      Reason :: term().

getopts(Io) ->
    request(Io, getopts).

-type setopt() :: 'binary' | 'list' | opt_pair().

-spec setopts(Opts) -> 'ok' | {'error', Reason} when
      Opts :: [setopt()],
      Reason :: term().

setopts(Opts) ->
    setopts(default_input(), Opts).

-spec setopts(IoDevice, Opts) -> 'ok' | {'error', Reason} when
      IoDevice :: device(),
      Opts :: [setopt()],
      Reason :: term().

setopts(Io, Opts) ->
    request(Io, {setopts, Opts}).

  Writing and reading Erlang terms .

-spec write(Term) -> 'ok' when
      Term :: term().

write(Term) ->
    o_request(?FUNCTION_NAME, [Term]).

-spec write(IoDevice, Term) -> 'ok' when
      IoDevice :: device(),
      Term :: term().

write(Io, Term) ->
    o_request(?FUNCTION_NAME, [Io, Term]).


-spec read(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: {'ok', Term :: term()}
              | server_no_data()
              | {'error', ErrorInfo},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Prompt) ->
    read(default_input(), Prompt).

-spec read(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: {'ok', Term :: term()}
              | server_no_data()
              | {'error', ErrorInfo},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Io, Prompt) ->
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of
	{ok,Toks,_EndLine} ->
	    erl_parse:parse_term(Toks);
	{error,E,_EndLine} ->
	    {error,E};
	{eof,_EndLine} ->
	    eof;
	Other ->
	    Other
    end.

-spec read(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: {'ok', Term :: term(), EndLocation :: location()}
              | {'eof', EndLocation :: location()}
              | server_no_data()
              | {'error', ErrorInfo, ErrorLocation :: location()},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Io, Prompt, Pos0) ->
    read(Io, Prompt, Pos0, []).

-spec read(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: {'ok', Term :: term(), EndLocation :: location()}
              | {'eof', EndLocation :: location()}
              | server_no_data()
              | {'error', ErrorInfo, ErrorLocation :: location()},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Io, Prompt, Pos0, Options) ->
    Args = [Pos0,Options],
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of
	{ok,Toks,EndLocation} ->
            case erl_parse:parse_term(Toks) of
                {ok,Term} -> {ok,Term,EndLocation};
                {error,ErrorInfo} -> {error,ErrorInfo,EndLocation}
            end;
	{error,_E,_EndLocation} = Error ->
	    Error;
	{eof,_EndLocation} = Eof ->
	    Eof;
	Other ->
	    Other
    end.

%% Formatted writing and reading.

conv_reason(arguments) -> badarg;
conv_reason(terminated) -> terminated;
conv_reason({no_translation,_,_}) -> no_translation;
conv_reason(_Reason) -> badarg.

-type format() :: atom() | string() | binary().

-spec fwrite(Format) -> 'ok' when
      Format :: format().

fwrite(Format) ->
    o_request(?FUNCTION_NAME, [Format]).

-spec fwrite(Format, Data) -> 'ok' when
      Format :: format(),
      Data :: [term()].

fwrite(Format, Args) ->
    o_request(?FUNCTION_NAME, [Format, Args]).

-spec fwrite(IoDevice, Format, Data) -> 'ok' when
      IoDevice :: device(),
      Format :: format(),
      Data :: [term()].

fwrite(Io, Format, Args) ->
    o_request(?FUNCTION_NAME, [Io, Format, Args]).

-spec fread(Prompt, Format) -> Result when
      Prompt :: prompt(),
      Format :: format(),
      Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}.

fread(Prompt, Format) ->
    fread(default_input(), Prompt, Format).

-spec fread(IoDevice, Prompt, Format) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Format :: format(),
      Result :: {'ok', Terms :: [term()]}
              | {'error', {'fread', FreadError :: io_lib:fread_error()}}
              | server_no_data().

fread(Io, Prompt, Format) ->
    request(Io, {fread,Prompt,Format}).

-spec format(Format) -> 'ok' when
      Format :: format().
format(Format) ->
    o_request(?FUNCTION_NAME, [Format]).

-spec format(Format, Data) -> 'ok' when
      Format :: format(),
      Data :: [term()].

format(Format, Args) ->
    o_request(?FUNCTION_NAME, [Format, Args]).

-spec format(IoDevice, Format, Data) -> 'ok' when
      IoDevice :: device(),
      Format :: format(),
      Data :: [term()].

format(Io, Format, Args) ->
    o_request(?FUNCTION_NAME, [Io, Format, Args]).

%% Scanning Erlang code.

-spec scan_erl_exprs(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().
 
scan_erl_exprs(Prompt) ->
    scan_erl_exprs(default_input(), Prompt, 1).

-spec scan_erl_exprs(Device, Prompt) -> Result when
      Device :: device(),
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_exprs(Io, Prompt) ->
    scan_erl_exprs(Io, Prompt, 1).

-spec scan_erl_exprs(Device, Prompt, StartLocation) -> Result when
      Device :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_exprs(Io, Prompt, Pos0) ->
    scan_erl_exprs(Io, Prompt, Pos0, []).

-spec scan_erl_exprs(Device, Prompt, StartLocation, Options) -> Result when
      Device :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_exprs(Io, Prompt, Pos0, Options) ->
    request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}).

-spec scan_erl_form(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Prompt) ->
    scan_erl_form(default_input(), Prompt, 1).

-spec scan_erl_form(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Io, Prompt) ->
    scan_erl_form(Io, Prompt, 1).

-spec scan_erl_form(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Io, Prompt, Pos0) ->
    scan_erl_form(Io, Prompt, Pos0, []).

-spec scan_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Io, Prompt, Pos0, Options) ->
    request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}).

%% Parsing Erlang code.

-type parse_ret() :: {'ok',
                      ExprList :: [erl_parse:abstract_expr()],
                      EndLocation :: location()}
                   | {'eof', EndLocation :: location()}
                   | {'error',
                      ErrorInfo :: erl_scan:error_info()
                                 | erl_parse:error_info(),
                      ErrorLocation :: location()}
                   | server_no_data().

-spec parse_erl_exprs(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: parse_ret().

parse_erl_exprs(Prompt) ->
    parse_erl_exprs(default_input(), Prompt, 1).

-spec parse_erl_exprs(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: parse_ret().

parse_erl_exprs(Io, Prompt) ->
    parse_erl_exprs(Io, Prompt, 1).

-spec parse_erl_exprs(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: parse_ret().

parse_erl_exprs(Io, Prompt, Pos0) ->
    parse_erl_exprs(Io, Prompt, Pos0, []).

-spec parse_erl_exprs(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: parse_ret().

parse_erl_exprs(Io, Prompt, Pos0, Options) ->
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}) of
	{ok,Toks,EndPos} ->
	    case erl_parse:parse_exprs(Toks) of
		{ok,Exprs} -> {ok,Exprs,EndPos};
		{error,E} -> {error,E,EndPos}
	    end;
	Other ->
	    Other
    end.

-type parse_form_ret() :: {'ok',
                           AbsForm :: erl_parse:abstract_form(),
                           EndLocation :: location()}
                        | {'eof', EndLocation :: location()}
                        | {'error',
                           ErrorInfo :: erl_scan:error_info()
                                      | erl_parse:error_info(),
                           ErrorLocation :: location()}
                        | server_no_data().

-spec parse_erl_form(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: parse_form_ret().

parse_erl_form(Prompt) ->
    parse_erl_form(default_input(), Prompt, 1).

-spec parse_erl_form(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: parse_form_ret().

parse_erl_form(Io, Prompt) ->
    parse_erl_form(Io, Prompt, 1).

-spec parse_erl_form(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: parse_form_ret().

parse_erl_form(Io, Prompt, Pos0) ->
    parse_erl_form(Io, Prompt, Pos0, []).

-spec parse_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: parse_form_ret().

parse_erl_form(Io, Prompt, Pos0, Options) ->
    Args = [Pos0, Options],
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of
	{ok,Toks,EndPos} ->
	    case erl_parse:parse_form(Toks) of
		{ok,Exprs} -> {ok,Exprs,EndPos};
		{error,E} -> {error,E,EndPos}
	    end;
	Other ->
	    Other
    end.

%% Miscellaneous functions.

request(Request) ->
    request(default_output(), Request).

request(Name, Request) ->
    request(Name, Request, error).
request(standard_io, Request, ErrorTag) ->
    request(group_leader(), Request, ErrorTag);
request(Pid, Request, ErrorTag) when is_pid(Pid) ->
    execute_request(Pid, io_request(Pid, Request), ErrorTag);
request(Name, Request, ErrorTag) when is_atom(Name) ->
    case whereis(Name) of
	undefined ->
	    {ErrorTag, arguments};
	Pid ->
	    request(Pid, Request, ErrorTag)
    end.

execute_request(Pid, {Convert,Converted}, ErrorTag) ->
    Mref = erlang:monitor(process, Pid),
    Pid ! {io_request,self(),Mref,Converted},

    receive
	{io_reply, Mref, Reply} ->
	    erlang:demonitor(Mref, [flush]),
	    if
		Convert ->
		    convert_binaries(Reply);
		true ->
		    Reply
	    end;
	{'DOWN', Mref, _, _, _} ->
	    receive
		{'EXIT', Pid, _What} -> true
	    after 0 -> true
	    end,
	    {ErrorTag,terminated}
    end.

requests(Requests) ->				%Requests as atomic action
    requests(default_output(), Requests).

requests(standard_io, Requests) ->              %Requests as atomic action
    requests(group_leader(), Requests);
requests(Pid, Requests) when is_pid(Pid) ->
    {Convert, Converted} = io_requests(Pid, Requests),
    execute_request(Pid,{Convert,{requests,Converted}},error);
requests(Name, Requests) when is_atom(Name) ->
    case whereis(Name) of
	undefined ->
	    {error, arguments};
	Pid ->
	    requests(Pid, Requests)
    end.


default_input() ->
    group_leader().

default_output() ->
    group_leader().

%% io_requests(Requests)
%%  Transform requests into correct i/o server messages. Only handle the
%%  one we KNOW must be changed, others, including incorrect ones, are
%%  passed straight through. Perform a flatten on the request list.

io_requests(Pid, Rs) ->
    io_requests(Pid, Rs, [], []).

io_requests(Pid, [{requests,Rs1}|Rs], Cont, Tail) ->
    io_requests(Pid, Rs1, [Rs|Cont], Tail);
io_requests(Pid, [R], [], _Tail) ->
    {Conv,Request} = io_request(Pid, R),
    {Conv,[Request]};
io_requests(Pid, [R|Rs], Cont, Tail) ->
    {_,Request} = io_request(Pid, R),
    {Conv,Requests} = io_requests(Pid, Rs, Cont, Tail),
    {Conv,[Request|Requests]};
io_requests(Pid, [], [Rs|Cont], Tail) ->
    io_requests(Pid, Rs, Cont, Tail);
io_requests(_Pid, [], [], _Tail) -> 
    {false,[]}.

bc_req(Pid, Req0, MaybeConvert) ->
    case net_kernel:dflag_unicode_io(Pid) of
	true ->
	    %% The most common case. A modern i/o server.
	    {false,Req0};
	false ->
	    %% Backward compatibility only. Unlikely to ever happen.
	    case tuple_to_list(Req0) of
		[Op,_Enc] ->
		    {MaybeConvert,Op};
		[Op,_Enc|T] ->
		    Req = list_to_tuple([Op|T]),
		    {MaybeConvert,Req}
	    end
    end.

io_request(Pid, {write,Term}) ->
    bc_req(Pid,{put_chars,unicode,io_lib,write,[Term]},false);
io_request(Pid, {format,Format,Args}) ->
    bc_req(Pid,{put_chars,unicode,io_lib,format,[Format,Args]},false);
io_request(Pid, {fwrite,Format,Args}) ->
    bc_req(Pid,{put_chars,unicode,io_lib,fwrite,[Format,Args]},false);
io_request(Pid, nl) ->
    bc_req(Pid,{put_chars,unicode,io_lib:nl()},false);
io_request(Pid, {put_chars,Enc,Chars}=Request0) 
  when is_list(Chars), node(Pid) =:= node() ->
    %% Convert to binary data if the I/O server is guaranteed to be new
    Request =
	case catch unicode:characters_to_binary(Chars,Enc) of
	    Binary when is_binary(Binary) ->
		{put_chars,Enc,Binary};
	    _ ->
		Request0
	end,
    {false,Request};
io_request(Pid, {put_chars,Enc,Chars}=Request0) 
  when is_list(Chars) ->
    case net_kernel:dflag_unicode_io(Pid) of
	true ->
	    case catch unicode:characters_to_binary(Chars,Enc,unicode) of
		Binary when is_binary(Binary) ->
		    {false,{put_chars,unicode,Binary}};
		_ ->
		    {false,Request0}
	    end;
	false ->
	    %% Convert back to old style put_chars message...
	    case catch unicode:characters_to_binary(Chars,Enc,latin1) of
		Binary when is_binary(Binary) ->
		    {false,{put_chars,Binary}};
		_ ->
		    {false,{put_chars,Chars}}
	    end
    end;
io_request(Pid, {fread,Prompt,Format}) ->
    bc_req(Pid,{get_until,unicode,Prompt,io_lib,fread,[Format]},true);
io_request(Pid, {get_until,Enc,Prompt,M,F,A}) ->
    bc_req(Pid,{get_until,Enc,Prompt,M,F,A},true);
io_request(Pid, {get_chars,Enc,Prompt,N}) ->
    bc_req(Pid,{get_chars,Enc,Prompt,N},true);
io_request(Pid, {get_line,Enc,Prompt}) ->
    bc_req(Pid,{get_line,Enc,Prompt},true);
io_request(Pid, {get_password,Enc}) ->
    bc_req(Pid,{get_password, Enc},true);
io_request(_Pid, R) ->				%Pass this straight through
    {false,R}.

convert_binaries(Bin) when is_binary(Bin) ->
    unicode:characters_to_binary(Bin,latin1,unicode);
convert_binaries(Else) ->
    Else.


 %CopyrightBegin%


 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     -2.0

 Unless required by applicable law or agreed to in writing, software
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.

 %CopyrightEnd%

 Implemented in native code
-------------------------------------------------------------------------
-------------------------------------------------------------------------
 Needs to be inlined for error_info to be correct
 We differentiate between errors that are created by this module
 and the errors we get from the Device

 User interface.

 Request what the user considers printable characters
 Put chars takes mixed *unicode* list from R13 onwards.
 Formatted writing and reading.
 Scanning Erlang code.
 Parsing Erlang code.
 Miscellaneous functions.
Requests as atomic action
Requests as atomic action
 io_requests(Requests)
  Transform requests into correct i/o server messages. Only handle the
  one we KNOW must be changed, others, including incorrect ones, are
  passed straight through. Perform a flatten on the request list.
 The most common case. A modern i/o server.
 Backward compatibility only. Unlikely to ever happen.
 Convert to binary data if the I/O server is guaranteed to be new
 Convert back to old style put_chars message...
Pass this straight through
  Copyright Ericsson AB 1996 - 2021 . All Rights Reserved .
  Licensed under the Apache License , Version 2.0 ( the " License " ) ;
  distributed under the License is distributed on an " AS IS " BASIS ,
-module(io).

-export([put_chars/1,put_chars/2,nl/0,nl/1,
	 get_chars/2,get_chars/3,get_line/1,get_line/2,
	 get_password/0, get_password/1,
	 setopts/1, setopts/2, getopts/0, getopts/1]).
-export([write/1,write/2,read/1,read/2,read/3,read/4]).
-export([columns/0,columns/1,rows/0,rows/1]).
-export([fwrite/1,fwrite/2,fwrite/3,fread/2,fread/3,
	 format/1,format/2,format/3]).
-export([scan_erl_exprs/1,scan_erl_exprs/2,scan_erl_exprs/3,scan_erl_exprs/4,
	 scan_erl_form/1,scan_erl_form/2,scan_erl_form/3,scan_erl_form/4,
	 parse_erl_exprs/1,parse_erl_exprs/2,parse_erl_exprs/3,
         parse_erl_exprs/4,parse_erl_form/1,parse_erl_form/2,
         parse_erl_form/3,parse_erl_form/4]).
-export([request/1,request/2,requests/1,requests/2]).
-export([printable_range/0]).

-export_type([device/0, format/0, server_no_data/0]).


-type device() :: atom() | pid().
-type prompt() :: atom() | unicode:chardata().

  ErrorDescription is whatever the I / O - server sends .
-type server_no_data() :: {'error', ErrorDescription :: term()} | 'eof'.

-type location() :: erl_anno:location().

-compile({inline,[o_request/2]}).
o_request(Function, OrigArgs) ->
    {Io, Request} =
        if
            Function =:= format; Function =:= fwrite ->
                case OrigArgs of
                    [Format] ->
                        {default_output(), {format, Format, []}};
                    [Format, Args] ->
                        {default_output(), {format, Format, Args}};
                    [D, Format, Args] ->
                        {D, {format, Format, Args}}
                end;
            Function =:= put_chars ->
                case OrigArgs of
                    [Chars] ->
                        {default_output(), {put_chars, unicode, Chars}};
                    [D, Chars] ->
                        {D, {put_chars, unicode, Chars}};
                    [D, Encoding, Chars] ->
                        {D, {put_chars, Encoding, Chars}}
                end;
            Function =:= nl ->
                case OrigArgs of
                    [] ->
                        {default_output(), nl};
                    [D] ->
                        {D, nl}
                end;
            Function =:= write ->
                case OrigArgs of
                    [Term] ->
                        {default_output(), {write, Term}};
                    [D, Term] ->
                        {D, {write, Term}}
                end
        end,
    ErrorRef = make_ref(),
    case request(Io, Request, ErrorRef) of
        {ErrorRef, Reason} ->
            erlang:error(conv_reason(Reason), OrigArgs,
                         [{error_info,#{cause => {?MODULE, Reason},
                                        module => erl_stdlib_errors}}]);
        {error, Reason} ->
            erlang:error(conv_reason(Reason), OrigArgs,
                         [{error_info,#{cause => {device, Reason},
                                        module => erl_stdlib_errors}}]);
        Other ->
            Other
    end.


-spec printable_range() -> 'unicode' | 'latin1'.
printable_range() ->
    erlang:nif_error(undefined).

-spec put_chars(CharData) -> 'ok' when
      CharData :: unicode:chardata().

put_chars(Chars) ->
    o_request(?FUNCTION_NAME, [Chars]).

-spec put_chars(IoDevice, CharData) -> 'ok' when
      IoDevice :: device(),
      CharData :: unicode:chardata().

put_chars(Io, Chars) ->
    o_request(?FUNCTION_NAME, [Io, Chars]).

-spec nl() -> 'ok'.

nl() ->
    o_request(?FUNCTION_NAME, []).

-spec nl(IoDevice) -> 'ok' when
      IoDevice :: device().

nl(Io) ->
    o_request(?FUNCTION_NAME, [Io]).

-spec columns() -> {'ok', pos_integer()} | {'error', 'enotsup'}.

columns() ->
    columns(default_output()).

-spec columns(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when
      IoDevice :: device().

columns(Io) ->
    case request(Io, {get_geometry,columns}) of
	N  when is_integer(N), N > 0 ->
	    {ok,N};
	_ ->
	    {error,enotsup}
    end.
	    
-spec rows() -> {'ok', pos_integer()} | {'error', 'enotsup'}.

rows() ->
    rows(default_output()).

-spec rows(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when
      IoDevice :: device().

rows(Io) ->
    case request(Io,{get_geometry,rows}) of
	N  when is_integer(N), N > 0 ->
	    {ok,N};
	_ ->
	    {error,enotsup}
    end.    

-spec get_chars(Prompt, Count) -> Data | server_no_data() when
      Prompt :: prompt(),
      Count :: non_neg_integer(),
      Data :: string() | unicode:unicode_binary().

get_chars(Prompt, N) ->
    get_chars(default_input(), Prompt, N).

-spec get_chars(IoDevice, Prompt, Count) -> Data | server_no_data() when
      IoDevice :: device(),
      Prompt :: prompt(),
      Count :: non_neg_integer(),
      Data :: string() | unicode:unicode_binary().

get_chars(Io, Prompt, N) when is_integer(N), N >= 0 ->
    request(Io, {get_chars,unicode,Prompt,N}).

-spec get_line(Prompt) -> Data | server_no_data() when
      Prompt :: prompt(),
      Data :: string() | unicode:unicode_binary().

get_line(Prompt) ->
    get_line(default_input(), Prompt).

-spec get_line(IoDevice, Prompt) -> Data | server_no_data() when
      IoDevice :: device(),
      Prompt :: prompt(),
      Data :: string() | unicode:unicode_binary().

get_line(Io, Prompt) ->
    request(Io, {get_line,unicode,Prompt}).

get_password() ->
    get_password(default_input()).

get_password(Io) ->
    request(Io, {get_password,unicode}).

-type encoding()   :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | 'utf32'
                    | {'utf16', 'big' | 'little'} | {'utf32','big' | 'little'}.
-type expand_fun() :: fun((term()) -> {'yes'|'no', string(), [string(), ...]}).
-type opt_pair()   :: {'binary', boolean()}
                    | {'echo', boolean()}
                    | {'expand_fun', expand_fun()}
                    | {'encoding', encoding()}.

-spec getopts() -> [opt_pair()] | {'error', Reason} when
      Reason :: term().

getopts() ->
    getopts(default_input()).

-spec getopts(IoDevice) -> [opt_pair()] | {'error', Reason} when
      IoDevice :: device(),
      Reason :: term().

getopts(Io) ->
    request(Io, getopts).

-type setopt() :: 'binary' | 'list' | opt_pair().

-spec setopts(Opts) -> 'ok' | {'error', Reason} when
      Opts :: [setopt()],
      Reason :: term().

setopts(Opts) ->
    setopts(default_input(), Opts).

-spec setopts(IoDevice, Opts) -> 'ok' | {'error', Reason} when
      IoDevice :: device(),
      Opts :: [setopt()],
      Reason :: term().

setopts(Io, Opts) ->
    request(Io, {setopts, Opts}).

  Writing and reading Erlang terms .

-spec write(Term) -> 'ok' when
      Term :: term().

write(Term) ->
    o_request(?FUNCTION_NAME, [Term]).

-spec write(IoDevice, Term) -> 'ok' when
      IoDevice :: device(),
      Term :: term().

write(Io, Term) ->
    o_request(?FUNCTION_NAME, [Io, Term]).


-spec read(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: {'ok', Term :: term()}
              | server_no_data()
              | {'error', ErrorInfo},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Prompt) ->
    read(default_input(), Prompt).

-spec read(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: {'ok', Term :: term()}
              | server_no_data()
              | {'error', ErrorInfo},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Io, Prompt) ->
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of
	{ok,Toks,_EndLine} ->
	    erl_parse:parse_term(Toks);
	{error,E,_EndLine} ->
	    {error,E};
	{eof,_EndLine} ->
	    eof;
	Other ->
	    Other
    end.

-spec read(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: {'ok', Term :: term(), EndLocation :: location()}
              | {'eof', EndLocation :: location()}
              | server_no_data()
              | {'error', ErrorInfo, ErrorLocation :: location()},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Io, Prompt, Pos0) ->
    read(Io, Prompt, Pos0, []).

-spec read(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: {'ok', Term :: term(), EndLocation :: location()}
              | {'eof', EndLocation :: location()}
              | server_no_data()
              | {'error', ErrorInfo, ErrorLocation :: location()},
      ErrorInfo :: erl_scan:error_info() | erl_parse:error_info().

read(Io, Prompt, Pos0, Options) ->
    Args = [Pos0,Options],
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of
	{ok,Toks,EndLocation} ->
            case erl_parse:parse_term(Toks) of
                {ok,Term} -> {ok,Term,EndLocation};
                {error,ErrorInfo} -> {error,ErrorInfo,EndLocation}
            end;
	{error,_E,_EndLocation} = Error ->
	    Error;
	{eof,_EndLocation} = Eof ->
	    Eof;
	Other ->
	    Other
    end.


conv_reason(arguments) -> badarg;
conv_reason(terminated) -> terminated;
conv_reason({no_translation,_,_}) -> no_translation;
conv_reason(_Reason) -> badarg.

-type format() :: atom() | string() | binary().

-spec fwrite(Format) -> 'ok' when
      Format :: format().

fwrite(Format) ->
    o_request(?FUNCTION_NAME, [Format]).

-spec fwrite(Format, Data) -> 'ok' when
      Format :: format(),
      Data :: [term()].

fwrite(Format, Args) ->
    o_request(?FUNCTION_NAME, [Format, Args]).

-spec fwrite(IoDevice, Format, Data) -> 'ok' when
      IoDevice :: device(),
      Format :: format(),
      Data :: [term()].

fwrite(Io, Format, Args) ->
    o_request(?FUNCTION_NAME, [Io, Format, Args]).

-spec fread(Prompt, Format) -> Result when
      Prompt :: prompt(),
      Format :: format(),
      Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}.

fread(Prompt, Format) ->
    fread(default_input(), Prompt, Format).

-spec fread(IoDevice, Prompt, Format) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Format :: format(),
      Result :: {'ok', Terms :: [term()]}
              | {'error', {'fread', FreadError :: io_lib:fread_error()}}
              | server_no_data().

fread(Io, Prompt, Format) ->
    request(Io, {fread,Prompt,Format}).

-spec format(Format) -> 'ok' when
      Format :: format().
format(Format) ->
    o_request(?FUNCTION_NAME, [Format]).

-spec format(Format, Data) -> 'ok' when
      Format :: format(),
      Data :: [term()].

format(Format, Args) ->
    o_request(?FUNCTION_NAME, [Format, Args]).

-spec format(IoDevice, Format, Data) -> 'ok' when
      IoDevice :: device(),
      Format :: format(),
      Data :: [term()].

format(Io, Format, Args) ->
    o_request(?FUNCTION_NAME, [Io, Format, Args]).


-spec scan_erl_exprs(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().
 
scan_erl_exprs(Prompt) ->
    scan_erl_exprs(default_input(), Prompt, 1).

-spec scan_erl_exprs(Device, Prompt) -> Result when
      Device :: device(),
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_exprs(Io, Prompt) ->
    scan_erl_exprs(Io, Prompt, 1).

-spec scan_erl_exprs(Device, Prompt, StartLocation) -> Result when
      Device :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_exprs(Io, Prompt, Pos0) ->
    scan_erl_exprs(Io, Prompt, Pos0, []).

-spec scan_erl_exprs(Device, Prompt, StartLocation, Options) -> Result when
      Device :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_exprs(Io, Prompt, Pos0, Options) ->
    request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}).

-spec scan_erl_form(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Prompt) ->
    scan_erl_form(default_input(), Prompt, 1).

-spec scan_erl_form(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Io, Prompt) ->
    scan_erl_form(Io, Prompt, 1).

-spec scan_erl_form(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Io, Prompt, Pos0) ->
    scan_erl_form(Io, Prompt, Pos0, []).

-spec scan_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: erl_scan:tokens_result() | server_no_data().

scan_erl_form(Io, Prompt, Pos0, Options) ->
    request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}).


-type parse_ret() :: {'ok',
                      ExprList :: [erl_parse:abstract_expr()],
                      EndLocation :: location()}
                   | {'eof', EndLocation :: location()}
                   | {'error',
                      ErrorInfo :: erl_scan:error_info()
                                 | erl_parse:error_info(),
                      ErrorLocation :: location()}
                   | server_no_data().

-spec parse_erl_exprs(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: parse_ret().

parse_erl_exprs(Prompt) ->
    parse_erl_exprs(default_input(), Prompt, 1).

-spec parse_erl_exprs(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: parse_ret().

parse_erl_exprs(Io, Prompt) ->
    parse_erl_exprs(Io, Prompt, 1).

-spec parse_erl_exprs(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: parse_ret().

parse_erl_exprs(Io, Prompt, Pos0) ->
    parse_erl_exprs(Io, Prompt, Pos0, []).

-spec parse_erl_exprs(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: parse_ret().

parse_erl_exprs(Io, Prompt, Pos0, Options) ->
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0,Options]}) of
	{ok,Toks,EndPos} ->
	    case erl_parse:parse_exprs(Toks) of
		{ok,Exprs} -> {ok,Exprs,EndPos};
		{error,E} -> {error,E,EndPos}
	    end;
	Other ->
	    Other
    end.

-type parse_form_ret() :: {'ok',
                           AbsForm :: erl_parse:abstract_form(),
                           EndLocation :: location()}
                        | {'eof', EndLocation :: location()}
                        | {'error',
                           ErrorInfo :: erl_scan:error_info()
                                      | erl_parse:error_info(),
                           ErrorLocation :: location()}
                        | server_no_data().

-spec parse_erl_form(Prompt) -> Result when
      Prompt :: prompt(),
      Result :: parse_form_ret().

parse_erl_form(Prompt) ->
    parse_erl_form(default_input(), Prompt, 1).

-spec parse_erl_form(IoDevice, Prompt) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      Result :: parse_form_ret().

parse_erl_form(Io, Prompt) ->
    parse_erl_form(Io, Prompt, 1).

-spec parse_erl_form(IoDevice, Prompt, StartLocation) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Result :: parse_form_ret().

parse_erl_form(Io, Prompt, Pos0) ->
    parse_erl_form(Io, Prompt, Pos0, []).

-spec parse_erl_form(IoDevice, Prompt, StartLocation, Options) -> Result when
      IoDevice :: device(),
      Prompt :: prompt(),
      StartLocation :: location(),
      Options :: erl_scan:options(),
      Result :: parse_form_ret().

parse_erl_form(Io, Prompt, Pos0, Options) ->
    Args = [Pos0, Options],
    case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,Args}) of
	{ok,Toks,EndPos} ->
	    case erl_parse:parse_form(Toks) of
		{ok,Exprs} -> {ok,Exprs,EndPos};
		{error,E} -> {error,E,EndPos}
	    end;
	Other ->
	    Other
    end.


request(Request) ->
    request(default_output(), Request).

request(Name, Request) ->
    request(Name, Request, error).
request(standard_io, Request, ErrorTag) ->
    request(group_leader(), Request, ErrorTag);
request(Pid, Request, ErrorTag) when is_pid(Pid) ->
    execute_request(Pid, io_request(Pid, Request), ErrorTag);
request(Name, Request, ErrorTag) when is_atom(Name) ->
    case whereis(Name) of
	undefined ->
	    {ErrorTag, arguments};
	Pid ->
	    request(Pid, Request, ErrorTag)
    end.

execute_request(Pid, {Convert,Converted}, ErrorTag) ->
    Mref = erlang:monitor(process, Pid),
    Pid ! {io_request,self(),Mref,Converted},

    receive
	{io_reply, Mref, Reply} ->
	    erlang:demonitor(Mref, [flush]),
	    if
		Convert ->
		    convert_binaries(Reply);
		true ->
		    Reply
	    end;
	{'DOWN', Mref, _, _, _} ->
	    receive
		{'EXIT', Pid, _What} -> true
	    after 0 -> true
	    end,
	    {ErrorTag,terminated}
    end.

    requests(default_output(), Requests).

    requests(group_leader(), Requests);
requests(Pid, Requests) when is_pid(Pid) ->
    {Convert, Converted} = io_requests(Pid, Requests),
    execute_request(Pid,{Convert,{requests,Converted}},error);
requests(Name, Requests) when is_atom(Name) ->
    case whereis(Name) of
	undefined ->
	    {error, arguments};
	Pid ->
	    requests(Pid, Requests)
    end.


default_input() ->
    group_leader().

default_output() ->
    group_leader().


io_requests(Pid, Rs) ->
    io_requests(Pid, Rs, [], []).

io_requests(Pid, [{requests,Rs1}|Rs], Cont, Tail) ->
    io_requests(Pid, Rs1, [Rs|Cont], Tail);
io_requests(Pid, [R], [], _Tail) ->
    {Conv,Request} = io_request(Pid, R),
    {Conv,[Request]};
io_requests(Pid, [R|Rs], Cont, Tail) ->
    {_,Request} = io_request(Pid, R),
    {Conv,Requests} = io_requests(Pid, Rs, Cont, Tail),
    {Conv,[Request|Requests]};
io_requests(Pid, [], [Rs|Cont], Tail) ->
    io_requests(Pid, Rs, Cont, Tail);
io_requests(_Pid, [], [], _Tail) -> 
    {false,[]}.

bc_req(Pid, Req0, MaybeConvert) ->
    case net_kernel:dflag_unicode_io(Pid) of
	true ->
	    {false,Req0};
	false ->
	    case tuple_to_list(Req0) of
		[Op,_Enc] ->
		    {MaybeConvert,Op};
		[Op,_Enc|T] ->
		    Req = list_to_tuple([Op|T]),
		    {MaybeConvert,Req}
	    end
    end.

io_request(Pid, {write,Term}) ->
    bc_req(Pid,{put_chars,unicode,io_lib,write,[Term]},false);
io_request(Pid, {format,Format,Args}) ->
    bc_req(Pid,{put_chars,unicode,io_lib,format,[Format,Args]},false);
io_request(Pid, {fwrite,Format,Args}) ->
    bc_req(Pid,{put_chars,unicode,io_lib,fwrite,[Format,Args]},false);
io_request(Pid, nl) ->
    bc_req(Pid,{put_chars,unicode,io_lib:nl()},false);
io_request(Pid, {put_chars,Enc,Chars}=Request0) 
  when is_list(Chars), node(Pid) =:= node() ->
    Request =
	case catch unicode:characters_to_binary(Chars,Enc) of
	    Binary when is_binary(Binary) ->
		{put_chars,Enc,Binary};
	    _ ->
		Request0
	end,
    {false,Request};
io_request(Pid, {put_chars,Enc,Chars}=Request0) 
  when is_list(Chars) ->
    case net_kernel:dflag_unicode_io(Pid) of
	true ->
	    case catch unicode:characters_to_binary(Chars,Enc,unicode) of
		Binary when is_binary(Binary) ->
		    {false,{put_chars,unicode,Binary}};
		_ ->
		    {false,Request0}
	    end;
	false ->
	    case catch unicode:characters_to_binary(Chars,Enc,latin1) of
		Binary when is_binary(Binary) ->
		    {false,{put_chars,Binary}};
		_ ->
		    {false,{put_chars,Chars}}
	    end
    end;
io_request(Pid, {fread,Prompt,Format}) ->
    bc_req(Pid,{get_until,unicode,Prompt,io_lib,fread,[Format]},true);
io_request(Pid, {get_until,Enc,Prompt,M,F,A}) ->
    bc_req(Pid,{get_until,Enc,Prompt,M,F,A},true);
io_request(Pid, {get_chars,Enc,Prompt,N}) ->
    bc_req(Pid,{get_chars,Enc,Prompt,N},true);
io_request(Pid, {get_line,Enc,Prompt}) ->
    bc_req(Pid,{get_line,Enc,Prompt},true);
io_request(Pid, {get_password,Enc}) ->
    bc_req(Pid,{get_password, Enc},true);
    {false,R}.

convert_binaries(Bin) when is_binary(Bin) ->
    unicode:characters_to_binary(Bin,latin1,unicode);
convert_binaries(Else) ->
    Else.
