1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-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(user). 21-compile(inline). 22 23%% Basic standard i/o server for user interface port. 24 25-export([start/0, start/1, start_out/0]). 26-export([interfaces/1]). 27 28-define(NAME, user). 29 30%% Defines for control ops 31-define(ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER, 16#018b0900). 32-define(CTRL_OP_GET_WINSIZE, (100 + ?ERTS_TTYSL_DRV_CONTROL_MAGIC_NUMBER)). 33 34%% 35%% The basic server and start-up. 36%% 37 38start() -> 39 start_port([eof,binary]). 40 41start([Mod,Fun|Args]) -> 42 %% Mod,Fun,Args should return a pid. That process is supposed to act 43 %% as the io port. 44 Pid = apply(Mod, Fun, Args), % This better work! 45 Id = spawn(fun() -> server(Pid) end), 46 register(?NAME, Id), 47 Id. 48 49start_out() -> 50 %% Output-only version of start/0 51 start_port([out,binary]). 52 53start_port(PortSettings) -> 54 Id = spawn(fun() -> server({fd,0,1}, PortSettings) end), 55 register(?NAME, Id), 56 Id. 57 58%% Return the pid of the shell process. 59%% Note: We can't ask the user process for this info since it 60%% may be busy waiting for data from the port. 61interfaces(User) -> 62 case process_info(User, dictionary) of 63 {dictionary,Dict} -> 64 case lists:keysearch(shell, 1, Dict) of 65 {value,Sh={shell,Shell}} when is_pid(Shell) -> 66 [Sh]; 67 _ -> 68 [] 69 end; 70 _ -> 71 [] 72 end. 73 74server(Pid) when is_pid(Pid) -> 75 process_flag(trap_exit, true), 76 link(Pid), 77 run(Pid). 78 79server(PortName,PortSettings) -> 80 process_flag(trap_exit, true), 81 Port = open_port(PortName,PortSettings), 82 run(Port). 83 84run(P) -> 85 put(read_mode,list), 86 put(encoding,latin1), 87 case init:get_argument(noshell) of 88 %% non-empty list -> noshell 89 {ok, [_|_]} -> 90 put(shell, noshell), 91 server_loop(P, queue:new()); 92 _ -> 93 group_leader(self(), self()), 94 catch_loop(P, start_init_shell()) 95 end. 96 97catch_loop(Port, Shell) -> 98 catch_loop(Port, Shell, queue:new()). 99 100catch_loop(Port, Shell, Q) -> 101 case catch server_loop(Port, Q) of 102 new_shell -> 103 exit(Shell, kill), 104 catch_loop(Port, start_new_shell()); 105 {unknown_exit,{Shell,Reason},_} -> % shell has exited 106 case Reason of 107 normal -> 108 put_port(<<"*** ">>, Port); 109 _ -> 110 put_port(<<"*** ERROR: ">>, Port) 111 end, 112 put_port(<<"Shell process terminated! ***\n">>, Port), 113 catch_loop(Port, start_new_shell()); 114 {unknown_exit,_,Q1} -> 115 catch_loop(Port, Shell, Q1); 116 {'EXIT',R} -> 117 exit(R) 118 end. 119 120link_and_save_shell(Shell) -> 121 link(Shell), 122 put(shell, Shell), 123 Shell. 124 125start_init_shell() -> 126 link_and_save_shell(shell:start(init)). 127 128start_new_shell() -> 129 link_and_save_shell(shell:start()). 130 131server_loop(Port, Q) -> 132 receive 133 {io_request,From,ReplyAs,Request} when is_pid(From) -> 134 server_loop(Port, do_io_request(Request, From, ReplyAs, Port, Q)); 135 {Port,{data,Bytes}} -> 136 case get(shell) of 137 noshell -> 138 server_loop(Port, queue:snoc(Q, Bytes)); 139 _ -> 140 case contains_ctrl_g_or_ctrl_c(Bytes) of 141 false -> 142 server_loop(Port, queue:snoc(Q, Bytes)); 143 _ -> 144 throw(new_shell) 145 end 146 end; 147 {Port, eof} -> 148 put(eof, true), 149 server_loop(Port, Q); 150 151 %% Ignore messages from port here. 152 {'EXIT',Port,badsig} -> % Ignore badsig errors 153 server_loop(Port, Q); 154 {'EXIT',Port,What} -> % Port has exited 155 exit(What); 156 157 %% Check if shell has exited 158 {'EXIT',SomePid,What} -> 159 case get(shell) of 160 noshell -> 161 server_loop(Port, Q); % Ignore 162 _ -> 163 throw({unknown_exit,{SomePid,What},Q}) 164 end; 165 166 _Other -> % Ignore other messages 167 server_loop(Port, Q) 168 end. 169 170 171get_fd_geometry(Port) -> 172 case (catch port_control(Port,?CTRL_OP_GET_WINSIZE,[])) of 173 List when length(List) =:= 8 -> 174 <<W:32/native,H:32/native>> = list_to_binary(List), 175 {W,H}; 176 _ -> 177 error 178 end. 179 180 181%% NewSaveBuffer = io_request(Request, FromPid, ReplyAs, Port, SaveBuffer) 182 183do_io_request(Req, From, ReplyAs, Port, Q0) -> 184 case io_request(Req, Port, Q0) of 185 {_Status,Reply,Q1} -> 186 _ = io_reply(From, ReplyAs, Reply), 187 Q1; 188 {exit,What} -> 189 ok = send_port(Port, close), 190 exit(What) 191 end. 192 193%% New in R13B 194%% Encoding option (unicode/latin1) 195io_request({put_chars,unicode,Chars}, Port, Q) -> % Binary new in R9C 196 case wrap_characters_to_binary(Chars, unicode, get(encoding)) of 197 error -> 198 {error,{error,put_chars},Q}; 199 Bin -> 200 put_chars(Bin, Port, Q) 201 end; 202io_request({put_chars,unicode,Mod,Func,Args}, Port, Q) -> 203 case catch apply(Mod,Func,Args) of 204 Data when is_list(Data); is_binary(Data) -> 205 case wrap_characters_to_binary(Data, unicode, get(encoding)) of 206 Bin when is_binary(Bin) -> 207 put_chars(Bin, Port, Q); 208 error -> 209 {error,{error,put_chars},Q} 210 end; 211 Undef -> 212 put_chars(Undef, Port, Q) 213 end; 214io_request({put_chars,latin1,Chars}, Port, Q) -> % Binary new in R9C 215 case catch unicode:characters_to_binary(Chars, latin1, get(encoding)) of 216 Data when is_binary(Data) -> 217 put_chars(Data, Port, Q); 218 _ -> 219 {error,{error,put_chars},Q} 220 end; 221io_request({put_chars,latin1,Mod,Func,Args}, Port, Q) -> 222 case catch apply(Mod,Func,Args) of 223 Data when is_list(Data); is_binary(Data) -> 224 case 225 catch unicode:characters_to_binary(Data,latin1,get(encoding)) 226 of 227 Bin when is_binary(Bin) -> 228 put_chars(Bin, Port, Q); 229 _ -> 230 {error,{error,put_chars},Q} 231 end; 232 Undef -> 233 put_chars(Undef, Port, Q) 234 end; 235io_request({get_chars,Enc,Prompt,N}, Port, Q) -> % New in R9C 236 get_chars(Prompt, io_lib, collect_chars, N, Port, Q, Enc); 237io_request({get_line,Enc,Prompt}, Port, Q) -> 238 case get(read_mode) of 239 binary -> 240 get_line_bin(Prompt,Port,Q,Enc); 241 _ -> 242 get_chars(Prompt, io_lib, collect_line, [], Port, Q, Enc) 243 end; 244io_request({get_until,Enc,Prompt,M,F,As}, Port, Q) -> 245 get_chars(Prompt, io_lib, get_until, {M,F,As}, Port, Q, Enc); 246%% End New in R13B 247io_request(getopts, Port, Q) -> 248 getopts(Port, Q); 249io_request({setopts,Opts}, Port, Q) when is_list(Opts) -> 250 setopts(Opts, Port, Q); 251io_request({requests,Reqs}, Port, Q) -> 252 io_requests(Reqs, {ok,ok,Q}, Port); 253 254%% New in R12 255io_request({get_geometry,columns},Port,Q) -> 256 case get_fd_geometry(Port) of 257 {W,_H} -> 258 {ok,W,Q}; 259 _ -> 260 {error,{error,enotsup},Q} 261 end; 262io_request({get_geometry,rows},Port,Q) -> 263 case get_fd_geometry(Port) of 264 {_W,H} -> 265 {ok,H,Q}; 266 _ -> 267 {error,{error,enotsup},Q} 268 end; 269%% BC with pre-R13 nodes 270io_request({put_chars,Chars}, Port, Q) -> 271 io_request({put_chars,latin1,Chars}, Port, Q); 272io_request({put_chars,Mod,Func,Args}, Port, Q) -> 273 io_request({put_chars,latin1,Mod,Func,Args}, Port, Q); 274io_request({get_chars,Prompt,N}, Port, Q) -> 275 io_request({get_chars,latin1,Prompt,N}, Port, Q); 276io_request({get_line,Prompt}, Port, Q) -> 277 io_request({get_line,latin1,Prompt}, Port, Q); 278io_request({get_until,Prompt,M,F,As}, Port, Q) -> 279 io_request({get_until,latin1,Prompt,M,F,As}, Port, Q); 280 281io_request(R, _Port, Q) -> %Unknown request 282 {error,{error,{request,R}},Q}. %Ignore but give error (?) 283 284%% Status = io_requests(RequestList, PrevStat, Port) 285%% Process a list of output requests as long as the previous status is 'ok'. 286 287io_requests([R|Rs], {ok,_Res,Q}, Port) -> 288 io_requests(Rs, io_request(R, Port, Q), Port); 289io_requests([_|_], Error, _) -> 290 Error; 291io_requests([], Stat, _) -> 292 Stat. 293 294%% put_port(DeepList, Port) 295%% Take a deep list of characters, flatten and output them to the 296%% port. 297 298put_port(List, Port) -> 299 true = port_command(Port, List), 300 ok. 301 302%% send_port(Port, Command) 303 304send_port(Port, Command) -> 305 Port ! {self(),Command}, 306 ok. 307 308%% io_reply(From, ReplyAs, Reply) 309%% The function for sending i/o command acknowledgement. 310%% The ACK contains the return value. 311 312io_reply(From, ReplyAs, Reply) -> 313 From ! {io_reply,ReplyAs,Reply}. 314 315%% put_chars 316put_chars(Chars, Port, Q) when is_binary(Chars) -> 317 ok = put_port(Chars, Port), 318 {ok,ok,Q}; 319put_chars(Chars, Port, Q) -> 320 case catch list_to_binary(Chars) of 321 Binary when is_binary(Binary) -> 322 put_chars(Binary, Port, Q); 323 _ -> 324 {error,{error,put_chars},Q} 325 end. 326 327expand_encoding([]) -> 328 []; 329expand_encoding([latin1 | T]) -> 330 [{encoding,latin1} | expand_encoding(T)]; 331expand_encoding([unicode | T]) -> 332 [{encoding,unicode} | expand_encoding(T)]; 333expand_encoding([H|T]) -> 334 [H|expand_encoding(T)]. 335 336%% setopts 337setopts(Opts0,Port,Q) -> 338 Opts = proplists:unfold( 339 proplists:substitute_negations( 340 [{list,binary}], 341 expand_encoding(Opts0))), 342 case check_valid_opts(Opts) of 343 true -> 344 do_setopts(Opts,Port,Q); 345 false -> 346 {error,{error,enotsup},Q} 347 end. 348check_valid_opts([]) -> 349 true; 350check_valid_opts([{binary,_}|T]) -> 351 check_valid_opts(T); 352check_valid_opts([{encoding,Valid}|T]) when Valid =:= latin1; Valid =:= utf8; Valid =:= unicode -> 353 check_valid_opts(T); 354check_valid_opts(_) -> 355 false. 356 357do_setopts(Opts, _Port, Q) -> 358 case proplists:get_value(encoding,Opts) of 359 Valid when Valid =:= unicode; Valid =:= utf8 -> 360 put(encoding,unicode); 361 latin1 -> 362 put(encoding,latin1); 363 undefined -> 364 ok 365 end, 366 case proplists:get_value(binary, Opts) of 367 true -> 368 put(read_mode,binary), 369 {ok,ok,Q}; 370 false -> 371 put(read_mode,list), 372 {ok,ok,Q}; 373 _ -> 374 {ok,ok,Q} 375 end. 376 377getopts(_Port,Q) -> 378 Bin = {binary, get(read_mode) =:= binary}, 379 Uni = {encoding, get(encoding)}, 380 {ok,[Bin,Uni],Q}. 381 382get_line_bin(Prompt,Port,Q, Enc) -> 383 case prompt(Port, Prompt) of 384 error -> 385 {error,{error,get_line},Q}; 386 ok -> 387 case {get(eof),queue:is_empty(Q)} of 388 {true,true} -> 389 {ok,eof,Q}; 390 _ -> 391 get_line(Prompt,Port, Q, [], Enc) 392 end 393 end. 394 395get_line(Prompt, Port, Q, Acc, Enc) -> 396 case queue:is_empty(Q) of 397 true -> 398 receive 399 {Port,{data,Bytes}} -> 400 get_line_bytes(Prompt, Port, Q, Acc, Bytes, Enc); 401 {Port, eof} -> 402 put(eof, true), 403 {ok, eof, queue:new()}; 404 {io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) -> 405 do_io_request(Req, From, ReplyAs, Port, 406 queue:new()), 407 %% No prompt. 408 get_line(Prompt, Port, Q, Acc, Enc); 409 {io_request,From,ReplyAs,Request} when is_pid(From) -> 410 do_io_request(Request, From, ReplyAs, Port, queue:new()), 411 case prompt(Port, Prompt) of 412 error -> 413 {error,{error,get_line},Q}; 414 ok -> 415 get_line(Prompt, Port, Q, Acc, Enc) 416 end; 417 {'EXIT',From,What} when node(From) =:= node() -> 418 {exit,What} 419 end; 420 false -> 421 get_line_doit(Prompt, Port, Q, Acc, Enc) 422 end. 423 424get_line_bytes(Prompt, Port, Q, Acc, Bytes, Enc) -> 425 case get(shell) of 426 noshell -> 427 get_line_doit(Prompt, Port, queue:snoc(Q, Bytes),Acc,Enc); 428 _ -> 429 case contains_ctrl_g_or_ctrl_c(Bytes) of 430 false -> 431 get_line_doit(Prompt, Port, queue:snoc(Q, Bytes), Acc, Enc); 432 _ -> 433 throw(new_shell) 434 end 435 end. 436is_cr_at(Pos,Bin) -> 437 case Bin of 438 <<_:Pos/binary,$\r,_/binary>> -> 439 true; 440 _ -> 441 false 442 end. 443srch(<<>>,_,_) -> 444 nomatch; 445srch(<<X:8,_/binary>>,X,N) -> 446 {match,[{N,1}]}; 447srch(<<_:8,T/binary>>,X,N) -> 448 srch(T,X,N+1). 449 450get_line_doit(Prompt, Port, Q, Accu, Enc) -> 451 case queue:is_empty(Q) of 452 true -> 453 case get(eof) of 454 true -> 455 case Accu of 456 [] -> 457 {ok,eof,Q}; 458 _ -> 459 {ok,binrev(Accu,[]),Q} 460 end; 461 _ -> 462 get_line(Prompt, Port, Q, Accu, Enc) 463 end; 464 false -> 465 Bin = queue:head(Q), 466 case srch(Bin,$\n,0) of 467 nomatch -> 468 X = byte_size(Bin)-1, 469 case is_cr_at(X,Bin) of 470 true -> 471 <<D:X/binary,_/binary>> = Bin, 472 get_line_doit(Prompt, Port, queue:tail(Q), 473 [<<$\r>>,D|Accu], Enc); 474 false -> 475 get_line_doit(Prompt, Port, queue:tail(Q), 476 [Bin|Accu], Enc) 477 end; 478 {match,[{Pos,1}]} -> 479 %% We are done 480 PosPlus = Pos + 1, 481 case Accu of 482 [] -> 483 {Head,Tail} = 484 case is_cr_at(Pos - 1,Bin) of 485 false -> 486 <<H:PosPlus/binary, 487 T/binary>> = Bin, 488 {H,T}; 489 true -> 490 PosMinus = Pos - 1, 491 <<H:PosMinus/binary, 492 _,_,T/binary>> = Bin, 493 {binrev([],[H,$\n]),T} 494 end, 495 case Tail of 496 <<>> -> 497 {ok, cast(Head,Enc), queue:tail(Q)}; 498 _ -> 499 {ok, cast(Head,Enc), 500 queue:cons(Tail, queue:tail(Q))} 501 end; 502 [<<$\r>>|Stack1] when Pos =:= 0 -> 503 <<_:PosPlus/binary,Tail/binary>> = Bin, 504 case Tail of 505 <<>> -> 506 {ok, cast(binrev(Stack1, [$\n]),Enc), 507 queue:tail(Q)}; 508 _ -> 509 {ok, cast(binrev(Stack1, [$\n]),Enc), 510 queue:cons(Tail, queue:tail(Q))} 511 end; 512 _ -> 513 {Head,Tail} = 514 case is_cr_at(Pos - 1,Bin) of 515 false -> 516 <<H:PosPlus/binary, 517 T/binary>> = Bin, 518 {H,T}; 519 true -> 520 PosMinus = Pos - 1, 521 <<H:PosMinus/binary, 522 _,_,T/binary>> = Bin, 523 {[H,$\n],T} 524 end, 525 case Tail of 526 <<>> -> 527 {ok, cast(binrev(Accu,[Head]),Enc), 528 queue:tail(Q)}; 529 _ -> 530 {ok, cast(binrev(Accu,[Head]),Enc), 531 queue:cons(Tail, queue:tail(Q))} 532 end 533 end 534 end 535 end. 536 537binrev(L, T) -> 538 list_to_binary(lists:reverse(L, T)). 539 540%% is_cr_at(Pos,Bin) -> 541%% case Bin of 542%% <<_:Pos/binary,$\r,_/binary>> -> 543%% true; 544%% _ -> 545%% false 546%% end. 547 548%% collect_line_bin_re(Bin,_Data,Stack,_) -> 549%% case re:run(Bin,<<"\n">>) of 550%% nomatch -> 551%% X = byte_size(Bin)-1, 552%% case is_cr_at(X,Bin) of 553%% true -> 554%% <<D:X/binary,_/binary>> = Bin, 555%% [<<$\r>>,D|Stack]; 556%% false -> 557%% [Bin|Stack] 558%% end; 559%% {match,[{Pos,1}]} -> 560%% PosPlus = Pos + 1, 561%% case Stack of 562%% [] -> 563%% case is_cr_at(Pos - 1,Bin) of 564%% false -> 565%% <<Head:PosPlus/binary,Tail/binary>> = Bin, 566%% {stop, Head, Tail}; 567%% true -> 568%% PosMinus = Pos - 1, 569%% <<Head:PosMinus/binary,_,_,Tail/binary>> = Bin, 570%% {stop, binrev([],[Head,$\n]),Tail} 571%% end; 572%% [<<$\r>>|Stack1] when Pos =:= 0 -> 573 574%% <<_:PosPlus/binary,Tail/binary>> = Bin, 575%% {stop,binrev(Stack1, [$\n]),Tail}; 576%% _ -> 577%% case is_cr_at(Pos - 1,Bin) of 578%% false -> 579%% <<Head:PosPlus/binary,Tail/binary>> = Bin, 580%% {stop,binrev(Stack, [Head]),Tail}; 581%% true -> 582%% PosMinus = Pos - 1, 583%% <<Head:PosMinus/binary,_,_,Tail/binary>> = Bin, 584%% {stop, binrev(Stack,[Head,$\n]),Tail} 585%% end 586%% end 587%% end. 588%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue, Encoding) 589%% Gets characters from the input port until the applied function 590%% returns {stop,Result,RestBuf}. Does not block output until input 591%% has been received. Encoding is the encoding of the data sent to 592%% the client and to Function. 593%% Returns: 594%% {Status,Result,NewQueue} 595%% {exit,Reason} 596 597%% Entry function. 598get_chars(Prompt, M, F, Xa, Port, Q, Enc) -> 599 case prompt(Port, Prompt) of 600 error -> 601 {error,{error,get_chars},Q}; 602 ok -> 603 case {get(eof),queue:is_empty(Q)} of 604 {true,true} -> 605 {ok,eof,Q}; 606 _ -> 607 get_chars(Prompt, M, F, Xa, Port, Q, start, Enc) 608 end 609 end. 610 611%% First loop. Wait for port data. Respond to output requests. 612get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) -> 613 case queue:is_empty(Q) of 614 true -> 615 receive 616 {Port,{data,Bytes}} -> 617 get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); 618 {Port, eof} -> 619 put(eof, true), 620 {ok, eof, queue:new()}; 621 %%{io_request,From,ReplyAs,Request} when is_pid(From) -> 622 %% get_chars_req(Prompt, M, F, Xa, Port, queue:new(), State, 623 %% Request, From, ReplyAs); 624 {io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) -> 625 do_io_request(Req, From, ReplyAs, Port, 626 queue:new()), %Keep Q over this call 627 %% No prompt. 628 get_chars(Prompt, M, F, Xa, Port, Q, State, Enc); 629 {io_request,From,ReplyAs,Request} when is_pid(From) -> 630 get_chars_req(Prompt, M, F, Xa, Port, Q, State, 631 Request, From, ReplyAs, Enc); 632 {'EXIT',From,What} when node(From) =:= node() -> 633 {exit,What} 634 end; 635 false -> 636 get_chars_apply(State, M, F, Xa, Port, Q, Enc) 637 end. 638 639get_chars_req(Prompt, M, F, XtraArg, Port, Q, State, 640 Req, From, ReplyAs, Enc) -> 641 do_io_request(Req, From, ReplyAs, Port, queue:new()), %Keep Q over this call 642 case prompt(Port, Prompt) of 643 error -> 644 {error,{error,get_chars},Q}; 645 ok -> 646 get_chars(Prompt, M, F, XtraArg, Port, Q, State, Enc) 647 end. 648 649%% Second loop. Pass data to client as long as it wants more. 650%% A ^G in data interrupts loop if 'noshell' is not undefined. 651get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc) -> 652 case get(shell) of 653 noshell -> 654 get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, Bytes),Enc); 655 _ -> 656 case contains_ctrl_g_or_ctrl_c(Bytes) of 657 false -> 658 get_chars_apply(State, M, F, Xa, Port, 659 queue:snoc(Q, Bytes),Enc); 660 _ -> 661 throw(new_shell) 662 end 663 end. 664 665get_chars_apply(State0, M, F, Xa, Port, Q, Enc) -> 666 case catch M:F(State0, cast(queue:head(Q),Enc), Enc, Xa) of 667 {stop,Result,<<>>} -> 668 {ok,Result,queue:tail(Q)}; 669 {stop,Result,[]} -> 670 {ok,Result,queue:tail(Q)}; 671 {stop,Result,eof} -> 672 {ok,Result,queue:tail(Q)}; 673 {stop,Result,Buf} -> 674 {ok,Result,queue:cons(Buf, queue:tail(Q))}; 675 {'EXIT',_Why} -> 676 {error,{error,err_func(M, F, Xa)},queue:new()}; 677 State1 -> 678 get_chars_more(State1, M, F, Xa, Port, queue:tail(Q), Enc) 679 end. 680 681get_chars_more(State, M, F, Xa, Port, Q, Enc) -> 682 case queue:is_empty(Q) of 683 true -> 684 case get(eof) of 685 undefined -> 686 receive 687 {Port,{data,Bytes}} -> 688 get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); 689 {Port,eof} -> 690 put(eof, true), 691 get_chars_apply(State, M, F, Xa, Port, 692 queue:snoc(Q, eof), Enc); 693 {'EXIT',From,What} when node(From) =:= node() -> 694 {exit,What} 695 end; 696 _ -> 697 get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, eof), Enc) 698 end; 699 false -> 700 get_chars_apply(State, M, F, Xa, Port, Q, Enc) 701 end. 702 703 704%% prompt(Port, Prompt) 705%% Print Prompt onto Port 706 707%% common case, reduces execution time by 20% 708prompt(_Port, '') -> ok; 709prompt(Port, Prompt) -> 710 Encoding = get(encoding), 711 PromptString = io_lib:format_prompt(Prompt, Encoding), 712 case wrap_characters_to_binary(PromptString, unicode, Encoding) of 713 Bin when is_binary(Bin) -> 714 put_port(Bin, Port); 715 error -> 716 error 717 end. 718 719%% Convert error code to make it look as before 720err_func(io_lib, get_until, {_,F,_}) -> 721 F; 722err_func(_, F, _) -> 723 F. 724 725%% using regexp reduces execution time by >50% compared to old code 726%% running two regexps in sequence is much faster than \\x03|\\x07 727contains_ctrl_g_or_ctrl_c(BinOrList)-> 728 case {re:run(BinOrList, <<3>>),re:run(BinOrList, <<7>>)} of 729 {nomatch, nomatch} -> false; 730 _ -> true 731 end. 732 733%% Convert a buffer between list and binary 734cast(Data, _Encoding) when is_atom(Data) -> 735 Data; 736cast(Data, Encoding) -> 737 IoEncoding = get(encoding), 738 cast(Data, get(read_mode), IoEncoding, Encoding). 739 740cast(B, binary, latin1, latin1) when is_binary(B) -> 741 B; 742cast(L, binary, latin1, latin1) -> 743 case catch erlang:iolist_to_binary(L) of 744 Bin when is_binary(Bin) -> Bin; 745 _ -> exit({no_translation, latin1, latin1}) 746 end; 747cast(Data, binary, unicode, latin1) when is_binary(Data); is_list(Data) -> 748 case catch unicode:characters_to_binary(Data, unicode, latin1) of 749 Bin when is_binary(Bin) -> Bin; 750 _ -> exit({no_translation, unicode, latin1}) 751 end; 752cast(Data, binary, latin1, unicode) when is_binary(Data); is_list(Data) -> 753 case catch unicode:characters_to_binary(Data, latin1, unicode) of 754 Bin when is_binary(Bin) -> Bin; 755 _ -> exit({no_translation, latin1, unicode}) 756 end; 757cast(B, binary, unicode, unicode) when is_binary(B) -> 758 B; 759cast(L, binary, unicode, unicode) -> 760 case catch unicode:characters_to_binary(L, unicode) of 761 Bin when is_binary(Bin) -> Bin; 762 _ -> exit({no_translation, unicode, unicode}) 763 end; 764cast(B, list, latin1, latin1) when is_binary(B) -> 765 binary_to_list(B); 766cast(L, list, latin1, latin1) -> 767 case catch erlang:iolist_to_binary(L) of 768 Bin when is_binary(Bin) -> binary_to_list(Bin); 769 _ -> exit({no_translation, latin1, latin1}) 770 end; 771cast(Data, list, unicode, latin1) when is_binary(Data); is_list(Data) -> 772 case catch unicode:characters_to_list(Data, unicode) of 773 Chars when is_list(Chars) -> 774 [ case X of 775 High when High > 255 -> 776 exit({no_translation, unicode, latin1}); 777 Low -> 778 Low 779 end || X <- Chars ]; 780 _ -> 781 exit({no_translation, unicode, latin1}) 782 end; 783cast(Data, list, latin1, unicode) when is_binary(Data); is_list(Data) -> 784 case catch unicode:characters_to_list(Data, latin1) of 785 Chars when is_list(Chars) -> Chars; 786 _ -> exit({no_translation, latin1, unicode}) 787 end; 788cast(Data, list, unicode, unicode) when is_binary(Data); is_list(Data) -> 789 case catch unicode:characters_to_list(Data, unicode) of 790 Chars when is_list(Chars) -> Chars; 791 _ -> exit({no_translation, unicode, unicode}) 792 end. 793 794wrap_characters_to_binary(Chars, unicode, latin1) -> 795 case catch unicode:characters_to_binary(Chars, unicode, latin1) of 796 Bin when is_binary(Bin) -> 797 Bin; 798 _ -> 799 case catch unicode:characters_to_list(Chars, unicode) of 800 L when is_list(L) -> 801 list_to_binary( 802 [ case X of 803 High when High > 255 -> 804 ["\\x{",erlang:integer_to_list(X, 16),$}]; 805 Low -> 806 Low 807 end || X <- L ]); 808 _ -> 809 error 810 end 811 end; 812wrap_characters_to_binary(Bin, From, From) when is_binary(Bin) -> 813 Bin; 814wrap_characters_to_binary(Chars, From, To) -> 815 case catch unicode:characters_to_binary(Chars, From, To) of 816 Bin when is_binary(Bin) -> 817 Bin; 818 _ -> 819 error 820 end. 821