]> jfr.im git - irc/quakenet/newserv.git/blob - newsearch/newsearch_ast.c
fix bug in G stats
[irc/quakenet/newserv.git] / newsearch / newsearch_ast.c
1 #include "newsearch.h"
2 #include "../lib/sstring.h"
3 #include "../lib/strlfunc.h"
4 #include "../lib/stringbuf.h"
5 #include <stdarg.h>
6 #include <string.h>
7
8 /* at least we have some type safety... */
9 typedef union exprunion {
10 searchASTExpr *expr;
11 char *literal;
12 } exprunion;
13
14 typedef 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 */
21 static 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 */
36 static 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 */
56 searchASTExpr *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 */
74 static 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... */
80 searchNode *search_astparse(searchCtx *ctx, char *loc) {
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
127 node = expr->u.child.fn(ctx, expr->u.child.argc, v);
128 free(v);
129 return node;
130 default:
131 parseError = "static_parse: bad node type";
132 return NULL;
133 }
134 }
135
136 int ast_nicksearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, NickDisplayFunc display, HeaderFunc header, void *headerarg, int limit) {
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
145 newsearch_ctxinit(&ctx, search_astparse, reply, wall, &cache, reg_nicksearch, sender, display, limit);
146
147 buf[0] = '\0';
148 reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree, reg_nicksearch));
149 search = ctx.parser(&ctx, (char *)tree);
150 if(!search) {
151 reply(sender, "Parse error: %s", parseError);
152 return CMD_ERROR;
153 }
154
155 reply(sender, "Executing...");
156 if(header)
157 header(sender, headerarg);
158 nicksearch_exe(search, &ctx);
159
160 (search->free)(&ctx, search);
161
162 return CMD_OK;
163 }
164
165 int ast_chansearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, ChanDisplayFunc display, HeaderFunc header, void *headerarg, int limit) {
166 searchCtx ctx;
167 searchASTCache cache;
168 searchNode *search;
169 char buf[1024];
170
171 newsearch_ctxinit(&ctx, search_astparse, reply, wall, &cache, reg_chansearch, sender, display, limit);
172
173 memset(&cache, 0, sizeof(cache));
174 cache.tree = tree;
175
176 buf[0] = '\0';
177 reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree, reg_chansearch));
178 search = ctx.parser(&ctx, (char *)tree);
179 if(!search) {
180 reply(sender, "Parse error: %s", parseError);
181 return CMD_ERROR;
182 }
183
184 reply(sender, "Executing...");
185 if(header)
186 header(sender, headerarg);
187 chansearch_exe(search, &ctx);
188
189 (search->free)(&ctx, search);
190
191 return CMD_OK;
192 }
193
194 int ast_usersearch(searchASTExpr *tree, replyFunc reply, void *sender, wallFunc wall, UserDisplayFunc display, HeaderFunc header, void *headerarg, int limit) {
195 searchCtx ctx;
196 searchASTCache cache;
197 searchNode *search;
198 char buf[1024];
199
200 memset(&cache, 0, sizeof(cache));
201 cache.tree = tree;
202
203 newsearch_ctxinit(&ctx, search_astparse, reply, wall, &cache, reg_usersearch, sender, display, limit);
204
205 buf[0] = '\0';
206 reply(sender, "Parsing: %s", ast_printtree(buf, sizeof(buf), tree, reg_usersearch));
207 search = ctx.parser(&ctx, (char *)tree);
208 if(!search) {
209 reply(sender, "Parse error: %s", parseError);
210 return CMD_ERROR;
211 }
212
213 reply(sender, "Executing...");
214 if(header)
215 header(sender, headerarg);
216 usersearch_exe(search, &ctx);
217
218 (search->free)(&ctx, search);
219
220 return CMD_OK;
221 }
222
223
224 /* horribly inefficient -- don't call me very often! */
225 static char *ast_printtree_real(StringBuf *buf, searchASTExpr *expr, searchCmd *cmd) {
226 char lbuf[256];
227 if(expr->type == AST_NODE_CHILD) {
228 int i;
229 sstring *command = getcommandname(cmd->searchtree, (void *)expr->u.child.fn);
230
231 if(command) {
232 snprintf(lbuf, sizeof(lbuf), "(%s", command->content);
233 } else {
234 snprintf(lbuf, sizeof(lbuf), "(%p", expr->u.child.fn);
235 }
236 sbaddstr(buf, lbuf);
237
238 for(i=0;i<expr->u.child.argc;i++) {
239 sbaddchar(buf, ' ');
240 ast_printtree_real(buf, &expr->u.child.argv[i], cmd);
241 }
242 sbaddchar(buf, ')');
243
244 } else if(expr->type == AST_NODE_LITERAL) {
245 char *p;
246
247 sbaddchar(buf, '"');
248
249 for(p=expr->u.literal;*p;p++) {
250 if(*p == '\\' || *p == '"')
251 sbaddchar(buf, '\\');
252 sbaddchar(buf, *p);
253 }
254
255 sbaddchar(buf, '"');
256 } else {
257 sbaddstr(buf, "???");
258 }
259
260 return buf->buf;
261 }
262
263 char *ast_printtree(char *buf, size_t bufsize, searchASTExpr *expr, searchCmd *cmd) {
264 StringBuf b;
265 char *p;
266
267 b.capacity = bufsize;
268 b.len = 0;
269 b.buf = buf;
270
271 p = ast_printtree_real(&b, expr, cmd);
272
273 sbterminate(&b);
274 return p;
275 }