first import of old cilux linux platform code
authoruser <moby@cilux.org>
Thu, 12 Mar 2009 15:23:17 +0000 (15:23 +0000)
committeruser <moby@cilux.org>
Thu, 12 Mar 2009 15:23:17 +0000 (15:23 +0000)
18 files changed:
bin/make.sh [new file with mode: 0755]
makefile [new file with mode: 0644]
modules/mid/tnl.frag [new file with mode: 0644]
modules/mid/tnl.vert [new file with mode: 0644]
src/drivers/mid/mid.c [new file with mode: 0644]
src/drivers/np/cache.c [new file with mode: 0644]
src/drivers/np/np.c [new file with mode: 0644]
src/drivers/np/uri2chan.c [new file with mode: 0644]
src/include/kernelapi.h [new file with mode: 0644]
src/include/ni.h [new file with mode: 0644]
src/ni/headers.c [new file with mode: 0644]
src/ni/ni.c [new file with mode: 0644]
src/platform/container.h [new file with mode: 0644]
src/platform/kernelapi.c [new file with mode: 0644]
src/platform/linux/cilux.c [new file with mode: 0644]
src/platform/linux/osapi.c [new file with mode: 0644]
src/platform/linux/osapi.h [new file with mode: 0644]
src/platform/linux/platform.h [new file with mode: 0644]

