Browse Source

Commit some work on WOE. The server runs now but needs more binings to the script.

Beoran 8 years ago
parent
commit
6bbce2c4bb
34 changed files with 4918 additions and 17 deletions
  1. 31 0
      .gitignore
  2. 36 0
      LICENSE
  3. 6 0
      README
  4. 18 5
      Tupfile
  5. 87 0
      data/script/main.rb
  6. 6 0
      data/var/motd
  7. 25 0
      include/callrb.h
  8. 65 0
      include/client.h
  9. 19 0
      include/config.h
  10. 117 0
      include/dynar.h
  11. 78 0
      include/esh.h
  12. 93 0
      include/every.h
  13. 58 0
      include/mem.h
  14. 88 0
      include/monolog.h
  15. 74 0
      include/rh.h
  16. 64 0
      include/server.h
  17. 22 0
      include/state.h
  18. 23 0
      include/toruby.h
  19. 239 0
      include/tr_macro.h
  20. 58 0
      src/callrb.c
  21. 142 0
      src/client.c
  22. 61 0
      src/config.c
  23. 728 0
      src/dynar.c
  24. 360 0
      src/esh.c
  25. 117 0
      src/every.c
  26. 0 10
      src/libtelnet.pc.in
  27. 107 2
      src/main.c
  28. 257 0
      src/mem.c
  29. 306 0
      src/monolog.c
  30. 481 0
      src/rh.c
  31. 704 0
      src/server.c
  32. 362 0
      src/toruby.c
  33. BIN
      src/woe-server
  34. 86 0
      woe.geany

+ 31 - 0
.gitignore

@@ -0,0 +1,31 @@
+# Compiled source #
+###################
+*.com
+*.class
+*.dll
+*.exe
+*.o
+*.so
+
+
+# backups
+*~
+# Cmake generated
+CMakeFiles
+Testing
+CMakeCache.txt
+CTestTestfile.cmake
+Makefile
+*.ninja
+.ninja_log
+# Testing
+test_graph.png
+# binaries
+bin
+# build dir CONTENTS
+build
+# autogenerated docs
+doc/nd
+doc/nd_project
+.tup
+woe.log

+ 36 - 0
LICENSE

@@ -0,0 +1,36 @@
+The Sleepycat License
+
+Woe is copyright (c)  2015 Beoran  (beoran@gmail.com), 
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, 
+   this list of conditions and the following disclaimer.
+   
+2. Redistributions in binary form must reproduce the above copyright notice, 
+   this list of conditions and the following disclaimer in the documentation 
+   and/or other materials provided with the distribution.
+   
+3. Redistributions in any form must be accompanied by information on how to 
+   obtain complete source code for the Eruta software and any accompanying 
+   software that uses the Eruta software. The source code must either be 
+   included in the distribution or be available for no more than the cost of 
+   distribution plus a nominal fee, and must be freely redistributable under 
+   reasonable conditions. For an executable file, complete source code means 
+   the source code for all modules it contains. It does not include source code 
+   for modules or files that typically accompany the major components of the 
+   operating system on which the executable file runs.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE 
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 6 - 0
README

@@ -0,0 +1,6 @@
+WOE - Workers of Eruta 
+
+Woe is a MUD server scriptable with mruby, used to implement the Workers of Eruta MUD.
+
+Workers of Eruta is set in the land of Eruta, 60000 years in the future.
+

+ 18 - 5
Tupfile

@@ -1,5 +1,5 @@
 
-CFLAGS = -I /usr/local/include -I ./include -Wall -Wno-unused 
+CFLAGS = -I /usr/local/include -I ./include -Wall --std=c99 -Wno-unused
 
 ifeq (@(RELEASE),y)
   CFLAGS += -Os
@@ -8,7 +8,20 @@ else
 endif
 
 # Source files of EKQ
-SRC_FILES  = src/libtelnet.c
+
+SRC_FILES   = src/libtelnet.c
+SRC_FILES  += src/config.c
+SRC_FILES  += src/esh.c
+SRC_FILES  += src/client.c
+SRC_FILES  += src/mem.c
+SRC_FILES  += src/every.c
+SRC_FILES  += src/dynar.c
+SRC_FILES  += src/monolog.c
+SRC_FILES  += src/server.c
+SRC_FILES  += src/rh.c
+SRC_FILES  += src/toruby.c
+
+
 # SRC_FILES += src/tr.c
 
 TEST_FILES = test/test_model.c
@@ -16,10 +29,10 @@ TEST_FILES = test/test_model.c
 
 MAIN_FILE  = src/main.c
 
+MRUBY_LIBS = -lmruby_core -lmruby
+WOE_LIBS   = $(MRUBY_LIBS) -lrt -lm
 
-MRUBY_LIBS=-lmruby_core -lmruby
-
-LDFLAGS = -L /usr/local/lib $(MRUBY_LIBS) -lm
+LDFLAGS = -L /usr/local/lib $(WOE_LIBS)
 
 !cc = |> ^ CC %f ^ gcc  $(CFLAGS) -c %f -o %o |>
 !ld = |> gcc %f $(LDFLAGS) -o %o |> 

+ 87 - 0
data/script/main.rb

@@ -0,0 +1,87 @@
+
+p "Hi from main.rb"
+p global_variables
+# p :$--TEST--
+# p $"--TEST
+log "hello to log"
+
+class Client 
+  attr_reader :id
+  attr_reader :buffer
+  attr_reader :in_lines
+  
+  def initialize(id)
+    @id       = id
+    @buffer   = ""
+    @in_lines = []
+  end
+  
+  def self.add(client_id)
+    @clients ||= {}
+    @clients[client_id] = Client.new(client_id)
+  end
+  
+  def self.get(client_id)
+    @clients ||= {}
+    return @clients[client_id]
+  end
+  
+  def self.remove(client_id)    
+    @clients[client_id] = nil
+  end
+  
+  def send(text) 
+  log "Client #{@id}, send #{text}"
+    Woe::Server.send_to_client(@id, text)
+  end
+  
+  def on_input(str)
+    @buffer ||= ""
+    @buffer << str
+    if @buffer["\r\n"]
+      command, rest = @buffer.split("\r\n", 1)
+      log "Client #{@id}, command #{command}"
+      if (command.strip == "quit") 
+        self.send("Bye bye!")
+        Woe::Server.disconnect(@id)
+        Client.remove(@id)
+        return nil
+      else 
+        self.send("I don't know how to #{command}")
+      end
+      @buffer = rest
+    end    
+  end  
+end
+
+
+
+def woe_on_connect(client_id)
+  p "Client #{client_id} connected"
+  Client.add(client_id)
+end
+
+def woe_on_disconnect(client_id)
+  p "Client #{client_id} disconnected"
+  Client.remove(client_id)
+end
+
+def woe_on_input(client_id, buf)
+  p "Client #{client_id} input #{buf}"
+  client = Client.get(client_id)
+  unless client
+    log "Unknown client #{client_id} in woe_on_input."
+    Woe::Server.disconnect(client_id)
+  else
+    p "Client #{client} #{client.id} ok."
+    client.on_input(buf)
+  end  
+end
+
+
+def woe_on_signal(signal)
+  log "Received signal in script #{signal}"
+  if signal !=28 
+    Woe::Server.quit 
+  end
+end

+ 6 - 0
data/var/motd

