diff --git a/lib/kernel/src/group.erl b/lib/kernel/src/group.erl index 83cc5ffcf51c..3a4185f787ee 100644 --- a/lib/kernel/src/group.erl +++ b/lib/kernel/src/group.erl @@ -49,7 +49,10 @@ server(Ancestors, Drv, Shell, Options) -> put(user_drv, Drv), ExpandFun = normalize_expand_fun(Options, fun edlin_expand:expand/2), put(expand_fun, ExpandFun), - put(echo, proplists:get_value(echo, Options, true)), + Echo = proplists:get_value(echo, Options, true), + put(echo, Echo), + Dumb = proplists:get_value(dumb, Options, false), + put(dumb, Dumb), put(expand_below, proplists:get_value(expand_below, Options, true)), server_loop(Drv, start_shell(Shell), []). @@ -490,7 +493,7 @@ get_chars_line(Prompt, M, F, Xa, Drv, Shell, Buf, Encoding) -> get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf, start, [], Encoding). get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, LineCont0, Encoding) -> - Result = case get(echo) of + Result = case not(get(dumb)) andalso get(echo) of true -> get_line(Buf0, Pbs, LineCont0, Drv, Shell, Encoding); false -> @@ -513,7 +516,7 @@ get_chars_loop(Pbs, M, F, Xa, Drv, Shell, Buf0, State, LineCont0, Encoding) -> get_chars_apply(Pbs, M, F, Xa, Drv, Shell, Buf, State0, LineCont, Encoding) -> %% multi line support means that we should not keep the state %% but we need to keep it for oldshell mode - {State, Line} = case get(echo) of + {State, Line} = case not(get(dumb)) andalso get(echo) of true -> {start, edlin:current_line(LineCont)}; false -> {State0, LineCont} end, @@ -836,7 +839,11 @@ get_line_echo_off(Chars, ToEnc, Pbs, Drv, Shell) -> Res end. -get_line_echo_off1({Chars,[]}, Drv, Shell) -> +get_line_echo_off1({Chars,[],Rs}, Drv, Shell) -> + case get(echo) of + true -> send_drv_reqs(Drv, Rs); + false -> skip + end, receive {Drv,{data,Cs}} -> get_line_echo_off1(edit_line(cast(Cs, list), Chars), Drv, Shell); @@ -844,11 +851,11 @@ get_line_echo_off1({Chars,[]}, Drv, Shell) -> get_line_echo_off1(edit_line(eof, Chars), Drv, Shell); {io_request,From,ReplyAs,Req} when is_pid(From) -> io_request(Req, From, ReplyAs, Drv, Shell, []), - get_line_echo_off1({Chars,[]}, Drv, Shell); + get_line_echo_off1({Chars,[],[]}, Drv, Shell); {reply,{From,ReplyAs},Reply} when From =/= undefined -> %% We take care of replies from puts here as well io_reply(From, ReplyAs, Reply), - get_line_echo_off1({Chars,[]},Drv, Shell); + get_line_echo_off1({Chars,[],[]},Drv, Shell); {'EXIT',Drv,interrupt} -> interrupted; {'EXIT',Drv,_} -> @@ -858,9 +865,12 @@ get_line_echo_off1({Chars,[]}, Drv, Shell) -> end; get_line_echo_off1(eof, _Drv, _Shell) -> {done,eof,eof}; -get_line_echo_off1({Chars,Rest}, _Drv, _Shell) -> +get_line_echo_off1({Chars,Rest,Rs}, Drv, _Shell) -> + case get(echo) of + true -> send_drv_reqs(Drv, Rs); + false -> skip + end, {done,lists:reverse(Chars),case Rest of done -> []; _ -> Rest end}. - get_chars_echo_off(Pbs, Drv, Shell) -> send_drv_reqs(Drv, [{insert_chars, unicode,Pbs}]), get_chars_echo_off1(Drv, Shell). @@ -895,22 +905,26 @@ get_chars_echo_off1(Drv, Shell) -> %% - ^d in posix/icanon mode: eof, delete-forward in edlin %% - ^r in posix/icanon mode: reprint (silly in echo-off mode :-)) %% - ^w in posix/icanon mode: word-erase (produces a beep in edlin) -edit_line(eof, []) -> +edit_line(Input, State) -> + edit_line(Input, State, []). +edit_line(eof, [], _) -> eof; -edit_line(eof, Chars) -> - {Chars,eof}; -edit_line([],Chars) -> - {Chars,[]}; -edit_line([$\r,$\n|Cs],Chars) -> - {[$\n | Chars], remainder_after_nl(Cs)}; -edit_line([NL|Cs],Chars) when NL =:= $\r; NL =:= $\n -> - {[$\n | Chars], remainder_after_nl(Cs)}; -edit_line([Erase|Cs],[]) when Erase =:= $\177; Erase =:= $\^H -> - edit_line(Cs,[]); -edit_line([Erase|Cs],[_|Chars]) when Erase =:= $\177; Erase =:= $\^H -> - edit_line(Cs,Chars); -edit_line([Char|Cs],Chars) -> - edit_line(Cs,[Char|Chars]). +edit_line(eof, Chars, Rs) -> + {Chars,eof, lists:reverse(Rs)}; +edit_line([],Chars, Rs) -> + {Chars,[],lists:reverse(Rs)}; +edit_line([$\r,$\n|Cs],Chars, Rs) -> + {[$\n | Chars], remainder_after_nl(Cs), lists:reverse([{put_chars, unicode, "\n"}|Rs])}; +edit_line([NL|Cs],Chars, Rs) when NL =:= $\r; NL =:= $\n -> + {[$\n | Chars], remainder_after_nl(Cs), lists:reverse([{put_chars, unicode, "\n"}|Rs])}; +edit_line([Erase|Cs],[], Rs) when Erase =:= $\177; Erase =:= $\^H -> + edit_line(Cs,[], Rs); +edit_line([Erase|Cs],[_|Chars], Rs) when Erase =:= $\177; Erase =:= $\^H -> + edit_line(Cs,Chars, [{delete_chars, -1}|Rs]); +edit_line([CtrlChar|Cs],Chars, Rs) when CtrlChar < 32 -> + edit_line(Cs,Chars,Rs); +edit_line([Char|Cs],Chars, Rs) -> + edit_line(Cs,[Char|Chars], [{put_chars, unicode, [Char]}|Rs]). remainder_after_nl("") -> done; remainder_after_nl(Cs) -> Cs. diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl index 30588fa6cf50..f3027b083af4 100644 --- a/lib/ssh/src/ssh_cli.erl +++ b/lib/ssh/src/ssh_cli.erl @@ -79,7 +79,7 @@ handle_ssh_msg({ssh_cm, _ConnectionHandler, #state{group = Group} = State0) -> {Enc, State} = guess_encoding(Data, State0), List = unicode:characters_to_list(Data, Enc), - to_group(List, Group), + to_group(List, Group, get_dumb(State#state.pty)), {ok, State}; handle_ssh_msg({ssh_cm, ConnectionHandler, @@ -393,21 +393,28 @@ out_enc(#state{encoding = PeerEnc, %%-------------------------------------------------------------------- -to_group([], _Group) -> +to_group([], _Group, _Dumb) -> ok; -to_group([$\^C | Tail], Group) -> +to_group([$\^C | Tail], Group, Dumb) -> exit(Group, interrupt), - to_group(Tail, Group); -to_group(Data, Group) -> + to_group(Tail, Group, Dumb); +to_group(Data, Group, Dumb) -> Func = fun(C) -> C /= $\^C end, Tail = case lists:splitwith(Func, Data) of {[], Right} -> Right; {Left, Right} -> - Group ! {self(), {data, Left}}, + %% Filter out escape sequences, only support Ctrl sequences + Left1 = if Dumb -> replace_escapes(Left); true -> Left end, + Group ! {self(), {data, Left1}}, Right end, - to_group(Tail, Group). + to_group(Tail, Group, Dumb). +replace_escapes(Data) -> + lists:flatten([ if C =:= 27 -> + [$^,C+64]; + true -> C + end || C <- Data]). %%-------------------------------------------------------------------- %%% io_request, handle io requests from the user process, @@ -511,10 +518,10 @@ get_tty_command(left, N, _TerminalType) -> -define(TABWIDTH, 8). %% convert input characters to buffer and to writeout -%% Note that the buf is reversed but the buftail is not +%% Note that Bef is reversed but Aft is not %% (this is handy; the head is always next to the cursor) conv_buf([], {LB, {Bef, Aft}, LA, Col}, AccWrite, _Tty) -> - {{LB, {Bef, Aft}, LA}, lists:reverse(AccWrite), Col}; + {{LB, {Bef, Aft}, LA, Col}, lists:reverse(AccWrite)}; conv_buf([13, 10 | Rest], {LB, {Bef, Aft}, LA, Col}, AccWrite, Tty = #ssh_pty{width = W}) -> conv_buf(Rest, {[lists:reverse(Bef)|LB], {[], tl2(Aft)}, LA, Col+(W-(Col rem W))}, [10, 13 | AccWrite], Tty); conv_buf([13 | Rest], {LB, {Bef, Aft}, LA, Col}, AccWrite, Tty = #ssh_pty{width = W}) -> @@ -532,21 +539,28 @@ conv_buf([C | Rest], {LB, {Bef, Aft}, LA, Col}, AccWrite, Tty) -> %%% put characters before the prompt put_chars(Chars, Buf, Tty) -> + Dumb = get_dumb(Tty), case Buf of - {[],{[],[]},[],_} -> {_, WriteBuf, _} = conv_buf(Chars, Buf, [], Tty), + {[],{[],[]},[],_} -> {_, WriteBuf} = conv_buf(Chars, Buf, [], Tty), {WriteBuf, Buf}; - _ -> + _ when Dumb =:= false -> {Delete, DeletedState} = io_request(delete_line, Buf, Tty, []), - {_, PutBuffer, _} = conv_buf(Chars, DeletedState, [], Tty), + {_, PutBuffer} = conv_buf(Chars, DeletedState, [], Tty), {Redraw, _} = io_request(redraw_prompt_pre_deleted, Buf, Tty, []), - {[Delete, PutBuffer, Redraw], Buf} + {[Delete, PutBuffer, Redraw], Buf}; + _ -> + %% When we have a dumb terminal, we get messages via put_chars requests + %% so state should be empty {[],{[],[]},[],_}, + %% but if we end up here its not, so keep the state + {_, WriteBuf} = conv_buf(Chars, Buf, [], Tty), + {WriteBuf, Buf} end. %%% insert character at current position insert_chars([], Buf, _Tty) -> {[], Buf}; insert_chars(Chars, {_LB,{_Bef, Aft},LA, _Col}=Buf, Tty) -> - {{NewLB, {NewBef, _NewAft}, _NewLA}, WriteBuf, NewCol} = conv_buf(Chars, Buf, [], Tty), + {{NewLB, {NewBef, _NewAft}, _NewLA, NewCol}, WriteBuf} = conv_buf(Chars, Buf, [], Tty), M = move_cursor(special_at_width(NewCol+length(Aft), Tty), NewCol, Tty), {[WriteBuf, Aft | M], {NewLB,{NewBef, Aft},LA, NewCol}}. @@ -725,7 +739,7 @@ start_shell(ConnectionHandler, State) -> Shell end, State#state{group = group:start(self(), ShellSpawner, - [{expand_below, false}, + [{dumb, get_dumb(State#state.pty)},{expand_below, false}, {echo, get_echo(State#state.pty)}]), buf = empty_buf()}. @@ -842,6 +856,13 @@ t2str(T) -> try io_lib:format("~s",[T]) end. %%-------------------------------------------------------------------- +get_dumb(Tty) -> + try + Tty#ssh_pty.term =:= "dumb" + catch + _:_ -> false + end. + % Pty can be undefined if the client never sets any pty options before % starting the shell. get_echo(Tty) -> diff --git a/lib/stdlib/src/edlin.erl b/lib/stdlib/src/edlin.erl index 431a3dc0ba06..a69924e127e2 100644 --- a/lib/stdlib/src/edlin.erl +++ b/lib/stdlib/src/edlin.erl @@ -74,7 +74,7 @@ start(Pbs) -> %% Only two modes used: 'none' and 'search'. Other modes can be %% handled inline through specific character handling. -start(Pbs, {_,{_,_},[]}=Cont) -> +start(Pbs, {_,{_,[]},[]}=Cont) -> %% Skip redraw if the cursor is at the end. {more_chars,{line,Pbs,Cont,{normal,none}},[{insert_chars,unicode,multi_line_prompt(Pbs)}]};