1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2009-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(standard_error).
21-behaviour(supervisor_bridge).
22
23%% Basic standard i/o server for user interface port.
24-export([start_link/0, init/1, terminate/2]).
25
26-define(NAME, standard_error).
27-define(PROCNAME_SUP, standard_error_sup).
28
29%% Defines for control ops
30-define(ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER, 16#018b0900).
31-define(CTRL_OP_GET_WINSIZE, (100 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)).
32
33%%
34%% The basic server and start-up.
35%%
36-spec start_link() -> 'ignore' | {'error',term()} | {'ok',pid()}.
37
38start_link() ->
39    supervisor_bridge:start_link({local, ?PROCNAME_SUP}, ?MODULE, []).
40
41-spec terminate(term(), pid()) -> 'ok'.
42
43terminate(_Reason,Pid) ->
44    (catch exit(Pid,kill)),
45    ok.
46
47-spec init([]) -> {'error','no_stderror'} | {'ok',pid(),pid()}.
48
49init([]) ->
50    case (catch start_port([out,binary])) of
51	Pid when is_pid(Pid) ->
52	    {ok,Pid,Pid};
53	_ ->
54	    {error,no_stderror}
55    end.
56
57start_port(PortSettings) ->
58    Id = spawn(fun () -> server({fd,2,2}, PortSettings) end),
59    register(?NAME, Id),
60    Id.
61
62server(PortName,PortSettings) ->
63    process_flag(trap_exit, true),
64    Port = open_port(PortName,PortSettings),
65    run(Port).
66
67run(P) ->
68    put(encoding, latin1),
69    server_loop(P).
70
71server_loop(Port) ->
72    receive
73	{io_request,From,ReplyAs,Request} when is_pid(From) ->
74	    _ = do_io_request(Request, From, ReplyAs, Port),
75	    server_loop(Port);
76	{'EXIT',Port,badsig} ->			% Ignore badsig errors
77	    server_loop(Port);
78	{'EXIT',Port,What} ->			% Port has exited
79	    exit(What);
80	_Other ->				% Ignore other messages
81	    server_loop(Port)
82    end.
83
84get_fd_geometry(Port) ->
85    case (catch port_control(Port,?CTRL_OP_GET_WINSIZE,[])) of
86	List when length(List) =:= 8 ->
87	    <<W:32/native,H:32/native>> = list_to_binary(List),
88	    {W,H};
89	_ ->
90	    error
91    end.
92
93%% NewSaveBuffer = io_request(Request, FromPid, ReplyAs, Port, SaveBuffer)
94
95do_io_request(Req, From, ReplyAs, Port) ->
96    {_Status,Reply}  = io_request(Req, Port),
97    io_reply(From, ReplyAs, Reply).
98
99%% New in R13B
100%% Encoding option (unicode/latin1)
101io_request({put_chars,unicode,Chars}, Port) ->
102    case wrap_characters_to_binary(Chars, unicode, get(encoding)) of
103        error ->
104            {error,{error,put_chars}};
105        Bin ->
106            put_chars(Bin, Port)
107    end;
108io_request({put_chars,unicode,Mod,Func,Args}, Port) ->
109    case catch apply(Mod, Func, Args) of
110        Data when is_list(Data); is_binary(Data) ->
111            case wrap_characters_to_binary(Data, unicode, get(encoding)) of
112                Bin when is_binary(Bin) ->
113                    put_chars(Bin, Port);
114                error ->
115                    {error,{error,put_chars}}
116            end;
117        _ ->
118            {error,{error,put_chars}}
119    end;
120io_request({put_chars,latin1,Chars}, Port) ->
121    case catch unicode:characters_to_binary(Chars, latin1, get(encoding)) of
122        Data when is_binary(Data) ->
123            put_chars(Data, Port);
124        _ ->
125            {error,{error,put_chars}}
126    end;
127io_request({put_chars,latin1,Mod,Func,Args}, Port) ->
128    case catch apply(Mod, Func, Args) of
129        Data when is_list(Data); is_binary(Data) ->
130            case
131                catch unicode:characters_to_binary(Data, latin1, get(encoding))
132            of
133                Bin when is_binary(Bin) ->
134                    put_chars(Bin, Port);
135                _ ->
136                    {error,{error,put_chars}}
137            end;
138        _ ->
139            {error,{error,put_chars}}
140    end;
141%% BC if called from pre-R13 node
142io_request({put_chars,Chars}, Port) ->
143    io_request({put_chars,latin1,Chars}, Port);
144io_request({put_chars,Mod,Func,Args}, Port) ->
145    io_request({put_chars,latin1,Mod,Func,Args}, Port);
146%% New in R12
147io_request({get_geometry,columns},Port) ->
148    case get_fd_geometry(Port) of
149	{W,_H} ->
150	    {ok,W};
151	_ ->
152	    {error,{error,enotsup}}
153    end;
154io_request({get_geometry,rows},Port) ->
155    case get_fd_geometry(Port) of
156	{_W,H} ->
157	    {ok,H};
158	_ ->
159	    {error,{error,enotsup}}
160    end;
161io_request(getopts, _Port) ->
162    getopts();
163io_request({setopts,Opts}, _Port) when is_list(Opts) ->
164    setopts(Opts);
165io_request({requests,Reqs}, Port) ->
166    io_requests(Reqs, {ok,ok}, Port);
167io_request(R, _Port) ->                      %Unknown request
168    {error,{error,{request,R}}}.		%Ignore but give error (?)
169
170%% Status = io_requests(RequestList, PrevStat, Port)
171%%  Process a list of output requests as long as the previous status is 'ok'.
172
173io_requests([R|Rs], {ok,_Res}, Port) ->
174    io_requests(Rs, io_request(R, Port), Port);
175io_requests([_|_], Error, _) ->
176    Error;
177io_requests([], Stat, _) ->
178    Stat.
179
180%% put_port(DeepList, Port)
181%%  Take a deep list of characters, flatten and output them to the
182%%  port.
183
184put_port(List, Port) ->
185    send_port(Port, {command, List}).
186
187%% send_port(Port, Command)
188
189send_port(Port, Command) ->
190    Port ! {self(),Command}.
191
192
193%% io_reply(From, ReplyAs, Reply)
194%%  The function for sending i/o command acknowledgement.
195%%  The ACK contains the return value.
196
197io_reply(From, ReplyAs, Reply) ->
198    From ! {io_reply,ReplyAs,Reply}.
199
200%% put_chars
201put_chars(Chars, Port) when is_binary(Chars) ->
202    _ = put_port(Chars, Port),
203    {ok,ok}.
204
205%% setopts
206setopts(Opts0) ->
207    Opts = expand_encoding(Opts0),
208    case check_valid_opts(Opts) of
209        true ->
210            do_setopts(Opts);
211        false ->
212            {error,{error,enotsup}}
213    end.
214
215check_valid_opts([]) ->
216    true;
217check_valid_opts([{encoding,Valid}|T]) when Valid =:= unicode;
218                                            Valid =:= utf8; Valid =:= latin1 ->
219    check_valid_opts(T);
220check_valid_opts(_) ->
221    false.
222
223expand_encoding([]) ->
224    [];
225expand_encoding([latin1 | T]) ->
226    [{encoding,latin1} | expand_encoding(T)];
227expand_encoding([unicode | T]) ->
228    [{encoding,unicode} | expand_encoding(T)];
229expand_encoding([H|T]) ->
230    [H|expand_encoding(T)].
231
232do_setopts(Opts) ->
233    case proplists:get_value(encoding, Opts) of
234        Valid when Valid =:= unicode; Valid =:= utf8 ->
235            put(encoding, unicode);
236        latin1 ->
237            put(encoding, latin1);
238        undefined ->
239            ok
240    end,
241    {ok,ok}.
242
243getopts() ->
244    Uni = {encoding,get(encoding)},
245    {ok,[Uni]}.
246
247wrap_characters_to_binary(Chars,From,To) ->
248    TrNl = (whereis(user_drv) =/= undefined),
249    Limit = case To of
250		latin1 ->
251		    255;
252		_Else ->
253		    16#10ffff
254	    end,
255    case catch unicode:characters_to_list(Chars, From) of
256        L when is_list(L) ->
257            unicode:characters_to_binary(
258              [ case X of
259                    $\n when TrNl ->
260                        "\r\n";
261                    High when High > Limit ->
262                        ["\\x{",erlang:integer_to_list(X, 16),$}];
263                    Low ->
264                        Low
265                end || X <- L ], unicode, To);
266        _ ->
267            error
268    end.
269