Skip to content

Commit

Permalink
Sync tty_cli.erl with ssh_cli.erl in Erlang
Browse files Browse the repository at this point in the history
This updates to the OTP 23.3.2 version which looks like it has fixes for
window size changes and line ending handling.

Two changes were needed:

1. `:onlcr` was set so that the CRLF behavior remained the same
2. The window change unit test behaves differently. This behavior seems
   intentional so I updated the test.
  • Loading branch information
fhunleth authored and jjcarstens committed Feb 3, 2021
1 parent 939a9c3 commit 5b9b309
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 23 deletions.
2 changes: 1 addition & 1 deletion lib/extty.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ defmodule ExTTY do
handler = Keyword.get(opts, :handler)
type = Keyword.get(opts, :type, :elixir)
shell_opts = Keyword.get(opts, :shell_opts, [])
pty = tty_pty(term: "xterm", width: 80, height: 24, modes: [echo: true])
pty = tty_pty(term: "xterm", width: 80, height: 24, modes: [echo: true, onlcr: 1])

{:ok,
%{
Expand Down
69 changes: 50 additions & 19 deletions src/tty_cli.erl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2018. All Rights Reserved.
%% Copyright Ericsson AB 2005-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,10 @@
%% %CopyrightEnd%
%%

%%
%% This file is almost copied verbatem from Erlang's lib/ssh/src/ssh_cli.erl.
%%

%%
%% Description: a gen_server implementing a simple
%% terminal (using the group module) for a CLI
Expand Down Expand Up @@ -120,31 +124,37 @@ get_tty_command(left, N, _TerminalType) ->
%% convert input characters to buffer and to writeout
%% Note that the buf is reversed but the buftail is not
%% (this is handy; the head is always next to the cursor)
conv_buf([], AccBuf, AccBufTail, AccWrite, Col) ->
conv_buf([], AccBuf, AccBufTail, AccWrite, Col, _Tty) ->
{AccBuf, AccBufTail, lists:reverse(AccWrite), Col};
conv_buf([13, 10 | Rest], _AccBuf, AccBufTail, AccWrite, _Col) ->
conv_buf(Rest, [], tl2(AccBufTail), [10, 13 | AccWrite], 0);
conv_buf([13 | Rest], _AccBuf, AccBufTail, AccWrite, _Col) ->
conv_buf(Rest, [], tl1(AccBufTail), [13 | AccWrite], 0);
conv_buf([10 | Rest], _AccBuf, AccBufTail, AccWrite, _Col) ->
conv_buf(Rest, [], tl1(AccBufTail), [10, 13 | AccWrite], 0);
conv_buf([C | Rest], AccBuf, AccBufTail, AccWrite, Col) ->
conv_buf(Rest, [C | AccBuf], tl1(AccBufTail), [C | AccWrite], Col + 1).
conv_buf([13, 10 | Rest], _AccBuf, AccBufTail, AccWrite, _Col, Tty) ->
conv_buf(Rest, [], tl2(AccBufTail), [10, 13 | AccWrite], 0, Tty);
conv_buf([13 | Rest], _AccBuf, AccBufTail, AccWrite, _Col, Tty) ->
conv_buf(Rest, [], tl1(AccBufTail), [13 | AccWrite], 0, Tty);
conv_buf([10 | Rest], _AccBuf, AccBufTail, AccWrite0, _Col, Tty) ->
AccWrite =
case pty_opt(onlcr,Tty) of
0 -> [10 | AccWrite0];
1 -> [10,13 | AccWrite0];
undefined -> [10 | AccWrite0]
end,
conv_buf(Rest, [], tl1(AccBufTail), AccWrite, 0, Tty);
conv_buf([C | Rest], AccBuf, AccBufTail, AccWrite, Col, Tty) ->
conv_buf(Rest, [C | AccBuf], tl1(AccBufTail), [C | AccWrite], Col + 1, Tty).


%%% put characters at current position (possibly overwriting
%%% characters after current position in buffer)
put_chars(Chars, {Buf, BufTail, Col}, _Tty) ->
put_chars(Chars, {Buf, BufTail, Col}, Tty) ->
{NewBuf, NewBufTail, WriteBuf, NewCol} =
conv_buf(Chars, Buf, BufTail, [], Col),
conv_buf(Chars, Buf, BufTail, [], Col, Tty),
{WriteBuf, {NewBuf, NewBufTail, NewCol}}.

%%% insert character at current position
insert_chars([], {Buf, BufTail, Col}, _Tty) ->
{[], {Buf, BufTail, Col}};
insert_chars(Chars, {Buf, BufTail, Col}, Tty) ->
{NewBuf, _NewBufTail, WriteBuf, NewCol} =
conv_buf(Chars, Buf, [], [], Col),
conv_buf(Chars, Buf, [], [], Col, Tty),
M = move_cursor(special_at_width(NewCol+length(BufTail), Tty), NewCol, Tty),
{[WriteBuf, BufTail | M], {NewBuf, BufTail, NewCol}}.

Expand All @@ -168,13 +178,28 @@ delete_chars(N, {Buf, BufTail, Col}, Tty) -> % N < 0
%%% if current window is wider than previous)
window_change(Tty, OldTty, Buf)
when OldTty#tty_pty.width == Tty#tty_pty.width ->
%% No line width change
{[], Buf};
window_change(Tty, OldTty, {Buf, BufTail, Col}) ->
M1 = move_cursor(Col, 0, OldTty),
N = erlang:max(Tty#tty_pty.width - OldTty#tty_pty.width, 0) * 2,
S = lists:reverse(Buf, [BufTail | lists:duplicate(N, $ )]),
M2 = move_cursor(length(Buf) + length(BufTail) + N, Col, Tty),
{[M1, S | M2], {Buf, BufTail, Col}}.
case OldTty#tty_pty.width - Tty#tty_pty.width of
0 ->
%% No line width change
{[], {Buf,BufTail,Col}};

DeltaW0 when DeltaW0 < 0,
BufTail == [] ->
% Line width is decreased, cursor is at end of input
{[], {Buf,BufTail,Col}};

DeltaW0 when DeltaW0 < 0,
BufTail =/= [] ->
% Line width is decreased, cursor is not at end of input
{[], {Buf,BufTail,Col}};

DeltaW0 when DeltaW0 > 0 ->
% Line width is increased
{[], {Buf,BufTail,Col}}
end.

%% move around in buffer, respecting pad characters
step_over(0, Buf, [?PAD | BufTail], Col) ->
Expand Down Expand Up @@ -244,4 +269,10 @@ bin_to_list(L) when is_list(L) ->
bin_to_list(I) when is_integer(I) ->
I.


%%%----------------------------------------------------------------
pty_opt(Name, Tty) ->
try
proplists:get_value(Name, Tty#tty_pty.modes, undefined)
catch
_:_ -> undefined
end.
26 changes: 26 additions & 0 deletions src/tty_pty.hrl
Original file line number Diff line number Diff line change
@@ -1,3 +1,29 @@
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2020. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

%%

%%
%% TTY PTY definitions
%%

-ifndef(TTY_PTY_HRL).
-define(TTY_PTY_HRL, 1).

Expand Down
6 changes: 3 additions & 3 deletions test/extty_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ defmodule ExTTYTest do
refute_receive _
end

test "window change redraws prompt" do
test "window change acknowledged" do
pid = start_supervised!({ExTTY, [handler: self()]})

assert_receive {:tty_data, message}
Expand All @@ -63,8 +63,8 @@ defmodule ExTTYTest do

:ok = ExTTY.window_change(pid, 40, 20)

# Redrawn prompt
assert_receive {:tty_data, "\e[8Diex(1)> "}
# This is what the code does, so check that it works the same
assert_receive {:tty_data, ""}

# And nothing else
refute_receive _
Expand Down

0 comments on commit 5b9b309

Please sign in to comment.