Merge branch 'nextbreach' of https://github.com/urbit/urbit into nextbreach

This commit is contained in:
Anton Dyudin 2014-06-11 20:16:45 -07:00 committed by Anton Dyudin
commit 26e99cbb23
34 changed files with 3036 additions and 172 deletions

View File

@ -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

View File

@ -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
View 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 },
{ }
};

View File

@ -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 },
{ }
};

View File

@ -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
View 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
View 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 },
{ }
};

View File

@ -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 {

View File

@ -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);
}
}

View File

@ -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);
{

View File

@ -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);
{

View File

@ -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);
{

View File

@ -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

View File

@ -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')

View File

@ -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
View File

@ -0,0 +1 @@
build/

View 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.

View 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

View 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!

View 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 &lt;id> or IAC DO &lt;id>. The remote host
then responds with IAC DO &lt;id> or IAC WILL &lt;id>, respectively. Alternatively,
the request may be denied using IAC DONT &lt;id> or IAC WONT &lt;id>, respectively.
In order to switch to a specific channel, the IAC SB &lt;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

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

View 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

View 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

View 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

View 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.

View 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;
}

View 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;
}

View 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;
}

View 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;
}%%

View File

@ -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,

View File

@ -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.

View File

@ -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

View File

@ -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
View File

@ -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));