]> jfr.im git - irc/quakenet/newserv.git/commitdiff
Add newsearch AST implementation, including example NO command (spew).
authorChris Porter <redacted>
Mon, 24 Mar 2008 09:17:42 +0000 (09:17 +0000)
committerChris Porter <redacted>
Mon, 24 Mar 2008 09:17:42 +0000 (09:17 +0000)
control/control.c
control/control.h
newsearch/Makefile.in
newsearch/newsearch.h
newsearch/newsearch_ast.c [new file with mode: 0644]
noperserv/noperserv_commands.c
parser/parser.c
parser/parser.h

index faf877f5a759fbb58a1705e3509edf17528e86ea..48c4a77f7ba76803bda00efe152c8e5a1cc37efb 100644 (file)
@@ -599,3 +599,14 @@ void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format
         controlnotice(np, "%s", broadcast);
 }
 
+void controlnswall(int noticelevel, char *format, ...) {
+  char broadcast[512];
+  va_list va;
+
+  va_start(va, format);
+  vsnprintf(broadcast, sizeof(broadcast), format, va);
+  va_end(va);
+
+  controlwall(NO_OPER, noticelevel, "%s", broadcast);
+}
+
index 65a414aa31401302a8b88362b381206612e35375..901cd53e78ff19f57cb4e2983686373b3d76f096 100644 (file)
@@ -16,6 +16,7 @@ int controlrmmod(void *sender, int cargc, char **cargv);
 void controlspecialrmmod(void *arg);
 void controlspecialreloadmod(void *arg);
 void controlhelp(nick *np, Command *cmd);
+void controlnswall(int noticelevel, char *format, ...);
 
 #define registercontrolcmd(a, b, c, d) registercontrolhelpcmd(a, b, c, d, NULL)
 
index d6f7f577ae1b780f72262fe0a9ef4ab86973a03f..43f3a49114e3a89ac5a96974ae0343b2591da92a 100644 (file)
@@ -6,4 +6,4 @@ LDFLAGS+=$(LIBPCRE)
 .PHONY: all
 all: newsearch.so
 
-newsearch.so: newsearch.o formats.o ns-not.o ns-and.o ns-or.o ns-eq.o ns-match.o ns-hostmask.o ns-realname.o ns-modes.o ns-nick.o ns-ident.o ns-regex.o ns-host.o ns-channel.o ns-lt.o ns-gt.o ns-timestamp.o ns-country.o ns-authname.o ns-ip.o ns-kill.o ns-gline.o ns-exists.o ns-services.o ns-size.o ns-name.o ns-topic.o ns-oppct.o ns-hostpct.o ns-authedpct.o ns-length.o ns-kick.o ns-authts.o ns-channels.o ns-server.o ns-authid.o ns-notice.o
+newsearch.so: newsearch.o formats.o ns-not.o ns-and.o ns-or.o ns-eq.o ns-match.o ns-hostmask.o ns-realname.o ns-modes.o ns-nick.o ns-ident.o ns-regex.o ns-host.o ns-channel.o ns-lt.o ns-gt.o ns-timestamp.o ns-country.o ns-authname.o ns-ip.o ns-kill.o ns-gline.o ns-exists.o ns-services.o ns-size.o ns-name.o ns-topic.o ns-oppct.o ns-hostpct.o ns-authedpct.o ns-length.o ns-kick.o ns-authts.o ns-channels.o ns-server.o ns-authid.o ns-notice.o newsearch_ast.o
index 58f7a59577c27bcc5849fcadfdcebcc6382ba8f1..ab568fe19cb29d3723135e79ec18fbc11978d437 100644 (file)
@@ -38,6 +38,7 @@ typedef struct searchCtx {
   searchParseFunc parser;
   replyFunc reply;
   wallFunc wall;
+  void *arg;
 } searchCtx;
 
 typedef struct searchNode *(*parseFunc)(searchCtx *, int, int, char **);
@@ -134,3 +135,56 @@ void chansearch_exe(struct searchNode *search, searchCtx *sctx, nick *sender, Ch
 
 int do_nicksearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv);
 int do_chansearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv);
