%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
-module(standard_error).
-behaviour(supervisor_bridge).
%% Basic standard i/o server for user interface port.
-export([start_link/0, init/1, terminate/2]).
-define(NAME, standard_error).
-define(PROCNAME_SUP, standard_error_sup).
%% Defines for control ops
-define(CTRL_OP_GET_WINSIZE,100).
%%
%% The basic server and start-up.
%%
-spec start_link() -> 'ignore' | {'error',term()} | {'ok',pid()}.
start_link() ->
supervisor_bridge:start_link({local, ?PROCNAME_SUP}, ?MODULE, []).
-spec terminate(term(), pid()) -> 'ok'.
terminate(_Reason,Pid) ->
(catch exit(Pid,kill)),
ok.
-spec init([]) -> {'error','no_stderror'} | {'ok',pid(),pid()}.
init([]) ->
case (catch start_port([out,binary])) of
Pid when is_pid(Pid) ->
{ok,Pid,Pid};
_ ->
{error,no_stderror}
end.
start_port(PortSettings) ->
Id = spawn(fun () -> server({fd,2,2}, PortSettings) end),
register(?NAME, Id),
Id.
server(PortName,PortSettings) ->
process_flag(trap_exit, true),
Port = open_port(PortName,PortSettings),
run(Port).
run(P) ->
put(encoding, latin1),
server_loop(P).
server_loop(Port) ->
receive
{io_request,From,ReplyAs,Request} when is_pid(From) ->
_ = do_io_request(Request, From, ReplyAs, Port),
server_loop(Port);
{'EXIT',Port,badsig} -> % Ignore badsig errors
server_loop(Port);
{'EXIT',Port,What} -> % Port has exited
exit(What);
_Other -> % Ignore other messages
server_loop(Port)
end.
get_fd_geometry(Port) ->
case (catch port_control(Port,?CTRL_OP_GET_WINSIZE,[])) of
List when length(List) =:= 8 ->
<<W:32/native,H:32/native>> = list_to_binary(List),
{W,H};
_ ->
error
end.
%% NewSaveBuffer = io_request(Request, FromPid, ReplyAs, Port, SaveBuffer)
do_io_request(Req, From, ReplyAs, Port) ->
{_Status,Reply} = io_request(Req, Port),
io_reply(From, ReplyAs, Reply).
%% New in R13B
% Wide characters (Unicode)
io_request({put_chars,Encoding,Chars}, Port) -> % Binary new in R9C
put_chars(wrap_characters_to_binary(Chars, Encoding, get(encoding)), Port);
io_request({put_chars,Encoding,Mod,Func,Args}, Port) ->
Result = case catch apply(Mod,Func,Args) of
Data when is_list(Data); is_binary(Data) ->
wrap_characters_to_binary(Data, Encoding, get(encoding));
Undef ->
Undef
end,
put_chars(Result, Port);
%% BC if called from pre-R13 node
io_request({put_chars,Chars}, Port) ->
io_request({put_chars,latin1,Chars}, Port);
io_request({put_chars,Mod,Func,Args}, Port) ->
io_request({put_chars,latin1,Mod,Func,Args}, Port);
%% New in R12
io_request({get_geometry,columns},Port) ->
case get_fd_geometry(Port) of
{W,_H} ->
{ok,W};
_ ->
{error,{error,enotsup}}
end;
io_request({get_geometry,rows},Port) ->
case get_fd_geometry(Port) of
{_W,H} ->
{ok,H};
_ ->
{error,{error,enotsup}}
end;
io_request(getopts, _Port) ->
getopts();
io_request({setopts,Opts}, _Port) when is_list(Opts) ->
setopts(Opts);
io_request({requests,Reqs}, Port) ->
io_requests(Reqs, {ok,ok}, Port);
io_request(R, _Port) -> %Unknown request
{error,{error,{request,R}}}. %Ignore but give error (?)
%% Status = io_requests(RequestList, PrevStat, Port)
%% Process a list of output requests as long as the previous status is 'ok'.
io_requests([R|Rs], {ok,_Res}, Port) ->
io_requests(Rs, io_request(R, Port), Port);
io_requests([_|_], Error, _) ->
Error;
io_requests([], Stat, _) ->
Stat.
%% put_port(DeepList, Port)
%% Take a deep list of characters, flatten and output them to the
%% port.
put_port(List, Port) ->
send_port(Port, {command, List}).
%% send_port(Port, Command)
send_port(Port, Command) ->
Port ! {self(),Command}.
%% io_reply(From, ReplyAs, Reply)
%% The function for sending i/o command acknowledgement.
%% The ACK contains the return value.
io_reply(From, ReplyAs, Reply) ->
From ! {io_reply,ReplyAs,Reply}.
%% put_chars
put_chars(Chars, Port) when is_binary(Chars) ->
_ = put_port(Chars, Port),
{ok,ok};
put_chars(Chars, Port) ->
case catch list_to_binary(Chars) of
Binary when is_binary(Binary) ->
put_chars(Binary, Port);
_ ->
{error,{error,put_chars}}
end.
%% setopts
setopts(Opts0) ->
Opts = expand_encoding(Opts0),
case check_valid_opts(Opts) of
true ->
do_setopts(Opts);
false ->
{error,{error,enotsup}}
end.
check_valid_opts([]) ->
true;
check_valid_opts([{encoding,Valid}|T]) when Valid =:= unicode;
Valid =:= utf8; Valid =:= latin1 ->
check_valid_opts(T);
check_valid_opts(_) ->
false.
expand_encoding([]) ->
[];
expand_encoding([latin1 | T]) ->
[{encoding,latin1} | expand_encoding(T)];
expand_encoding([unicode | T]) ->
[{encoding,unicode} | expand_encoding(T)];
expand_encoding([H|T]) ->
[H|expand_encoding(T)].
do_setopts(Opts) ->
case proplists:get_value(encoding, Opts) of
Valid when Valid =:= unicode; Valid =:= utf8 ->
put(encoding, unicode);
latin1 ->
put(encoding, latin1);
undefined ->
ok
end,
{ok,ok}.
getopts() ->
Uni = {encoding,get(encoding)},
{ok,[Uni]}.
wrap_characters_to_binary(Chars,From,To) ->
TrNl = (whereis(user_drv) =/= undefined),
Limit = case To of
latin1 ->
255;
_Else ->
16#10ffff
end,
unicode:characters_to_binary(
[ case X of
$\n ->
if
TrNl ->
"\r\n";
true ->
$\n
end;
High when High > Limit ->
["\\x{",erlang:integer_to_list(X, 16),$}];
Ordinary ->
Ordinary
end || X <- unicode:characters_to_list(Chars,From) ],unicode,To).