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