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%% get_chars(Prompt, Module, Function, XtraArg, Port, Queue, Encoding) 541%% Gets characters from the input port until the applied function 542%% returns {stop,Result,RestBuf}. Does not block output until input 543%% has been received. Encoding is the encoding of the data sent to 544%% the client and to Function. 545%% Returns: 546%% {Status,Result,NewQueue} 547%% {exit,Reason} 548 549%% Entry function. 550get_chars(Prompt, M, F, Xa, Port, Q, Enc) -> 551 case prompt(Port, Prompt) of 552 error -> 553 {error,{error,get_chars},Q}; 554 ok -> 555 case {get(eof),queue:is_empty(Q)} of 556 {true,true} -> 557 {ok,eof,Q}; 558 _ -> 559 get_chars(Prompt, M, F, Xa, Port, Q, start, Enc) 560 end 561 end. 562 563%% First loop. Wait for port data. Respond to output requests. 564get_chars(Prompt, M, F, Xa, Port, Q, State, Enc) -> 565 case queue:is_empty(Q) of 566 true -> 567 receive 568 {Port,{data,Bytes}} -> 569 get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); 570 {Port, eof} -> 571 put(eof, true), 572 {ok, eof, queue:new()}; 573 {io_request,From,ReplyAs,{get_geometry,_}=Req} when is_pid(From) -> 574 do_io_request(Req, From, ReplyAs, Port, 575 queue:new()), %Keep Q over this call 576 %% No prompt. 577 get_chars(Prompt, M, F, Xa, Port, Q, State, Enc); 578 {io_request,From,ReplyAs,Request} when is_pid(From) -> 579 get_chars_req(Prompt, M, F, Xa, Port, Q, State, 580 Request, From, ReplyAs, Enc); 581 {'EXIT',From,What} when node(From) =:= node() -> 582 {exit,What} 583 end; 584 false -> 585 get_chars_apply(State, M, F, Xa, Port, Q, Enc) 586 end. 587 588get_chars_req(Prompt, M, F, XtraArg, Port, Q, State, 589 Req, From, ReplyAs, Enc) -> 590 do_io_request(Req, From, ReplyAs, Port, queue:new()), %Keep Q over this call 591 case prompt(Port, Prompt) of 592 error -> 593 {error,{error,get_chars},Q}; 594 ok -> 595 get_chars(Prompt, M, F, XtraArg, Port, Q, State, Enc) 596 end. 597 598%% Second loop. Pass data to client as long as it wants more. 599%% A ^G in data interrupts loop if 'noshell' is not undefined. 600get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc) -> 601 case get(shell) of 602 noshell -> 603 get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, Bytes),Enc); 604 _ -> 605 case contains_ctrl_g_or_ctrl_c(Bytes) of 606 false -> 607 get_chars_apply(State, M, F, Xa, Port, 608 queue:snoc(Q, Bytes),Enc); 609 _ -> 610 throw(new_shell) 611 end 612 end. 613 614get_chars_apply(State0, M, F, Xa, Port, Q, Enc) -> 615 case catch M:F(State0, cast(queue:head(Q),Enc), Enc, Xa) of 616 {stop,Result,<<>>} -> 617 {ok,Result,queue:tail(Q)}; 618 {stop,Result,[]} -> 619 {ok,Result,queue:tail(Q)}; 620 {stop,Result,eof} -> 621 {ok,Result,queue:tail(Q)}; 622 {stop,Result,Buf} -> 623 {ok,Result,queue:cons(Buf, queue:tail(Q))}; 624 {'EXIT',_Why} -> 625 {error,{error,err_func(M, F, Xa)},queue:new()}; 626 State1 -> 627 get_chars_more(State1, M, F, Xa, Port, queue:tail(Q), Enc) 628 end. 629 630get_chars_more(State, M, F, Xa, Port, Q, Enc) -> 631 case queue:is_empty(Q) of 632 true -> 633 case get(eof) of 634 undefined -> 635 receive 636 {Port,{data,Bytes}} -> 637 get_chars_bytes(State, M, F, Xa, Port, Q, Bytes, Enc); 638 {Port,eof} -> 639 put(eof, true), 640 get_chars_apply(State, M, F, Xa, Port, 641 queue:snoc(Q, eof), Enc); 642 {'EXIT',From,What} when node(From) =:= node() -> 643 {exit,What} 644 end; 645 _ -> 646 get_chars_apply(State, M, F, Xa, Port, queue:snoc(Q, eof), Enc) 647 end; 648 false -> 649 get_chars_apply(State, M, F, Xa, Port, Q, Enc) 650 end. 651 652 653%% prompt(Port, Prompt) 654%% Print Prompt onto Port 655 656%% common case, reduces execution time by 20% 657prompt(_Port, '') -> ok; 658prompt(Port, Prompt) -> 659 Encoding = get(encoding), 660 PromptString = io_lib:format_prompt(Prompt, Encoding), 661 case wrap_characters_to_binary(PromptString, unicode, Encoding) of 662 Bin when is_binary(Bin) -> 663 put_port(Bin, Port); 664 error -> 665 error 666 end. 667 668%% Convert error code to make it look as before 669err_func(io_lib, get_until, {_,F,_}) -> 670 F; 671err_func(_, F, _) -> 672 F. 673 674%% using regexp reduces execution time by >50% compared to old code 675%% running two regexps in sequence is much faster than \\x03|\\x07 676contains_ctrl_g_or_ctrl_c(BinOrList)-> 677 case {re:run(BinOrList, <<3>>),re:run(BinOrList, <<7>>)} of 678 {nomatch, nomatch} -> false; 679 _ -> true 680 end. 681 682%% Convert a buffer between list and binary 683cast(Data, _Encoding) when is_atom(Data) -> 684 Data; 685cast(Data, Encoding) -> 686 IoEncoding = get(encoding), 687 cast(Data, get(read_mode), IoEncoding, Encoding). 688 689cast(B, binary, latin1, latin1) when is_binary(B) -> 690 B; 691cast(L, binary, latin1, latin1) -> 692 case catch erlang:iolist_to_binary(L) of 693 Bin when is_binary(Bin) -> Bin; 694 _ -> exit({no_translation, latin1, latin1}) 695 end; 696cast(Data, binary, unicode, latin1) when is_binary(Data); is_list(Data) -> 697 case catch unicode:characters_to_binary(Data, unicode, latin1) of 698 Bin when is_binary(Bin) -> Bin; 699 _ -> exit({no_translation, unicode, latin1}) 700 end; 701cast(Data, binary, latin1, unicode) when is_binary(Data); is_list(Data) -> 702 case catch unicode:characters_to_binary(Data, latin1, unicode) of 703 Bin when is_binary(Bin) -> Bin; 704 _ -> exit({no_translation, latin1, unicode}) 705 end; 706cast(B, binary, unicode, unicode) when is_binary(B) -> 707 B; 708cast(L, binary, unicode, unicode) -> 709 case catch unicode:characters_to_binary(L, unicode) of 710 Bin when is_binary(Bin) -> Bin; 711 _ -> exit({no_translation, unicode, unicode}) 712 end; 713cast(B, list, latin1, latin1) when is_binary(B) -> 714 binary_to_list(B); 715cast(L, list, latin1, latin1) -> 716 case catch erlang:iolist_to_binary(L) of 717 Bin when is_binary(Bin) -> binary_to_list(Bin); 718 _ -> exit({no_translation, latin1, latin1}) 719 end; 720cast(Data, list, unicode, latin1) when is_binary(Data); is_list(Data) -> 721 case catch unicode:characters_to_list(Data, unicode) of 722 Chars when is_list(Chars) -> 723 [ case X of 724 High when High > 255 -> 725 exit({no_translation, unicode, latin1}); 726 Low -> 727 Low 728 end || X <- Chars ]; 729 _ -> 730 exit({no_translation, unicode, latin1}) 731 end; 732cast(Data, list, latin1, unicode) when is_binary(Data); is_list(Data) -> 733 case catch unicode:characters_to_list(Data, latin1) of 734 Chars when is_list(Chars) -> Chars; 735 _ -> exit({no_translation, latin1, unicode}) 736 end; 737cast(Data, list, unicode, unicode) when is_binary(Data); is_list(Data) -> 738 case catch unicode:characters_to_list(Data, unicode) of 739 Chars when is_list(Chars) -> Chars; 740 _ -> exit({no_translation, unicode, unicode}) 741 end. 742 743wrap_characters_to_binary(Chars, unicode, latin1) -> 744 case catch unicode:characters_to_binary(Chars, unicode, latin1) of 745 Bin when is_binary(Bin) -> 746 Bin; 747 _ -> 748 case catch unicode:characters_to_list(Chars, unicode) of 749 L when is_list(L) -> 750 list_to_binary( 751 [ case X of 752 High when High > 255 -> 753 ["\\x{",erlang:integer_to_list(X, 16),$}]; 754 Low -> 755 Low 756 end || X <- L ]); 757 _ -> 758 error 759 end 760 end; 761wrap_characters_to_binary(Bin, From, From) when is_binary(Bin) -> 762 Bin; 763wrap_characters_to_binary(Chars, From, To) -> 764 case catch unicode:characters_to_binary(Chars, From, To) of 765 Bin when is_binary(Bin) -> 766 Bin; 767 _ -> 768 error 769 end. 770