@@ -0,0 +1,6 @@
+__        __         _                    ___   __   _____            _        
+\ \      / /__  _ __| | _____ _ __ ___   / _ \ / _| | ____|_ __ _   _| |_ __ _ 
+ \ \ /\ / / _ \| '__| |/ / _ \ '__/ __| | | | | |_  |  _| | '__| | | | __/ _` |
+  \ V  V / (_) | |  |   <  __/ |  \__ \ | |_| |  _| | |___| |  | |_| | || (_| |
+   \_/\_/ \___/|_|  |_|\_\___|_|  |___/  \___/|_|   |_____|_|   \__,_|\__\__,_|
+                                                                               

+ 25 - 0
include/callrb.h

@@ -0,0 +1,25 @@
+#ifndef collide_H_INCLUDED
+#define collide_H_INCLUDED
+
+#include "state.h"
+
+enum CollisionKinds_ {
+  COLLIDE_BEGIN     = 1,
+  COLLIDE_COLLIDING = 2,
+  COLLIDE_END       = 3
+};
+
+int callrb_sprite_event(SpriteState * spritestate, int kind, void * data);
+
+int callrb_on_start();
+
+int callrb_on_reload();
+
+int callrb_on_update(State * self);
+
+
+#endif
+
+
+
+

+ 65 - 0
include/client.h

@@ -0,0 +1,65 @@
+#ifndef WOE_CLIENT_H
+#define WOE_CLIENT_H
+
+#if !defined(_POSIX_SOURCE)
+#define _POSIX_SOURCE
+#endif
+#if !defined(_BSD_SOURCE)
+#define _BSD_SOURCE
+#endif
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "libtelnet.h"
+
+
+#ifndef WOE_CLIENTS_MAX
+/* Must beless than ulimit -nor */
+#define WOE_CLIENTS_MAX 1000
+#endif
+
+#ifndef WOE_CLIENT_BUFFER_SIZE 
+#define WOE_CLIENT_BUFFER_SIZE 256
+#endif
+
+struct woe_server;
+
+struct woe_client {  
+  struct woe_server * server;
+  int                 index;
+  int                 sock;
+  telnet_t *          telnet;
+  char                linebuf[256];
+  int                 linepos;
+  int                 busy;    
+  struct sockaddr_in  addr;
+  socklen_t           addrlen;
+};
+
+
+int woe_clients_max(void);
+
+int woe_client_input(struct woe_client * cli, const char * buffer, size_t size);
+int woe_client_zmp(struct woe_client * cli, int argc, char *argv[]);
+
+
+struct woe_client * 
+woe_client_new(struct woe_server * srv,  int index, int socket, 
+               struct sockaddr_in *addr, socklen_t addrlen);
+
+struct woe_client * woe_client_free(struct woe_client * cli);
+
+
+
+#endif

+ 19 - 0
include/config.h

@@ -0,0 +1,19 @@
+
+#ifndef WOE_CONFIG_H
+#define WOE_CONFIG_H
+
+
+struct woe_config {
+  int     port;
+  char *  data_dir;
+  char *  log_file;
+};
+
+int woe_config_init_args(struct woe_config * config, int argc, char * argv[]);
+
+struct woe_config woe_config_get();
+struct woe_config woe_config_put(struct woe_config * config);
+
+
+  
+#endif  

+ 117 - 0
include/dynar.h

@@ -0,0 +1,117 @@
+#ifndef ERUTA_DYNAR_H
+#define ERUTA_DYNAR_H
+
+#include <stdlib.h> // need size_t
+// can use with every, so include the header 
+#include "every.h"
+#include "mem.h"
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+typedef struct Dynar_ Dynar;
+
+typedef struct Lilis_ Lilis;
+
+typedef int DynarCompare(const void * one, const void * two);
+
+int dynar_size (Dynar * self );
+int dynar_amount (Dynar * self );
+int dynar_elementsize (Dynar * self );
+Dynar * dynar_done (Dynar * self );
+Dynar * dynar_destroy (Dynar * self , MemDestructor * destroy );
+Dynar * dynar_free (Dynar * self );
+Dynar * dynar_free_destroy (Dynar * self , MemDestructor * destroy );
+Dynar * dynar_alloc(void);
+Dynar * dynar_initempty (Dynar * self , int elsz );
+Dynar * dynar_newempty (int elsz );
+Dynar * dynar_size_ (Dynar * self , int newsize );
+Dynar * dynar_init (Dynar * self , int amount , int elsz );
+Dynar * dynar_initptr (Dynar * self , int amount );
+Dynar * dynar_new (int amount , int elsz );
+Dynar * dynar_newptr (int amount );
+Dynar * dynar_grow (Dynar * self , int amount );
+int dynar_sizeindex_ok (Dynar * self , int index );
+int dynar_index_ok (Dynar * self , int index );
+void * dynar_getraw_unsafe (Dynar * self , int index );
+void * dynar_getcopy_unsafe (Dynar * self , int index , void * out );
+Dynar * dynar_putraw_unsafe (Dynar * self , int index , void * ptr );
+void * dynar_getraw (Dynar * self , int index );
+void * dynar_getcopy (Dynar * self , int index , void * ptr );
+Dynar * dynar_putraw (Dynar * self , int index , void * ptr );
+void * dynar_putptr (Dynar * self , int index , void * ptr );
+void * dynar_getptr (Dynar * self , int index );
+void * dynar_putdata (Dynar * self , int index , void * ptr );
+void * dynar_getdata (Dynar * self , int index );
+
+Dynar * dynar_putnullall(Dynar * self);
+
+Dynar * dynar_qsort(Dynar * self  , DynarCompare * compare);
+void * dynar_lsearch(Dynar * self, const void * key, DynarCompare * compare);
+void * dynar_bsearch(Dynar * self, const void * key, DynarCompare * compare);
+
+
+Every * dynar_everynow_data (Every * every );
+Every * dynar_everynow_ptr (Every * every );
+Every * dynar_everyinit_data (Every * every );
+Every * dynar_everynext_data (Every * every );
+void * dynar_everyput_data (Every * every , void * data );
+void * dynar_everydone (Every * every );
+Every * dynar_everyinit_ptr (Every * every );
+Every * dynar_everynext_ptr (Every * every );
+void * dynar_everyput_ptr (Every * every , void * data );
+Every * dynar_every_data (Dynar * dynar );
+Every * dynar_every_ptr (Dynar * dynar );
+
+void * dynar_each_ptr (Dynar * self , EachDo * eachdo , void * extra );
+void * dynar_each_data(Dynar * self, EachDo * eachdo, void * extra);
+
+void * dynar_walkptr(Dynar * self, Walker * walker, void * extra);
+void * dynar_walkptrbetween(Dynar * self, int low, int high, 
+                            Walker * walker, void * extra);
+void * dynar_walkdata(Dynar * self, Walker * walker, void * extra);
+void * dynar_walkmapptrbetween(Dynar * self, int low, int high, 
+                            Walker * walker, void * extra);
+void * dynar_walkmapptr(Dynar * self, Walker * walker, void * extra);
+
+Dynar * dynar_resize(Dynar * self, int newsize, MemDestructor * destroy);
+
+
+Dynar * dynar_new_long();
+Dynar * dynar_put_long(Dynar * self, int index, long value);
+Dynar * dynar_get_long(Dynar * self, int index, long * value);
+Dynar * dynar_append_long(Dynar * self, long value);
+
+Dynar * dynar_destroy_structs(Dynar * self, MemDestructor * destroy);
+Dynar * dynar_destroy_structs_and_free(Dynar * self, MemDestructor * destroy);
+
+
+Lilis * lilis_freetail (Lilis * self );
+Lilis * lilis_done (Lilis * self );
+Lilis * lilis_free (Lilis * self );
+Lilis * lilis_alloc (void);
+Lilis * lilis_init (Lilis * self , Lilis * next , Lilis * prev , void * data );
+Lilis * lilis_initempty (Lilis * self );
+Lilis * lilis_new (Lilis * next , Lilis * prev , void * data );
+Lilis * lilis_newempty(void);
+Lilis * lilis_add (Lilis * self , Lilis * other );
+Lilis * lilis_addnew (Lilis * self , void * data );
+Lilis * lilis_removenext (Lilis * self );
+Lilis * lilis_remove (Lilis * self );
+Lilis * lilis_erase (Lilis * self );
+Lilis * lilis_erasenext (Lilis * self );
+Lilis * lilis_next (Lilis * self );
+Lilis * lilis_previous (Lilis * self );
+void * lilis_data (Lilis * self );
+
+
+
+
+#endif
+
+

+ 78 - 0
include/esh.h

@@ -0,0 +1,78 @@
+#ifndef ESH_H_INCLUDED
+#define EHS_H_INCLUDED
+
+/* Explicit String Handling. These functions help handling strings directly 
+ * using struct wrappers, you need to pass in the parameters explicitly and 
+ * most often by reference.
+ * 
+ * Nevertheless a struct wrapper is also provided , named woesb, 
+ * or WOE String Buffer.
+ */
+#include <stdlib.h> 
+#include <stdio.h>
+#include <stdarg.h> 
+
+ 
+int    esh_bad_p(char **me, size_t * size, size_t * space);
+char * esh_make_empty(char ** me, size_t * size, size_t * space);
+char * esh_nul_terminate(char ** me, size_t * size, size_t * space);
+char * esh_alloc(char ** me, size_t * size, size_t * space, size_t init_space);
+char * esh_grow(char ** me, size_t  * size, size_t * space, size_t new_space);
+char * esh_ensure_space(char ** me, size_t * size, size_t * space, size_t grow_by);
+char * esh_append_char(char ** me, size_t * size, size_t * space, char ch);
+char * esh_append_buf(char  ** me, size_t * size, size_t * space, char * buf, size_t bufsize);
+char * esh_append_cstr(char ** me, size_t * size, size_t * space, char * str);
+char * esh_init_buf(char ** me, size_t * size, size_t * space, char * buf, size_t bufsize);
+char * esh_init_cstr(char ** me, size_t * size, size_t * space, char * buf);
+char * esh_new_buf(char **me, size_t * size, size_t * space, char * buf, size_t bufsize);
+char * esh_new(char **me, size_t * size, size_t * space, char * init);
+char * esh_new_empty(char **me, size_t * size, size_t * space);
+char * esh_free(char ** me, size_t * size, size_t * space);
+char * esh_read_file(char ** me, size_t * size, size_t * space, FILE * file);
+char * esh_read_filename(char ** me, size_t * size, size_t * space, char * fn);
+
+
+char * esh_join(char ** me, size_t * size, size_t * space,  ...);
+char * esh_join_va(char ** me, size_t * size, size_t * space, va_list strings);
+char * esh_init_join_cstr(char ** me, size_t * size, size_t * space, char * first, ...);
+char * esh_init_join_cstr_va(char ** me, size_t * size, size_t * space, char * first, va_list strings);
+
+
+
+struct woesb {
+  char      * text;
+  size_t      size;
+  size_t      space;  
+};
+ 
+int woesb_bad_p(struct woesb * me);
+struct woesb * woesb_make_empty(struct woesb * me);
+struct woesb * woesb_nul_terminate(struct woesb * me);
+struct woesb * woesb_alloc(struct woesb * me, size_t init_space);
+struct woesb * woesb_grow(struct woesb * me, size_t new_space);
+struct woesb * woesb_ensure_space(struct woesb * me, size_t grow_by);
+struct woesb * woesb_append_char(struct woesb * me, char ch);
+struct woesb * woesb_append_buf(struct woesb * me, char * buf, size_t bufsize);
+struct woesb * woesb_append_cstr(struct woesb * me, char * str);
+struct woesb * woesb_init_buf(struct woesb * me, char * buf, size_t bufsize);
+struct woesb * woesb_init_cstr(struct woesb * me, char * buf);
+struct woesb * woesb_new_buf(struct woesb * me, char * buf, size_t bufsize);
+struct woesb * woesb_new(struct woesb * me, char * init);
+struct woesb * woesb_new_empty(struct woesb * me);
+struct woesb * woesb_free(struct woesb * me);
+struct woesb * woesb_read_file(struct woesb * me, FILE * file);
+struct woesb * woesb_read_filename(struct woesb * me, char * fn); 
+
+struct woesb * woesb_join(struct woesb * me, ...);
+struct woesb * woesb_join_va(struct woesb * me, va_list strings);
+struct woesb * woesb_init_join(struct woesb * me, char * first, ...);
+struct woesb * woesb_init_join_va(struct woesb * me, char * first, va_list strings);
+struct woesb * woesb_new_join_va(struct woesb * me, char * first, va_list strings);
+struct woesb * woesb_new_join(struct woesb * me, char * first, ...);
+
+
+#endif
+
+
+
+

+ 93 - 0
include/every.h

@@ -0,0 +1,93 @@
+#ifndef EVERY_H_INCLUDED
+#define EVERY_H_INCLUDED
+
+/*
+* Every is an iterator that allows to iterate over every element of
+* a collection.
+*/
+
+struct Every_;
+typedef struct Every_ Every;
+
+struct EveryActs_;
+typedef struct EveryActs_ EveryActs;
+
+
+// types for the funtion pointers that every uses
+typedef Every * (EveryNext(Every * every));
+typedef void  * (EveryPut(Every * every, void * element));
+typedef void  * (EveryDone(Every * every));
+typedef Every * (EveryInit(Every * every));
+
+// Struct with all function pointers for Every
+struct EveryActs_ {
+  EveryDone * done;
+  EveryInit * init;
+  EveryNext * next;
+  EveryPut  * put;
+};
+
+/*
+* Every is a struct that can be used with collections like Dynar to fetch
+* every element of the collection.
+*/
+struct Every_ {
+  void         * now;
+  void         * on;
+  int            index;
+  void         * extra;
+  EveryActs    * acts;
+};
+
+
+struct Each_;
+typedef struct Each_ Each;
+/*
+* Each is an alternative iterator interface, that
+* requires a function callback in stead of an object. 
+*/
+struct Each_ {
+  void * on;
+  void * now;
+  void * extra;
+  int    index;
+};
+
+/* Function pointer for the each iteration method.  */
+typedef void * (EachDo(Each * element));
+
+/* An even simpler iterator interface, one for simply iterating and one 
+for searching. Iteration is stopped if AllData returns not-NULL, 
+and the returned data will be returned by the function that uses AllData 
+too. Extra is used for passing in extra data if any. */
+typedef void * (AllData)(void * data, void * extra);
+
+
+/* Generic comparator function. Much like strcmp. */
+typedef int (AllCompare)(void * one, void * two);
+
+Every * every_alloc (void);
+Every * every_done (Every * self );
+Every * every_free (Every * self );
+Every * every_init (Every * self , EveryActs * acts );
+Every * every_new (EveryActs * acts );
+void * every_get (Every * self );
+void * every_put (Every * self , void * data );
+
+Every * every_next (Every * self , void * data );
+Each * each_init (Each * self , void * on , void * data );
+Each * each_next (Each * self , void * now );
+
+void * each_now (Each * self );
+void * each_on (Each * self );
+void * each_extra (Each * self );
+int each_index (Each * self);
+
+/* Yet another iteration interface: a Walker is a simple callback function. */
+typedef void * (Walker)(void * element, void * extra);  
+
+// and some helper macros
+#define EACH_NOW(EACH, TYPE) ((TYPE *) each_now(EACH))
+
+
+#endif

+ 58 - 0
include/mem.h

@@ -0,0 +1,58 @@
+#ifndef ERUTA_MEM_H
+#define ERUTA_MEM_H
+
+#include <stdlib.h>
+
+/* Wrapper for calloc/malloc */
+void * mem_calloc(size_t amount, size_t size);
+
+/* Wrapper for calloc/malloc */
+void * mem_alloc(size_t size);
+
+/* Wrapper for free */
+void * mem_free(void * ptr);
+
+/* Wrapper for realloc */
+void * mem_realloc(void *ptr, size_t newsize);
+
+/* Resizes memory, taking care not to lose ptr* if the reallocation
+fails. */
+void * mem_resize(void ** ptr, size_t newsize);
+
+/* Wrapper for memmove, for consistency */
+void * mem_move(void * dest, void * src, size_t size);
+
+/* A function pointer that can act as a destructor. 
+Should return NULL on sucessful freeing, and non-null 
+if freeing failed. */
+typedef void * (MemDestructor)(void * ptr);
+
+/* Counted reference. Just an idea... */
+struct Refc {
+  void          * ptr; 
+  int             refc;
+  MemDestructor * destroy;
+};
+
+
+
+/* Handy macros. */
+/*
+#define STRUCT_NALLOC(STRUCT, AMOUNT) \
+        ((STRUCT *) mem_calloc(sizeof(STRUCT), (AMOUNT)))
+*/
+
+#define MEM_ALLOCATE(STRUCTNAME) mem_alloc(sizeof(STRUCTNAME))
+
+#define STRUCT_NALLOC(STRUCT, AMOUNT) \
+        (mem_calloc(sizeof(STRUCT), (AMOUNT)))
+
+       
+#define STRUCT_ALLOC(STRUCT)          \
+        ((STRUCT *)MEM_ALLOCATE(STRUCT))
+        
+#define STRUCT_FREE(VAR)              \
+        mem_free(VAR)
+
+
+#endif

+ 88 - 0
include/monolog.h

@@ -0,0 +1,88 @@
+#ifndef monolog_H_INCLUDED
+#define monolog_H_INCLUDED
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+
+/* Finer grained logging. */
+
+/** The interface for a logger */
+struct MonologLoggerInterface {
+  /* Logger function. */ 
+  int   (*log)(char * file, int line, char * level, void * data, char * format, va_list args);
+  /* Optional destructor. */
+  void (*free)(void * data);
+}; 
+
+int monolog_add_logger(void * data, struct MonologLoggerInterface * logger);
+int monolog_remove_logger(int index);
+int monolog_enable_level(char * name);
+int monolog_disable_level(char * name);
+int monolog_log_va(char * file, int line, char * level, char * format, va_list args);
+int monolog_log(char * file, int line, char * level, char * format, ...);
+int monolog_init(void);
+void monolog_done();
+
+int monolog_stdout_logger(char * file, int line, char * level, void * data, char * format, va_list args);
+
+int monolog_stderr_logger(char * file, int line, char * level,  void * data, char * format, va_list args);
+
+/** Log function for logger that logs to a FILE. */
+int monolog_file_logger(char * file, int line, char * level,  void * data, char * format, va_list args);
+
+/** Free function for logger that logs to a FILE. */
+void monolog_file_free(void * data);
+
+
+/* Logger helper macros */
+#define DEFINE_LOGGER(NAME, LOGF, FREEF) \
+  static struct MonologLoggerInterface NAME  = { LOGF, FREEF }
+
+#define DEFINE_STDERR_LOGGER(NAME) DEFINE_LOGGER(NAME, monolog_stderr_logger, NULL)
+#define DEFINE_STDOUT_LOGGER(NAME) DEFINE_LOGGER(NAME, monolog_stdout_logger, NULL)
+#define DEFINE_FILE_LOGGER(NAME)   DEFINE_LOGGER(NAME, monolog_file_logger, monolog_file_free)
+
+/* Logging helper macros */
+
+#define LOG_LEVEL(LEVEL, ...) \
+  monolog_log(__FILE__, __LINE__, LEVEL, __VA_ARGS__)
+
+#define LOG(...)          LOG_LEVEL(__FILE__,   __VA_ARGS__)
+#define LOG_ERROR(...)    LOG_LEVEL("ERROR",    __VA_ARGS__)
+#define LOG_WARNING(...)  LOG_LEVEL("WARNING",  __VA_ARGS__)
+#define LOG_NOTE(...)     LOG_LEVEL("NOTE",     __VA_ARGS__)
+#define LOG_DEBUG(...)    LOG_LEVEL("DEBUG",    __VA_ARGS__)
+
+#define LOG_ENABLE(FORMAT)            \
+    monolog_enable_level(#FORMAT)
+
+#define LOG_ENABLE_ERROR()        LOG_ENABLE(ERROR)  
+#define LOG_ENABLE_WARNING()      LOG_ENABLE(WARNING)
+#define LOG_ENABLE_NOTE()         LOG_ENABLE(NOTE)
+#define LOG_ENABLE_DEBUG()        LOG_ENABLE(DEBUG)
+
+#define LOG_DISABLE(FORMAT)            \
+    monolog_disable_level(#FORMAT)
+    
+#define LOG_DISABLE_ERROR()       LOG_DISABLE(error)  
+#define LOG_DISABLE_WARNING()     LOG_DISABLE(warning)
+#define LOG_DISABLE_NOTE()        LOG_DISABLE(note)
+#define LOG_DISABLE_DEBUG()       LOG_DISABLE(debug)
+
+
+
+
+#endif
+
+
+
+

+ 74 - 0
include/rh.h

@@ -0,0 +1,74 @@
+#ifndef rh_H_INCLUDED
+#define rh_H_INCLUDED
+
+#include <stdarg.h>
+
+#include <mruby.h>
+#include <mruby/data.h>
+#include <mruby/compile.h>
+#include <mruby/proc.h>
+#include <mruby/string.h>
+
+// shortcut typedef. Also to alllow possible future enhancement.
+typedef mrb_state Ruby;
+
+typedef struct Script_ Script;
+
+
+Ruby * rh_new(void);
+Ruby * rh_free(Ruby * self);
+
+mrb_value rh_inspect(mrb_state *mrb , mrb_value obj );
+
+char * rh_inspect_cstr(mrb_state *mrb , mrb_value value);
+
+int rh_run_file(Ruby * self , const char * filename, FILE * file );
+
+int rh_run_filename (Ruby * self , const char * filename );
+
+/* rh_run_script only works fo files in a (sub) folder of the WOE
+ * directory, where rh_run_filename is generic. */
+int rh_run_script(Ruby * self, const char * filename);
+
+char * rh_exception_string (Ruby * self );
+
+mrb_value rh_simple_funcall(Ruby * ruby, char * name);
+
+mrb_value rh_run_function_args(Ruby * ruby, mrb_value rubyself, 
+                          char * name, int argc, mrb_value * argv);
+
+mrb_value rh_run_toplevel_args(Ruby * ruby, char * name, int argc, mrb_value * argv);
+
+mrb_value rh_run_function_va(Ruby * ruby, mrb_value rubyself, 
+                          char * name, char * format, va_list args);
+
+mrb_value rh_run_toplevel_va(Ruby * ruby, char * name, char * format, va_list args);
+
+
+mrb_value rh_run_function(Ruby * ruby, mrb_value rubyself, char * name, char * format, ...);
+
+mrb_value rh_run_toplevel(Ruby * ruby, char * name, char * format, ...);
+int rh_tobool(mrb_value v);                              
+                              
+#define rh_bool_value(B) ( (B) ? mrb_true_value() : mrb_false_value())
+
+/* Tries to (re-)load the main ruby file, output to console. */
+int rh_load_main();
+
+Ruby * rh_open_ruby_state();
+Ruby * rh_close_ruby_state();
+Ruby * rh_get_ruby_state(Ruby * ruby);
+
+
+
+int rh_load_main();
+int rh_on_start();
+int rh_on_reload(); 
+
+
+
+#endif
+
+
+
+

+ 64 - 0
include/server.h

@@ -0,0 +1,64 @@
+#ifndef WOE_SERVER_H
+#define WOE_SERVER_H
+
+#ifndef WOE_SERVER_MAX_TIMERS
+#define WOE_SERVER_MAX_TIMERS 128
+#endif
+
+#include <stdarg.h>
+#include <mruby.h>
+
+struct woe_server;
+
+struct woe_server * woe_server_new(int port);
+struct woe_server * woe_server_free(struct woe_server * srv);
+
+int woe_server_listen(struct woe_server * srv);
+int woe_server_update(struct woe_server * srv, int timeout);
+
+int woe_server_busy(struct woe_server * srv);
+void woe_server_request_shutdown(struct woe_server * srv);
+
+enum woe_timer_type {
+  WOE_TIMER_TYPE_REPEAT,
+  WOE_TIMER_TYPE_ONCE
+};
+
+
+int woe_server_start_timer(struct woe_server * srv,
+                           int sec, int ns, enum woe_timer_type type);
+                           
+
+int woe_server_send_to_client(struct woe_server * srv, int client, char * data, size_t size);                           
+
+struct woe_server * woe_server_set_mrb(struct woe_server * srv, mrb_state * mrb);
+mrb_state * woe_server_get_mrb(struct woe_server * srv);
+
+int woe_server_disconnect_id(struct woe_server * srv, int id);
+
+
+int woe_server_iac(struct woe_server * srv, int client, int command);
+int woe_server_negotiate(struct woe_server * srv, int client, int how, int option);
+int woe_server_begin_sb(struct woe_server * srv, int client, int telopt);
+int woe_server_finish_sb(struct woe_server * srv, int client);
+int woe_server_subnegotiation(struct woe_server * srv, int client, int telopt, char * buffer, int size);
+int woe_server_begin_compress2(struct woe_server * srv, int client);
+int woe_server_vprintf(struct woe_server * srv, int client, const char *fmt, va_list va);
+int woe_server_printf(struct woe_server * srv, int client, const char *fmt, ...);
+int woe_server_raw_vprintf(struct woe_server * srv, int client, const char *fmt, va_list va);
+int woe_server_raw_printf(struct woe_server * srv, int client, const char *fmt, ...);
+int woe_server_begin_newenviron(struct woe_server * srv, int client, int type);
+int woe_server_newenviron_value(struct woe_server * srv, int client, int type, char * value);
+int woe_server_finish_newenviron(struct woe_server * srv, int client);
+int woe_server_ttype_send(struct woe_server * srv, int client);
+int woe_server_ttype_is(struct woe_server * srv, int client, char * ttype);
+int woe_server_send_zmp(struct woe_server * srv, int client, int argc, const char ** argv);
+int woe_server_send_vzmpv(struct woe_server * srv, int client, va_list va);
+int woe_server_send_zmpv(struct woe_server * srv, int client, ...);
+int woe_server_begin_zmp(struct woe_server * srv, int client, const char * cmd);
+int woe_server_zmp_arg(struct woe_server * srv, int client, const char * arg);
+int woe_server_finish_zmp(struct woe_server * srv, int client, const char * cmd);
+
+
+
+#endif

+ 22 - 0
include/state.h

@@ -0,0 +1,22 @@
+#ifndef STATE_H_INCLUDED
+#define STATE_H_INCLUDED
+
+#include "rh.h"
+#include "server.h"
+#include "config.h"
+
+/* All state of WOE in a handy struct */
+struct woe_state {
+  struct woe_server * server;
+  struct woe_config * config;
+  mrb_state         * mrb;
+};
+
+
+#define MRB_WOE_STATE(MRB) ((struct woe_state *)(MRB->ud))
+#define MRB_WOE_SERVER(MRB) (MRB_WOE_STATE(MRB)->server)
+#define MRB_WOE_CONFIG(MRB) (MRB_WOE_STATE(MRB)->config)
+
+
+#endif
+

+ 23 - 0
include/toruby.h

@@ -0,0 +1,23 @@
+#ifndef toruby_H_INCLUDED
+#define toruby_H_INCLUDED
+
+#include <mruby.h>
+
+/* This file was generated with:
+'cfunctions -c -aoff -n -w toruby_proto src/toruby.c' */
+#ifndef CFH_TORUBY_PROTO
+#define CFH_TORUBY_PROTO
+
+/* From 'src/toruby.c': */
+
+int tr_init (mrb_state * mrb );
+
+#endif /* CFH_TORUBY_PROTO */
+#include "rh.h"
+
+
+#endif
+
+
+
+

+ 239 - 0
include/tr_macro.h

@@ -0,0 +1,239 @@
+/*
+ * This file contains macros to help generate the bindings
+ * of functionality to MRuby.
+ */
+
+#ifndef TR_MACRO_H_INCLUDED
+#define TR_MACRO_H_INCLUDED
+
+#define RH_WRAP(RUBY, TYPE, OBJECT)                         \
+mrb_obj_value(Data_Wrap_Struct(RUBY,                        \
+              RUBY->object_class, &toruby_##TYPE, OBJECT));
+
+#define TR_NAME(NAME)               tr_##NAME
+#define TR_PAIR_HELPER(NAME)        TR_NAME(NAME), NAME
+#define TR_PAIR(NAME)               TR_PAIR_HELPER(NAME)
+#define TR_MACRO_AID(MACRO, ARGS)   MACRO ARGS        
+#define TR_PAIR_DO_AID(MACRO, NAME) TR_MACRO_AID(MACRO, (TR_PAIR(NAME)))
+#define TR_PAIR_DO(MACRO, NAME)     TR_PAIR_DO_AID(MACRO, NAME)
+
+#define TORUBY_0_ICALLER(NAME, TOCALL)                                         \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  (void) self; (void) mrb;                                                     \
+  return mrb_fixnum_value(TOCALL());                                           \
+}
+
+#define TORUBY_0_FGETTER(NAME, TOCALL)                                         \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  (void) self;                                                                 \
+  return mrb_float_value(mrb, TOCALL());                                       \
+}
+
+#define SCEGRA_ICALLER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index;                                                                   \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "i", &index);                                              \
+  return mrb_fixnum_value(TOCALL(index));                                      \
+}
+
+#define SCEGRA_BCALLER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index;                                                                   \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "i", &index);                                              \
+  return rh_bool_value(TOCALL(index));                                         \
+}
+
+
+#define SCEGRA_FCALLER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  mrb_int index;                                                               \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "i", &index);                                              \
+  return mrb_float_value(mrb, TOCALL(index));                                  \
+  }
+
+#define SCEGRA_ISETTER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index, value;                                                            \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "ii", &index, &value);                                     \
+  return mrb_fixnum_value(TOCALL(index, value));                               \
+}
+
+#define SCEGRA_BSETTER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index; mrb_value value;                                                  \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "io", &index, &value);                                     \
+  return mrb_fixnum_value(TOCALL(index, rh_tobool(value)));                    \
+}
+  
+#define SCEGRA_PSETTER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index, x, y;                                                             \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "iii", &index, &x, &y);                                    \
+  return mrb_fixnum_value(TOCALL(index, x, y));                                \
+}
+
+#define SCEGRA_CSETTER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index, r, g, b, a;                                                       \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "iiiii", &index, &r, &g, &b, &a);                          \
+  return mrb_fixnum_value(TOCALL(index, r, g, b, a));                          \
+}
+
+#define SCEGRA_FSETTER(NAME, TOCALL)                                           \
+  static mrb_value NAME(mrb_state * mrb, mrb_value self) {                     \
+  int index; mrb_float value;                                                  \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "if", &index, &value);                                     \
+  return mrb_fixnum_value(TOCALL(index, value));                               \
+}
+
+#define TR_WRAP_NOARG_BOOL(NAME, TOCALL)                                       \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  (void) self; (void) mrb;                                                     \
+  return rh_bool_value(TOCALL());                                              \
+}
+
+#define TR_WRAP_I_BOOL(NAME, TOCALL)                                           \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1;                                                                  \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "i", &i1);                                                 \
+  return rh_bool_value(TOCALL(i1));                                            \
+}
+
+#define TR_WRAP_B_BOOL(NAME, TOCALL)                                           \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_value b1;                                                                \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "o", &b1);                                                 \
+  return rh_bool_value(TOCALL(rh_tobool(b1)));                                 \
+}
+
+#define TR_WRAP_IZ_INT(NAME, TOCALL)                                           \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1 = -1;                                                             \
+  char * str = NULL;                                                           \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "iz", &i1, &str);                                          \
+  return mrb_fixnum_value(TOCALL(i1, str));                                    \
+}
+
+
+#define TR_WRAP_I_INT(NAME, TOCALL)                                            \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1;                                                                  \
+  mrb_get_args(mrb, "i", &i1);                                                 \
+  (void) self;                                                                 \
+  return mrb_fixnum_value(TOCALL(i1));                                         \
+}
+
+#define TR_WRAP_II_INT(NAME, TOCALL)                                           \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1, i2;                                                              \
+  mrb_get_args(mrb, "ii", &i1, &i2);                                           \
+  (void) self;                                                                 \
+  return mrb_fixnum_value(TOCALL(i1, i2));                                     \
+}
+
+#define TR_WRAP_III_INT(NAME, TOCALL)                                          \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1, i2, i3;                                                          \
+  mrb_get_args(mrb, "iii", &i1, &i2, &i3)                                      \
+  (void) self;                                                                 \
+  return mrb_fixnum_value(TOCALL(i1, i2, i3));                                 \
+}
+
+
+#define TR_WRAP_IIII_INT(NAME, TOCALL)                                         \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1, i2, i3, i4;                                                      \
+  mrb_get_args(mrb, "iiii", &i1, &i2, &i3, &i4);                               \
+  (void) self;                                                                 \
+  return mrb_fixnum_value(TOCALL(i1, i2, i3, i4));                             \
+}
+
+#define TR_WRAP_IIIIB_INT(NAME, TOCALL)                                        \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  mrb_int i1, i2, i3, i4;                                                      \
+  mrb_value b5;                                                                \
+  (void) self;                                                                 \
+  mrb_get_args(mrb, "iiiib", &i1, &i2, &i3, &i4, &b5);                         \
+  return mrb_fixnum_value(TOCALL(i1, i2, i3, i4, rh_tobool(b5)));              \
+}
+
+
+#define TR_WRAP_NOARG_INT(NAME, TOCALL)                                        \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  (void) self; (void) mrb;                                                     \
+  return mrb_fixnum_value(TOCALL());                                           \
+}
+
+
+#define TR_SPRITE_GET(SPRITE, STATE, SPRITEID)                                 \
+  SPRITE = state_sprite(STATE, SPRITEID);                                      \
+  if (!SPRITE) {                                                               \
+    return mrb_nil_value();                                                    \
+  }                                                                            
+
+#define TR_SPRITE_FUNC_INIT(SPRITE, STATE, SPRITEID)                           \
+  mrb_int SPRITEID = -1;                                                       \
+  State * STATE    = state_get();                                              \
+  Sprite * sprite  = NULL;                                                     \
+  (void) self;                                                                 
+
+
+#define TR_SPRITE_II_INT(NAME, TOCALL)                                         \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {                       \
+  TR_SPRITE_FUNC_INIT(sprite, state, index)                                    \
+  mrb_int result;                                                              \
+  mrb_int i1, i2;                                                              \
+  mrb_get_args(mrb, "iii", &index, &i1, &i2);                                  \
+  TR_SPRITE_GET(sprite, state, index);                                         \
+  result = TOCALL(sprite, i1, i2);                                             \
+  return mrb_fixnum_value(result);                                             \
+}
+
+
+
+
+#define TR_METHOD(MRB, CLASS, NAME, IMPL, FLAGS)                               \
+        mrb_define_method((MRB), (CLASS), (NAME), (IMPL), (FLAGS))
+
+#define TR_METHOD_ARGC(MRB, CLASS, NAME, IMPL, ARGC)\
+        mrb_define_method((MRB), (CLASS), (NAME), (IMPL), ARGS_REQ(ARGC))
+
+#define TR_METHOD_NOARG(MRB, CLASS, NAME, IMPL)\
+        mrb_define_method((MRB), (CLASS), (NAME), (IMPL), ARGS_NONE())
+
+#define TR_METHOD_OPTARG(MRB, CLASS, NAME, IMPL, ARGC, OPTC) \
+mrb_define_method((MRB), (CLASS), (NAME), (IMPL), ARGS_REQ(ARGC) | ARGS_OPT(OPTC))
+
+#define TR_CLASS_METHOD(MRB, CLASS, NAME, IMPL, FLAGS)\
+        mrb_define_class_method((MRB), (CLASS), (NAME), (IMPL), (FLAGS))
+
+#define TR_CLASS_METHOD_ARGC(MRB, CLASS, NAME, IMPL, ARGC)\
+        mrb_define_class_method((MRB), (CLASS), (NAME), (IMPL), ARGS_REQ(ARGC))
+
+#define TR_CLASS_METHOD_NOARG(MRB, CLASS, NAME, IMPL)\
+        mrb_define_class_method((MRB), (CLASS), (NAME), (IMPL), ARGS_NONE())
+
+#define TR_CLASS_METHOD_OPTARG(MRB, CLASS, NAME, IMPL, ARGC, OPTC) \
+mrb_define_class_method((MRB), (CLASS), (NAME), (IMPL), ARGS_REQ(ARGC) | ARGS_OPT(OPTC))
+
+
+
+#define TR_CONST_INT(MRB, CLASS, NAME, VALUE) \
+      mrb_define_const((MRB), (CLASS), (NAME), mrb_fixnum_value(VALUE))
+
+#define TR_CONST_INT_EASY(MRB, CLASS, PREFIX, NAME) \
+        TR_CONST_INT(MRB, CLASS, #NAME, PREFIX##NAME)
+
+
+#endif // TR_H_INCLUDED

+ 58 - 0
src/callrb.c

@@ -0,0 +1,58 @@
+
+/* Callbacks from the C side into the mruby side.
+ * Used to signal several events such as collisions or sprite
+ * animation.
+*/
+#include "state.h"
+#include "rh.h"
+#include "spritestate.h"
+#include "callrb.h"
+
+/* Sprite event handler. Calls an mruby callback. */
+int callrb_sprite_event(SpriteState * spritestate, int kind, void * data) { 
+  mrb_value res;
+  Sprite * sprite;
+  State * state;
+  int spriteid, thingid, pose, direction;
+  Ruby * ruby;
+  void * thing;
+  sprite    = spritestate_sprite(spritestate);
+  spriteid  = sprite_id(sprite);
+  thing     = spritestate_data(spritestate);  
+  pose      = spritestate_pose(spritestate);
+  direction = spritestate_direction(spritestate);
+  state     = state_get();
+  ruby      = state_ruby(state);
+  res       = rh_run_toplevel(ruby, "eruta_on_sprite", "iiii", 
+                spriteid, pose, direction, kind);
+  (void) data;
+  return rh_tobool(res);
+}
+
+/* Calls the eruta_on_start function  */ 
+int callrb_on_start() { 
+  mrb_value res;
+  State * state = state_get();
+  Ruby * ruby   = state_ruby(state);
+  res           = rh_run_toplevel(ruby, "woe_on_start", "");
+  return rh_tobool(res);
+}
+
+/* Calls the eruta_on_reload function  */
+int callrb_on_reload() { 
+  mrb_value res;
+  State * state = state_get();
+  Ruby * ruby   = state_ruby(state);
+  res           = rh_run_toplevel(ruby, "woe_on_reload", "");
+  return rh_tobool(res);
+}
+
+
+/* Calls the eruta_on_update function. */
+int callrb_on_update(mrb_ruby * self, double dt) {
+  mrb_value res;  
+  mrb_value mval = mrb_float_value(self, dt);
+  res = rh_run_toplevel_args(state_ruby(self), "woe_on_update", 1, &mval);
+  return rh_tobool(res);
+}
+

+ 142 - 0
src/client.c

@@ -0,0 +1,142 @@
+/**
+ * This file client.c, handles clients of the WOE server.
+ */
+
+#if !defined(_WIN32)
+# if !defined(_POSIX_SOURCE)
+#   define _POSIX_SOURCE
+# endif
+# if !defined(_BSD_SOURCE)
+#   define _BSD_SOURCE
+# endif
+
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <poll.h>
+# include <unistd.h>
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+
+# define snprintf _snprintf
+# define poll WSAPoll
+# define close closesocket
+# define strdup _strdup
+# define ECONNRESET WSAECONNRESET
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+
+#include "libtelnet.h"
+#include "monolog.h"
+#include "rh.h"
+#include "client.h"
+#include "server.h"
+
+
+
+struct woe_client * woe_client_alloc() {
+  return calloc(sizeof(struct woe_client), 1);
+}
+
+struct woe_client * woe_client_init(struct woe_client * client,
+  struct woe_server * server, int index, int socket, 
+  struct sockaddr_in *addr, socklen_t addrlen) {
+  if (!client) return NULL;
+  client->server  = server;
+  client->sock    = socket;
+  client->addr    = *addr;
+  client->addrlen = addrlen;
+  client->busy    = !0;
+  return client;
+}
+
+struct woe_client * woe_client_new(struct woe_server * server, int index, int socket, 
+  struct sockaddr_in *addr, socklen_t addrlen) {
+  struct woe_client * client = woe_client_alloc();  
+  return woe_client_init(client, server, index, socket, addr, addrlen);
+}
+
+struct woe_client * woe_client_done(struct woe_client * client) {
+  /* Do nothing yet, refactor later. */
+  if (!client) return NULL;
+  if (client->telnet) telnet_free(client->telnet);
+  client->sock   = -1;
+  client->telnet = NULL;
+  LOG_NOTE("Connection to client %d closed.\n", client->index);
+  client->index = -1;
+  return client;
+}
+
+
+struct woe_client * woe_client_free(struct woe_client * client) {
+  woe_client_done(client);
+  free(client);  
+  return NULL;
+}
+
+
+int woe_client_send(struct woe_client * client, const char *buffer, unsigned int size) {
+  int res;
+  if (!client) return 1;
+  /* ignore on invalid socket */
+  if (client->sock < 0)  return 2;
+  
+  /* send data */
+  while (size > 0) {
+    if ((res = send(client->sock, buffer, size, 0)) == -1) {
+      if (errno != EINTR && errno != ECONNRESET) {
+        LOG_ERROR("send() failed: %s\n", strerror(errno));
+        return 3;
+      } else {
+        return 0;
+      }
+    } else if (res == 0) {
+      LOG_ERROR("send() unexpectedly returned 0\n");
+      return 4;
+    }
+
+    /* Update pointer and size to see if we've got more to send */
+    buffer += res;
+    size   -= res;
+  }
+  
+  return 0;
+}
+
+int woe_client_input(struct woe_client * cli, const char *buffer, size_t size) {
+  unsigned int i;
+  mrb_state * mrb;
+  LOG_NOTE("Received input for client %d");
+  mrb = woe_server_get_mrb(cli->server);  
+  if (mrb) {  
+    rh_run_toplevel(mrb, "woe_on_input", "is", cli->index, buffer, size);
+  }
+  return 0;
+}
+
+int woe_client_zmp(struct woe_client * cli, int argc, char *argv[]) {
+  unsigned int i;
+  mrb_state * mrb;
+  LOG_NOTE("Received ZMP reply for client %d");
+  mrb = woe_server_get_mrb(cli->server);  
+  if (mrb) {  
+    rh_run_toplevel(mrb, "woe_begin_zmp", "ii", cli->index, argc);
+    for (i=0; i < argc; i++) {
+      rh_run_toplevel(mrb, "woe_zmp_arg", "is", cli->index, argv[i]);
+    }  
+    rh_run_toplevel(mrb, "woe_finish_zmp", "ii", cli->index, argc);
+  }
+  return 0;
+}
+
+
+
+

+ 61 - 0
src/config.c

@@ -0,0 +1,61 @@
+#include "config.h"
+
+#define _POSIX_C_SOURCE 200801L
+#define _POSIX_SOURCE 200801L
+
+#include <stdlib.h>
+#include <unistd.h>
+
+
+
+
+int woe_config_init_args(struct woe_config * config, int argc, char * argv[]) {
+  char opt;
+  
+  config->port      = 7777;
+  config->data_dir  = getenv("WOE_DATA");
+  if (!config->data_dir) {
+    config->data_dir= "data";
+  }
+  
+  config->log_file  = getenv("WOE_LOG");
+  if (!config->log_file) {
+    config->log_file = "woe.log";
+  }
+    
+  while ((opt = getopt(argc, argv, "p:d:l:")) != -1) {
+    switch (opt) {
+    case 'p':
+      config->port = atoi(optarg);
+      break;
+    
+    case 'd': 
+      config->data_dir = optarg;
+      break;
+      
+    case 'l': 
+      config->log_file = optarg;
+      break;  
+            
+    default: /* '?' */
+      break;
+    }
+  }
+  return 0;
+}
+
+
+static struct woe_config global_woe_config;
+
+struct woe_config woe_config_get() {
+  return global_woe_config;
+}
+
+struct woe_config woe_config_put(struct woe_config * config) {
+  if (config) {
+    global_woe_config = *config;
+  }  
+  return woe_config_get();
+}
+
+

+ 728 - 0
src/dynar.c

@@ -0,0 +1,728 @@
+#include "dynar.h"
+#include "mem.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define ARRAY_ROOM 16
+
+/**
+* Dynar is a DYNamic ARray of size elements each of elsz size.
+* For simplicity, dynarr is exactly sized, that is,
+* it does not allocate any extra elements but the amount requested.
+* In other words it's capacity is equal to it's size.
+*/
+struct Dynar_ {
+  char * data;
+  // use a char * pointer since that makes pointer arithmetic easier
+  int size;
+  int elsz;
+};
+
+/** Gets the array's size. Returns 0 if self is NULL. */
+int dynar_size(Dynar * self) {
+  if(!self) return 0;
+  return self->size;
+}
+
+/** Gets the amount of elements in the vector. Returns 0 if self is NULL.
+ * Alias for dynar_size(self).
+ **/
+int dynar_amount(Dynar * self) {
+  if(!self) return 0;
+  return self->size;
+}
+
+/** Gets the array's element size.  Returns 0 if self is NULL. */
+int dynar_elementsize(Dynar * self) {
+  if(!self) return 0;
+  return self->elsz;
+}
+
+/** Frees the contents of an array. Has the same effect as emptying the array. Does not call a desctructor on any elements contained! */
+Dynar * dynar_done(Dynar * self) {
+  if(!self) return NULL;
+  mem_free(self->data);
+  self->data = NULL;
+  self->size = 0;
+  return self;
+}
+
+/** Calls a destructor on the contents of the array if it is not null. 
+ The contents are considered to be pointers. Does not call dynar_free()!!! */
+Dynar * dynar_destroy(Dynar * self, MemDestructor * destroy) {
+  int index;
+  int size = dynar_size(self);
+  if(!self)     return self;
+  if(!destroy)  return NULL;
+  for(index = 0; index < size ; index++) {
+    void * ptr = dynar_getptr(self, index);    
+    destroy(ptr);
+  }
+  return self;
+}
+
+/** Calls a destructor on the contents of the array if it is not null. 
+ The contents are considered to be structs. Does not call dynar_free()!!! */
+Dynar * dynar_destroy_structs(Dynar * self, MemDestructor * destroy) {
+  int index;
+  int size = dynar_size(self);
+  if(!self)     return self;
+  if(!destroy)  return NULL;
+  for(index = 0; index < size ; index++) {
+    void * ptr = dynar_getdata(self, index);    
+    destroy(ptr);
+  }
+  return self;
+}
+
+
+/** Frees an array. Returns NULL. */
+Dynar * dynar_free(Dynar * self) {
+  dynar_done(self);
+  return mem_free(self);
+}
+
+/** Calls a destructor on the elements of the array and then frees the array */
+Dynar * dynar_free_destroy(Dynar * self, MemDestructor * destroy) {
+  dynar_destroy(self, destroy);
+  return dynar_free(self);
+}
+
+/** Calls a destructor on the elements of the array and then frees the array */
+Dynar * dynar_destroy_structs_and_free(Dynar * self, MemDestructor * destroy) {
+  dynar_destroy_structs(self, destroy);
+  return dynar_free(self);
+}
+
+
+/** Allocates a new empty array.  */
+Dynar * dynar_alloc() {
+  return STRUCT_ALLOC(Dynar);
+}  
+
+
+/** Initializes an empty array with 0 elements of size elsz each. */
+Dynar * dynar_initempty(Dynar * self, int elsz) {
+  if(!self)    return NULL;
+  if(elsz < 0) return NULL;
+  self->size = 0;
+  self->elsz = elsz;
+  return self;
+}
+
+/** Allocates a new empty array for elements of size elsz each. */
+Dynar * dynar_newempty(int elsz) {
+  Dynar * res = dynar_alloc();
+  if(!dynar_initempty(res, elsz)) {
+    return dynar_free(res);
+  }
+  return res;
+}
+
+/** Changes the size of the dynamic array. Newsize must be >= 1. */
+Dynar * dynar_size_(Dynar* self, int newsize) {
+  int elsz = dynar_elementsize(self);
+  void * newd = NULL;
+  int delta;
+  if(!self) return NULL;
+  // don't allow a newsize of 0, since that will make realloc call
+  // free on self->data, which we don't want.
+  if(newsize < 1) return NULL;
+  newd = mem_realloc(self->data, newsize * elsz);
+  if(!newd) return NULL;
+  // if we get here realloc was successful, so it should be safe to reassign
+  // self->data
+  self->data = newd;
+  // now, empty the unused new data, but only if growing! (XXX: is this ok???) 
+  delta =  newsize - self->size;
+  if(delta > 0) { 
+    memset(self->data + (self->size * self->elsz), 0, (delta * self->elsz));
+  }
+  self->size = newsize;  
+  return self;
+}
+
+/** Initializes a new array with a amount elements of size
+elsz each. */
+Dynar * dynar_init(Dynar * self, int amount, int elsz) {
+  Dynar * aid = dynar_initempty(self, elsz);
+  if(!aid) return NULL;
+  if(!dynar_size_(self, amount)) return NULL;
+  return self;
+}
+
+/** Initializes a new array with a capacity to store amount void* pointers.*/
+Dynar * dynar_initptr(Dynar * self, int amount) {
+  return dynar_init(self, amount, sizeof(void *));
+}
+
+/** Allocates a new array with amount elements of size elsz each. */
+Dynar * dynar_new(int amount, int elsz) {
+  Dynar * res = dynar_alloc();
+  if(!dynar_init(res, amount, elsz)) {
+      return dynar_free(res);
+  }
+  return res;
+}
+
+
+/** Allocates a new array that will be able to contain amount void * pointers.*/
+Dynar * dynar_newptr(int amount) {
+  return dynar_new(amount, sizeof(void *));
+}
+
+
+/** Sets the amount of elements of the the array, but ony if
+* amount bigger than the bigger than the current size.
+* Returns NULL if the array was not grown, otherwise returns self.
+*/
+Dynar * dynar_grow(Dynar * self, int amount) {
+  int mysize = dynar_size(self);
+  if (mysize >= amount) return NULL;
+  return dynar_size_(self, amount);
+}
+
+/** Checks if the index is smaller than the array's available room . */
+int dynar_sizeindex_ok(Dynar * self, int index) {
+  if(!self) return FALSE;
+  return index < dynar_size(self);
+}
+
+/** Checks if the int index is smaller than the array's available room. */
+int dynar_index_ok(Dynar * self, int index) {
+  if(!self) return FALSE;
+  if (index < 0) return FALSE;
+  return dynar_sizeindex_ok(self, (int)(index));
+}
+
+/** Returns a pointer to the index-th element of the array.
+Does no bounds checking! */
+void * dynar_getraw_unsafe(Dynar * self, int index) {
+  return self->data + (index * self->elsz);
+}
+
+/** Copies dynar_elementsize(self) of bytes from the index-th element
+of the array to out, which must be pointing to a bufer of at least
+dynar_elementsize(self). Does no bounds checking!
+Returns NULL on failure, out on success.
+*/
+void * dynar_getcopy_unsafe(Dynar * self, int index, void * out) {
+  char * cptr = (char *) dynar_getraw_unsafe(self, index);
+  int size = dynar_elementsize(self);
+  if((!self) || (!out) || (!cptr)) return NULL;
+  mem_move(out, cptr, size);
+  return out;
+}
+
+/** Copies dynar_elementsize(self) of bytes from the data pointed to
+* by ptr into the location pointed to by index.
+* Does no bounds checking!
+*/
+Dynar * dynar_putraw_unsafe(Dynar * self, int index, void * ptr) {
+  char * cptr = (char *) dynar_getraw_unsafe(self, index);
+  int size = dynar_elementsize(self);
+  if((!self) || (!ptr) || (!cptr)) return NULL;
+  mem_move(cptr, ptr, size);
+  return self;
+}
+
+/** Returns a pointer to the index-th element of the array.
+Does bounds checking and return NULL if out of bounds */
+void * dynar_getraw(Dynar * self, int index) {
+  // Bounds check
+  if(!dynar_index_ok(self, index)) { return NULL; }
+  return dynar_getraw_unsafe(self, index);
+}
+
+/** Returns a pointer to the index-th element of the array.
+Does bounds checking and return NULL if out of bounds */
+void * dynar_getcopy(Dynar * self, int index, void * ptr) {
+  // Bounds check
+  if(!dynar_index_ok(self, index)) { return NULL; }
+  return dynar_getcopy_unsafe(self, index, ptr);
+}
+
+/** Copies the dynar_elementsize(self) of bytes from the data pointed to
+* by ptr into the location pointed to by index.
+* Does bounds checking and return NULL if ouut of bounds.
+*/
+Dynar * dynar_putraw(Dynar * self, int index, void * ptr) {
+  // Bounds check
+  if(!dynar_index_ok(self, index)) { return NULL; }
+  return dynar_putraw_unsafe(self, index, ptr);
+}
+
+/** Stores a pointer at the index of the array.
+* Does bounds checking. dynar_elementsize(self) sould have been
+* initialized as sizeof(void *) by using dynar_newptr
+*/
+void * dynar_putptr(Dynar * self, int index, void * ptr) {
+  return dynar_putraw(self, index, &ptr);
+  // use &ptr because we want to put the contents of the pointer,
+  // not the pointer itself. 
+}
+
+/** Returns a pointer that was stored at the index index of the array. */
+void * dynar_getptr(Dynar * self, int index) {
+  void * res = NULL; 
+  if(!dynar_getcopy(self, index, &res)) return NULL;
+  // use &ptr because we want to fetch the contents of the pointer,
+  // which will be copied from the element of the array.
+  return res;
+}
+
+/** Copies the element that *ptr points to into this array at position
+index */
+void * dynar_putdata(Dynar * self, int index, void * ptr) {
+  return dynar_putraw(self, index, ptr);
+}
+
+/** Returns a pointer to the index-th element of the array. */
+void * dynar_getdata(Dynar * self, int index) {
+  return dynar_getraw(self, index);
+}
+
+/* Applies quick sort to the array using the given comparator. */
+Dynar * dynar_qsort(Dynar * self, DynarCompare  * compare) {
+  void * base; int nmemb; size_t size;
+  if(!self) return NULL;
+  base  = self->data;
+  nmemb = self->size;
+  size  = self->elsz;
+  qsort(base, nmemb, size, compare);
+  return self;
+}
+
+/* Applies a binary search to the array using the given comparator. 
+ User must call dynar_qsort first. */
+void * dynar_bsearch(Dynar * self, const void * key, DynarCompare  * compare) {
+  void * base; int nmemb; size_t size;
+  if (!self) return NULL;
+  base  = self->data;
+  nmemb = self->size;
+  size  = self->elsz;
+  return bsearch(key, base, nmemb, size, compare);
+}
+
+/* Puts NULL in every element of this array using dynar_putptr */
+Dynar * dynar_putnullall(Dynar * self) {
+  int stop = dynar_size(self);
+  int index;
+  for (index = 0; index < stop; index++) {
+    dynar_putptr(self, index, NULL);
+  }
+  return self; 
+}
+
+
+
+/* Iterator helper: fill in every->now as data. */
+Every * dynar_everynow_data(Every * every) {
+  every->now   = dynar_getdata(every->on, every->index);
+  if(every->now) return every;
+  return NULL;
+}
+
+/* Iterator helper: fill in every->now as pointer. */
+Every * dynar_everynow_ptr(Every * every) {
+  every->now   = dynar_getptr(every->on, every->index);
+  if(every->now) return every;
+  return NULL;
+}
+
+/* Iterator helpers: init */
+Every  * dynar_everyinit_data(Every * every) {
+  every->index = 0;
+  return dynar_everynow_data(every);
+}
+
+/* Iterator helpers: next */
+Every  * dynar_everynext_data(Every * every) {
+  every->index++;
+  return dynar_everynow_data(every);
+}
+
+/* Iterator helpers: put. */
+void  * dynar_everyput_data(Every * every, void * data) {
+  return dynar_putdata(every->on, every->index, data);
+}
+
+/* Iterator helpers: done. */
+void  * dynar_everydone(Every * every) {
+  return every;
+}
+
+/* Iterator helpers: init pointers */
+Every  * dynar_everyinit_ptr(Every * every) {
+  every->index = 0;
+  return dynar_everynow_ptr(every);
+}
+
+/* Iterator helpers: next pointers */
+Every  * dynar_everynext_ptr(Every * every) {
+  every->index++;
+  return dynar_everynow_ptr(every);
+}
+
+/* Iterator helpers: put pointers. */
+void  * dynar_everyput_ptr(Every * every, void * data) {
+  return dynar_putptr(every->on, every->index, data);
+}
+
+
+/* Iterator helper table. */
+static EveryActs dynar_every_data_acts_ = {
+  dynar_everydone,
+  dynar_everyinit_data,
+  dynar_everynext_data,
+  dynar_everyput_data  
+};
+
+/* Iterator helper table. */
+static EveryActs dynar_every_ptr_acts_ = {
+  dynar_everydone,
+  dynar_everyinit_ptr,
+  dynar_everynext_ptr,
+  dynar_everyput_ptr
+};
+
+
+/** Iterates over the data. Call every_free when done. */
+Every * dynar_every_data(Dynar * dynar) {
+  return every_new(&dynar_every_data_acts_);
+}
+
+/** Iterates over the pointers in this array. Call every_free when done. */
+Every * dynar_every_ptr(Dynar * dynar) {
+  return every_new(&dynar_every_ptr_acts_);
+}
+
+
+/* Walks over the array using the each interface, accessing
+the data as pointers. eachdo should return non-null to break the iteration,
+the data thus found is returned bu this function. */
+void * dynar_each_ptr(Dynar * self, EachDo * eachdo, void * extra) {
+  Each each;
+  int index;
+  int size = dynar_size(self);  
+  each_init(&each, self, extra); 
+  for(index = 0; index < size ; index++) {
+    void * aid;
+    void * ptr = dynar_getptr(self, index);    
+    each_next(&each, ptr);
+    aid = eachdo(&each);
+    if (aid) return aid;
+  }
+  return NULL;
+}
+
+/* Walks over the array using the Each interface, accessing
+the data as stored structs. */
+void * dynar_each_data(Dynar * self, EachDo * eachdo, void * extra) {
+  Each each;
+  int index;
+  int size = dynar_size(self);
+  each_init(&each, self, extra);
+  for(index = 0; index < size ; index++) {
+    void * aid;
+    void * ptr = dynar_getdata(self, index);
+    each_next(&each, ptr);
+    aid = eachdo(&each);
+    if (aid) return aid;
+  }
+  return NULL;
+}
+
+/* Walks over the array using the walker interface, accessing
+the data as stored pointers. */
+void * dynar_walkptrbetween(Dynar * self, int low, int high, 
+                            Walker * walker, void * extra) {
+  int index;
+  int size = dynar_size(self);
+  low  = (low < 0)      ?  0   : low;
+  high = (high > size)  ? size : high;
+  for(index = low; index < high ; index++) {
+    void * aid;
+    void * ptr = dynar_getptr(self, index);
+    aid        = walker(ptr, extra);
+    if (aid) return aid;
+  }
+  return NULL;
+}
+
+/* Walks over the array using the walker interface, accessing
+the data as stored structs. */
+void * dynar_walkptr(Dynar * self, Walker * walker, void * extra) {
+  return dynar_walkptrbetween(self, 0, dynar_size(self), walker, extra);
+}
+
+/* Walks over the array using the walker interface, accessing
+the data as stored pointers. */
+void * dynar_walkdatabetween(Dynar * self, int low, int high,
+                             Walker * walker, void * extra) {
+  int index;
+  int size = dynar_size(self);
+  low  = (low < 0)      ?  0   : low;
+  high = (high > size)  ? size : high;
+  for(index = low; index < high ; index++) {
+    void * aid;
+    void * ptr = dynar_getdata(self, index);
+    aid        = walker(ptr, extra);
+    if (aid) return aid;
+  }
+  return NULL;
+}
+
+/* Walks over the array using the walker interface, accessing
+the data as stored structs. */
+void * dynar_walkdata(Dynar * self, Walker * walker, void * extra) {
+  return dynar_walkdatabetween(self, 0, dynar_size(self), walker, extra);
+}
+
+/* Walks over the array updating it, using the walker interface, accessing
+the data as stored pointers. */
+void * dynar_walkmapptrbetween(Dynar * self, int low, int high, 
+                            Walker * walker, void * extra) {
+  int index;
+  int size = dynar_size(self);
+  low  = (low < 0)      ?  0   : low;
+  high = (high > size)  ? size : high;
+  for(index = low; index < high ; index++) {
+    void * aid;
+    void * ptr = dynar_getptr(self, index);
+    aid        = walker(ptr, extra);
+    dynar_putptr(self, index, ptr);
+  }
+  return NULL;
+}
+
+/* Walks over the array updating it using the walker interface, accessing
+the data as stored structs. */
+void * dynar_walkmapptr(Dynar * self, Walker * walker, void * extra) {
+  return dynar_walkmapptrbetween(self, 0, dynar_size(self), walker, extra);
+}
+
+/* Resizes a dynar filled with pointers, and if the dynar shrinks, calls the 
+ * given destructor on all elements that are to be removed. Returns self on success 
+ * and NULL on failure. Even in this case, the superfluous elements 
+  fmay have been removed. */
+Dynar * dynar_resize(Dynar * self, int newsize, MemDestructor * destroy) {
+  int index; 
+  int last;
+  if (!self) return NULL;
+  last = dynar_size(self);
+  for(index = newsize; index < last; index ++) {
+     void * aid = dynar_getptr(self, index);
+     dynar_putptr(self, index, destroy(aid));
+  }
+  return dynar_size_(self, newsize);
+}
+
+
+
+/** Makes a new empty dynar to contain long integers  */
+Dynar * dynar_new_long() {
+  return dynar_newempty(sizeof(long));
+}
+
+/** Stores a long integer into a dynar (preferrably created with dynar_new_long */
+Dynar * dynar_put_long(Dynar * self, int index, long value) {
+  return dynar_putdata(self, index, &value);
+}
+
+/** Gets a long integer from a dynar */
+Dynar * dynar_get_long(Dynar * self, int index, long * value) {
+  return dynar_getcopy(self, index, value);
+}
+
+/** Grows the size of the array by 1 and appends the long in the last position */
+Dynar * dynar_append_long(Dynar * self, long value) {
+  int pos = dynar_size(self);
+  if (!dynar_size_(self, pos+1)) return NULL;
+  return dynar_put_long(self, pos, value);  
+}
+
+
+
+
+
+
+/**
+* Lilis is a doubly Linked List that points to it's members via void pointers 
+* but does not own them  in the sense that it doesn't clean them up unless requested.
+*/
+struct Lilis_ {
+  Lilis * next;
+  Lilis * prev;
+  void  * data;
+};
+
+
+/**
+* frees the tail of a llist only. Does nothing to self or other.
+*/
+
+Lilis * lilis_freetail(Lilis * self) {
+  Lilis * next, * now;
+  if(!self) return NULL;
+  now = self->next;   // skip current node, that will be deleted in lilis_free
+  while (now) { 
+    next = now->next; // already get next one
+    mem_free(now);    // now we can safely free the current node
+    now  = next;      // set current to next one.
+  }
+  return self;
+}
+
+
+/** Frees the tail of the list pointed to by self. It does not free any of the 
+emlements contained in * data. Does not alter self->prev, and doesn't 
+free that either. */
+Lilis * lilis_done(Lilis * self ) {  
+  if(!self) return NULL;
+  lilis_freetail(self);
+  self->data = NULL;
+  self->next = NULL;
+  return self;
+}
+
+/** Frees a linked list node and it's tail. Returns NULL. */
+Lilis * lilis_free(Lilis * self) {
+  lilis_done(self);
+  return mem_free(self);
+}
+
+/** Allocates a new empty linked list.  */
+Lilis * lilis_alloc() {
+  return STRUCT_ALLOC(Lilis);
+}  
+
+
+/** Initializes a linked list node */
+Lilis * lilis_init(Lilis * self, Lilis * next, Lilis * prev, void * data) {
+  if(!self)    return NULL;  
+  self->data = data;
+  self->next = next;
+  self->prev = prev;
+  return self;
+}
+
+/** Initializes a new linked list node that points to nothing. */
+Lilis * lilis_initempty(Lilis * self) {
+  return lilis_init(self, NULL, NULL, NULL);
+}
+
+
+/** Returns a newly allocatd linked list node. */
+Lilis * lilis_new(Lilis * next, Lilis * prev, void * data) {
+  Lilis * res = lilis_alloc();
+  if(!lilis_init(res, next, prev, data)) {
+    return lilis_free(res);
+  }
+  return res;
+}
+
+/** Returns a newly allocated linked list node that points to nothing. */
+Lilis * lilis_newempty() {
+  return lilis_new(NULL, NULL, NULL);
+}
+
+/** Appends a node to the current one, inserting it if needed.
+* returns other.
+*/
+Lilis * lilis_add(Lilis * self, Lilis * other)  {
+  Lilis * oldnext = self->next;
+  if ((!self) || (!other)) return NULL;
+  if (oldnext) {
+    oldnext->prev = other;   // next one's previous becomes the inserted link    
+    other->next   = oldnext; // inserted link's next is the old next
+  }
+  // these two belowear always needed even if there is no oldnext. 
+  other->prev   = self;    // and other's previous one is self.
+  self->next    = other;   // and curent next is the inserted link
+  return other;
+}
+
+/**  Creates a new node and adds it */
+Lilis * lilis_addnew(Lilis * self, void * data) {
+  Lilis * other = lilis_new(NULL, NULL, data);
+  return lilis_add(self, other);
+}
+
+
+/** Removes the node that follows self, but does NOT call lilis_free on it. 
+* returns the removed node
+*/
+Lilis * lilis_removenext(Lilis * self) {
+  Lilis * next, * nextnext;
+  if(!self) return NULL;
+  next = self->next;
+  if(!next) return NULL;
+  nextnext = next->next;
+  if(nextnext) {
+    nextnext->prev = self;
+    self->next     = nextnext;
+  } else {
+    self->next     = NULL;
+  }
+  return next;
+}
+
+/** Removes self, modifies prev if this is needed, but does NOT call 
+* lilis_free on it. 
+* returns the removed node
+*/
+Lilis * lilis_remove(Lilis * self) {
+  Lilis * prev, * next;
+  if(!self) return NULL;
+  next = self->next;
+  prev = self->prev;
+  if(next) { // link prev node to next node, so this node is cut out. 
+    prev->next = next;
+    next->prev = prev;
+  } else { // no next node, prev also gets no next node.
+    prev->next = NULL;
+  }
+  // finally unlink self.
+  self->next = NULL;
+  self->prev = NULL;
+  return next;
+
+}
+
+/** Removes the node self, and calls lilis_free on it.
+* returns NULL
+*/
+Lilis * lilis_erase(Lilis * self) {
+  Lilis * eraseme = lilis_remove(self);
+  return lilis_free(eraseme);
+}
+
+
+
+/** Removes the node that follows self, and calls lilis_free on it.
+* returns NULL
+*/
+Lilis * lilis_erasenext(Lilis * self) {
+  Lilis * eraseme = lilis_removenext(self);
+  return lilis_free(eraseme);
+}
+
+/** Gets the next node in the list. */
+Lilis * lilis_next(Lilis * self) {
+  if(!self) return NULL;
+  return self->next;
+}
+
+/** Gets the previous  node in the list. */
+Lilis * lilis_previous(Lilis * self) {
+  if(!self) return NULL;
+  return self->prev;
+}
+
+/** Gets the data of the list node */
+void * lilis_data(Lilis * self) {
+  if(!self) return NULL;
+  return self->data;
+}

+ 360 - 0
src/esh.c

@@ -0,0 +1,360 @@
+
+#include "esh.h"
+#include <stdio.h>
+#include <string.h>
+
+/* Returns true if the string is in an unusable state, false if not. */
+int esh_bad_p(char **me, size_t * size, size_t * space) {
+  if (!me) return !0;
+  if (!*me) return !0;
+  if (!size) return !0;
+  if (!space) return !0;
+  return 0;
+}
+
+
+/* Makes the string an empty string by writing a nul character at positon 0
+ * and setting size to 0. */
+char * esh_make_empty(char ** me, size_t * size, size_t * space) {
+  if (esh_bad_p(me, size, space)) return NULL;
+  if ((*space) < 1) return NULL;
+  (*me)[0] = '\0';
+  (*size)  = 0;
+  return (*me);
+}
+
+/* Ensures the string is NUL terminated. */
+char * esh_nul_terminate(char ** me, size_t * size, size_t * space) {
+  if (esh_bad_p(me, size, space)) return NULL;
+  if (((*size) + 1) > (*space)) {
+    (*me)[(*space)] = '\0';
+    (*size) = (*space) - 1;
+  }  else { 
+    (*me)[(*size)+1] = '\0';
+  }
+  return (*me);
+}
+
+
+/* allocates a new string buffer with init_space. If init_space == 0 
+ *  uses 1024 in stead.*/
+char * esh_alloc(char ** me, size_t * size, size_t * space, size_t init_space) {
+  if (!me) return NULL;
+  if (!space) return NULL;
+  if (!size) return NULL;
+  if (init_space < 1) init_space = 1024; 
+  (*me) = calloc(init_space, 1);
+  (*space) = init_space;
+  if (!*me) return NULL;
+  return esh_make_empty(me, size, space);
+}
+
+/* Grows the given string's space. */
+char * esh_grow(char ** me, size_t  * size, size_t * space, size_t new_space) {
+  char * aid;
+  (void) size;
+  if (!me) return NULL;
+  if (!*me) return NULL;
+  if (new_space <= (*space)) return NULL;
+  
+  aid = realloc(*me, new_space);
+  if (!aid) return NULL;
+    
+  memset(aid + (*space), '\0', new_space - (*space));
+  (*space) = new_space;
+  (*me)    = aid;   
+  return (*me);
+} 
+
+/* Makes sure there is enough space to add amount characters. */
+char * esh_ensure_space(char ** me, size_t * size, size_t * space, size_t grow_by) {
+  if (!me) return NULL;
+  if (!*me) return NULL;
+  if (!size) return NULL;
+  if (!space) return NULL;
+  
+  if ((*space) < ((*size) + grow_by)) { 
+    if (!esh_grow(me, size, space, (*space) + grow_by + 255)) return NULL;
+  }
+  
+  return (*me);
+} 
+
+char * esh_append_char(char ** me, size_t * size, size_t * space, char ch) {
+  if (!esh_ensure_space(me, size, space, 1)) return NULL;
+    
+  (*me)[(*size)] = ch;
+  (*size) = (*size) + 1;
+  return (*me);
+}
+
+
+char * esh_append_buf(char  ** me, size_t * size, size_t * space, char * buf, size_t bufsize) {
+  if (!esh_ensure_space(me, size, space, bufsize + 1)) return NULL;
+  strncpy((*me) + (*size), buf, bufsize);
+  (*size) = (*size) + bufsize;
+  return esh_nul_terminate(me, size, space);
+}
+
+char * esh_append_cstr(char ** me, size_t * size, size_t * space, char * str) {
+  return esh_append_buf(me, size, space, str, strlen(str));
+}
+
+char * esh_init_buf(char ** me, size_t * size, size_t * space, char * buf, size_t bufsize) {
+  if (!size) return NULL;
+  if (!esh_make_empty(me, size, space)) return NULL; 
+  return esh_append_buf(me, size, space, buf, bufsize);
+} 
+
+char * esh_init_cstr(char ** me, size_t * size, size_t * space, char * buf) {
+  return esh_init_buf(me, size, space, buf, strlen(buf));
+} 
+
+char * esh_new_cstr(char **me, size_t * size, size_t * space, char * buf) {
+  size_t bufsize;
+  if (!buf) return esh_new_empty(me, size, space);
+  bufsize = strlen(buf);
+  return esh_new_buf(me, size, space, buf, bufsize);
+}
+
+
+char * esh_new_buf(char **me, size_t * size, size_t * space, char * buf, size_t bufsize) {
+  if (!esh_alloc(me, size, space, bufsize)) return NULL;
+  return esh_init_buf(me, size, space, buf, bufsize);
+}
+
+/* Creates a new string with the given space and initialies it from init. */
+char * esh_new(char **me, size_t * size, size_t * space, char * init) {
+  return esh_new_buf(me, size, space, init, strlen(init));
+}
+
+/* Creates a new empty string with enough space. */
+char * esh_new_empty(char **me, size_t * size, size_t * space) {
+  return esh_new(me, size, space, "");
+}
+
+/* Frees the string and sets it to NULL. */
+char * esh_free(char ** me, size_t * size, size_t * space) {
+  if (!me) return NULL;
+  if (!*me) return NULL;
+  free(*me);
+  *me    = NULL;
+  *size  = 0;
+  *space = 0;
+  return NULL;
+}
+
+/** Reads in a file into the buffer. */
+char * esh_read_file(char ** me, size_t * size, size_t * space, FILE * file) {
+  char aid[1024];
+  int read;
+  if (esh_bad_p(me, size, space)) return NULL;
+  if (!file) return NULL;
+  
+  while(!feof(file)) { 
+    read = fread(aid, 1, sizeof(aid), file);
+    if (read > 0) { 
+      esh_append_buf(me, size, space, aid, read);
+    }
+  }
+  return (*me);
+}
+
+/** Reads a named file into the buffer. */
+char * esh_read_filename(char ** me, size_t * size, size_t * space, char * fn) {
+  char * res;
+  FILE * file = fopen(fn, "r");
+  res = esh_read_file(me, size, space, file);
+  if (file) fclose(file);
+  return res;
+}
+
+/** Joins a NULL terminated list onto va*/
+char * esh_join_va(char ** me, size_t * size, size_t * space, va_list strings) {
+  char * next;
+  while ( (next = va_arg(strings, char *)) ) {
+    if (!esh_append_cstr(me, size, space, next)) return NULL;
+  }
+  return *me;
+}
+
+/* Joins a NULL terminated lists of strings onto me */
+char * esh_join(char ** me, size_t * size, size_t * space, ...) {
+  char * res;
+  va_list strings;
+  va_start(strings, space);
+  res = esh_join_va(me, size, space, strings);
+  va_end(strings);
+  return res;
+}
+
+char * esh_init_join_cstr_va(char ** me, size_t * size, size_t * space, char * first, va_list strings) {
+  if (!esh_init_cstr(me, size, space, first)) return NULL;
+  return esh_join_va(me, size, space, strings);
+}
+
+char * esh_init_join_cstr(char ** me, size_t * size, size_t * space, char * first, ...) 
+{
+  char * res;
+  va_list strings;
+  va_start(strings, first);
+  res = esh_init_join_cstr_va(me, size, space, first, strings);
+  va_end(strings);
+  return res;
+}
+
+
+char * esh_new_join_cstr_va(char ** me, size_t * size, size_t * space, char * first, va_list strings) {
+  if (!esh_new_cstr(me, size, space, first)) return NULL;
+  return esh_join_va(me, size, space, strings);
+}
+
+char * esh_new_join_cstr(char ** me, size_t * size, size_t * space, char * first, ...) 
+{
+  char * res;
+  va_list strings;
+  va_start(strings, first);
+  res = esh_init_join_cstr_va(me, size, space, first, strings);
+  va_end(strings);
+  return res;
+}
+
+
+
+/** Deletes n bytes */
+
+#define SWIS_EXPAND(SWIS) &((SWIS)->text), &((SWIS)->size), &((SWIS)->space)
+
+int woesb_bad_p(struct woesb * me) {
+  return (esh_bad_p(SWIS_EXPAND(me)));
+}
+
+struct woesb * woesb_make_empty(struct woesb * me) {
+  if(!esh_make_empty(SWIS_EXPAND(me))) return NULL;
+  return me;
+}
+
+
+struct woesb * woesb_nul_terminate(struct woesb * me) {
+  if(!esh_nul_terminate(SWIS_EXPAND(me))) return NULL;
+  return me;  
+}
+
+struct woesb * woesb_alloc(struct woesb * me, size_t init_space) {
+  if(!esh_alloc(SWIS_EXPAND(me), init_space)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_grow(struct woesb * me, size_t new_space) {
+  if(!esh_grow(SWIS_EXPAND(me), new_space)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_ensure_space(struct woesb * me, size_t grow_by) {
+  if(!esh_ensure_space(SWIS_EXPAND(me), grow_by)) return NULL;
+  return me;
+}
+
+
+struct woesb * woesb_append_char(struct woesb * me, char ch) {
+  if(!esh_append_char(SWIS_EXPAND(me), ch)) return NULL;
+  return me;
+}
+
+
+struct woesb * woesb_append_buf(struct woesb * me, char * buf, size_t bufsize) {
+  if(!esh_append_buf(SWIS_EXPAND(me), buf, bufsize)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_append_cstr(struct woesb * me, char * str) {
+  if(!esh_append_cstr(SWIS_EXPAND(me), str)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_init_buf(struct woesb * me, char * buf, size_t bufsize) {
+  if(!esh_init_buf(SWIS_EXPAND(me), buf, bufsize)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_init_cstr(struct woesb * me, char * buf) {
+  if(!esh_init_cstr(SWIS_EXPAND(me), buf)) return NULL;
+  return me;
+}
+
+
+struct woesb * woesb_new_buf(struct woesb * me, char * buf, size_t bufsize) {
+  if(!esh_new_buf(SWIS_EXPAND(me), buf, bufsize)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_new(struct woesb * me, char * init) {
+  if(!esh_new(SWIS_EXPAND(me), init)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_new_empty(struct woesb * me) {
+  if(!esh_new_empty(SWIS_EXPAND(me))) return NULL;
+  return me;
+}
+
+struct woesb * woesb_free(struct woesb * me) {
+  esh_free(SWIS_EXPAND(me)); 
+  return NULL;
+}
+
+struct woesb * woesb_read_file(struct woesb * me, FILE * file) {
+  if(!esh_read_file(SWIS_EXPAND(me), file)) return NULL;
+  return me;
+}
+ 
+struct woesb * woesb_read_filename(struct woesb * me, char * fn) {
+  if(!esh_read_filename(SWIS_EXPAND(me), fn)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_join_va(struct woesb * me, va_list strings) {
+  if (!esh_join_va(SWIS_EXPAND(me), strings)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_join(struct woesb * me, ...) {
+  struct woesb * res;
+  va_list strings;
+  va_start(strings, me);
+  res = woesb_join_va(me, strings);
+  va_end(strings);
+  return res;
+}
+
+struct woesb * woesb_init_join_va(struct woesb * me, char * first, va_list strings) {
+  if (!esh_init_join_cstr_va(SWIS_EXPAND(me), first, strings)) return NULL;
+  return me;
+}
+
+
+struct woesb * woesb_init_join(struct woesb * me, char * first, ...) {
+  struct woesb * res;
+  va_list strings;
+  va_start(strings, first);
+  res = woesb_init_join_va(me, first, strings);
+  va_end(strings);
+  return res;  
+}
+
+
+struct woesb * woesb_new_join_va(struct woesb * me, char * first, va_list strings) {
+  if (!esh_new_join_cstr_va(SWIS_EXPAND(me), first, strings)) return NULL;
+  return me;
+}
+
+struct woesb * woesb_new_join(struct woesb * me, char * first, ...) {
+  struct woesb * res;
+  va_list strings;
+  va_start(strings, first);
+  res = woesb_new_join_va(me, first, strings);
+  va_end(strings);
+  return res;  
+}
+
+

+ 117 - 0
src/every.c

@@ -0,0 +1,117 @@
+#include "mem.h"
+#include "every.h"
+
+/** This file contains two iterator or iterator interfaces,
+namely Every and Each. Every uses a struct to
+keep track of it's state, while Each uses a callback function. */
+
+/** Allocates an uninitialized every object. */
+Every * every_alloc() {
+  return STRUCT_ALLOC(Every);
+}
+
+/** Deinitializes an every object.  */
+Every * every_done(Every * self) {
+  if(!self) return NULL;
+return self;
+}
+
+/** Frees an every object. */
+Every * every_free(Every * self) {
+  every_done(self);
+  mem_free(self);
+  return NULL;
+}
+
+/** Initializes an every object. */
+Every * every_init(Every * self, EveryActs * acts) {
+  if(!self) return NULL;
+  self->acts    = acts;
+  if(self->acts->init)
+    return self->acts->init(self);
+  return self;
+}
+
+/** Creates a new every object. */
+Every * every_new(EveryActs * acts) {
+  Every * self = every_alloc();
+  return every_init(self, acts);
+} 
+
+/** Gets the current object pointed to by the Every object. */
+void * every_get(Every * self) {
+  if(!self) return NULL;
+  return self->now;
+}
+
+/** Puts an object at the current position if possible.
+* Returns NULL if the put failed.
+*/
+void * every_put(Every * self, void * data) {
+  if(!self) return NULL;
+  if(self->acts->put) {
+    return self->acts->put(self, data);
+  }
+  return NULL;
+}
+
+/** Moves on to the next object and returns it.
+* Return NULL if no next object.
+*/
+Every * every_next(Every * self, void * data) {
+  if(self->acts->next) {
+    self->acts->next(self);
+    return every_get(self);
+  }
+  return NULL;
+}
+
+
+/** Initializes an EachElement */
+Each * each_init(Each * self, void * on, void * data) {
+  if(!self) return NULL;
+  self->on    = on;
+  self->extra = data;
+  self->now   = NULL;
+  self->index = -1;  
+  return self;
+}
+
+/** Moves on to next element, incrementing index. */
+Each * each_next(Each * self, void * now) {
+  if(!self) return NULL;
+  self->now = now;
+  self->index++;
+  return self;
+}
+
+/** Gets now pointer of each struct */
+void * each_now(Each * self) {
+  if(!self) return NULL;
+  return self->now;
+}  
+
+/** Gets on pointer of each struct */
+void * each_on(Each * self) {
+  if(!self) return NULL;
+  return self->on;
+}
+
+/** Gets extra data pointer of each struct */
+void * each_extra(Each * self) {
+  if(!self) return NULL;
+  return self->extra;
+}
+
+/** Gets index pointer of each struct, -1 if the struct is NULL. */
+int each_index(Each * self) {
+  if(!self) return -1;
+  return self->index;
+}
+
+
+
+
+
+
+

+ 0 - 10
src/libtelnet.pc.in

@@ -1,10 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=${prefix}/include
-
-Name: libtelnet
-Description: TELNET protocol handling library
-Version: @VERSION@
-Libs: -L${libdir} -ltelnet @LIBS@
-Cflags: -I${includedir}

+ 107 - 2
src/main.c

@@ -1,9 +1,114 @@
+#define _POSIX_C_SOURCE 200801L
+#define _POSIX_SOURCE 200801L
+
 #include <stdlib.h>
-#include "libtelnet.h"
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+
+
+#include "server.h"
+#include "monolog.h"
+#include "config.h"
+#include "state.h"
+#include "rh.h"
+#include "toruby.h"
+
+
+
+DEFINE_FILE_LOGGER(woe_file_logger);
+DEFINE_STDOUT_LOGGER(woe_stdout_logger);
+DEFINE_STDERR_LOGGER(woe_stderr_logger);
+
 
+FILE * woe_start_monolog(char * logname) {
+  FILE * fout;
+
+  monolog_init();
+  LOG_ENABLE_ERROR();
+  monolog_enable_level("WARNING");
+  monolog_enable_level("NOTE");
+
+  fout = fopen(logname, "w");
+  
+  monolog_add_logger(NULL, &woe_stdout_logger);
+  
+  if (fout) {
+    monolog_add_logger(fout, &woe_file_logger);
+    fprintf(stderr, "Opened log file %s %p\n", logname, fout);
+  } else {
+    fprintf(stderr, "Could not open log file %s\n", logname);
+  }
+    
+  return fout;
+}
+
+
+
+void woe_stop_monolog() {  
+  monolog_done();
+}
 
 
+int main(int argc, char * argv[]) {
+  sigset_t            signal_set  = { { 0 } };
+  sigset_t            signal_old  = { { 0 } };
+  siginfo_t           signal_info = { 0 };
+  struct timespec     signal_time = { 0, 10 };
+  
+  struct woe_state    state  = { 0 };
+  struct woe_config   config = { 0 };  
+  state.config     = &config;
+    
+  FILE * logfile   = NULL;
+  state.mrb        = mrb_open();
+  state.mrb->ud    = &state;
+  tr_init(state.mrb);
+  woe_config_init_args(state.config, argc, argv);
+  
+  logfile          = woe_start_monolog(state.config->log_file);
+  
+  state.server     = woe_server_new(state.config->port);
+  
+  woe_server_set_mrb(state.server, state.mrb);
+  
+  /* Handle all signals, except a few. */
+  sigfillset(&signal_set);
+  sigdelset(&signal_set, SIGKILL);
+  sigdelset(&signal_set, SIGTERM);
+  sigdelset(&signal_set, SIGINT);
+  sigdelset(&signal_set, SIGSEGV);
+  sigdelset(&signal_set, SIGTSTP);
+  
+  /* Ignore them all, because we will wait for them in stead. */
+  sigprocmask(SIG_SETMASK, &signal_set, &signal_old);
 
-int main(void) {
+  
+  rh_run_script(state.mrb, "main.rb");
+  
+  if (woe_server_listen(state.server) > 0) {
+     LOG_ERROR("Cannot listen. Stop.");   
+  } else {
+    rh_run_toplevel(state.mrb, "woe_on_start", "");
+    while (woe_server_busy(state.server)) {
+      int caught = 0;
+      
+      woe_server_update(state.server, 1);
+      caught = sigtimedwait(&signal_set, NULL, &signal_time);
+      if (caught > 0) {
+        LOG_NOTE("Received signal %d\n", caught);
+        rh_run_toplevel(state.mrb, "woe_on_signal", "i", caught);
+        /* woe_server_request_shutdown(state.server); */
+      }
+    }
+  }
+ 
+  rh_run_toplevel(state.mrb, "woe_on_stop", "");
+  woe_server_free(state.server);
+  mrb_close(state.mrb);
+  LOG_NOTE("Shutting down WOE\n");
+  
+  woe_stop_monolog();
   return 0;
 }

+ 257 - 0
src/mem.c

@@ -0,0 +1,257 @@
+/* Memory handling, malloc wrapers, etc */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mem.h"
+
+
+/* Calls exit(EXIT_FAILURE) if ptr is NULL. Is message is not null, prints this on stderr.  */
+void * exit_if_null(void * ptr, char * message) {
+  if(!ptr) {
+    if(message) {
+      fprintf(stderr, "%s\n", message);
+    }
+    exit(EXIT_FAILURE);
+  }
+  return ptr;
+}
+
+#define EXIT_ON_NOMEMORY(PTR) exit_if_null(PTR, "Out of memory!")
+
+/** Wrapper for calloc/malloc */
+void * mem_calloc(size_t amount, size_t size) {
+  void * ptr = calloc(amount, size);
+  return EXIT_ON_NOMEMORY(ptr);
+}
+
+/** Wrapper for calloc/malloc */
+void * mem_alloc(size_t size) {
+  void * ptr = mem_calloc(1, size);
+  return EXIT_ON_NOMEMORY(ptr);
+}
+
+/** Wrapper for free */
+void * mem_free(void * ptr) {
+  free(ptr);
+  return NULL;
+}
+
+/** Wrapper for realloc */
+void * mem_realloc(void *ptr, size_t newsize) {
+  void * aid = realloc(ptr, newsize);
+  return EXIT_ON_NOMEMORY(aid);
+}
+
+/** Resizes memory, taking care not to lose ptr* if the reallocation
+fails. */
+void * mem_resize(void ** ptr, size_t newsize) {
+  if(*ptr) { 
+    void * res = mem_realloc((*ptr), newsize);
+    if(res) {
+      (*ptr) = res;
+      return res;
+    }
+  }
+  return NULL;
+}
+
+/** Wrapper for memmove, for consistency */
+void * mem_move(void * dest, void * src, size_t size) {
+  return memmove(dest , src, size);
+}
+
+/* Thoughts on hierarchical data structures.
+* Often, in C data is allocatedin a hierarchical way. 
+* It's useful to have functions that take this into consideration...
+*/ 
+
+typedef struct Memtree_ Memtree;
+
+struct Memtree_ {
+  void * memory;
+  size_t size;
+  Memtree * next, * parent, * child;
+};
+
+#ifdef COMMENT_
+/* Tree memory allocator. */
+void * mem_tree_alloc(void * vparent, size_t size) {
+  char * aid;
+  Memtree * me, * next;
+  Memtree * parent = vparent;
+  aid        = mem_calloc(1, size + sizeof(Memtree));
+  if (!aid) return NULL;
+  me         = (Memtree *) aid;
+  me->memory = aid + sizeof(Memtree); 
+  me->size   = size;
+  if (parent) {
+    me->parent = parent;
+    if (parent->child) {
+      next = parent->child;
+    } else {
+      next = NULL
+    }
+        
+    parent->child = me;
+    me->next      = next;
+  } else {
+    me->parent   = NULL;
+    me->next     = NULL;
+    me->child    = NULL;
+  }
+  return me->memory;
+}
+
+
+void mem_tree_free(void * vparent) {
+  char * aid;
+  Memtree * me, * next;
+  Memtree * parent = vparent;
+  aid        = vparent;
+  if (!aid) return;
+  me         = (Memtree *) (aid + sizeof(Memtree));
+  if (!me) return;
+  me->memory = NULL;
+  me->size   = 0; 
+  if (me->child) {
+    mem_tree_free(me->child);
+  } else if (me->next) {
+    for (next = me->next; next; next = next->next) {
+      
+      mem_tree_free(next);
+    } 
+  } else {
+    next = NULL
+  }
+  
+  mem_free(me);  
+}
+
+#endif
+
+/* 
+* Thoughts on memory allocation in C. 
+*
+* In theory, memory allocations could just use malloc an free and 
+* that would be enough, however, in practise there are several factors that
+* mean that just calling free() on any pointer won't free all allocated
+* memory and resources associated to it. In general, when a pointer to 
+* a resource needs to be cleaned up, there are 3 concens to take care of:
+*
+* 1) Cleaning up of non-memory resources like OS handles, API haandles, 
+* FILEs, etc. that must be closed or cleaned up before the pointer/struct 
+* that refers to them may be deallocated. In general, this could be 
+* called deinitialization.
+*
+* 2) The second problem is that the pointer may point to a complex data
+* structure that may have been allocated my several mallocs. In this case
+* multiple frees are needed. This could be called deconstruction.
+* 
+* 3) Finally the pointer will usually point to allocated memory that must 
+* be deallocated. For this, free could suffice, but not in all cases.
+* This could be deallocation.
+*
+* These 3 steps are of course mirrored when the resource is created, as can 
+* be seen in the diagram below.
+* 
+*               step                  state of resource
+*
+* Before creation:                  : VOID
+*  
+*           /- allocation           : ALLOCATED
+* Creation {-- construction         : CONSTRUCTED
+*           \- initialization       : INITIALIZED
+*
+* Use of the resource....
+*
+*              /- deinitialization  : CONSTRUCTED
+* Destruction {-- deconstruction    : ALLLOCATED
+*              \- deallocation      : VOID
+*
+* It should be noticed that with proper care an object that has not been
+* intialized, or that merely has been deinitialized (i.e. in the CONSTRUCTED
+* state) could be used or reused simply by initializing or reinitializing it. 
+* It could be useful for performance or practical reasons or to enable such
+* recycling of already allocated and constructed resources. 
+* 
+* On the other hand a resource that has been deallocated or not allocated 
+* cannnot be used correctly at all. What then about a resource that has not 
+* been constructed yet or one that has already been deconstructed? The barrier
+* is more fluid and uncrear here. However, in many progaming languages, these
+* 3 steps get simplified ito 2 or even 1 step. However, experience shows that 
+* having only one step is too gross, especially in the face of reference 
+* counting or on-GC deinitialization. Therefore, it's best to have 2
+* steps on construction and 2 steps on deconstruction namely 
+*
+* construction by XXX_new  : XXX_build    XXX_init. 
+* destruction  by XXX_free : XXX_done     XXX_demolish
+*
+* The rule is that XXX_done and XXX_init should be complementary, 
+* any resource RES that has XXX_init(XXX_done(RES), params...) called 
+* will be usable again. Furthemore XXX_done should be idempotent,
+* that is XXX_done(XXX_done(RES)), etc is allowed. 
+* XXX_init need not be idempotent as long as it can undo XXX_done correctly.
+* XXX_free is guaranteed to call XXX_done and XXX_demolish. 
+* XXX_demolish must not neccesarily be exposed on the public API or 
+* even exist.
+* XXX_new calls XXX_build and XXX_init in turn.
+*
+*
+* The problem remains then : how to know which destructor to call?
+* In case it's known at compile-time  which other destructors will be needed
+* it's simple. However, some containers may want to store objects whose type 
+* is only known at run time. For them it is neccesary to either not accept 
+* ownership and let some other routine deallocate them) which is 
+* bothersome, certainy for, say, hash tables, or to properly devise a system
+* where the correct destructors can be passed at run time. 
+* 
+* And then we arrive at interfaces that use and/or consist of virtual tables, 
+* that is tablew with function pointers. For the destructor a single pointer
+* could suffice, but while we're going virtual, allowing interface/class like
+* tables will be better since it alllows more generality and saves space.
+*
+*
+* Another point is what to do if memory allocation fails. While for a critical 
+* application, it woule make sense to try to free ups some space and try
+* again, for non-critical ones like Eruta, the best thing to do is to 
+* simply end the program, since there's nothing sensible that could be done,
+* apart from trying to restore the sceen to it's previous resolution if possible.
+*  
+*
+*/
+
+
+
+/** Thoughts on memory management and garbage collection. 
+* The probelm in C is that it is not always clear who is the owner 
+* for an object, and and who is just using a reference. 
+* If ownership is well defined, then correct cleanup is easier to arrange.
+*
+* Here's an idea on reference linking for shared ownership:
+*/
+
+typedef struct Memref_ Memref;
+
+struct Memref_ {
+  void   * pointer;
+  Memref * next;
+  Memref * previous;
+};
+
+Memref memref(void * pointer) {
+  Memref ref = { pointer, 0, 0 };
+  return ref;
+}
+
+
+
+
+
+
+
+
+
+
+

+ 306 - 0
src/monolog.c

@@ -0,0 +1,306 @@
+
+#include "monolog.h"
+#include "dynar.h"
+
+#include <string.h>
+
+struct Monologger {
+  struct MonologLoggerInterface logger;
+  void                        * data;
+  int                           index;
+};
+
+struct Monolevel {
+  char * name;
+  int enabled;
+};
+
+
+struct Monolog {
+  Dynar * loggers;
+  Dynar * levels;
+  int last_logger;
+};
+
+static struct Monolog monolog_struct = { NULL, NULL, 0 };
+static struct Monolog *      monolog = &monolog_struct;
+
+void monolog_done() {
+  int index;
+  if (monolog->loggers) {
+    /* clean up all loggers */
+    for (index = 0; index < dynar_size(monolog->loggers); index ++) {
+      monolog_remove_logger(index);
+    }
+     
+    dynar_free(monolog->loggers);
+    monolog->loggers = NULL;
+  }
+  
+  if (monolog->levels) {
+    dynar_free(monolog->levels);
+    monolog->levels = NULL;
+  }
+
+  monolog->last_logger = 0;
+}
+
+/* Compares loggers with each other for sorting.  This is qsort compatible. */
+static int monolog_compare_loggers(void * p1, void * p2) {
+  struct Monologger * me   = *((struct Monologger **) p1);
+  struct Monologger * you  = *((struct Monologger **) p2);
+  if (!me) return -1;
+  if (!you) return 1;
+  if (!me->logger.log) return -1;
+  if (!you->logger.log) return 1;
+
+  /* Compare index if no nulls. */
+  if (me->index < you->index) return -1;
+  if (me->index > you->index) return  1;
+  return 0;
+}
+
+
+/* Compares loglevels with each other for sorting.  This is qsort compatible. */
+static int monolog_compare_loglevels(const void * p1, const void * p2) {
+  const struct Monolevel * me   = p1;
+  const struct Monolevel * you  = p2;
+  if(!me) return -1;
+  if(!you) return 1;
+  if(!me->name) return -1;
+  if(!you->name) return 1;
+  /* Compare name if no nulls. */
+  return strcmp(me->name, you->name);
+  return 0;
+}
+
+
+
+
+#define MONOLOG_START_LEVELS  32
+#define MONOLOG_START_LOGGERS 32
+
+static int monolog_get_loggers_max() {
+  return dynar_size(monolog->loggers);
+}
+
+static struct Monologger * monolog_get_logger(int index) {
+  return dynar_getdata(monolog->loggers, index);
+}
+
+static int monolog_get_unused_logger() {
+  int index;
+  for (index = 0 ; index < dynar_size(monolog->loggers); index++) {
+    struct Monologger * logger = monolog_get_logger(index);
+    if (!logger->logger.log) {
+      return index;
+    }
+  }
+  return -1;
+}
+
+
+static struct Monologger *
+monolog_set_logger(int index, void *data, struct MonologLoggerInterface * ifa) {
+  struct Monologger * logger = monolog_get_logger(index);
+  if(!logger) return NULL;
+  logger->index  = index;
+  logger->data   = data;
+  logger->logger = (*ifa);
+  return logger;
+}
+
+static struct Monolevel * monolog_get_level(int index) {
+  return dynar_getdata(monolog->levels, index);
+}
+
+static int monolog_get_unused_level() {
+  int index;
+  for (index = 0 ; index < dynar_size(monolog->levels); index++) {
+    struct Monolevel * level = monolog_get_level(index);
+    if (!level->name) {
+      return index;
+    }
+  }
+  return -1;
+}
+
+
+static struct Monolevel *
+monolog_set_level (int index, char * name, int enabled) {
+  struct Monolevel * level = monolog_get_level(index);
+  if (!level) return NULL;
+  level->name     = name;
+  level->enabled  = enabled;
+  return level;
+}
+
+
+int monolog_init() {
+  int index;
+  monolog->loggers = dynar_new(MONOLOG_START_LEVELS, sizeof(struct Monologger));
+  if(!monolog->loggers) {
+    return FALSE;
+  }
+  
+  monolog->levels  = dynar_new(MONOLOG_START_LOGGERS, sizeof(struct Monolevel));
+    if(!monolog->levels) {
+    monolog_done();
+    return FALSE;
+  }
+
+  for (index = 0; index < dynar_amount(monolog->loggers) ; index++ ) {
+    struct MonologLoggerInterface ifa = { NULL, NULL };
+    monolog_set_logger(index, NULL, &ifa);
+  }
+  
+  for (index = 0; index < dynar_amount(monolog->levels) ; index++ ) {
+    monolog_set_level(index, NULL, FALSE);
+  }
+  
+  return TRUE;
+}
+
+
+
+/** Adds a loger. Returns negative or error zero or positive (the index of the logger)
+ * on success. */
+int monolog_add_logger(void * data, struct MonologLoggerInterface * logger) {
+  int logindex;
+
+  if (monolog->last_logger >= dynar_amount(monolog->loggers))   {
+    if (!dynar_grow(monolog->loggers, MONOLOG_START_LOGGERS)) {
+      return -1;
+    }
+  }
+
+  logindex = monolog_get_unused_logger();
+  if (logindex < 0) {
+    return -2;
+  }
+
+  monolog_set_logger(logindex, data, logger);
+  return logindex;
+}
+
+
+int monolog_remove_logger(int index) {
+  struct MonologLoggerInterface ifa = { NULL, NULL };
+  struct Monologger * logger = monolog_get_logger(index);
+  /* Call logger's destructor if needed. */
+  if (logger->logger.free) {
+    logger->logger.free(logger->data);
+  }
+  monolog_set_logger(index, NULL, &ifa);
+  return index;
+} 
+
+struct Monolevel * monolog_find_level_by_name(char * name) {
+  struct Monolevel key;
+  key.name    = name;
+  key.enabled = 123;
+  return dynar_bsearch(monolog->levels, &key, monolog_compare_loglevels); 
+}
+
+int monolog_setup_level(char * name, int enabled) {
+  struct Monolevel * level;
+  int levelindex;
+  level = monolog_find_level_by_name(name);
+  if (!level) { 
+    levelindex = monolog_get_unused_level();
+    if (levelindex < 0) return -1;
+    level = monolog_get_level(levelindex);
+  }
+  level->name    = name;
+  level->enabled = enabled;
+  dynar_qsort(monolog->levels, monolog_compare_loglevels);
+  return 1;
+}
+
+
+int monolog_enable_level(char * name) {
+  return monolog_setup_level(name, TRUE);
+}
+
+int monolog_disable_level(char * name) {
+  return monolog_setup_level(name, FALSE);  
+}
+
+int monolog_log_va(char * file, int line, char * name, char * format, va_list args) {
+  int index;
+  va_list args_copy;
+  struct Monolevel * level = monolog_find_level_by_name(name);
+  if ((!level) || (!level->enabled)) {
+    return -1;
+  }
+  
+  for (index = 0; index < dynar_size(monolog->loggers); index++) {
+    struct Monologger * logger = monolog_get_logger(index);
+    if (logger && logger->logger.log) {
+      va_copy(args_copy, args);
+      logger->logger.log(file, line, level->name, logger->data, format, args_copy);
+      va_end(args_copy);
+    }
+  }
+  return 0;
+}
+
+
+int monolog_log(char * file, int line, char * level, char * format, ...) {
+  int result;
+  va_list args;
+  va_start(args, format);
+  result = monolog_log_va(file, line, level, format, args);
+  va_end(args);
+  return result;
+}
+
+
+/* Log function for logger that logs to stdout. */
+int monolog_stdout_logger
+  (char * file, int line, char * level, void * data, char * format, va_list args)
+{
+  (void) data;
+  printf("%s: %s: %d: ", level, file, line);
+  return vprintf(format, args);
+}
+
+/* Log function for logger that logs to stderr. */
+int monolog_stderr_logger
+  (char * file, int line, char * level, void * data, char * format, va_list args)
+{
+  (void) data;
+  fprintf(stderr, "%s: %s: %d: ", level, file, line);
+  return vfprintf(stderr, format, args);
+}
+
+
+/** Log function for logger that logs to a FILE. */
+int monolog_file_logger
+(char * file, int line, char * level, void * data, char * format, va_list args) {
+  FILE * fout = data;
+  int res;
+  if (fout) {
+    fprintf(fout, "%s: %s: %d: ", level, file, line);
+    res =  vfprintf(fout, format, args);
+    fflush(fout);
+    return res;
+  }
+  return -1;
+}
+
+
+/** Free function for logger that logs to a FILE. */
+void monolog_file_free(void * data) {
+  FILE * fout = data;
+  if (fout) {
+    fclose(fout);
+  }
+}
+
+
+
+
+
+
+

+ 481 - 0
src/rh.c

@@ -0,0 +1,481 @@
+  
+#include "rh.h"
+#include "mem.h"
+#include "monolog.h"
+#include "state.h"
+
+#include <string.h>
+
+#include <stdarg.h>
+#include <mruby.h>
+#include <mruby/error.h>
+#include <mruby/variable.h>
+
+#include "esh.h"
+#include "config.h"
+
+/* Debugging level for console output. */ 
+#define LOG_CONSOLE(FORMAT, ...) LOG_LEVEL("CONSOLE", FORMAT, __VA_ARGS__)
+#define LOG_ENABLE_CONSOLE()     LOG_ENABLE(CONSOLE)
+#define LOG_DISABLE_CONSOLE()    LOG_DISABLE(CONSOLE)
+
+/* Amount of parameters a mruby function can be called with using _va functions */
+#define RH_ARGS_MAX 64
+
+/*
+* RH contains helper functions for the mruby ruby interpreter.
+*/
+
+/*
+ * Converts C like arguments to an array of mruby mrb_values.
+ * Returns amount of arguments parsed, negative on failure.
+ * 
+ * Format specifier characters for the format argument: 
+ *
+ * z: String from null terminated C string      [char*]
+ * s: String                                    [char*, int] 
+ * Y: sYmbol from null terminated C string      [char*]
+ * y: symbol                                    [char*, int] 
+ * 
+ * f: Float                                     [double]
+ * i: Integer                                   [int]
+ * b: Boolean                                   [int] 
+ * 0: nil
+ * Other characters: Error. Will return -(index or erroneous character) .
+ */ 
+int rh_args_va(Ruby * ruby, mrb_value * values,  int size,  char * format, va_list list) {
+  int ch; int index;
+  int i; double d; const char * str;
+  index = 0; 
+  
+  for (ch = (*format) ; (ch) && (index < size) ; format++ , ch = (*format)) {
+    mrb_value val;
+    int error = FALSE;
+    switch(ch) {
+      case 's':
+        str = va_arg(list, const char*);
+        i   = va_arg(list, int);
+        val = mrb_str_new(ruby, str, i);
+        break;
+        
+      case 'z':
+        str = va_arg(list, const char*);
+        val = mrb_str_new_cstr(ruby, str);
+        break;
+        
+      case 'y':
+        str = va_arg(list, const char*);
+        i   = va_arg(list, int);
+        val = mrb_symbol_value(mrb_intern(ruby, str, (size_t)i));
+        break;
+        
+      case 'Y':
+        str = va_arg(list, const char*);
+        val = mrb_symbol_value(mrb_intern_cstr(ruby, str));
+        break; 
+        
+      case 'i':
+        i   = va_arg(list, int);
+        val = mrb_fixnum_value(i);
+        break;
+        
+      case 'f':
+        d   = va_arg(list, double);
+        val = mrb_float_value(ruby, d);
+        break;
+        
+      case 'b': 
+        i   = va_arg(list, int);
+        val = ( i ? mrb_true_value() : mrb_false_value());
+        break;
+      
+      case '0':
+        val = mrb_nil_value();
+        break;
+        
+      default:
+        error = TRUE;
+        break;
+    }
+    if (error) { 
+      return -index;
+    }
+    values[index] = val;
+    index ++;
+  }
+  return index;
+}
+
+
+/* strdup isn't ANSI C, just posix... :p so we need our own version.*/
+char *rh_strdup(const char *str) {
+    char * res = malloc(strlen(str) + 1);
+    if(res) { strcpy(res, str); }
+    return res;
+}
+
+/* Helps convert C values to mruby values in an array. */
+int rh_args(Ruby * ruby, mrb_value * values,  int size,  char * format, ...) {
+  int res;
+  va_list list;
+  va_start(list, format);
+  res = rh_args_va(ruby, values, size, format, list);
+  va_end(list);
+  return res;
+}
+
+
+/** Calulates the execption string. Result only tempoarily available..
+XXX: check if this doesn't leak memory... you must free the results manually.
+*/
+char * rh_exceptionstring(Ruby * self) {
+  char      * result;
+  mrb_value   value;
+  mrb_value   backtrace;
+  mrb_value   backtrace_str;
+  
+  if (!self->exc) return NULL; 
+  //
+  /* XXX: Too bad, the backtrace doesn't seem to be filled in for some reason...
+   * Should figure out how to fix this.
+   */
+  mrb_print_backtrace(self);
+  backtrace = // mrb_get_backtrace(self);
+  mrb_funcall(self, mrb_obj_value(self->exc), "backtrace", 0);
+
+  backtrace_str = mrb_funcall(self, backtrace, "join", 1, mrb_str_new_lit(self, "\n"));
+  LOG_ERROR("backtrace: %s\n", mrb_string_value_cstr(self, &backtrace_str));
+  value  = mrb_funcall(self, mrb_obj_value(self->exc), "inspect", 0);
+  
+  
+  
+  // reset exception since we have it's string value.
+  // Does this leak memory or not???
+  self->exc = NULL;
+  return rh_strdup(mrb_string_value_cstr(self, &value));
+}
+
+
+/** Allocates and initialzes a new ruby state. */
+Ruby * rh_new() {
+   Ruby * self = mrb_open();
+   /*mrb_define_method(self, self->kernel_module, 
+                     "path", tr_Path, ARGS_REQ(1));*/
+   return self;
+}
+
+/** Frees a ruby state. */
+Ruby * rh_free(Ruby * self) {
+  mrb_close(self);
+  return NULL;
+}
+
+/** Returns an mrb_value that contains the value of object.inspect. 
+*/
+mrb_value rh_inspect(mrb_state *mrb, mrb_value obj) {
+  return mrb_inspect(mrb, obj);
+}
+
+char * rh_inspect_cstr(mrb_state *mrb, mrb_value value) {
+  mrb_value res = rh_inspect(mrb, value);
+  /* XXX: check that it's not modified anywere or export the const? */
+  return (char *) mrb_string_value_cstr(mrb, &res);
+}
+
+
+/* Does the actual reporting depending on the current state of 
+ ruby and the returned value. */
+int rh_make_report(Ruby * self, mrb_value v) { 
+  int res = 0;
+  char * str;
+ 
+  /* Report exceptions */
+  str = rh_exceptionstring(self);
+  if(str) {
+    LOG_WARNING("mruby exception: %s\n", str);
+    free(str);
+    return 0;
+  }
+  
+  /* Report result value if it's not nil on debug and console levels.
+   */
+  if (!mrb_nil_p(v)) {
+    str = rh_inspect_cstr(self, v);
+    LOG_DEBUG("mruby result: %s\n", str);
+    LOG_CONSOLE("-> %s\n", str);
+    return 0;
+  }
+  return 0;
+}
+
+
+/* Runs a file and reports any errors over the monolog logging system. */
+int rh_run_file(Ruby * self, const char * filename, FILE * file) {
+  int res;
+  char * str;
+  mrbc_context * c ; 
+  mrb_value v;
+  int ai;
+  ai = mrb_gc_arena_save(self);
+  c = mrbc_context_new(self);  
+  mrbc_filename(self, c, filename);
+  v = mrb_load_file_cxt(self, file, c);
+  mrbc_context_free(self, c);  
+  /* Report exceptions */
+  res = rh_make_report(self, v);  
+  mrb_gc_arena_restore(self, ai);
+  return res;
+}
+
+int rh_run_filename(Ruby * self, const char * filename) {
+  FILE * file = fopen(filename, "rt");
+  int res;
+  if (!file) {
+    LOG_ERROR("No such ruby file: %s\n", filename);
+    return -1;
+  }
+  res = rh_run_file(self, filename, file);
+  fclose(file);
+  return 0;
+}
+
+
+/**
+* Executes a ruby file in Eruta's data/script directory with reporting.
+* Returns -2 if the file was not found.
+* Returns -3 if the path wasn't found.
+*/
+int rh_run_script(Ruby * self, const char * filename) {
+  struct woesb path = {0};
+  int runres;
+  
+  if (!woesb_new_join(&path, MRB_WOE_CONFIG(self)->data_dir, "/script/", filename, NULL)) {
+    woesb_free(&path);
+    LOG_ERROR("Out of memory when joining path.\n");
+    return -3;
+  }
+  
+  if (strstr(path.text, "..")) {
+    woesb_free(&path);
+    LOG_ERROR("Path may not contain '..' \n");
+    return -4;
+  }
+  
+  runres = rh_run_filename(self, path.text);
+  woesb_free(&path);
+  return runres;
+}
+
+
+/* Executes a ruby command string. 
+Errors are reported to the reporter callback if it isn't NULL. */
+int rh_dostring(Ruby * self, const char * command) {
+  int res = 0;
+  char * str;
+  mrb_value v;
+  int ai;
+  ai = mrb_gc_arena_save(self);  
+  #ifdef RH_USE_CONTEXT
+  mrbc_context      * c  = mrbc_context_new(self);
+  mrbc_filename(self, c, "command");
+  v = mrb_load_string_cxt(self, command, c);
+  mrbc_context_free(self, c); 
+  #else
+  v = mrb_load_string(self, command);
+  #endif
+  /* Report exceptions */
+  res = rh_make_report(self, v);
+  /* mrb GC area seems to be an area for 1024 "new" objects for the generational 
+   * GC. It can overflow if a lot of new objects are generated 
+   * (say exceptions, etc) on the C side. To prevent this the area must be saved 
+   * and restored anywhere many ruby objects may have been generated.
+   * It seems that here too this is needed.
+   */
+  mrb_gc_arena_restore(self, ai);
+  return res;
+}
+
+
+/* Executes a ruby function with parameters. 
+Errors are reported to the reporter callback if it isn't NULL. */
+mrb_value rh_run_function_args(Ruby * self, mrb_value rubyself, char * funcname,    
+                        int argc, mrb_value * argv) {
+  int res = 0;
+  char * str;
+  mrb_value v;
+  mrb_sym symname = mrb_intern_cstr(self, funcname);
+  int ai;
+  if(!mrb_respond_to(self, rubyself, symname)) {
+    return mrb_nil_value();
+  }  
+  
+  ai = mrb_gc_arena_save(self);
+  v = mrb_funcall_argv(self, rubyself, symname, argc, argv);
+  res = rh_make_report(self, v);
+  mrb_gc_arena_restore(self, ai);
+  return v;
+}
+
+
+/** Runs a function in the ruby interpreter, with C arguments according to the 
+ * given format string, logging results and errors back to
+ * the reporter. The limit is RH_ARGS_MAX arguments.
+ */
+mrb_value 
+rh_run_function_va(Ruby * self, mrb_value rubyself, char * funcname,
+                        char * format, va_list list) {
+  mrb_value argv[RH_ARGS_MAX];
+  int argc;
+  argc = rh_args_va(self, argv, RH_ARGS_MAX, format, list);
+  if (argc < 0) return mrb_nil_value();
+  return rh_run_function_args(self, rubyself, funcname, argc, argv);
+}
+
+
+/** Runs a function in the ruby interpreter, under the toplevel self. 
+ * This logs results and errors using monolog.h interfaces.
+ */
+mrb_value rh_run_toplevel_args(Ruby * ruby, char * name, int argc, mrb_value * argv) {
+  return rh_run_function_args(ruby, mrb_top_self(ruby), name, argc, argv);
+} 
+
+
+/** Runs a function in the ruby interpreter, under the toplevel self. 
+ * This logs results and errors using monolog.h interfaces.
+ */
+mrb_value rh_run_toplevel_va(Ruby * ruby, char * name, char * format, va_list list) {
+  return rh_run_function_va(ruby, mrb_top_self(ruby), name, format, list);
+} 
+
+
+/** Runs a function in the ruby interpreter, logging results and errors
+ * using monolog.h interfaces.
+ */
+mrb_value rh_run_function(Ruby * ruby, mrb_value rubyself, 
+                          char * name, char * format, ...) {
+  mrb_value res; 
+  va_list list;
+  va_start(list, format);
+  res = rh_run_function_va(ruby, rubyself, name, format, list);
+  va_end(list);
+  return res;
+}
+
+/** Runs a function in the ruby interpreter, under the toplevel self. 
+ * This logs results and errors using monolog.h interfaces.
+ */
+mrb_value rh_run_toplevel(Ruby * ruby, char * name, char * format, ...) {
+  mrb_value res; 
+  va_list list;
+  va_start(list, format);
+  res = rh_run_function_va(ruby, mrb_top_self(ruby), name, format, list);
+  va_end(list);
+  return res;
+}
+
+
+/* Calls a function, doesn't log anything. */
+mrb_value rh_simple_funcall(Ruby * ruby, char * name) {
+  int ai;  
+  Ruby * mrb = (Ruby *) ruby;  
+  mrb_value args[16];
+  ai = mrb_gc_arena_save(mrb);
+  LOG("GC Area: %d\n", ai);
+  // if(ai> 99) exit(0);
+  mrb_value v = mrb_funcall_argv(mrb, mrb_top_self(mrb), mrb_intern_cstr(mrb, name), 
+                    0, args);
+  
+  if (mrb->exc) {
+    if (!mrb_undef_p(v)) {        
+      mrb_p(mrb, mrb_obj_value(mrb->exc));
+    }
+    return mrb_nil_value();
+  }
+  mrb_gc_arena_restore(mrb, 0);
+  return v;
+}
+
+/* Maybe wrap this too? 
+mrb_value mrb_funcall_argv(mrb_state*, mrb_value, mrb_sym, int, mrb_value*);
+*/
+
+/* Converts and mrb_value to a C boolean as per the ruby interpretation. */
+int rh_tobool(mrb_value v) {
+  if (mrb_nil_p(v))             return FALSE;
+  return mrb_bool(v);
+}
+
+/* Documentation for mrb_get_args
+  retrieve arguments from mrb_state.
+
+  mrb_get_args(mrb, format, ...)
+  
+  returns number of arguments parsed.
+
+  format specifiers:
+
+   o: Object [mrb_value]
+   S: String [mrb_value]
+   A: Array [mrb_value]
+   H: Hash [mrb_value]
+   s: String [char*,int]
+   z: String [char*]
+   a: Array [mrb_value*,int]
+   f: Float [mrb_float]
+   i: Integer [mrb_int]
+   n: Symbol [mrb_sym]
+   &: Block [mrb_value]
+   *: rest argument [mrb_value*,int]
+   |: optional
+*/
+
+
+
+/* Stores a pointer in the mrb state. Handy to avoid having to use global 
+ * variables o,n the C side. It's done by adding a ruby global variable
+ * with a name that cannot be used from the mruby side. */
+void * rh_store_pointer(mrb_state * mrb, const char * name, void * ptr) {
+  struct woesb hname;
+  mrb_value      val;
+  mrb_sym      rname;
+
+  if (!woesb_new_join(&hname, "--", name, "--", NULL)) {
+    woesb_free(&hname);
+    LOG_ERROR("Out of memory when making hidden name.\n");
+    return NULL;
+  }
+  
+  rname = mrb_intern_cstr(mrb, hname.text);
+  val   = mrb_cptr_value(mrb, ptr);  
+  mrb_gv_set(mrb, rname, val);
+  woesb_free(&hname);
+  return ptr;
+}
+
+/** Fetches a previously stored pointer. */
+void * rh_fetch_pointer(mrb_state * mrb, const char * name) {
+  struct woesb hname;
+  mrb_value      val;
+  mrb_sym      rname;
+
+  if (!woesb_new_join(&hname, "--", name, "--", NULL)) {
+    woesb_free(&hname);
+    LOG_ERROR("Out of memory when making hidden name.\n");
+    return NULL;
+  }
+  
+  rname = mrb_intern_cstr(mrb, hname.text);
+  val   = mrb_gv_get(mrb, rname);
+  woesb_free(&hname);
+  return mrb_cptr(val);
+}
+
+
+/* Tries to (re-)load the main ruby file, output to console. */
+int rh_load_main() { 
+  return -1; /*rh_run_script(woe_global_ruby, "main.rb"); */
+}
+
+
+
+

+ 704 - 0
src/server.c

@@ -0,0 +1,704 @@
+# if !defined(_POSIX_SOURCE)
+#   define _POSIX_SOURCE
+# endif
+# if !defined(_BSD_SOURCE)
+#   define _BSD_SOURCE
+# endif
+
+# include <sys/socket.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+# include <netdb.h>
+# include <poll.h>
+# include <unistd.h>
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "client.h"
+#include "server.h"
+#include "monolog.h"
+
+#include "libtelnet.h"
+
+#include "rh.h"
+
+
+static const telnet_telopt_t woe_telnet_opts[] = {
+
+  { TELNET_TELOPT_COMPRESS2,  TELNET_WILL, TELNET_DONT },
+  { TELNET_TELOPT_ECHO     ,  TELNET_WONT, TELNET_DONT },
+
+  { -1, 0, 0 }
+};
+
+
+#define WOE_SERVER_BUFFER_SIZE      10000
+#define WOE_SERVER_LISTEN_BACKLOG   8
+
+struct woe_server {
+  char                buffer[WOE_SERVER_BUFFER_SIZE];
+  short               listen_port;
+  int                 listen_sock;
+  int                 busy;
+  struct sockaddr_in  addr;
+  socklen_t           addrlen;
+  mrb_state         * mrb;
+  
+  struct pollfd       pfd[WOE_CLIENTS_MAX + 1];
+  struct woe_client * clients[WOE_CLIENTS_MAX];  
+  void (*event_handler)      (telnet_t *telnet, telnet_event_t *ev, void *user_data);
+  void (*disconnect_handler) (struct woe_server * srv, struct woe_client * cli, void *user_data);
+};
+
+
+
+int woe_server_disconnect(struct woe_server * srv, struct woe_client * client);
+
+
+int woe_send(int sock, const char *buffer, unsigned int size) {
+  int res;
+
+  if (sock == -1)
+    return -1;
+
+  /* send data */
+  while (size > 0) {
+    if ((res = send(sock, buffer, size, 0)) == -1) {
+      if (errno != EINTR && errno != ECONNRESET) {
+        LOG_ERROR("send() failed: %s\n", strerror(errno));
+        return -2;
+      } else {
+        return 0;
+      }
+    } else if (res == 0) {
+      LOG_ERROR("send() unexpectedly returned 0\n");
+      return -3;
+    }
+
+    /* update pointer and size to see if we've got more to send */
+    buffer += res;
+    size -= res;
+  }
+  return 0;
+}
+
+
+
+static void woe_event_handler(telnet_t *telnet, telnet_event_t *ev, void *user_data) {
+  struct woe_client * client = (struct woe_client *) user_data;
+
+  switch (ev->type) {
+  /* data received */
+  case TELNET_EV_DATA:
+    woe_client_input(client, ev->data.buffer, ev->data.size);
+    /* telnet_negotiate(telnet, TELNET_WONT, TELNET_TELOPT_ECHO);
+      telnet_negotiate(telnet, TELNET_WILL, TELNET_TELOPT_ECHO); */
+    break;
+    
+  /* data must be sent */
+  case TELNET_EV_SEND:
+    woe_send(client->sock, ev->data.buffer, ev->data.size);
+    break;
+        
+  /* enable compress2 if accepted by client */
+  case TELNET_EV_DO:
+    if (ev->neg.telopt == TELNET_TELOPT_COMPRESS2) telnet_begin_compress2(telnet);
+    woe_client_negotiate(client, TELNET_DO, ev->neg.telopt);
+    break;
+
+
+  case TELNET_EV_DONT:
+    woe_client_negotiate(client, TELNET_DONT, ev->neg.telopt);
+    break;
+    
+  case TELNET_EV_WILL:  
+    woe_client_negotiate(client, TELNET_WILL, ev->neg.telopt);
+    break;
+    
+  case TELNET_EV_WONT:  
+    woe_client_negotiate(client, TELNET_WILL, ev->neg.telopt);
+    break;
+  
+    
+  /* error */
+  case TELNET_EV_ERROR:  
+    LOG_ERROR("Telnet error for client.\n");    
+    woe_server_disconnect(client->server, client);
+    break;
+  
+  case TELNET_EV_ZMP
+    woe_client_zmp(client, ev->zmp.argc, ev->zmp.argv);
+    break;
+     
+  default:
+     LOG_NOTE("Ignored telnet event %d.\n", ev->type);
+    /* ignore */
+    break;
+  }
+}
+
+
+void woe_server_request_shutdown(struct woe_server * srv) {  
+  if (srv) srv->busy = 0;
+}
+
+int woe_server_busy(struct woe_server * srv) {
+  if (!srv) return 0;
+  return srv->busy;
+}
+
+
+struct woe_server * woe_server_free(struct woe_server * srv) {
+  close(srv->listen_sock);
+  free(srv);
+  return NULL;
+}
+
+
+
+struct woe_server * woe_server_init(struct woe_server * srv, int port) {
+  int index;
+  if (!srv) return NULL;
+  srv->listen_sock = -1;
+  srv->listen_port = port;
+  srv->busy        = !0;
+  srv->mrb         = NULL;
+  memset(srv->buffer  , '\0'  , sizeof(srv->buffer));
+  memset(srv->pfd     , 0     , sizeof(srv->pfd));
+  memset(&srv->addr   , 0     , sizeof(srv->addr));
+  
+  for (index = 0; index < WOE_CLIENTS_MAX; ++index) {
+     srv->clients[index] = NULL;
+  }
+  
+   srv->event_handler = woe_event_handler;
+  return srv;
+}
+
+struct woe_server * woe_server_new(int port) {
+  struct woe_server * srv = calloc(1, sizeof(struct woe_server));
+  if (!srv) return NULL;  
+  return woe_server_init(srv, port);
+}
+
+
+static const telnet_telopt_t telopts[] = {
+  { TELNET_TELOPT_COMPRESS2,  TELNET_WILL, TELNET_DONT },
+  { -1, 0, 0 }
+};
+
+
+struct woe_server * woe_server_set_mrb(struct woe_server * srv, mrb_state * mrb) {
+  if (!srv) return NULL;  
+  srv->mrb = mrb;
+  return srv;
+}
+
+mrb_state * woe_server_get_mrb(struct woe_server * srv) {
+  if (!srv) return NULL;
+  return srv->mrb;
+}
+
+
+/** Sets up the server to listen at its configured port. */
+int woe_server_listen(struct woe_server * srv) {
+  int         res;
+  /* create listening socket */
+  if ((srv->listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+    LOG_ERROR("socket() failed: %s\n", strerror(errno));
+    return 1;
+  }
+
+  /* Reuse address option */
+  res = 1;
+  if (setsockopt(srv->listen_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&res, sizeof(res))
+     == -1) {
+    LOG_ERROR("setsockopt() failed: %s\n", strerror(errno));
+    return 2;
+  }
+     
+  /* Bind to listening addr/port */  
+  srv->addr.sin_family       = AF_INET;
+  srv->addr.sin_addr.s_addr  = INADDR_ANY;
+  srv->addr.sin_port         = htons(srv->listen_port);
+  if (bind(srv->listen_sock, (struct sockaddr *)&srv->addr, sizeof(srv->addr)) == -1) {
+    LOG_ERROR("setsockopt() failed: %s\n", strerror(errno));
+    return 3;
+  }
+
+  /* listen for clients */
+  if (listen(srv->listen_sock, WOE_SERVER_LISTEN_BACKLOG) == -1) {
+    fprintf(stderr, "listen() failed: %s\n", strerror(errno));
+    return 4;
+  }
+
+  LOG_NOTE("Listening on port %d\n", srv->listen_port);
+
+  /* Initialize listening descriptors */
+  srv->pfd[WOE_CLIENTS_MAX].fd = srv->listen_sock;
+  srv->pfd[WOE_CLIENTS_MAX].events = POLLIN;
+  
+  return 0;
+}
+
+/** Returns one of the clients of the server or NULL if not in use or out of 
+ * range */
+struct woe_client * woe_server_get_client(struct woe_server * srv, int index) {
+  if (!srv)                           return NULL;
+  if (index < 0)                      return NULL;
+  if (index >= WOE_CLIENTS_MAX)       return NULL;
+  return srv->clients[index];
+}
+
+/** Stores a client of the server at the given index*/
+struct woe_client * woe_server_put_client(struct woe_server * srv, int index, struct woe_client * cli) {
+  if (!srv)                           return NULL;
+  if (!cli)                           return NULL;
+  if (index < 0)                      return NULL;
+  if (index >= WOE_CLIENTS_MAX)       return NULL;
+  if (srv->clients[index]) {
+    woe_client_free(srv->clients[index]);
+  }  
+  srv->clients[index] = cli;
+  return cli; 
+}
+
+/** Removes a client of the server at the given index*/
+struct woe_client * woe_server_remove_client(struct woe_server * srv, int index) {
+  if (!srv)                           return NULL;
+  if (index < 0)                      return NULL;
+  if (index >= WOE_CLIENTS_MAX)       return NULL;
+  if (srv->clients[index]) {
+    woe_client_free(srv->clients[index]);
+  }  
+  srv->clients[index] = NULL;
+  return NULL; 
+}
+
+
+/** Find an index to put a new user and returns a pointer to it. 
+ *  Returns -1 if no free space is available. 
+ **/
+int woe_server_get_available_client_index(struct woe_server * srv) {
+   int i;
+   for (i = 0; i < WOE_CLIENTS_MAX; ++i) {
+     struct woe_client * client = woe_server_get_client(srv, i);
+     if (!client) {
+       return i;
+     } 
+   }
+   return -1;
+}
+
+/** Creates a new client for this server. Return null if no memory or no space 
+ * for a new client. */
+struct woe_client * woe_server_make_new_client(struct woe_server * srv, 
+  int socket, struct sockaddr_in *addr, socklen_t addrlen) {
+   struct woe_client * new;
+   int index = woe_server_get_available_client_index(srv);
+   if (index < 0) return NULL;
+   new = woe_client_new(srv, index, socket, addr, addrlen);
+   return woe_server_put_client(srv, index, new);
+} 
+
+
+
+/* Handles a new connection to this server. */
+int woe_server_handle_connect(struct woe_server * srv) {
+  struct woe_client * client;
+  int res;
+  struct sockaddr_in addr;
+  socklen_t addrlen;
+ 
+  /* accept the connection */
+  addrlen = sizeof(addr);
+  if ((res = accept(srv->listen_sock, (struct sockaddr *)&addr, &addrlen)) == -1) {
+    LOG_ERROR("accept() failed: %s\n", strerror(errno));
+    return 1;
+  }
+  
+  LOG_NOTE("Connection received.\n");
+  
+  client = woe_server_make_new_client(srv, res, &addr, addrlen);
+  
+  /* No space for a new client */
+  if (!client) {
+    LOG_WARNING("Connection rejected (too many users or OOM)\n");
+    write(res, "Too many users.\r\n", 14);
+    close(res);  
+    return 2;
+  }
+
+  /* init, welcome */
+  client->telnet  = telnet_init(woe_telnet_opts, srv->event_handler, 0, client);
+  if (!client->telnet) {
+    LOG_ERROR("Could not initialize telnet connection for user.");
+    woe_server_disconnect(srv, client);
+    return 3;    
+  }
+  /*telnet_negotiate(client->telnet, TELNET_DO, TELNET_TELOPT_ECHO);*/
+  telnet_negotiate(client->telnet, TELNET_WILL, TELNET_TELOPT_COMPRESS2);
+  telnet_printf(client->telnet, "Welcome to WOE!\r\n");
+  /* telnet_negotiate(client->telnet, TELNET_WILL, TELNET_TELOPT_ECHO); */
+  
+  if (srv->mrb) {
+    rh_run_toplevel(srv->mrb, "woe_on_connect", "i", client->index);
+  }
+  
+  return 0;
+}
+
+
+/** Sends a telnet command to the numbered client. */
+int woe_server_iac(struct woe_server * srv, int client, int command) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_iac(pclient->telnet, command);
+  return 0;
+}
+
+
+/** Send a telnet negotiation to the numbered client. */
+int woe_server_negotiate(struct woe_server * srv, int client, int how, int option) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_negotiate(pclient->telnet, how, option);
+  return 0;
+}
+
+/** Sends a telnet start of subnegotiation to the numbered client. */
+int woe_server_begin_sb(struct woe_server * srv, int client, int telopt) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_begin_sb(pclient->telnet, telopt);
+  return 0;
+}
+
+/** Sends a telnet end of subnegotiation to the numbered client. */
+int woe_server_finish_sb(struct woe_server * srv, int client) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_finish_sb(pclient->telnet);
+  return 0;
+}
+
+
+/** Sends a complete telnet subnegotiation  buffer to the numbered client. */
+int woe_server_subnegotiation(struct woe_server * srv, int client, int telopt, char * buffer, int size) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_subnegotiation(pclient->telnet, telopt, buffer, size);
+  return 0;
+}
+
+/** Begin sending compressed data to the to the numbered client. */
+int woe_server_begin_compress2(struct woe_server * srv, int client) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_begin_compress2(pclient->telnet);
+  return 0;
+}
+
+
+/** Send formated output with newline escaping to the to the numbered client. */
+int woe_server_vprintf(struct woe_server * srv, int client, const char *fmt, va_list va) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_vprintf(pclient->telnet, fmt, va);  
+  return 0;
+}
+
+/** Send formated output with newline escaping to the to the numbered client. */
+int woe_server_printf(struct woe_server * srv, int client, const char *fmt, ...) {
+  va_list va;
+  int res;
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  va_start(va, fmt);
+  telnet_vprintf(pclient->telnet, fmt, va);
+  va_end(va);
+  return 0;
+}
+
+/** Send formated output without newline escaping to the to the numbered client. */
+int woe_server_raw_vprintf(struct woe_server * srv, int client, const char *fmt, va_list va) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_raw_vprintf(pclient->telnet, fmt, va);
+  return 0;  
+}
+
+/** Send formated output without newline escaping to the to the numbered client. */
+int woe_server_raw_printf(struct woe_server * srv, int client, const char *fmt, ...) {
+  va_list va;
+  int res;
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  va_start(va, fmt);
+  res = telnet_raw_vprintf(pclient->telnet, fmt, va);
+  va_end(va);
+  return 0;
+}
+
+
+/** Begin a NEW-ENVIRON subnegotiation with the numbered client. */
+int woe_server_begin_newenviron(struct woe_server * srv, int client, int type) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_begin_newenviron(pclient->telnet, type);
+  return 0;
+}
+
+/** Send a NEW-ENVIRON variable name or value to the numbered client. */
+int woe_server_newenviron_value(struct woe_server * srv, int client, int type, char * value) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_newenviron_value(pclient->telnet, type, value);
+  return 0;
+}
+
+/** Finish a NEW-ENVIRON subnegotiation with the numbered client. */
+int woe_server_finish_newenviron(struct woe_server * srv, int client) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_finish_newenviron(pclient->telnet);
+  return 0;
+}
+
+/** Send a TERMINAL-TYPE SEND command to the numbered client. */
+int woe_server_ttype_send(struct woe_server * srv, int client) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_ttype_send(pclient->telnet);
+  return 0;
+}
+
+/** Send a TERMINAL-TYPE IS command to the numbered client. */
+int woe_server_ttype_is(struct woe_server * srv, int client, char * ttype) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_ttype_is(pclient->telnet, ttype);
+  return 0;
+}
+
+
+/** Send a ZMP command to the numbered client. */
+int woe_server_send_zmp(struct woe_server * srv, int client, int argc, const char ** argv) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_send_zmp(pclient->telnet, argc, argv);
+  return 0;
+}
+
+/** Send a ZMP command to the numbered client. */
+int woe_server_send_vzmpv(struct woe_server * srv, int client, va_list va) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_send_vzmpv(pclient->telnet, va);
+  return 0;
+}
+
+/** Send a ZMP command to the numbered client. */
+int woe_server_send_zmpv(struct woe_server * srv, int client, ...) {
+  va_list va;
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  va_start(va, client);
+  telnet_send_vzmpv(pclient->telnet, va);
+  va_end(va);
+  return 0;
+}
+
+/** Begin sending a ZMP command to the numbered client. */
+int woe_server_begin_zmp(struct woe_server * srv, int client, const char * cmd) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_begin_zmp(pclient->telnet, cmd);
+  return 0;
+}
+
+
+/** Send a ZMP command argument to the numbered client. */
+int woe_server_zmp_arg(struct woe_server * srv, int client, const char * arg) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_zmp_arg(pclient->telnet, arg);
+  return 0;
+}
+
+/** Finish sending a ZMP command to the numbered client. */
+int woe_server_finish_zmp(struct woe_server * srv, int client, const char * cmd) {
+  struct woe_client * pclient;
+  if (!srv)     return -1;
+  pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -2;
+  telnet_finish_zmp(pclient->telnet);
+  return 0;
+}
+
+
+/** Disconnect a client from the server. */
+int woe_server_disconnect(struct woe_server * srv, struct woe_client * client) {
+  int index;
+  
+  if (!srv)     return 1;
+  if (!client)  return 2; 
+  close(client->sock);
+  if (srv->disconnect_handler) {
+    srv->disconnect_handler(srv, client, NULL);
+  }
+  index = client->index;
+  
+  if (srv->mrb) {
+    rh_run_toplevel(srv->mrb, "woe_on_disconnect", "i", index);
+  }
+    
+  /* Get rid of client, will also free memory asociated. */
+  woe_server_remove_client(srv, index);  
+  return 0;
+}
+
+
+/** Forcfullly disconnect a client from the server by id. 
+ * Set a quit flag that woe_server_update will check. */
+int woe_server_disconnect_id(struct woe_server * srv, int id) {
+  struct woe_client * client = woe_server_get_client(srv, id);
+  if (!client) return -1;
+  client->busy = 0;
+  return 0;
+}
+
+/** Polls the server once and updates any of the clients if needed. */
+int woe_server_update(struct woe_server * srv, int timeout) {
+  int i, res;
+
+  /* prepare for poll */
+  memset(srv->pfd     , 0     , sizeof(srv->pfd));
+  
+  
+  for (i = 0; i != WOE_CLIENTS_MAX; ++i) {
+   struct woe_client * client = woe_server_get_client(srv, i);
+   if (client) {
+     srv->pfd[i].fd     = client->sock;
+     srv->pfd[i].events = POLLIN;
+   } else {
+     srv->pfd[i].fd     = -1;
+     srv->pfd[i].events = 0;
+   }
+  }
+  
+  /* Also listen for connnect events. */
+  srv->pfd[WOE_CLIENTS_MAX].fd = srv->listen_sock;
+  srv->pfd[WOE_CLIENTS_MAX].events = POLLIN;
+  
+  
+
+  /* Poll for activity */
+  res = poll(srv->pfd, WOE_CLIENTS_MAX + 1, timeout);
+
+  /* Check for time out */
+  if (res == 0) {
+   /* Time out but that's OK. */
+   return 0;
+  }
+
+  /* Log errors. */
+  if (res == -1 && errno != EINTR) {
+    LOG_ERROR("poll() failed: %s\n", strerror(errno));
+    return 1;
+  }
+
+  /* Handle new connection connection */
+  if (srv->pfd[WOE_CLIENTS_MAX].revents & POLLIN) {
+    woe_server_handle_connect(srv);
+  }
+
+   /* Read from clients */
+  for (i = 0; i < WOE_CLIENTS_MAX; ++i) {
+    struct woe_client * client = woe_server_get_client(srv, i);
+    if (!client) continue;
+    
+    /* Input from clients. */
+    if (srv->pfd[i].revents & POLLIN) {
+      res = recv(client->sock, srv->buffer, sizeof(srv->buffer), 0);
+      if (res < 0) {
+        LOG_ERROR("recv(client) failed: %s\n", strerror(errno));
+      } else if (res == 0) {
+        /* Disconnect the client. */
+        woe_server_disconnect(srv, client);
+      } else {
+        /* Let telnet lib process incoming data. */
+        telnet_recv(client->telnet, srv->buffer, res);
+        // telnet_send(client->telnet, srv->buffer, res);
+        // telnet_send(telnet, ev->data.buffer, ev->data.size);
+      }
+    }
+  }
+  
+  /* Disconnect clients that should quit */
+  for (i = 0; i < WOE_CLIENTS_MAX; ++i) {
+    struct woe_client * client = woe_server_get_client(srv, i);
+    if (!client) continue;
+    if (!client->busy) {
+      woe_server_disconnect(srv, client);
+    }  
+  }
+  
+  return 0;
+}
+
+
+int woe_server_send_to_client(struct woe_server * srv, int client, char * data, size_t size) {
+  struct woe_client * pclient = woe_server_get_client(srv, client);
+  if (!pclient) return -1;
+  telnet_send(pclient->telnet, data, size);
+  return size;
+}
+
+
+

+ 362 - 0
src/toruby.c

@@ -0,0 +1,362 @@
+/*
+* toruby.c helps expose functionality from C to Mruby for Eruta.
+* All functions are prefixed with tr_.
+* Look at the tr_*.c files.
+* */
+
+#include "toruby.h"
+#include "tr_macro.h"
+#include "monolog.h"
+#include "rh.h"
+#include "state.h"
+#include "server.h"
+
+#include <mruby/hash.h>
+#include <mruby/class.h>
+#include <mruby/data.h>
+#include <mruby/array.h>
+/*
+#include "tr_macro.h"
+#include "tr_audio.h"
+#include "tr_graph.h"
+#include "tr_store.h"
+#include "tr_sprite.h"
+*/
+
+/* Documentation of mrb_get_args: 
+ 
+  retrieve arguments from mrb_state.
+
+  mrb_get_args(mrb, format, ...)
+
+  returns number of arguments parsed.
+
+  format specifiers:
+
+   o: Object [mrb_value]
+   S: String [mrb_value]
+   A: Array [mrb_value]
+   H: Hash [mrb_value]
+   s: String [char*,int]
+   z: String [char*] nul terminated
+   a: Array [mrb_value*,mrb_int]
+   f: Float [mrb_float]
+   i: Integer [mrb_int]
+   b: Binary [int]
+   n: Symbol [mrb_sym]
+   &: Block [mrb_value]
+   *: rest argument [mrb_value*,int]
+   |: optional
+ */
+
+
+
+
+/** Writes a NOTE message to the log. */
+static mrb_value tr_log(mrb_state * mrb, mrb_value self) {
+  (void) self; (void) mrb;
+  
+  mrb_value text    = mrb_nil_value();
+  mrb_get_args(mrb, "S", &text);
+  
+  LOG_NOTE("%s\n", RSTRING_PTR(text));
+  return self;
+}
+
+/** Writes a messageto a certain log level log. */
+static mrb_value tr_log_to(mrb_state * mrb, mrb_value self) {
+  (void) self; (void) mrb;
+  
+  mrb_value level   = mrb_nil_value();
+  mrb_value text    = mrb_nil_value();
+
+  mrb_get_args(mrb, "SS", &level, &text);
+  
+  LOG_LEVEL(RSTRING_PTR(level), "%s\n", RSTRING_PTR(text));
+  return self;
+}
+
+
+/** Cause a warning to be logged */
+static mrb_value tr_warn(mrb_state * mrb, mrb_value self) {
+  (void) self; (void) mrb;
+  
+  mrb_value text    = mrb_nil_value();
+  mrb_get_args(mrb, "S", &text);
+  LOG_WARNING("%s\n", RSTRING_PTR(text));
+  return self;
+}
+
+
+/** Enables a certain log level */
+static mrb_value tr_log_enable(mrb_state * mrb, mrb_value self) {
+  (void) self; (void) mrb;
+  
+  mrb_value text    = mrb_nil_value();
+  mrb_get_args(mrb, "S", &text);
+  monolog_enable_level(RSTRING_PTR(text));
+  return self;
+}
+
+/** Disables a certain log level */
+static mrb_value tr_log_disable(mrb_state * mrb, mrb_value self) {
+  (void) self; (void) mrb;
+
+  mrb_value text    = mrb_nil_value();
+  mrb_get_args(mrb, "S", &text);
+  monolog_disable_level(RSTRING_PTR(text));
+  return self;
+}
+
+
+/* Loads another script from the script directory. */
+static mrb_value tr_script(mrb_state * mrb, mrb_value self) {
+  int res; 
+  char * command;
+  
+  (void) self;
+  
+  mrb_value text        = mrb_nil_value();
+  mrb_get_args(mrb, "S", &text);
+  command               = mrb_str_to_cstr(mrb, text);
+  
+  res = rh_run_script(mrb, command);
+  return mrb_fixnum_value(res);
+}
+
+
+/* Sends data to a given client */
+static mrb_value tr_send_to_client(mrb_state * mrb, mrb_value self) {
+  int res; 
+  mrb_int client = -1;
+  char * data    = NULL;
+  int    size    = 0;
+  struct woe_server * srv = MRB_WOE_SERVER(mrb);
+  
+  (void) self;
+  mrb_get_args(mrb, "is", &client, &data, &size);
+  res = woe_server_send_to_client(srv, client, data, size);
+  
+  return mrb_fixnum_value(res);
+}
+
+/* Shuts down a given client */
+static mrb_value tr_server_done(mrb_state * mrb, mrb_value self) {
+  struct woe_server * srv = MRB_WOE_SERVER(mrb);
+  
+  (void) self;
+  woe_server_request_shutdown(srv);
+  return mrb_nil_value(); 
+}
+
+
+/* Disconnects a given client by id */
+static mrb_value tr_disconnect_client(mrb_state * mrb, mrb_value self) {
+  int res; 
+  mrb_int client;
+  struct woe_server * srv = MRB_WOE_SERVER(mrb);
+  
+  (void) self;
+  mrb_get_args(mrb, "i", &client);
+  res = woe_server_disconnect_id(srv, client);
+  
+  return mrb_fixnum_value(res);
+}
+
+
+/* Yeah, I know, but this reduces boilerplate. */
+#define WRAP_SERVER_BEGIN(NAME) \
+static mrb_value NAME(mrb_state * mrb, mrb_value self) {          \
+  int res;                                                        \
+  mrb_int client = -1;                                            \
+  struct woe_server * srv = MRB_WOE_SERVER(mrb);                  \
+  (void) self;                                                    \
+  
+  
+#define WRAP_SERVER_END() \
+  return mrb_fixnum_value(res); \
+}
+    
+
+WRAP_SERVER_BEGIN(tr_server_iac) { 
+  mrb_int command = 0;  
+  mrb_get_args(mrb, "ii", &client, &command);
+  res = woe_server_iac(srv, client, command);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_negotiate) { 
+  mrb_int how = 0, option = 0;  
+  mrb_get_args(mrb, "iii", &client, &how, &option);
+  res = woe_server_negotiate(srv, client, how, option);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_begin_sb) { 
+  mrb_int telopt = 0;  
+  mrb_get_args(mrb, "ii", &client, &telopt);
+  res = woe_server_begin_sb(srv, client, telopt);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_finish_sb) { 
+  mrb_get_args(mrb, "i", &client);
+  res = woe_server_finish_sb(srv, client);
+} WRAP_SERVER_END()
+
+
+WRAP_SERVER_BEGIN(tr_server_subnegotiation) { 
+  mrb_int telopt = 0, size = 0;
+  char * data = NULL;  
+  mrb_get_args(mrb, "iis", &client, &telopt, &data, &size);
+  res = woe_server_subnegotiation(srv, client, telopt, data, size);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_begin_compress2) { 
+  mrb_get_args(mrb, "i", &client);
+  res = woe_server_begin_compress2(srv, client);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_puts) { 
+  char * fmt = NULL;  
+  mrb_get_args(mrb, "iz", &client, &fmt);
+  res = woe_server_printf(srv, client, fmt);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_raw_puts) { 
+  char * fmt = NULL;  
+  mrb_get_args(mrb, "iz", &client, &fmt);
+  res = woe_server_raw_printf(srv, client, fmt);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_begin_newenviron) { 
+  mrb_int type;
+  mrb_get_args(mrb, "ii", &client, &type);
+  res = woe_server_begin_newenviron(srv, client, type);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_newenviron_value) { 
+  mrb_int type;
+  char * value = NULL;  
+  mrb_get_args(mrb, "iiz", &client, &type, &value);
+  res = woe_server_newenviron_value(srv, client, type, value);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_finish_newenviron) { 
+  mrb_get_args(mrb, "i", &client);
+  res = woe_server_finish_newenviron(srv, client);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_ttype_send) { 
+  mrb_get_args(mrb, "i", &client);
+  res = woe_server_ttype_send(srv, client);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_ttype_is) { 
+  mrb_int type;
+  char * ttype = NULL;  
+  mrb_get_args(mrb, "iz", &client, &ttype);
+  res = woe_server_ttype_is(srv, client, ttype);
+} WRAP_SERVER_END()
+
+/*
+int woe_server_send_zmp(struct woe_server * srv, int client, int argc, const char ** argv);
+int woe_server_send_vzmpv(struct woe_server * srv, int client, va_list va);
+int woe_server_send_zmpv(struct woe_server * srv, int client, ...);
+*/
+WRAP_SERVER_BEGIN(tr_server_begin_zmp) { 
+  char * command = NULL;
+  mrb_get_args(mrb, "iz", &client, &command);
+  res = woe_server_begin_zmp(srv, client, command);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_zmp_arg) { 
+  char * arg = NULL;  
+  mrb_get_args(mrb, "iz", &client, &arg);
+  res = woe_server_zmp_arg(srv, client, arg);
+} WRAP_SERVER_END()
+
+WRAP_SERVER_BEGIN(tr_server_finish_zmp) { 
+  char * command;
+  mrb_get_args(mrb, "iz", &client, &command);
+  res = woe_server_finish_zmp(srv, client, command);
+} WRAP_SERVER_END()
+
+/* Initializes the functionality that Eruta exposes to Ruby. */
+int tr_init(mrb_state * mrb) {
+  // luaL_dostring(lua, "print 'Hello!' ");
+  struct RClass *woe;
+  struct RClass *srv;
+  struct RClass *krn;
+ 
+  woe = mrb_define_module(mrb, "Woe");
+  srv = mrb_define_module_under(mrb, woe, "Server"); 
+  TR_CLASS_METHOD_NOARG(mrb, woe, "quit"  , tr_server_done);
+  TR_CLASS_METHOD_NOARG(mrb, srv, "quit"  , tr_server_done);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "send_to_client"  , tr_send_to_client, 2);
+  TR_CLASS_METHOD_NOARG(mrb, srv, "disconnect"  , tr_disconnect_client);
+
+int woe_server_iac(struct woe_server * srv, int client, int command);
+int woe_server_negotiate(struct woe_server * srv, int client, int how, int option);
+int woe_server_begin_sb(struct woe_server * srv, int client, int telopt);
+int woe_server_finish_sb(struct woe_server * srv, int client);
+int woe_server_subnegotiation(struct woe_server * srv, int client, int telopt, char * buffer, int size);
+int woe_server_begin_compress2(struct woe_server * srv, int client);
+int woe_server_vprintf(struct woe_server * srv, int client, const char *fmt, va_list va);
+int woe_server_printf(struct woe_server * srv, int client, const char *fmt, ...);
+int woe_server_raw_vprintf(struct woe_server * srv, int client, const char *fmt, va_list va);
+int woe_server_raw_printf(struct woe_server * srv, int client, const char *fmt, ...);
+int woe_server_begin_newenviron(struct woe_server * srv, int client, int type);
+int woe_server_newenviron_value(struct woe_server * srv, int client, int type, char * value);
+int woe_server_finish_newenviron(struct woe_server * srv, int client);
+int woe_server_ttype_send(struct woe_server * srv, int client);
+int woe_server_ttype_is(struct woe_server * srv, int client, char * ttype);
+int woe_server_send_zmp(struct woe_server * srv, int client, int argc, const char ** argv);
+int woe_server_send_vzmpv(struct woe_server * srv, int client, va_list va);
+int woe_server_send_zmpv(struct woe_server * srv, int client, ...);
+int woe_server_begin_zmp(struct woe_server * srv, int client, const char * cmd);
+int woe_server_zmp_arg(struct woe_server * srv, int client, const char * arg);
+int woe_server_finish_zmp(struct woe_server * srv, int client, const char * cmd);
+
+
+  TR_CLASS_METHOD_ARGC(mrb, srv, "iac"  , tr_server_iac, 2);
+
+  TR_CLASS_METHOD_ARGC(mrb, srv, "negotiate"      , tr_server_negotiate     , 3);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "begin_sb"       , tr_server_begin_sb      , 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "finish_sb"      , tr_server_finish_sb     , 1);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "subnegotiation" , tr_server_subnegotiation, 3);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "begin_compress2", tr_server_begin_compress2, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "puts"           , tr_server_puts, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "raw_puts"       , tr_server_raw_puts, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "begin_newenviron" , tr_server_begin_newenviron, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "newenviron_value" , tr_server_newenviron_value, 3);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "finish_newenviron", tr_server_finish_newenviron, 1);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "ttype_send"  , tr_server_ttype_send, 1);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "ttype_is"    , tr_server_ttype_is, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "begin_zmp"  , tr_server_finish_zmp, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "zmp_arg"  , tr_server_finish_zmp, 2);
+  TR_CLASS_METHOD_ARGC(mrb, srv, "finish_zmp"  , tr_server_finish_zmp, 2);
+
+  
+  krn = mrb_module_get(mrb, "Kernel");
+  if(!krn) return -1;
+  
+  TR_METHOD_ARGC(mrb, krn, "warn"         , tr_warn   , 1);
+  TR_METHOD_ARGC(mrb, krn, "warning"      , tr_warn   , 1);
+  TR_METHOD_ARGC(mrb, krn, "log"          , tr_log    , 1);
+  TR_METHOD_ARGC(mrb, krn, "log_to"       , tr_log_to , 2);
+  TR_METHOD_ARGC(mrb, krn, "log_enable"   , tr_log_disable , 1);
+  TR_METHOD_ARGC(mrb, krn, "log_disable"  , tr_log_enable  , 1);
+  TR_METHOD_ARGC(mrb, krn, "script"       , tr_script , 1);
+
+   
+  // must restore gc area here ????
+  mrb_gc_arena_restore(mrb, 0);
+  
+  return 0;
+}
+
+
+
+
+
+
+
+

