--- /dev/null
+#!/bin/sh
+
+pkill cilux
+make clean linux 2>&1 | tee out.log
+egrep -i warn out.log
+
--- /dev/null
+
+# ---------------------------------------------------------------------------- #
+
+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 '--------------------'
+
+# ---------------------------------------------------------------------------- #
+
+
+
--- /dev/null
+
+uniform sampler2D texture;
+
+varying mediump float varyLight;
+varying mediump vec2 varyTexCoord;
+
+void main (void)
+{
+ gl_FragColor = varyLight * texture2D(texture, varyTexCoord);
+}
+
--- /dev/null
+
+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;
+}
+
--- /dev/null
+
+
+/* -------------------------------------------------------------------------- */
+
+#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);
+}
+
+/* ------------------------------------------------------------- */
+
+
+
--- /dev/null
+
+/* -}{----------------------------------------------------------------------- */
+
+#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);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+
--- /dev/null
+
+/* -}{----------------------------------------------------------------------- */
+
+#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;
+}
+
+/* -}{----------------------------------------------------------------------- */
+
--- /dev/null
+
+/* -}{----------------------------------------------------------------------- */
+
+#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);
+}
+
+/* -}{----------------------------------------------------------------------- */
+
--- /dev/null
+#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
+
+
--- /dev/null
+
+#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
+
--- /dev/null
+
+/* -}{----------------------------------------------------------------------- */
+
+#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:");
+ }
+}
+
+/* -}{---- ------------------------------------------------------------------ */
+
--- /dev/null
+
+/* -}{----------------------------------------------------------------------- */
+
+#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);
+ }
+}
+
+/* -}{----------------------------------------------------------------------- */
+
+
+
+
--- /dev/null
+/* -------------------------------------------------------------------------- */
+/*
+ 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);
+
+/* -------------------------------------------------------------------------- */
+
--- /dev/null
+
+/* -------------------------------------------------------------------------- */
+
+#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;
+}
+
+
+/* -------------------------------------------------------------------------- */
+
+
+
--- /dev/null
+
+#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);
+ }
+ }
+}
+
+/* -------------------------------------------------------------------------- */
+
+
--- /dev/null
+
+/* -------------------------------------------------------------------------- */
+
+#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;
+}
+
+/* -------------------------------------------------------------------------- */
+
--- /dev/null
+
+/* -------------------------------------------------------------------------- */
+
+#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
+
+/* -------------------------------------------------------------------------- */
+
--- /dev/null
+#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
+