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