BIN
src/woe-server


+ 86 - 0
woe.geany

@@ -0,0 +1,86 @@
+[file_prefs]
+final_new_line=true
+ensure_convert_new_lines=true
+strip_trailing_spaces=false
+replace_tabs=true
+
+[indentation]
+indent_width=2
+indent_type=0
+indent_hard_tab_width=8
+detect_indent=false
+detect_indent_width=false
+indent_mode=2
+
+[project]
+name=WOE
+base_path=/home/bjorn/src/woe
+description=
+file_patterns=
+
+[long line marker]
+long_line_behaviour=1
+long_line_column=80
+
+[files]
+current_page=16
+FILE_NAME_0=1078;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fmain.c;0;2
+FILE_NAME_1=1675;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fcallrb.c;0;2
+FILE_NAME_2=2828;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Ftoruby.c;0;2
+FILE_NAME_3=94;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fekq%2Ftest%2Ftest_monolog.c;0;2
+FILE_NAME_4=0;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fwork%2Fsrc%2Fsplix%2Fsrc%2Fbarg.c;0;2
+FILE_NAME_5=16838;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fwork%2Fsrc%2Fsplix%2Fsrc%2Fsplix.c;0;2
+FILE_NAME_6=1696;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fserver.c;0;2
+FILE_NAME_7=4816;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Flibtelnet.c;0;2
+FILE_NAME_8=1491;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fclient.c;0;2
+FILE_NAME_9=158;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fconfig.c;0;2
+FILE_NAME_10=464;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fclient.h;0;2
+FILE_NAME_11=240;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fconfig.h;0;2
+FILE_NAME_12=3599;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fesh.h;0;2
+FILE_NAME_13=3217;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fesh.c;0;2
+FILE_NAME_14=641;None;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2FTupfile;0;2
+FILE_NAME_15=1810;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Frh.h;0;2
+FILE_NAME_16=3505;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Frh.c;0;2
+FILE_NAME_17=294;Ruby;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fdata%2Fscript%2Fmain.rb;0;2
+FILE_NAME_18=17639;C;0;EUTF-8;0;1;0;%2Fusr%2Flocal%2Finclude%2Fmruby.h;0;2
+FILE_NAME_19=4107;C;0;EUTF-8;0;1;0;%2Fusr%2Flocal%2Finclude%2Fmruby%2Fvalue.h;0;2
+FILE_NAME_20=2;C;0;EUTF-8;0;1;0;%2Fusr%2Flocal%2Finclude%2Fmruby%2Firep.h;0;2
+FILE_NAME_21=4903;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fekq%2Fsrc%2Frh.c;0;2
+FILE_NAME_22=1167;C;0;EUTF-8;0;1;0;%2Fusr%2Flocal%2Finclude%2Fmruby%2Fobject.h;0;2
+FILE_NAME_23=0;C;0;EUTF-8;0;1;0;%2Fusr%2Flocal%2Finclude%2Fmruby%2Fvariable.h;0;2
+FILE_NAME_24=2341;C;0;EUTF-8;0;1;0;%2Fusr%2Flocal%2Finclude%2Fmruby%2Fclass.h;0;2
+FILE_NAME_25=60;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fmem.h;0;2
+FILE_NAME_26=426;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fstate.h;0;2
+FILE_NAME_27=9297;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fmruby-matz%2Fsrc%2Fvariable.c;0;2
+FILE_NAME_28=4154;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fmruby-matz%2Fmrbgems%2Fmruby-time%2Fsrc%2Ftime.c;0;2
+FILE_NAME_29=0;Markdown;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fmruby-matz%2Ftest%2FREADME.md;0;2
+FILE_NAME_30=0;Ruby;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fmruby-matz%2Ftest%2Ft%2Fobject.rb;0;2
+FILE_NAME_31=0;Ruby;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fmruby-matz%2Ftest%2Ft%2Fbasicobject.rb;0;2
+FILE_NAME_32=2520;Ruby;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fmruby-matz%2Ftest%2Ft%2Fsyntax.rb;0;2
+FILE_NAME_33=61;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fmonolog.c;0;2
+FILE_NAME_34=15684;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Flibtelnet.h;0;2
+FILE_NAME_35=95;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fmonolog.h;0;2
+FILE_NAME_36=235;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fdynar.h;0;2
+FILE_NAME_37=103;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fmem.c;0;2
+FILE_NAME_38=5343;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Fsrc%2Fdynar.c;0;2
+FILE_NAME_39=685;C;0;EUTF-8;0;1;0;%2Fhome%2Fbjorn%2Fsrc%2Fwoe%2Finclude%2Fserver.h;0;2
+
+[VTE]
+last_dir=/home/bjorn/src/woe
+
+[build-menu]
+EX_00_LB=_Execute
+EX_00_CM=bin/woe-server
+EX_00_WD=%p
+NF_00_LB=_Tup
+NF_00_CM=tup
+NF_00_WD=%p
+NF_01_LB=Make Custom _Target
+NF_01_CM=make 
+NF_01_WD=%p
+EX_01_LB=Execute Test
+EX_01_CM=bin/test_bxml
+EX_01_WD=%p
+NF_03_LB=Make WOE
+NF_03_CM=make woe
+NF_03_WD=%p