]> jfr.im git - irc/quakenet/newserv.git/blobdiff - core/hooks.c
CHANSERV: remove E type escapes
[irc/quakenet/newserv.git] / core / hooks.c
index baf23b013eab43e6312bb1d4577e7bfbfdfb3521..104e525c21398f5eca535a5c82b9110767df91c7 100644 (file)
 
 #include "hooks.h"
 #include <assert.h>
-#include "../lib/array.h"
+#include "../core/error.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
 
-array hooks[HOOKMAX];
+#define HF_CONTIGUOUS 0x01
+
+typedef struct Hook {
+  HookCallback callback;
+  long priority;
+  short flags;
+  struct Hook *next;
+} Hook;
+
+typedef struct HookHead {
+  int dirty;
+  Hook *head;
+} HookHead;
+
+static HookHead hooks[HOOKMAX];
+static int dirtyhooks[HOOKMAX];
+static int dirtyhookcount;
+
+unsigned int hookqueuelength = 0;
+
+static void collectgarbage(HookHead *h);
+static void markdirty(int hook);
 
 void inithooks() {
-  int i;
-  
-  hookqueuelength=0;
-  for (i=0;i<HOOKMAX;i++) {
-    array_init(&(hooks[i]),sizeof(HookCallback));
-    array_setlim1(&(hooks[i]),2);
-    array_setlim2(&(hooks[i]),2);
-  }
 }
 
 int registerhook(int hooknum, HookCallback callback) {
-  int i;
-  HookCallback *hcbs;
-  
-  if (hooknum>HOOKMAX)
+  return registerpriorityhook(hooknum, callback, PRIORITY_DEFAULT);
+}
+
+int registerpriorityhook(int hooknum, HookCallback callback, long priority) {
+  Hook *hp, *pred, *n;
+  HookHead *h;
+
+  if(hooknum>HOOKMAX)
     return 1;
-    
-  hcbs=(HookCallback *)(hooks[hooknum].content);
-  for(i=0;i<hooks[hooknum].cursi;i++)
-    if(hcbs[i]==callback)
+
+  if(hookqueuelength > 0)
+    Error("core", ERR_WARNING, "Attempting to register hook %d inside a hook queue: %p", hooknum, callback);
+
+  for(pred=NULL,h=&hooks[hooknum],hp=h->head;hp;hp=hp->next) {
+    if(hp->callback==callback)
       return 1;
+    if(priority>=hp->priority)
+      pred=hp;
+  }
+
+  n = malloc(sizeof(Hook));
+  n->priority = priority;
+  n->callback = callback;
+  n->flags = 0;
+
+  if(!pred) {
+    n->next = h->head;
+    h->head = n;
+  } else {
+    n->next = pred->next;
+    pred->next = n;
+  }
 
-  i=array_getfreeslot(&hooks[hooknum]);
-  hcbs=(HookCallback *)(hooks[hooknum].content);
-  hcbs[i]=callback;
-  
   return 0;
 }
 
 int deregisterhook(int hooknum, HookCallback callback) {
-  int i;
-  HookCallback *hcbs;
-  
+  Hook *hp;
+  HookHead *h;
+
   if (hooknum>HOOKMAX)
     return 1;
     
-  hcbs=(HookCallback *)(hooks[hooknum].content);
+  if(hookqueuelength > 0)
+    Error("core", ERR_WARNING, "Attempting to deregister hook %d inside a hook queue: %p", hooknum, callback);
 
-  for(i=0;i<hooks[hooknum].cursi;i++)
-    if(hcbs[i]==callback) {
-      array_delslot(&(hooks[hooknum]),i);
+  for(h=&hooks[hooknum],hp=h->head;hp;hp=hp->next) {
+    if(hp->callback==callback) {
+      markdirty(hooknum);
+      hp->callback = NULL;
       return 0;
     }
+  }
 
   return 1;
 }
   
 void triggerhook(int hooknum, void *arg) {
   int i;
-  HookCallback *hcbs;
-  
+  Hook *hp;
+
   if (hooknum>HOOKMAX)
     return;
     
   hookqueuelength++;
-  hcbs=(HookCallback *)(hooks[hooknum].content);
-  for (i=0;i<hooks[hooknum].cursi;i++) {
-    (hcbs[i])(hooknum, arg);
+  for(hp=hooks[hooknum].head;hp;hp=hp->next) {
+    if(hp->callback)
+      (hp->callback)(hooknum, arg);
   }
   hookqueuelength--;
-  
-  if (!hookqueuelength && hooknum!=HOOK_CORE_ENDOFHOOKSQUEUE)
+
+  if (!hookqueuelength && hooknum!=HOOK_CORE_ENDOFHOOKSQUEUE) {
     triggerhook(HOOK_CORE_ENDOFHOOKSQUEUE, 0);
+
+    for(i=0;i<dirtyhookcount;i++) {
+      collectgarbage(&hooks[dirtyhooks[i]]);
+    }
+    dirtyhookcount=0;
+  }
+}
+
+static void markdirty(int hook) {
+  if(hooks[hook].dirty)
+    return;
+
+  hooks[hook].dirty = 1;
+  dirtyhooks[dirtyhookcount++] = hook;
+}
+
+static void collectgarbage(HookHead *h) {
+  Hook *hp, *pp, *np;
+
+  for(pp=NULL,hp=h->head;hp;hp=np) {
+    np=hp->next;
+    if(hp->callback==NULL) {
+      free(hp);
+    } else {
+      if(pp==NULL) {
+        h->head = hp;
+      } else {
+        pp->next = hp;
+      }
+      pp = hp;
+    }
+  }
+  if(!pp) {
+    h->head = NULL;
+  } else {
+    pp->next = NULL;
+  }
+
+  h->dirty = 0;
 }
-