diff --git a/src/modules/call_out/.cvsignore b/src/modules/call_out/.cvsignore
new file mode 100644
index 0000000000000000000000000000000000000000..76e8a85506890a9ad4125ab962e5b80341ffecdc
--- /dev/null
+++ b/src/modules/call_out/.cvsignore
@@ -0,0 +1,2 @@
+configure
+Makefile.in
diff --git a/src/modules/call_out/.gitignore b/src/modules/call_out/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..1f3e111bcb9a733de582cbc47266cc26dd1ec797
--- /dev/null
+++ b/src/modules/call_out/.gitignore
@@ -0,0 +1,2 @@
+/configure
+/Makefile.in
diff --git a/src/modules/call_out/Makefile.src b/src/modules/call_out/Makefile.src
new file mode 100644
index 0000000000000000000000000000000000000000..0de1983c20ea991e4703f07109ad3f8a690806ba
--- /dev/null
+++ b/src/modules/call_out/Makefile.src
@@ -0,0 +1,17 @@
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+PREFLAGS=-I$(SRCDIR) -I$(SRCDIR)/../.. -I../..
+CFLAGS=$(PREFLAGS) $(OTHERFLAGS)
+
+FILES=call_out.o
+
+call_out.a: $(FILES)
+	-rm -f call_out.a
+	ar cq call_out.a $(FILES)
+	-@RANLIB@ call_out.a
+
+clean:
+	-rm -f *.o *.a
+
+depend:
+	gcc -MM $(PREFLAGS) $(SRCDIR)/*.c | $(FIXDEP) $(SRCDIR)
diff --git a/src/modules/call_out/call_out.c b/src/modules/call_out/call_out.c
new file mode 100644
index 0000000000000000000000000000000000000000..74ee5cb654f0cb46b3c1ae3331f4381a85316105
--- /dev/null
+++ b/src/modules/call_out/call_out.c
@@ -0,0 +1,359 @@
+/*\
+||| This file a part of uLPC, and is copyright by Fredrik Hubinette
+||| uLPC is distributed as GPL (General Public License)
+||| See the files COPYING and DISCLAIMER for more information.
+\*/
+#include "global.h"
+#include "array.h"
+#include "dynamic_buffer.h"
+#include "object.h"
+#include "interpret.h"
+#include "error.h"
+#include "builtin_efuns.h"
+#include "memory.h"
+#include "main.h"
+#include "backend.h"
+#include "time_stuff.h"
+#include "add_efun.h"
+
+#include "callback.h"
+
+#include <math.h>
+
+struct call_out_s
+{
+  struct timeval tv;
+  struct object *caller;
+  struct array *args;
+};
+
+typedef struct call_out_s call_out;
+
+call_out **pending_calls=0;      /* pointer to first busy pointer */
+int num_pending_calls;           /* no of busy pointers in buffer */
+static call_out **call_buffer=0; /* pointer to buffer */
+static int call_buffer_size;     /* no of pointers in buffer */
+
+static void verify_call_outs()
+{
+#ifdef DEBUG
+  struct array *v;
+  int e;
+
+  if(!d_flag) return;
+  if(!call_buffer) return;
+
+  if(num_pending_calls<0 || num_pending_calls>call_buffer_size)
+    fatal("Error in call out tables.\n");
+
+  if(pending_calls+num_pending_calls!=call_buffer+call_buffer_size)
+    fatal("Error in call out tables.\n");
+
+  for(e=0;e<num_pending_calls;e++)
+  {
+    if(e)
+    {
+      if(my_timercmp(&pending_calls[e-1]->tv,>,&pending_calls[e]->tv))
+	fatal("Error in call out order.\n");
+    }
+    
+    if(!(v=pending_calls[e]->args))
+      fatal("No arguments to call\n");
+
+    if(v->refs!=1)
+      fatal("Array should exactly have one reference.\n");
+
+    if(v->malloced_size<v->size)
+      fatal("Impossible array.\n");
+  }
+#endif
+}
+
+
+/* start a new call out, return 1 for success */
+static int new_call_out(int num_arg,struct svalue *argp)
+{
+  int e,c;
+  call_out *new,**p,**pos;
+
+  if(!call_buffer)
+  {
+    call_buffer_size=20;
+    call_buffer=(call_out **)xalloc(sizeof(call_out *)*call_buffer_size);
+    if(!call_buffer) return 0;
+    pending_calls=call_buffer+call_buffer_size;
+    num_pending_calls=0;
+  }
+
+  if(num_pending_calls==call_buffer_size)
+  {
+    /* here we need to allocate space for more pointers */
+    call_out **new_buffer;
+
+    new_buffer=(call_out **)xalloc(sizeof(call_out *)*call_buffer_size*2);
+    if(!new_buffer)
+      return 0;
+
+    MEMCPY((char *)(new_buffer+call_buffer_size),
+	   (char *)call_buffer,
+	   sizeof(call_out *)*call_buffer_size);
+    free((char *)call_buffer);
+    call_buffer=new_buffer;
+    pending_calls=call_buffer+call_buffer_size;
+    call_buffer_size*=2;
+  }
+
+  /* time to allocate a new call_out struct */
+  f_aggregate(num_arg-1);
+
+  new=(call_out *)xalloc(sizeof(call_out));
+
+  if(!new) return 0;
+  
+  if(argp[0].type==T_INT)
+  {
+    new->tv.tv_sec=argp[0].u.integer;
+    new->tv.tv_usec=0;
+  }
+  else if(argp[0].type == T_FLOAT)
+  {
+    FLOAT_TYPE tmp=argp[0].u.float_number;
+    new->tv.tv_sec=floor(tmp);
+    new->tv.tv_usec=(long)(1000000.0 * (tmp - floor(tmp)));
+  }
+  else
+  {
+    fatal("Bad timeout to new_call_out!\n");
+  }
+
+  my_add_timeval(& new->tv, &current_time);
+
+  if(fp && fp->current_object)
+  {
+    new->caller=fp->current_object;
+    new->caller->refs++;
+  }else{
+    new->caller=0;
+  }
+
+  new->args=sp[-1].u.array;
+  sp -= 2;
+
+  /* time to link it into the buffer using binsearch */
+  pos=pending_calls;
+
+  e=num_pending_calls;
+  while(e>0)
+  {
+    c=e/2;
+    if(my_timercmp(& new->tv,>,& pos[c]->tv))
+    {
+      pos+=c+1;
+      e-=c+1;
+    }else{
+      e=c;
+    }
+  }
+  pos--;
+  pending_calls--;
+  for(p=pending_calls;p<pos;p++) p[0]=p[1];
+  *pos=new;
+  num_pending_calls++;
+
+  verify_call_outs();
+  return 1;
+}
+
+void f_call_out(INT32 args)
+{
+  struct svalue tmp;
+  if(args<2)
+    error("Too few arguments to call_out.\n");
+
+  if(sp[1-args].type != T_INT && sp[1-args].type!=T_FLOAT)
+    error("Bad argument 2 to call_out.\n");
+
+  /* Swap, for compatibility */
+  tmp=sp[-args];
+  sp[-args]=sp[1-args];
+  sp[1-args]=tmp;
+
+  new_call_out(args,sp-args);
+}
+
+void do_call_outs(struct callback *ignored, void *ignored_too)
+{
+  call_out *c;
+  int args;
+  time_t tmp;
+  verify_call_outs();
+
+  tmp=(time_t)TIME(0);
+  while(num_pending_calls &&
+	my_timercmp(&pending_calls[0]->tv,<=,&current_time))
+  {
+    /* unlink call out */
+    c=pending_calls[0];
+    pending_calls++;
+    num_pending_calls--;
+
+    if(c->caller) free_object(c->caller);
+
+    args=c->args->size;
+    push_array_items(c->args);
+    free((char *)c);
+    check_destructed(sp-args);
+    if(sp[-args].type!=T_INT)
+    {
+      f_call_function(args);
+      pop_stack();
+    }else{
+      pop_n_elems(args);
+    }
+    verify_call_outs();
+
+    if(tmp != (time_t) TIME(0)) break;
+  }
+
+  if(num_pending_calls)
+    if(my_timercmp(& pending_calls[0]->tv, < , &next_timeout))
+      next_timeout = pending_calls[0]->tv;
+}
+
+static int find_call_out(struct svalue *fun)
+{
+  int e;
+  for(e=0;e<num_pending_calls;e++)
+  {
+    if(is_eq(fun, ITEM(pending_calls[e]->args)))
+      return e;
+  }
+  return -1;
+}
+
+void f_find_call_out(INT32 args)
+{
+  int e;
+  verify_call_outs();
+  e=find_call_out(sp - args);
+  pop_n_elems(args);
+  if(e==-1)
+  {
+    sp->type=T_INT;
+    sp->subtype=NUMBER_UNDEFINED;
+    sp->u.integer=-1;
+    sp++;
+  }else{
+    push_int(pending_calls[e]->tv.tv_sec - current_time.tv_sec);
+  }
+  verify_call_outs();
+}
+
+void f_remove_call_out(INT32 args)
+{
+  int e;
+  verify_call_outs();
+  e=find_call_out(sp-args);
+  if(e!=-1)
+  {
+    pop_n_elems(args);
+    push_int(pending_calls[e]->tv.tv_sec - current_time.tv_sec);
+    free_array(pending_calls[e]->args);
+    if(pending_calls[e]->caller)
+      free_object(pending_calls[e]->caller);
+    free((char*)(pending_calls[e]));
+    for(;e>0;e--)
+      pending_calls[e]=pending_calls[e-1];
+    pending_calls++;
+    num_pending_calls--;
+  }else{
+    pop_n_elems(args);
+    sp->type=T_INT;
+    sp->subtype=NUMBER_UNDEFINED;
+    sp->u.integer=-1;
+    sp++;
+  }
+  verify_call_outs();
+}
+
+/* return an array containing info about all call outs:
+ * ({  ({ delay, caller, function, args, ... }), ... })
+ */
+struct array *get_all_call_outs()
+{
+  int e;
+  struct array *ret;
+
+  verify_call_outs();
+  ret=allocate_array_no_init(num_pending_calls,0);
+  for(e=0;e<num_pending_calls;e++)
+  {
+    struct array *v;
+    v=allocate_array_no_init(pending_calls[e]->args->size+2, 0);
+    ITEM(v)[0].type=T_INT;
+    ITEM(v)[0].subtype=NUMBER_NUMBER;
+    ITEM(v)[0].u.integer=pending_calls[e]->tv.tv_sec - current_time.tv_sec;
+
+    if(pending_calls[e]->caller)
+    {
+      ITEM(v)[1].type=T_OBJECT;
+      (ITEM(v)[1].u.object=pending_calls[e]->caller) ->refs++;
+    }else{
+      ITEM(v)[1].type=T_INT;
+      ITEM(v)[1].subtype=NUMBER_NUMBER;
+      ITEM(v)[1].u.integer=0;
+    }
+
+    assign_svalues_no_free(ITEM(v)+2,ITEM(pending_calls[e]->args),pending_calls[e]->args->size,BIT_MIXED);
+
+    ITEM(ret)[e].type=T_ARRAY;
+    ITEM(ret)[e].u.array=v;
+  }
+  return ret;
+}
+
+void f_call_out_info(INT32 args)
+{
+  pop_n_elems(args);
+  push_array(get_all_call_outs());
+}
+
+void free_all_call_outs()
+{
+  int e;
+  verify_call_outs();
+  for(e=0;e<num_pending_calls;e++)
+  {
+    free_array(pending_calls[e]->args);
+    if(pending_calls[e]->caller) free_object(pending_calls[e]->caller);
+    free((char*)(pending_calls[e]));
+  }
+  if(call_buffer) free((char*)call_buffer);
+  num_pending_calls=0;
+  call_buffer=NULL;
+  pending_calls=NULL;
+}
+
+#ifdef DEBUG
+void verify_all_call_outs()
+{
+  verify_call_outs();
+}
+#endif
+
+void init_call_out_efuns(void)
+{
+  add_backend_callback(do_call_outs,0,0);
+
+  add_efun("call_out",f_call_out,"function(function,float|int,mixed...:void)",OPT_SIDE_EFFECT);
+  add_efun("call_out_info",f_call_out_info,"function(:array*)",OPT_EXTERNAL_DEPEND);
+  add_efun("find_call_out",f_find_call_out,"function(function:int)",OPT_EXTERNAL_DEPEND);
+  add_efun("remove_call_out",f_remove_call_out,"function(function:int)",OPT_SIDE_EFFECT);
+}
+
+void init_call_out_programs(void) {}
+
+void exit_call_out(void)
+{
+  free_all_call_outs();
+}
diff --git a/src/modules/call_out/configure.in b/src/modules/call_out/configure.in
new file mode 100644
index 0000000000000000000000000000000000000000..ae228dce800c86dd4524a5b715255fe3a7eb943d
--- /dev/null
+++ b/src/modules/call_out/configure.in
@@ -0,0 +1,9 @@
+AC_INIT(call_out.c)
+
+AC_PROG_CC
+AC_PROG_RANLIB
+AC_SUBST(RANLIB)
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
+
+