+
+void *literal_exe(searchCtx *ctx, struct searchNode *thenode, void *theinput);
+void literal_free(searchCtx *ctx, struct searchNode *thenode);
+
+/* AST functions */
+
+struct searchASTNode;
+
+#define AST_NODE_CHILD 1
+#define AST_NODE_LITERAL 2
+
+/* items to store in the ast lookup cache */
+#define AST_RECENT 10
+
+typedef struct searchASTExpr {
+  int type;
+  union {
+    char *literal;
+    struct searchASTNode *child;
+  } u;
+  struct searchASTExpr *next;
+} searchASTExpr;
+
+typedef struct searchASTNode {
+  parseFunc fn;
+  int argc;
+  struct searchASTExpr **argv;
+} searchASTNode;
+
+/*
+ *
+ * FEAR THE COMPOUND LITERALS
+ * MUHAHAHHAHAHAHAHAHAAH
+ *
+ */
+#define __NSASTExpr(x, y, ...) &(searchASTExpr){.type = x, .u.y = __VA_ARGS__, .next = NULL}
+#define __NSASTList(...) (searchASTExpr *[]){__VA_ARGS__}
+#define __NSASTNode(x, ...) &(searchASTNode){.fn = x, .argc = sizeof(__NSASTList(__VA_ARGS__)) / sizeof(__NSASTList(__VA_ARGS__)[0]), .argv = __NSASTList(__VA_ARGS__)}
+#define __NSASTChild(...) __NSASTExpr(AST_NODE_CHILD, child, __VA_ARGS__)
+
+#define NSASTLiteral(data) __NSASTExpr(AST_NODE_LITERAL, literal, data)
+#define NSASTNode(fn, ...) __NSASTChild(__NSASTNode(fn, __VA_ARGS__))
+
+searchNode *search_astparse(searchCtx *, int, char *);
+
+int ast_nicksearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, NickDisplayFunc display, int limit);
+int ast_chansearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, ChanDisplayFunc display, int limit);
+
+char *ast_printtree(char *buf, size_t bufsize, searchASTExpr *expr);
+
+/* erk */
+extern CommandTree *searchTree;
+
diff --git a/newsearch/newsearch_ast.c b/newsearch/newsearch_ast.c
new file mode 100644 (file)
index 0000000..f79f019
--- /dev/null
@@ -0,0 +1,219 @@
+#include "newsearch.h"
+#include "../lib/sstring.h"
+#include "../lib/strlfunc.h"
+#include <stdarg.h>
+#include <string.h>
+
+/* at least we have some type safety... */
+typedef union exprunion {
+  searchASTExpr *expr;
+  char *literal;
+} exprunion;
+
+typedef struct searchASTCache {
+  searchASTExpr *tree;
+  searchASTExpr *cache[AST_RECENT];
+  int nextpos;
+} searchASTCache;
+
+/* comares either a string and a string or an expression and an expression */
+static searchASTExpr *compareloc(searchASTExpr *expr, exprunion *loc) {
+  if(expr->type == AST_NODE_LITERAL) {
+    if(expr->u.literal == loc->literal)
+      return expr;
+  } else if(expr->type == AST_NODE_CHILD) {
+    if(expr == loc->expr)
+      return expr;
+  } else {
+    parseError = "static_parse_compare: bad node type";
+    return NULL;
+  }
+  return NULL;
+}
+
+/* searches the abstract syntax tree for the supplied expression/string */
+static searchASTExpr *treesearch(searchASTExpr *expr, exprunion *loc) {
+  searchASTExpr *ret = compareloc(expr, loc);
+  if(ret)
+    return ret;
+  
+  if(expr->type == AST_NODE_CHILD) {
+    int i;
+    for(i=0;i<expr->u.child->argc;i++) {
+      searchASTExpr *d = treesearch(expr->u.child->argv[i], loc);
+      if(d)
+        return d;
+    }
+  }
+  return NULL;
+}
+
+/*
+ * searches the AST cache, if this fails it goes and searches the tree.
+ * the cache is hit most of the time and I guess makes it nearly O(1) amortised...
+ */
+searchASTExpr *cachesearch(searchASTCache *cache, exprunion *loc) {
+  searchASTExpr *ret = compareloc(cache->tree, loc);
+  int i;
+  if(ret)
+    return ret;
+
+  for(i=0;i<AST_RECENT;i++) {
+    if(!cache->cache[i])
+      continue;
+    ret = compareloc(cache->cache[i], loc);
+    if(ret)
+      return ret;
+  }
+
+  return treesearch(cache->tree, loc);
+}
+
+/* pushes an item into the cache */
+static void cachepush(searchASTCache *cache, searchASTExpr *expr) {
+  cache->cache[cache->nextpos] = expr;
+  cache->nextpos = (cache->nextpos + 1) % AST_RECENT;
+}
+
+/* ast parser, the way we pass context around is very very hacky... */
+searchNode *search_astparse(searchCtx *ctx, int type, char *loc) {
+  searchASTCache *cache = ctx->arg;
+  searchASTExpr *expr = cachesearch(cache, (exprunion *)&loc);
+  searchNode *node;
+  char **v;
+  int i;
+
+  if(!expr) {
+    parseError = "WARNING: AST parsing failed";
+    return NULL;
+  }
+
+  switch(expr->type) {
+    case AST_NODE_LITERAL:
+      if (!(node=(searchNode *)malloc(sizeof(searchNode)))) {
+        parseError = "malloc: could not allocate memory for this search.";
+        return NULL;
+      }
+      node->localdata  = getsstring(expr->u.literal,512);
+      node->returntype = RETURNTYPE_CONST | RETURNTYPE_STRING;
+      node->exe        = literal_exe;
+      node->free       = literal_free;
+      return node;
+    case AST_NODE_CHILD:
+      v = (char **)malloc(expr->u.child->argc * sizeof(char *));
+      if(!v) {
+        parseError = "malloc: could not allocate memory for this search.";
+        return NULL;
+      }
+      for(i=0;i<expr->u.child->argc;i++) {
+        searchASTExpr *child = expr->u.child->argv[i];
+
+        cachepush(cache, child);
+        switch(child->type) {
+          case AST_NODE_LITERAL:
+            v[i] = child->u.literal;
+            break;
+          case AST_NODE_CHILD:
+            v[i] = (char *)child;
+            break;
+          default:
+            parseError = "static_parse: bad child node type";
+            free(v);
+            return NULL;
+        }
+      }
+
+      node = expr->u.child->fn(ctx, type, expr->u.child->argc, v);
+      free(v);
+      return node;
+   default:
+      parseError = "static_parse: bad node type";
+      return NULL;
+  }
+}
+
+int ast_nicksearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, NickDisplayFunc display, int limit) {
+  searchCtx ctx;
+  searchASTCache cache;
+  searchNode *search;
+  char buf[1024];
+
+  memset(&cache, 0, sizeof(cache));
+  cache.tree = tree;
+
+  ctx.reply = reply;
+  ctx.wall = wall;
+  ctx.parser = search_astparse;
+  ctx.arg = (void *)&cache;
+
+  buf[0] = '\0';
+  reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree));
+  search = ctx.parser(&ctx, SEARCHTYPE_NICK, (char *)tree);
+  if(!search) {
+    reply(sender, "Parse error: %s", parseError);
+    return CMD_ERROR;
+  }
+
+  reply(sender, "Executing...");
+  nicksearch_exe(search, &ctx, sender, display, limit);
+
+  (search->free)(&ctx, search);
+
+  return CMD_OK;
+}
+
+int ast_chansearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, ChanDisplayFunc display, int limit) {
+  searchCtx ctx;
+  searchASTCache cache;
+  searchNode *search;
+  char buf[1024];
+
+  ctx.reply = reply;
+  ctx.wall = wall;
+  ctx.parser = search_astparse;
+  ctx.arg = (void *)&cache;
+
+  buf[0] = '\0';
+  reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree));
+  search = ctx.parser(&ctx, SEARCHTYPE_CHANNEL, (char *)tree);
+  if(!search) {
+    reply(sender, "Parse error: %s", parseError);
+    return CMD_ERROR;
+  }
+
+  reply(sender, "Executing...");
+  chansearch_exe(search, &ctx, sender, display, limit);
+
+  (search->free)(&ctx, search);
+
+  return CMD_OK;
+}
+
+/* horribly, horribly inefficient -- don't call me very often! */
+char *ast_printtree(char *buf, size_t bufsize, searchASTExpr *expr) {
+  char lbuf[256];
+  if(expr->type == AST_NODE_CHILD) {    
+    int i;
+    sstring *command = getcommandname(searchTree, (void *)expr->u.child->fn);
+    char *space = expr->u.child->argc>0?" ":"";
+
+    if(command) {
+      snprintf(lbuf, sizeof(lbuf), "(%s%s", command->content, space);
+    } else {
+      snprintf(lbuf, sizeof(lbuf), "(%p%s", expr->u.child->fn, space);
+    }
+    strlcat(buf, lbuf, bufsize);
+
+    for(i=0;i<expr->u.child->argc;i++)
+      ast_printtree(buf, bufsize, expr->u.child->argv[i]);
+
+    strlcat(buf, ")", bufsize);
+  } else if(expr->type == AST_NODE_LITERAL) {
+    snprintf(lbuf, sizeof(lbuf), " %s", expr->u.literal);
+    strlcat(buf, lbuf, bufsize);
+  } else {
+    strlcat(buf, " ??? ", bufsize);
+  }
+
+  return buf;
+}
index 023457417395927da212c57800dffe931c0705a5..9a94b64160798d117a5fce9b0dc8e8268cecc165 100644 (file)
@@ -7,6 +7,7 @@
 
 #include "../control/control.h"
 #include "../nick/nick.h"
