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