some stupid bug

This commit is contained in:
Peter Harpending
2026-09-02 04:44:11 -07:00
parent 17cca5ecfe
commit ede7d8e977
4 changed files with 153 additions and 30 deletions
+18
View File
@@ -208,3 +208,21 @@
% 2. Control sequences reference: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
% 3. Historical background: https://invisible-island.net/xterm/modified-keys.html
%-define(XTERM_
% readline keys
-define(KBD_ESC, [27]).
-define(KBD_C_a, [1]).
-define(KBD_C_b, [2]).
-define(KBD_C_d, [4]).
-define(KBD_C_e, [5]).
-define(KBD_C_f, [6]).
-define(KBD_C_g, [7]).
-define(KBD_C_k, [11]).
-define(KBD_C_l, [12]).
-define(KBD_C_n, [14]).
-define(KBD_C_p, [16]).
% M-b -> [ESC, $b]
-define(KBD_M_b, [?KBD_ESC, $b]).
-define(KBD_M_d, [?KBD_ESC, $d]).
-define(KBD_M_f, [?KBD_ESC, $f]).
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env escript
%% script for keycode mapping
main([]) ->
ok = shell:start_interactive({noshell, raw}),
dum([]).
dum(Q) ->
io:format("~tw\r\n", [Q]),
NewChar = io:get_chars("", 1),
dum(Q ++ NewChar).
+82 -10
View File
@@ -1,18 +1,32 @@
#!/usr/bin/env escript
%% needs:
%% - line parsing
%% - keyboard input
%% - drawing to screen
-mode(compile).
% john state
-record(js,
{tsock = none :: gen_tcp:socket(),
nsock = none :: enoise:noise_socket()}).
{rcv = <<>> :: binary(),
kbdq = [] :: iolist(),
ibuf = [] :: string(),
tsock = none :: none | gen_tcp:socket(),
nsock = none :: none | enoise:noise_socket()}).
main([]) ->
main([]) -> main_ii(6969);
main([PortNum]) -> main_ii(list_to_integer(PortNum)).
main_ii(PortNum) ->
TcpOpts = [binary, {packet, 0}, {active, once}],
{ok, TSock} = gen_tcp:connect("localhost", 6969, TcpOpts),
{ok, TSock} = gen_tcp:connect("localhost", PortNum, TcpOpts),
ok = inet:setopts(TSock, [{active, once}]),
{ok, NSock, _HandshakeState} = enoise:connect(TSock, noise_options()),
% need keyboard process
Self = self(),
spawn_link(fun() -> kbd_loop(Self) end),
j_loop(#js{tsock = TSock, nsock = NSock}).
noise_options() ->
@@ -22,15 +36,73 @@ noise_options() ->
{e, KP}].
j_loop(JS = #js{nsock = NSock}) ->
j_loop(JS = #js{rcv = Rcv, tsock = TSock, nsock = NSock}) ->
ok = inet:setopts(TSock, [{active, once}]),
receive
{noise, NSock, Bytes} ->
ok = io:format("< ~tp~n", [Bytes]),
j_loop(JS);
NewRcv = <<Rcv/binary, Bytes/binary>>,
io:format("NewRcv = ~p~n", [NewRcv]),
NewJS = j_received(NewRcv, JS),
j_loop(NewJS);
{kbd, line, Line} ->
NewJS = j_kbdln(Line, JS),
j_loop(NewJS);
{tcp_closed, TSock} ->
io:format("tcp socket closed unexpectedly, exiting~n"),
exit(tcp_closed);
Unknown ->
ok = io:format("~tp unknown message, killing self: ~tp~n",
[self(), Unknown]),
exit(normal)
ok = io:format("unknown message ~tp~n", [Unknown]),
j_loop(JS)
end.
j_kbdln(Line, JS = #js{nsock = NS}) ->
FinalMsg = unicode:characters_to_binary([string:chomp(Line), "\r\n"]),
ok = enoise:send(NS, FinalMsg),
JS.
% not the time for readline
% % user hit return, flush queue
% j_kbd("\r", JS = #js{kbdq = KQ, nsock = NS}) ->
% FinalMsg = unicode:characters_to_binary([KQ, "\r\n"]),
% ok = enoise:send(NS, FinalMsg),
% NewJS = JS#js{kbdq = []}
% NewJS;
% % user hit something else, add to queue
% % fixme: parse ANSI escape sequences here, maybe xterm
% j_kbd(Str1, JS = #js{kbdq = KQ}) ->
% JS#js{kbdq = KQ ++ Str1}.
j_received(Rcv, JS) ->
case takeln(Rcv) of
none ->
JS#js{rcv = Rcv};
{line, Line, Rcv2} ->
ok = j_rln(Line),
j_received(Rcv2, JS)
end.
j_rln(Line) ->
io:format("< ~ts~n", [string:chomp(Line)]).
takeln(X) ->
takeln(<<>>, X).
takeln(Acc, <<"\r\n", Rest/binary>>) ->
{line, <<Acc/binary, "\r\n">>, Rest};
takeln(Acc, <<B:8, Rest/binary>>) ->
takeln(<<Acc/binary, B:8>>, Rest);
takeln(_Acc, <<>>) ->
none.
kbd_loop(Parent) ->
Line = io:get_line("> "),
Parent ! {kbd, line, Line},
kbd_loop(Parent).
%kbd_loop(Parent) ->
% Char = io:get_chars("", 1),
% Parent ! {kbd, Char},
% kbd_loop(Parent).
+41 -20
View File
@@ -5,9 +5,10 @@
% ho from pimp pov
-record(pho,
{pid :: pid(),
ref :: reference(),
name :: string()}).
{pid = none :: none | pid(),
ref = none :: none | reference(),
taken = false :: boolean(),
name = none :: none | string()}).
%% pimp state
-record(ps,
@@ -25,10 +26,9 @@
main([]) ->
io:format("RBX example 4 telnet echo server daemon~n"
"`one' part of the many-to-one relationship~n",
[]),
p_init().
p_init(6969);
main([PortNum]) ->
p_init(list_to_integer(PortNum)).
noise_options() ->
@@ -43,9 +43,8 @@ noise_options() ->
% pimp process startup
p_init() ->
p_init(Port) ->
p_logln("pimp startup (p_init)"),
Port = 6969,
Opts = [binary, {packet, 0}, {active, once}],
case gen_tcp:listen(Port, Opts) of
{ok, LSock} ->
@@ -60,12 +59,12 @@ p_init() ->
p_loop(PS = #ps{hos = Hos}) ->
receive
{ho, _HoPid, taken} ->
NewPS = p_spawn_ho(PS),
{ho, HoPid, taken} ->
NewPS = p_taken(HoPid, PS),
p_loop(NewPS);
{ho, HoPid, sent, Data} ->
HoName = p_ho_name(HoPid, PS),
SendMessage = io_lib:format("~ts :: ~ts\r\n", [HoName, Data]),
SendMessage = io_lib:format("~ts> ~ts\r\n", [HoName, Data]),
ok = p_echo_except(HoPid, HoName, SendMessage, Hos),
p_loop(PS);
Down = {'DOWN', _, _, _, _} ->
@@ -76,6 +75,20 @@ p_loop(PS = #ps{hos = Hos}) ->
p_loop(PS)
end.
p_taken(Pid, PS = #ps{hos = Hos}) ->
Hos_II = mark_taken(Pid, Hos, []),
PS_II = PS#ps{hos = Hos_II},
PS_III = p_spawn_ho(PS_II),
PS_III.
mark_taken(Pid, [Ho = #pho{pid = Pid} | Rest], Acc) ->
NewHo = Ho#pho{taken = true},
lists:reverse(Acc) ++ [NewHo | Rest];
mark_taken(Pid, [WrongHo | Rest], Acc) ->
mark_taken(Pid, Rest, [WrongHo | Acc]);
mark_taken(_, [], Acc) ->
lists:reverse(Acc).
% given pid of ho, get her name
p_ho_name(HoPid, #ps{hos = Hos}) ->
case lists:keyfind(HoPid, #pho.pid, Hos) of
@@ -100,7 +113,8 @@ p_spawn_ho(PS = #ps{lsock = LSock, hos = Hos, ho_names = Names}) ->
% ho down, remove her from roster
p_ho_down({'DOWN', _Ref, process, Pid, Info}, PS = #ps{hos = Hos}) ->
ok = p_logfln("ho down: ~tp; reason: ~tw", [Pid, Info]),
Ho = lists:keyfind(Pid, #pho.pid, Hos),
ok = p_logfln("ho down: ~ts; reason: ~tw", [Ho#pho.name, Info]),
NewHos = lists:keydelete(Pid, #pho.pid, Hos),
NewPS = PS#ps{hos = NewHos},
NewPS.
@@ -110,7 +124,10 @@ p_ho_down({'DOWN', _Ref, process, Pid, Info}, PS = #ps{hos = Hos}) ->
% skip if equal
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = SrcPid} | Rest]) ->
p_echo_except(SrcPid, SrcName, Data, Rest);
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = DstPid} | Rest]) ->
% skip if not taken
p_echo_except(SrcPid, SrcName, Data, [#pho{taken = false} | Rest]) ->
p_echo_except(SrcPid, SrcName, Data, Rest);
p_echo_except(SrcPid, SrcName, Data, [#pho{pid = DstPid, taken = true} | Rest]) ->
DstPid ! {pimp, send, Data},
p_echo_except(SrcPid, SrcName, Data, Rest);
p_echo_except(_, _, _, []) ->
@@ -147,7 +164,8 @@ h_loop(HS = #hs{name = Name, daddy = Daddy, asock = ASock, nsock = NSock}) ->
ok = h_rcvd(Name, Data_II),
Daddy ! {ho, self(), sent, Data_II},
h_loop(HS);
{pimp, send, Data} ->
{pimp, send, Data} = M ->
io:format("~p received ~p~n", [self(), M]),
ok = h_send(Name, NSock, Data),
h_loop(HS);
{tcp_closed, ASock} ->
@@ -160,7 +178,7 @@ h_loop(HS = #hs{name = Name, daddy = Daddy, asock = ASock, nsock = NSock}) ->
% pimp log in bold
p_logln(LogStr) ->
Msg = io_lib:format("!! Daddy :: ~ts~n", [LogStr]),
Msg = io_lib:format("Daddy: ~ts~n", [LogStr]),
PimpChars = ?ANSI_BOLD([Msg]),
io:put_chars(PimpChars).
@@ -170,7 +188,7 @@ p_logfln(LogStr, Args) ->
% ho log dimmed
h_logln(HoName, LogStr) ->
FMsg = io_lib:format("!! ~ts :: ~ts~n", [HoName, LogStr]),
FMsg = io_lib:format("~ts: ~ts~n", [HoName, LogStr]),
HoChars = ?ANSI_DIM([FMsg]),
io:put_chars(HoChars).
@@ -179,12 +197,15 @@ h_logfln(HoName, MsgStr, Args) ->
% normal: john messages
h_rcvd(Name, MsgStr) ->
Msg = io_lib:format("<< ~ts :: ~ts~n", [Name, MsgStr]),
Msg = io_lib:format("~ts< ~ts~n", [Name, string:chomp(MsgStr)]),
io:put_chars(Msg).
h_send(Name, NSock, Msg) ->
ok = io:format(">> ~ts :: ~ts~n", [Name, Msg]),
enoise:send(NSock, Msg).
FMsg = io_lib:format("~ts> ~ts~n", [Name, string:chomp(Msg)]),
HoChars = [?ANSI_DIM(FMsg)],
ok = io:put_chars(HoChars),
Send = unicode:characters_to_binary([string:chomp(Msg), "\r\n"]),
ok = enoise:send(NSock, Send).
init_ho_names() ->