diff --git a/bin/make.sh b/bin/make.sh
new file mode 100755 (executable)
index 0000000..b00a095
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+pkill cilux
+make clean linux 2>&1 | tee out.log
+egrep -i warn out.log
+
diff --git a/makefile b/makefile
new file mode 100644 (file)
index 0000000..95ec4e2
--- /dev/null
+++ b/makefile
@@ -0,0 +1,84 @@
+
+# ---------------------------------------------------------------------------- #
+
+CILUX_VERSION=0.3.AA
+
+# ---------------------------------------------------------------------------- #
+
+all: linux
+
+linux: CC=/usr/bin/gcc
+linux: CCLIB=/usr/bin/gcc -shared -Wl,-soname
+linux: STRIP=strip
+linux: STRIP=ls -l
+linux: CCOPTIONS=-g -O -Wall -Wimplicit
+linux: COMPILEOPTIONS=-march=i386 -mtune=i586 -fPIC
+linux: LINKOPTIONS=-Wl,-export-dynamic -Wl,-rpath,.
+linux: LINKOPTIONS=-Wl,-export-dynamic
+linux: INCLUDES=-I/usr/local/include -I../include -I../../include -I.. -I../platform/linux -I../../platform/linux
+linux: LIBRARIES=-L/usr/local/lib -lnsl -ldl -lm -lX11 -lEGL -lGLESv2
+linux: linux-all
+
+linux-all: cilux mod-ni.so mod-np.so mod-mid.so install-lin
+
+# ---------------------------------------------------------------------------- #
+
+version-lin:
+       @echo '$@'
+       @rm -f src/platform/linux/version.h
+       @echo -n "static char* cilux_version=\"Cilux " >  src/platform/linux/version.h
+       @echo -n $(CILUX_VERSION)                      >> src/platform/linux/version.h
+       @echo -n " Linux (Built "                      >> src/platform/linux/version.h
+       @date | tr '\012' ')'                          >> src/platform/linux/version.h
+       @echo "\";"                                    >> src/platform/linux/version.h
+       @echo "static char* cilux_ciux=\"np\";"        >> src/platform/linux/version.h
+
+cilux: version-lin src/platform/linux/cilux.c src/platform/linux/osapi.c src/platform/kernelapi.c src/include/kernelapi.h src/platform/linux/platform.h
+       (cd src/platform/linux; $(CC) $(CCOPTIONS) -c cilux.c -o cilux.o $(COMPILEOPTIONS) $(INCLUDES))
+       (cd src/platform;       $(CC) $(CCOPTIONS) -c kernelapi.c -o kernelapi.o $(COMPILEOPTIONS) $(INCLUDES))
+       (cd src/platform/linux; $(CC) $(CCOPTIONS) -o ../../../cilux cilux.o ../kernelapi.o $(LINKOPTIONS) $(LIBRARIES))
+       $(STRIP) cilux
+       @echo '--------------------'
+
+mod-ni.so: src/ni/ni.c src/ni/headers.c src/include/ni.h src/include/kernelapi.h src/platform/linux/platform.h
+       (cd src/ni; $(CC) $(CCOPTIONS) -c headers.c -o headers.o $(COMPILEOPTIONS) $(INCLUDES))
+       (cd src/ni; $(CC) $(CCOPTIONS) -c ni.c     -o ni.o     $(COMPILEOPTIONS) $(INCLUDES))
+       $(CCLIB),mod-ni.so -o mod-ni.so src/ni/headers.o src/ni/ni.o
+       $(STRIP) mod-ni.so
+       @echo '--------------------'
+
+mod-np.so: src/drivers/np/np.c src/drivers/np/uri2chan.c src/include/kernelapi.h src/platform/linux/platform.h
+       (cd src/drivers/np; $(CC) $(CCOPTIONS) -c np.c      -o np.o   $(COMPILEOPTIONS) $(INCLUDES))
+       (cd src/drivers/np; $(CC) $(CCOPTIONS) -c uri2chan.c -o uri2chan.o $(COMPILEOPTIONS) $(INCLUDES))
+       (cd src/drivers/np; $(CC) $(CCOPTIONS) -c cache.c    -o cache.o $(COMPILEOPTIONS) $(INCLUDES))
+       $(CCLIB),mod-np.so -o mod-np.so src/drivers/np/np.o src/drivers/np/uri2chan.o src/drivers/np/cache.o
+       $(STRIP) mod-np.so
+       @echo '--------------------'
+
+mod-mid.so: src/drivers/mid/mid.c src/include/kernelapi.h src/platform/linux/platform.h
+       (cd src/drivers/mid; $(CC) $(CCOPTIONS) -c mid.c -o mid.o $(COMPILEOPTIONS) $(INCLUDES))
+       $(CCLIB),mod-mid.so -o mod-mid.so src/drivers/mid/mid.o
+       $(STRIP) mod-mid.so
+       @echo '--------------------'
+
+install-lin:
+       cp cilux /usr/local/bin
+       cp mod-ni.so mod-np.so /usr/local/lib
+       cp mod-mid.so modules/mid
+       @echo '--------------------'
+
+# ---------------------------------------------------------------------------- #
+
+clean:
+       find . -name '*.o'   | xargs rm -f
+       find . -name '*.log' | xargs rm -f
+       rm -f cilux *.so
+       rm -f ,*
+       rm -f modules/*/mod-*.*
+       rm -f src/platform/*/version.h
+       @echo '--------------------'
+
+# ---------------------------------------------------------------------------- #
+
+
+
diff --git a/modules/mid/tnl.frag b/modules/mid/tnl.frag
new file mode 100644 (file)
index 0000000..30cef92
--- /dev/null
@@ -0,0 +1,11 @@
+
+uniform sampler2D     texture;
+
+varying mediump float varyLight;
+varying mediump vec2  varyTexCoord;
+
+void main (void)
+{
+    gl_FragColor = varyLight * texture2D(texture, varyTexCoord);
+}
+
diff --git a/modules/mid/tnl.vert b/modules/mid/tnl.vert
new file mode 100644 (file)
index 0000000..7c5dbdd
--- /dev/null
@@ -0,0 +1,20 @@
+
+uniform mediump vec3  frameLightDirection;
+uniform mediump mat4  frameTRSV;
+uniform mediump mat4  frameMVP;
+uniform mediump mat3  frameTRSN;
+
+attribute highp   vec4 vertPos;
+attribute mediump vec3 vertNormal;
+attribute mediump vec4 vertTexCoord;
+
+varying mediump float varyLight;
+varying mediump vec2  varyTexCoord;
+
+void main(void)
+{
+    gl_Position         = vertPos    * frameTRSV * frameMVP;
+    varyLight = max( dot( vertNormal * frameTRSN, frameLightDirection ), 0.0 ) + 0.4;
+    varyTexCoord =        vertTexCoord.st;
+}
+
diff --git a/src/drivers/mid/mid.c b/src/drivers/mid/mid.c
new file mode 100644 (file)
index 0000000..c599675
--- /dev/null
@@ -0,0 +1,574 @@
+
+
+/* -------------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#include <ni.h>
+
+#include <math.h>
+
+/* ------------------------------------------------------------- */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "X11/Xutil.h"
+
+#include <EGL/egl.h>
+#include <GLES2/gl2.h>
+
+#define WINDOW_WIDTH  640
+#define WINDOW_HEIGHT 480
+
+#define POS_ARRAY      0
+#define NORMAL_ARRAY   1
+#define TEXCOORD_ARRAY 2
+
+#define TEX_SIZE 128
+
+/* ------------------------------------------------------------- */
+
+Window        x11Window   = 0;
+Display*      x11Display  = 0;
+long          x11Screen   = 0;
+XVisualInfo*  x11Visual   = 0;
+Colormap      x11Colormap = 0;
+
+EGLDisplay    eglDisplay = 0;
+EGLConfig     eglConfig  = 0;
+EGLSurface    eglSurface = 0;
+EGLContext    eglContext = 0;
+
+/* ------------------------------------------------------------- */
+
+/* -------------------------------------------------------------------------- */
+
+#define GLfloat float
+static GLfloat   xco=   0;
+static GLfloat   yco=   1;
+static GLfloat   zco= -35;
+static GLfloat   view_rotx=0.0, view_roty=0.0, view_rotz=0.0;
+static int       shift=0;
+
+/* -------------------------------------------------------------------------- */
+
+static int  handles_resource(char* name);
+static void sync_resource(ni_resource* res);
+static void init_gl(void);
+static void make_world(void);
+static void reshape(int width, int height);
+static void draw(void);
+static void key(unsigned char k, int down);
+static void plane(void* n);
+static void double_cube(void* s);
+static void cube(GLfloat size, int outside);
+static int  set_object(char* n,
+                       GLfloat x, GLfloat y, GLfloat z, GLfloat angle,
+                       void (*listFn)(void*),
+                       void*  listFnArgs        );
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT int mid_module_loaded(void)
+{
+       ni_register_driver("mid", handles_resource, sync_resource);
+
+       init_gl();
+       make_world();
+
+       k_gl_register_reshape(reshape);
+       k_gl_register_draw(draw);
+       k_gl_register_key(key);
+
+       k_log_out("MID Driver initialised");
+
+       testloop();
+
+       return 1;
+}
+
+EXPORT int mid_module_event(void* data)
+{
+       k_log_out("MID got event: %p", data);
+       ni_event* evt=data;
+       ni_event_delete(evt);
+       return 1;
+}
+
+/* -------------------------------------------------------------------------- */
+
+int handles_resource(char* name)
+{
+       return 0;
+}
+
+void sync_resource(ni_resource* res)
+{
+}
+
+/* -------------------------------------------------------------------------- */
+
+void init_gl(void)
+{
+}
+
+void make_world(void)
+{
+}
+
+void reshape(int width, int height)
+{
+}
+
+void draw(void)
+{
+}
+
+#define SHIFT 0
+void key(unsigned char k, int down)
+{
+       if(k==SHIFT &&  down){ shift=1; return; }
+       if(k==SHIFT && !down){ shift=0; return; }
+       if(!down) return;
+
+       if(shift) k-=('a'-'A');
+
+       float speed=0.25;
+       switch (k) {
+       case 'H':
+               xco-=speed*(float)sin((view_roty-90)*3.14/180);
+               zco+=speed*(float)cos((view_roty-90)*3.14/180);
+               if(xco< -35) xco= -35;
+               if(xco>  35) xco=  35;
+               if(zco< -35) zco= -35;
+               if(zco>  35) zco=  35;
+       break;
+       case 'L':
+               xco+=speed*(float)sin((view_roty-90)*3.14/180);
+               zco-=speed*(float)cos((view_roty-90)*3.14/180);
+               if(xco< -35) xco= -35;
+               if(xco>  35) xco=  35;
+               if(zco< -35) zco= -35;
+               if(zco>  35) zco=  35;
+       break;
+       case 'i':
+               xco-=speed*(float)sin(view_roty*3.14/180);
+               zco+=speed*(float)cos(view_roty*3.14/180);
+               if(xco< -35) xco= -35;
+               if(xco>  35) xco=  35;
+               if(zco< -35) zco= -35;
+               if(zco>  35) zco=  35;
+       break;
+       case 'o':
+               xco+=speed*(float)sin(view_roty*3.14/180);
+               zco-=speed*(float)cos(view_roty*3.14/180);
+               if(xco< -35) xco= -35;
+               if(xco>  35) xco=  35;
+               if(zco< -35) zco= -35;
+               if(zco>  35) zco=  35;
+       break;
+       case 'j':
+               yco-=speed;
+               if(yco< 0.2) yco= 0.2;
+       break;
+       case 'k':
+               yco+=speed;
+       break;
+       case 'l':
+               view_roty += speed*20;
+       break;
+       case 'h':
+               view_roty -= speed*20;
+       break;
+/*
+       case 'J':
+               view_rotx += 2.0;
+       break;
+       case 'K':
+               view_rotx -= 2.0;
+       break;
+       case 'z':
+               view_rotz += 2.0;
+       break;
+       case 'Z':
+               view_rotz -= 2.0;
+       break;
+*/
+       default:
+       return;
+       }
+       draw();
+}
+
+/* -------------------------------------------------------------------------- */
+/* ------------------------------------------------------------- */
+
+void cleanupAndExit(int code)
+{
+    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglTerminate(eglDisplay);
+
+    if(x11Window)   XDestroyWindow(x11Display, x11Window);
+    if(x11Colormap) XFreeColormap( x11Display, x11Colormap);
+    if(x11Display)  XCloseDisplay( x11Display);
+
+    exit(code);
+}
+
+/* ------------------------------------------------------------- */
+
+void getX11Display(int windowwidth, int windowheight)
+{
+    x11Display = XOpenDisplay(0);
+
+    if(!x11Display) {
+        printf("Error: Unable to open X display\n");
+        cleanupAndExit(-1);
+    }
+
+    x11Screen = XDefaultScreen(x11Display);
+    Window rootWindow = RootWindow(x11Display, x11Screen);
+    int depth = DefaultDepth(x11Display, x11Screen);
+    x11Visual = malloc(sizeof(XVisualInfo));
+    XMatchVisualInfo(x11Display, x11Screen, depth, TrueColor, x11Visual);
+
+    if(!x11Visual) {
+        printf("Error: Unable to acquire visual\n");
+        cleanupAndExit(-1);
+    }
+
+    x11Colormap = XCreateColormap(x11Display, rootWindow, x11Visual->visual, AllocNone);
+    XSetWindowAttributes XSWA;
+    XSWA.colormap = x11Colormap;
+    XSWA.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask;
+    unsigned int cwmask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
+
+    x11Window = XCreateWindow(x11Display, RootWindow(x11Display, x11Screen), 0, 0, windowwidth, windowheight,
+                              0, CopyFromParent, InputOutput, CopyFromParent, cwmask, &XSWA);
+    XMapWindow(x11Display, x11Window);
+    XFlush(x11Display);
+}
+
+int isEGLError(char* where)
+{
+    EGLint err = eglGetError();
+    if(err != EGL_SUCCESS) {
+        printf("EGL failed at %s (%d).\n", where, err);
+        return 1;
+    }
+    return 0;
+}
+
+void setUpEGL(void)
+{
+    eglDisplay = eglGetDisplay((EGLNativeDisplayType)x11Display);
+
+    EGLint iMajorVersion, iMinorVersion;
+    if(!eglInitialize(eglDisplay, &iMajorVersion, &iMinorVersion)) {
+        printf("Error: eglInitialize() failed.\n");
+        cleanupAndExit( -1);
+    }
+
+    EGLint pi32ConfigAttribs[5];
+    pi32ConfigAttribs[0] = EGL_SURFACE_TYPE;
+    pi32ConfigAttribs[1] = EGL_WINDOW_BIT;
+    pi32ConfigAttribs[2] = EGL_RENDERABLE_TYPE;
+    pi32ConfigAttribs[3] = EGL_OPENGL_ES2_BIT;    
+    pi32ConfigAttribs[4] = EGL_NONE;
+
+    EGLint pi32ContextAttribs[3];
+    pi32ContextAttribs[0] = EGL_CONTEXT_CLIENT_VERSION;
+    pi32ContextAttribs[1] = 2;
+    pi32ContextAttribs[2] = EGL_NONE;
+
+    int iConfigs;
+    if(!eglChooseConfig(eglDisplay, pi32ConfigAttribs, &eglConfig, 1, &iConfigs) || (iConfigs != 1)) {
+        printf("Error: eglChooseConfig() failed.\n");
+        cleanupAndExit( -1);
+    }
+
+    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)x11Window, NULL);
+    
+    if(isEGLError("eglCreateWindowSurface")) cleanupAndExit( -1);
+
+    eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, pi32ContextAttribs);
+
+    if(isEGLError("eglCreateContext")) cleanupAndExit( -1);
+
+    eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
+
+    if(isEGLError("eglMakeCurrent")) cleanupAndExit( -1);
+}
+
+/* ------------------------------------------------------------- */
+
+GLuint isShaderError(GLuint thing)
+{
+    GLint isShader = glIsShader(thing);
+
+    GLint ok;
+    if(isShader){
+        glGetShaderiv(thing, GL_COMPILE_STATUS, &ok);
+    }else{
+        glGetProgramiv(thing, GL_LINK_STATUS, &ok);
+    }
+    if(ok) return 0;
+
+    GLint infoLen=0;
+    if(isShader){
+        glGetShaderiv(thing, GL_INFO_LOG_LENGTH, &infoLen);
+    }else{
+        glGetProgramiv(thing, GL_INFO_LOG_LENGTH, &infoLen);
+    }
+    if(infoLen){
+         char* infoLog = malloc(sizeof(char)*infoLen);
+         if(isShader){
+              glGetShaderInfoLog(thing, infoLen, NULL, infoLog);
+         }else{
+              glGetProgramInfoLog(thing, infoLen, NULL, infoLog);
+         }
+         printf("%s: %s\n", isShader? "Shader compile": "Program link", infoLog);
+         free(infoLog);
+    }
+    return 1;
+}
+
+char* file2string(const char *path)
+{
+    FILE *fd;
+    long len, r;
+    char *str;
+    if(!(fd=fopen(path, "r"))) {
+        fprintf(stderr, "Can't open file '%s' for reading\n", path);
+        return NULL;
+    }
+    fseek(fd, 0, SEEK_END);
+    len = ftell(fd);
+    fseek(fd, 0, SEEK_SET);
+    if(!(str=malloc(len*sizeof(char)))) {
+        fprintf(stderr, "Can't malloc space for '%s'\n", path);
+        return NULL;
+    }
+    r = fread(str, sizeof(char), len, fd);
+    str[r-1] = '\0';
+    fclose(fd);
+    return str;
+}
+
+/* ------------------------------------------------------------- */
+
+GLuint       program;
+GLuint       texture;
+GLuint       vbo;
+unsigned int numberOfVertices;
+unsigned int posStep;
+unsigned int normStep;
+unsigned int tcStep;
+unsigned int stride;
+float        angle=0.0;
+float        viewAngle = 0.0;
+
+GLuint useTheProgram()
+{
+    const char *vsSource = file2string("tnl.vert");
+    const char *fsSource = file2string("tnl.frag");
+
+    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
+    glShaderSource(vs, 1, &vsSource, NULL);
+    glCompileShader(vs);
+    if(isShaderError(vs)) return 0;
+    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
+    glShaderSource(fs, 1, &fsSource, NULL);
+    glCompileShader(fs);
+    if(isShaderError(fs)) return 0;
+    free((void*)vsSource);
+    free((void*)fsSource);
+    program = glCreateProgram();
+    glAttachShader(program, vs);
+    glAttachShader(program, fs);
+
+    glBindAttribLocation(program, POS_ARRAY,      "vertPos");
+    glBindAttribLocation(program, NORMAL_ARRAY,   "vertNormal");  /* vertNormal and vertTexCoord don't need to be bound here.. ? */
+    glBindAttribLocation(program, TEXCOORD_ARRAY, "vertTexCoord");
+
+    glLinkProgram(program);
+    if(isShaderError(program)) return 0;
+
+    glUseProgram(program);
+
+    glUniform3f(glGetUniformLocation(program, "frameLightDirection"), 1.0, 0.0, 1.0);
+    glUniform1i(glGetUniformLocation(program, "texture"), 0); /* 0 ?? */
+
+    return 1;
+}
+
+void setUpTnL(){
+
+    glGenTextures(1, &texture);
+    glBindTexture(GL_TEXTURE_2D, texture);
+
+    GLuint* td = malloc(sizeof(GLuint)*TEX_SIZE*TEX_SIZE);
+    int i,j;
+    for(i=0; i<TEX_SIZE; i++)
+    for(j=0; j<TEX_SIZE; j++) {
+        GLuint col = (255L<<24) + ((255L-j*2)<<16) + ((255L-i)<<8) + (255L-i*2);
+        if ( ((i*j)/8) % 2 ) col = (GLuint) (255L<<24) + (255L<<16) + (0L<<8) + (255L);
+        td[j*TEX_SIZE+i] = col;
+    }
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEX_SIZE, TEX_SIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, td);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
+    free(td);
+
+    GLfloat trivertices[] = {
+                             -0.4f,-0.4f,0.4f,  // Pos
+                              0.0f,0.0f,1.0f,   // Normal
+                              0.0f,0.0f,        // TexCoord
+
+                              0.4f,-0.0f,0.0f,  // Pos
+                              0.0f,0.0f,1.0f,   // Normal
+                              1.0f,0.0f,        // TexCoord
+
+                              0.0f,0.4f,0.4f,   // Pos
+                              0.5f,0.5f,0.5f,   // Normal
+                              0.5f,1.0f,        // TexCoord
+
+                              0.4f,0.0f,0.4f,   // Pos
+                              0.5f,0.5f,0.5f,   // Normal
+                              0.5f,0.0f,        // TexCoord
+
+                              0.4f,0.4f,0.4f,   // Pos
+                              0.0f,0.0f,1.0f,   // Normal
+                              0.0f,0.0f,        // TexCoord
+
+    };
+
+    numberOfVertices = 4;
+    posStep  = 3 * sizeof(GLfloat);
+    normStep = 3 * sizeof(GLfloat);
+    tcStep   = 2 * sizeof(GLfloat);
+    stride = posStep + normStep + tcStep;
+
+    glGenBuffers(1, &vbo);
+    glBindBuffer(GL_ARRAY_BUFFER, vbo);
+    glBufferData(GL_ARRAY_BUFFER, numberOfVertices * stride, trivertices, GL_STATIC_DRAW);
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+int drawStuff(int width, int height)
+{
+    glViewport(0, 0, width, height);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    float TransRotScaleVerticeMatrix[] = {
+        cos(angle),  0, sin(angle), 0,
+        0,           1, 0,          0,
+        -sin(angle), 0, cos(angle), 0,
+        0,           0, 0,          1
+    };
+
+    float ModelViewProjectionMatrix[] = {
+        cos(viewAngle),  0, sin(viewAngle), 0,
+        0,               1, 0,              0,
+        -sin(viewAngle), 0, cos(viewAngle), 0,
+        0,               0, 0,              1
+    };
+
+    float TransRotScaleNormalMatrix[] = {
+        cos(angle),   0,   sin(angle),
+        0,            1,   0,
+        -sin(angle),  0,   cos(angle)
+    };
+
+    glUniformMatrix4fv(glGetUniformLocation(program, "frameTRSV"), 1, GL_FALSE, TransRotScaleVerticeMatrix);
+    glUniformMatrix4fv(glGetUniformLocation(program, "frameMVP"),  1, GL_FALSE, ModelViewProjectionMatrix);
+    glUniformMatrix3fv(glGetUniformLocation(program, "frameTRSN"), 1, GL_FALSE, TransRotScaleNormalMatrix);
+
+
+    glBindBuffer(GL_ARRAY_BUFFER, vbo);
+
+    glEnableVertexAttribArray(POS_ARRAY);
+    glVertexAttribPointer(POS_ARRAY, 3, GL_FLOAT, GL_FALSE, stride, 0);
+
+    glEnableVertexAttribArray(NORMAL_ARRAY);
+    glVertexAttribPointer(NORMAL_ARRAY, 3, GL_FLOAT, GL_FALSE, stride, (void*)posStep  );
+
+    glEnableVertexAttribArray(TEXCOORD_ARRAY);
+    glVertexAttribPointer(TEXCOORD_ARRAY, 2, GL_FLOAT, GL_FALSE, stride, (void*)(posStep + normStep) );
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, numberOfVertices);
+
+    glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+    eglSwapBuffers(eglDisplay, eglSurface);
+
+    angle += .001f;
+
+    return !isEGLError("drawStuff");
+}
+
+void deleteStuff(){
+    glDeleteTextures(1, &texture);
+    glDeleteBuffers(1, &vbo);
+    glDeleteProgram(program);
+/*  glDeleteShader();
+    glDeleteShader();*/
+}
+/* ------------------------------------------------------------- */
+
+void testloop()
+{
+    printf("Test OpenGL ES 2.0\n---------------------------\n");
+
+    getX11Display(WINDOW_WIDTH, WINDOW_HEIGHT);
+
+    setUpEGL();
+    glClearColor(0.6f, 0.8f, 1.0f, 1.0f);
+
+    if(!useTheProgram()) cleanupAndExit(-1);
+    setUpTnL();
+
+    int done = 0;
+    while(!done){
+
+        if(!drawStuff(WINDOW_WIDTH, WINDOW_HEIGHT)) cleanupAndExit(-1);
+
+        int nm = XPending(x11Display);
+        int m;
+        for(m=0; m< nm; m++) {
+
+            XEvent event;
+            XNextEvent(x11Display, &event);
+            switch(event.type){
+                case ButtonPress:
+                    done = 1;
+                break;
+                case KeyPress:
+                    if(event.xkey.keycode == 113) viewAngle += 0.1;
+                    if(event.xkey.keycode == 114) viewAngle -= 0.1;
+                break;
+                default:
+                break;
+            }
+        }
+    }
+    cleanupAndExit(0);
+}
+
+/* ------------------------------------------------------------- */
+
+
+
diff --git a/src/drivers/np/cache.c b/src/drivers/np/cache.c
new file mode 100644 (file)
index 0000000..81133d0
--- /dev/null
@@ -0,0 +1,182 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#include <ni.h>
+
+/* -}{----------------------------------------------------------------------- */
+
+extern void   ensure_self_sub(ni_event* evq);
+
+/* -}{----------------------------------------------------------------------- */
+
+#define TMPBUFSIZE  4096
+static char   tmpbuf[TMPBUFSIZE];
+
+/* -}{----------------------------------------------------------------------- */
+
+static void cache_read(char*, char*, char*, int, k_stat, void*);
+static void entity_written(char*, char*, char*, int, k_stat, void*);
+static void write_headers(char* path, ni_resource* res);
+static void headers_written(char*, char*, char*, int, k_stat, void*);
+
+
+/* -}{----------------------------------------------------------------------- */
+
+char* make_cache_path(char* uri)
+{
+       char* path;
+       size_t l=strlen(uri);
+       if(l>100){ k_log_out("URI too long to save: %s", uri); return 0; }
+       if(*(uri+l-1)!='/'){
+               path=k_strdup(uri);
+       }
+       else{
+               path=k_malloc(l+1+10);
+               sprintf(path, "%sindex.html", uri);
+       }
+       return path;
+}
+
+void look_in_file_cache(ni_event* evq)
+{
+       k_hashtable* sub=evq->ent_head;
+       char* uri=k_hashtable_get(sub, "Sub-To:");
+       size_t l=strlen(uri);
+
+       if(l>100){ ensure_self_sub(evq); return; }
+
+       char hdr[128];
+       if(*(uri+l-1)!='/') sprintf(hdr, "%s.hdr", uri);
+       else                sprintf(hdr, "%sindex.html.hdr", uri);
+
+       if(strstr(hdr, "..")){ ensure_self_sub(evq); return; }
+       if(strchr(hdr, '?' )){ ensure_self_sub(evq); return; }
+
+       char* c=strchr(hdr, ':');
+       char* e=strchr(hdr, '/');
+       if(c && c< e) *c='-';
+
+       k_file_read(".", k_strdup(hdr), USE_MALL, 0, cache_read, evq);
+}
+
+void save_in_file_cache(ni_resource* res)
+{
+       char*        uri     =res->uri;
+       char*        path    =make_cache_path(uri); if(!path) return;
+       k_hashtable* ent_head=res->ent_head;
+       void*        data    =res->entity;
+       int          partial =k_hashtable_is(     ent_head, "Status:", "206");
+       size_t       size    =k_hashtable_get_int(ent_head, "Content-Length:");
+       char*        conrange=k_hashtable_get(    ent_head, "Content-Range:");
+       int          constant=k_hashtable_is(     ent_head, "CUX:", "C");
+
+       if(!data){
+               write_headers(path, res);
+       }
+       else
+       if(constant){
+               int ok=k_file_sync(data, size);
+               if(!ok){
+                       k_log_err("Failed to write (sync mmap): %s", uri);
+                       k_free(path);
+                       return;
+               }
+               write_headers(path, res);
+       }
+       else{
+               if(partial){
+                       k_log_out("save_in_file_cache partial %s", conrange);
+                       if(!conrange || strncmp(conrange, "0-", 2)){
+                               k_log_err("not saving partial");
+                               k_free(path);
+                               return;
+                       }
+                       size=atoi(conrange+2);
+               }
+               k_log_out("save_in_file_cache updateable: %d bytes", size);
+               k_file_write(".", path, data, size, entity_written, res);
+       }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+void entity_written(char*  basedir,
+                    char*  path,
+                    char*  data,
+                    int    usedmmap,
+                    k_stat kstat,
+                    void*  context)
+{
+       if(data) k_log_out("File cache entity written: %s", path);
+       else{
+               k_log_err("Failed to write entity:    %s", path); 
+               k_free(path);
+               return;
+       }
+       ni_resource* res=context;
+       write_headers(path, res);
+}
+
+void write_headers(char* path, ni_resource* res)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s.hdr", path);
+       k_free(path); path=k_strdup(tmpbuf);
+       k_hashtable_snprintf(res->ent_head, tmpbuf, TMPBUFSIZE);
+       k_file_write(".", path, tmpbuf, strlen(tmpbuf), headers_written, 0);
+}
+
+void headers_written(char*  basedir,
+                     char*  path,
+                     char*  data,
+                     int    usedmmap,
+                     k_stat kstat,
+                     void*  context)
+{
+       if(data) k_log_out("File cache headers written: %s", path);
+       else     k_log_err("Failed to write headers:    %s", path);
+       k_free(path);
+}
+
+void cache_read(char*  basedir,
+                char*  path,
+                char*  data,
+                int    usedmmap,
+                k_stat kstat,
+                void*  context)
+{
+       if(!data) k_log_out("no cache of %s", path);
+       else      k_log_out("file cache read: %d  %s", kstat.size, path);
+       char* e=strstr(path, ".hdr");
+       if(e){
+               ni_event* evq=context;
+               if(!data){ k_free(path); ensure_self_sub(evq); return; }
+
+               char* header=data;
+               *(header+kstat.size-2)=0;
+               k_hashtable* ent_head=k_hashtable_new("nHeaders/cache_read", 1);
+               k_hashtable_put(ent_head, "", header);
+               if(!ni_get_headers(header, 0, ent_head)){
+                       k_hashtable_delete(ent_head);
+                       k_free(path);
+                       ensure_self_sub(evq);
+                       return;
+               }
+               ni_event_delete(evq);
+               *e=0;
+               ni_event* evt=ni_event_new(0, 0, ent_head, 0);
+               int constant=k_hashtable_is(ent_head, "CUX:", "C");
+               size_t m=constant? USE_MMAP: USE_MALL;
+               k_file_read(".", path, m, 0, cache_read, evt);
+               return;
+       }
+       ni_event* evt=context;
+       k_free(path);
+       evt->entity=data;
+       ni_event_show(evt, "File Cache Found:");
+       k_event_post("ni", evt);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+
diff --git a/src/drivers/np/np.c b/src/drivers/np/np.c
new file mode 100644 (file)
index 0000000..e72616b
--- /dev/null
@@ -0,0 +1,562 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#include <ni.h>
+
+/* -}{----------------------------------------------------------------------- */
+
+#define TMPBUFSIZE  4096
+static char         tmpbuf[TMPBUFSIZE];
+static k_hashtable* own_resources;
+
+/* -}{----------------------------------------------------------------------- */
+
+extern void  run_tests(void);
+
+extern char*   make_cache_path(char* uri);
+extern void    look_in_file_cache(ni_event* evq);
+extern void    save_in_file_cache(ni_resource* res);
+
+extern void    init_uri2chan(void);
+extern char*   get_host_for(char* uri);
+extern char*   get_channel_for(char* host);
+extern char*   use_ping_info(k_hashtable*, k_channel*);
+extern void    use_from_info(k_hashtable*, k_channel*);
+extern void    ping_tunnels(void);
+extern void    send_ping(k_channel* chan, char* firstline, char* to);
+
+/* -}{----------------------------------------------------------------------- */
+
+static int           handles_resource(char* name);
+static void          sync_resource(ni_resource* res);
+       int           connection_writable(k_channel* chan, int bufpos, int len);
+       int           connection_readable(k_channel* chan, int bufpos, int len);
+static int           recv_next_event(    k_channel* chan);
+static void          recv_request(       k_channel* chan, char* header);
+static void          recv_response(      k_channel* chan, char* header);
+static void          got_mmap(char*, char*, char*, int, k_stat, void*);
+static void          set_read_buffer(k_channel*, char*, size_t, ni_event*);
+static int           recv_entity(        k_channel* chan, int bufpos, int eof);
+static int           expecting_response(char* pub, ni_event* evt, k_channel*);
+static void          do_request(   ni_event* evq);
+       void          ensure_self_sub(ni_event* evq);
+static void          ping_resource_subs(void* arg, char* key, void* val);
+static void          ping_sub(ni_resource* res, k_hashtable* sub);
+static ni_resource* own_resource(char* uri);
+static void          send_request(ni_event* evq);
+static void          send_response(ni_event* evt);
+static k_channel*    ensure_chan(char* chanm);
+
+/* -}{----------------------------------------------------------------------- */
+
+EXPORT int np_module_loaded(void)
+{
+       ni_register_driver("np", handles_resource, sync_resource);
+
+       if(strstr(k_version, "test")){
+               //run_tests();
+       }
+
+       init_uri2chan();
+
+       own_resources=k_hashtable_new("Own Resources", 0);
+
+       k_log_out("NP Driver initialised");
+
+       return 0;
+}
+
+EXPORT void np_module_tick(void)
+{
+       static long tix;
+       tix++;
+       if(!(tix % 1000)){
+               k_hashtable_apply(own_resources, ping_resource_subs, 0);
+       }
+       if(!(tix % 3000)){
+               ping_tunnels();
+       }
+}
+
+EXPORT int np_module_event(void* data)
+{
+       ni_event* evt=data;
+       if(!k_hashtable_get(evt->ent_head, "Status:")){
+               do_request(evt);
+       }
+       else{
+               send_response(evt);
+       }
+       return 0;
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+int handles_resource(char* name)
+{
+       return 0;
+}
+
+void sync_resource(ni_resource* res)
+{
+       save_in_file_cache(res);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+int connection_readable(k_channel* chan, int bufpos, int len)
+{
+       if(0) k_log_out("connection_readable %s %p %d %d %p",
+                                chan->name, chan, bufpos, len, chan->context);
+       int sof=(len==  0);
+       int eof=(len== -1);
+
+       if(sof) return 0;
+
+       do{
+               ni_event* evt=chan->context;
+               if(!evt){
+                       int n=recv_next_event(chan);
+                       if(n<0) break;
+                       bufpos-=n;
+               }
+               else{
+                       int n=recv_entity(chan, bufpos, eof);
+                       if(n<0) break;
+                       bufpos-=n;
+               }
+
+       } while(1);
+
+       if(eof && chan->context){
+               ni_event* evt=chan->context;
+               evt->entity=0;
+               ni_event_delete(evt);
+               chan->context=0;
+       }
+
+       return 0;
+}
+
+int connection_writable(k_channel* chan, int bufpos, int len)
+{
+       if(0) k_log_out("connection_writable %p %d %d %p",
+                                      chan, bufpos, len, chan->context);
+//if(len>20000) exit(1);
+       int sof=(len==  0);
+       int eof=(len== -1);
+
+       if(sof){
+               send_ping(chan, "PING ni/0.5" CRLF, 0);
+               return 0;
+       }
+
+       if(eof && chan->context){
+               ni_event* evt=chan->context;
+               evt->entity=0;
+               ni_event_delete(evt);
+               chan->context=0;
+       }
+
+       return 0;
+}
+
+/* -}{---- Receiving -------------------------------------------------------- */
+
+int recv_next_event(k_channel* chan)
+{
+       char* header=k_channel_chop_div(chan, CRLF CRLF);
+       if(!header) return -1;
+       int n=strlen(header)+strlen(CRLF CRLF);
+
+       if(!strncmp(header, "GET",  3) ||
+          !strncmp(header, "SUB",  3) ||
+          !strncmp(header, "UNSUB",5) ||
+          !strncmp(header, "HEAD", 4) ||
+          !strncmp(header, "PING", 4)   ){
+
+               recv_request(chan, header);
+               return n;
+       }
+       if(!strncmp(header, "ni/", 4)   ){
+
+               recv_response(chan, header);
+               return n;
+       }
+       k_free(header);
+       k_log_err("Failed reading request or response - closing connection");
+       k_channel_close(chan);
+       return n;
+}
+
+void recv_request(k_channel* chan, char* header)
+{
+       ni_event* evq;
+       evq=ni_get_request_headers(header);
+       if(!evq){
+               k_log_err("Failed reading request headers - closing connection");
+               k_channel_close(chan);
+               return;
+       }
+       if(!k_hashtable_isn(evq->evt_head, "Protocol:", "ni/", 4)){
+               ni_event_delete(evq);
+               k_log_err("Failed reading request not ni - closing connection");
+               k_channel_close(chan);
+               return;
+       }
+
+       k_hashtable* ent_head=evq->ent_head;
+       int ping=k_hashtable_is(ent_head, "Method:", "PING");
+
+       if(!evq->uri && !ping){
+               evq->uri=k_strdup(chan->name);
+               k_hashtable_put_dup(ent_head, "URI:", chan->name);
+       }
+
+       ni_event_show(evq, "ni Protocol Request");
+
+       if(k_hashtable_isi(evq->evt_head, "Connection:", "Keep-Alive")){
+               chan->linger=1;
+               if(k_hashtable_isn(ent_head, "Sub-To:", "./test", 6)){
+                       chan->linger=0;
+               }
+       }
+       if(ping){
+               char* from=use_ping_info(ent_head, chan);
+               if(from) send_ping(chan, "ni/0.5 270 PING" CRLF, from);
+               ni_event_delete(evq);
+               return;
+       }
+       //use_from_info(ent_head, chan);
+
+       ni_event* evp=ni_event_new(evq->uri, 0, k_hashtable_dup(ent_head), 0);
+
+       ni_event_delete(evq);
+
+       k_event_post("ni", evp);
+}
+
+void recv_response(k_channel* chan, char* header)
+{
+       ni_event* evt=ni_get_response_headers(header);
+       if(!evt){
+               k_log_err("recv_response: headers failed but doing nothing!");
+               return;
+       }
+       char*        pub=     evt->uri;
+       k_hashtable* ent_head=evt->ent_head;
+
+       if(!expecting_response(pub, evt, chan)) return;
+
+       ni_event_show(evt, "Response");
+
+       int head=k_hashtable_is(     ent_head, "Status:", "260");
+       int nmod=k_hashtable_is(     ent_head, "Status:", "304");
+       int ping=k_hashtable_is(     ent_head, "Status:", "270");
+       int cl  =k_hashtable_get_int(ent_head, "Content-Length:");
+       int entity=!(head || nmod || ping || cl==0);
+
+       if(ping){
+               use_ping_info(ent_head, chan);
+               ni_event_delete(evt);
+               return;
+       }
+       use_from_info(ent_head, chan);
+
+       if(entity){
+               k_hashtable_set(ent_head, "Status:",      "260");
+               k_hashtable_set(ent_head, "Status-Text:", "Headers Only");
+       }
+       k_event_post("ni", evt);
+
+       if(entity){
+
+               k_hashtable* eh=k_hashtable_new("nHeaders/recv_response", 1);
+               char* from      =k_hashtable_get(ent_head, "From:");
+               char* contlen   =k_hashtable_get(ent_head, "Content-Length:");
+               char* cux       =k_hashtable_get(ent_head, "CUX:");
+               k_hashtable_set(eh, "Status:",        "206");
+               k_hashtable_set(eh, "Status-Text:",   "Partial Content");
+               k_hashtable_put_dup(eh, "From:",           from);
+               k_hashtable_put_dup(eh, "Content-Length:", contlen);
+               k_hashtable_put_dup(eh, "CUX:",            cux);
+               ni_event* evc=ni_event_new(pub, 0, eh, 0);
+               chan->context=evc;
+
+               int constant=k_hashtable_is(ent_head, "CUX:", "C");
+               if(constant){
+                       char* path=make_cache_path(pub); if(!path) return;
+                       k_file_read(".", path, USE_MMAP, cl, got_mmap, chan);
+               }
+               else{
+                       char* data=k_malloc(cl);
+                       set_read_buffer(chan, data, cl, evc);
+               }
+       }
+}
+
+void got_mmap(char*  basedir,
+              char*  path,
+              char*  data,
+              int    usedmmap,
+              k_stat kstat,
+              void*  context){
+
+       k_free(path);
+       k_channel* chan=context;
+       ni_event* evt=chan->context;
+       if(!evt){ k_log_err("got_mmap: evt=0"); return; }
+       if(!data || !usedmmap){ k_log_err("got_mmap: mmap failed"); return; }
+
+       size_t cl=k_hashtable_get_int(evt->ent_head, "Content-Length:");
+       set_read_buffer(chan, data, cl, evt);
+}
+
+void set_read_buffer(k_channel* chan, char* data, size_t cl, ni_event* evt)
+{
+       evt->entity=data;
+       int r=k_channel_setbuf(chan, data, cl);
+       if(0) k_log_out("k_channel_setbuf %d", r);
+       if(r==BUFFER_ALREADY_SET){
+               k_log_err("oops! k_channel_setbuf BUFFER_ALREADY_SET");
+               return;
+       }
+       if(r==BUFFER_FILLED){
+               k_hashtable_set(evt->ent_head, "Status:",      "200");
+               k_hashtable_set(evt->ent_head, "Status-Text:", "OK");
+               chan->context=0;
+               k_event_post("ni", evt);
+       }
+}
+
+int recv_entity(k_channel* chan, int bufpos, int eof)
+{
+       ni_event*   evt=chan->context;
+       k_hashtable* ent_head=evt->ent_head;
+
+       char* cls=k_hashtable_get(    ent_head, "Content-Length:");
+       int   cl =k_hashtable_get_int(ent_head, "Content-Length:");
+
+       if(!cls && !eof) return -1;
+
+       int partial=0;
+       int eofcontlen=eof && (!cls || bufpos < cl);
+       if(eofcontlen){
+               if(cls){
+                       char* clg=k_strdup(cls);
+                       k_hashtable_put(ent_head, "Content-Length-Given:", clg);
+                       partial=1;
+               }
+               cl=bufpos;
+               char b[32]; snprintf(b, 32, "%d", cl);
+               k_hashtable_put_dup(ent_head, "Content-Length:", b);
+       }
+
+       if(bufpos < cl){
+               if(bufpos){
+                       ni_event* evp=ni_event_dup(evt);
+                       snprintf(tmpbuf, TMPBUFSIZE, "0-%d", bufpos);
+                       char* cr=k_strdup(tmpbuf);
+                       k_hashtable_put(evp->ent_head, "Content-Range:", cr);
+                       k_event_post("ni", evp);
+               }
+               return -1;
+       }
+
+       static char dummy_empty_entity[0];
+       if(!k_channel_getbuf(chan)){
+               int cn=k_hashtable_is(ent_head, "CUX:", "C");
+               if(cl) evt->entity=k_channel_chop_len(chan, cl);
+               else   evt->entity=cn? dummy_empty_entity: k_malloc(1);
+       }
+
+       if(!partial){
+               k_hashtable_set(ent_head, "Status:",      "200");
+               k_hashtable_set(ent_head, "Status-Text:", "OK");
+       }
+       chan->context=0;
+       k_event_post("ni", evt);
+
+       return cl;
+}
+
+int expecting_response(char* pub, ni_event* evt, k_channel* chan)
+{
+       if(pub && 0){
+               k_log_err("unwanted response: %s", pub);
+               ni_event_delete(evt);
+               k_channel_close(chan);
+               return 0;
+       }
+       return 1;
+}
+
+/* -}{---- Sending ---------------------------------------------------------- */
+
+void do_request(ni_event* evq)
+{
+       k_hashtable* sub=evq->ent_head;
+       int tc=k_hashtable_isi(sub, "Sub-Type:", "Cache");
+       int to=k_hashtable_isi(sub, "Sub-Type:", "Original");
+
+       if(tc){
+               char* ims=k_hashtable_get(sub, "If-Modified-Since:");
+               if(ims) ensure_self_sub(evq);
+               else    look_in_file_cache(evq);
+       }
+       else
+       if(to){
+               send_request(evq);
+       }
+}
+
+void ensure_self_sub(ni_event* evq)
+{
+       k_hashtable* sub=evq->ent_head;
+       char*        pub=k_hashtable_get(sub, "Sub-To:");
+
+       ni_resource* res=own_resource(pub);
+       k_hashtable*  enh=res->ent_head;
+       k_hashtable*  selfsub=k_hashtable_get(enh, "Sub-To:");
+       if(selfsub && !k_hashtable_is(selfsub, "Status-Cache:", "OK")){
+               k_log_err("cancel selfsub as new one needed");
+       }
+
+       k_hashtable* ss=k_hashtable_dup(sub);
+       k_hashtable_remove( ss, "From:");
+       k_hashtable_put_dup(ss, "URI:",       pub);
+       k_hashtable_set(    ss, "Sub-Type:", "Original");
+       k_hashtable_put_dup(ss, "Via:",       get_host_for(pub));
+       if(k_hashtable_get( ss, "If-Modified-Since:")){
+               char* lm=k_hashtable_get(enh, "Last-Modified:");
+               if(!res->entity) lm=0;
+               k_hashtable_set(ss, "If-Modified-Since:", lm? lm: "0");
+       }
+       ni_event* evs=ni_event_new(0, 0, ss, 0);
+       k_event_post("ni", evs);
+
+       ni_event_delete(evq);
+}
+
+void ping_resource_subs(void* arg, char* key, void* val)
+{
+       ni_resource* res=val;
+       k_hashtable* pubcache=k_hashtable_get(res->ent_head, "Pub-Cache:");
+       if(!pubcache || !k_hashtable_get(pubcache, "Method:")) return;
+       k_hashtable* subs=k_hashtable_get(res->ent_head, "Sub-To:");
+       k_hashtable* sub;
+       for(sub=subs; sub; sub=sub->next){
+               if(!k_hashtable_is(sub, "Status-Cache:", "OK")){
+                       if(!k_hashtable_get(sub, "Status:")){
+                               ping_sub(res, sub);
+                       }
+                       else{
+                               int ts=k_hashtable_get_int(sub, "Timestamp:");
+                               if(0) k_log_out("check dried-up request: %d", ts);
+                       }
+               }
+       }
+}
+
+void ping_sub(ni_resource* res, k_hashtable* sub)
+{
+       ni_resource_show(res, "ping_resource_subs");
+
+       k_hashtable* ss=k_hashtable_dup(sub);
+
+       char* subto=k_hashtable_extract(ss, "URI:");
+       k_hashtable_put_dup(ss, "URI:",    res->uri);
+       k_hashtable_put(    ss, "Sub-To:", subto);
+       k_hashtable_set(    ss, "Sub-Type:", "Original");
+       k_hashtable_put_dup(ss, "Via:",    get_host_for(res->uri));
+
+       ni_event* evs=ni_event_new(0, 0, ss, 0);
+       k_event_post("ni", evs);
+}
+
+ni_resource* own_resource(char* uri)
+{
+       ni_resource* res=k_hashtable_get(own_resources, uri);
+       if(!res){
+               res=ni_resource_get(uri);
+               k_hashtable_set(own_resources, uri, res);
+       }
+       return res;
+}
+
+void send_request(ni_event* evt)
+{
+       k_hashtable* eh=evt->ent_head;
+       char* method=k_strdup(k_hashtable_get(eh, "Method:"));
+       char* to    =k_strdup(k_hashtable_get(eh, "Sub-To:"));
+       char* via   =k_strdup(k_hashtable_get(eh, "Via:"));
+
+       char* chanm=get_channel_for(via);
+       if(!chanm) goto free_and_return;
+
+       k_channel* chan=ensure_chan(chanm);
+       if(!chan) goto free_and_return;
+
+       ni_fix_ni_headers(eh, 0);
+       ni_request(evt, to, method, chan);
+
+       free_and_return:
+       k_free(method); k_free(to); k_free(via);
+       ni_event_delete(evt);
+}
+
+void send_response(ni_event* evt)
+{
+       ni_event_show(evt, "send_response");
+
+       k_hashtable* eh=evt->ent_head;
+
+       k_hashtable* sub=k_hashtable_get(eh,  "Pub-To:");
+       char* uri       =k_hashtable_get(sub, "URI:");
+       char* from      =k_hashtable_get(sub, "From:");
+       char* method    =k_hashtable_get(sub, "Method:");
+       int   methead   =k_hashtable_is( sub, "Method:", "HEAD");
+
+       char* to=from? from: uri;
+
+       char* host=from? from: get_host_for(uri);
+       char* chanm=get_channel_for(host);
+       if(!chanm){
+               if(0) k_log_out("no ni protocol channel %s", to);
+               ni_event_delete(evt);
+               return;
+       }
+
+       k_channel* chan=ensure_chan(chanm);
+       if(!chan){
+               if(0) k_log_out("no ni protocol channel %s", to);
+               ni_event_delete(evt);
+               return;
+       }
+
+       k_hashtable_extract(eh, "Pub-To:");
+
+       char* protocol="ni/0.5";
+
+       ni_fix_ni_headers(eh, methead);
+       ni_response(evt, to, method, protocol, 0, chan);
+
+       k_hashtable_delete(sub);
+       evt->entity=0;
+       ni_event_delete(evt);
+}
+
+k_channel* ensure_chan(char* chanm)
+{
+       k_channel* chan=k_channel_get_name(chanm);
+       if(!chan){
+               k_log_err("Cannot find current channel for %s", chanm);
+               k_channel_connect_name(chanm, connection_readable,
+                                             connection_writable);
+       }
+       return chan;
+}
+
+/* -}{----------------------------------------------------------------------- */
+
diff --git a/src/drivers/np/uri2chan.c b/src/drivers/np/uri2chan.c
new file mode 100644 (file)
index 0000000..b4de726
--- /dev/null
@@ -0,0 +1,294 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#include <ni.h>
+
+/* -}{----------------------------------------------------------------------- */
+
+extern int connection_writable(k_channel* chan, int bufpos, int len);
+extern int connection_readable(k_channel* chan, int bufpos, int len);
+
+/* -}{----------------------------------------------------------------------- */
+
+#define TMPBUFSIZE  4096
+static char         tmpbuf[TMPBUFSIZE];
+static int          is_np;
+static char*        nexus_channel;
+static k_hashtable* chans_for_host;
+
+/* -}{----------------------------------------------------------------------- */
+
+static void   nexus_file_read(char*, char*, char*, int, k_stat, void*);
+static void   write_nexus(char* hostname);
+static void   nexus_file_written(char*, char*, char*, int, k_stat, void*);
+static int    root_nexus(void);
+static void   listen_http(void);
+static void   listen_nexus(void);
+static void   connect_to_nexus(void);
+static void   ping_this(void* arg, char* key, void* val);
+static char*  generate_new_hostname(k_hashtable* ent_head, k_channel* chan);
+static void   set_channel_for(char* host, char* chanm);
+static void   show_np(void);
+static void   show_list(void* arg, char* key, void* val);
+
+/* -}{----------------------------------------------------------------------- */
+
+void init_uri2chan(void)
+{
+       chans_for_host =k_hashtable_new("Channels for Host", 0);
+
+       k_file_read(".", "nexus.txt", USE_MALL, 0, nexus_file_read, 0);
+
+       is_np=!strcmp(k_ciux, "np");
+       if(is_np)        listen_nexus();
+       if(root_nexus()){
+               k_log_out("root nexus - but listening on http");
+               listen_http();
+       }
+       else{
+               k_log_out("connecting to nexus %s", nexus_channel);
+               connect_to_nexus();
+       }
+}
+
+char* get_host_for(char* uri)
+{
+       if(0) k_log_out("get_host_for %s", uri);
+       char* host=0;
+       if(0) k_log_out("host for %s=%s", uri, host);
+       return host;
+}
+
+char* get_channel_for(char* host)
+{
+       if(0) k_log_out("get_channel_for %s", host);
+       char* chanm=k_hashtable_get(chans_for_host, host);
+       if(!chanm){
+       }
+       if(0) k_log_out("channel for %s=%s", host, chanm);
+       return chanm;
+}
+
+char* use_ping_info(k_hashtable* ent_head, k_channel* chan)
+{
+       char* from=k_hashtable_get(ent_head, "From:");
+       char* to  =k_hashtable_get(ent_head, "To:");
+
+       if(!from) return 0;
+       if(!strcmp(from, "-")) from=generate_new_hostname(ent_head, chan);
+       if(!from) return 0;
+       if(!ni_hostname() && to) write_nexus(to);
+
+       set_channel_for(from, chan->name);
+
+       if(0) k_log_out("%s PING %s", chan->name, from);
+       if(0) show_np();
+
+       return from;
+}
+
+void use_from_info(k_hashtable* ent_head, k_channel* chan)
+{
+       char* from=k_hashtable_get(ent_head, "From:");
+
+       if(!from) return;
+
+       set_channel_for(from, chan->name);
+
+       if(0) show_np();
+}
+
+void ping_tunnels(void)
+{
+       k_hashtable* channelspinged=k_hashtable_new("channelspinged", 0);
+       k_hashtable_apply(chans_for_host, ping_this, channelspinged);
+       k_hashtable_delete(channelspinged);
+}
+
+void send_ping(k_channel* chan, char* firstline, char* to)
+{
+       char* from=ni_hostname();
+       if(!from) from="-";
+
+       int ln=0;
+       int bufsize=TMPBUFSIZE;
+
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, firstline);
+       if(ln>=bufsize) return;
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, "From: %s" CRLF, from);
+       if(ln>=bufsize) return;
+       if(to){
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, "To: %s" CRLF, to);
+       if(ln>=bufsize) return;
+       }
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, "Server: %s" CRLF, k_version);
+       if(ln>=bufsize) return;
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, "Channels: %s" CRLF, "-");
+       if(ln>=bufsize) return;
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, CRLF);
+       if(ln>=bufsize) return;
+
+       char* head=k_strdup(tmpbuf);
+       k_channel_send(chan, head, ln, FREE_ON_SENT);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+void nexus_file_read(char*  basedir,
+                     char*  filepath,
+                     char*  data,
+                     int    usedmmap,
+                     k_stat kstat,
+                     void*  context)
+{
+       int L=0;
+       if(data) data[kstat.size-1]=0;
+       else     data=k_strdup("");
+       k_hashtable* nex=k_hashtable_new("nexus", 1);
+       if(!ni_get_headers(data, nex, 0)){
+               k_log_err("Corrupt nexus.txt file");
+               k_hashtable_delete(nex);
+               k_free(data);
+               data=k_strdup("");
+               nex=k_hashtable_new("nexus", 1);
+               ni_get_headers(data, nex, 0);
+       }
+
+       char* hostname;
+       nexus_channel=k_hashtable_get_dup(nex, "Nexus-Channel:");
+       hostname     =k_hashtable_get(    nex, "Hostname:");
+
+       if(hostname) ni_hostname_set(hostname);
+
+       if(L) if(nexus_channel) k_log_out("Nexus-Channel: %s", nexus_channel);
+       if(L) if(hostname)      k_log_out("Hostname: %s",      hostname);
+
+       k_hashtable_delete(nex);
+       k_free(data);
+}
+
+void write_nexus(char* hostname)
+{
+       ni_hostname_set(hostname);
+
+       snprintf(tmpbuf, TMPBUFSIZE, "Nexus-Channel: %s\r\n"
+                                    "Hostname:      %s\r\n", nexus_channel,
+                                                             hostname);
+
+       k_file_write(".", "nexus.txt", tmpbuf, strlen(tmpbuf),
+                                      nexus_file_written, 0);
+}
+
+void nexus_file_written(char*  basedir,
+                        char*  filepath,
+                        char*  data,
+                        int    usedmmap,
+                        k_stat kstat,
+                        void*  context)
+{
+       if(!data) k_fatal("Failed to write nexus.txt");
+       else      k_log_out("New nexus.txt written:\n%s", data);
+}
+
+int root_nexus(void)
+{
+       char* hostname=ni_hostname();
+       if(!hostname || strchr(hostname, '-')) return 0;
+       return 1;
+}
+
+void listen_http(void)
+{
+       char* chanm="|nip-server|-|8081|-|";
+       k_channel_connect_name(chanm, connection_readable,
+                                     connection_writable);
+}
+
+void listen_nexus(void)
+{
+       char* chanm="|nip-server|-|7747|-|";
+       k_channel_connect_name(chanm, connection_readable,
+                                     connection_writable);
+}
+
+void connect_to_nexus(void)
+{
+       if(nexus_channel){
+               k_channel_connect_name(nexus_channel, connection_readable,
+                                                     connection_writable);
+       }
+       else k_log_out("discovery of nexus not implemented yet!");
+}
+
+void ping_this(void* arg, char* key, void* val)
+{
+       k_hashtable* channelspinged=arg;
+       char*        chanm=val;
+
+       if(strncmp(chanm, "nip-client", 11)     ) return;
+       if(k_hashtable_get(channelspinged, chanm)) return;
+       k_hashtable_set(channelspinged, chanm, chanm);
+
+       k_channel* chan=k_channel_get_name(chanm);
+       //k_log_out("ping_this: host %s channel %s got=%p", key, chanm, chan);
+
+       if(!chan) k_channel_connect_name(chanm, connection_readable,
+                                               connection_writable);
+
+       int pingtodeath=0;
+       if(chan && pingtodeath) send_ping(chan, "PING ni/0.5" CRLF, 0);
+}
+
+char* generate_new_hostname(k_hashtable* ent_head, k_channel* chan)
+{
+       if(!ni_hostname()) return 0;
+       unsigned long a=chan->clientip.s_addr;
+       #define IPQUAD(a) \
+               ((unsigned char *)&a)[0], \
+               ((unsigned char *)&a)[1], \
+               ((unsigned char *)&a)[2], \
+               ((unsigned char *)&a)[3]
+       snprintf(tmpbuf, TMPBUFSIZE, "%s-%02x%02x%02x%02x.%d",
+                              ni_hostname(), IPQUAD(a), 7747);
+       if(0) k_log_out("generated new hostname: %s\n", tmpbuf);
+       return k_strdup(tmpbuf);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+void set_channel_for(char* host, char* chanm)
+{
+       k_hashtable_put_dup(chans_for_host, host, chanm);
+}
+
+void show_np(void)
+{
+       k_log_out("-------- channel for host ---------------");
+       k_hashtable_apply(chans_for_host,  show_list, 0);
+       k_channel_show_all();
+}
+
+void show_list(void* arg, char* key, void* val)
+{
+       char* chanm=val;
+       k_log_out("%s %s", key, chanm);
+}
+
+void generate_random(void)
+{
+       time_t t=time(0);
+       short r;
+       k_random_bytes((char*)&r, sizeof(r));
+       unsigned char r1=(t >>24) & 255;
+       unsigned char r2=(t >>16) & 255;
+       unsigned char r3=(t >> 8) & 255;
+       unsigned char r4=(t     ) & 255;
+       unsigned char r5=(r >> 8) & 255;
+       unsigned char r6=(r     ) & 255;
+       snprintf(tmpbuf, TMPBUFSIZE, "%02x-%02x-%02x-%02x-%02x-%02x",
+                                     r1, r2, r3, r4, r5, r6);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
diff --git a/src/include/kernelapi.h b/src/include/kernelapi.h
new file mode 100644 (file)
index 0000000..c659303
--- /dev/null
@@ -0,0 +1,218 @@
+#ifndef KERNEL_H
+#define KERNEL_H
+
+/* 
+   This API is the equivalent of a system call API and thereby
+   insulates its user from a GPL implementation.
+*/
+
+#include <platform.h>
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC CONSOLE k_console;
+PUBLIC char*   k_version;
+PUBLIC char*   k_ciux;
+PUBLIC void  (*k_terminate)(void);
+
+/* -------------------------------------------------------------------------- */
+
+#define NAME_OF_MODULE_LOADED_FN "_module_loaded"
+
+typedef int (*k_module_loaded_fn)(void);
+
+/* -------------------------------------------------------------------------- */
+
+#define NAME_OF_MODULE_TICK_FN "_module_tick"
+
+typedef void (*k_module_tick_fn)(void);
+
+/* -------------------------------------------------------------------------- */
+
+#define NAME_OF_MODULE_EVENT_FN  "_module_event"
+
+typedef int (*k_module_event_fn)(void* data);
+
+PUBLIC int k_event_post(char* module, void* data);
+
+/* -------------------------------------------------------------------------- */
+
+typedef void (*k_gl_reshape_event)(int width, int height);
+typedef void (*k_gl_draw_event)(void);
+typedef void (*k_gl_key_event)(unsigned char k, int down);
+PUBLIC  void   k_gl_register_reshape(k_gl_reshape_event);
+PUBLIC  void   k_gl_register_draw(   k_gl_draw_event);
+PUBLIC  void   k_gl_register_key(    k_gl_key_event);
+PUBLIC  void gluxSwapBuffers(void);
+
+/* -------------------------------------------------------------------------- */
+
+#define CRLF "\015\012"
+#define CHAN_LISTEN 1
+#define CHAN_ESTABL 2
+#define CHAN_ANY    3
+
+typedef struct k_channel      k_channel;
+typedef struct k_channel_priv k_channel_priv;
+
+typedef int (*k_channel_event)(k_channel* chan,
+                               int        bufpos, 
+                               int        size);
+
+struct k_channel{ struct k_channel* next;
+
+       char*           name;
+       char*           listenhost;
+       int             listenport;
+       char*           banner;
+       k_channel_event rdcallback;
+       k_channel_event wrcallback;
+       void*           context;
+       int             type;
+       struct in_addr  clientip;
+       int             linger;
+       k_channel_priv* priv;
+};
+
+PUBLIC char*      k_channel_name(char* type, char* ip, int port, char* other);
+PUBLIC k_channel* k_channel_connect_name(char*           chanm,
+                                         k_channel_event rdcallback,
+                                         k_channel_event wrcallback);
+PUBLIC k_channel* k_channel_get_name(char* chanm);
+PUBLIC void       k_channel_show_all(void);
+
+PUBLIC k_channel* k_channel_listen(char*           name,
+                                   int             listenport,
+                                   char*           banner,
+                                   k_channel_event rdcallback,
+                                   k_channel_event wrcallback,
+                                   void*           context);
+
+PUBLIC k_channel* k_channel_connect(char*           name,
+                                    char*           listenhost,
+                                    int             listenport,
+                                    k_channel_event rdcallback,
+                                    k_channel_event wrcallback,
+                                    void*           context);
+
+#define FREE_ON_SENT 1
+PUBLIC k_channel* k_channel_send(k_channel*  chan,
+                                 char*       base,
+                                 size_t      size,
+                                 int         free);
+
+#define BUFFER_ALREADY_SET -1
+#define BUFFER_FILLED       1
+#define BUFFER_SET          0
+PUBLIC char* k_channel_chop_div( k_channel* chan, char*  divider);
+PUBLIC char* k_channel_chop_len( k_channel* chan, size_t size);
+PUBLIC int   k_channel_setbuf(   k_channel* chan, char*  rdbuffer, size_t size);
+PUBLIC char* k_channel_getbuf(   k_channel* chan);
+
+PUBLIC void k_channel_close(k_channel* chan);
+
+/* -------------------------------------------------------------------------- */
+
+#define STAT_F 0100000
+#define STAT_D 0040000
+#define STAT_L 0120000
+#define MAX_FILESIZE (1024*1024*1024)
+#define USE_MMAP 0
+#define USE_MALL MAX_FILESIZE
+
+typedef struct k_stat{
+       int    type;
+       size_t size;
+       time_t time;
+       mode_t perm;
+} k_stat;
+
+typedef void (*k_file_event)(char*  basedir,
+                             char*  filename,
+                             char*  data,
+                             int    usedmmap,
+                             k_stat stat,
+                             void*  context);
+
+PUBLIC void k_file_stat( char* basedir, char* filename,
+                                          k_file_event callback, void* context);
+PUBLIC void k_file_read( char* basedir, char* filename, size_t msz, size_t size,
+                                          k_file_event callback, void* context);
+PUBLIC void k_file_write(char* basedir, char* filename, char* data, size_t size,
+                                          k_file_event callback, void* context);
+PUBLIC void k_dir_list(  char* basedir, char* dirname,
+                         char* pattern[], char* format[],  
+                                          k_file_event callback, void* context);
+PUBLIC int  k_file_sync(char* data, size_t size);
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct k_hashtable{ struct k_hashtable* next; } k_hashtable;
+
+typedef void (*k_hashtable_apply_fn)(void* arg, char* key, void* val);
+
+PUBLIC k_hashtable* k_hashtable_new(char* name, int ignorecase);
+PUBLIC k_hashtable* k_hashtable_dup( k_hashtable* tab);
+PUBLIC k_hashtable* k_hashtable_deep(k_hashtable* tab);
+PUBLIC k_hashtable* k_hashtable_dip( k_hashtable* tab, char* includes[]);
+PUBLIC void  k_hashtable_merge(  k_hashtable* tab, k_hashtable* tam);
+PUBLIC void  k_hashtable_set(    k_hashtable* tab, char* key, void* val);
+PUBLIC void  k_hashtable_put(    k_hashtable* tab, char* key, void* val);
+PUBLIC void  k_hashtable_put_dup(k_hashtable* tab, char* key, char* val);
+PUBLIC void  k_hashtable_put_int(k_hashtable* tab, char* key, int   val);
+PUBLIC void  k_hashtable_sub(    k_hashtable* tab, char* key, k_hashtable* sub);
+PUBLIC void* k_hashtable_get(    k_hashtable* tab, char* key);
+PUBLIC char* k_hashtable_get_dup(k_hashtable* tab, char* key);
+PUBLIC int   k_hashtable_get_int(k_hashtable* tab, char* key);
+PUBLIC int   k_hashtable_is(     k_hashtable* tab, char* key, char* val);
+PUBLIC int   k_hashtable_isi(    k_hashtable* tab, char* key, char* val);
+PUBLIC int   k_hashtable_isn(    k_hashtable* tab, char* key, char* val,size_t);
+PUBLIC void  k_hashtable_remove( k_hashtable* tab, char* key);
+PUBLIC void* k_hashtable_extract(k_hashtable* tab, char* key);
+PUBLIC void  k_hashtable_apply(  k_hashtable* tab, k_hashtable_apply_fn, void*);
+PUBLIC void  k_hashtable_show(      k_hashtable* tab);
+PUBLIC void  k_hashtable_show_chars(k_hashtable* tab);
+PUBLIC int   k_hashtable_snprintf(  k_hashtable* tab, char* buf, size_t size);
+PUBLIC int   k_hashtable_snprintf_i(k_hashtable* tab, char* buf, size_t size,
+                                                            char* includes[]);
+PUBLIC int   k_hashtable_snprintf_x(k_hashtable* tab, char* buf, size_t size,
+                                                            char* excludes[]);
+PUBLIC void  k_hashtable_delete(k_hashtable* tab);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC int  k_string_matches_pattern(char* string, char* pattern);
+PUBLIC int  k_string_ends_with(      char* string, char* postfix);
+PUBLIC void k_string_url_decode(     char* string);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC char*  k_time_to_rfc(time_t t);
+PUBLIC char*  k_time_to_rfc_relative(int plus);
+PUBLIC time_t k_time_from_rfc(char* s);
+PUBLIC void   k_time_get_now(char* buf, size_t size, char* format);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC void k_random_bytes(char* buf, size_t size);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC void* k_malloc(size_t size);
+PUBLIC void* k_realloc(void* o, size_t size);
+PUBLIC char* k_strdup(char* s);
+PUBLIC void* k_memdup(void* s, size_t size);
+PUBLIC void  k_free(void* o);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC void k_log_out(char* format, ...);
+PUBLIC void k_log_err(char* format, ...);
+PUBLIC void k_fatal(  char* format, ...);
+
+/* -------------------------------------------------------------------------- */
+
+
+#endif
+
+
diff --git a/src/include/ni.h b/src/include/ni.h
new file mode 100644 (file)
index 0000000..32c90b2
--- /dev/null
@@ -0,0 +1,77 @@
+
+#ifndef ni_H
+#define ni_H
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC char* ni_hostname(void);
+PUBLIC void  ni_hostname_set(char* name);
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct ni_resource{
+       char*        uri;
+       k_hashtable* ent_head;
+       void*        entity;
+} ni_resource;
+
+PUBLIC ni_resource* ni_resource_new(char*        uri,
+                                    k_hashtable* ent_head,
+                                    char*        entity);
+PUBLIC ni_resource* ni_resource_dup(   ni_resource* res);
+PUBLIC void         ni_resource_delete(ni_resource* res);
+PUBLIC void         ni_resource_show(  ni_resource* res, char* text);
+PUBLIC ni_resource* ni_resource_get(char* uri);
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct ni_event{
+       char*        uri;
+       k_hashtable* evt_head;
+       k_hashtable* ent_head;
+       void*        entity;
+} ni_event;
+
+PUBLIC ni_event* ni_event_new(char*        uri,
+                              k_hashtable* evt_head,
+                              k_hashtable* ent_head,
+                              char*        entity);
+PUBLIC ni_event* ni_event_dup(   ni_event* res);
+PUBLIC void      ni_event_delete(ni_event* res);
+PUBLIC void      ni_event_show(  ni_event* res, char* text);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC ni_event* ni_res_to_evt(ni_resource* res);
+
+/* -------------------------------------------------------------------------- */
+
+typedef int  (*ni_handles_resource)(char*);
+typedef void (*ni_sync_resource   )(ni_resource*);
+
+PUBLIC void ni_register_driver(char*               name,
+                               ni_handles_resource handles_resource,
+                               ni_sync_resource    sync_resource);
+
+/* -------------------------------------------------------------------------- */
+
+PUBLIC ni_event* ni_get_request_headers( char* header);
+PUBLIC ni_event* ni_get_response_headers(char* header);
+PUBLIC char*     ni_get_headers( char* header, k_hashtable*, k_hashtable*);
+PUBLIC void      ni_fix_http_headers(k_hashtable* ent_head);
+PUBLIC void      ni_fix_ni_headers(k_hashtable* ent_head, int methead);
+PUBLIC void      ni_response(ni_event* evt,
+                             char*      to,
+                             char*      method,
+                             char*      protocol,
+                             char*      connection,
+                             k_channel* chan);
+EXPORT void      ni_request(ni_event* evt,
+                            char*      to,
+                            char*      method,
+                            k_channel* chan);
+
+/* -------------------------------------------------------------------------- */
+
+#endif
+
diff --git a/src/ni/headers.c b/src/ni/headers.c
new file mode 100644 (file)
index 0000000..391fcc2
--- /dev/null
@@ -0,0 +1,523 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#undef  PUBLIC
+#define PUBLIC EXPORT
+#include <ni.h>
+
+/* -}{---- ------------------------------------------------------------------ */
+
+static k_hashtable* entity_headers;
+
+/* -}{---- ------------------------------------------------------------------ */
+
+#define TMPBUFSIZE  4096
+static char         tmpbuf[TMPBUFSIZE];
+
+/* -}{----------------------------------------------------------------------- */
+
+static char* get_request( char* header, k_hashtable*, k_hashtable*);
+static char* get_response(char* header, k_hashtable*, k_hashtable*);
+static char* get_val(char** atp);
+static char* end_of_val(char* at);
+static void  fix_keepalive(k_hashtable* evt_head);
+static void  fix_cache_control(k_hashtable* ent_head);
+static void  fix_uri(k_hashtable* ent_head);
+static void  fix_subscribe(k_hashtable*, k_hashtable*);
+
+/* -}{----------------------------------------------------------------------- */
+
+#define WHITESPACEH  " \t"
+#define WHITESPACEV  "\012\015"
+#define WHITESPACE   WHITESPACEH WHITESPACEV
+
+EXPORT ni_event* ni_get_request_headers(char* header)
+{
+       k_hashtable* evt_head=k_hashtable_new("vHeaders/get_req_hdrs", 1);
+       k_hashtable* ent_head=k_hashtable_new("nHeaders/get_req_hdrs", 1);
+       k_hashtable_put(ent_head, "", header);
+
+       char* h=get_request(header, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       h=ni_get_headers(h, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       fix_keepalive(evt_head);
+       fix_cache_control(ent_head);
+       fix_uri(ent_head);
+       fix_subscribe(evt_head, ent_head);
+
+       ni_event* evq=ni_event_new(0, evt_head, ent_head, 0);
+
+       return evq;
+}
+
+EXPORT ni_event* ni_get_response_headers(char* header)
+{
+       k_hashtable* evt_head=k_hashtable_new("vHeaders/get_resp_hdrs", 1);
+       k_hashtable* ent_head=k_hashtable_new("nHeaders/get_resp_hdrs", 1);
+       k_hashtable_put(ent_head, "", header);
+
+       char* h=get_response(header, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       h=ni_get_headers(h, evt_head, ent_head);
+       if(!h){
+               k_hashtable_delete(evt_head);
+               k_hashtable_delete(ent_head);
+               return 0;
+       }
+
+       ni_event* evt=ni_event_new(0, evt_head, ent_head, 0);
+
+       return evt;
+}
+
+EXPORT char* ni_get_headers(char*        header,
+                             k_hashtable* evt_head,
+                             k_hashtable* ent_head)
+{
+       char* at=header;
+       char* tag;
+       char* val;
+       while(*at){
+
+               tag=at;
+               at=strpbrk(at, WHITESPACE);
+               if(!at || at==tag) return 0;
+               char* e=at;
+               val=get_val(&at);
+               *e=0;
+
+               if(strlen(val) > 2048) return 0;
+
+               k_hashtable* h;
+               h=k_hashtable_get(entity_headers, tag)? ent_head: evt_head;
+               if(h){
+                       char* old=k_hashtable_get(h, tag);
+                       if(!old)  k_hashtable_set(h, tag, val);
+               }
+       }
+       return at;
+}
+
+EXPORT void ni_fix_http_headers(k_hashtable* ent_head)
+{
+       if(k_hashtable_is(ent_head, "Status:", "260")){
+               char* crn=k_hashtable_get(ent_head,  "Content-Range:");
+               char* clg=k_hashtable_get(ent_head,  "Content-Length-Given:");
+               if(crn || clg){
+                       k_hashtable_set(ent_head, "Status:", "206");
+                       k_hashtable_set(ent_head, "Status-Text:", "Partial Content");
+               }
+               else{
+                       k_hashtable_set(ent_head, "Status:", "200");
+                       k_hashtable_set(ent_head, "Status-Text:", "OK");
+               }
+       }
+       k_hashtable_remove(ent_head, "URI:");
+       k_hashtable_remove(ent_head, "Method:");
+       k_hashtable_remove(ent_head, "From:");
+       k_hashtable_remove(ent_head, "Content-Length-Given:");
+       k_hashtable_remove(ent_head, "Last-Modified-Epoch:");
+
+       char* cc=k_hashtable_get(ent_head, "Cache-Control:");
+       if(cc && strstr(cc, "no-cache")){
+               k_hashtable_set(ent_head, "Pragma:", "no-cache");
+       }
+}
+
+EXPORT void ni_fix_ni_headers(k_hashtable* ent_head, int methead)
+{
+       if(methead && (k_hashtable_is(ent_head, "Status:", "200") ||
+                      k_hashtable_is(ent_head, "Status:", "206")   )){
+
+               k_hashtable_set(ent_head, "Status:", "260");
+               k_hashtable_set(ent_head, "Status-Text:", "Headers Only");
+       }
+       k_hashtable_remove(ent_head, "Method:");
+       k_hashtable_remove(ent_head, "Sub-To:");
+       k_hashtable_remove(ent_head, "Via:");
+       k_hashtable_set(   ent_head, "From:", ni_hostname());
+
+       char* uri=k_hashtable_get(ent_head, "URI:");
+       if(*uri=='.'){
+               snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", ni_hostname(), uri+2);
+               k_hashtable_put_dup(ent_head, "URI:", tmpbuf);
+       }
+}
+
+EXPORT void ni_response(ni_event* evt,
+                         char*      to,
+                         char*      method,
+                         char*      protocol,
+                         char*      connection,
+                         k_channel* chan)
+{
+       int L=0;
+       snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc_relative(0));
+       k_hashtable_put_dup(evt->evt_head, "Date:",       tmpbuf);
+       k_hashtable_set(    evt->evt_head, "Server:",     k_version);
+       if(connection)
+       k_hashtable_put_dup(evt->evt_head, "Connection:", connection);
+
+       int   status    =k_hashtable_get_int(evt->ent_head, "Status:");
+       char* statustext=k_hashtable_get(    evt->ent_head, "Status-Text:");
+       int   datalength=k_hashtable_get_int(evt->ent_head, "Content-Length:");
+       int   constant  =k_hashtable_is(     evt->ent_head, "CUX:", "C");
+
+       char* buf    =tmpbuf;
+       int   bufsize=TMPBUFSIZE;
+       int   ln=0;
+
+       ln+=snprintf(buf+ln, bufsize-ln, "%s %d %s" CRLF, 
+                                         protocol, status, statustext);
+       if(ln>=bufsize) return;
+
+       static char* exheaders[]={ "Status:", "Status-Text:",
+                                  "Protocol:", "CUX:", 0 };
+
+       ln+=k_hashtable_snprintf_x(evt->evt_head, buf+ln,bufsize-ln, exheaders);
+       if(ln>=bufsize) return;
+
+       ln+=k_hashtable_snprintf_x(evt->ent_head, buf+ln,bufsize-ln, exheaders);
+       if(ln>=bufsize) return;
+
+       ln+=snprintf(buf+ln, bufsize-ln, CRLF);
+       if(ln>=bufsize) return;
+
+       char* head=k_strdup(buf);
+       if(L) k_log_out("Actual response headers:\n%s", head);
+       k_channel_send(chan, head, ln, FREE_ON_SENT);
+
+       if(evt->entity && datalength){
+               k_channel_send(chan, evt->entity, datalength, !constant);
+       }
+       else{
+               datalength=0;
+               if(!constant) k_free(evt->entity);
+       }
+       k_log_out("%s %s %s %d %d", to, method, evt->uri, status, datalength);
+}
+
+EXPORT void ni_request(ni_event* evt, char* to, char* method, k_channel* chan)
+{
+       k_hashtable* sub=evt->ent_head;
+
+       int ln=0;
+       int bufsize=TMPBUFSIZE;
+
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s //%s ni/0.5" CRLF, method, to);
+       if(ln>=bufsize) return;
+
+       ln+=k_hashtable_snprintf(sub, tmpbuf+ln, bufsize-ln);
+       if(ln>=bufsize) return;
+
+       ln+=snprintf(tmpbuf+ln, bufsize-ln, CRLF);
+       if(ln>=bufsize) return;
+
+       char* head=k_strdup(tmpbuf);
+       if(0) k_log_out("Actual request headers:\n%s", head);
+       k_channel_send(chan, head, ln, FREE_ON_SENT);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+char* get_request(char* header, k_hashtable* evt_head, k_hashtable* ent_head)
+{
+       char* at=header;
+       char* method=at;
+       at=strpbrk(at, WHITESPACEH);
+       if(!at) return 0;
+       *at++=0;
+
+       k_hashtable_set(ent_head, "Method:", method);
+
+       char* file=0;
+       char* host=0;
+       if(strcmp(method, "PING")){
+
+               at+=strspn(at, WHITESPACEH);
+               file=at;
+               at=strpbrk(at, WHITESPACEH);
+               if(!at) return 0;
+               *at++=0;
+               if(strlen(file) > 1024 ) return 0;
+
+               if(!strncmp(file, "http://", 7)){
+                       char* s=file+7;
+                       if(!*s || *s=='/') return 0;
+                       char* e=strchr(s, '/');
+                       if(!e) return 0;
+                       host=s;
+                       *e++=0;
+                       file=e;
+               }
+               else
+               if(*file=='/') file++;
+               else return 0;
+       
+               if(*file=='/'){
+                       char* s=file+1;
+                       if(!*s || *s=='/') return 0;
+                       char* e=strchr(s, '/');
+                       if(!e) return 0;
+                       host=s;
+                       *e++=0;
+                       file=e;
+               }
+       }
+       if(file) k_hashtable_set(evt_head, "File:", file);
+       if(host) k_hashtable_set(evt_head, "Host:", host);
+
+       at+=strspn(at, WHITESPACEH);
+       char* protocol=at;
+       at=strpbrk(at, WHITESPACE);
+       if(!at) at=protocol+strlen(protocol);
+       else   *at++=0;
+
+       k_hashtable_set(evt_head, "Protocol:", protocol);
+
+       at+=strspn(at, WHITESPACE);
+       return at;
+}
+
+char* get_response(char* header, k_hashtable* evt_head, k_hashtable* ent_head)
+{
+       char* at=header;
+       char* protocol=at;
+       at=strpbrk(at, WHITESPACEH);
+       if(!at) return 0;
+       *at++=0;
+
+       k_hashtable_set(evt_head, "Protocol:", protocol);
+
+       at+=strspn(at, WHITESPACEH);
+       char* status=at;
+       at=strpbrk(at, WHITESPACEH);
+       if(!at) return 0;
+       *at++=0;
+
+       at+=strspn(at, WHITESPACEH);
+       char* statustext=at;
+       at=strpbrk(at, WHITESPACEV);
+       if(!at) return 0;
+       *at++=0;
+
+       k_hashtable_set(ent_head, "Status:",      status);
+       k_hashtable_set(ent_head, "Status-Text:", statustext);
+
+       at+=strspn(at, WHITESPACE);
+       return at;
+}
+
+char* get_val(char** atp)
+{
+       char* at=*atp;
+       char* val=at;
+
+       val+=strspn(val, WHITESPACEH);
+       val+=strspn(val, WHITESPACEV);
+
+       char* eov=end_of_val(at);
+       if(eov<val) val=eov;
+       if(*eov) *eov++=0;
+
+       *atp=eov+strspn(eov, WHITESPACEV);
+       return val;
+}
+
+char* end_of_val(char* at)
+{
+       char* eov;
+       do{
+               char* v=strpbrk(at, WHITESPACEV);
+               if(!v) return at+strlen(at);
+               eov=v;
+               at=v+strspn(v, WHITESPACEV);
+
+       }while(strspn(at, WHITESPACEH));
+
+       return eov;
+}
+
+void fix_keepalive(k_hashtable* evt_head)
+{
+       int keepalive=0;
+       int is11=k_hashtable_is( evt_head, "Protocol:",   "HTTP/1.1");
+       int isps=k_hashtable_isn(evt_head, "Protocol:",   "ni/", 4);
+       int iska=k_hashtable_isi(evt_head, "Connection:", "Keep-Alive");
+       int iscl=k_hashtable_isi(evt_head, "Connection:", "close");
+       if((!is11 && iska) || (is11 && !iscl) || isps){
+               keepalive=1;
+       }
+       if(0) k_log_out("is11=%d iska=%d iscl=%d isps=%d ka=%d",
+                        is11,   iska,   iscl,   isps,   keepalive);
+       k_hashtable_set(evt_head, "Connection:", 
+                                 keepalive? "Keep-Alive": "close");
+}
+
+void fix_cache_control(k_hashtable* ent_head)
+{
+       char* cachec=k_hashtable_get(    ent_head, "Cache-Control:");
+       char* pragma=k_hashtable_extract(ent_head, "Pragma:");
+       if(!cachec && pragma && strstr(pragma, "no-cache")){
+               k_hashtable_set(         ent_head, "Cache-Control:","no-cache");
+       }
+}
+
+void fix_uri(k_hashtable* ent_head)
+{
+       char* uri=k_hashtable_get(ent_head, "URI:");
+       if(!uri) return;
+       char* nihostname=ni_hostname();
+       int l=strlen(nihostname);
+       if(strncmp(uri, nihostname, l)) return;
+       snprintf(tmpbuf, TMPBUFSIZE, ".%s", uri+l);
+       k_hashtable_put_dup(ent_head, "URI:", tmpbuf);
+}
+
+void fix_subscribe(k_hashtable* evt_head, k_hashtable* ent_head)
+{
+       char* host=k_hashtable_extract(evt_head, "Host:");
+       char* file=k_hashtable_extract(evt_head, "File:");
+       if(host){
+               k_string_url_decode(host);
+               int localhostdns=!strcmp( host, "localhost") ||
+                                !strncmp(host, "localhost:", 10);
+               int localhostni=!strcmp(host, ni_hostname());
+               char* lastdot=strrchr(host, '.');
+               int dotdotnumber=lastdot && atoi(lastdot+1) >0;
+               if(localhostdns || localhostni || dotdotnumber){
+                       host=".";
+               }
+       }
+       else{
+               host=".";
+       }
+       if(file){
+               k_string_url_decode(file);
+               snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", host, file);
+               k_hashtable_put_dup(ent_head, "Sub-To:", tmpbuf);
+               k_hashtable_set(    ent_head, "Sub-Type:", "Original");
+       }
+}
+
+void init_headers(void)
+{
+       entity_headers  =k_hashtable_new("Entity Headers", 1);
+
+       k_hashtable_set(entity_headers, "URI:", (void*)1);
+       k_hashtable_set(entity_headers, "From:", (void*)1);
+       k_hashtable_set(entity_headers, "To:", (void*)1);
+       k_hashtable_set(entity_headers, "Via:", (void*)1);
+       k_hashtable_set(entity_headers, "Sub-To:", (void*)1);
+       k_hashtable_set(entity_headers, "Sub-Type:", (void*)1);
+       k_hashtable_set(entity_headers, "Method:", (void*)1);
+       k_hashtable_set(entity_headers, "Status:", (void*)1);
+       k_hashtable_set(entity_headers, "Status-Text:", (void*)1);
+       k_hashtable_set(entity_headers, "Last-Modified-Epoch:", (void*)1);
+       k_hashtable_set(entity_headers, "CUX:", (void*)1);
+
+       k_hashtable_set(entity_headers, "Range:", (void*)1);
+       k_hashtable_set(entity_headers, "If-Modified-Since:", (void*)1);
+       k_hashtable_set(entity_headers, "If-None-Match:", (void*)1);
+       k_hashtable_set(entity_headers, "Cache-Control:", (void*)1);
+       k_hashtable_set(entity_headers, "Pragma:", (void*)1);
+
+       k_hashtable_set(entity_headers, "Content-Length:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Type:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Encoding:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Location:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-MD5:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Language:", (void*)1);
+       k_hashtable_set(entity_headers, "Content-Range:", (void*)1);
+       k_hashtable_set(entity_headers, "Last-Modified:", (void*)1);
+       k_hashtable_set(entity_headers, "ETag:", (void*)1);
+       k_hashtable_set(entity_headers, "Expires:", (void*)1);
+
+       k_hashtable_set(entity_headers, "Allow:", (void*)1);
+}
+
+void drop_entity_headers(k_hashtable* ent_head)
+{
+       k_hashtable_remove(ent_head, "Content-Length:");
+       k_hashtable_remove(ent_head, "Content-Range:");
+       k_hashtable_remove(ent_head, "Content-Type:");
+       k_hashtable_remove(ent_head, "Content-Encoding:");
+       k_hashtable_remove(ent_head, "Content-Location:");
+       k_hashtable_remove(ent_head, "Last-Modified:");
+#ifdef DO_THESE_ONES_TOO
+       k_hashtable_remove(ent_head, "Allow:");
+       k_hashtable_remove(ent_head, "Content-Language:");
+       k_hashtable_remove(ent_head, "Content-MD5:");
+       k_hashtable_remove(ent_head, "Expires:");
+#endif
+}
+
+void fill_headers(k_hashtable* ent_head,
+                  char*  status,
+                  char*  statustext,
+                  char*  uri,
+                  time_t modifitime,
+                  int    datalength,
+                  char*  mimetype,
+                  char*  encoding,
+                  int    nocache)
+{
+       if(status){
+               k_hashtable_put_dup(ent_head, "Status:",      status);
+               k_hashtable_put_dup(ent_head, "Status-Text:", statustext);
+       }
+       if(uri){
+               k_hashtable_put_dup(ent_head, "URI:", uri);
+       }
+       if(modifitime>=0){
+               snprintf(tmpbuf, TMPBUFSIZE, "%s", k_time_to_rfc(modifitime));
+               k_hashtable_put_dup(ent_head, "Last-Modified:", tmpbuf);
+       }
+       if(datalength>=0){
+               snprintf(tmpbuf, TMPBUFSIZE, "%d", datalength);
+               k_hashtable_put_dup(ent_head, "Content-Length:", tmpbuf);
+       }
+       if(mimetype){
+               k_hashtable_put_dup(ent_head, "Content-Type:",   mimetype);
+       }
+       if(encoding){
+               k_hashtable_put_dup(ent_head, "Content-Encoding:", encoding);
+       }
+       if(nocache==1){
+               k_hashtable_put_dup(ent_head, "Cache-Control:",
+                                             "no-cache,no-store");
+       }
+       else
+       if(nocache==0){
+               long maxage=31449600;
+               snprintf(tmpbuf, TMPBUFSIZE, "max-age=%ld", maxage);
+               char* maxages=k_time_to_rfc_relative(maxage);
+               k_hashtable_put_dup(ent_head, "Cache-Control:", tmpbuf);
+               k_hashtable_put_dup(ent_head, "Expires:", maxages);
+       }
+       else
+       if(nocache== -1){
+               k_hashtable_remove(ent_head, "Cache-Control:");
+               k_hashtable_remove(ent_head, "Expires:");
+       }
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
diff --git a/src/ni/ni.c b/src/ni/ni.c
new file mode 100644 (file)
index 0000000..1f0a7ef
--- /dev/null
@@ -0,0 +1,783 @@
+
+/* -}{----------------------------------------------------------------------- */
+
+#include <kernelapi.h>
+#undef  PUBLIC
+#define PUBLIC EXPORT
+#include <ni.h>
+
+/* -}{---- ------------------------------------------------------------------ */
+
+#define TMPBUFSIZE  4096
+static char         tmpbuf[TMPBUFSIZE];
+static char*        hostname;
+static k_hashtable* resources;
+
+/* -}{---- ------------------------------------------------------------------ */
+
+typedef struct ni_driver{
+       char*                name;
+       ni_handles_resource handles_resource;
+       ni_sync_resource    sync_resource;
+} ni_driver;
+
+/* -}{---- From headers.c --------------------------------------------------- */
+
+extern void  init_headers(void);
+extern void  drop_entity_headers(k_hashtable* ent_head);
+extern void  fill_headers(k_hashtable* ent_head,
+                          char*  status,
+                          char*  statustext,
+                          char*  uri,
+                          time_t modifitime,
+                          int    datalength,
+                          char*  mimetype,
+                          char*  encoding,
+                          int    nocache);
+
+/* -}{---- ------------------------------------------------------------------ */
+
+static void          incoming_request(ni_event* evq);
+static ni_resource* ensure_res(char* pub);
+static void          ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub);
+static void          ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub);
+static k_hashtable* get_this(k_hashtable*, char*, k_hashtable*);
+static k_hashtable* get_using(k_hashtable* curr, char* tag, char* val);
+static void          post_to_driver(char* pub, ni_event* evq);
+static void          incoming_resource(ni_event* evt);
+static void    test_pub_tos(ni_resource* res, ni_event* evt, int entityok);
+static int     satisfiable(k_hashtable*, ni_resource*, ni_event*, int);
+static void    update_pubcache(int, k_hashtable**, k_hashtable*, k_hashtable*);
+static int     sub_less(k_hashtable* sub1, k_hashtable* sub2);
+static void    fix_via_subs(k_hashtable* evteh, k_hashtable* reseh);
+static int     sub_ok(k_hashtable* sub);
+static int     range_ok(char* range, char* conrange);
+static void    merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh);
+static void    merge_entity_range(ni_resource* res, ni_event* evt);
+static void    respond(   k_hashtable* sub, ni_resource* res, ni_event* evt);
+static void    respond_ok(k_hashtable* sub, ni_event* evv);
+static void    respond_nf(k_hashtable* sub, ni_event* evv);
+static void*   entity_to_octets(k_hashtable* ent_head, void* entity);
+static char*       handled_by(char* pub);
+static void        call_sync_resource(ni_resource* res);
+static ni_driver* ni_driver_new(char*                name,
+                                  ni_handles_resource handles_resource,
+                                  ni_sync_resource    sync_resource);
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT int ni_module_loaded(void)
+{
+       resources=k_hashtable_new("Resources", 0);
+
+       init_headers();
+
+       k_log_out("NI initialised");
+       return 1;
+}
+
+EXPORT void ni_module_tick(void)
+{
+}
+
+EXPORT int ni_module_event(void* data)
+{
+       ni_event* evt=data;
+
+       if(!k_hashtable_get(evt->ent_head, "Status:")){
+
+               incoming_request(evt);
+       }
+       else{
+               incoming_resource(evt);
+       }
+       return 1;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+void incoming_request(ni_event* evq)
+{
+       int L=1;
+       if(L) ni_event_show(evq, "NI got request event");
+
+       k_hashtable* sub=evq->ent_head;
+       char* uri  =k_hashtable_get(sub, "URI:");
+       char* pub  =k_hashtable_get(sub, "Sub-To:"); if(!pub) return;
+       int   styor=k_hashtable_isi(sub, "Sub-Type:", "Original");
+       char* via  =k_hashtable_get(sub, "Via:");
+       char* from =k_hashtable_get(sub, "From:");
+       if(L) k_log_out("------------------ %s", pub);
+
+       int nocache=k_hashtable_isi(sub, "Cache-Control:", "no-cache");
+       if(nocache){
+               k_hashtable_remove( sub, "Cache-Control:");
+               k_hashtable_set(    sub, "If-Modified-Since:", "0");
+       }
+
+       if(!from){
+               ni_resource* ses=k_hashtable_get(resources, uri);
+               if(ses) ensure_sub_entry(ses->ent_head, sub);
+               if(ses) if(L) ni_resource_show(ses, "Subscribing Resource:");
+               if(styor && via){
+                       post_to_driver(pub, evq);
+                       return;
+               }
+       }
+
+       ni_resource* res=ensure_res(pub);
+       if(L) ni_resource_show(res, "Publishing Resource:");
+
+       k_hashtable* pubcache=0;
+       int sat=satisfiable(sub, res, 0, 0);
+       update_pubcache(sat, &pubcache, res->ent_head, sub);
+       if(sat){
+               if(L) k_log_out("Memory cache hit for %s", pub);
+               respond(k_hashtable_dup(sub), res, 0);
+               ni_event_delete(evq);
+       }
+       else{
+               ensure_pub_entry(res->ent_head, sub);
+               if(L) ni_resource_show(res, "Pending Resource:");
+               if(pubcache){
+                       evq->ent_head=pubcache;
+                       post_to_driver(pub, evq);
+                       k_hashtable_delete(sub);
+               }
+               else{
+                       if(L) k_log_out("In-progress Pub-Cache sufficient");
+                       ni_event_delete(evq);
+               }
+       }
+}
+
+ni_resource* ensure_res(char* pub)
+{
+       ni_resource* res=k_hashtable_get(resources, pub);
+       if(!res){
+               res=ni_resource_new(pub, k_hashtable_new("resHeaders", 1), 0);
+               k_hashtable_set(resources, pub, res);
+       }
+       return res;
+}
+
+void ensure_sub_entry(k_hashtable* ent_head, k_hashtable* sub)
+{
+       char* tag="Sub-To:";
+       k_hashtable* sup=get_this(ent_head, tag, sub);
+       if(!sup){
+               k_hashtable* sup=k_hashtable_dup(sub);
+               char* uri=k_hashtable_extract(sup, "Sub-To:");
+               k_hashtable_put(              sup, "URI:", uri);
+               k_hashtable_remove(           sup, "Sub-Type:");
+               k_hashtable_sub(ent_head, tag, sup);
+       }
+       else{
+               k_hashtable_remove(sup, "Range:");
+               k_hashtable_remove(sup, "Content-Range:");
+               k_hashtable_remove(sup, "Status:");
+               k_hashtable_remove(sup, "Status-Cache:");
+               char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
+               char* via=k_strdup(k_hashtable_get(sub, "Via:"));
+               char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
+               char* rng=k_strdup(k_hashtable_get(sub, "Range:"));
+               if(mth) k_hashtable_put(sup, "Method:",            mth);
+               if(via) k_hashtable_put(sup, "Via:",               via);
+               if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
+               if(rng) k_hashtable_put(sup, "Range:",             rng);
+       }
+}
+
+void ensure_pub_entry(k_hashtable* ent_head, k_hashtable* sub)
+{
+       char* tag="Pub-To:";
+       k_hashtable* sup=get_this(ent_head, tag, sub);
+       if(!sup){
+               k_hashtable* sup=k_hashtable_dup(sub);
+               k_hashtable_remove(sup, "Sub-To:");
+               k_hashtable_remove(sup, "Sub-Type:");
+               k_hashtable_sub(ent_head, tag, sup);
+       }
+       else {
+               k_hashtable_remove(sup, "Method:");
+               k_hashtable_remove(sup, "If-Modified-Since:");
+               k_hashtable_remove(sup, "Cache-Control:");
+               k_hashtable_remove(sup, "Update:");
+               char* mth=k_strdup(k_hashtable_get(sub, "Method:"));
+               char* ims=k_strdup(k_hashtable_get(sub, "If-Modified-Since:"));
+               char* ccn=k_strdup(k_hashtable_get(sub, "Cache-Control:"));
+               char* upd=k_strdup(k_hashtable_get(sub, "Update:"));
+               if(mth) k_hashtable_put(sup, "Method:",            mth);
+               if(ims) k_hashtable_put(sup, "If-Modified-Since:", ims);
+               if(ccn) k_hashtable_put(sup, "Cache-Control:",     ccn);
+               if(upd) k_hashtable_put(sup, "Update:",            upd);
+       }
+}
+
+k_hashtable* get_this(k_hashtable* ent_head, char* pubsub, k_hashtable* try)
+{
+       k_hashtable* curr=k_hashtable_get(ent_head, pubsub);
+       if(!curr) return 0;
+
+       char* tag;
+       char* val;
+
+       tag="From:";
+       val=k_hashtable_get(try, tag);
+       if(val) return get_using(curr, tag, val);
+
+       tag="URI:";
+       val=k_hashtable_get(try, tag);
+       if(val) return get_using(curr, tag, val);
+
+       return 0;
+}
+
+k_hashtable* get_using(k_hashtable* curr, char* tag, char* val)
+{
+       k_hashtable* c;
+       for(c=curr; c; c=c->next) if(k_hashtable_is(c, tag, val)) return c;
+       return c;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+void incoming_resource(ni_event* evt)
+{
+       int L=0;
+       if(L) ni_event_show(evt, "ni got resource incoming event");
+
+       ni_resource* res=k_hashtable_get(resources, evt->uri);
+       int fullconst=res && k_hashtable_is(res->ent_head, "Status:", "200") &&
+                            k_hashtable_is(res->ent_head, "CUX:",    "C");
+
+       if(fullconst){
+               k_log_err("Resource is complete and Constant");
+               ni_event_delete(evt);
+               return;
+       }
+       if(!res) res=ensure_res(evt->uri);
+
+       k_hashtable* reseh=res->ent_head;
+       k_hashtable* evteh=evt->ent_head;
+       int okfull  =k_hashtable_is(evteh, "Status:", "200");
+       int partial =k_hashtable_is(evteh, "Status:", "206");
+       int headonly=k_hashtable_is(evteh, "Status:", "260");
+       int updated =k_hashtable_is(evteh, "Status:", "266");
+       int notmod  =k_hashtable_is(evteh, "Status:", "304");
+       int notfound=k_hashtable_is(evteh, "Status:", "404");
+
+       int entityok= (okfull || partial || notmod);
+       int entityev=!(headonly || notmod || notfound);
+       int entityin=!!evt->entity;
+       int entityon=!!res->entity;
+
+       if(!entityev){
+       }
+       if(okfull || !entityon){
+               k_hashtable_merge(reseh, evteh);
+               if(okfull) k_hashtable_remove(reseh, "Content-Range:");
+       }
+       else{
+               merge_non_entity_headers(reseh, evteh);
+       }
+
+       fix_via_subs(evteh, reseh);
+
+       if(entityin){
+               if(okfull || (partial && !entityon)){
+                       if(res->entity!=evt->entity){
+                               if(!k_hashtable_is(reseh, "CUX:", "C")){
+                                       k_free(res->entity);
+                               }
+                               res->entity=evt->entity;
+                       }
+               }
+               else
+               if(partial && entityon){
+                       merge_entity_range(res, evt);
+               }
+       }
+
+       if(L) ni_resource_show(res, "updated resource - sync and try pubs:");
+
+       call_sync_resource(res);
+
+       test_pub_tos(res, evt, entityok);
+
+       evt->entity=0;
+       ni_event_delete(evt);
+
+       if(L) ni_resource_show(res, "resource after ni:");
+}
+
+void fix_via_subs(k_hashtable* evteh, k_hashtable* reseh)
+{
+       char* from=k_hashtable_get(evteh, "From:");
+       if(!from) return;
+       k_hashtable* subs=k_hashtable_get(reseh, "Sub-To:");
+       k_hashtable* sub;
+       for(sub=subs; sub; sub=sub->next){
+               if(!k_hashtable_is(sub, "Via:", from)) continue;
+               int   st=k_hashtable_get_int(evteh, "Status:");
+               char* cr=k_hashtable_get_dup(evteh, "Content-Range:");
+               if(st!=304) k_hashtable_put_int(sub, "Status:", st);
+               if(st==200) k_hashtable_remove( sub, "Content-Range:");
+               else
+               if(cr)          k_hashtable_put(sub, "Content-Range:", cr);
+               if(sub_ok(sub)) k_hashtable_set(sub, "Status-Cache:", "OK");
+       }
+}
+
+void test_pub_tos(ni_resource* res, ni_event* evt, int entityok)
+{
+       k_hashtable* keeps=0;
+       k_hashtable* sub=k_hashtable_extract(res->ent_head, "Pub-To:");
+       while(sub){
+               k_hashtable* subnext=sub->next;
+               sub->next=0;
+               int keep=1;
+               int sat=satisfiable(sub, res, evt, entityok);
+               update_pubcache(sat, 0, res->ent_head, sub);
+               if(sat){
+                       respond(k_hashtable_dup(sub), res, evt);
+                       if(!k_hashtable_get(sub, "Update:")) keep=0;
+               }
+               if(keep){
+                       sub->next=keeps;
+                       keeps=sub;
+               }
+               else{
+                       k_hashtable_delete(sub);
+               }
+               sub=subnext;
+       }
+       sub=keeps;
+       while(sub){
+               k_hashtable* subnext=sub->next;
+               k_hashtable_sub(res->ent_head, "Pub-To:", sub);
+               sub=subnext;
+       }
+}
+
+int satisfiable(k_hashtable*  sub,
+                ni_resource* res,
+                ni_event*    evt,
+                int           entityok)
+{
+       int L=0;
+       int   get  =k_hashtable_is( sub, "Method:", "GET");
+       int   head =k_hashtable_is( sub, "Method:", "HEAD");
+       int   dosub=k_hashtable_is( sub, "Method:", "SUB");
+       int   unsub=k_hashtable_is( sub, "Method:", "UNSUB");
+       int   full=!k_hashtable_isi(sub, "Cache-Control:", "no-full");
+       int   updt =k_hashtable_isi(sub, "Update:", "changes");
+       char* ims  =k_hashtable_get(sub, "If-Modified-Since:");
+       char* range=k_hashtable_get(sub, "Range:");
+
+       int getsub  =get || dosub;
+       int getrange=getsub && range;
+
+       k_hashtable* reseh=res->ent_head;
+       int   gotall=    k_hashtable_is( reseh, "Status:", "200");
+       int   gotrange=  k_hashtable_is( reseh, "Status:", "206");
+       int   gothead=   k_hashtable_is( reseh, "Status:", "260");
+       int   gotupdated=evt && 
+                        k_hashtable_is(evt->ent_head, "Status:", "266");
+       int   notfound=  k_hashtable_is( reseh, "Status:", "404");
+       int   constant=  k_hashtable_is( reseh, "CUX:",    "C"  );
+       char* conrange=  k_hashtable_get(reseh, "Content-Range:");
+
+       if(unsub)    return 0;
+       if(notfound) return 1;
+
+       int gotany=(gothead || gotrange || gotall);
+       int snapok=(constant || !ims || entityok);
+
+       int sat=
+          (full && 
+           ((getsub   && gotall                                && snapok) ||
+            (getrange && gotrange && range_ok(range, conrange) && snapok) ||
+            (head     && gotany                                         )   ))
+             ||
+           (updt &&
+            ((dosub    && gotupdated)));
+
+       if(L) k_log_out("satisfiable %d", sat);
+       return sat;
+}
+
+void update_pubcache(int           sat,
+                     k_hashtable** pubcachep,
+                     k_hashtable*  reseh,
+                     k_hashtable*  sub)
+{
+       if(!sat){ if(pubcachep){
+               k_hashtable* pubcache;
+               pubcache=k_hashtable_get(reseh, "Pub-Cache:");
+               if(!pubcache || sub_less(pubcache, sub)){
+                       if(!pubcache){
+                               pubcache=k_hashtable_dup(sub);
+                               k_hashtable_sub(reseh, "Pub-Cache:", pubcache);
+                       }
+                       else{
+                               k_hashtable_merge(pubcache, sub);
+                       }
+                       k_hashtable_remove(pubcache, "URI:");
+                       k_hashtable_remove(pubcache, "Sub-Type:");
+                       *pubcachep=k_hashtable_dup(pubcache);
+                       k_hashtable_set(*pubcachep, "Sub-Type:", "Cache");
+               }
+       }}
+       else{ if(!pubcachep){
+               k_hashtable* pubcache;
+               pubcache=k_hashtable_get(reseh, "Pub-Cache:");
+               if(!sub_less(sub, pubcache)){
+                       k_hashtable_remove(pubcache, "Method:");
+                       k_hashtable_remove(pubcache, "If-Modified-Since:");
+               }
+       }}
+}
+
+int sub_ok(k_hashtable* sub)
+{
+       char* gotresp =k_hashtable_get(sub, "Status:");
+       if(!gotresp) return 0;
+       int   notfound=k_hashtable_is( sub, "Status:", "404");
+       if(notfound) return 0;
+       int   gotall  =k_hashtable_is( sub, "Status:", "200");
+       int   gotrange=k_hashtable_is( sub, "Status:", "206");
+       int   gothead= k_hashtable_is( sub, "Status:", "260");
+       int   doget   =k_hashtable_is( sub, "Method:", "GET");
+       int   dohead  =k_hashtable_is( sub, "Method:", "HEAD");
+       int   dosub   =k_hashtable_is( sub, "Method:", "SUB");
+       char* range   =k_hashtable_get(sub, "Range:");
+       char* conrange=k_hashtable_get(sub, "Content-Range:");
+
+       int dogetsub=doget || dosub;
+       int dogetrange=dogetsub && range;
+       int gotany  =(gothead || gotrange || gotall);
+
+       return (dogetsub   &&  gotall                               ) ||
+              (dogetrange &&  gotrange && range_ok(range, conrange)) ||
+              (dohead     &&  gotany                               );
+}
+
+int sub_less(k_hashtable* sub1, k_hashtable* sub2)
+{
+       int L=0;
+       if(L) k_log_out("sub_less sub1:");
+       if(L) k_hashtable_show_chars(sub1);
+       if(L) k_log_out("sub_less sub2:");
+       if(L) k_hashtable_show_chars(sub2);
+       char* mth1 =k_hashtable_get(sub1, "Method:");
+       int   head1=k_hashtable_is( sub1, "Method:", "HEAD");
+       int   get2 =k_hashtable_is( sub2, "Method:", "GET");
+       char* ims1 =k_hashtable_get(sub1, "If-Modified-Since:");
+       char* ims2 =k_hashtable_get(sub2, "If-Modified-Since:");
+       int   full=!k_hashtable_isi(sub2, "Cache-Control:", "no-full");
+       return full && ((!mth1) || (head1 && get2) || (!ims1 && ims2));
+}
+
+int range_ok(char* range, char* conrange)
+{
+       k_log_out("range_ok not implemented: %s vs %s", range, conrange);
+       return 0;
+}
+
+void merge_non_entity_headers(k_hashtable* reseh, k_hashtable* evteh)
+{
+}
+
+void merge_entity_range(ni_resource* res, ni_event* evt)
+{
+       k_log_out("merge_entity_range not implemented yet");
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+void respond(k_hashtable* sub, ni_resource* res, ni_event* evt)
+{
+       int updated=evt && k_hashtable_is(evt->ent_head, "Status:", "266");
+       ni_event* evv=updated? ni_event_dup(evt): 
+                               ni_res_to_evt(res);
+       int L=0;
+       if(L) ni_event_show(evv, "respond");
+
+       k_hashtable_remove(evv->ent_head, "Permit:");
+       k_hashtable_remove(evv->ent_head, "Sub-To:");
+       k_hashtable_remove(evv->ent_head, "Pub-To:");
+       k_hashtable_remove(evv->ent_head, "Pub-Cache:");
+       k_hashtable_sub(   evv->ent_head, "Pub-To:", sub);
+
+       int nf;
+       nf=k_hashtable_is( evv->ent_head, "Status:", "404");
+       if(!nf) respond_ok(sub, evv);
+       else    respond_nf(sub, evv);
+}
+
+void respond_ok(k_hashtable* sub, ni_event* evv)
+{
+       char* status=0;
+       char* statustext=0;
+       int   datalength= -1;
+       int   nocache=0;
+       k_hashtable* ent_head=evv->ent_head;
+
+       time_t modifitime=k_hashtable_get_int(ent_head, "Last-Modified-Epoch:");
+       char*  modistring=k_hashtable_get(    ent_head, "Last-Modified:");
+       if(!modifitime){
+               modifitime=k_time_from_rfc(modistring);
+       }
+       char*  imss=k_hashtable_get(sub, "If-Modified-Since:");
+       time_t imst=k_time_from_rfc(imss);
+
+       if(modifitime== -1 || imst== -1 || modifitime > imst){
+               if(k_hashtable_is(sub, "Method:", "HEAD")){
+                       evv->entity=0;
+               }
+               if(!k_hashtable_is(ent_head, "CUX:", "C")){
+                       evv->entity=entity_to_octets(ent_head, evv->entity);
+                       nocache=1;
+               }
+       }
+       else{
+               status="304";
+               statustext="Not Modified";
+               modifitime= -1;
+               nocache= -1;
+               evv->entity=0;
+               drop_entity_headers(ent_head);
+       }
+
+       fill_headers(ent_head, status, statustext, 0,
+                    modistring? -1: modifitime,
+                    datalength, 0, 0, nocache);
+
+       char*  pub =k_hashtable_get(sub, "URI:");
+       int L=0;
+       if(L) ni_event_show(evv, "respond_ok");
+       post_to_driver(pub, evv);
+}
+
+void respond_nf(k_hashtable* sub, ni_event* evv)
+{
+       fill_headers(evv->ent_head, "404", "File Not Found", 0, -1, 0, 0, 0, 1);
+       char*  pub =k_hashtable_get(sub, "URI:");
+       post_to_driver(pub, evv);
+}
+
+void post_to_driver(char* pub, ni_event* evq)
+{
+       char* driver=handled_by(pub);
+       if(0) k_log_out("Passing %s on to %s", pub, driver);
+       if(0) ni_event_show(evq, "Post to Driver:");
+       k_event_post(driver, evq);
+}
+
+void* entity_to_octets(k_hashtable* ent_head, void* entity)
+{
+       if(!entity) return 0;
+       size_t size=k_hashtable_get_int(ent_head, "Content-Length:");
+       return k_memdup(entity, size);
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT char* ni_hostname()
+{
+       return hostname? hostname: "not set";
+}
+
+EXPORT void ni_hostname_set(char* name)
+{
+       k_free(hostname);
+       hostname=k_strdup(name);
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+static ni_driver* npdriver;
+static ni_driver* moddriver;
+
+char* handled_by(char* pub)
+{
+       if(moddriver->handles_resource(pub)) return moddriver->name;
+       return "np";
+}
+
+void call_sync_resource(ni_resource* res)
+{
+       if(moddriver->handles_resource(res->uri)) moddriver->sync_resource(res);
+       else                                      npdriver->sync_resource(res);
+}
+
+ni_driver* ni_driver_new(char*                name, 
+                           ni_handles_resource handles_resource,
+                           ni_sync_resource    sync_resource)
+{
+       ni_driver* driver=k_malloc(sizeof(ni_driver));
+       driver->name            =name;
+       driver->handles_resource=handles_resource;
+       driver->sync_resource   =sync_resource;
+       return driver;
+}
+
+EXPORT void ni_register_driver(char*                name,
+                                ni_handles_resource handles_resource,
+                                ni_sync_resource    sync_resource)
+{
+       ni_driver* d=ni_driver_new(name, handles_resource, sync_resource);
+       if(!strcmp(name, "np")) npdriver=d;
+       else                     moddriver=d;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_resource* ni_resource_new(char*        uri,
+                                      k_hashtable* ent_head,
+                                      char*        entity)
+{
+       char* urih=k_hashtable_get(ent_head, "URI:");
+       if(urih) uri=urih;
+       else
+       if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
+
+       ni_resource* res=k_malloc(sizeof(ni_resource));
+       res->uri     =(uri? k_strdup(uri): 0);
+       res->ent_head=ent_head;
+       res->entity  =entity;
+       return res;
+}
+
+EXPORT ni_resource* ni_resource_dup(ni_resource* res)
+{
+       ni_resource* rep=k_malloc(sizeof(ni_resource));
+       rep->uri     =k_strdup(       res->uri);
+       rep->ent_head=k_hashtable_dup(res->ent_head);
+       rep->entity  =                res->entity;
+       return rep;
+}
+
+EXPORT void ni_resource_delete(ni_resource* res)
+{
+       if(!res) return;
+       if(res->entity && res->ent_head){
+               int constant=k_hashtable_is(res->ent_head, "CUX:", "C");
+               if(!constant) k_free(res->entity);
+       }
+       k_hashtable_delete(res->ent_head);
+       k_free(            res->uri);
+       k_free(            res);
+}
+
+static char* excto[]={ "Permit:", "Sub-To:", "Pub-To:", 0 };
+static char* perto[]={ "Permit:", 0 };
+static char* subto[]={ "Sub-To:",    0 };
+static char* pubto[]={ "Pub-To:",    0 };
+
+EXPORT void ni_resource_show(ni_resource* res, char* text)
+{
+       if(!res){
+               k_log_out("\n---%s--------\n------------\n\n----------",
+                               text);
+       }
+       else{
+               char*  b=tmpbuf;
+               size_t s=TMPBUFSIZE;
+               size_t l=0;
+               l+=k_hashtable_snprintf_x(res->ent_head, b+l, s-l, excto);
+               l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, perto);
+               l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, subto);
+               l+=k_hashtable_snprintf_i(res->ent_head, b+l, s-l, pubto);
+               k_log_out("\n---%s----%s--\n%s----------\n%p\n----------", 
+                               text, res->uri, tmpbuf, res->entity);
+       }
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_resource* ni_resource_get(char* uri)
+{
+       return k_hashtable_get(resources, uri);
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_event* ni_res_to_evt(ni_resource* res)
+{
+       ni_event* evp=k_malloc(sizeof(ni_event));
+       evp->uri     =k_strdup(       res->uri);
+       evp->evt_head=k_hashtable_new("vHeaders/ni_res_to_evt", 1);
+       evp->ent_head=k_hashtable_dup(res->ent_head);
+       evp->entity  =                res->entity;
+       return evp;
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
+EXPORT ni_event* ni_event_new(char*        uri,
+                                k_hashtable* evt_head,
+                                k_hashtable* ent_head,
+                                char*        entity)
+{
+       char* urih=k_hashtable_get(ent_head, "URI:");
+       if(urih) uri=urih;
+       else
+       if(uri) k_hashtable_put_dup(ent_head, "URI:", uri);
+
+       ni_event* evt=k_malloc(sizeof(ni_event));
+       evt->uri     =(uri? k_strdup(uri): 0);
+       evt->evt_head=evt_head;
+       evt->ent_head=ent_head;
+       evt->entity  =entity;
+       return evt;
+}
+
+EXPORT ni_event* ni_event_dup(ni_event* evt)
+{
+       ni_event* evp=k_malloc(sizeof(ni_event));
+       evp->uri     =k_strdup(       evt->uri);
+       evp->evt_head=k_hashtable_dup(evt->evt_head);
+       evp->ent_head=k_hashtable_dup(evt->ent_head);
+       evp->entity  =                evt->entity;
+       return evp;
+}
+
+EXPORT void ni_event_delete(ni_event* evt)
+{
+       if(!evt) return;
+       if(evt->entity && evt->ent_head){
+               int constant=k_hashtable_is(evt->ent_head, "CUX:", "C");
+               if(!constant) k_free(evt->entity);
+       }
+       k_hashtable_delete(evt->evt_head);
+       k_hashtable_delete(evt->ent_head);
+       k_free(            evt->uri);
+       k_free(            evt);
+}
+
+EXPORT void ni_event_show(ni_event* evt, char* text)
+{
+       if(!evt){
+               k_log_out("\n---%s--------\n------------\n\n----------",
+                               text);
+       }
+       else{
+               char* buf=tmpbuf;
+               int   siz=TMPBUFSIZE;
+               int n=k_hashtable_snprintf(evt->evt_head, buf,   siz)+1;
+               ;     k_hashtable_snprintf(evt->ent_head, buf+n, siz-n);
+               k_log_out("\n---%s----%s--\n"
+                         "%s----------\n"
+                         "%s----------\n"
+                         "%p\n----------", 
+                               text, evt->uri, buf, buf+n, evt->entity);
+       }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+
+
+
diff --git a/src/platform/container.h b/src/platform/container.h
new file mode 100644 (file)
index 0000000..adb9cce
--- /dev/null
@@ -0,0 +1,19 @@
+/* -------------------------------------------------------------------------- */
+/*
+    container.h:  lifecycle and external event functions 
+                  from executable to kernel
+*/
+
+PUBLIC void     c_init(CONSOLE console,
+                       char*   version,
+                       char*   ciux,
+                       void  (*terminate)(void));
+
+PUBLIC C_RUN_RV c_run(C_RUN_ARG arg);
+
+PUBLIC void     c_running(int running);
+PUBLIC void     c_key(unsigned char key, int down);
+PUBLIC void     c_signal(int signum);
+
+/* -------------------------------------------------------------------------- */
+
diff --git a/src/platform/kernelapi.c b/src/platform/kernelapi.c
new file mode 100644 (file)
index 0000000..86c406b
--- /dev/null
@@ -0,0 +1,2053 @@
+
+/* -------------------------------------------------------------------------- */
+
+#define PUBLIC EXPORT
+#include <kernelapi.h>
+#include <osapi.h>
+#include "container.h"
+
+/* -------------------------------------------------------------------------- */
+
+#define OTHER_THREAD
+#define CERNDATE              "%d/%b/%Y:%H:%M:%S"
+#define RDSIZE                40960
+#define WRSIZE                2
+#define TMPBUFSIZE            40960
+#define WRITEMAX              30000
+#define LOGBUFSIZE            1024
+#define OOM_STRING            "Out of memory!\nBailing."
+#define MAX_MODULES           64
+#define MAX_EVENTS_PER_MODULE 3
+#define CHAN_CLOSING(chan)    (chan->priv->state!=CHAN_OPEN)
+#define CHAN_CLOSE            0
+#define CHAN_LINGER           1
+#define CHAN_OPEN             2
+#define SETCB_RD              1
+#define SETCB_WR              2
+#define MIN(a,b)              (((a) < (b))? (a): (b))
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct wrbuffer{
+       char*  base;
+       size_t size;
+       char*  free;
+} wrbuffer;
+
+struct k_channel_priv{
+
+       SOCK_T    SOCK;
+       int       state;
+       int       event;
+       int       rwmask;
+       char*     rdbuffer;
+       size_t    rdbufpos;
+       size_t    rdbufsize;
+       int       rdbufforeign;
+       wrbuffer* wrbuffers;
+       int       wrbufpos;
+       int       wrbufsize;
+       int       lingercount;
+};
+
+typedef struct event{ struct event* next;
+       void* data;
+} event;
+
+typedef struct k_module{
+       char*             name;
+       k_module_tick_fn  tick_fn;
+       k_module_event_fn event_fn;
+       event*            queue;
+} k_module;
+
+typedef struct key_event{ struct key_event* next;
+       unsigned char key;
+       int           down;
+} key_event;
+
+/* -------------------------------------------------------------------------- */
+
+static int                running=2;
+static k_gl_reshape_event reshape_fn=0;
+static k_gl_draw_event    draw_fn=0;
+static k_gl_key_event     key_fn=0;
+static k_module           modules[MAX_MODULES];
+static int                nextmodule=0;
+static key_event*         key_consumer=0;
+static key_event*         key_producer=0;
+static k_channel*         k_channels=0;
+static k_hashtable*       connecting_chans;
+static k_hashtable*       current_chans;
+static k_hashtable*       monthhash;
+static char               tmpbuf[TMPBUFSIZE];
+static char               logbuf[LOGBUFSIZE];
+static FILEP              logfile=0;
+static int                log_gmt=0;
+static char               dummy_empty_data[0];
+
+/* -------------------------------------------------------------------------- */
+
+static void    gen_conn_name(k_channel* chan);
+static void    chan_closed(k_channel* chan);
+static SOCK_T  make_listen_socket(int listenport);
+static SOCK_T  make_connect_socket(void);
+static int     listen_socket(SOCK_T s, int listenport);
+static int     connect_socket(SOCK_T s, char*, int);
+static void    add_module_entry(char*, k_module_tick_fn, k_module_event_fn);
+static int                run_queues(void);
+static void               show_open_channels(void);
+static k_channel*         find_k_channel_for_socket(SOCK_T s);
+static k_channel*         register_k_channel(char*           name,
+                                             char*           listenhost,
+                                             int             listenport,
+                                             char*           banner,
+                                             k_channel_event rdcallback,
+                                             k_channel_event wrcallback,
+                                             void*           context,
+                                             int             type,
+                                             struct in_addr  clientip,
+                                             SOCK_T          s);
+static event**            get_module_queue(char* name);
+static k_module_loaded_fn get_module_loaded_fn(char* name, MODULE module);
+static k_module_tick_fn   get_module_tick_fn(  char* name, MODULE module);
+static k_module_event_fn  get_module_event_fn( char* name, MODULE module);
+static int                check_i(char* key, char* includes[], int ignorecase);
+static int                check_x(char* key, char* excludes[], int ignorecase);
+static unsigned int       string_hash(char* p);
+static int                set_rdbuffer(k_channel*, char* rdbuffer, size_t size);
+static char*              get_rdbuffer(k_channel* chan);
+static void    append_to_wrbuffers(k_channel*, char*, size_t, int);
+static char*   chop_from_rdbuffer_div(k_channel* chan, char* div);
+static char*   chop_from_rdbuffer_len(k_channel* chan, size_t size);
+static void    chop_from_wrbuffers(   k_channel* chan, size_t size);
+static void    got_key(unsigned char key, int down);
+static void    init_keys(void);
+static void    dir_read(char*, char*, char*, int, k_stat, void*);
+static void    load_module(char* name, int dot);
+static void    init_modules(void);
+static void    log_net_err(char* where, int e);
+static int     dir_list(char*, int, char*, char*, char*);
+static int     ensure_dir(char* fullname);
+static void    next_keys(void);
+static void    read_more_and_notify(k_channel* chan);
+static void    readable_socket(k_channel* chan);
+static void    register_connections(k_channel* chan);
+static void    do_regular_things(void);
+static void    tick_modules(void);
+static void    remove_dead_k_channels(void);
+static void    unregister_k_channel(k_channel* chan, int now);
+static void    write_more_and_notify(k_channel* chan);
+static char    hex_to_int(char c);
+static void    write_to_logfile_cern_style(char* text, int error);
+static void    writeable_socket(k_channel* chan);
+static void               exception_socket(k_channel* chan);
+static SOCK_T             make_udp_listen_socket(int listenport);
+static struct sockaddr_in make_sockaddr_in(unsigned long address,
+                                           unsigned int port);
+static void               init_hashtables(void);
+
+/* -------------------------------------------------------------------------- */
+
+#include <osapi.c>
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void c_init(CONSOLE console,
+                   char*   version,
+                   char*   ciux,
+                   void  (*terminate)(void))
+{
+       k_console  =console;
+       k_version  =version;
+       k_ciux     =ciux;
+       k_terminate=terminate;
+
+       init_hashtables();
+       init_keys();
+
+       if(LOAD_MODULES_EARLY){
+               init_gl();
+               init_net();
+               init_modules();
+       }
+}
+
+EXPORT C_RUN_RV c_run(C_RUN_ARG arg)
+{
+       init_thread();
+
+       if(!LOAD_MODULES_EARLY){
+               init_gl();
+               init_net();
+               init_modules();
+       }
+
+       if(reshape_fn)(*reshape_fn)(640,480);
+       if(draw_fn)   (*draw_fn)();
+
+       k_log_out("Cilux U-Web Exchange started");
+       k_log_out("%s", k_version);
+       k_log_out("---------------------------------------------");
+
+       int queue_events_to_do=0;
+       while(1){
+               if(running==2) poller(queue_events_to_do);
+               if(running==1) SLEEP_MS(LOOP_TICK);
+               if(running==0) break;
+
+               if(running==2) queue_events_to_do=run_queues();
+               if(running==1) ;
+               if(running==0) break;
+       }
+
+       k_log_out("Cilux U-Web Exchange terminated");
+
+       return 0;
+}
+
+EXPORT OTHER_THREAD void c_running(int r)
+{
+       running=r;
+}
+
+EXPORT OTHER_THREAD void c_key(unsigned char key, int down)
+{
+       got_key(key, down);
+}
+
+EXPORT OTHER_THREAD void c_signal(int signum)
+{
+       k_log_out("Received signal %d", signum);
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_gl_register_reshape(k_gl_reshape_event callback){
+       reshape_fn=callback;
+}
+
+EXPORT void k_gl_register_draw(k_gl_draw_event callback){
+       draw_fn=callback;
+}
+
+EXPORT void k_gl_register_key(k_gl_key_event callback){
+       key_fn=callback;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void init_hashtables(void)
+{
+       connecting_chans=k_hashtable_new("Connecting Channels", 0);
+       current_chans   =k_hashtable_new("Current Channels", 0);
+       monthhash       =k_hashtable_new("Months", 1);
+       k_hashtable_set(monthhash, "Jan", (void*)1);
+       k_hashtable_set(monthhash, "Feb", (void*)2);
+       k_hashtable_set(monthhash, "Mar", (void*)3);
+       k_hashtable_set(monthhash, "Apr", (void*)4);
+       k_hashtable_set(monthhash, "May", (void*)5);
+       k_hashtable_set(monthhash, "Jun", (void*)6);
+       k_hashtable_set(monthhash, "Jul", (void*)7);
+       k_hashtable_set(monthhash, "Aug", (void*)8);
+       k_hashtable_set(monthhash, "Sep", (void*)9);
+       k_hashtable_set(monthhash, "Oct", (void*)10);
+       k_hashtable_set(monthhash, "Nov", (void*)11);
+       k_hashtable_set(monthhash, "Dec", (void*)12);
+}
+
+void init_logging(void)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s.log", k_ciux);
+       logfile=FOPEN(tmpbuf, "a");
+       if(!logfile) k_log_err("couldn't open logfile %s", tmpbuf);
+}
+
+void init_modules(void)
+{
+       char* patts[]={ "{*.dll,*.so}", 0};
+       char* temps[]={ "%s:", 0 };
+       k_dir_list(".", ".", patts, temps, dir_read, 0);
+}
+
+void dir_read(char*  basedir,
+              char*  path,
+              char*  data,
+              int    usedmmap,
+              k_stat kstat,
+              void*  context)
+{
+       int is_np=0;
+       if(data){
+               if(!strncmp(data, MODPRE, strlen(MODPRE))){
+                       k_ciux=k_strdup(data+strlen(MODPRE));
+                       char* c=strchr(k_ciux, '.');
+                       *c=0;
+               }
+               else{
+                       is_np=1;
+                       k_ciux="np";
+               }
+               k_free(data);
+       }
+
+       init_logging();
+
+       k_log_out("========================================");
+
+       load_module("ni", 0);
+       load_module("np", 0);
+       if(!is_np) load_module(k_ciux, 1);
+
+       k_log_out("========================================");
+}
+
+void load_module(char* name, int dot)
+{
+       k_log_out("------------ %s --------------", name);
+       char dlname[64];
+       snprintf(dlname, 64, "%s%s%s%s", dot? "./": "", MODPRE, name, MODPOST);
+       MODULE module=DLOPEN(dlname);
+       if(module){
+               k_module_loaded_fn loaded_fn=get_module_loaded_fn(name, module);
+               k_module_tick_fn   tick_fn  =get_module_tick_fn(  name, module);
+               k_module_event_fn  event_fn =get_module_event_fn( name, module);
+               if(loaded_fn && event_fn){
+                       add_module_entry(name, tick_fn, event_fn);
+                       (*loaded_fn)();
+               }
+               else k_log_err("Couldn't load module %s (hooks)", dlname);
+       }
+       else k_log_err("Couldn't load module %s (dlopen) %s", dlname, DLERROR);
+}
+
+k_module_loaded_fn get_module_loaded_fn(char* name, MODULE module)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_LOADED_FN);
+       k_module_loaded_fn f=(k_module_loaded_fn)DLSYM(module, tmpbuf);
+       if(!f) k_log_err("no %s", tmpbuf);
+       return f;
+}
+
+k_module_tick_fn get_module_tick_fn(char* name, MODULE module)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_TICK_FN);
+       k_module_tick_fn f=(k_module_tick_fn)DLSYM(module, tmpbuf);
+       if(!f) k_log_err("no %s", tmpbuf);
+       return f;
+}
+
+k_module_event_fn get_module_event_fn(char* name, MODULE module)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s%s", name, NAME_OF_MODULE_EVENT_FN);
+       k_module_event_fn f=(k_module_event_fn)DLSYM(module, tmpbuf);
+       if(!f) k_log_err("no %s", tmpbuf);
+       return f;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void add_module_entry(char*             name,
+                      k_module_tick_fn  tick_fn,
+                      k_module_event_fn event_fn)
+{
+       if(nextmodule==MAX_MODULES){
+               k_fatal("Too many modules! Maximum %d", MAX_MODULES);
+       }
+       modules[nextmodule  ].name    =name;
+       modules[nextmodule  ].tick_fn =tick_fn;
+       modules[nextmodule  ].event_fn=event_fn;
+       modules[nextmodule++].queue   =0;
+}
+
+event* event_new(void* data)
+{
+       event* ev;
+       ev=k_malloc(sizeof(event));
+       ev->next=0;
+       ev->data=data;
+       return ev;
+}
+
+EXPORT int k_event_post(char* module, void* data)
+{
+       event* ev=event_new(data);
+       event** queue=get_module_queue(module);
+       if(!queue){
+               k_log_err("k_event_post(%s): no such module", module);
+               return 0;
+       }
+       if(!*queue) *queue=ev;
+       else{
+               event* p=*queue;
+               do{
+                       if(!p->next){
+                               p->next=ev;
+                               break;
+                       }
+                       p=p->next;
+               } while(1);
+       }
+       return 1;
+}
+
+event** get_module_queue(char* name)
+{
+       int i;
+       for(i=0; i< nextmodule; i++){
+               if(!strcmp(modules[i].name, name)) return &modules[i].queue;
+       }
+       return 0;
+}
+
+int run_queues(void)
+{
+       int i,j;
+       for(i=0; i< nextmodule; i++){
+               for(j=0; modules[i].queue && j < MAX_EVENTS_PER_MODULE; j++){
+                       event* event=modules[i].queue;
+                       modules[i].queue=event->next;
+                       event->next=0;
+                       (*modules[i].event_fn)(event->data);
+                       k_free(event);
+               }
+       }
+       for(i=0; i< nextmodule; i++){
+               if(modules[i].queue) return 1;
+       }
+       return 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+void init_keys(void)
+{
+       key_event* ke;
+       ke=k_malloc(sizeof(key_event));
+       ke->key=0;
+       ke->down=0;
+       ke->next=0;
+       key_consumer=ke;
+       key_producer=ke;
+}
+
+OTHER_THREAD void got_key(unsigned char key, int down)
+{
+       if(!key_producer) return;
+       key_event* ke;
+       ke=k_malloc(sizeof(key_event));
+       ke->key=key;
+       ke->down=down;
+       ke->next=0;
+       key_producer->next=ke;
+       key_producer=ke;
+}
+
+void next_keys(void)
+{
+       while(key_consumer!=key_producer){
+               key_event* ke=key_consumer->next;
+               if(key_fn)  (*key_fn)(ke->key, ke->down);
+               k_free(key_consumer);
+               key_consumer=ke;
+       }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+EXPORT char* k_channel_name(char* type, char* ip, int port, char* other)
+{
+       int ln=0;
+       int bufsize=TMPBUFSIZE;
+
+       if(type)  ln+=snprintf(tmpbuf+ln, bufsize-ln, "|%s|", type);
+       else      ln+=snprintf(tmpbuf+ln, bufsize-ln,  "|-|");
+       if(ln>=bufsize) return 0;
+       
+       if(ip)    ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s|", ip);
+       else      ln+=snprintf(tmpbuf+ln, bufsize-ln,  "-|");
+       if(ln>=bufsize) return 0;
+
+       if(port)  ln+=snprintf(tmpbuf+ln, bufsize-ln, "%d|", port);
+       else      ln+=snprintf(tmpbuf+ln, bufsize-ln,  "-|");
+       if(ln>=bufsize) return 0;
+
+       if(other) ln+=snprintf(tmpbuf+ln, bufsize-ln, "%s|", other);
+       else      ln+=snprintf(tmpbuf+ln, bufsize-ln,  "-|");
+       if(ln>=bufsize) return 0;
+
+       return k_strdup(tmpbuf);
+}
+
+EXPORT k_channel* k_channel_connect_name(char*           chanm,
+                                         k_channel_event rdcallback,
+                                         k_channel_event wrcallback)
+{
+       if(k_hashtable_get(connecting_chans, chanm)){
+               if(1) k_log_out("Already connecting to %s", chanm);
+               return 0;
+       }
+       k_hashtable_set(connecting_chans, chanm, chanm);
+
+       k_channel* chan=0;
+       char* t=k_strdup(chanm);
+       char* a=strchr(t+1, '|'); *a++=0;
+       char* p=strchr(a,   '|'); *p++=0;
+       char* o=strchr(p,   '|'); *o++=0;
+       int   pi=atoi(p);
+
+       if(strstr(t, "-client")){
+               chan=k_channel_connect(t, a, pi, rdcallback, wrcallback, 0);
+               if(!chan) k_log_err("couldn't connect to %s:%d", a, pi);
+       }
+       else
+       if(strstr(t, "-server") && *a=='-'){
+               chan=k_channel_listen(t, pi, 0, rdcallback, wrcallback, 0);
+               if(!chan) k_log_err("couldn't listen on port %d", pi);
+       }
+       else
+       if(strstr(t, "-server")){
+               chan=k_channel_connect(t, a, pi, rdcallback, wrcallback, 0);
+               if(!chan) k_log_err("couldn't connect to %s:%d", a, pi);
+       }
+
+       *--a= '|';
+       *--p= '|';
+       *--o= '|';
+
+       if(!chan) k_free(t);
+
+       return chan;
+}
+
+EXPORT k_channel* k_channel_get_name(char* chanm)
+{
+       k_channel* chan=k_hashtable_get(current_chans, chanm);
+       return chan;
+}
+
+EXPORT void k_channel_show_all()
+{
+       k_log_out("----------------------------------------");
+       k_hashtable_show(connecting_chans);
+       k_log_out("----------------------------------------");
+       k_hashtable_show(current_chans);
+       k_log_out("----------------------------------------");
+}
+
+void gen_conn_name(k_channel* chan)
+{
+       char* t=chan->name+1;
+       char* a=strchr(t, '|'); *a++=0;
+       char* p=strchr(a, '|'); *p++=0;
+       char* o=strchr(p, '|'); *o++=0;
+       char* ip=inet_ntoa(chan->clientip);
+       int   pi=atoi(p);
+
+       char b[32];
+       static long uniquenum;
+       snprintf(b, 32, "%ld", ++uniquenum);
+       chan->name=k_channel_name(t, ip, pi, b);
+
+       *--a= '|';
+       *--p= '|';
+       *--o= '|';
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT k_channel* k_channel_listen(char*           name,
+                                   int             listenport,
+                                   char*           banner,
+                                   k_channel_event rdcallback,
+                                   k_channel_event wrcallback,
+                                   void*           context)
+{
+       SOCK_T s=make_listen_socket(listenport);
+       if(!s) return 0;
+
+       struct in_addr noclientip=EMPTY_IN_ADDR;
+       k_channel* chan=register_k_channel(name, 
+                                          0, 
+                                          listenport, 
+                                          banner, 
+                                          rdcallback, 
+                                          wrcallback, 
+                                          context,
+                                          CHAN_LISTEN, 
+                                          noclientip, 
+                                          s);
+       int r=listen_socket(s, listenport);
+       if(r== -1){
+               unregister_k_channel(chan,1);
+               return 0;
+       }
+       return chan;
+}
+
+EXPORT k_channel* k_channel_connect(char*           name,
+                                    char*           listenhost,
+                                    int             listenport,
+                                    k_channel_event rdcallback,
+                                    k_channel_event wrcallback,
+                                    void*           context)
+{
+       SOCK_T s=make_connect_socket();
+       if(!s) return 0;
+
+       struct in_addr noclientip=EMPTY_IN_ADDR;
+       k_channel* chan=register_k_channel(name,
+                                          listenhost,
+                                          listenport, 
+                                          0, 
+                                          rdcallback, 
+                                          wrcallback, 
+                                          context,
+                                          CHAN_ESTABL, 
+                                          noclientip, 
+                                          s);
+       int r=connect_socket(s, listenhost, listenport);
+       if(r== -1){
+               unregister_k_channel(chan,1);
+               return 0;
+       }
+       return chan;
+}
+
+EXPORT k_channel* k_channel_send(k_channel* chan,
+                                 char*      base,
+                                 size_t     size,
+                                 int        free)
+{
+       append_to_wrbuffers(chan, base, size, free);
+       set_callback(chan, SETCB_WR);
+       return chan;
+}
+
+EXPORT char* k_channel_chop_div(k_channel* chan, char* divider)
+{
+       return chop_from_rdbuffer_div(chan, divider);
+}
+
+EXPORT char* k_channel_chop_len(k_channel* chan, size_t size)
+{
+       return chop_from_rdbuffer_len(chan, size);
+}
+
+EXPORT int k_channel_setbuf(k_channel* chan, char* rdbuffer, size_t size)
+{
+       return set_rdbuffer(chan, rdbuffer, size);
+}
+
+EXPORT char* k_channel_getbuf(k_channel* chan)
+{
+       return get_rdbuffer(chan);
+}
+
+EXPORT void k_channel_close(k_channel* chan)
+{
+       unregister_k_channel(chan,0);
+}
+
+k_channel* register_k_channel(char*           name,
+                              char*           listenhost,
+                              int             listenport,
+                              char*           banner,
+                              k_channel_event rdcallback,
+                              k_channel_event wrcallback,
+                              void*           context,
+                              int             type,
+                              struct in_addr  clientip,
+                              SOCK_T          s)
+{
+       k_channel* chan;
+       chan=k_malloc(sizeof(k_channel));
+
+       chan->name=name;
+       chan->listenhost=listenhost;
+       chan->listenport=listenport;
+       chan->banner=banner;
+       chan->rdcallback=rdcallback;
+       chan->wrcallback=wrcallback;
+       chan->context=context;
+       chan->type=type;
+       chan->clientip=clientip;
+       chan->linger=0;
+
+       chan->priv=k_malloc(sizeof(k_channel_priv));
+       chan->priv->SOCK=s;
+       chan->priv->state=CHAN_OPEN;
+       chan->priv->event=0;                            /* what is this for? */
+       chan->priv->rwmask=0;                           /* what is this for? */
+       chan->priv->rdbuffer=(char*)k_malloc(RDSIZE);   /* 40K per connection??!! */
+       chan->priv->rdbufpos=0;
+       chan->priv->rdbufsize=RDSIZE;
+       chan->priv->rdbufforeign=0;
+       chan->priv->wrbuffers=(wrbuffer*)k_malloc(WRSIZE*sizeof(wrbuffer));
+       chan->priv->wrbufpos=0;
+       chan->priv->wrbufsize=WRSIZE;
+       chan->priv->lingercount=LINGER_LOOPS;
+
+       if(1){
+               set_callback(chan, SETCB_RD);
+       }
+       if(chan->listenhost){
+               set_callback(chan, SETCB_WR);
+       }
+
+       chan->next=k_channels;
+       k_channels=chan;
+
+       return chan;
+}
+
+void unregister_k_channel(k_channel* chan, int now)
+{
+       int closing=CHAN_CLOSING(chan);
+       if(closing && !now) return;
+       int linger=chan->linger && !now;
+       chan->priv->state=linger? CHAN_LINGER: CHAN_CLOSE;
+       if(linger) un_set_callback(chan, SETCB_WR);
+       else       un_set_callback(chan, SETCB_WR | SETCB_RD);
+       if(closing) return;
+       chan_closed(chan);
+       if(chan->rdcallback)(*chan->rdcallback)(chan, chan->priv->rdbufpos, -1);
+       else
+       if(chan->wrcallback)(*chan->wrcallback)(chan, chan->priv->wrbufpos, -1);
+}
+
+void chan_closed(k_channel* chan)
+{
+       if(0) k_log_out("chan_closed %s", chan->name);
+       k_hashtable_remove(connecting_chans, chan->name);
+       k_hashtable_remove(current_chans,    chan->name);
+       if(0) k_channel_show_all();
+}
+
+void do_regular_things(void)
+{
+       tick_modules();
+       remove_dead_k_channels();
+}
+
+void tick_modules(void)
+{
+       int i;
+       for(i=0; i< nextmodule; i++){
+               k_module_tick_fn tf=modules[i].tick_fn;
+               if(tf) (*tf)();
+       }
+}
+
+void remove_dead_k_channels(void)
+{
+       int i;
+       k_channel* cn;
+       k_channel* cp=0;
+       k_channel* cc=k_channels;
+       while(cc){
+               cn=cc->next;
+               if( cc->priv->state==CHAN_CLOSE ||
+                  (cc->priv->state==CHAN_LINGER && !cc->priv->lingercount--)){
+                       un_set_callback(cc, SETCB_WR | SETCB_RD);
+                       if(!cp) k_channels=cn;
+                       else    cp->next  =cn;
+                       SOCKET_CLOSE(cc->priv->SOCK);
+                       for(i=0; i< cc->priv->wrbufpos; i++){
+                               k_free(cc->priv->wrbuffers[i].free);
+                       }
+                       k_free(cc->priv->wrbuffers);
+                       k_free(cc->priv->rdbuffer);
+                       k_free(cc->priv);
+                       k_free(cc->name);
+                       k_free(cc);
+               }
+               else cp=cc;
+               cc=cn;
+       }
+}
+
+k_channel* find_k_channel_for_socket(SOCK_T s)
+{
+       k_channel* chan;
+       for(chan=k_channels; chan; chan=chan->next){
+               if(chan->priv->SOCK==s) break;
+       }
+       return chan;
+}
+
+void show_open_channels(void)
+{
+       k_channel* chan=k_channels;
+       while(chan){
+               if(chan->priv->state==CHAN_OPEN  ){
+                       if(0) k_log_out("chan open      %x", chan->priv->SOCK);
+               }
+               if(chan->priv->state==CHAN_LINGER){
+                       if(0) k_log_out("chan lingering %x", chan->priv->SOCK);
+               }
+               if(chan->priv->state==CHAN_CLOSE ){
+                       if(0) k_log_out("chan closed    %x", chan->priv->SOCK);
+               }
+               chan=chan->next;
+       }
+}
+
+void readable_socket(k_channel* chan)
+{
+       if(chan->type==CHAN_LISTEN){
+               register_connections(chan);
+       }
+       else{
+               read_more_and_notify(chan);
+       }
+}
+
+void writeable_socket(k_channel* chan)
+{
+       write_more_and_notify(chan);
+}
+
+void exception_socket(k_channel* chan)
+{
+       if(0) k_log_out("exception_socket");
+       unregister_k_channel(chan,1);
+}
+
+void register_connections(k_channel* chan)
+{
+       SOCK_T             as=chan->priv->SOCK;
+       struct sockaddr_in iaddr;
+       struct sockaddr*   iaddrp=(struct sockaddr*)&iaddr;
+       socklen_t          size=sizeof(struct sockaddr_in);
+       while(1){
+               SOCK_T s;
+               int r=ACCEPT(as, iaddrp, &size, s);
+               GETERRNO(r);
+               if(r== -1){
+                       if(ERRNO==INTERRUPTED) break;
+                       if(ISNOTACTIVE(ERRNO)) break;
+                       log_net_err("accept", ERRNO);
+                       break;
+               }
+               SET_NON_BLOCKING(s);
+               SET_NO_DELAY(s);
+
+               if(chan->banner){
+                       SOCKET_WRITE(s, chan->banner, strlen(chan->banner));
+               }
+
+               k_channel* chann=register_k_channel(chan->name,
+                                                   0,
+                                                   chan->listenport,
+                                                   chan->banner,
+                                                   chan->rdcallback,
+                                                   chan->wrcallback,
+                                                   chan->context,
+                                                   CHAN_ESTABL,
+                                                   iaddr.sin_addr,
+                                                   s);
+               gen_conn_name(chann);
+               k_hashtable_set(current_chans, chann->name, chann);
+               if(chann->rdcallback) (*chann->rdcallback)(chann, 0, 0);
+       }
+}
+
+void read_more_and_notify(k_channel* chan)
+{
+       int pos=chan->priv->rdbufpos;
+       int size=SOCKET_READ(chan->priv->SOCK,
+                            chan->priv->rdbuffer +pos,
+                            chan->priv->rdbufsize-pos);
+       GETERRNO(size);
+       if(size==0){ size= -1; ERRNO=EPIPE; }
+       if(size== -1){
+               if(ERRNO==INTERRUPTED) return;
+               if(ISNOTACTIVE(ERRNO)) return;
+               log_net_err("socket_read", ERRNO);
+               unregister_k_channel(chan,1);
+               return;
+       }
+       if(CHAN_CLOSING(chan)) return;
+
+       chan->priv->rdbufpos+=size;
+
+       if(chan->rdcallback){
+               (*chan->rdcallback)(chan, chan->priv->rdbufpos, size);
+       }
+
+       if(chan->priv->rdbufpos==chan->priv->rdbufsize){
+               if(chan->priv->rdbufforeign){
+                       k_log_err("Run out of room in given buffer");
+                       k_log_err("rdbufsize=%d", chan->priv->rdbufsize);
+                       unregister_k_channel(chan,1);
+                       return;
+               }
+               chan->priv->rdbufsize*=2;
+               chan->priv->rdbuffer=k_realloc(chan->priv->rdbuffer,
+                                              chan->priv->rdbufsize);
+               if(0) k_log_out("rdbufsize=%d", chan->priv->rdbufsize);
+               // DoS vulnerability unless buffer size limited
+       }
+}
+
+void write_more_and_notify(k_channel* chan)
+{
+       if(!chan->priv->wrbufpos){
+               if(chan->name){
+                       k_hashtable_remove(connecting_chans, chan->name);
+                       k_hashtable_set(   current_chans,    chan->name, chan);
+               }
+               if(chan->wrcallback) (*chan->wrcallback)(chan, 0, 0);
+               if(!chan->priv->wrbufpos) un_set_callback(chan, SETCB_WR); // Eerrk?
+               return;
+       }
+
+       int writemax=MIN(WRITEMAX, chan->priv->wrbuffers[0].size);
+       int size=SOCKET_WRITE(chan->priv->SOCK,
+                             chan->priv->wrbuffers[0].base,
+                             writemax);
+       GETERRNO(size);
+       if(size==0){ size= -1; ERRNO=NOTACTIVE; }
+       if(size== -1){
+               if(ERRNO==INTERRUPTED) return;
+               if(ISNOTACTIVE(ERRNO)) return;
+               log_net_err("socket_write", ERRNO);
+               unregister_k_channel(chan,1);
+               return;
+       }
+
+       chop_from_wrbuffers(chan, size);
+
+       if(chan->wrcallback){
+               (*chan->wrcallback)(chan, chan->priv->wrbufpos, size);
+       }
+
+       if(!chan->priv->wrbufpos) un_set_callback(chan, SETCB_WR);
+}
+
+char* strnstr(char* haystack, size_t size, char* needle) // Errk!
+{
+       if(!needle || !*needle) return haystack;
+       char* h;
+       char* e=haystack+size;
+       for(h=haystack; h < e && *h; h++){
+               char* i=h;
+               char* n=needle;
+               while(i < e && *i && *n && *i==*n){ i++; n++; }
+               if(!*n) return h;
+       }
+       return 0;
+}
+
+char* chop_from_rdbuffer_div(k_channel* chan, char* divider)
+{
+       char* s=strnstr(chan->priv->rdbuffer, chan->priv->rdbufpos, divider);
+       if(!s) return 0;
+       *s=0;
+       size_t size=(s+1 - chan->priv->rdbuffer);
+       s+=strlen(divider);
+       char* chunk=k_memdup(chan->priv->rdbuffer, size);
+       char* e=chan->priv->rdbuffer+chan->priv->rdbufpos;
+       memmove(chan->priv->rdbuffer, s, e-s);
+       chan->priv->rdbufpos=e-s;
+       return chunk;
+}
+
+char* chop_from_rdbuffer_len(k_channel* chan, size_t size)
+{
+       if(!size || size > chan->priv->rdbufpos) return 0;
+       char* chunk=k_memdup(chan->priv->rdbuffer, size);
+       char* s=chan->priv->rdbuffer+size;
+       char* e=chan->priv->rdbuffer+chan->priv->rdbufpos;
+       memmove(chan->priv->rdbuffer, s, e-s);
+       chan->priv->rdbufpos=e-s;
+       return chunk;
+}
+
+int set_rdbuffer(k_channel* chan, char* rdbuffer, size_t size)
+{
+       if(chan->priv->rdbufforeign) return BUFFER_ALREADY_SET;
+       size_t pos=chan->priv->rdbufpos;
+       if(pos>=size){
+               memcpy(rdbuffer, chan->priv->rdbuffer, size);
+               char* s=chan->priv->rdbuffer+size;
+               char* e=chan->priv->rdbuffer+pos;
+               memmove(chan->priv->rdbuffer, s, e-s);
+               chan->priv->rdbufpos=e-s;
+               return BUFFER_FILLED;
+       }
+       memcpy(rdbuffer, chan->priv->rdbuffer, pos);
+       k_free(chan->priv->rdbuffer);
+       chan->priv->rdbuffer=rdbuffer;
+       chan->priv->rdbufsize=size;
+       chan->priv->rdbufforeign=1;
+       return BUFFER_SET;
+}
+
+char* get_rdbuffer(k_channel* chan)
+{
+       if(!chan->priv->rdbufforeign) return 0;
+       char* rdbuffer=chan->priv->rdbuffer;
+       chan->priv->rdbuffer=(char*)k_malloc(RDSIZE);
+       chan->priv->rdbufpos=0;
+       chan->priv->rdbufsize=RDSIZE;
+       chan->priv->rdbufforeign=0;
+       return rdbuffer;
+}
+
+void append_to_wrbuffers(k_channel* chan, char* base, size_t size, int free)
+{
+       if(chan->priv->wrbufpos==chan->priv->wrbufsize){
+               chan->priv->wrbufsize*=2;
+               chan->priv->wrbuffers=k_realloc(chan->priv->wrbuffers, 
+                                               chan->priv->wrbufsize *
+                                                         sizeof(wrbuffer));
+               if(0) k_log_out("wrbufsize=%d", chan->priv->wrbufsize);
+       }
+       chan->priv->wrbuffers[chan->priv->wrbufpos].base=base;
+       chan->priv->wrbuffers[chan->priv->wrbufpos].size=size;
+       chan->priv->wrbuffers[chan->priv->wrbufpos].free=free? base: 0;
+       chan->priv->wrbufpos++;
+}
+
+void chop_from_wrbuffers(k_channel* chan, size_t size)
+{
+       if(chan->priv->wrbuffers[0].size != size){
+               chan->priv->wrbuffers[0].base+=size;
+               chan->priv->wrbuffers[0].size-=size;
+       }
+       else{
+               k_free(chan->priv->wrbuffers[0].free);
+               chan->priv->wrbufpos--;
+               int i;
+               for(i=0; i< chan->priv->wrbufpos; i++){
+                       chan->priv->wrbuffers[i]=chan->priv->wrbuffers[i+1];
+               }
+       }
+}
+
+/* -------------------------------------------------------------------------- */
+
+SOCK_T make_listen_socket(int listenport)
+{
+       SOCK_T s;
+       int r=SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP, s);
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("socket", ERRNO);
+               return 0;
+       }
+       SET_NON_BLOCKING(s);
+       SET_REUSEADDR(s);
+
+       struct sockaddr_in iaddr;
+       struct sockaddr*   iaddrp=(struct sockaddr*)&iaddr;
+       iaddr=make_sockaddr_in(INADDR_ANY, listenport);
+
+       r=BIND(s, iaddrp, sizeof(iaddr));
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("bind", ERRNO);
+               SOCKET_CLOSE(s);
+               return 0;
+       }
+
+       return s;
+}
+
+SOCK_T make_connect_socket(void)
+{
+       SOCK_T s;
+       int r=SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP, s);
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("socket", ERRNO);
+               return 0;
+       }
+       SET_NON_BLOCKING(s);
+       SET_NO_DELAY(s);
+       return s;
+}
+
+int listen_socket(SOCK_T s, int listenport)
+{
+       if(0) k_log_out("Listening on port %d", listenport);
+       int r=LISTEN(s, 128);
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("listen", ERRNO);
+               SOCKET_CLOSE(s);
+               return -1;
+       }
+       return 0;
+}
+
+int connect_socket(SOCK_T s, char* listenhost, int listenport)
+{
+       if(0) k_log_out("Hanging on gethostbyname for %s", listenhost);
+       struct hostent* hostaddr=gethostbyname(listenhost);
+       if(!hostaddr){
+               SOCKET_CLOSE(s);
+               return -1;
+       }
+
+       struct sockaddr_in iaddr;
+       struct sockaddr*   iaddrp=(struct sockaddr*)&iaddr;
+       iaddr=make_sockaddr_in(0, listenport);
+       memcpy(&iaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
+
+       if(0) k_log_out("Connect: %s(%s):%d ",
+                           listenhost, inet_ntoa(iaddr.sin_addr), listenport);
+       do{
+               int r=CONNECT(s, iaddrp, sizeof(iaddr));
+               GETERRNO(r);
+               if(r== -1 && ERRNO==INTERRUPTED) continue;
+               if(r== -1 && !ISCONNECTING(ERRNO)){
+                       log_net_err("connect", ERRNO);
+                       SOCKET_CLOSE(s);
+                       return -1;
+               }
+               break;
+       }while(1);
+
+       return 0;
+}
+
+SOCK_T make_udp_listen_socket(int listenport)
+{
+       SOCK_T s;
+       int r;
+       struct sockaddr_in iaddr;
+       struct sockaddr*   iaddrp=(struct sockaddr*)&iaddr;
+
+       r=SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_UDP, s);
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("socket", ERRNO);
+               return 0;
+       }
+
+       SET_REUSEADDR(s);
+
+       iaddr=make_sockaddr_in(INADDR_ANY, listenport);
+
+       r=BIND(s, iaddrp, sizeof(iaddr));
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("bind", ERRNO);
+               SOCKET_CLOSE(s);
+               return 0;
+       }
+
+       return s;
+}
+
+EXPORT SOCK_T make_udp_send_socket(char* listenhost, int listenport)
+{
+       SOCK_T s;
+       int r;
+       struct sockaddr_in iaddr;
+       struct sockaddr*   iaddrp=(struct sockaddr*)&iaddr;
+
+       r=SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_UDP, s);
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("socket", ERRNO);
+               return 0;
+       }
+
+       struct hostent* hostaddr=gethostbyname(listenhost);
+       if(!hostaddr){
+               log_net_err("gethostbyname", h_errno);
+               SOCKET_CLOSE(s);
+               return 0;
+       }
+
+       iaddr=make_sockaddr_in(0, listenport);
+       memcpy(&iaddr.sin_addr, hostaddr->h_addr, hostaddr->h_length);
+
+       r=CONNECT(s, iaddrp, sizeof(iaddr));
+       GETERRNO(r);
+       if(r== -1){
+               log_net_err("connect", ERRNO);
+               SOCKET_CLOSE(s);
+               return 0;
+       }
+
+       return s;
+}
+
+struct sockaddr_in make_sockaddr_in(unsigned long address, unsigned int port)
+{
+       struct sockaddr_in iaddr;
+       struct sockaddr*   iaddrp=(struct sockaddr*)&iaddr;
+       memset(iaddrp, 0, sizeof(iaddr));
+       iaddr.sin_addr.s_addr=htonl(address);
+       iaddr.sin_port       =htons(port);
+       iaddrp->sa_family=AF_INET;
+       return iaddr;
+}
+
+void log_net_err(char* where, int e)
+{
+       if(0) k_log_out("Network (%s): %s (%d)", where, str_error(e), e);
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_file_stat(char*        basedir,
+                        char*        filename,
+                        k_file_event callback,
+                        void*        context)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename);
+       char* fullname=tmpbuf;
+
+       k_stat kstat={0,0,0,0};
+
+       stat_only(fullname, &kstat);
+       (*callback)(basedir, filename, 0, 0, kstat, context);
+}
+
+EXPORT void k_file_read(char*        basedir,
+                        char*        filename,
+                        size_t       mmapsize,
+                        size_t       size,
+                        k_file_event callback,
+                        void*        context)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename);
+       char* fullname=tmpbuf;
+
+       FILE_T filehandle;
+       k_stat kstat={0,0,0,0};
+
+       int write=!!size;
+       if(write && !ensure_dir(fullname)){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+
+       stat_only(fullname, &kstat);
+       if(!kstat.type && !write){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+       filehandle=open_only(fullname, write);
+       if(!filehandle){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+       if(!kstat.type || (write && kstat.size!=size)){
+               if(ftruncate(filehandle, size)){
+                       FCLOSE(filehandle);
+                       (*callback)(basedir, filename, 0, 0, kstat, context);
+                       return;
+               }
+       }
+       FCLOSE(filehandle);
+
+       if(!write) size=kstat.size;
+       else       kstat.size=size;
+
+       if(size>MAX_FILESIZE){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+       int dommap=(size >= mmapsize);
+
+       int prot=(write? PROT_WRITE: 0)|PROT_READ;
+
+       char* data;
+
+       if(size==0) data=dommap? dummy_empty_data: k_malloc(1);
+       else
+       if(dommap)  data=mmap_name(  0, size, prot, MAP_SHARED, fullname, 0);
+       else        data=mmap_malloc(0, size, prot, MAP_SHARED, fullname, 0);
+
+       if(data==MAP_FAILED){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+       (*callback)(basedir, filename, data, dommap, kstat, context);
+}
+
+EXPORT int k_file_sync(char* data, size_t size)
+{
+       int r=msync(data, size, MS_ASYNC);
+       return !r;
+}
+
+EXPORT void k_file_write(char*        basedir,
+                         char*        filename,
+                         char*        data,
+                         size_t       datalength,
+                         k_file_event callback,
+                         void*        context)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, filename);
+       char* fullname=tmpbuf;
+
+       FILE_T filehandle;
+       k_stat kstat={0,0,0,0};
+
+       if(!ensure_dir(fullname)){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+
+       filehandle=open_only(fullname, 1);
+       if(!filehandle){
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+       if(datalength!=0) kstat.size=WRITEFILE(filehandle, data, datalength);
+       if(kstat.size== -1){
+               FCLOSE(filehandle);
+               (*callback)(basedir, filename, 0, 0, kstat, context);
+               return;
+       }
+       FCLOSE(filehandle);
+       (*callback)(basedir, filename, data, 0, kstat, context);
+}
+
+EXPORT void k_dir_list(char*        basedir,
+                       char*        dirname,
+                       char*        pattern[],
+                       char*        format[],
+                       k_file_event callback,
+                       void*        context)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%s/%s", basedir, dirname);
+       char* fullname=k_strdup(tmpbuf);
+
+       k_stat kstat={ STAT_D, 0, 0, 0 };
+       char* buf=tmpbuf;
+       int   size=TMPBUFSIZE;
+       int i;
+       for(i=0; format[i]; i++){
+               int n=dir_list(buf, size, fullname, pattern[i], format[i]);
+               if(n== -1){
+                       (*callback)(basedir, dirname, 0, 0, kstat, context);
+                       k_free(fullname);
+                       return;
+               }
+               buf+=n; size-=n;
+       }
+       kstat.size=TMPBUFSIZE-size;
+       (*callback)(basedir, dirname, k_strdup(tmpbuf), 0, kstat, context);
+       k_free(fullname);
+}
+
+int dir_list(char* buf, int size, char* fullname, char* pattern, char* format)
+{
+       DIR* d=opendir(fullname);
+       if(!d) return -1;
+
+       char* b=buf;
+       int   s=size;
+       *b=0;
+       struct dirent* de;
+       while((de=readdir(d))){
+
+               char* name=de->d_name;
+               if(*name=='.') continue;
+
+               if(pattern && *pattern &&
+                         !k_string_matches_pattern(name, pattern)) continue;
+
+               int n=snprintf(b, s, format, name, name, name, name, name);
+               if(n< 0 || n>=s){ closedir(d); return -1; }
+               b+=n; s-=n;
+       }
+       closedir(d);
+       return size-s;
+}
+
+int ensure_dir(char* fullname)
+{
+       char* e=strrchr(fullname, '/');
+       if(!e) return 1;
+       *e=0;
+       k_stat kstat={0,0,0,0};
+       stat_only(fullname, &kstat);
+       if(kstat.type!=STAT_D){
+               MKDIR(fullname);
+               stat_only(fullname, &kstat);
+       }
+       *e='/';
+       return kstat.type==STAT_D;
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT int k_string_matches_pattern(char* string, char* pattern)
+{
+       char patt[32];
+       strncpy(patt, pattern, 32);
+       patt[31]=0;
+       pattern=patt;
+       if(*pattern=='{'){
+               while(1){
+                       pattern++;
+                       char* e=strpbrk(pattern, ",}");
+                       if(!e) return 0;
+                       char x=*e; *e=0;
+                       if(k_string_matches_pattern(string, pattern)) return 1;
+                       *e=x;
+                       pattern=e;
+               }
+       }
+       else
+       if(*pattern=='*'){
+               pattern++;
+               return k_string_ends_with(string, pattern);
+       }
+       return 0;
+}
+
+EXPORT int k_string_ends_with(char* string, char* postfix)
+{
+       char* i=string+strlen(string)-strlen(postfix);
+       if(i<string) return 0;
+       return !strcmp(i, postfix);
+}
+
+EXPORT void k_string_url_decode(char* s)
+{
+       char* t=s;
+       while(*s){
+               if(s[0]=='%' && isxdigit(s[1]) && isxdigit(s[2])){
+                       *t=hex_to_int(s[1])*16+hex_to_int(s[2]);
+                       s+=3; t++;
+               }
+               else{
+                       *t=*s;
+                       s++; t++;
+               }
+       }
+       *t=0;
+}
+
+char hex_to_int(char c)
+{
+       return
+       (c >= '0' && c <= '9')? c-'0':
+       (c >= 'A' && c <= 'F')? c-'A'+10:
+       (c >= 'a' && c <= 'f')? c-'a'+10: 0;
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_log_out(char* format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       if(LOG_TO_STD){
+               VPRINTFOUT(format,ap);
+               PRINTFOUT("\n");
+               FFLUSH(stdout);
+       }
+       else{
+               vsnprintf(logbuf, LOGBUFSIZE,format,ap);
+               *(logbuf+LOGBUFSIZE-2)='\n';
+               *(logbuf+LOGBUFSIZE-1)=0;
+               write_to_logfile_cern_style(logbuf,0);
+       }
+       va_end(ap);
+}
+
+EXPORT void k_log_err(char* format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       if(LOG_TO_STD){
+               VPRINTFERR(format,ap);
+               PRINTFERR("\n");
+               FFLUSH(stderr);
+       }
+       else{
+               vsnprintf(logbuf, LOGBUFSIZE,format,ap);
+               *(logbuf+LOGBUFSIZE-2)='\n';
+               *(logbuf+LOGBUFSIZE-1)=0;
+               write_to_logfile_cern_style(logbuf,1);
+       }
+       va_end(ap);
+}
+
+EXPORT void k_fatal(char* format, ...)
+{
+       va_list ap;
+       va_start(ap, format);
+       k_log_out(format, ap);
+       va_end(ap);
+       k_log_out("Terminating Cilux");
+       k_log_out("---------------------");
+       EXIT(1);
+}
+
+void write_to_logfile_cern_style(char* text, int error)
+{
+       if(!logfile) return;
+
+       time_t now=time(0);
+       struct tm* tm;
+       int        z;
+       if(log_gmt){
+               tm=gmtime(&now);
+               z=0;
+       }
+       else{
+               tm=localtime(&now);
+               z=TIMEZONE(tm)/60;
+       }
+       char sgn;
+       if(z >=0){ sgn='+';        }
+       else{      sgn='-'; z= -z; }
+
+       char cern[64];
+       char date[64];
+       strftime(cern, sizeof(cern), CERNDATE, tm);
+#ifdef SHOW_ZONE
+       snprintf(date, sizeof(date), "%s %c%02d%02d", cern, sgn, z/60, z%60);
+#else
+       snprintf(date, sizeof(date), "%s",            cern);
+#endif
+       FPRINTF(logfile, "[%s|%s] %s%s\n", date, k_ciux,
+                                          error? "Warning: ": "", text);
+       FFLUSH(logfile);
+}
+
+/* -------------------------------------------------------------------------- */
+
+#define EXAMPLE_DATE   Fri, 04 Feb 2005 12:14:48 GMT
+#define RFC1123STRF   "%a, %d %b %Y %H:%M:%S GMT"
+#define RFC1123SSCANF "%3s, %d %3s %4d %2d:%2d:%2d GMT"
+
+static char b[100];
+
+EXPORT char* k_time_to_rfc(time_t t)
+{
+       strftime(b, sizeof(b), RFC1123STRF, gmtime(&t));
+       return b;
+}
+
+EXPORT char* k_time_to_rfc_relative(int plus)
+{
+       time_t t=time(0)+plus;
+       strftime(b, sizeof(b), RFC1123STRF, gmtime(&t));
+       return b;
+}
+
+EXPORT time_t k_time_from_rfc(char* s)
+{
+       if(!s || strlen(s)!=29) return -1;
+       struct tm tm; tm.tm_wday=0; tm.tm_yday=0; tm.tm_isdst=0;
+       char wd[32], mn[32];
+       int r=sscanf(s, RFC1123SSCANF, wd, &tm.tm_mday, mn, &tm.tm_year,
+                                      &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+       if(r!=7) return -1;
+       tm.tm_mon=(int)k_hashtable_get(monthhash, mn);
+       if(tm.tm_mon) tm.tm_mon -=1;
+       tm.tm_year-=1900;
+       return MKTIME(&tm);
+}
+
+EXPORT void k_time_get_now(char* buf, size_t size, char* format)
+{
+       time_t now=time(0);
+       struct tm* tm;
+       int gmt=1;
+       if(gmt){
+               tm=gmtime(&now);
+       }
+       else{
+               tm=localtime(&now);
+       }
+       strftime(buf, size, format, tm);
+}
+
+/* -------------------------------------------------------------------------- */
+
+typedef struct hash_item hash_item;
+
+typedef struct hashtable{ struct hashtable* next;
+       char*       name;
+       int         buckets;
+       int         size;
+       hash_item** lists;
+       int         ignorecase;
+} hashtable;
+
+struct hash_item{ hash_item* next;
+       char*        key;
+       void*        val;
+       int          free;
+       k_hashtable* sub;
+};
+
+/* -------------------------------------------------------------------------- */
+
+#define STRCMPCASE(tab, listkey, key)\
+((tab)->ignorecase? strcasecmp((listkey), (key)): strcmp((listkey), (key)))
+#define BX 30
+#define WARN_SIZE(h)\
+if((h)->size && !((h)->size % BX)) k_log_out("%s# %d", (h)->name, (h)->size);
+
+EXPORT k_hashtable* k_hashtable_new(char* name, int ignorecase)
+{
+       hashtable* tab;
+       tab=k_malloc(sizeof(hashtable));
+       tab->name=name;
+       tab->buckets=BX;
+       tab->size=0;
+       tab->lists=k_malloc((tab->buckets)*sizeof(hash_item*));
+       tab->ignorecase=ignorecase;
+       tab->next=0;
+       int i; for(i=0; i< tab->buckets; i++) tab->lists[i]=0;
+       return (k_hashtable*)tab;
+}
+
+k_hashtable* k_hashtable_dup2(k_hashtable* tab, int deep)
+{
+       if(!tab) return 0;
+       hashtable* tah=(hashtable*)tab;
+       hashtable* tad;
+       tad=k_malloc(sizeof(hashtable));
+       tad->name=tah->name;
+       tad->buckets=tah->buckets;
+       tad->size=tah->size;
+       tad->lists=k_malloc((tah->buckets)*sizeof(hash_item*));
+       tad->ignorecase=tah->ignorecase;
+       tad->next=0;
+       hash_item** lisp=0;
+       hash_item** lisd=0;
+       int i;
+       for(i=0; i< tah->buckets; i++){
+               for(lisp=&tah->lists[i], lisd=&tad->lists[i];
+                   (*lisp);
+                   lisp=&(*lisp)->next, lisd=&(*lisd)->next){
+
+                       (*lisd)=k_malloc(sizeof(hash_item));
+                       (*lisd)->key=k_strdup((*lisp)->key);
+                       (*lisd)->val=0;
+                       (*lisd)->free=0;
+                       (*lisd)->sub=0;
+                       if((*lisp)->val){
+                               (*lisd)->val=k_strdup((*lisp)->val);
+                               (*lisd)->free=1;
+                       }
+                       if(deep && (*lisp)->sub){
+                               (*lisd)->sub=k_hashtable_dup2((*lisp)->sub, 1);
+                       }
+                       if(!(*lisd)->val && !(*lisd)->sub){
+                               (*lisd)->val="...";
+                       }
+               }
+               (*lisd)=0;
+       }
+       tad->next=(hashtable*)k_hashtable_dup2(tab->next, deep);
+       return (k_hashtable*)tad;
+}
+
+EXPORT k_hashtable* k_hashtable_dup(k_hashtable* tab)
+{
+       return k_hashtable_dup2(tab, 0);
+}
+
+EXPORT k_hashtable* k_hashtable_deep(k_hashtable* tab)
+{
+       return k_hashtable_dup2(tab, 1);
+}
+
+EXPORT k_hashtable* k_hashtable_dip(k_hashtable* tab, char* includes[])
+{
+       if(!tab) return 0;
+       hashtable* tah=(hashtable*)tab;
+       hashtable* tad;
+       tad=(hashtable*)k_hashtable_new(tah->name, tah->ignorecase);
+       int i;
+       for(i=0; includes[i]; i++){
+               char* in=k_hashtable_get((k_hashtable*)tah, includes[i]);
+               if(!in) continue;
+               in=k_strdup(in);
+               k_hashtable_put((k_hashtable*)tad, includes[i], in);
+       }
+       tad->next=(hashtable*)k_hashtable_dip(tab->next, includes);
+       return (k_hashtable*)tad;
+}
+
+EXPORT void k_hashtable_merge(k_hashtable* tab, k_hashtable* tam)
+{
+       hashtable* tan=(hashtable*)tam;
+       hash_item** lisp;
+       int i;
+       for(i=0; i< tan->buckets; i++){
+               for(lisp=&tan->lists[i]; (*lisp); lisp=&(*lisp)->next){
+                       char*        key =(*lisp)->key;
+                       void*        val =(*lisp)->val;
+                       int          free=(*lisp)->free;
+                       k_hashtable* sub =(*lisp)->sub;
+                       if(!*key && free) continue;
+                       if(val){
+                               k_hashtable_put(tab, key, k_strdup(val));
+                       }
+                       if(sub){
+                               k_hashtable_sub(tab, key, k_hashtable_dup(sub));
+                       }
+               }
+       }
+}
+
+void k_hashtable_set_put(k_hashtable* tab, char* key, void* val, int free)
+{
+       if(!(tab && key && val)) return;
+       hashtable* tah=(hashtable*)tab;
+       hash_item** lisp;
+       lisp=&tah->lists[string_hash(key) % tah->buckets];
+       while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+               lisp=&(*lisp)->next;
+       }
+       if(!(*lisp)){
+               (*lisp)=k_malloc(sizeof(hash_item));
+               (*lisp)->key=k_strdup(key);
+               (*lisp)->val=val;
+               (*lisp)->free=free;
+               (*lisp)->sub=0;
+               (*lisp)->next=0;
+               tah->size++;
+               WARN_SIZE(tah);
+       }
+       else{
+               if((*lisp)->free) k_free((*lisp)->val);
+               k_hashtable_delete((*lisp)->sub);
+               (*lisp)->val=val;
+               (*lisp)->free=free;
+               (*lisp)->sub=0;
+       }
+}
+
+EXPORT void k_hashtable_set(k_hashtable* tab, char* key, void* val)
+{
+       k_hashtable_set_put(tab, key, val, 0);
+}
+
+EXPORT void k_hashtable_put_int(k_hashtable* tab, char* key, int val)
+{
+       snprintf(tmpbuf, TMPBUFSIZE, "%d", val);
+       k_hashtable_set_put(tab, key, k_strdup(tmpbuf), 1);
+}
+
+EXPORT void k_hashtable_put(k_hashtable* tab, char* key, void* val)
+{
+       k_hashtable_set_put(tab, key, val, 1);
+}
+
+EXPORT void k_hashtable_put_dup(k_hashtable* tab, char* key, char* val)
+{
+       k_hashtable_set_put(tab, key, k_strdup(val), 1);
+}
+
+EXPORT void k_hashtable_sub(k_hashtable* tab, char* key, k_hashtable* sub)
+{
+       if(!(tab && key && sub)) return;
+       sub->next=0;
+       hashtable* tah=(hashtable*)tab;
+       hash_item** lisp;
+       lisp=&tah->lists[string_hash(key) % tah->buckets];
+       while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+               lisp=&(*lisp)->next;
+       }
+       if(!(*lisp)){
+               (*lisp)=k_malloc(sizeof(hash_item));
+               (*lisp)->key=k_strdup(key);
+               (*lisp)->val=0;
+               (*lisp)->free=0;
+               (*lisp)->sub=sub;
+               (*lisp)->next=0;
+               tah->size++;
+               WARN_SIZE(tah);
+       }
+       else{
+               k_hashtable* val =(*lisp)->val;
+               k_hashtable* head=(*lisp)->sub;
+               if(!head){
+                       if(val) k_log_err("key '%s' in use: no sub!", key);
+                       else (*lisp)->sub=sub;
+               }
+               else{
+                       while(head->next) head=head->next;
+                       head->next=sub;
+               }
+       }
+}
+
+EXPORT void* k_hashtable_get(k_hashtable* tab, char* key)
+{
+       if(!(tab && key)) return 0;
+       hashtable* tah=(hashtable*)tab;
+       hash_item* list;
+       list=tah->lists[string_hash(key) % tah->buckets];
+       while(list && STRCMPCASE(tah, list->key, key)){
+               list=list->next;
+       }
+       return list? (list->val? list->val: list->sub): 0;
+}
+
+EXPORT int k_hashtable_get_int(k_hashtable* tab, char* key)
+{
+       char* val=k_hashtable_get(tab, key);
+       return val? atoi(val): 0;
+}
+
+EXPORT char* k_hashtable_get_dup(k_hashtable* tab, char* key)
+{
+       char* v=k_hashtable_get(tab, key);
+       return k_strdup(v);
+}
+
+EXPORT int k_hashtable_is(k_hashtable* tab, char* key, char* val)
+{
+       char* v=k_hashtable_get(tab, key);
+       return (v && val && !strcmp(v, val));
+}
+
+EXPORT int k_hashtable_isi(k_hashtable* tab, char* key, char* val)
+{
+       char* v=k_hashtable_get(tab, key);
+       return (v && val && !strcasecmp(v, val));
+}
+
+EXPORT int k_hashtable_isn(k_hashtable* tab, char* key, char* val, size_t size)
+{
+       char* v=k_hashtable_get(tab, key);
+       return (v && val && !strncmp(v, val, size));
+}
+
+EXPORT void* k_hashtable_extract(k_hashtable* tab, char* key)
+{
+       hashtable*  tah=(hashtable*)tab;
+       hash_item** lisp;
+       hash_item*  next;
+       lisp=&tah->lists[string_hash(key) % tah->buckets];
+       while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+               lisp=&(*lisp)->next;
+       }
+       void* val=0;
+       if((*lisp)){
+               next=  (*lisp)->next;
+               k_free((*lisp)->key);
+               val=   (*lisp)->val;
+               if(!val) val=(*lisp)->sub;
+               k_free((*lisp));
+               (*lisp)=next;
+               tah->size--;
+               WARN_SIZE(tah);
+       }
+       return val;
+}
+
+EXPORT void k_hashtable_remove(k_hashtable* tab, char* key)
+{
+       hashtable*  tah=(hashtable*)tab;
+       hash_item** lisp;
+       hash_item*  next;
+       lisp=&tah->lists[string_hash(key) % tah->buckets];
+       while((*lisp) && STRCMPCASE(tah, (*lisp)->key, key)){
+               lisp=&(*lisp)->next;
+       }
+       if((*lisp)){
+               next=  (*lisp)->next;
+               k_free((*lisp)->key);
+               if((*lisp)->free) k_free((*lisp)->val);
+               k_hashtable_delete((*lisp)->sub);
+               k_free((*lisp));
+               (*lisp)=next;
+               tah->size--;
+               WARN_SIZE(tah);
+       }
+}
+
+EXPORT void k_hashtable_delete(k_hashtable* tab)
+{
+       if(!tab) return;
+       hashtable* tah=(hashtable*)tab;
+       hash_item* list;
+       hash_item* next;
+       int i;
+       for(i=0; i< tah->buckets; i++){
+               list=tah->lists[i];
+               while(list){
+                       next=list->next;
+                       k_free(list->key);
+                       if(list->free) k_free(list->val);
+                       k_hashtable_delete(list->sub);
+                       k_free(list);
+                       list=next;
+               }
+       }
+       k_free(tah->lists);
+       k_hashtable_delete((k_hashtable*)tah->next);
+       k_free(tah);
+}
+
+EXPORT void k_hashtable_apply(k_hashtable*         tab,
+                              k_hashtable_apply_fn fn,
+                              void*                arg)
+{
+       if(!tab) return;
+       hashtable* tah=(hashtable*)tab;
+       hash_item** lisp;
+       int i;
+       for(i=0; i< tah->buckets; i++){
+               for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){
+                       if((*lisp)->val){
+                               (*fn)(arg, (*lisp)->key, (*lisp)->val);
+                       }
+                       else{
+                               (*fn)(arg, (*lisp)->key, (*lisp)->sub);
+                       }
+               }
+       }
+}
+
+void k_hashtable_show2(k_hashtable* tab, int chars)
+{
+       if(!tab) return;
+       hashtable* tah=(hashtable*)tab;
+       if(!chars) k_log_out("%s size=%d", tah->name, tah->size);
+       hash_item** lisp;
+       int i;
+       for(i=0; i< tah->buckets; i++){
+               for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){
+                       if(chars){
+                               if(*((*lisp)->key)){
+                                       k_log_out("%s %s",
+                                          (*lisp)->key, (char*)((*lisp)->val));
+                               }
+                       }
+                       else{
+                               k_log_out("buck %d key '%s' val %x sub %x",
+                                i, (*lisp)->key, (*lisp)->val, (*lisp)->sub);
+                       }
+               }
+       }
+}
+
+EXPORT void k_hashtable_show(k_hashtable* tab)
+{
+       k_hashtable_show2(tab,0);
+}
+
+EXPORT void k_hashtable_show_chars(k_hashtable* tab)
+{
+       k_hashtable_show2(tab,1);
+}
+
+int k_hashtable_snprintf_xi(k_hashtable* tab,
+                            char*        buf,
+                            size_t       size,
+                            char*        includes[],
+                            char*        excludes[],
+                            int          indent)
+{
+       if(!tab){ if(size) *buf=0; return 0; }
+       char ind[]="                                                         ";
+       ind[indent]=0;
+       hashtable* tah=(hashtable*)tab;
+       *buf=0;
+       char* at=buf;
+       int   ln=size;
+       hash_item** lisp;
+       int i;
+       for(i=0; i< tah->buckets; i++){
+       for(lisp=&tah->lists[i]; (*lisp); lisp=&(*lisp)->next){
+               if(!*((*lisp)->key)) continue;
+               if(check_i((*lisp)->key, includes, tah->ignorecase)) continue;
+               if(check_x((*lisp)->key, excludes, tah->ignorecase)) continue;
+               int n;
+               if((*lisp)->val){
+                       n=snprintf(at, ln, "%s%s %s" CRLF, ind, (*lisp)->key,
+                                                       (char*)((*lisp)->val));
+                       if(n<  0) return n;
+                       if(n>=ln) return size;
+                       at+=n; ln-=n;
+               }
+               else{
+                       k_hashtable* sub=(*lisp)->sub;
+                       do{
+                               n=snprintf(at, ln, "%s%s" CRLF,
+                                                          ind, (*lisp)->key);
+                               if(n<  0) return n;
+                               if(n>=ln) return size;
+                               at+=n; ln-=n;
+       
+                               n=k_hashtable_snprintf_xi(sub, at, ln, 0, 0,
+                                                         indent+8);
+                               if(n<  0) return n;
+                               if(n>=ln) return size;
+                               at+=n; ln-=n;
+
+                       }while((sub=sub->next));
+               }
+       }
+       }
+       return size-ln;
+}
+
+EXPORT int k_hashtable_snprintf(k_hashtable* tab, char* buf, size_t size)
+{
+       return k_hashtable_snprintf_xi(tab, buf, size, 0, 0, 0);
+}
+
+EXPORT int k_hashtable_snprintf_i(k_hashtable* tab,
+                                  char*        buf,
+                                  size_t       size,
+                                  char*        includes[])
+{
+       return k_hashtable_snprintf_xi(tab, buf, size, includes, 0, 0);
+}
+
+EXPORT int k_hashtable_snprintf_x(k_hashtable* tab,
+                                  char*        buf,
+                                  size_t       size,
+                                  char*        excludes[])
+{
+       return k_hashtable_snprintf_xi(tab, buf, size, 0, excludes, 0);
+}
+
+int check_i(char* key, char* includes[], int ignorecase)
+{
+       if(!includes) return 0;
+       int e;
+       for(e=0; includes[e]; e++){
+               if(ignorecase?  !strcasecmp(key, includes[e]):
+                               !strcmp(    key, includes[e]) ) break;
+       }
+       return !includes[e];
+}
+
+int check_x(char* key, char* excludes[], int ignorecase)
+{
+       if(!excludes) return 0;
+       int e;
+       for(e=0; excludes[e]; e++){
+               if(ignorecase?  !strcasecmp(key, excludes[e]):
+                               !strcmp(    key, excludes[e]) ) break;
+       }
+       return !!excludes[e];
+}
+
+unsigned int string_hash(char* p)
+{
+       unsigned int h=0;
+       while(*p) h=(h<<5)-h+tolower(*p++);
+       return h;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+
diff --git a/src/platform/linux/cilux.c b/src/platform/linux/cilux.c
new file mode 100644 (file)
index 0000000..009d621
--- /dev/null
@@ -0,0 +1,73 @@
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <platform.h>
+#include <container.h>
+#include "version.h"
+
+/* -------------------------------------------------------------------------- */
+
+static void re_exec_as_other();
+
+/* -------------------------------------------------------------------------- */
+
+int main(int argc, char* argv[])
+{
+       c_init(0, cilux_version, cilux_ciux, 0);
+       re_exec_as_other();
+       c_run(0);
+
+       return 0;
+}
+
+void re_exec_as_other()
+{
+       char* other="other";
+       uid_t uid=0;
+       gid_t gid=0;
+       if(getuid()==0){
+               struct passwd* pw=getpwnam(other);
+               if(!pw){ printf("fail: getpwnam(\"%s\");\n", other); exit(1); }
+               uid=pw->pw_uid;
+               gid=pw->pw_gid;
+       }
+
+       pid_t pid=fork();
+       switch(pid){
+               case 0:
+                       break;
+               case -1:
+                       printf("Failed to fork\n");
+                       exit(1);
+               default:
+                       printf("pid %d\n", pid);
+                       exit(0);
+       }
+       setsid();
+
+       if(getuid()==0){
+               if(setgroups(0,0)== -1){
+                       printf("fail: setgroups(0,0)\n");
+                       exit(1);
+               }
+               if(setgid(gid)== -1){
+                       printf("fail: setgid(%d)\n", gid);
+                       exit(1);
+               }
+               if(initgroups(other, gid)== -1){
+                       printf("fail: initgroups(\"%s\",%d)\n", other, gid);
+                       exit(1);
+               }
+               if(setuid(uid)== -1){
+                       printf("fail: setuid(%d)\n", uid);
+                       exit(1);
+               }
+       }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
diff --git a/src/platform/linux/osapi.c b/src/platform/linux/osapi.c
new file mode 100644 (file)
index 0000000..96d64b7
--- /dev/null
@@ -0,0 +1,275 @@
+
+/* -------------------------------------------------------------------------- */
+
+#include <errno.h>
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+
+/* -------------------------------------------------------------------------- */
+
+static fd_set rd_fd_set;
+static fd_set wr_fd_set;
+static fd_set ex_fd_set;
+
+/* -------------------------------------------------------------------------- */
+
+static void signal_terminate(int);
+static void signal_alarm(int);
+static void signal_other(int);
+static void set_timeval_ms(struct timeval* t, int ms);
+
+/* -------------------------------------------------------------------------- */
+
+void init_thread(void)
+{
+       signal(SIGTERM, signal_terminate);
+       signal(SIGINT,  signal_terminate);
+       signal(SIGQUIT, signal_terminate);
+       signal(SIGALRM, signal_alarm);
+       signal(SIGPIPE, signal_other);
+       signal(SIGCHLD, SIG_IGN);
+       signal(SIGHUP,  SIG_IGN);
+       signal(SIGUSR1, SIG_IGN);
+       signal(SIGUSR2, SIG_IGN);
+}
+
+void init_gl(void)
+{
+}
+
+void init_net(void)
+{
+       FD_ZERO(&rd_fd_set);
+       FD_ZERO(&wr_fd_set);
+       FD_ZERO(&ex_fd_set);
+}
+
+/* -------------------------------------------------------------------------- */
+
+OTHER_THREAD void signal_terminate(int signum)
+{
+       c_running(0);
+}
+
+OTHER_THREAD void signal_alarm(int signum)
+{
+       c_signal(signum);
+}
+
+OTHER_THREAD void signal_other(int signum)
+{
+       c_signal(signum);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void set_callback(k_channel* chan, int rdwr)
+{
+       if(rdwr & SETCB_RD) FD_SET(chan->priv->SOCK, &rd_fd_set);
+       if(rdwr & SETCB_WR) FD_SET(chan->priv->SOCK, &wr_fd_set);
+}
+
+void un_set_callback(k_channel* chan, int rdwr)
+{
+       if(rdwr & SETCB_RD) FD_CLR(chan->priv->SOCK, &rd_fd_set);
+       if(rdwr & SETCB_WR) FD_CLR(chan->priv->SOCK, &wr_fd_set);
+}
+
+void poller(int no_block)
+{
+       fd_set rd=rd_fd_set;
+       fd_set wr=wr_fd_set;
+       fd_set ex=ex_fd_set;
+
+       k_channel* chan;
+
+       int highest=0;
+       for(chan=k_channels; chan; chan=chan->next){
+               if(chan->priv->state==CHAN_CLOSE) continue;
+               if(highest < chan->priv->SOCK) highest=chan->priv->SOCK;
+       }
+
+       struct timeval  t;
+       struct timeval* tp=&t;
+       set_timeval_ms(tp, no_block? 0: LOOP_TICK);
+
+       if(highest==0){
+               select(0, 0, 0, 0, tp);
+               next_keys();
+               if(!no_block) do_regular_things();
+               return;
+       }
+
+       int len=select(highest+1, &rd, &wr, &ex, tp);
+       GETERRNO(len);
+
+       next_keys();
+
+       if(len==0){
+               do_regular_things();
+               return;
+       }
+       if(len== -1){
+               if(ERRNO==INTERRUPTED) return;
+               log_net_err("select", ERRNO);
+               sleep(1);
+               do_regular_things();
+               return;
+       }
+
+       for(chan=k_channels; chan; chan=chan->next){
+               if(FD_ISSET(chan->priv->SOCK, &ex)){
+                       exception_socket(chan);
+                       continue;
+               }
+               if(FD_ISSET(chan->priv->SOCK, &wr)){
+                       int err; socklen_t len=sizeof(int);
+                       if(getsockopt(chan->priv->SOCK,
+                                     SOL_SOCKET,
+                                     SO_ERROR, &err, &len) || err){
+                               exception_socket(chan);
+                               continue;
+                       }
+                       else{
+                               writeable_socket(chan);
+                       }
+               }
+               if(FD_ISSET(chan->priv->SOCK, &rd)){
+                       readable_socket(chan);
+               }
+       }
+}
+
+void set_timeval_ms(struct timeval* t, int ms)
+{
+       t->tv_sec=ms/1000;
+       t->tv_usec=(ms-(t->tv_sec)*1000)*1000;
+}
+
+char* str_error(int e)
+{
+       return strerror(e);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void stat_only(char* fullname, k_stat* kstat)
+{
+       kstat->type=0;
+       struct stat s;
+       if(stat(fullname, &s)) return;
+       kstat->type=s.st_mode & 0170000;
+       kstat->size=s.st_size;
+       kstat->time=s.st_mtime;
+       kstat->perm=s.st_mode & 0007777;
+}
+
+FILE_T stat_open(char* fullname, k_stat* kstat)
+{
+       stat_only(fullname, kstat);
+       if(!kstat->type) return 0;
+       FILE_T filehandle=open(fullname, O_RDONLY);
+       if(filehandle<0) return 0;
+       return filehandle;
+}
+
+FILE_T open_only(char* fullname, int wr)
+{
+       int rw=wr? O_RDWR|O_CREAT|O_TRUNC: O_RDONLY;
+       FILE_T filehandle=open(fullname, rw, 0644);
+       if(filehandle<0) return 0;
+       return filehandle;
+}
+
+void* mmap_malloc(void* s, size_t size, int prot, int f, char* fullname, int o)
+{
+       FILE_T fh=open(fullname, O_RDONLY, 0644);
+       if(fh<0) return MAP_FAILED;
+
+       char* data=k_malloc(size);
+       int charsread=0;
+       int len;
+       do{
+               len=read(fh, data+charsread, size-charsread);
+               if(len< 0 && FERRNO(len)==EINTR) continue;
+               if(len<=0) break;
+               charsread+=len;
+       } while(charsread<size);
+
+       close(fh);
+       if(len<0 || charsread!=size){ k_free(data); return MAP_FAILED; }
+       return data;
+}
+
+void* mmap_name(void* s, size_t size, int prot, int f, char* fullname, int o)
+{
+       if(!size) return MAP_FAILED;
+       int w=(prot & PROT_WRITE);
+       FILE_T fh=open(fullname, (w? O_RDWR|O_CREAT: O_RDONLY), 0644);
+       if(fh<0) return MAP_FAILED;
+       return mmap(s, size, prot, f, fh, o);
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void k_random_bytes(char* buf, size_t size)
+{
+       *(short*)buf=getpid();
+       if(size!=2) k_log_err("Linux randomness not implemented yet!");
+}
+
+/* -------------------------------------------------------------------------- */
+
+EXPORT void* k_malloc(size_t size)
+{
+       void* p=malloc(size);
+       if(!p){
+               k_fatal(OOM_STRING);
+       }
+       return p;
+}
+
+EXPORT void* k_realloc(void* o, size_t size)
+{
+       void* p=realloc(o, size);
+       if(!p){
+               k_fatal(OOM_STRING);
+       }
+       return p;
+}
+
+EXPORT void k_free(void* o)
+{
+       if(o) free(o);
+}
+
+EXPORT char* k_strdup(char* s)
+{
+       if(!s) return 0;
+       char* p=strdup(s);
+       if(!p){
+               k_fatal(OOM_STRING);
+       }
+       return p;
+}
+
+EXPORT void* k_memdup(void* s, size_t size)
+{
+       void* p=malloc(size);
+       if(!p){
+               k_fatal(OOM_STRING);
+       }
+       memcpy(p, s, size);
+       return p;
+}
+
+/* -------------------------------------------------------------------------- */
+
diff --git a/src/platform/linux/osapi.h b/src/platform/linux/osapi.h
new file mode 100644 (file)
index 0000000..d84d0c0
--- /dev/null
@@ -0,0 +1,58 @@
+
+/* -------------------------------------------------------------------------- */
+
+#define LOAD_MODULES_EARLY    1
+#define LOAD_GL               0
+#define MODULE                void*
+#define MODPRE                "mod-"
+#define MODPOST               ".so"
+#define DLOPEN(n)             dlopen(n, RTLD_GLOBAL|RTLD_NOW)
+#define DLSYM                 dlsym
+#define DLERROR               dlerror()
+#define LOOP_TICK             10
+#define LINGER_LOOPS          500
+
+#define EMPTY_IN_ADDR         { 0 }
+#define SOCK                  fd
+#define SOCK_T                int
+#define SOCKET(a,t,p,s)       s=socket(a,t,p)
+#define ACCEPT(as,a,l,s)      s=accept(as,a,l)
+#define BIND                  bind
+#define LISTEN                listen
+#define CONNECT               connect
+#define SOCKET_READ( s,b,l)   recv(s,b,l,MSG_NOSIGNAL)
+#define SOCKET_WRITE(s,b,l)   send(s,b,l,MSG_NOSIGNAL)
+#define SOCKET_CLOSE          close
+#define SET_NON_BLOCKING(s)   int f=fcntl(s, F_GETFL); if(f!=-1) fcntl(s, F_SETFL, f|O_NONBLOCK)
+#define SET_NO_DELAY(s)       int arg=1; r=setsockopt(s, SOL_TCP,    TCP_NODELAY,  (char*)&arg, sizeof(arg))
+#define SET_REUSEADDR(s)      int arg=1; r=setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, sizeof(arg))
+#define GETERRNO(n)
+#define ERRNO                 errno
+#define INTERRUPTED           EINTR
+#define NOTACTIVE             EWOULDBLOCK
+#define ISCONNECTING(e)       (e==EINPROGRESS || e==EALREADY)
+#define ISNOTACTIVE(e)        (e==EAGAIN || e==EWOULDBLOCK)
+
+#define FILEP                 FILE*
+#define FILE_T                int
+#define FOPEN                 fopen
+#define READFILE              read
+#define WRITEFILE             write
+#define MKDIR(n)              mkdir(n, 0777)
+#define FPRINTF               fprintf
+#define FFLUSH(f)             fflush(f)
+#define FCLOSE(f)             close(f)
+#define PRINTFOUT(f)          printf(f)
+#define PRINTFERR(f)          fprintf(stderr,f)
+#define VPRINTFOUT(f,ap)      vprintf(f,ap)
+#define VPRINTFERR(f,ap)      vfprintf(stderr,f,ap)
+#define FERRNO(n)             errno
+#define EXIT(n)               exit(n)
+#define LOG_TO_STD            0
+
+#define TIMEZONE(tm)          tm->tm_gmtoff
+#define SLEEP_MS(x)           do{struct timeval t;t.tv_sec=0;t.tv_usec=(x)*1000;select(0,0,0,0,&t);}while(0)
+#define MKTIME                mktime
+
+/* -------------------------------------------------------------------------- */
+
diff --git a/src/platform/linux/platform.h b/src/platform/linux/platform.h
new file mode 100644 (file)
index 0000000..85c1e52
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef LINUX_PLATFORM_H
+#define LINUX_PLATFORM_H
+
+#include <string.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#define MMAPSIZE (128*1024)
+
+#ifndef PUBLIC
+#define PUBLIC extern
+#endif
+#define EXPORT
+
+#define CONSOLE    void*
+#define C_RUN_RV   int
+#define C_RUN_ARG  void*
+
+#endif
+