+#include "../newsearch/newsearch.h"
 #include "../lib/irc_string.h"
 #include "../lib/strlfunc.h"
 #include "../localuser/localuserchannel.h"
@@ -34,7 +35,7 @@ void _init() {
 
   registercontrolhelpcmd("spewchan", NO_OPER, 1, &controlspewchan, "Usage: spewchan <pattern>\nShows all channels which are matched by the given pattern");
 
-/*  registercontrolhelpcmd("spew", NO_OPER, 1, &controlspew, "Usage: spewchan <pattern>\nShows all userss which are matched by the given pattern"); */
+  registercontrolhelpcmd("spew", NO_OPER, 1, &controlspew, "Usage: spewchan <pattern>\nShows all userss which are matched by the given pattern");
 
   registercontrolhelpcmd("resync", NO_OPER, 1, &controlresync, "Usage: resync <channel>\nResyncs a desynched channel");
 
@@ -52,7 +53,7 @@ void _fini() {
   deregistercontrolcmd("broadcast", controlbroadcast);
 
   deregistercontrolcmd("resync", controlresync);
-/*   deregistercontrolcmd("spew", controlspew); */
+  deregistercontrolcmd("spew", controlspew);
   deregistercontrolcmd("spewchan", controlspewchan);
 
 /*  deregistercontrolcmd("kill", controlkill); */
@@ -150,11 +151,16 @@ int controlresync(void *sender, int cargc, char **cargv) {
 }
 
 int controlspew(void *sender, int cargc, char **cargv) {
-/*  nick *np = (nick *)sender; */
+  searchASTExpr *tree;
 
-  return CMD_OK;
+  if(cargc < 1)
+    return CMD_USAGE;
+
+  tree = NSASTNode(match_parse, NSASTNode(hostmask_parse), NSASTLiteral(cargv[0]));
+  return ast_nicksearch(tree, controlreply, sender, controlnswall, printnick, 500);
 }
 
+/* this function is the definition of horrible */
 int controlspewchan(void *sender, int cargc, char **cargv) {
   nick *np = (nick*)sender;
   nick *np2;
index eadae3c626b658539aed85fd767d02565c1d8093..476d6a87574bd1ce4d8258dbe92217303230ee51 100644 (file)
@@ -347,3 +347,23 @@ int getcommandlist(CommandTree *ct, Command **commandlist, int maxcommands) {
   
   return count;
 }
+
+/* Returns the command name given a handler */
+sstring *getcommandname(CommandTree *ct, CommandHandler handler) {
+  int i;
+  sstring *s;
+
+  if(ct->cmd && ct->cmd->handler == handler) {
+    return ct->cmd->command;
+  }
+
+  for (i=0;i<26;i++) {
+    if(ct->next[i]) {
+      s=getcommandname(ct->next[i], handler);
+      if(s)
+        return s;
+    }
+  }
+
+  return NULL;
+}
index 1aa030548ea465acd18e788818e215f11a23af3d..e98e577dc088c2c3b263e2494cdb3abc70753b1f 100644 (file)
@@ -53,6 +53,7 @@ Command *addcommandhelptotree(CommandTree *ct, const char *cmdname, int level, i
 int deletecommandfromtree(CommandTree *ct, const char *cmdname, CommandHandler handler);
 Command *findcommandintree(CommandTree *ct, const char *cmdname, int strictcheck);
 int getcommandlist(CommandTree *ct, Command **commandlist, int maxcommands);
+sstring *getcommandname(CommandTree *ct, CommandHandler handler);
 
 #define addcommandtotree(a, b, c, d, e) addcommandhelptotree(a, b, c, d, e, NULL)