mirror of
https://github.com/urbit/shrub.git
synced 2024-12-15 04:22:48 +03:00
Merge branch 'nextbreach' of https://github.com/urbit/urbit into nextbreach
This commit is contained in:
commit
26e99cbb23
19
Makefile
19
Makefile
@ -34,7 +34,7 @@ RM=rm -f
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
CXXFLAGS=$(CFLAGS)
|
||||
CLD=g++ -O -g -L/usr/local/lib -L/opt/local/lib
|
||||
CLD=g++ -O2 -g -L/usr/local/lib -L/opt/local/lib
|
||||
|
||||
ifeq ($(OS),osx)
|
||||
CLDOSFLAGS=-bind_at_load
|
||||
@ -57,11 +57,13 @@ endif
|
||||
INCLUDE=include
|
||||
MDEFINES=-DU2_OS_$(OS) -DU2_OS_ENDIAN_$(ENDIAN) -D U2_LIB=\"$(LIB)\"
|
||||
|
||||
CFLAGS= -g \
|
||||
CFLAGS= -O2 -g \
|
||||
-funsigned-char \
|
||||
-I/usr/local/include \
|
||||
-I/opt/local/include \
|
||||
-I$(INCLUDE) \
|
||||
-Ioutside/libuv/include \
|
||||
-Ioutside/anachronism/include \
|
||||
-Ioutside/bpt \
|
||||
-Ioutside/re2 \
|
||||
-Ioutside/cre2/src/src \
|
||||
@ -151,12 +153,15 @@ J164_4_OFILES=\
|
||||
gen164/4/in.o \
|
||||
gen164/4/by.o \
|
||||
gen164/4/in_has.o \
|
||||
gen164/4/in_int.o \
|
||||
gen164/4/in_gas.o \
|
||||
gen164/4/in_put.o \
|
||||
gen164/4/in_tap.o \
|
||||
gen164/4/in_uni.o \
|
||||
gen164/4/by_gas.o \
|
||||
gen164/4/by_get.o \
|
||||
gen164/4/by_has.o \
|
||||
gen164/4/by_int.o \
|
||||
gen164/4/by_put.o \
|
||||
gen164/4/by_uni.o
|
||||
|
||||
@ -292,6 +297,8 @@ LIBRE2=outside/re2/obj/libre2.a
|
||||
|
||||
LIBED25519=outside/ed25519/ed25519.a
|
||||
|
||||
LIBANACHRONISM=outside/anachronism/build/libanachronism.a
|
||||
|
||||
BPT_O=outside/bpt/bitmapped_patricia_tree.o
|
||||
|
||||
all: $(BIN)/vere
|
||||
@ -305,6 +312,9 @@ $(LIBRE2):
|
||||
$(LIBED25519):
|
||||
$(MAKE) -C outside/ed25519
|
||||
|
||||
$(LIBANACHRONISM):
|
||||
$(MAKE) -C outside/anachronism static
|
||||
|
||||
$(BPT_O): outside/bpt/bitmapped_patricia_tree.c
|
||||
$(CC) -g -O2 -o $@ -c $<
|
||||
|
||||
@ -313,9 +323,9 @@ $(CRE2_OFILES): outside/cre2/src/src/cre2.cpp outside/cre2/src/src/cre2.h $(LIBR
|
||||
|
||||
$(V_OFILES) f/loom.o f/trac.o: include/v/vere.h
|
||||
|
||||
$(BIN)/vere: $(LIBCRE) $(VERE_OFILES) $(LIBUV) $(LIBRE2) $(LIBED25519) $(BPT_O)
|
||||
$(BIN)/vere: $(LIBCRE) $(VERE_OFILES) $(LIBUV) $(LIBRE2) $(LIBED25519) $(BPT_O) $(LIBANACHRONISM)
|
||||
mkdir -p $(BIN)
|
||||
$(CLD) $(CLDOSFLAGS) -o $(BIN)/vere $(VERE_OFILES) $(LIBUV) $(LIBCRE) $(LIBRE2) $(LIBED25519) $(BPT_O) $(LIBS)
|
||||
$(CLD) $(CLDOSFLAGS) -o $(BIN)/vere $(VERE_OFILES) $(LIBUV) $(LIBCRE) $(LIBRE2) $(LIBED25519) $(BPT_O) $(LIBANACHRONISM) $(LIBS)
|
||||
|
||||
tags:
|
||||
ctags -R -f .tags --exclude=root
|
||||
@ -349,6 +359,7 @@ distclean: clean
|
||||
$(MAKE) -C outside/libuv clean
|
||||
$(MAKE) -C outside/re2 clean
|
||||
$(MAKE) -C outside/ed25519 clean
|
||||
$(MAKE) -C outside/anachronism clean
|
||||
$(RM) $(BPT_O)
|
||||
|
||||
.PHONY: clean distclean
|
||||
|
@ -10,6 +10,7 @@
|
||||
extern u2_ho_jet j2_mcj(Pt4, by, gas)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, by, get)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, by, has)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, by, int)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, by, put)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, by, uni)[];
|
||||
|
||||
@ -20,6 +21,7 @@
|
||||
{ j2_sc(Pt4, by, gas), j2_mcj(Pt4, by, gas), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, by, get), j2_mcj(Pt4, by, get), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, by, has), j2_mcj(Pt4, by, has), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, by, int), j2_mcj(Pt4, by, int), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, by, put), j2_mcj(Pt4, by, put), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, by, uni), j2_mcj(Pt4, by, uni), 0, 0, u2_none },
|
||||
{}
|
||||
|
135
gen164/4/by_int.c
Normal file
135
gen164/4/by_int.c
Normal file
@ -0,0 +1,135 @@
|
||||
/* j/4/by_int.c
|
||||
**
|
||||
** This file is in the public domain.
|
||||
*/
|
||||
#include "all.h"
|
||||
#include "../pit.h"
|
||||
|
||||
/* functions
|
||||
*/
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, by, int)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b) // retain
|
||||
{
|
||||
if ( u2_nul == a ) {
|
||||
return u2_rx(wir_r, u2_nul);
|
||||
}
|
||||
else if ( u2_nul == b ) {
|
||||
return u2_rx(wir_r, u2_nul);
|
||||
}
|
||||
else {
|
||||
u2_noun l_a, n_a, r_a, lr_a, p_n_a, q_n_a;
|
||||
u2_noun l_b, n_b, r_b, lr_b, p_n_b, q_n_b;
|
||||
|
||||
if ( (u2_no == u2_as_cell(a, &n_a, &lr_a)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( (u2_no == u2_as_cell(b, &n_b, &lr_b)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_a, &l_a, &r_a) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_b, &l_b, &r_b) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(n_a, &p_n_a, &q_n_a) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(n_b, &p_n_b, &q_n_b) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, vor)(wir_r, p_n_a, p_n_b) ) {
|
||||
if ( u2_yes == u2_sing(p_n_a, p_n_b) ) {
|
||||
return u2_rt(
|
||||
wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, by, int)(wir_r, u2_rx(wir_r, l_a), u2_rx(wir_r, l_b)),
|
||||
j2_mcc(Pt4, by, int)(wir_r, u2_rx(wir_r, r_a), u2_rx(wir_r, r_b)));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, p_n_b, p_n_a) ) {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, l_b),
|
||||
u2_rx(wir_r, u2_nul))),
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, a),
|
||||
u2_rx(wir_r, r_b)));
|
||||
}
|
||||
else {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, r_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, u2_nul),
|
||||
u2_rx(wir_r, r_b))),
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, a),
|
||||
u2_rx(wir_r, l_b)));
|
||||
}
|
||||
}
|
||||
else if ( u2_yes == u2_sing(p_n_b, p_n_a) ) {
|
||||
return u2_rt(
|
||||
wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, by, int)(wir_r, u2_rx(wir_r, l_b), u2_rx(wir_r, l_a)),
|
||||
j2_mcc(Pt4, by, int)(wir_r, u2_rx(wir_r, r_b), u2_rx(wir_r, r_a)));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, p_n_a, p_n_b) ) {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, l_b),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rx(wir_r, u2_nul))),
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, a),
|
||||
u2_rx(wir_r, r_a)));
|
||||
}
|
||||
else {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, r_b),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, u2_nul),
|
||||
u2_rx(wir_r, r_a))),
|
||||
j2_mcc(Pt4, by, int)(wir_r,
|
||||
u2_rx(wir_r, a),
|
||||
u2_rx(wir_r, l_a)));
|
||||
}
|
||||
}
|
||||
}
|
||||
u2_weak // transfer
|
||||
j2_mc(Pt4, by, int)(u2_wire wir_r,
|
||||
u2_noun cor) // retain
|
||||
{
|
||||
u2_noun a, b;
|
||||
|
||||
if ( u2_no == u2_mean(cor, u2_cv_sam, &b, u2_cv_con_sam, &a, 0) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
} else {
|
||||
return j2_mcc(Pt4, by, int)(wir_r, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/* structures
|
||||
*/
|
||||
u2_ho_jet
|
||||
j2_mcj(Pt4, by, int)[] = {
|
||||
{ ".2", c3__lite, j2_mc(Pt4, by, int), Tier4, u2_none, u2_none },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* j/4/uni.c
|
||||
/* j/4/by_uni.c
|
||||
**
|
||||
** This file is in the public domain.
|
||||
*/
|
||||
@ -12,25 +12,105 @@
|
||||
u2_noun a, // retain
|
||||
u2_noun b) // retain
|
||||
{
|
||||
if ( u2_nul == b ) {
|
||||
return u2k(a);
|
||||
if ( u2_nul == a ) {
|
||||
return u2_rx(wir_r, b);
|
||||
}
|
||||
else if ( u2_nul == b ) {
|
||||
return u2_rx(wir_r, a);
|
||||
}
|
||||
else {
|
||||
u2_noun l_b, n_b, r_b;
|
||||
u2_noun pn_b, qn_b;
|
||||
u2_noun l_a, n_a, r_a, lr_a, p_n_a, q_n_a;
|
||||
u2_noun l_b, n_b, r_b, lr_b, p_n_b, q_n_b;
|
||||
|
||||
if ( (u2_no == u2_as_trel(b, &n_b, &l_b, &r_b)) ||
|
||||
(u2_no == u2_as_cell(n_b, &pn_b, &qn_b)) )
|
||||
{
|
||||
if ( (u2_no == u2_as_cell(a, &n_a, &lr_a)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( (u2_no == u2_as_cell(b, &n_b, &lr_b)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( (u2_no == u2_as_cell(n_a, &p_n_a, &q_n_a)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( (u2_no == u2_as_cell(n_b, &p_n_b, &q_n_b)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_a, &l_a, &r_a) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_b, &l_b, &r_b) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, vor)(wir_r, p_n_a, p_n_b) ) {
|
||||
if ( u2_yes == u2_sing(p_n_a, p_n_b) ) {
|
||||
return u2_rt(wir_r, n_b,
|
||||
j2_mcc(Pt4, by, uni)(
|
||||
wir_r, u2_rx(wir_r, l_a), u2_rx(wir_r, l_b)),
|
||||
j2_mcc(Pt4, by, uni)(
|
||||
wir_r, u2_rx(wir_r, r_a), u2_rx(wir_r, r_b)));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, p_n_b, p_n_a) ) {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
j2_mcc(Pt4, by, uni)(wir_r,
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, l_b),
|
||||
u2_rx(wir_r, u2_nul))),
|
||||
u2_rx(wir_r, r_a)),
|
||||
u2_rx(wir_r, r_b));
|
||||
}
|
||||
else {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, l_a),
|
||||
j2_mcc(Pt4, by, uni)(wir_r,
|
||||
u2_rx(wir_r, r_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, u2_nul),
|
||||
u2_rx(wir_r, r_b)))),
|
||||
u2_rx(wir_r, l_b));
|
||||
}
|
||||
}
|
||||
else if ( u2_yes == u2_sing(p_n_b, p_n_a) ) {
|
||||
return u2_rt(
|
||||
wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, by, uni)(wir_r, u2_rx(wir_r, r_b), u2_rx(wir_r, r_a)),
|
||||
j2_mcc(Pt4, by, uni)(wir_r, u2_rx(wir_r, l_b), u2_rx(wir_r, l_a)));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, p_n_a, p_n_b) ) {
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
u2_rx(wir_r, r_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, by, uni)(wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rx(wir_r, u2_nul)),
|
||||
u2_rx(wir_r, l_b)),
|
||||
u2_rx(wir_r, r_b)));
|
||||
}
|
||||
else {
|
||||
u2_noun l = j2_mcc(Pt4, by, uni)(wir_r, a, l_b);
|
||||
u2_noun r = j2_mcc(Pt4, by, uni)(wir_r, l, r_b);
|
||||
u2_noun z = j2_mcc(Pt4, by, put)(wir_r, r, pn_b, qn_b);
|
||||
|
||||
u2z(l); u2z(r);
|
||||
return z;
|
||||
return j2_mcc(Pt4, by, uni)(
|
||||
wir_r,
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, l_b),
|
||||
j2_mcc(Pt4, by, uni)(wir_r,
|
||||
u2_rx(wir_r, r_b),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, u2_nul),
|
||||
u2_rx(wir_r, r_a)))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,3 +134,4 @@
|
||||
{ ".2", c3__lite, j2_mc(Pt4, by, uni), Tier4, u2_none, u2_none },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -9,8 +9,10 @@
|
||||
*/
|
||||
extern u2_ho_jet j2_mcj(Pt4, in, gas)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, in, has)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, in, int)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, in, put)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, in, tap)[];
|
||||
extern u2_ho_jet j2_mcj(Pt4, in, uni)[];
|
||||
|
||||
/* structures
|
||||
*/
|
||||
@ -18,8 +20,10 @@
|
||||
j2_mbd(Pt4, in)[] = {
|
||||
{ j2_sc(Pt4, in, gas), j2_mcj(Pt4, in, gas), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, in, has), j2_mcj(Pt4, in, has), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, in, int), j2_mcj(Pt4, in, int), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, in, put), j2_mcj(Pt4, in, put), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, in, tap), j2_mcj(Pt4, in, tap), 0, 0, u2_none },
|
||||
{ j2_sc(Pt4, in, uni), j2_mcj(Pt4, in, uni), 0, 0, u2_none },
|
||||
{}
|
||||
};
|
||||
|
||||
|
97
gen164/4/in_int.c
Normal file
97
gen164/4/in_int.c
Normal file
@ -0,0 +1,97 @@
|
||||
/* j/4/in_int.c
|
||||
**
|
||||
** This file is in the public domain.
|
||||
*/
|
||||
#include "all.h"
|
||||
#include "../pit.h"
|
||||
|
||||
/* functions
|
||||
*/
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, int)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b) // retain
|
||||
{
|
||||
if ( u2_nul == a ) {
|
||||
return u2_rx(wir_r, u2_nul);
|
||||
}
|
||||
else if ( u2_nul == b ) {
|
||||
return u2_rx(wir_r, u2_nul);
|
||||
}
|
||||
else {
|
||||
u2_noun l_a, n_a, r_a, lr_a;
|
||||
u2_noun l_b, n_b, r_b, lr_b;
|
||||
u2_noun c;
|
||||
|
||||
if ( (u2_no == u2_as_cell(a, &n_a, &lr_a)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( (u2_no == u2_as_cell(b, &n_b, &lr_b)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else {
|
||||
if ( u2_yes == j2_mbc(Pt3, vor)(wir_r, n_b, n_a) ) {
|
||||
c = a; a = b; b = c;
|
||||
c = n_a; n_a = n_b; n_b = c;
|
||||
c = lr_a; lr_a = lr_b; lr_b = c;
|
||||
}
|
||||
if ( u2_no == u2_as_cell(lr_a, &l_a, &r_a) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_b, &l_b, &r_b) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_yes == u2_sing(n_a, n_b) ) {
|
||||
return u2_rt(wir_r, u2_rx(wir_r, n_a),
|
||||
j2_mcc(Pt4, in, int)(wir_r, l_a, l_b),
|
||||
j2_mcc(Pt4, in, int)(wir_r, r_a, r_b));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, n_b, n_a) ) {
|
||||
return j2_mcc(Pt4, in, uni)(wir_r,
|
||||
j2_mcc(Pt4, in, int)(wir_r,
|
||||
l_a,
|
||||
u2_rt(wir_r,
|
||||
n_b,
|
||||
l_b,
|
||||
u2_nul)),
|
||||
j2_mcc(Pt4, in, int)(wir_r,
|
||||
a,
|
||||
r_b));
|
||||
}
|
||||
else {
|
||||
return j2_mcc(Pt4, in, uni)(wir_r,
|
||||
j2_mcc(Pt4, in, int)(wir_r,
|
||||
r_a,
|
||||
u2_rt(wir_r,
|
||||
n_b,
|
||||
u2_nul,
|
||||
r_b)),
|
||||
j2_mcc(Pt4, in, int)(wir_r,
|
||||
a,
|
||||
l_b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
u2_weak // transfer
|
||||
j2_mc(Pt4, in, int)(u2_wire wir_r,
|
||||
u2_noun cor) // retain
|
||||
{
|
||||
u2_noun a, b;
|
||||
|
||||
if ( u2_no == u2_mean(cor, u2_cv_sam, &b, u2_cv_con_sam, &a, 0) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
} else {
|
||||
return j2_mcc(Pt4, in, int)(wir_r, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/* structures
|
||||
*/
|
||||
u2_ho_jet
|
||||
j2_mcj(Pt4, in, int)[] = {
|
||||
{ ".2", c3__lite, j2_mc(Pt4, in, int), Tier4, u2_none, u2_none },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
139
gen164/4/in_uni.c
Normal file
139
gen164/4/in_uni.c
Normal file
@ -0,0 +1,139 @@
|
||||
/* j/4/in_uni.c
|
||||
**
|
||||
** This file is in the public domain.
|
||||
*/
|
||||
#include "all.h"
|
||||
#include "../pit.h"
|
||||
|
||||
/* functions
|
||||
*/
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, uni)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b) // retain
|
||||
{
|
||||
if ( u2_nul == a ) {
|
||||
return u2_rx(wir_r, b);
|
||||
}
|
||||
else if ( u2_nul == b ) {
|
||||
return u2_rx(wir_r, a);
|
||||
}
|
||||
else {
|
||||
u2_noun l_a, n_a, r_a, lr_a;
|
||||
u2_noun l_b, n_b, r_b, lr_b;
|
||||
|
||||
if ( (u2_no == u2_as_cell(a, &n_a, &lr_a)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( (u2_no == u2_as_cell(b, &n_b, &lr_b)) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else {
|
||||
if ( u2_yes == j2_mbc(Pt3, vor)(wir_r, n_a, n_b) ) {
|
||||
if ( u2_no == u2_as_cell(lr_a, &l_a, &r_a) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_b, &l_b, &r_b) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_yes == u2_sing(n_a, n_b) ) {
|
||||
return u2_rt(
|
||||
wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, in, uni)(wir_r, u2_rx(wir_r, l_a), u2_rx(wir_r, l_b)),
|
||||
j2_mcc(Pt4, in, uni)(wir_r, u2_rx(wir_r, r_a), u2_rx(wir_r, r_b)));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, n_b, n_a) ) {
|
||||
return j2_mcc(Pt4, in, uni)(
|
||||
wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
j2_mcc(Pt4, in, uni)(wir_r,
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, l_b),
|
||||
u2_rx(wir_r, u2_nul))),
|
||||
u2_rx(wir_r, r_a)),
|
||||
u2_rx(wir_r, r_b));
|
||||
}
|
||||
else {
|
||||
return j2_mcc(Pt4, in, uni)(
|
||||
wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, l_a),
|
||||
j2_mcc(Pt4, in, uni)(wir_r,
|
||||
u2_rx(wir_r, r_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, u2_nul),
|
||||
u2_rx(wir_r, r_b)))),
|
||||
u2_rx(wir_r, l_b));
|
||||
}
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_b, &l_b, &r_b) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_no == u2_as_cell(lr_a, &l_a, &r_a) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
}
|
||||
else if ( u2_yes == u2_sing(n_b, n_a) ) {
|
||||
return u2_rt(
|
||||
wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, in, uni)(wir_r, u2_rx(wir_r, r_b), u2_rx(wir_r, r_a)),
|
||||
j2_mcc(Pt4, in, uni)(wir_r, u2_rx(wir_r, l_b), u2_rx(wir_r, l_a)));
|
||||
}
|
||||
else if ( u2_yes == j2_mbc(Pt3, hor)(wir_r, n_a, n_b) ) {
|
||||
return j2_mcc(Pt4, in, uni)(
|
||||
wir_r,
|
||||
u2_rx(wir_r, r_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
j2_mcc(Pt4, in, uni)(wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_nul),
|
||||
u2_rx(wir_r, l_b)),
|
||||
u2_rx(wir_r, r_b)));
|
||||
}
|
||||
else {
|
||||
return j2_mcc(Pt4, in, uni)(
|
||||
wir_r,
|
||||
u2_rx(wir_r, l_a),
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_b),
|
||||
u2_rx(wir_r, l_b),
|
||||
j2_mcc(Pt4, in, uni)(wir_r,
|
||||
u2_rt(wir_r,
|
||||
u2_rx(wir_r, n_a),
|
||||
u2_rx(wir_r, u2_nul),
|
||||
u2_rx(wir_r, r_a)),
|
||||
u2_rx(wir_r, r_b))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
u2_weak // transfer
|
||||
j2_mc(Pt4, in, uni)(u2_wire wir_r,
|
||||
u2_noun cor) // retain
|
||||
{
|
||||
u2_noun a, b;
|
||||
|
||||
if ( u2_no == u2_mean(cor, u2_cv_sam, &b, u2_cv_con_sam, &a, 0) ) {
|
||||
return u2_bl_bail(wir_r, c3__exit);
|
||||
} else {
|
||||
return j2_mcc(Pt4, in, uni)(wir_r, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/* structures
|
||||
*/
|
||||
u2_ho_jet
|
||||
j2_mcj(Pt4, in, uni)[] = {
|
||||
{ ".2", c3__lite, j2_mc(Pt4, in, uni), Tier4, u2_none, u2_none },
|
||||
{ }
|
||||
};
|
||||
|
@ -18,7 +18,6 @@
|
||||
c3_y* rep_y = u2_cr_tape(rep);
|
||||
|
||||
|
||||
|
||||
char* rec = (char*)lub_y;
|
||||
char* end;
|
||||
while(*rec != 0) {
|
||||
@ -59,7 +58,7 @@
|
||||
opt = cre2_opt_new();
|
||||
if (opt) {
|
||||
cre2_opt_set_log_errors(opt, 0);
|
||||
cre2_opt_set_encoding(opt, CRE2_Latin1);
|
||||
cre2_opt_set_encoding(opt, CRE2_UTF8);
|
||||
cre2_opt_set_perl_classes(opt, 1);
|
||||
cre2_opt_set_one_line(opt, 1);
|
||||
cre2_opt_set_longest_match(opt, 1);
|
||||
@ -76,7 +75,7 @@
|
||||
|
||||
if (!match) {
|
||||
if(rad_y[ic])
|
||||
ret = u2_cn_cell((u2_atom)rad_y[ic], ret);
|
||||
ret = u2_cn_cell((c3_y)rad_y[ic], ret);
|
||||
ic++;
|
||||
}
|
||||
else {
|
||||
|
169
gen164/6/ap.c
169
gen164/6/ap.c
@ -85,6 +85,15 @@
|
||||
}
|
||||
/***
|
||||
****
|
||||
***/
|
||||
_open_do_p(bczp) // $!
|
||||
{
|
||||
return u2_bt
|
||||
(wir_r, c3__bccb, c3__axil, u2_rx(wir_r, p_gen));
|
||||
}
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
_open_do_p(brhp) // |-
|
||||
{
|
||||
@ -102,9 +111,7 @@
|
||||
u2_nul,
|
||||
u2_nul));
|
||||
}
|
||||
/***
|
||||
****
|
||||
***/
|
||||
|
||||
/***
|
||||
****
|
||||
***/
|
||||
@ -124,6 +131,17 @@
|
||||
_open_in_wtbr(wir_r, tp_gen));
|
||||
}
|
||||
}
|
||||
_open_do_pqr(wtkt) // ?^
|
||||
{
|
||||
// [%wtkt *] [%wtcl [%wtts [%axil %atom %$] p.gen] r.gen q.gen]
|
||||
return u2_bq
|
||||
(wir_r, c3__wtcl,
|
||||
u2_bt(wir_r, c3__wtts,
|
||||
u2_bt(wir_r, c3__axil, c3__atom, u2_blip),
|
||||
u2_rx(wir_r, p_gen)),
|
||||
u2_rx(wir_r, r_gen),
|
||||
u2_rx(wir_r, q_gen));
|
||||
}
|
||||
_open_do_pq(wtgl) // ?<
|
||||
{
|
||||
return u2_bq
|
||||
@ -184,10 +202,9 @@
|
||||
u2_bt(wir_r, c3__dtzz, 'f', u2_no));
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
_open_do_pqr(wtls) // ?+
|
||||
{ u2_noun tul = u2_bc(wir_r,
|
||||
u2_bc(wir_r, u2_bc(wir_r, c3__bcts, c3__noun),
|
||||
u2_bc(wir_r, u2_bc(wir_r, c3__axil, c3__noun),
|
||||
u2_rx(wir_r, q_gen)),
|
||||
u2_nul);
|
||||
u2_noun zal = j2_mbc(Pt2, weld)(wir_r, r_gen, tul);
|
||||
@ -196,7 +213,6 @@
|
||||
u2_rz(wir_r, tul);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
_open_do_p(wtzp) // ?!
|
||||
{
|
||||
return u2_bq
|
||||
@ -210,7 +226,6 @@
|
||||
***/
|
||||
_open_do_pq(clhp) // :-
|
||||
{
|
||||
printf("wongwong!\n");
|
||||
return u2_bc
|
||||
(wir_r, u2_rx(wir_r, p_gen),
|
||||
u2_rx(wir_r, q_gen));
|
||||
@ -268,6 +283,15 @@
|
||||
/***
|
||||
****
|
||||
***/
|
||||
_open_do_pq(cncb) // %_
|
||||
{
|
||||
return u2_bc
|
||||
(wir_r, c3__ktls,
|
||||
u2_bq(wir_r, u2_bc(wir_r, c3__cnzz, u2_rx(wir_r, p_gen)),
|
||||
c3__cnts,
|
||||
u2_rx(wir_r, p_gen),
|
||||
u2_rx(wir_r, q_gen)));
|
||||
}
|
||||
_open_do_pq(cncl) // %:
|
||||
{
|
||||
return u2_bq
|
||||
@ -328,14 +352,60 @@
|
||||
u2_bc(wir_r, u2_nul, _6)),
|
||||
u2_rx(wir_r, r_gen))));
|
||||
}
|
||||
|
||||
_open_do_p(cnzy) // %cnzy
|
||||
{
|
||||
return u2_bt
|
||||
(wir_r, c3__cnts,
|
||||
u2_bc(wir_r, u2_rx(wir_r, p_gen), u2_nul),
|
||||
u2_nul);
|
||||
}
|
||||
_open_do_p(cnzz) // %cnzz
|
||||
{
|
||||
return u2_bt
|
||||
(wir_r, c3__cnts, u2_rx(wir_r, p_gen), u2_nul);
|
||||
}
|
||||
/***
|
||||
****
|
||||
***/
|
||||
_open_do_pq(brkt) // &-
|
||||
_open_do_p(hxgl) // #<
|
||||
{
|
||||
return u2_bq
|
||||
(wir_r, c3__cnhp,
|
||||
u2_bc(wir_r, c3__cnzy, c3__noah),
|
||||
u2_bc(wir_r, c3__zpgr,
|
||||
u2_bc(wir_r, c3__cltr, u2_rx(wir_r, p_gen))),
|
||||
u2_nul);
|
||||
}
|
||||
_open_do_p(hxgr) // #>
|
||||
{
|
||||
return u2_bq
|
||||
(wir_r, c3__cnhp,
|
||||
u2_bc(wir_r, c3__cnzy, c3__cain),
|
||||
u2_bc(wir_r, c3__zpgr,
|
||||
u2_bc(wir_r, c3__cltr, u2_rx(wir_r, p_gen))),
|
||||
u2_nul);
|
||||
}
|
||||
/***
|
||||
****
|
||||
***/
|
||||
_open_do_pq(ktdt) // ^.
|
||||
{
|
||||
return u2_bt
|
||||
(wir_r, c3__ktls,
|
||||
u2_bq(wir_r, c3__cnhp, u2_rx(wir_r, p_gen), u2_rx(wir_r, q_gen), u2_nul),
|
||||
u2_rx(wir_r, q_gen));
|
||||
}
|
||||
/***
|
||||
****
|
||||
***/
|
||||
_open_do_pq(brcb) // |_
|
||||
{
|
||||
return u2_bt(wir_r, c3__tsls,
|
||||
u2_bc(wir_r, c3__bctr, u2_rx(wir_r, p_gen)),
|
||||
u2_bc(wir_r, c3__brcn, u2_rx(wir_r, q_gen)));
|
||||
}
|
||||
_open_do_pq(brkt) // |^
|
||||
{
|
||||
// [%brkt *] [%tsgr [%brcn (~(put by q.gen) %% [%ash p.gen])] %%]
|
||||
//
|
||||
u2_noun diz = u2_bc(wir_r, c3__ash, u2_rx(wir_r, p_gen));
|
||||
u2_noun ret = u2_bt
|
||||
(wir_r,
|
||||
@ -348,6 +418,13 @@
|
||||
u2_rz(wir_r, diz);
|
||||
return ret;
|
||||
}
|
||||
_open_do_pq(brls) // |+
|
||||
{
|
||||
return u2_bc(wir_r, c3__ktbr,
|
||||
u2_bt(wir_r, c3__brts,
|
||||
u2_rx(wir_r, p_gen),
|
||||
u2_rx(wir_r, q_gen)));
|
||||
}
|
||||
/***
|
||||
****
|
||||
***/
|
||||
@ -378,6 +455,15 @@
|
||||
u2_bq(wir_r, c3__live,
|
||||
c3__dtzz, u2_blip, u2_rx(wir_r, p_gen)),
|
||||
u2_rx(wir_r, q_gen));
|
||||
}
|
||||
_open_do_pq(sgcb) // ~_
|
||||
{
|
||||
return u2_bt
|
||||
(wir_r, c3__sggr,
|
||||
u2_bc(wir_r, c3__mean,
|
||||
u2_bc(wir_r, c3__brdt,
|
||||
u2_rx(wir_r, p_gen))),
|
||||
u2_rx(wir_r, q_gen));
|
||||
}
|
||||
static u2_noun
|
||||
_sgcn_a(u2_wire wir_r,
|
||||
@ -415,6 +501,15 @@
|
||||
_sgcn_a(wir_r, r_gen, u2_nul))),
|
||||
u2_rx(wir_r, s_gen));
|
||||
}
|
||||
_open_do_pq(sgfs) // ~/
|
||||
{
|
||||
return u2_bc
|
||||
(wir_r, c3__sgcn,
|
||||
u2_bq(wir_r, u2_rx(wir_r, p_gen),
|
||||
u2_bc(wir_r, u2_nul, _7),
|
||||
u2_nul,
|
||||
u2_rx(wir_r, q_gen)));
|
||||
}
|
||||
_open_do_pq(sgls) // ~+
|
||||
{
|
||||
return u2_bt
|
||||
@ -465,12 +560,6 @@
|
||||
_smcl_in(wir_r, q_gen));
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
_open_do_p(smdq)
|
||||
{
|
||||
return _ap_snig(p_gen);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* functions
|
||||
*/
|
||||
@ -526,49 +615,57 @@
|
||||
return u2_bl_bail(wir_r, c3__fail); \
|
||||
} else return _open_in_##stem(wir_r, p_gen, q_gen, r_gen, s_gen);
|
||||
|
||||
_open_pq (tsgl);
|
||||
_open_pq (tshp);
|
||||
_open_pq (tsls);
|
||||
|
||||
_open_p (bczp);
|
||||
_open_p (brhp);
|
||||
_open_p (brdt);
|
||||
|
||||
_open_pq (brcb);
|
||||
_open_pq (brkt);
|
||||
_open_pq (brls);
|
||||
_open_pq (clcb);
|
||||
_open_pq (clhp);
|
||||
_open_pqrs(clkt);
|
||||
_open_pqr (clls);
|
||||
_open_p (cltr);
|
||||
_open_p (clsg);
|
||||
|
||||
_open_pqr (wtdt);
|
||||
_open_pq (wtgl);
|
||||
_open_p (wtzp);
|
||||
_open_p (wtbr);
|
||||
_open_p (wtpm);
|
||||
// _open_pqr (wtls);
|
||||
// _open_pqr (wtsg);
|
||||
_open_pq (wthp);
|
||||
_open_pq (wtgr);
|
||||
|
||||
_open_pq (cncb);
|
||||
_open_pq (cncl);
|
||||
_open_pq (cndt);
|
||||
_open_pqrs(cnkt);
|
||||
_open_pq (cnhp);
|
||||
_open_pqr (cnls);
|
||||
_open_pqr (cnsg);
|
||||
_open_p (cnzy);
|
||||
_open_p (cnzz);
|
||||
|
||||
_open_pq (brkt);
|
||||
_open_p (hxgl);
|
||||
_open_p (hxgr);
|
||||
|
||||
_open_pq (ktdt);
|
||||
|
||||
_open_pq (sgts);
|
||||
_open_pq (sgbr);
|
||||
_open_pq (sggl);
|
||||
_open_pq (sgbc);
|
||||
_open_pq (sgcb);
|
||||
_open_pqrs(sgcn);
|
||||
_open_pq (sgfs);
|
||||
_open_pq (sgls);
|
||||
|
||||
_open_pq (smcl);
|
||||
// _open_p (smdq);
|
||||
// _open_pq (smsg);
|
||||
|
||||
_open_pq (tsgl);
|
||||
_open_pq (tshp);
|
||||
_open_pq (tsls);
|
||||
|
||||
_open_pqr (wtdt);
|
||||
_open_pq (wtgl);
|
||||
_open_p (wtzp);
|
||||
_open_p (wtbr);
|
||||
_open_pq (wthp);
|
||||
_open_pq (wtgr);
|
||||
_open_pqr (wtls);
|
||||
_open_pqr (wtkt);
|
||||
_open_p (wtpm);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "all.h"
|
||||
#include "../pit.h"
|
||||
|
||||
int FOO;
|
||||
|
||||
/* logic
|
||||
*/
|
||||
static u2_noun
|
||||
@ -662,6 +664,27 @@
|
||||
u2_rz(wir_r, zel);
|
||||
return ret;
|
||||
}
|
||||
case c3__kthx: u2_bi_cell(wir_r, u2_t(gen), &p_gen, &q_gen);
|
||||
_mint_used(wir_r);
|
||||
{
|
||||
u2_noun huz = j2_mcy(Pt6, ut, play)(wir_r, van, sut, p_gen);
|
||||
u2_noun hif = _mint_nice(wir_r, van, gol, huz);
|
||||
u2_noun zel = _mint_in(wir_r, van, sut, hif, q_gen);
|
||||
u2_noun ret = u2_bc(wir_r, hif, u2_rx(wir_r, u2_t(zel)));
|
||||
|
||||
{
|
||||
u2_noun goz = j2_mcy(Pt6, ut, play)(wir_r, van, sut, q_gen);
|
||||
u2_noun bar;
|
||||
|
||||
FOO = 1;
|
||||
fprintf(stderr, "\r\n");
|
||||
bar = j2_mcy(Pt6, ut, nest)(wir_r, van, huz, u2_no, goz);
|
||||
fprintf(stderr, "kthx: bar %d\r\n", bar);
|
||||
FOO = 0;
|
||||
}
|
||||
u2_rz(wir_r, zel);
|
||||
return ret;
|
||||
}
|
||||
case c3__tsgr: u2_bi_cell(wir_r, u2_t(gen), &p_gen, &q_gen);
|
||||
_mint_used(wir_r);
|
||||
{
|
||||
|
@ -604,6 +604,19 @@
|
||||
u2_rz(wir_r, zel);
|
||||
return ret;
|
||||
}
|
||||
case c3__kthx: u2_bi_cell(wir_r, u2_t(gen), &p_gen, &q_gen);
|
||||
_mull_used(wir_r);
|
||||
{
|
||||
u2_noun p_hif = _mull_nice
|
||||
(wir_r, van, gol, j2_mcy(Pt6, ut, play)(wir_r, van, sut, p_gen));
|
||||
u2_noun q_hif = j2_mcy(Pt6, ut, play)(wir_r, van, dox, p_gen);
|
||||
|
||||
u2_noun zel = _mull_in(wir_r, van, sut, p_hif, dox, q_gen);
|
||||
u2_noun ret = u2_bc(wir_r, p_hif, q_hif);
|
||||
|
||||
u2_rz(wir_r, zel);
|
||||
return ret;
|
||||
}
|
||||
case c3__tsgr: u2_bi_cell(wir_r, u2_t(gen), &p_gen, &q_gen);
|
||||
_mull_used(wir_r);
|
||||
{
|
||||
|
@ -339,6 +339,11 @@
|
||||
{
|
||||
return _play_x(wir_r, van, sut, p_gen);
|
||||
}
|
||||
case c3__kthx: u2_bi_cell(wir_r, u2_t(gen), &p_gen, &q_gen);
|
||||
_play_used(wir_r);
|
||||
{
|
||||
return _play_x(wir_r, van, sut, p_gen);
|
||||
}
|
||||
case c3__ktls: u2_bi_cell(wir_r, u2_t(gen), &p_gen, &q_gen);
|
||||
_play_used(wir_r);
|
||||
{
|
||||
|
34
gen164/pit.h
34
gen164/pit.h
@ -294,11 +294,6 @@
|
||||
|
||||
/** Tier 4.
|
||||
**/
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, put)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, gas)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
@ -309,16 +304,42 @@
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, int)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, put)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, tap)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, in, uni)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
|
||||
u2_noun // transfer
|
||||
j2_mcc(Pt4, by, get)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, by, gas)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, by, int)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, by, put)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
@ -326,9 +347,10 @@
|
||||
u2_noun c); // retain
|
||||
|
||||
u2_weak // transfer
|
||||
j2_mcc(Pt4, by, gas)(u2_wire wir_r,
|
||||
j2_mcc(Pt4, by, uni)(u2_wire wir_r,
|
||||
u2_noun a, // retain
|
||||
u2_noun b); // retain
|
||||
|
||||
/** Tier 5.
|
||||
**/
|
||||
u2_noun // transfer
|
||||
|
@ -4,6 +4,7 @@
|
||||
*/
|
||||
/** Definitions.
|
||||
**/
|
||||
|
||||
# define c3__abo c3_s3('a','b','o')
|
||||
# define c3__actd c3_s4('a','c','t','d')
|
||||
# define c3__add c3_s3('a','d','d')
|
||||
@ -21,6 +22,7 @@
|
||||
# define c3__at c3_s2('a','t')
|
||||
# define c3__atom c3_s4('a','t','o','m')
|
||||
# define c3__axe c3_s3('a','x','e')
|
||||
# define c3__axil c3_s4('a','x','i','l')
|
||||
# define c3__axis c3_s4('a','x','i','s')
|
||||
# define c3__bac c3_s3('b','a','c')
|
||||
# define c3__bach c3_s4('b','a','c','h')
|
||||
@ -39,6 +41,7 @@
|
||||
# define c3__bbye c3_s4('b','b','y','e')
|
||||
# define c3__bcbn c3_s4('b','c','b','n')
|
||||
# define c3__bcbr c3_s4('b','c','b','r')
|
||||
# define c3__bccb c3_s4('b','c','c','b')
|
||||
# define c3__bccn c3_s4('b','c','c','n')
|
||||
# define c3__bcdg c3_s4('b','c','d','g')
|
||||
# define c3__bckt c3_s4('b','c','k','t')
|
||||
@ -49,6 +52,7 @@
|
||||
# define c3__bcsg c3_s4('b','c','s','g')
|
||||
# define c3__bctr c3_s4('b','c','t','r')
|
||||
# define c3__bcts c3_s4('b','c','t','s')
|
||||
# define c3__bczp c3_s4('b','c','z','p')
|
||||
# define c3__bead c3_s4('b','e','a','d')
|
||||
# define c3__bean c3_s4('b','e','a','n')
|
||||
# define c3__bear c3_s4('b','e','a','r')
|
||||
@ -97,6 +101,7 @@
|
||||
# define c3__bran c3_s4('b','r','a','n')
|
||||
# define c3__brax c3_s4('b','r','a','x')
|
||||
# define c3__brbn c3_s4('b','r','b','n')
|
||||
# define c3__brcb c3_s4('b','r','c','b')
|
||||
# define c3__brcn c3_s4('b','r','c','n')
|
||||
# define c3__brcs c3_s4('b','r','c','s')
|
||||
# define c3__brdg c3_s4('b','r','d','g')
|
||||
@ -139,6 +144,7 @@
|
||||
# define c3__by c3_s2('b','y')
|
||||
# define c3__byte c3_s4('b','y','t','e')
|
||||
# define c3__cage c3_s4('c','a','g','e')
|
||||
# define c3__cain c3_s4('c','a','i','n')
|
||||
# define c3__call c3_s4('c','a','l','l')
|
||||
# define c3__can c3_s3('c','a','n')
|
||||
# define c3__cap c3_s3('c','a','p')
|
||||
@ -185,6 +191,7 @@
|
||||
# define c3__cltr c3_s4('c','l','t','r')
|
||||
# define c3__cnbc c3_s4('c','n','b','c')
|
||||
# define c3__cnbr c3_s4('c','n','b','r')
|
||||
# define c3__cncb c3_s4('c','n','c','b')
|
||||
# define c3__cncl c3_s4('c','n','c','l')
|
||||
# define c3__cndt c3_s4('c','n','d','t')
|
||||
# define c3__cnhp c3_s4('c','n','h','p')
|
||||
@ -451,6 +458,7 @@
|
||||
# define c3__hand c3_s4('h','a','n','d')
|
||||
# define c3__hang c3_s4('h','a','n','g')
|
||||
# define c3__hard c3_s4('h','a','r','d')
|
||||
# define c3__harm c3_s4('h','a','r','m')
|
||||
# define c3__harp c3_s4('h','a','r','p')
|
||||
# define c3__have c3_s4('h','a','v','e')
|
||||
# define c3__head c3_s4('h','e','a','d')
|
||||
@ -496,6 +504,8 @@
|
||||
# define c3__hunk c3_s4('h','u','n','k')
|
||||
# define c3__hxtr c3_s4('h','x','t','r')
|
||||
# define c3__hxts c3_s4('h','x','t','s')
|
||||
# define c3__hxgl c3_s4('h','x','g','l')
|
||||
# define c3__hxgr c3_s4('h','x','g','r')
|
||||
# define c3__ic c3_s2('i','c')
|
||||
# define c3__ice c3_s3('i','c','e')
|
||||
# define c3__if c3_s2('i','f')
|
||||
@ -536,6 +546,7 @@
|
||||
# define c3__ktdt c3_s4('k','t','d','t')
|
||||
# define c3__ktgl c3_s4('k','t','g','l')
|
||||
# define c3__ktgr c3_s4('k','t','g','r')
|
||||
# define c3__kthx c3_s4('k','t','h','x')
|
||||
# define c3__ktlc c3_s4('k','t','l','c')
|
||||
# define c3__ktld c3_s4('k','t','l','d')
|
||||
# define c3__ktls c3_s4('k','t','l','s')
|
||||
@ -680,6 +691,7 @@
|
||||
# define c3__nil c3_s3('n','i','l')
|
||||
# define c3__nilk c3_s4('n','i','l','k')
|
||||
# define c3__no c3_s2('n','o')
|
||||
# define c3__noah c3_s4('n','o','a','h')
|
||||
# define c3__nock c3_s4('n','o','c','k')
|
||||
# define c3__none c3_s4('n','o','n','e')
|
||||
# define c3__nop c3_s3('n','o','p')
|
||||
@ -868,6 +880,7 @@
|
||||
# define c3__sgdp c3_s4('s','g','d','p')
|
||||
# define c3__sgdt c3_s4('s','g','d','t')
|
||||
# define c3__sgdx c3_s4('s','g','d','x')
|
||||
# define c3__sgfs c3_s4('s','g','f','s')
|
||||
# define c3__sggl c3_s4('s','g','g','l')
|
||||
# define c3__sggr c3_s4('s','g','g','r')
|
||||
# define c3__sghp c3_s4('s','g','h','p')
|
||||
@ -1101,6 +1114,7 @@
|
||||
# define c3__wtgr c3_s4('w','t','g','r')
|
||||
# define c3__wthp c3_s4('w','t','h','p')
|
||||
# define c3__wthx c3_s4('w','t','h','x')
|
||||
# define c3__wtkt c3_s4('w','t','k','t')
|
||||
# define c3__wtls c3_s4('w','t','l','s')
|
||||
# define c3__wtms c3_s4('w','t','m','s')
|
||||
# define c3__wtpm c3_s4('w','t','p','m')
|
||||
@ -1130,6 +1144,7 @@
|
||||
# define c3__zpdg c3_s4('z','p','d','g')
|
||||
# define c3__zpdx c3_s4('z','p','d','x')
|
||||
# define c3__zpfs c3_s4('z','p','f','s')
|
||||
# define c3__zpgr c3_s4('z','p','g','r')
|
||||
# define c3__zphs c3_s4('z','p','h','x')
|
||||
# define c3__zphx c3_s4('z','p','h','x')
|
||||
# define c3__zplc c3_s4('z','p','l','c')
|
||||
|
@ -409,26 +409,37 @@
|
||||
/* u2_utim: unix timer control.
|
||||
*/
|
||||
typedef struct _u2_utim {
|
||||
uv_timer_t wat_u; // libev timer control
|
||||
u2_uwen* wen_u; // timers in ascending order
|
||||
uv_timer_t wat_u; // timer control
|
||||
u2_uwen* wen_u; // timers in ascending order
|
||||
};
|
||||
#endif
|
||||
|
||||
/* u2_utty: unix tty.
|
||||
*/
|
||||
typedef struct _u2_utty {
|
||||
uv_pipe_t pop_u;
|
||||
struct termios bak_u; // cooked terminal state
|
||||
struct termios raw_u; // raw terminal state
|
||||
union {
|
||||
uv_pipe_t pop_u;
|
||||
uv_tcp_t wax_u;
|
||||
};
|
||||
struct _u2_utty* nex_u; // next in host list
|
||||
c3_i fid_i; // file descriptor
|
||||
c3_i cug_i; // blocking fcntl flags
|
||||
c3_i nob_i; // nonblocking fcntl flags
|
||||
c3_w tid_l; // terminal identity number
|
||||
u2_utfo ufo_u; // terminfo strings
|
||||
c3_i cug_i; // blocking fcntl flags
|
||||
c3_i nob_i; // nonblocking fcntl flags
|
||||
u2_utat tat_u; // control state
|
||||
struct _u2_utty* nex_u; // next in host list
|
||||
struct termios bak_u; // cooked terminal state
|
||||
struct termios raw_u; // raw terminal state
|
||||
} u2_utty;
|
||||
|
||||
/* u2_utel: unix telnet listener.
|
||||
*/
|
||||
typedef struct _u2_utel {
|
||||
struct _u2_utty uty_t; // pseudo-tty
|
||||
c3_s por_s; // file descriptor
|
||||
void* tel_u; // telnet context
|
||||
} u2_utel;
|
||||
|
||||
/* u2_raty: raft server type.
|
||||
*/
|
||||
typedef enum {
|
||||
@ -534,8 +545,8 @@
|
||||
uv_loop_t* lup_u; // libuv event loop
|
||||
u2_http* htp_u; // http servers
|
||||
u2_cttp ctp_u; // http clients
|
||||
u2_utty* uty_u; // all terminals
|
||||
u2_utty* tem_u; // main terminal (1)
|
||||
u2_utel tel_u; // telnet listener
|
||||
u2_utty* uty_u; // linked terminal list
|
||||
u2_ames sam_u; // packet interface
|
||||
u2_save sav_u; // autosave
|
||||
u2_opts ops_u; // commandline options
|
||||
@ -569,9 +580,6 @@
|
||||
c3_global u2_bean u2_Flag_Profile;
|
||||
c3_global u2_bean u2_Flag_Verbose;
|
||||
|
||||
# define u2_ve_at() ( &u2_Host.ver_e[u2_Host.kno_w] )
|
||||
|
||||
|
||||
/** Functions.
|
||||
**/
|
||||
/* Urbit time: 128 bits, leap-free.
|
||||
@ -904,7 +912,7 @@
|
||||
/* u2_term_ef_boil(): initial effects for restored server.
|
||||
*/
|
||||
void
|
||||
u2_term_ef_boil(c3_l ono_l);
|
||||
u2_term_ef_boil();
|
||||
|
||||
/* u2_term_ef_winc(): window change.
|
||||
*/
|
||||
@ -932,6 +940,11 @@
|
||||
void
|
||||
u2_term_io_init(void);
|
||||
|
||||
/* u2_term_io_talk(): start terminal listener.
|
||||
*/
|
||||
void
|
||||
u2_term_io_talk(void);
|
||||
|
||||
/* u2_term_io_exit(): terminate terminal I/O.
|
||||
*/
|
||||
void
|
||||
|
1
outside/anachronism/.gitignore
vendored
Normal file
1
outside/anachronism/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
build/
|
19
outside/anachronism/LICENSE
Normal file
19
outside/anachronism/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2010 Jonathan Castello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
65
outside/anachronism/Makefile
Normal file
65
outside/anachronism/Makefile
Normal file
@ -0,0 +1,65 @@
|
||||
SHELL = sh
|
||||
|
||||
CC = gcc
|
||||
FLAGS = -c -fPIC -Iinclude/
|
||||
CFLAGS = --pedantic -Wall -Wextra -march=native -std=gnu99
|
||||
INCLUDE = include/anachronism
|
||||
|
||||
VERSION_MAJOR = 0
|
||||
VERSION = $(VERSION_MAJOR).3.1
|
||||
|
||||
SO = libanachronism.so
|
||||
SOFILE = $(SO).$(VERSION)
|
||||
SONAME = $(SO).$(VERSION_MAJOR)
|
||||
|
||||
|
||||
all: static shared
|
||||
shared: build/ build/$(SOFILE)
|
||||
static: build/ build/libanachronism.a
|
||||
|
||||
build/:
|
||||
mkdir build
|
||||
|
||||
build/$(SOFILE): build/nvt.o build/parser.o
|
||||
$(CC) -shared -Wl,-soname,$(SONAME) -o build/$(SOFILE) build/nvt.o build/parser.o
|
||||
|
||||
build/libanachronism.a: build/nvt.o build/parser.o
|
||||
ar rcs build/libanachronism.a build/nvt.o build/parser.o
|
||||
|
||||
build/nvt.o: src/nvt.c $(INCLUDE)/nvt.h $(INCLUDE)/common.h
|
||||
$(CC) $(FLAGS) $(CFLAGS) src/nvt.c -o build/nvt.o
|
||||
|
||||
build/parser.o: src/parser.c $(INCLUDE)/parser.h $(INCLUDE)/common.h
|
||||
$(CC) $(FLAGS) $(CFLAGS) src/parser.c -o build/parser.o
|
||||
|
||||
src/parser.c: src/parser.rl src/parser_common.rl
|
||||
ragel -C -G2 src/parser.rl -o src/parser.c
|
||||
|
||||
|
||||
graph: doc/parser.png
|
||||
|
||||
doc/parser.png: src/parser.rl src/parser_common.rl
|
||||
ragel -V -p src/parser.rl | dot -Tpng > doc/parser.png
|
||||
|
||||
install: all
|
||||
install -D -d /usr/local/include/anachronism/ /usr/local/lib
|
||||
install -D include/anachronism/* /usr/local/include/anachronism/
|
||||
install -D build/$(SOFILE) /usr/local/lib/$(SOFILE)
|
||||
install -D build/libanachronism.a /usr/local/lib/libanachronism.a
|
||||
ln -s -f /usr/local/lib/$(SOFILE) /usr/local/lib/$(SONAME)
|
||||
ln -s -f /usr/local/lib/$(SOFILE) /usr/local/lib/$(SO)
|
||||
|
||||
uninstall:
|
||||
-rm -rf /usr/local/include/anachronism
|
||||
-rm /usr/local/lib/libanachronism.a
|
||||
-rm /usr/local/lib/$(SOFILE)
|
||||
-rm /usr/local/lib/$(SONAME)
|
||||
-rm /usr/local/lib/$(SO)
|
||||
|
||||
clean:
|
||||
-rm -f build/nvt.o build/router.o build/parser.o
|
||||
|
||||
distclean: clean
|
||||
-rm -f build/libanachronism.a build/$(SOFILE)
|
||||
|
||||
.PHONY: all static shared clean distclean install uninstall
|
158
outside/anachronism/README.md
Normal file
158
outside/anachronism/README.md
Normal file
@ -0,0 +1,158 @@
|
||||
# Anachronism
|
||||
Anachronism is a fully-compliant implementation of [the Telnet protocol][wiki-telnet]. Fallen
|
||||
out of favor in this day and age, most people only know it as a command-line
|
||||
tool for debugging HTTP. Today, Telnet is most commonly used in the realm of
|
||||
[MUDs][wiki-muds], though there are still a few other niches filled by Telnet.
|
||||
|
||||
Anachronism offers a simple API for translating between streams of data and
|
||||
events, and is completely network-agnostic. Anachronism also offers **channels**, an
|
||||
abstraction layer which treats Telnet as a data multiplexer. Channels make it
|
||||
extremely easy to build reusable modules for Telnet sub-protocols such
|
||||
as MCCP (MUD Client Compression Protocol), which can be written once and plugged
|
||||
into any application that wants to include support.
|
||||
|
||||
[wiki-telnet]: http://en.wikipedia.org/wiki/Telnet (Telnet at Wikipedia)
|
||||
[wiki-muds]: http://en.wikipedia.org/wiki/MUD (MUDs at Wikipedia)
|
||||
|
||||
## Installation
|
||||
While Anachronism has no dependencies and is theoretically cross-platform, I've
|
||||
only written a Makefile for Linux. Help would be appreciated for making this
|
||||
work across more platforms.
|
||||
|
||||
make
|
||||
sudo make install
|
||||
|
||||
This will install Anachronism's shared and static libraries to /usr/local/lib,
|
||||
and its header files to /usr/local/include/anachronism/. You may also need to
|
||||
run `ldconfig` to make Anachronism available to your project's compiler/linker.
|
||||
|
||||
## Usage
|
||||
The anachronism/nvt.h header can be consulted for more complete documentation.
|
||||
|
||||
### Basic usage
|
||||
The core type exposed by Anachronism is the telnet\_nvt, which represents the
|
||||
Telnet RFC's "Network Virtual Terminal". An NVT is created using
|
||||
telnet\_nvt\_new(). When creating an NVT, you must provide it with a set of
|
||||
callbacks to send events to, and an optional void\* to store as the event
|
||||
handler's context. You can use telnet\_recv() to process incoming data, and
|
||||
the telnet\_send\_\*() set of functions to emit outgoing data.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <anachronism/nvt.h>
|
||||
|
||||
void on_event(telnet_nvt* nvt, telnet_event* event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
// A data event (normal text received)
|
||||
case TELNET_EV_DATA:
|
||||
{
|
||||
telnet_data_event* ev = (telnet_data_event*)event;
|
||||
printf("[IN]: %.*s\n", ev->length, ev->data);
|
||||
break;
|
||||
}
|
||||
|
||||
// Outgoing data emitted by the NVT
|
||||
case TELNET_EV_SEND:
|
||||
{
|
||||
telnet_send_event* ev = (telnet_send_event*)event;
|
||||
printf("[OUT]: %.*s\n", ev->length, ev->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create an NVT
|
||||
telnet_nvt* nvt = telnet_nvt_new(NULL, &on_event, NULL, NULL);
|
||||
|
||||
// Process some incoming data
|
||||
const char* data = "foo bar baz";
|
||||
telnet_receive(nvt, (const telnet_byte*)data, strlen(data), NULL);
|
||||
|
||||
// Free the NVT
|
||||
telnet_nvt_free(nvt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
### Telopts
|
||||
Anachronism provides an easy-to-use interface to Telnet's "telopt" functionality
|
||||
via the telnet\_telopt\_*() set of functions. As telopts are negotiated and
|
||||
utilized, events are sent to the telopt callback provided to telnet_nvt_new().
|
||||
|
||||
#include <stdio.h>
|
||||
#include <anachronism/nvt.h>
|
||||
|
||||
void on_event(telnet_nvt* nvt, telnet_event* event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
// Outgoing data emitted by the NVT
|
||||
case TELNET_EV_SEND:
|
||||
{
|
||||
telnet_send_event* ev = (telnet_send_event*)event;
|
||||
printf("[OUT]: %.*s\n", ev->length, ev->data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_telopt_event(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_event* event)
|
||||
{
|
||||
// telopt is the telopt this event was triggered for
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case TELNET_EV_TELOPT_TOGGLE:
|
||||
telnet_telopt_toggle_event* ev = (telnet_telopt_toggle_event*)event;
|
||||
// ev->where is TELNET_TELOPT_LOCAL or TELNET_TELOPT_REMOTE,
|
||||
// corresponding to Telnet's WILL/WONT and DO/DONT commands.
|
||||
// ev->status is TELNET_TELOPT_ON or TELNET_TELOPT_OFF.
|
||||
break;
|
||||
case TELNET_EV_TELOPT_FOCUS:
|
||||
telnet_telopt_focus_event* ev = (telnet_telopt_focus_event*)event;
|
||||
// ev->focus is 1 or 0 depending on if a subnegotiation packet has
|
||||
// begun or ended.
|
||||
break;
|
||||
case TELNET_EV_TELOPT_DATA:
|
||||
telnet_telopt_data_event* ev = (telnet_telopt_data_event*)event;
|
||||
// ev->data is a pointer to the received data.
|
||||
// ev->length is the length of the data buffer.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Create an NVT
|
||||
telnet_nvt* nvt = telnet_nvt_new(NULL, &on_event, &on_telopt_event, NULL);
|
||||
|
||||
// Ask to enable a telopt locally (a WILL command)
|
||||
telnet_request_enable(nvt, 230, TELNET_LOCAL);
|
||||
|
||||
// Process some incoming data
|
||||
const char* data = "\xFF\xFD\xE6" // IAC DO 230 (turn channel on)
|
||||
"\xFF\xFA\xE6" // IAC SB 230 (switch to channel)
|
||||
"foo bar baz" (send data)
|
||||
"\xFF\xF0"; // IAC SE (switch to main)
|
||||
telnet_receive(nvt, (const telnet_byte*)data, strlen(data), NULL);
|
||||
|
||||
// Free the NVT
|
||||
telnet_nvt_free(nvt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
### Interrupting
|
||||
TODO: Explain how to interrupt the parser.
|
||||
|
||||
## Alternatives
|
||||
* [libtelnet][github-libtelnet], by Elanthis<br>
|
||||
It incorporates a number of (rather MUD-specific) protocols by default,
|
||||
though its API is quite different.
|
||||
|
||||
[github-libtelnet]: https://github.com/elanthis/libtelnet (libtelnet on GitHub)
|
||||
|
||||
## Credits
|
||||
Someone from #startups on Freenode IRC suggested the name (I'm sure as a joke).
|
||||
If you read this, remind me who you are so I can credit you properly!
|
50
outside/anachronism/doc/channels.md
Normal file
50
outside/anachronism/doc/channels.md
Normal file
@ -0,0 +1,50 @@
|
||||
# Telnet
|
||||
|
||||
## Channels
|
||||
Telnet supports data multiplexing by way of 256 built-in sub-channels, each
|
||||
identified by a byte in the interval [\x00-\xFF]. By switching between
|
||||
channels, you can send completely separate streams of data through the same
|
||||
connection.
|
||||
|
||||
All channels start out closed by default. To open a channel, one host must
|
||||
request or offer a channel using IAC WILL <id> or IAC DO <id>. The remote host
|
||||
then responds with IAC DO <id> or IAC WILL <id>, respectively. Alternatively,
|
||||
the request may be denied using IAC DONT <id> or IAC WONT <id>, respectively.
|
||||
|
||||
In order to switch to a specific channel, the IAC SB <id> sequence must
|
||||
be used. All data sent afterwards will be routed through that specific channel.
|
||||
To switch back to the main channel, IAC SE must be used. Note that subchannels
|
||||
do not support any IAC sequences except IAC IAC (an escaped \xFF byte) and
|
||||
IAC SE (return to the main channel). In particular, you cannot switch directly
|
||||
from one subchannel to another: you must revert to the main channel first.
|
||||
|
||||
Due to the unbiased nature of Telnet, neither side of the connection is
|
||||
automatically recognized as the server or the client. However, a host may either
|
||||
request a channel (as a client) or offer a channel (as a server). The WILL/WONT
|
||||
commands are used in the role of server ("I will", "I wont"), while DO/DONT
|
||||
are used in the role of client ("You do", "You do not"). As such, a channel
|
||||
may be opened twice (even simultaneously).
|
||||
|
||||
As an example, lets assume a terminal is connected to a server using Telnet. The
|
||||
server offers MCCP (data compression), but wants to know what the terminal's
|
||||
window size is. The following communication might occur:
|
||||
|
||||
<server> IAC DO NAWS
|
||||
<server> IAC WILL MCCP
|
||||
<client> IAC WILL NAWS
|
||||
<client> IAC SB NAWS \x50 \x00 \x50 \x00 IAC SE
|
||||
<client> IAC DO MCCP
|
||||
<server> IAC SB MCCP IAC SE
|
||||
<server> (compressed data)
|
||||
|
||||
Notice that MCCP was negotiated such that the server offers the compression.
|
||||
Only the server-to-client flow of data is compressed; the client would not
|
||||
compress its data unless the channel was negotiated in the other direction as
|
||||
well.
|
||||
|
||||
In general, a specific subchannel is tied to a specific Telnet subprotocol. For
|
||||
example, the EXOPL subprotocol is assigned to channel 255, so that channel
|
||||
should be avoided for any other purpose. A full list of registered subprotocols
|
||||
can be found on the [IANA website][1].
|
||||
|
||||
[1]: http://www.iana.org/assignments/telnet-options
|
BIN
outside/anachronism/doc/parser.png
Normal file
BIN
outside/anachronism/doc/parser.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 148 KiB |
24
outside/anachronism/include/anachronism/common.h
Normal file
24
outside/anachronism/include/anachronism/common.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef ANACHRONISM_COMMON_H
|
||||
#define ANACHRONISM_COMMON_H
|
||||
|
||||
#include <stdlib.h> /* for size_t */
|
||||
|
||||
// Telnet bytes must be unsigned
|
||||
typedef unsigned char telnet_byte;
|
||||
|
||||
// Error codes returned from API functions
|
||||
// Positive codes are success/notice codes.
|
||||
// Nonpositive codes are errors.
|
||||
// ALLOC is 0 for parity with the NULL result from malloc().
|
||||
typedef enum telnet_error
|
||||
{
|
||||
TELNET_E_NOT_SUBNEGOTIABLE = -4, // The telopt is not open for subnegotiation.
|
||||
TELNET_E_BAD_PARSER = -3, // The telnet_parser* passed is NULL
|
||||
TELNET_E_BAD_NVT = -2, // The telnet_nvt* passed is NULL
|
||||
TELNET_E_INVALID_COMMAND = -1, // The telnet_byte passed is not an allowed command in this API method
|
||||
TELNET_E_ALLOC = 0, // Not enough memory to allocate essential library structures
|
||||
TELNET_E_OK = 1, // Huge Success!
|
||||
TELNET_E_INTERRUPT = 2, // Parser interrupted by user code.
|
||||
} telnet_error;
|
||||
|
||||
#endif // ANACHRONISM_COMMON_H
|
214
outside/anachronism/include/anachronism/nvt.h
Normal file
214
outside/anachronism/include/anachronism/nvt.h
Normal file
@ -0,0 +1,214 @@
|
||||
#ifndef ANACHRONISM_ANACHRONISM_H
|
||||
#define ANACHRONISM_ANACHRONISM_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <anachronism/common.h>
|
||||
|
||||
// predefined Telnet commands from 240-255
|
||||
enum
|
||||
{
|
||||
IAC_SE = 240,
|
||||
IAC_NOP,
|
||||
IAC_DM,
|
||||
IAC_BRK,
|
||||
IAC_IP,
|
||||
IAC_AO,
|
||||
IAC_AYT,
|
||||
IAC_EC,
|
||||
IAC_EL,
|
||||
IAC_GA,
|
||||
IAC_SB,
|
||||
IAC_WILL,
|
||||
IAC_WONT,
|
||||
IAC_DO,
|
||||
IAC_DONT,
|
||||
IAC_IAC,
|
||||
};
|
||||
|
||||
typedef enum telnet_telopt_location
|
||||
{
|
||||
TELNET_LOCAL,
|
||||
TELNET_REMOTE,
|
||||
} telnet_telopt_location;
|
||||
|
||||
|
||||
/**
|
||||
* NVT Events
|
||||
*/
|
||||
|
||||
typedef enum telnet_event_type
|
||||
{
|
||||
TELNET_EV_DATA, /* A stretch of plain data was received. (data, length) */
|
||||
TELNET_EV_COMMAND, /* A simple IAC comamnd was recevied. (command) */
|
||||
TELNET_EV_WARNING, /* A non-fatal invalid sequence was received. (message, position) */
|
||||
TELNET_EV_SEND, /* Outgoing data to be sent. (data, length) */
|
||||
} telnet_event_type;
|
||||
|
||||
typedef struct telnet_event
|
||||
{
|
||||
telnet_event_type type;
|
||||
} telnet_event;
|
||||
|
||||
typedef struct telnet_data_event
|
||||
{
|
||||
telnet_event SUPER_;
|
||||
const telnet_byte* data;
|
||||
size_t length;
|
||||
} telnet_data_event;
|
||||
|
||||
typedef struct telnet_command_event
|
||||
{
|
||||
telnet_event SUPER_;
|
||||
telnet_byte command;
|
||||
} telnet_command_event;
|
||||
|
||||
typedef struct telnet_warning_event
|
||||
{
|
||||
telnet_event SUPER_;
|
||||
const char* message;
|
||||
size_t position;
|
||||
} telnet_warning_event;
|
||||
|
||||
typedef struct telnet_send_event
|
||||
{
|
||||
telnet_event SUPER_;
|
||||
const telnet_byte* data;
|
||||
size_t length;
|
||||
} telnet_send_event;
|
||||
|
||||
|
||||
/**
|
||||
* Telopt Events
|
||||
*/
|
||||
|
||||
typedef enum telnet_telopt_event_type
|
||||
{
|
||||
TELNET_EV_TELOPT_TOGGLE,
|
||||
TELNET_EV_TELOPT_FOCUS,
|
||||
TELNET_EV_TELOPT_DATA,
|
||||
} telnet_telopt_event_type;
|
||||
|
||||
typedef struct telnet_telopt_event
|
||||
{
|
||||
telnet_telopt_event_type type;
|
||||
} telnet_telopt_event;
|
||||
|
||||
typedef struct telnet_telopt_toggle_event
|
||||
{
|
||||
telnet_telopt_event SUPER_;
|
||||
telnet_telopt_location where;
|
||||
unsigned char status;
|
||||
} telnet_telopt_toggle_event;
|
||||
|
||||
typedef struct telnet_telopt_focus_event
|
||||
{
|
||||
telnet_telopt_event SUPER_;
|
||||
unsigned char focus;
|
||||
} telnet_telopt_focus_event;
|
||||
|
||||
typedef struct telnet_telopt_data_event
|
||||
{
|
||||
telnet_telopt_event SUPER_;
|
||||
const telnet_byte* data;
|
||||
size_t length;
|
||||
} telnet_telopt_data_event;
|
||||
|
||||
|
||||
|
||||
typedef struct telnet_nvt telnet_nvt;
|
||||
|
||||
|
||||
typedef void (*telnet_nvt_event_callback)(telnet_nvt* nvt, telnet_event* event);
|
||||
typedef void (*telnet_telopt_event_callback)(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_event* event);
|
||||
typedef unsigned char (*telnet_negotiate_event_callback)(telnet_nvt* nvt, telnet_byte telopt, telnet_telopt_location where);
|
||||
|
||||
/**
|
||||
Creates a new Telnet NVT.
|
||||
|
||||
Errors:
|
||||
TELNET_E_ALLOC - Unable to allocate enough memory for the NVT.
|
||||
*/
|
||||
telnet_nvt* telnet_nvt_new(void* userdata,
|
||||
telnet_nvt_event_callback nvt_callback,
|
||||
telnet_telopt_event_callback telopt_callback,
|
||||
telnet_negotiate_event_callback negotiate_callback);
|
||||
|
||||
void telnet_nvt_free(telnet_nvt* nvt);
|
||||
|
||||
/**
|
||||
Every NVT can have some user-specific data attached, such as a user-defined struct.
|
||||
This can be accessed (primarily by event callbacks) to differentiate between NVTs.
|
||||
|
||||
Errors:
|
||||
TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter.
|
||||
|
||||
Example:
|
||||
// assuming a FILE was passed to telnet_nvt_new():
|
||||
FILE out = NULL;
|
||||
telnet_get_userdata(nvt, (void**)&out);
|
||||
*/
|
||||
telnet_error telnet_get_userdata(telnet_nvt* nvt, void** udata);
|
||||
|
||||
/**
|
||||
Processes incoming data.
|
||||
If `bytes_used` is non-NULL, it will be set to the length of the string that
|
||||
was read. This is generally only useful if you use telnet_halt() in a callback.
|
||||
|
||||
Errors:
|
||||
TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter.
|
||||
TELNET_E_ALLOC - Unable to allocate destination buffer for incoming text.
|
||||
TELNET_E_INTERRUPT - User code interrupted the parser.
|
||||
*/
|
||||
telnet_error telnet_receive(telnet_nvt* nvt, const telnet_byte* data, size_t length, size_t* bytes_used);
|
||||
|
||||
/**
|
||||
If currently parsing (i.e. telnet_recv() is running), interrupts the parser.
|
||||
This is useful for things such as MCCP, where a Telnet sequence hails the start of
|
||||
data that must be decompressed before being parsed.
|
||||
|
||||
Errors:
|
||||
TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter.
|
||||
*/
|
||||
telnet_error telnet_interrupt(telnet_nvt* nvt);
|
||||
|
||||
|
||||
/**
|
||||
Sends a string as a stream of escaped Telnet data.
|
||||
|
||||
Errors:
|
||||
TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter.
|
||||
TELNET_E_ALLOC - Unable to allocate destination buffer for outgoing text.
|
||||
*/
|
||||
telnet_error telnet_send_data(telnet_nvt* nvt, const telnet_byte* data, const size_t length);
|
||||
|
||||
/**
|
||||
Sends a Telnet command.
|
||||
|
||||
Errors:
|
||||
TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter.
|
||||
TELNET_E_INVALID_COMMAND - The command cannot be WILL, WONT, DO, DONT, SB, or SE.
|
||||
*/
|
||||
telnet_error telnet_send_command(telnet_nvt* nvt, const telnet_byte command);
|
||||
|
||||
/**
|
||||
Sends a subnegotiation packet.
|
||||
|
||||
Errors:
|
||||
TELNET_E_BAD_NVT - Invalid telnet_nvt* parameter.
|
||||
TELNET_E_ALLOC - Unable to allocate destination buffer for outgoing text.
|
||||
*/
|
||||
telnet_error telnet_send_subnegotiation(telnet_nvt* nvt, const telnet_byte option, const telnet_byte* data, const size_t length);
|
||||
|
||||
|
||||
telnet_error telnet_telopt_enable(telnet_nvt* nvt, const telnet_byte telopt, telnet_telopt_location where);
|
||||
telnet_error telnet_telopt_disable(telnet_nvt* nvt, const telnet_byte telopt, telnet_telopt_location where);
|
||||
telnet_error telnet_telopt_status(telnet_nvt* nvt, const telnet_byte telopt, telnet_telopt_location where, unsigned char* status);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // ANACHRONISM_ANACHRONISM_H
|
73
outside/anachronism/include/anachronism/parser.h
Normal file
73
outside/anachronism/include/anachronism/parser.h
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef ANACHRONISM_PARSER_H
|
||||
#define ANACHRONISM_PARSER_H
|
||||
|
||||
#include <anachronism/common.h>
|
||||
|
||||
typedef enum telnet_parser_event_type
|
||||
{
|
||||
TELNET_EV_PARSER_DATA,
|
||||
TELNET_EV_PARSER_COMMAND,
|
||||
TELNET_EV_PARSER_OPTION,
|
||||
TELNET_EV_PARSER_SUBNEGOTIATION,
|
||||
TELNET_EV_PARSER_WARNING,
|
||||
} telnet_parser_event_type;
|
||||
|
||||
typedef struct telnet_parser_event
|
||||
{
|
||||
telnet_parser_event_type type;
|
||||
} telnet_parser_event;
|
||||
|
||||
typedef struct telnet_parser_data_event
|
||||
{
|
||||
telnet_parser_event SUPER_;
|
||||
const telnet_byte* data;
|
||||
size_t length;
|
||||
} telnet_parser_data_event;
|
||||
|
||||
typedef struct telnet_parser_command_event
|
||||
{
|
||||
telnet_parser_event SUPER_;
|
||||
telnet_byte command;
|
||||
} telnet_parser_command_event;
|
||||
|
||||
typedef struct telnet_parser_option_event
|
||||
{
|
||||
telnet_parser_event SUPER_;
|
||||
telnet_byte command;
|
||||
telnet_byte option;
|
||||
} telnet_parser_option_event;
|
||||
|
||||
typedef struct telnet_parser_subnegotiation_event
|
||||
{
|
||||
telnet_parser_event SUPER_;
|
||||
int active;
|
||||
telnet_byte option;
|
||||
} telnet_parser_subnegotiation_event;
|
||||
|
||||
typedef struct telnet_parser_warning_event
|
||||
{
|
||||
telnet_parser_event SUPER_;
|
||||
const char* message;
|
||||
size_t position;
|
||||
} telnet_parser_warning_event;
|
||||
|
||||
|
||||
|
||||
typedef struct telnet_parser telnet_parser;
|
||||
|
||||
typedef void (*telnet_parser_callback)(telnet_parser* parser, telnet_parser_event* event);
|
||||
|
||||
|
||||
telnet_parser* telnet_parser_new(void* userdata, telnet_parser_callback callback);
|
||||
void telnet_parser_free(telnet_parser* parser);
|
||||
|
||||
telnet_error telnet_parser_get_userdata(telnet_parser* parser, void** userdata);
|
||||
|
||||
telnet_error telnet_parser_parse(telnet_parser* parser,
|
||||
const telnet_byte* data,
|
||||
size_t length,
|
||||
size_t* bytes_used);
|
||||
|
||||
telnet_error telnet_parser_interrupt(telnet_parser* parser);
|
||||
|
||||
#endif // ANACHRONISM_PARSER_H
|
6
outside/anachronism/src/README.md
Normal file
6
outside/anachronism/src/README.md
Normal file
@ -0,0 +1,6 @@
|
||||
* parser_common.rl
|
||||
<br>The language-agnostic Ragel grammar for the Telnet protocol.
|
||||
* parser.rl
|
||||
<br>The C implementation of the Ragel grammar. Compiled to parser.c by Ragel.
|
||||
* nvt.c
|
||||
<br>The core implementation of Anachronism's NVT and Channel constructs.
|
631
outside/anachronism/src/nvt.c
Normal file
631
outside/anachronism/src/nvt.c
Normal file
@ -0,0 +1,631 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <anachronism/nvt.h>
|
||||
#include <anachronism/parser.h>
|
||||
|
||||
|
||||
#define TELOPT_TOGGLE_CALLBACK(nvt, telopt, where_, status_) do { \
|
||||
if ((nvt)->telopt_callback) { \
|
||||
telnet_telopt_toggle_event ev; \
|
||||
ev.SUPER_.type = TELNET_EV_TELOPT_TOGGLE; \
|
||||
ev.where = (where_); \
|
||||
ev.status = (status_); \
|
||||
\
|
||||
(nvt)->telopt_callback((nvt), (telopt), (telnet_telopt_event*)&ev); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TELOPT_FOCUS_CALLBACK(nvt, telopt, status_) do { \
|
||||
if ((nvt)->telopt_callback) { \
|
||||
telnet_telopt_focus_event ev; \
|
||||
ev.SUPER_.type = TELNET_EV_TELOPT_FOCUS; \
|
||||
ev.status = (status_); \
|
||||
\
|
||||
(nvt)->telopt_callback((nvt), (telopt), (telnet_telopt_event*)&ev); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define TELOPT_DATA_CALLBACK(nvt, telopt, data_, length_) do { \
|
||||
if ((nvt)->telopt_callback) { \
|
||||
telnet_telopt_data_event ev; \
|
||||
ev.SUPER_.type = TELNET_EV_TELOPT_DATA; \
|
||||
ev.data = (data_); \
|
||||
ev.length = (length_); \
|
||||
\
|
||||
(nvt)->telopt_callback((nvt), (telopt), (telnet_telopt_event*)&ev); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SEND_CALLBACK(nvt, data_, length_) do { \
|
||||
if ((nvt)->callback) { \
|
||||
telnet_send_event ev; \
|
||||
ev.SUPER_.type = TELNET_EV_SEND; \
|
||||
ev.data = (data_); \
|
||||
ev.length = (length_); \
|
||||
\
|
||||
(nvt)->callback((nvt), (telnet_event*)&ev); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
// Q Method of Implementing TELNET Option Negotiation
|
||||
// ftp://ftp.rfc-editor.org/in-notes/rfc1143.txt
|
||||
typedef enum qstate {
|
||||
Q_NO = 0, Q_WANTYES, Q_WANTYESNO,
|
||||
Q_YES, Q_WANTNO, Q_WANTNOYES,
|
||||
} qstate;
|
||||
|
||||
typedef struct telnet_qstate
|
||||
{
|
||||
unsigned remote : 3;
|
||||
unsigned local : 3;
|
||||
} telnet_qstate;
|
||||
|
||||
struct telnet_nvt
|
||||
{
|
||||
telnet_parser* parser;
|
||||
telnet_qstate options[256]; // track the state of each subnegotiation option
|
||||
short current_remote;
|
||||
|
||||
telnet_nvt_event_callback callback;
|
||||
telnet_telopt_event_callback telopt_callback;
|
||||
telnet_negotiate_event_callback negotiate_callback;
|
||||
|
||||
void* userdata;
|
||||
};
|
||||
|
||||
static unsigned char telopt_status(telnet_nvt* nvt,
|
||||
telnet_byte telopt,
|
||||
telnet_telopt_location where)
|
||||
{
|
||||
unsigned int qval = (where == TELNET_LOCAL) ?
|
||||
nvt->options[telopt].local :
|
||||
nvt->options[telopt].remote;
|
||||
|
||||
switch (qval) {
|
||||
case Q_YES: case Q_WANTNO: case Q_WANTNOYES:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#define telopt_subnegotiable(nvt, telopt) (telopt_status((nvt), (telopt), TELNET_REMOTE) || telopt_status((nvt), (telopt), TELNET_LOCAL))
|
||||
|
||||
|
||||
static void send_option(telnet_nvt* nvt, telnet_byte command, telnet_byte telopt)
|
||||
{
|
||||
const telnet_byte buf[] = {IAC_IAC, command, telopt};
|
||||
SEND_CALLBACK(nvt, buf, 3);
|
||||
}
|
||||
|
||||
static void process_option_event(telnet_nvt* nvt,
|
||||
telnet_byte command,
|
||||
telnet_byte telopt)
|
||||
{
|
||||
telnet_qstate* q = &nvt->options[telopt];
|
||||
// Every qstate begins zeroed-out, and Q_NO is 0.
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case IAC_WILL:
|
||||
switch (q->remote)
|
||||
{
|
||||
case Q_NO:
|
||||
if (nvt->negotiate_callback && nvt->negotiate_callback(nvt, telopt, TELNET_REMOTE)) {
|
||||
send_option(nvt, IAC_DO, telopt);
|
||||
q->remote = Q_YES;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1);
|
||||
} else {
|
||||
send_option(nvt, IAC_DONT, telopt);
|
||||
}
|
||||
break;
|
||||
case Q_WANTNO:
|
||||
// error
|
||||
q->remote = Q_NO;
|
||||
break;
|
||||
case Q_WANTNOYES:
|
||||
// error
|
||||
q->remote = Q_YES;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1);
|
||||
break;
|
||||
case Q_WANTYES:
|
||||
q->remote = Q_YES;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1);
|
||||
break;
|
||||
case Q_WANTYESNO:
|
||||
send_option(nvt, IAC_DONT, telopt);
|
||||
q->remote = Q_WANTNO;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IAC_WONT:
|
||||
switch (q->remote)
|
||||
{
|
||||
case Q_YES:
|
||||
send_option(nvt, IAC_DONT, telopt);
|
||||
q->remote = Q_NO;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 0);
|
||||
break;
|
||||
case Q_WANTNO:
|
||||
q->remote = Q_NO;
|
||||
break;
|
||||
case Q_WANTNOYES:
|
||||
send_option(nvt, IAC_DO, telopt);
|
||||
q->remote = Q_WANTYES;
|
||||
break;
|
||||
case Q_WANTYES:
|
||||
q->remote = Q_NO;
|
||||
break;
|
||||
case Q_WANTYESNO:
|
||||
q->remote = Q_NO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IAC_DO:
|
||||
switch (q->local)
|
||||
{
|
||||
case Q_NO:
|
||||
if (nvt->negotiate_callback && nvt->negotiate_callback(nvt, telopt, TELNET_LOCAL)) {
|
||||
send_option(nvt, IAC_WILL, telopt);
|
||||
q->local = Q_YES;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1);
|
||||
} else {
|
||||
send_option(nvt, IAC_WONT, telopt);
|
||||
}
|
||||
break;
|
||||
case Q_WANTNO:
|
||||
// error
|
||||
q->local = Q_NO;
|
||||
break;
|
||||
case Q_WANTNOYES:
|
||||
// error
|
||||
q->local = Q_YES;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1);
|
||||
break;
|
||||
case Q_WANTYES:
|
||||
q->local = Q_YES;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1);
|
||||
break;
|
||||
case Q_WANTYESNO:
|
||||
send_option(nvt, IAC_WONT, telopt);
|
||||
q->local = Q_WANTNO;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IAC_DONT:
|
||||
switch (q->local)
|
||||
{
|
||||
case Q_YES:
|
||||
send_option(nvt, IAC_DONT, telopt);
|
||||
q->local = Q_NO;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 0);
|
||||
break;
|
||||
case Q_WANTNO:
|
||||
q->local = Q_NO;
|
||||
break;
|
||||
case Q_WANTNOYES:
|
||||
send_option(nvt, IAC_WILL, telopt);
|
||||
q->local = Q_WANTYES;
|
||||
break;
|
||||
case Q_WANTYES:
|
||||
q->local = Q_NO;
|
||||
break;
|
||||
case Q_WANTYESNO:
|
||||
q->local = Q_NO;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_data_event(telnet_nvt* nvt,
|
||||
const telnet_byte* data,
|
||||
size_t length)
|
||||
{
|
||||
if (nvt->current_remote == -1) {
|
||||
// Main-line data
|
||||
if (nvt->callback) {
|
||||
telnet_data_event ev;
|
||||
ev.SUPER_.type = TELNET_EV_DATA;
|
||||
ev.data = data;
|
||||
ev.length = length;
|
||||
nvt->callback(nvt, (telnet_event*)&ev);
|
||||
}
|
||||
} else {
|
||||
// Telopt data
|
||||
telnet_byte telopt = (telnet_byte)nvt->current_remote;
|
||||
|
||||
if (nvt->telopt_callback) {
|
||||
// Make sure the telopt is enabled
|
||||
if (telopt_subnegotiable(nvt, telopt)) {
|
||||
telnet_telopt_data_event ev;
|
||||
ev.SUPER_.type = TELNET_EV_TELOPT_DATA;
|
||||
ev.data = data;
|
||||
ev.length = length;
|
||||
nvt->telopt_callback(nvt, telopt, (telnet_telopt_event*)&ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_subnegotiation_event(telnet_nvt* nvt,
|
||||
int open,
|
||||
telnet_byte telopt)
|
||||
{
|
||||
if (open) {
|
||||
nvt->current_remote = telopt;
|
||||
} else {
|
||||
nvt->current_remote = -1;
|
||||
}
|
||||
|
||||
if (nvt->telopt_callback) {
|
||||
// Make sure the telopt is enabled
|
||||
if (telopt_subnegotiable(nvt, telopt)) {
|
||||
telnet_telopt_focus_event ev;
|
||||
ev.SUPER_.type = TELNET_EV_TELOPT_FOCUS;
|
||||
ev.focus = open;
|
||||
nvt->telopt_callback(nvt, telopt, (telnet_telopt_event*)&ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_event(telnet_parser* parser, telnet_parser_event* event)
|
||||
{
|
||||
telnet_nvt* nvt = NULL;
|
||||
telnet_parser_get_userdata(parser, (void*)&nvt);
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case TELNET_EV_PARSER_DATA:
|
||||
{
|
||||
telnet_parser_data_event* ev = (telnet_parser_data_event*)event;
|
||||
process_data_event(nvt, ev->data, ev->length);
|
||||
break;
|
||||
}
|
||||
|
||||
case TELNET_EV_PARSER_OPTION:
|
||||
{
|
||||
telnet_parser_option_event* ev = (telnet_parser_option_event*)event;
|
||||
process_option_event(nvt, ev->command, ev->option);
|
||||
break;
|
||||
}
|
||||
|
||||
case TELNET_EV_PARSER_SUBNEGOTIATION:
|
||||
{
|
||||
telnet_parser_subnegotiation_event* ev = (telnet_parser_subnegotiation_event*)event;
|
||||
process_subnegotiation_event(nvt, ev->active, ev->option);
|
||||
break;
|
||||
}
|
||||
|
||||
case TELNET_EV_PARSER_COMMAND:
|
||||
{
|
||||
if (nvt->callback) {
|
||||
telnet_parser_command_event* parser_ev = (telnet_parser_command_event*) event;
|
||||
|
||||
telnet_command_event ev;
|
||||
ev.SUPER_.type = TELNET_EV_COMMAND;
|
||||
ev.command = parser_ev->command;
|
||||
nvt->callback(nvt, (telnet_event*)&ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TELNET_EV_PARSER_WARNING:
|
||||
{
|
||||
if (nvt->callback) {
|
||||
telnet_parser_warning_event* parser_ev = (telnet_parser_warning_event*) event;
|
||||
|
||||
telnet_warning_event ev;
|
||||
ev.SUPER_.type = TELNET_EV_WARNING;
|
||||
ev.message = parser_ev->message;
|
||||
ev.position = parser_ev->position;
|
||||
nvt->callback(nvt, (telnet_event*)&ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
telnet_nvt* telnet_nvt_new(void* userdata,
|
||||
telnet_nvt_event_callback nvt_callback,
|
||||
telnet_telopt_event_callback telopt_callback,
|
||||
telnet_negotiate_event_callback negotiate_callback)
|
||||
{
|
||||
telnet_nvt* nvt = malloc(sizeof(telnet_nvt));
|
||||
if (nvt)
|
||||
{
|
||||
telnet_parser* parser = telnet_parser_new((void*)nvt, &process_event);
|
||||
if (parser)
|
||||
{
|
||||
memset(nvt, 0, sizeof(*nvt));
|
||||
nvt->parser = parser;
|
||||
nvt->callback = nvt_callback;
|
||||
nvt->telopt_callback = telopt_callback;
|
||||
nvt->negotiate_callback = negotiate_callback;
|
||||
nvt->userdata = userdata;
|
||||
nvt->current_remote = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(nvt);
|
||||
nvt = NULL;
|
||||
}
|
||||
}
|
||||
return nvt;
|
||||
}
|
||||
|
||||
void telnet_nvt_free(telnet_nvt* nvt)
|
||||
{
|
||||
if (nvt)
|
||||
{
|
||||
telnet_parser_free(nvt->parser);
|
||||
free(nvt);
|
||||
}
|
||||
}
|
||||
|
||||
telnet_error telnet_get_userdata(telnet_nvt* nvt, void** userdata)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
|
||||
*userdata = nvt->userdata;
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_receive(telnet_nvt* nvt, const telnet_byte* data, size_t length, size_t* bytes_used)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
|
||||
return telnet_parser_parse(nvt->parser, data, length, bytes_used);
|
||||
}
|
||||
|
||||
telnet_error telnet_interrupt(telnet_nvt* nvt)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
|
||||
return telnet_parser_interrupt(nvt->parser);
|
||||
}
|
||||
|
||||
|
||||
static int safe_concat(const telnet_byte* in, size_t inlen, telnet_byte* out, size_t outlen)
|
||||
{
|
||||
// Copy as much as possible into the buffer.
|
||||
memcpy(out, in, (outlen < inlen) ? outlen : inlen);
|
||||
|
||||
// true if everything could be copied, false otherwise
|
||||
return outlen >= inlen;
|
||||
}
|
||||
|
||||
// Escapes any special characters in data, writing the result data to out.
|
||||
// Returns -1 if not everything could be copied (and out is full).
|
||||
// Otherwise returns the length of the data in out.
|
||||
//
|
||||
// To avoid potential -1 return values, pass in an out buffer double the length of the data buffer.
|
||||
static size_t telnet_escape(const telnet_byte* data, size_t length, telnet_byte* out, size_t outsize)
|
||||
{
|
||||
if (data == NULL || out == NULL)
|
||||
return 0;
|
||||
|
||||
size_t outlen = 0;
|
||||
size_t left = 0;
|
||||
size_t right = 0;
|
||||
const char* seq = NULL;
|
||||
for (; right < length; ++right)
|
||||
{
|
||||
switch (data[right])
|
||||
{
|
||||
case IAC_IAC:
|
||||
seq = "\xFF\xFF";
|
||||
break;
|
||||
case '\r':
|
||||
// Only escape \r if it doesn't immediately precede \n.
|
||||
if (right + 1 >= length || data[right+1] != '\n')
|
||||
{
|
||||
seq = "\r\0";
|
||||
break;
|
||||
}
|
||||
// !!FALLTHROUGH!!
|
||||
default:
|
||||
continue; // Move to the next character
|
||||
}
|
||||
|
||||
// Add any normal data that hasn't been added yet.
|
||||
if (safe_concat(data+left, right-left, out+outlen, outsize-outlen) == 0)
|
||||
return -1;
|
||||
outlen += right - left;
|
||||
left = right + 1;
|
||||
|
||||
// Add the escape sequence.
|
||||
if (safe_concat((const telnet_byte*)seq, 2, out+outlen, outsize-outlen) == 0)
|
||||
return -1;
|
||||
outlen += 2;
|
||||
}
|
||||
|
||||
// Add any leftover normal data.
|
||||
if (left < right)
|
||||
{
|
||||
if (safe_concat(data+left, right-left, out+outlen, outsize-outlen) == 0)
|
||||
return -1;
|
||||
outlen += right - left;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
telnet_error telnet_send_data(telnet_nvt* nvt, const telnet_byte* data, const size_t length)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
else if (!nvt->callback)
|
||||
return TELNET_E_OK; // immediate success since they apparently don't want the data to go anywhere
|
||||
|
||||
// Due to the nature of the protocol, the most any one byte can be encoded as is two bytes.
|
||||
// Hence, the smallest buffer guaranteed to contain any input is double the length of the source.
|
||||
size_t bufsize = sizeof(telnet_byte) * length * 2;
|
||||
telnet_byte* buf = malloc(bufsize);
|
||||
if (!buf)
|
||||
return TELNET_E_ALLOC;
|
||||
|
||||
bufsize = telnet_escape(data, length, buf, bufsize);
|
||||
|
||||
SEND_CALLBACK(nvt, buf, bufsize);
|
||||
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_send_command(telnet_nvt* nvt, const telnet_byte command)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
else if (command >= IAC_SB || command == IAC_SE)
|
||||
return TELNET_E_INVALID_COMMAND; // Invalid command
|
||||
|
||||
const telnet_byte buf[] = {IAC_IAC, command};
|
||||
SEND_CALLBACK(nvt, buf, 2);
|
||||
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_send_subnegotiation(telnet_nvt* nvt, const telnet_byte option, const telnet_byte* data, const size_t length)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
else if (!telopt_subnegotiable(nvt, option))
|
||||
return TELNET_E_NOT_SUBNEGOTIABLE;
|
||||
else if (!nvt->callback)
|
||||
return TELNET_E_OK;
|
||||
|
||||
// length*2 is the maximum buffer size needed for an escaped string.
|
||||
// The extra five bytes are for the IAC, SB, <option>, IAC, and SE frame around the data.
|
||||
size_t bufsize = (sizeof(telnet_byte) * length * 2) + 5;
|
||||
telnet_byte* buf = malloc(bufsize);
|
||||
if (!buf)
|
||||
return TELNET_E_ALLOC;
|
||||
|
||||
// Begin with IAC SB <option>
|
||||
telnet_byte iac[] = {IAC_IAC, IAC_SB, option};
|
||||
memcpy(buf, iac, 3);
|
||||
|
||||
// Add the subnegotiation body
|
||||
size_t escaped_length = telnet_escape(data, length, buf+3, bufsize-3) + 3;
|
||||
|
||||
// End with IAC SE
|
||||
iac[1] = IAC_SE;
|
||||
memcpy(buf+escaped_length, iac, 2);
|
||||
escaped_length += 2;
|
||||
|
||||
SEND_CALLBACK(nvt, buf, escaped_length);
|
||||
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
|
||||
telnet_error telnet_telopt_enable(telnet_nvt* nvt,
|
||||
telnet_byte telopt,
|
||||
telnet_telopt_location where)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
|
||||
telnet_qstate* q = &nvt->options[telopt];
|
||||
if (where == TELNET_LOCAL) {
|
||||
switch (q->local)
|
||||
{
|
||||
case Q_NO:
|
||||
q->local = Q_WANTYES;
|
||||
send_option(nvt, IAC_WILL, telopt);
|
||||
break;
|
||||
case Q_WANTNO:
|
||||
q->local = Q_WANTNOYES;
|
||||
break;
|
||||
case Q_WANTYESNO:
|
||||
q->local = Q_WANTYES;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (q->remote)
|
||||
{
|
||||
case Q_NO:
|
||||
q->remote = Q_WANTYES;
|
||||
send_option(nvt, IAC_DO, telopt);
|
||||
break;
|
||||
case Q_WANTNO:
|
||||
q->remote = Q_WANTNOYES;
|
||||
break;
|
||||
case Q_WANTYESNO:
|
||||
q->remote = Q_WANTYES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_telopt_disable(telnet_nvt* nvt,
|
||||
telnet_byte telopt,
|
||||
telnet_telopt_location where)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
|
||||
telnet_qstate* q = &nvt->options[telopt];
|
||||
if (where == TELNET_LOCAL) {
|
||||
switch (q->local)
|
||||
{
|
||||
case Q_YES:
|
||||
send_option(nvt, IAC_WONT, telopt);
|
||||
q->local = Q_WANTNO;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_LOCAL, 0);
|
||||
break;
|
||||
case Q_WANTNOYES:
|
||||
q->local = Q_WANTNO;
|
||||
break;
|
||||
case Q_WANTYES:
|
||||
q->local = Q_WANTYESNO;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (q->remote)
|
||||
{
|
||||
case Q_YES:
|
||||
send_option(nvt, IAC_DONT, telopt);
|
||||
q->remote = Q_WANTNO;
|
||||
TELOPT_TOGGLE_CALLBACK(nvt, telopt, TELNET_REMOTE, 0);
|
||||
break;
|
||||
case Q_WANTNOYES:
|
||||
q->remote = Q_WANTNO;
|
||||
break;
|
||||
case Q_WANTYES:
|
||||
q->remote = Q_WANTYESNO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_telopt_status(telnet_nvt* nvt,
|
||||
telnet_byte telopt,
|
||||
telnet_telopt_location where,
|
||||
unsigned char* status)
|
||||
{
|
||||
if (!nvt)
|
||||
return TELNET_E_BAD_NVT;
|
||||
|
||||
*status = telopt_status(nvt, telopt, where);
|
||||
return TELNET_E_OK;
|
||||
}
|
459
outside/anachronism/src/parser.c
Normal file
459
outside/anachronism/src/parser.c
Normal file
@ -0,0 +1,459 @@
|
||||
|
||||
#line 1 "src/parser.rl"
|
||||
#include <string.h>
|
||||
#include <anachronism/parser.h>
|
||||
|
||||
#define BASE_EV(ev, t) \
|
||||
(ev).SUPER_.type = TELNET_EV_PARSER_##t
|
||||
|
||||
#define EV_DATA(ev, text, len) do {\
|
||||
BASE_EV(ev, DATA);\
|
||||
(ev).data = (text);\
|
||||
(ev).length = (len);\
|
||||
} while (0)
|
||||
|
||||
#define EV_COMMAND(ev, cmd) do {\
|
||||
BASE_EV(ev, COMMAND);\
|
||||
(ev).command = (cmd);\
|
||||
} while (0)
|
||||
|
||||
#define EV_OPTION(ev, cmd, opt) do {\
|
||||
BASE_EV(ev, OPTION);\
|
||||
(ev).command = (cmd);\
|
||||
(ev).option = (opt);\
|
||||
} while (0)
|
||||
|
||||
#define EV_SUBNEGOTIATION(ev, act, opt) do {\
|
||||
BASE_EV(ev, SUBNEGOTIATION);\
|
||||
(ev).active = (act);\
|
||||
(ev).option = (opt);\
|
||||
} while (0)
|
||||
|
||||
#define EV_WARNING(ev, msg, pos) do {\
|
||||
BASE_EV(ev, WARNING);\
|
||||
(ev).message = (msg);\
|
||||
(ev).position = (pos);\
|
||||
} while (0)
|
||||
|
||||
struct telnet_parser {
|
||||
int cs; /* current Ragel state */
|
||||
const telnet_byte* p; /* current position */
|
||||
const telnet_byte* pe; /* end of current packet */
|
||||
const telnet_byte* eof; /* end-of-file marker */
|
||||
|
||||
telnet_byte option_mark; /* temporary storage for a command byte */
|
||||
unsigned char interrupted; /* Flag for interrupts */
|
||||
|
||||
telnet_parser_callback callback; /* Receiver of Telnet events*/
|
||||
void* userdata; /* Context for parser callback */
|
||||
};
|
||||
|
||||
|
||||
#line 53 "src/parser.c"
|
||||
static const int telnet_parser_start = 7;
|
||||
static const int telnet_parser_first_final = 7;
|
||||
static const int telnet_parser_error = -1;
|
||||
|
||||
static const int telnet_parser_en_main = 7;
|
||||
|
||||
|
||||
#line 130 "src/parser.rl"
|
||||
|
||||
|
||||
telnet_parser* telnet_parser_new(void* userdata,
|
||||
telnet_parser_callback callback)
|
||||
{
|
||||
telnet_parser* parser = malloc(sizeof(telnet_parser));
|
||||
if (parser)
|
||||
{
|
||||
memset(parser, 0, sizeof(*parser));
|
||||
|
||||
#line 72 "src/parser.c"
|
||||
{
|
||||
parser->cs = telnet_parser_start;
|
||||
}
|
||||
|
||||
#line 140 "src/parser.rl"
|
||||
parser->callback = callback;
|
||||
parser->userdata = userdata;
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
void telnet_parser_free(telnet_parser* parser)
|
||||
{
|
||||
free(parser);
|
||||
}
|
||||
|
||||
telnet_error telnet_parser_get_userdata(telnet_parser* parser, void** userdata)
|
||||
{
|
||||
if (!parser)
|
||||
return TELNET_E_BAD_PARSER;
|
||||
|
||||
*userdata = parser->userdata;
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_parser_parse(telnet_parser* parser,
|
||||
const telnet_byte* data,
|
||||
size_t length,
|
||||
size_t* bytes_used)
|
||||
{
|
||||
if (!parser)
|
||||
return TELNET_E_BAD_PARSER;
|
||||
|
||||
// Reset the interrupt flag
|
||||
parser->interrupted = 0;
|
||||
|
||||
// Only bother saving text if it'll be used
|
||||
telnet_byte* buf = NULL;
|
||||
size_t buflen = 0;
|
||||
if (parser->callback)
|
||||
{
|
||||
// Because of how the parser translates data, a run of text is guaranteed to
|
||||
// be at most 'length' characters long. In practice it's usually less, due to
|
||||
// escaped characters (IAC IAC -> IAC) and text separated by commands.
|
||||
buf = malloc(length * sizeof(*buf));
|
||||
if (!buf)
|
||||
return TELNET_E_ALLOC;
|
||||
}
|
||||
|
||||
parser->p = data;
|
||||
parser->pe = data + length;
|
||||
parser->eof = parser->pe;
|
||||
|
||||
|
||||
#line 127 "src/parser.c"
|
||||
{
|
||||
if ( ( parser->p) == ( parser->pe) )
|
||||
goto _test_eof;
|
||||
switch ( parser->cs )
|
||||
{
|
||||
tr1:
|
||||
#line 6 "src/parser_common.rl"
|
||||
{( parser->p)--;}
|
||||
#line 111 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_warning_event ev;
|
||||
EV_WARNING(ev, "Invalid \\r: not followed by \\n or \\0.", ( parser->p)-data);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
goto st7;
|
||||
tr2:
|
||||
#line 67 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf)
|
||||
buf[buflen++] = (*( parser->p));
|
||||
}
|
||||
goto st7;
|
||||
tr3:
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
#line 72 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf)
|
||||
{
|
||||
telnet_parser_command_event ev;
|
||||
EV_COMMAND(ev, (*( parser->p)));
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
goto st7;
|
||||
tr12:
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
#line 6 "src/parser_common.rl"
|
||||
{( parser->p)--;}
|
||||
#line 119 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_warning_event ev;
|
||||
EV_WARNING(ev, "IAC followed by invalid command.", ( parser->p)-data);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
#line 102 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_subnegotiation_event ev;
|
||||
EV_SUBNEGOTIATION(ev, 0, parser->option_mark);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
goto st7;
|
||||
tr13:
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
#line 102 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_subnegotiation_event ev;
|
||||
EV_SUBNEGOTIATION(ev, 0, parser->option_mark);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
goto st7;
|
||||
tr14:
|
||||
#line 84 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf)
|
||||
{
|
||||
telnet_parser_option_event ev;
|
||||
EV_OPTION(ev, parser->option_mark, (*( parser->p)));
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
goto st7;
|
||||
st7:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof7;
|
||||
case 7:
|
||||
#line 252 "src/parser.c"
|
||||
switch( (*( parser->p)) ) {
|
||||
case 13u: goto tr15;
|
||||
case 255u: goto st1;
|
||||
}
|
||||
goto tr2;
|
||||
tr15:
|
||||
#line 67 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf)
|
||||
buf[buflen++] = (*( parser->p));
|
||||
}
|
||||
goto st0;
|
||||
st0:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof0;
|
||||
case 0:
|
||||
#line 269 "src/parser.c"
|
||||
switch( (*( parser->p)) ) {
|
||||
case 0u: goto st7;
|
||||
case 10u: goto tr2;
|
||||
}
|
||||
goto tr1;
|
||||
st1:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof1;
|
||||
case 1:
|
||||
switch( (*( parser->p)) ) {
|
||||
case 250u: goto tr4;
|
||||
case 255u: goto tr2;
|
||||
}
|
||||
if ( 251u <= (*( parser->p)) && (*( parser->p)) <= 254u )
|
||||
goto tr5;
|
||||
goto tr3;
|
||||
tr4:
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
goto st2;
|
||||
st2:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof2;
|
||||
case 2:
|
||||
#line 302 "src/parser.c"
|
||||
goto tr6;
|
||||
tr11:
|
||||
#line 6 "src/parser_common.rl"
|
||||
{( parser->p)--;}
|
||||
#line 111 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_warning_event ev;
|
||||
EV_WARNING(ev, "Invalid \\r: not followed by \\n or \\0.", ( parser->p)-data);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
goto st3;
|
||||
tr7:
|
||||
#line 67 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf)
|
||||
buf[buflen++] = (*( parser->p));
|
||||
}
|
||||
goto st3;
|
||||
tr6:
|
||||
#line 93 "src/parser.rl"
|
||||
{
|
||||
parser->option_mark = (*( parser->p));
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_subnegotiation_event ev;
|
||||
EV_SUBNEGOTIATION(ev, 1, parser->option_mark);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
goto st3;
|
||||
st3:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof3;
|
||||
case 3:
|
||||
#line 350 "src/parser.c"
|
||||
switch( (*( parser->p)) ) {
|
||||
case 13u: goto tr8;
|
||||
case 255u: goto st5;
|
||||
}
|
||||
goto tr7;
|
||||
tr8:
|
||||
#line 67 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buf)
|
||||
buf[buflen++] = (*( parser->p));
|
||||
}
|
||||
goto st4;
|
||||
st4:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof4;
|
||||
case 4:
|
||||
#line 367 "src/parser.c"
|
||||
switch( (*( parser->p)) ) {
|
||||
case 0u: goto st3;
|
||||
case 10u: goto tr7;
|
||||
}
|
||||
goto tr11;
|
||||
st5:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof5;
|
||||
case 5:
|
||||
switch( (*( parser->p)) ) {
|
||||
case 240u: goto tr13;
|
||||
case 255u: goto tr7;
|
||||
}
|
||||
goto tr12;
|
||||
tr5:
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
#line 81 "src/parser.rl"
|
||||
{
|
||||
parser->option_mark = (*( parser->p));
|
||||
}
|
||||
goto st6;
|
||||
st6:
|
||||
if ( ++( parser->p) == ( parser->pe) )
|
||||
goto _test_eof6;
|
||||
case 6:
|
||||
#line 402 "src/parser.c"
|
||||
goto tr14;
|
||||
}
|
||||
_test_eof7: parser->cs = 7; goto _test_eof;
|
||||
_test_eof0: parser->cs = 0; goto _test_eof;
|
||||
_test_eof1: parser->cs = 1; goto _test_eof;
|
||||
_test_eof2: parser->cs = 2; goto _test_eof;
|
||||
_test_eof3: parser->cs = 3; goto _test_eof;
|
||||
_test_eof4: parser->cs = 4; goto _test_eof;
|
||||
_test_eof5: parser->cs = 5; goto _test_eof;
|
||||
_test_eof6: parser->cs = 6; goto _test_eof;
|
||||
|
||||
_test_eof: {}
|
||||
if ( ( parser->p) == ( parser->eof) )
|
||||
{
|
||||
switch ( parser->cs ) {
|
||||
case 3:
|
||||
case 7:
|
||||
#line 57 "src/parser.rl"
|
||||
{
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#line 431 "src/parser.c"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#line 189 "src/parser.rl"
|
||||
|
||||
if (bytes_used != NULL)
|
||||
*bytes_used = parser->p - data;
|
||||
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
parser->p = parser->pe = parser->eof = NULL;
|
||||
|
||||
return (parser->interrupted) ? TELNET_E_INTERRUPT : TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_parser_interrupt(telnet_parser* parser)
|
||||
{
|
||||
if (!parser)
|
||||
return TELNET_E_BAD_PARSER;
|
||||
|
||||
// Force the parser to stop where it's at.
|
||||
if (parser->p)
|
||||
parser->eof = parser->pe = parser->p + 1;
|
||||
|
||||
parser->interrupted = 1;
|
||||
return TELNET_E_OK;
|
||||
}
|
211
outside/anachronism/src/parser.rl
Normal file
211
outside/anachronism/src/parser.rl
Normal file
@ -0,0 +1,211 @@
|
||||
#include <string.h>
|
||||
#include <anachronism/parser.h>
|
||||
|
||||
#define BASE_EV(ev, t) \
|
||||
(ev).SUPER_.type = TELNET_EV_PARSER_##t
|
||||
|
||||
#define EV_DATA(ev, text, len) do {\
|
||||
BASE_EV(ev, DATA);\
|
||||
(ev).data = (text);\
|
||||
(ev).length = (len);\
|
||||
} while (0)
|
||||
|
||||
#define EV_COMMAND(ev, cmd) do {\
|
||||
BASE_EV(ev, COMMAND);\
|
||||
(ev).command = (cmd);\
|
||||
} while (0)
|
||||
|
||||
#define EV_OPTION(ev, cmd, opt) do {\
|
||||
BASE_EV(ev, OPTION);\
|
||||
(ev).command = (cmd);\
|
||||
(ev).option = (opt);\
|
||||
} while (0)
|
||||
|
||||
#define EV_SUBNEGOTIATION(ev, act, opt) do {\
|
||||
BASE_EV(ev, SUBNEGOTIATION);\
|
||||
(ev).active = (act);\
|
||||
(ev).option = (opt);\
|
||||
} while (0)
|
||||
|
||||
#define EV_WARNING(ev, msg, pos) do {\
|
||||
BASE_EV(ev, WARNING);\
|
||||
(ev).message = (msg);\
|
||||
(ev).position = (pos);\
|
||||
} while (0)
|
||||
|
||||
struct telnet_parser {
|
||||
int cs; /* current Ragel state */
|
||||
const telnet_byte* p; /* current position */
|
||||
const telnet_byte* pe; /* end of current packet */
|
||||
const telnet_byte* eof; /* end-of-file marker */
|
||||
|
||||
telnet_byte option_mark; /* temporary storage for a command byte */
|
||||
unsigned char interrupted; /* Flag for interrupts */
|
||||
|
||||
telnet_parser_callback callback; /* Receiver of Telnet events*/
|
||||
void* userdata; /* Context for parser callback */
|
||||
};
|
||||
|
||||
%%{
|
||||
machine telnet_parser;
|
||||
|
||||
access parser->;
|
||||
variable p parser->p;
|
||||
variable pe parser->pe;
|
||||
variable eof parser->eof;
|
||||
|
||||
action flush_text {
|
||||
if (parser->callback && buflen > 0)
|
||||
{
|
||||
telnet_parser_data_event ev;
|
||||
EV_DATA(ev, buf, buflen);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
buflen = 0;
|
||||
}
|
||||
}
|
||||
|
||||
action char {
|
||||
if (parser->callback && buf)
|
||||
buf[buflen++] = fc;
|
||||
}
|
||||
|
||||
action basic_command {
|
||||
if (parser->callback && buf)
|
||||
{
|
||||
telnet_parser_command_event ev;
|
||||
EV_COMMAND(ev, fc);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
|
||||
action option_mark {
|
||||
parser->option_mark = fc;
|
||||
}
|
||||
action option_command {
|
||||
if (parser->callback && buf)
|
||||
{
|
||||
telnet_parser_option_event ev;
|
||||
EV_OPTION(ev, parser->option_mark, fc);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
|
||||
action subneg_command {
|
||||
parser->option_mark = fc;
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_subnegotiation_event ev;
|
||||
EV_SUBNEGOTIATION(ev, 1, parser->option_mark);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
action subneg_command_end {
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_subnegotiation_event ev;
|
||||
EV_SUBNEGOTIATION(ev, 0, parser->option_mark);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
|
||||
action warning_cr {
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_warning_event ev;
|
||||
EV_WARNING(ev, "Invalid \\r: not followed by \\n or \\0.", fpc-data);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
action warning_iac {
|
||||
if (parser->callback && buf != NULL)
|
||||
{
|
||||
telnet_parser_warning_event ev;
|
||||
EV_WARNING(ev, "IAC followed by invalid command.", fpc-data);
|
||||
parser->callback(parser, (telnet_parser_event*)&ev);
|
||||
}
|
||||
}
|
||||
|
||||
include telnet_parser_common "parser_common.rl";
|
||||
write data;
|
||||
}%%
|
||||
|
||||
telnet_parser* telnet_parser_new(void* userdata,
|
||||
telnet_parser_callback callback)
|
||||
{
|
||||
telnet_parser* parser = malloc(sizeof(telnet_parser));
|
||||
if (parser)
|
||||
{
|
||||
memset(parser, 0, sizeof(*parser));
|
||||
%% write init;
|
||||
parser->callback = callback;
|
||||
parser->userdata = userdata;
|
||||
}
|
||||
return parser;
|
||||
}
|
||||
|
||||
void telnet_parser_free(telnet_parser* parser)
|
||||
{
|
||||
free(parser);
|
||||
}
|
||||
|
||||
telnet_error telnet_parser_get_userdata(telnet_parser* parser, void** userdata)
|
||||
{
|
||||
if (!parser)
|
||||
return TELNET_E_BAD_PARSER;
|
||||
|
||||
*userdata = parser->userdata;
|
||||
return TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_parser_parse(telnet_parser* parser,
|
||||
const telnet_byte* data,
|
||||
size_t length,
|
||||
size_t* bytes_used)
|
||||
{
|
||||
if (!parser)
|
||||
return TELNET_E_BAD_PARSER;
|
||||
|
||||
// Reset the interrupt flag
|
||||
parser->interrupted = 0;
|
||||
|
||||
// Only bother saving text if it'll be used
|
||||
telnet_byte* buf = NULL;
|
||||
size_t buflen = 0;
|
||||
if (parser->callback)
|
||||
{
|
||||
// Because of how the parser translates data, a run of text is guaranteed to
|
||||
// be at most 'length' characters long. In practice it's usually less, due to
|
||||
// escaped characters (IAC IAC -> IAC) and text separated by commands.
|
||||
buf = malloc(length * sizeof(*buf));
|
||||
if (!buf)
|
||||
return TELNET_E_ALLOC;
|
||||
}
|
||||
|
||||
parser->p = data;
|
||||
parser->pe = data + length;
|
||||
parser->eof = parser->pe;
|
||||
|
||||
%% write exec;
|
||||
|
||||
if (bytes_used != NULL)
|
||||
*bytes_used = parser->p - data;
|
||||
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
parser->p = parser->pe = parser->eof = NULL;
|
||||
|
||||
return (parser->interrupted) ? TELNET_E_INTERRUPT : TELNET_E_OK;
|
||||
}
|
||||
|
||||
telnet_error telnet_parser_interrupt(telnet_parser* parser)
|
||||
{
|
||||
if (!parser)
|
||||
return TELNET_E_BAD_PARSER;
|
||||
|
||||
// Force the parser to stop where it's at.
|
||||
if (parser->p)
|
||||
parser->eof = parser->pe = parser->p + 1;
|
||||
|
||||
parser->interrupted = 1;
|
||||
return TELNET_E_OK;
|
||||
}
|
85
outside/anachronism/src/parser_common.rl
Normal file
85
outside/anachronism/src/parser_common.rl
Normal file
@ -0,0 +1,85 @@
|
||||
%%{
|
||||
machine telnet_parser_common;
|
||||
alphtype unsigned char;
|
||||
|
||||
# Shorthand for tidiness.
|
||||
action fhold {fhold;}
|
||||
|
||||
# Special bytes that must be handled differently from normal text:
|
||||
CR = "\r"; # Only \0 or \n may follow
|
||||
IAC = 255; # Telnet command marker
|
||||
special_byte = CR | IAC;
|
||||
|
||||
# The only bytes that may follow a CR:
|
||||
NL = "\n";
|
||||
NUL = "\0";
|
||||
|
||||
# The only bytes that may follow an IAC:
|
||||
SE = 240;
|
||||
NOP = 241;
|
||||
DM = 242;
|
||||
BRK = 243;
|
||||
IP = 244;
|
||||
AO = 245;
|
||||
AYT = 246;
|
||||
EC = 247;
|
||||
EL = 248;
|
||||
GA = 249;
|
||||
SB = 250;
|
||||
WILL = 251;
|
||||
WONT = 252;
|
||||
DO = 253;
|
||||
DONT = 254;
|
||||
# IAC IAC is interpreted as a plain-text IAC byte.
|
||||
|
||||
# Sorting the above IAC commands by type:
|
||||
iac_option_type = WILL | WONT | DO | DONT;
|
||||
iac_subneg_type = SB;
|
||||
iac_command_type = ^(iac_option_type | iac_subneg_type | IAC);
|
||||
|
||||
###
|
||||
# Plain text
|
||||
###
|
||||
plain_text = (^special_byte) @char;
|
||||
cr_seq = CR @char
|
||||
( NUL
|
||||
| NL @char
|
||||
| ^(NUL|NL) @fhold @warning_cr @flush_text
|
||||
);
|
||||
|
||||
###
|
||||
# IAC sequence
|
||||
###
|
||||
iac_command = iac_command_type @basic_command;
|
||||
|
||||
iac_option = iac_option_type @option_mark
|
||||
any @option_command;
|
||||
|
||||
iac_subneg = iac_subneg_type any @subneg_command
|
||||
( plain_text
|
||||
| cr_seq
|
||||
| IAC IAC @char
|
||||
)** %/flush_text
|
||||
IAC
|
||||
( SE
|
||||
| ^(IAC|SE) @fhold @warning_iac
|
||||
) >flush_text @subneg_command_end;
|
||||
|
||||
iac_seq = ( iac_command
|
||||
| iac_option
|
||||
| iac_subneg
|
||||
);
|
||||
|
||||
###
|
||||
# Telnet stream
|
||||
###
|
||||
telnet_stream = ( plain_text
|
||||
| cr_seq
|
||||
| IAC
|
||||
( IAC @char
|
||||
| iac_seq >flush_text
|
||||
)
|
||||
)** %/flush_text;
|
||||
|
||||
main := telnet_stream;
|
||||
}%%
|
12
v/http.c
12
v/http.c
@ -260,6 +260,15 @@ _http_respond_request(u2_hreq* req_u,
|
||||
req_u->end = u2_yes;
|
||||
}
|
||||
|
||||
/* _http_conn_free_early(): free http connection on failure.
|
||||
*/
|
||||
static void
|
||||
_http_conn_free_early(uv_handle_t* han_t)
|
||||
{
|
||||
u2_hcon* hon_u = (void*) han_t;
|
||||
free(hon_u);
|
||||
}
|
||||
|
||||
/* _http_conn_free(): free http connection on close.
|
||||
*/
|
||||
static void
|
||||
@ -605,8 +614,7 @@ _http_conn_new(u2_http *htp_u)
|
||||
uL(fprintf(uH, "http: accept: %s\n",
|
||||
uv_strerror(uv_last_error(u2L))));
|
||||
|
||||
uv_close((uv_handle_t*)&hon_u->wax_u, 0);
|
||||
free(hon_u);
|
||||
uv_close((uv_handle_t*)&hon_u->wax_u, _http_conn_free_early);
|
||||
}
|
||||
else {
|
||||
uv_read_start((uv_stream_t*)&hon_u->wax_u,
|
||||
|
1
v/loop.c
1
v/loop.c
@ -178,6 +178,7 @@ _lo_talk()
|
||||
u2_unix_io_talk();
|
||||
u2_ames_io_talk();
|
||||
u2_http_io_talk();
|
||||
u2_term_io_talk();
|
||||
}
|
||||
|
||||
/* u2_lo_exit(): terminate I/O across the process.
|
||||
|
27
v/reck.c
27
v/reck.c
@ -223,33 +223,6 @@ _reck_scot(u2_reck* rec_u, u2_noun dim)
|
||||
return u2_do("scot", dim);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* _reck_spoo(): noun path from c, kind of a hack.
|
||||
*/
|
||||
static u2_noun
|
||||
_reck_spoo(c3_c* pax_c)
|
||||
{
|
||||
if ( !*pax_c ) {
|
||||
return u2_nul;
|
||||
} else {
|
||||
c3_c* ash_c = strchr(pax_c, '/');
|
||||
|
||||
if ( !ash_c ) {
|
||||
return u2nc(u2_ci_string(pax_c), u2_nul);
|
||||
}
|
||||
else {
|
||||
u2_noun pan;
|
||||
|
||||
*ash_c = 0;
|
||||
pan = u2_ci_string(pax_c);
|
||||
*ash_c = '/';
|
||||
|
||||
return u2nc(pan, _reck_spoo(ash_c + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* u2_reck_time(): set the reck time.
|
||||
*/
|
||||
void
|
||||
|
2
v/sist.c
2
v/sist.c
@ -1176,7 +1176,7 @@ _sist_rest(u2_reck* rec_u)
|
||||
|
||||
// Hey, fscker! It worked.
|
||||
{
|
||||
u2_term_ef_boil(tno_l);
|
||||
u2_term_ef_boil();
|
||||
}
|
||||
}
|
||||
|
||||
|
360
v/term.c
360
v/term.c
@ -18,11 +18,20 @@
|
||||
#include <curses.h>
|
||||
#include <termios.h>
|
||||
#include <term.h>
|
||||
|
||||
#include <anachronism/common.h>
|
||||
#include <anachronism/nvt.h>
|
||||
#include "all.h"
|
||||
#include "v/vere.h"
|
||||
|
||||
static void _term_read_tn_cb(uv_stream_t*, ssize_t, uv_buf_t);
|
||||
static void _term_read_cb(uv_stream_t*, ssize_t, uv_buf_t);
|
||||
static void _term_suck(u2_utty*, const c3_y*, ssize_t);
|
||||
static void _tel_event(telnet_nvt*, telnet_event*);
|
||||
static void _tel_opt(telnet_nvt*, telnet_byte, telnet_telopt_event*);
|
||||
|
||||
#define _T_ECHO 1 // local echo
|
||||
#define _T_CTIM 3 // suppress GA/char-at-a-time
|
||||
#define _T_NAWS 31 // negotiate about window size
|
||||
|
||||
/* _term_alloc(): libuv buffer allocator.
|
||||
*/
|
||||
@ -32,6 +41,44 @@ _term_alloc(uv_handle_t* had_u, size_t len_i)
|
||||
return uv_buf_init(c3_malloc(len_i), len_i);
|
||||
}
|
||||
|
||||
/* _term_close_cb(): free terminal.
|
||||
*/
|
||||
static void
|
||||
_term_close_cb(uv_handle_t* han_t)
|
||||
{
|
||||
u2_utty* tty_u = (void*) han_t;
|
||||
if ( u2_Host.uty_u == tty_u ) {
|
||||
u2_Host.uty_u = tty_u->nex_u;
|
||||
}
|
||||
else {
|
||||
u2_utty* uty_u;
|
||||
for (uty_u = u2_Host.uty_u; uty_u; uty_u = uty_u->nex_u ) {
|
||||
if ( uty_u->nex_u == tty_u ) {
|
||||
uty_u->nex_u = tty_u->nex_u;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
u2_noun tid = u2_dc("scot", c3__ud, tty_u->tid_l);
|
||||
u2_noun pax = u2nq(c3__gold, c3__term, tid, u2_nul);
|
||||
u2_reck_plan(u2A, u2k(pax), u2nc(c3__hook, u2_nul));
|
||||
u2z(pax);
|
||||
}
|
||||
free(tty_u);
|
||||
}
|
||||
|
||||
/* _tel_close_cb(): close telnet terminal
|
||||
*/
|
||||
static void
|
||||
_tel_close_cb(uv_handle_t* han_t)
|
||||
{
|
||||
u2_utel* pty_u = (u2_utel*)(void*)han_t;
|
||||
telnet_nvt_free(pty_u->tel_u);
|
||||
_term_close_cb(han_t);
|
||||
}
|
||||
|
||||
/* u2_term_io_init(): initialize terminal.
|
||||
*/
|
||||
void
|
||||
@ -77,7 +124,6 @@ u2_term_io_init()
|
||||
|
||||
uty_u->ufo_u.inn.max_w = 0;
|
||||
|
||||
#if 1
|
||||
_utfo(inn, kcuu1);
|
||||
_utfo(inn, kcud1);
|
||||
_utfo(inn, kcub1);
|
||||
@ -94,18 +140,6 @@ u2_term_io_init()
|
||||
_utfo(out, cud1);
|
||||
// _utfo(out, cub);
|
||||
// _utfo(out, cuf);
|
||||
#else
|
||||
// libuv hardcodes an ansi terminal - which doesn't seem to work...
|
||||
//
|
||||
uty_u->ufo_u.out.clear_y = "\033[H\033[J";
|
||||
uty_u->ufo_u.out.el_y = "\033[K";
|
||||
uty_u->ufo_u.out.ed_y = "\033[J";
|
||||
uty_u->ufo_u.out.bel_y = "\007";
|
||||
uty_u->ufo_u.out.cub1_y = "\010";
|
||||
uty_u->ufo_u.out.cud1_y = "\033[B";
|
||||
uty_u->ufo_u.out.cuu1_y = "\033[A";
|
||||
uty_u->ufo_u.out.cuf1_y = "\033[C";
|
||||
#endif
|
||||
|
||||
// Terminfo chronically reports the wrong sequence for arrow
|
||||
// keys on xterms. Drastic fix for ridiculous unacceptable bug.
|
||||
@ -142,7 +176,6 @@ u2_term_io_init()
|
||||
|
||||
// Load old terminal state to restore.
|
||||
//
|
||||
#if 1
|
||||
{
|
||||
if ( 0 != tcgetattr(uty_u->fid_i, &uty_u->bak_u) ) {
|
||||
c3_assert(!"init-tcgetattr");
|
||||
@ -167,7 +200,6 @@ u2_term_io_init()
|
||||
uty_u->raw_u.c_cc[VMIN] = 0;
|
||||
uty_u->raw_u.c_cc[VTIME] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Initialize mirror and accumulator state.
|
||||
//
|
||||
@ -188,10 +220,8 @@ u2_term_io_init()
|
||||
//
|
||||
{
|
||||
uty_u->tid_l = 1;
|
||||
|
||||
uty_u->nex_u = u2_Host.uty_u;
|
||||
uty_u->nex_u = 0;
|
||||
u2_Host.uty_u = uty_u;
|
||||
u2_Host.tem_u = uty_u;
|
||||
}
|
||||
|
||||
if ( u2_no == u2_Host.ops_u.dem ) {
|
||||
@ -208,6 +238,120 @@ u2_term_io_init()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_term_listen_cb(uv_stream_t *wax_u, int sas_i)
|
||||
{
|
||||
u2_utel* pty_u = calloc(1, sizeof(*pty_u));
|
||||
u2_utty* tty_u = &pty_u->uty_t;
|
||||
uv_tcp_init(u2L, &tty_u->wax_u);
|
||||
if ( 0 != uv_accept(wax_u, (uv_stream_t*)&tty_u->wax_u) ) {
|
||||
uL(fprintf(uH, "term: accept: %s\n",
|
||||
uv_strerror(uv_last_error(u2L))));
|
||||
|
||||
uv_close((uv_handle_t*)&tty_u->wax_u, NULL);
|
||||
free(tty_u);
|
||||
}
|
||||
else {
|
||||
uv_read_start((uv_stream_t*)&tty_u->wax_u,
|
||||
_term_alloc,
|
||||
_term_read_tn_cb);
|
||||
|
||||
tty_u->ufo_u.out.clear_y = (const c3_y*)"\033[H\033[J";
|
||||
tty_u->ufo_u.out.el_y = (const c3_y*)"\033[K";
|
||||
tty_u->ufo_u.out.ed_y = (const c3_y*)"\033[J";
|
||||
tty_u->ufo_u.out.bel_y = (const c3_y*)"\007";
|
||||
tty_u->ufo_u.out.cub1_y = (const c3_y*)"\010";
|
||||
tty_u->ufo_u.out.cud1_y = (const c3_y*)"\033[B";
|
||||
tty_u->ufo_u.out.cuu1_y = (const c3_y*)"\033[A";
|
||||
tty_u->ufo_u.out.cuf1_y = (const c3_y*)"\033[C";
|
||||
|
||||
tty_u->ufo_u.inn.kcuu1_y = (const c3_y*)"\033[A";
|
||||
tty_u->ufo_u.inn.kcud1_y = (const c3_y*)"\033[B";
|
||||
tty_u->ufo_u.inn.kcuf1_y = (const c3_y*)"\033[C";
|
||||
tty_u->ufo_u.inn.kcub1_y = (const c3_y*)"\033[D";
|
||||
tty_u->ufo_u.inn.max_w = strlen("\033[D");
|
||||
|
||||
tty_u->fid_i = -1;
|
||||
|
||||
tty_u->tat_u.mir.lin_w = 0;
|
||||
tty_u->tat_u.mir.len_w = 0;
|
||||
tty_u->tat_u.mir.cus_w = 0;
|
||||
|
||||
tty_u->tat_u.esc.ape = u2_no;
|
||||
tty_u->tat_u.esc.bra = u2_no;
|
||||
|
||||
tty_u->tat_u.fut.len_w = 0;
|
||||
tty_u->tat_u.fut.wid_w = 0;
|
||||
|
||||
tty_u->tat_u.siz.col_l = 80;
|
||||
tty_u->tat_u.siz.row_l = 25;
|
||||
|
||||
tty_u->tid_l = u2_Host.uty_u->tid_l + 1;
|
||||
tty_u->nex_u = u2_Host.uty_u;
|
||||
u2_Host.uty_u = tty_u;
|
||||
pty_u->tel_u = telnet_nvt_new(tty_u, _tel_event, _tel_opt, NULL);
|
||||
|
||||
{
|
||||
u2_noun tid = u2_dc("scot", c3__ud, tty_u->tid_l);
|
||||
u2_noun pax = u2nq(c3__gold, c3__term, tid, u2_nul);
|
||||
u2_reck_plan(u2A, u2k(pax), u2nc(c3__blew, u2nc(80, 25)));
|
||||
u2_reck_plan(u2A, u2k(pax), u2nc(c3__hail, u2_nul));
|
||||
u2z(pax);
|
||||
}
|
||||
|
||||
telnet_telopt_enable(pty_u->tel_u, _T_ECHO, TELNET_LOCAL);
|
||||
telnet_telopt_enable(pty_u->tel_u, _T_CTIM, TELNET_LOCAL);
|
||||
telnet_telopt_enable(pty_u->tel_u, _T_NAWS, TELNET_REMOTE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
u2_term_io_talk(void)
|
||||
{
|
||||
struct sockaddr_in add_u;
|
||||
u2_utel* tel_u = &u2_Host.tel_u;
|
||||
|
||||
uv_tcp_init(u2L, &tel_u->uty_t.wax_u);
|
||||
tel_u->por_s = 10023;
|
||||
|
||||
memset(&add_u, 0, sizeof(add_u));
|
||||
add_u.sin_family = AF_INET;
|
||||
add_u.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
|
||||
/* Try ascending ports.
|
||||
*/
|
||||
while ( 1 ) {
|
||||
add_u.sin_port = htons(tel_u->por_s);
|
||||
|
||||
if ( 0 != uv_tcp_bind(&tel_u->uty_t.wax_u, add_u) ) {
|
||||
uv_err_t las_u = uv_last_error(u2L);
|
||||
|
||||
if ( UV_EADDRINUSE == las_u.code ) {
|
||||
tel_u->por_s++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
uL(fprintf(uH, "term: bind: %s\n", uv_strerror(las_u)));
|
||||
}
|
||||
}
|
||||
if ( 0 != uv_listen((uv_stream_t*)&tel_u->uty_t.wax_u,
|
||||
16, _term_listen_cb) )
|
||||
{
|
||||
uv_err_t las_u = uv_last_error(u2L);
|
||||
|
||||
if ( UV_EADDRINUSE == las_u.code ) {
|
||||
tel_u->por_s++;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
uL(fprintf(uH, "term: listen: %s\n", uv_strerror(las_u)));
|
||||
}
|
||||
}
|
||||
uL(fprintf(uH, "term: live on %d\n", tel_u->por_s));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* u2_term_io_exit(): clean up terminal.
|
||||
*/
|
||||
void
|
||||
@ -220,6 +364,7 @@ u2_term_io_exit(void)
|
||||
u2_utty* uty_u;
|
||||
|
||||
for ( uty_u = u2_Host.uty_u; uty_u; uty_u = uty_u->nex_u ) {
|
||||
if ( uty_u->fid_i == -1 ) { continue; }
|
||||
if ( 0 != tcsetattr(uty_u->fid_i, TCSADRAIN, &uty_u->bak_u) ) {
|
||||
c3_assert(!"exit-tcsetattr");
|
||||
}
|
||||
@ -525,6 +670,80 @@ _term_io_belt(u2_utty* uty_u, u2_noun blb)
|
||||
u2_reck_plan(u2A, pax, u2nc(c3__belt, blb));
|
||||
}
|
||||
|
||||
/* _tel_event(): telnet sucker
|
||||
*/
|
||||
#define _te_nvt telnet_nvt
|
||||
#define _te_evt telnet_event
|
||||
#define _te_dvt telnet_data_event
|
||||
#define _te_svt telnet_send_event
|
||||
static void
|
||||
_tel_event(_te_nvt* nvt, _te_evt* evt)
|
||||
{
|
||||
u2_utel* tel_u;
|
||||
c3_assert(0 < telnet_get_userdata(nvt, (void**)&tel_u));
|
||||
switch (evt->type)
|
||||
{
|
||||
case TELNET_EV_DATA:
|
||||
{
|
||||
_te_dvt* dv = (_te_dvt*)evt;
|
||||
_term_suck((u2_utty*)tel_u, dv->data, dv->length);
|
||||
break;
|
||||
}
|
||||
|
||||
case TELNET_EV_SEND:
|
||||
{
|
||||
_te_svt* sv = (_te_svt*)evt;
|
||||
_term_it_write_bytes((u2_utty*)tel_u, sv->length, sv->data);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define _to_evt telnet_telopt_event
|
||||
#define _to_dvt telnet_telopt_data_event
|
||||
#define _to_tvt telnet_telopt_toggle_event
|
||||
/* _tel_opt(): telnet event sucker
|
||||
*/
|
||||
static void
|
||||
_tel_opt(_te_nvt* nvt, telnet_byte opt, _to_evt* evt)
|
||||
{
|
||||
switch (evt->type)
|
||||
{
|
||||
default: break;
|
||||
case TELNET_EV_TELOPT_DATA:
|
||||
{
|
||||
_to_dvt* dv = (_to_dvt*)evt;
|
||||
u2_utel* tel_u;
|
||||
u2_noun pax;
|
||||
u2_noun blu;
|
||||
u2_noun tid;
|
||||
c3_s col_s;
|
||||
c3_s row_s;
|
||||
|
||||
if ( opt != _T_NAWS ) {
|
||||
return;
|
||||
}
|
||||
|
||||
c3_assert(0 < telnet_get_userdata(nvt, (void**)&tel_u));
|
||||
|
||||
col_s = dv->data[1] | (dv->data[0] << 8);
|
||||
row_s = dv->data[3] | (dv->data[2] << 8);
|
||||
|
||||
tel_u->uty_t.tat_u.siz.col_l = col_s;
|
||||
tel_u->uty_t.tat_u.siz.row_l = row_s;
|
||||
|
||||
tid = u2_dc("scot", c3__ud, tel_u->uty_t.tid_l);
|
||||
pax = u2nq(c3__gold, c3__term, tid, u2_nul);
|
||||
blu = u2nc(col_s, row_s);
|
||||
u2_reck_plan(u2A, pax, u2nc(c3__blew, blu));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* _term_io_suck_char(): process a single character.
|
||||
*/
|
||||
static void
|
||||
@ -532,8 +751,6 @@ _term_io_suck_char(u2_utty* uty_u, c3_y cay_y)
|
||||
{
|
||||
u2_utat* tat_u = &uty_u->tat_u;
|
||||
|
||||
// uL(fprintf(uH, "suck-char %x\n", cay_y));
|
||||
|
||||
if ( u2_yes == tat_u->esc.ape ) {
|
||||
if ( u2_yes == tat_u->esc.bra ) {
|
||||
switch ( cay_y ) {
|
||||
@ -611,6 +828,58 @@ _term_io_suck_char(u2_utty* uty_u, c3_y cay_y)
|
||||
}
|
||||
}
|
||||
|
||||
/* _term_read_tn_cb(): telnet read callback.
|
||||
*/
|
||||
static void
|
||||
_term_read_tn_cb(uv_stream_t* str_u,
|
||||
ssize_t siz_i,
|
||||
uv_buf_t buf_u)
|
||||
{
|
||||
u2_utel* pty_u = (u2_utel*)(void*)str_u;
|
||||
|
||||
u2_lo_open();
|
||||
{
|
||||
if ( siz_i < 0 ) {
|
||||
uv_err_t las_u = uv_last_error(u2L);
|
||||
|
||||
uL(fprintf(uH, "term %d: read: %s\n",
|
||||
pty_u->uty_t.tid_l, uv_strerror(las_u)));
|
||||
uv_close((uv_handle_t*)str_u, _tel_close_cb);
|
||||
goto err;
|
||||
}
|
||||
else {
|
||||
telnet_receive(pty_u->tel_u, (const telnet_byte*)buf_u.base, siz_i, 0);
|
||||
}
|
||||
|
||||
err:
|
||||
free(buf_u.base);
|
||||
}
|
||||
u2_lo_shut(u2_yes);
|
||||
}
|
||||
|
||||
/* _term_suck(): process a chunk of input
|
||||
*/
|
||||
static inline void
|
||||
_term_suck(u2_utty* uty_u, const c3_y* buf, ssize_t siz_i)
|
||||
{
|
||||
u2_lo_open();
|
||||
{
|
||||
if ( siz_i < 0 ) {
|
||||
uv_err_t las_u = uv_last_error(u2L);
|
||||
|
||||
uL(fprintf(uH, "term %d: read: %s\n", uty_u->tid_l, uv_strerror(las_u)));
|
||||
}
|
||||
else {
|
||||
c3_i i;
|
||||
|
||||
for ( i=0; i < siz_i; i++ ) {
|
||||
_term_io_suck_char(uty_u, buf[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
u2_lo_shut(u2_yes);
|
||||
}
|
||||
|
||||
/* _term_read_cb(): server read callback.
|
||||
*/
|
||||
static void
|
||||
@ -619,27 +888,8 @@ _term_read_cb(uv_stream_t* str_u,
|
||||
uv_buf_t buf_u)
|
||||
{
|
||||
u2_utty* uty_u = (u2_utty*)(void*)str_u;
|
||||
|
||||
u2_lo_open();
|
||||
{
|
||||
if ( siz_i < 0 ) {
|
||||
uv_err_t las_u = uv_last_error(u2L);
|
||||
|
||||
uL(fprintf(uH, "term: read: %s\n", uv_strerror(las_u)));
|
||||
}
|
||||
else {
|
||||
c3_i i;
|
||||
|
||||
for ( i=0; i < siz_i; i++ ) {
|
||||
_term_io_suck_char(uty_u, buf_u.base[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if ( buf_u.base ) {
|
||||
free(buf_u.base);
|
||||
}
|
||||
}
|
||||
u2_lo_shut(u2_yes);
|
||||
_term_suck(uty_u, (const c3_y*)buf_u.base, siz_i);
|
||||
free(buf_u.base);
|
||||
}
|
||||
|
||||
/* _term_main(): return main or console terminal.
|
||||
@ -650,7 +900,7 @@ _term_main()
|
||||
u2_utty* uty_u;
|
||||
|
||||
for ( uty_u = u2_Host.uty_u; uty_u; uty_u = uty_u->nex_u ) {
|
||||
if ( uty_u->fid_i <= 2 ) {
|
||||
if ( (uty_u->fid_i != -1) && (uty_u->fid_i <= 2) ) {
|
||||
return uty_u;
|
||||
}
|
||||
}
|
||||
@ -682,7 +932,6 @@ u2_term_get_blew(c3_l tid_l)
|
||||
u2_utty* uty_u = _term_ef_get(tid_l);
|
||||
c3_l col_l, row_l;
|
||||
|
||||
#if 1
|
||||
struct winsize siz_u;
|
||||
if ( uty_u && (0 == ioctl(uty_u->fid_i, TIOCGWINSZ, &siz_u)) ) {
|
||||
col_l = siz_u.ws_col;
|
||||
@ -691,15 +940,7 @@ u2_term_get_blew(c3_l tid_l)
|
||||
col_l = 80;
|
||||
row_l = 24;
|
||||
}
|
||||
#else
|
||||
{
|
||||
c3_i col_i, row_i;
|
||||
|
||||
uv_tty_get_winsize(&uty_u->wax_u, &col_i, &row_i);
|
||||
col_l = col_i;
|
||||
row_l = row_i;
|
||||
}
|
||||
#endif
|
||||
if ( uty_u ) {
|
||||
uty_u->tat_u.siz.col_l = col_l;
|
||||
uty_u->tat_u.siz.row_l = row_l;
|
||||
@ -731,24 +972,13 @@ u2_term_ef_ctlc(void)
|
||||
/* u2_term_ef_boil(): initial effects for loaded servers.
|
||||
*/
|
||||
void
|
||||
u2_term_ef_boil(c3_l ono_l)
|
||||
u2_term_ef_boil(void)
|
||||
{
|
||||
if ( ono_l ) {
|
||||
u2_noun tid_l;
|
||||
|
||||
for ( tid_l = 2; tid_l <= ono_l; tid_l++ ) {
|
||||
u2_noun tin = u2_dc("scot", c3__ud, tid_l);
|
||||
u2_noun pax = u2nq(c3__gold, c3__term, tin, u2_nul);
|
||||
u2_noun hud = u2nc(c3__wipe, u2_nul);
|
||||
|
||||
u2_reck_plan(u2A, pax, hud);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
u2_noun pax = u2nq(c3__gold, c3__term, '1', u2_nul);
|
||||
|
||||
// u2_reck_plan(u2A, u2k(pax), u2nc(c3__init, u2k(u2h(u2A->own))));
|
||||
u2_reck_plan(u2A, u2k(pax), u2nc(c3__harm, u2_nul));
|
||||
u2_reck_plan(u2A, u2k(pax), u2nc(c3__blew, u2_term_get_blew(1)));
|
||||
u2_reck_plan(u2A, u2k(pax), u2nc(c3__hail, u2_nul));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user