diff --git a/Makefile b/Makefile index 7e50a625f..420d6d80e 100644 --- a/Makefile +++ b/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 diff --git a/gen164/4/by.c b/gen164/4/by.c index 5ac8ab96a..878b07537 100644 --- a/gen164/4/by.c +++ b/gen164/4/by.c @@ -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 }, {} diff --git a/gen164/4/by_int.c b/gen164/4/by_int.c new file mode 100644 index 000000000..3080dbf70 --- /dev/null +++ b/gen164/4/by_int.c @@ -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 }, + { } + }; + + + diff --git a/gen164/4/by_uni.c b/gen164/4/by_uni.c index 4de9cb930..b2517506a 100644 --- a/gen164/4/by_uni.c +++ b/gen164/4/by_uni.c @@ -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 }, { } }; + diff --git a/gen164/4/in.c b/gen164/4/in.c index 9ef2fea78..80a85bc85 100644 --- a/gen164/4/in.c +++ b/gen164/4/in.c @@ -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 }, {} }; diff --git a/gen164/4/in_int.c b/gen164/4/in_int.c new file mode 100644 index 000000000..1156962d6 --- /dev/null +++ b/gen164/4/in_int.c @@ -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 }, + { } + }; + + diff --git a/gen164/4/in_uni.c b/gen164/4/in_uni.c new file mode 100644 index 000000000..8a6273bc8 --- /dev/null +++ b/gen164/4/in_uni.c @@ -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 }, + { } + }; + diff --git a/gen164/5/repg.c b/gen164/5/repg.c index 585117c79..dc7a595a9 100644 --- a/gen164/5/repg.c +++ b/gen164/5/repg.c @@ -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 { diff --git a/gen164/6/ap.c b/gen164/6/ap.c index 5113a2396..6643c52fe 100644 --- a/gen164/6/ap.c +++ b/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); } } diff --git a/gen164/6/ut_mint.c b/gen164/6/ut_mint.c index 5ae1a6d99..ebc6070eb 100644 --- a/gen164/6/ut_mint.c +++ b/gen164/6/ut_mint.c @@ -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); { diff --git a/gen164/6/ut_mull.c b/gen164/6/ut_mull.c index 0a9f73c5f..7571b7f52 100644 --- a/gen164/6/ut_mull.c +++ b/gen164/6/ut_mull.c @@ -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); { diff --git a/gen164/6/ut_play.c b/gen164/6/ut_play.c index bf88c34ab..45e7274e4 100644 --- a/gen164/6/ut_play.c +++ b/gen164/6/ut_play.c @@ -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); { diff --git a/gen164/pit.h b/gen164/pit.h index ab55fd54f..e5b32f915 100644 --- a/gen164/pit.h +++ b/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 diff --git a/include/c/motes.h b/include/c/motes.h index d8e340b6b..f94460125 100644 --- a/include/c/motes.h +++ b/include/c/motes.h @@ -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') diff --git a/include/v/vere.h b/include/v/vere.h index 2dd5ef790..1e74672fb 100644 --- a/include/v/vere.h +++ b/include/v/vere.h @@ -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 diff --git a/outside/anachronism/.gitignore b/outside/anachronism/.gitignore new file mode 100644 index 000000000..567609b12 --- /dev/null +++ b/outside/anachronism/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/outside/anachronism/LICENSE b/outside/anachronism/LICENSE new file mode 100644 index 000000000..72d4b706e --- /dev/null +++ b/outside/anachronism/LICENSE @@ -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. diff --git a/outside/anachronism/Makefile b/outside/anachronism/Makefile new file mode 100644 index 000000000..729a33dac --- /dev/null +++ b/outside/anachronism/Makefile @@ -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 diff --git a/outside/anachronism/README.md b/outside/anachronism/README.md new file mode 100644 index 000000000..894825e04 --- /dev/null +++ b/outside/anachronism/README.md @@ -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 + #include + + 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 + #include + + 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
+ 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! diff --git a/outside/anachronism/doc/channels.md b/outside/anachronism/doc/channels.md new file mode 100644 index 000000000..bfcbf838c --- /dev/null +++ b/outside/anachronism/doc/channels.md @@ -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: + + IAC DO NAWS + IAC WILL MCCP + IAC WILL NAWS + IAC SB NAWS \x50 \x00 \x50 \x00 IAC SE + IAC DO MCCP + IAC SB MCCP IAC SE + (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 diff --git a/outside/anachronism/doc/parser.png b/outside/anachronism/doc/parser.png new file mode 100644 index 000000000..b6a5e05a9 Binary files /dev/null and b/outside/anachronism/doc/parser.png differ diff --git a/outside/anachronism/include/anachronism/common.h b/outside/anachronism/include/anachronism/common.h new file mode 100644 index 000000000..9dcc157a8 --- /dev/null +++ b/outside/anachronism/include/anachronism/common.h @@ -0,0 +1,24 @@ +#ifndef ANACHRONISM_COMMON_H +#define ANACHRONISM_COMMON_H + +#include /* 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 diff --git a/outside/anachronism/include/anachronism/nvt.h b/outside/anachronism/include/anachronism/nvt.h new file mode 100644 index 000000000..3ab5d5a57 --- /dev/null +++ b/outside/anachronism/include/anachronism/nvt.h @@ -0,0 +1,214 @@ +#ifndef ANACHRONISM_ANACHRONISM_H +#define ANACHRONISM_ANACHRONISM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// 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 diff --git a/outside/anachronism/include/anachronism/parser.h b/outside/anachronism/include/anachronism/parser.h new file mode 100644 index 000000000..8309ee5ae --- /dev/null +++ b/outside/anachronism/include/anachronism/parser.h @@ -0,0 +1,73 @@ +#ifndef ANACHRONISM_PARSER_H +#define ANACHRONISM_PARSER_H + +#include + +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 diff --git a/outside/anachronism/src/README.md b/outside/anachronism/src/README.md new file mode 100644 index 000000000..8ee680823 --- /dev/null +++ b/outside/anachronism/src/README.md @@ -0,0 +1,6 @@ +* parser_common.rl +
The language-agnostic Ragel grammar for the Telnet protocol. +* parser.rl +
The C implementation of the Ragel grammar. Compiled to parser.c by Ragel. +* nvt.c +
The core implementation of Anachronism's NVT and Channel constructs. diff --git a/outside/anachronism/src/nvt.c b/outside/anachronism/src/nvt.c new file mode 100644 index 000000000..f33d93f75 --- /dev/null +++ b/outside/anachronism/src/nvt.c @@ -0,0 +1,631 @@ +#include +#include +#include +#include + + +#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,