]> jfr.im git - irc/quakenet/newserv.git/blame - newsearch/newsearch_ast.c
Dynamic help for newsearch, and permissions on terms/output functions
[irc/quakenet/newserv.git] / newsearch / newsearch_ast.c
CommitLineData
0a659cde
CP
1#include "newsearch.h"
2#include "../lib/sstring.h"
3#include "../lib/strlfunc.h"
a3176c57 4#include "../lib/stringbuf.h"
0a659cde
CP
5#include <stdarg.h>
6#include <string.h>
7
8/* at least we have some type safety... */
9typedef union exprunion {
10 searchASTExpr *expr;
11 char *literal;
12} exprunion;
13
14typedef struct searchASTCache {
15 searchASTExpr *tree;
16 searchASTExpr *cache[AST_RECENT];
17 int nextpos;
18} searchASTCache;
19
20/* comares either a string and a string or an expression and an expression */
21static searchASTExpr *compareloc(searchASTExpr *expr, exprunion *loc) {
22 if(expr->type == AST_NODE_LITERAL) {
23 if(expr->u.literal == loc->literal)
24 return expr;
25 } else if(expr->type == AST_NODE_CHILD) {
26 if(expr == loc->expr)
27 return expr;
28 } else {
29 parseError = "static_parse_compare: bad node type";
30 return NULL;
31 }
32 return NULL;
33}
34
35/* searches the abstract syntax tree for the supplied expression/string */
36static searchASTExpr *treesearch(searchASTExpr *expr, exprunion *loc) {
37 searchASTExpr *ret = compareloc(expr, loc);
38 if(ret)
39 return ret;
40
41 if(expr->type == AST_NODE_CHILD) {
42 int i;
43 for(i=0;i<expr->u.child->argc;i++) {
44 searchASTExpr *d = treesearch(expr->u.child->argv[i], loc);
45 if(d)
46 return d;
47 }
48 }
49 return NULL;
50}
51
52/*
53 * searches the AST cache, if this fails it goes and searches the tree.
54 * the cache is hit most of the time and I guess makes it nearly O(1) amortised...
55 */
56searchASTExpr *cachesearch(searchASTCache *cache, exprunion *loc) {
57 searchASTExpr *ret = compareloc(cache->tree, loc);
58 int i;
59 if(ret)
60 return ret;
61
62 for(i=0;i<AST_RECENT;i++) {
63 if(!cache->cache[i])
64 continue;
65 ret = compareloc(cache->cache[i], loc);
66 if(ret)
67 return ret;
68 }
69
70 return treesearch(cache->tree, loc);
71}
72
73/* pushes an item into the cache */
74static void cachepush(searchASTCache *cache, searchASTExpr *expr) {
75 cache->cache[cache->nextpos] = expr;
76 cache->nextpos = (cache->nextpos + 1) % AST_RECENT;
77}
78
79/* ast parser, the way we pass context around is very very hacky... */
f33f3f52 80searchNode *search_astparse(searchCtx *ctx, char *loc) {
0a659cde
CP
81 searchASTCache *cache = ctx->arg;
82 searchASTExpr *expr = cachesearch(cache, (exprunion *)&loc);
83 searchNode *node;
84 char **v;
85 int i;
86
87 if(!expr) {
88 parseError = "WARNING: AST parsing failed";
89 return NULL;
90 }
91
92 switch(expr->type) {
93 case AST_NODE_LITERAL:
94 if (!(node=(searchNode *)malloc(sizeof(searchNode)))) {
95 parseError = "malloc: could not allocate memory for this search.";
96 return NULL;
97 }
98 node->localdata = getsstring(expr->u.literal,512);
99 node->returntype = RETURNTYPE_CONST | RETURNTYPE_STRING;
100 node->exe = literal_exe;
101 node->free = literal_free;
102 return node;
103 case AST_NODE_CHILD:
104 v = (char **)malloc(expr->u.child->argc * sizeof(char *));
105 if(!v) {
106 parseError = "malloc: could not allocate memory for this search.";
107 return NULL;
108 }
109 for(i=0;i<expr->u.child->argc;i++) {
110 searchASTExpr *child = expr->u.child->argv[i];
111
112 cachepush(cache, child);
113 switch(child->type) {
114 case AST_NODE_LITERAL:
115 v[i] = child->u.literal;
116 break;
117 case AST_NODE_CHILD:
118 v[i] = (char *)child;
119 break;
120 default:
121 parseError = "static_parse: bad child node type";
122 free(v);
123 return NULL;
124 }
125 }
126
f33f3f52 127 node = expr->u.child->fn(ctx, expr->u.child->argc, v);
0a659cde
CP
128 free(v);
129 return node;
130 default:
131 parseError = "static_parse: bad node type";
132 return NULL;
133 }
134}
135
2181a10f 136int ast_nicksearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, NickDisplayFunc display, HeaderFunc header, void *headerarg, int limit) {
0a659cde
CP
137 searchCtx ctx;
138 searchASTCache cache;
139 searchNode *search;
140 char buf[1024];
141
142 memset(&cache, 0, sizeof(cache));
143 cache.tree = tree;
144
e8ff7b61 145 newsearch_ctxinit(&ctx, search_astparse, reply, wall, &cache, reg_nicksearch, sender);
0a659cde
CP
146
147 buf[0] = '\0';
a92bb8e1 148 reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree, reg_nicksearch));
f33f3f52 149 search = ctx.parser(&ctx, (char *)tree);
0a659cde
CP
150 if(!search) {
151 reply(sender, "Parse error: %s", parseError);
152 return CMD_ERROR;
153 }
154
155 reply(sender, "Executing...");
2181a10f
CP
156 if(header)
157 header(sender, headerarg);
0a659cde
CP
158 nicksearch_exe(search, &ctx, sender, display, limit);
159
160 (search->free)(&ctx, search);
161
162 return CMD_OK;
163}
164
2181a10f 165int ast_chansearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, ChanDisplayFunc display, HeaderFunc header, void *headerarg, int limit) {
0a659cde
CP
166 searchCtx ctx;
167 searchASTCache cache;
168 searchNode *search;
169 char buf[1024];
170
e8ff7b61 171 newsearch_ctxinit(&ctx, search_astparse, reply, wall, &cache, reg_chansearch, sender);
0a659cde
CP
172
173 buf[0] = '\0';
a92bb8e1 174 reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree, reg_chansearch));
f33f3f52 175 search = ctx.parser(&ctx, (char *)tree);
0a659cde
CP
176 if(!search) {
177 reply(sender, "Parse error: %s", parseError);
178 return CMD_ERROR;
179 }
180
181 reply(sender, "Executing...");
2181a10f
CP
182 if(header)
183 header(sender, headerarg);
0a659cde
CP
184 chansearch_exe(search, &ctx, sender, display, limit);
185
186 (search->free)(&ctx, search);
187
188 return CMD_OK;
189}
190
2181a10f 191int ast_usersearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, UserDisplayFunc display, HeaderFunc header, void *headerarg, int limit) {
cef8cb48
CP
192 searchCtx ctx;
193 searchASTCache cache;
194 searchNode *search;
195 char buf[1024];
196
197 memset(&cache, 0, sizeof(cache));
198 cache.tree = tree;
199
e8ff7b61 200 newsearch_ctxinit(&ctx, search_astparse, reply, wall, &cache, reg_usersearch, sender);
cef8cb48
CP
201
202 buf[0] = '\0';
a92bb8e1 203 reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree, reg_usersearch));
f33f3f52 204 search = ctx.parser(&ctx, (char *)tree);
cef8cb48
CP
205 if(!search) {
206 reply(sender, "Parse error: %s", parseError);
207 return CMD_ERROR;
208 }
209
210 reply(sender, "Executing...");
2181a10f
CP
211 if(header)
212 header(sender, headerarg);
cef8cb48
CP
213 usersearch_exe(search, &ctx, sender, display, limit);
214
215 (search->free)(&ctx, search);
216
217 return CMD_OK;
218}
219
a3176c57
CP
220
221/* horribly inefficient -- don't call me very often! */
a92bb8e1 222static char *ast_printtree_real(StringBuf *buf, searchASTExpr *expr, searchCmd *cmd) {
0a659cde
CP
223 char lbuf[256];
224 if(expr->type == AST_NODE_CHILD) {
225 int i;
a92bb8e1 226 sstring *command = getcommandname(cmd->searchtree, (void *)expr->u.child->fn);
0a659cde
CP
227
228 if(command) {
a3176c57 229 snprintf(lbuf, sizeof(lbuf), "(%s", command->content);
0a659cde 230 } else {
a3176c57 231 snprintf(lbuf, sizeof(lbuf), "(%p", expr->u.child->fn);
0a659cde 232 }
a3176c57 233 sbaddstr(buf, lbuf);
0a659cde 234
a3176c57
CP
235 for(i=0;i<expr->u.child->argc;i++) {
236 sbaddchar(buf, ' ');
a92bb8e1 237 ast_printtree_real(buf, expr->u.child->argv[i], cmd);
a3176c57
CP
238 }
239 sbaddchar(buf, ')');
0a659cde 240
0a659cde 241 } else if(expr->type == AST_NODE_LITERAL) {
a3176c57 242 sbaddstr(buf, expr->u.literal);
0a659cde 243 } else {
a3176c57 244 sbaddstr(buf, "???");
0a659cde
CP
245 }
246
a3176c57
CP
247 return buf->buf;
248}
249
a92bb8e1 250char *ast_printtree(char *buf, size_t bufsize, searchASTExpr *expr, searchCmd *cmd) {
a3176c57
CP
251 StringBuf b;
252 char *p;
253
254 b.capacity = bufsize;
255 b.len = 0;
256 b.buf = buf;
257
a92bb8e1 258 p = ast_printtree_real(&b, expr, cmd);
a3176c57
CP
259
260 sbterminate(&b);
261 return p;
0a659cde 262}