]> jfr.im git - irc/quakenet/newserv.git/blame - newsearch/newsearch.c
merge
[irc/quakenet/newserv.git] / newsearch / newsearch.c
CommitLineData
c86edd1d 1#include <stdio.h>
0da2a4ae 2#include <stdarg.h>
b697c21c 3#include <string.h>
c86edd1d
Q
4#include "newsearch.h"
5
6#include "../irc/irc_config.h"
c86edd1d
Q
7#include "../lib/irc_string.h"
8#include "../parser/parser.h"
9#include "../control/control.h"
10#include "../lib/splitline.h"
87698d77 11#include "../lib/version.h"
ba8a65c4 12#include "../lib/stringbuf.h"
b697c21c 13#include "../lib/strlfunc.h"
87698d77 14
70b0a4e5 15MODULE_VERSION("");
c86edd1d
Q
16
17CommandTree *searchTree;
73cfc302 18CommandTree *chanOutputTree;
19CommandTree *nickOutputTree;
e8ad630b 20CommandTree *userOutputTree;
c86edd1d
Q
21
22int do_nicksearch(void *source, int cargc, char **cargv);
56a84a81 23int do_chansearch(void *source, int cargc, char **cargv);
e8ad630b 24int do_usersearch(void *source, int cargc, char **cargv);
d00af236 25
3d2bf13b
CP
26void printnick_channels(searchCtx *, nick *, nick *);
27void printchannel(searchCtx *, nick *, chanindex *);
28void printchannel_topic(searchCtx *, nick *, chanindex *);
29void printchannel_services(searchCtx *, nick *, chanindex *);
c86edd1d 30
55dd7529
CP
31UserDisplayFunc defaultuserfn = printuser;
32NickDisplayFunc defaultnickfn = printnick;
33ChanDisplayFunc defaultchanfn = printchannel;
34
c86edd1d
Q
35void registersearchterm(char *term, parseFunc parsefunc);
36void deregistersearchterm(char *term, parseFunc parsefunc);
37
73cfc302 38void regchandisp(const char *name, ChanDisplayFunc handler) {
39 addcommandtotree(chanOutputTree, name, 0, 0, (CommandHandler)handler);
40}
41
42void unregchandisp(const char *name, ChanDisplayFunc handler) {
43 deletecommandfromtree(chanOutputTree, name, (CommandHandler)handler);
44}
45
46void regnickdisp(const char *name, NickDisplayFunc handler) {
47 addcommandtotree(nickOutputTree, name, 0, 0, (CommandHandler)handler);
48}
49
50void unregnickdisp(const char *name, NickDisplayFunc handler) {
51 deletecommandfromtree(nickOutputTree, name, (CommandHandler)handler);
52}
53
e8ad630b
CP
54void reguserdisp(const char *name, UserDisplayFunc handler) {
55 addcommandtotree(userOutputTree, name, 0, 0, (CommandHandler)handler);
56}
57
58void unreguserdisp(const char *name, UserDisplayFunc handler) {
59 deletecommandfromtree(userOutputTree, name, (CommandHandler)handler);
60}
61
c86edd1d 62const char *parseError;
16cc6e32
IB
63/* used for *_free functions that need to warn users of certain things
64 i.e. hitting too many users in a (kill) or (gline) */
219d27f1 65nick *senderNSExtern;
c86edd1d
Q
66
67void _init() {
68 searchTree=newcommandtree();
73cfc302 69 chanOutputTree=newcommandtree();
70 nickOutputTree=newcommandtree();
e8ad630b 71 userOutputTree=newcommandtree();
c86edd1d
Q
72
73 /* Boolean operations */
74 registersearchterm("and",and_parse);
75 registersearchterm("not",not_parse);
76 registersearchterm("or",or_parse);
f1903ace 77
c86edd1d 78 registersearchterm("eq",eq_parse);
f1903ace
CP
79
80 registersearchterm("lt",lt_parse);
81 registersearchterm("gt",gt_parse);
c86edd1d
Q
82
83 /* String operations */
84 registersearchterm("match",match_parse);
85 registersearchterm("regex",regex_parse);
c7f7a584 86 registersearchterm("length",length_parse);
87
c86edd1d
Q
88 /* Nickname operations */
89 registersearchterm("hostmask",hostmask_parse);
90 registersearchterm("realname",realname_parse);
052247fa 91 registersearchterm("authname",authname_parse);
0ed3408e 92 registersearchterm("authts",authts_parse);
c86edd1d
Q
93 registersearchterm("ident",ident_parse);
94 registersearchterm("host",host_parse);
95 registersearchterm("channel",channel_parse);
f1903ace 96 registersearchterm("timestamp",timestamp_parse);
c433958f 97 registersearchterm("country",country_parse);
29161bbe 98 registersearchterm("ip",ip_parse);
afa6ced3 99 registersearchterm("channels",channels_parse);
6c131217 100 registersearchterm("server",server_parse);
ec6602e5 101 registersearchterm("authid",authid_parse);
c86edd1d 102
56a84a81 103 /* Channel operations */
f519b0a2 104 registersearchterm("exists",exists_parse);
71ad10f8 105 registersearchterm("services",services_parse);
24f0affb 106 registersearchterm("size",size_parse);
e64f5cd7 107 registersearchterm("name",name_parse);
88cd4141 108 registersearchterm("topic",topic_parse);
f90de116 109 registersearchterm("oppct",oppct_parse);
9cbe50dd 110 registersearchterm("uniquehostpct",hostpct_parse);
9d867b50 111 registersearchterm("authedpct",authedpct_parse);
4e65afe3 112 registersearchterm("kick",kick_parse);
56a84a81 113
c86edd1d
Q
114 /* Nickname / channel operations */
115 registersearchterm("modes",modes_parse);
56a84a81 116 registersearchterm("nick",nick_parse);
c86edd1d 117
16cc6e32
IB
118 /* Kill / gline parameters */
119 registersearchterm("kill",kill_parse);
120 registersearchterm("gline",gline_parse);
4622c762 121
b697c21c
CP
122 /* Iteration functionality */
123 registersearchterm("any",any_parse);
124 registersearchterm("all",all_parse);
125 registersearchterm("var",var_parse);
126
127 /* Iterable functions */
128 registersearchterm("channeliter",channeliter_parse);
129
4622c762
IB
130 /* Notice functionality */
131 registersearchterm("notice",notice_parse);
73cfc302 132
133 /* Nick output filters */
134 regnickdisp("default",printnick);
d00af236 135 regnickdisp("channels",printnick_channels);
136
73cfc302 137 /* Channel output filters */
138 regchandisp("default",printchannel);
139 regchandisp("topic",printchannel_topic);
140 regchandisp("services",printchannel_services);
16cc6e32 141
e8ad630b
CP
142 /* Nick output filters */
143 reguserdisp("default",printuser);
144
38cee035 145 registercontrolhelpcmd("nicksearch",NO_OPER,4,do_nicksearch, "Usage: nicksearch <criteria>\nSearches for nicknames with the given criteria.");
56a84a81 146 registercontrolhelpcmd("chansearch",NO_OPER,4,do_chansearch, "Usage: chansearch <criteria>\nSearches for channels with the given criteria.");
e8ad630b 147 registercontrolhelpcmd("usersearch",NO_OPER,4,do_usersearch, "Usage: usersearch <criteria>\nSearches for users with the given criteria.");
c86edd1d
Q
148}
149
150void _fini() {
151 destroycommandtree(searchTree);
73cfc302 152 destroycommandtree(chanOutputTree);
153 destroycommandtree(nickOutputTree);
e8ad630b 154 destroycommandtree(userOutputTree);
c86edd1d 155 deregistercontrolcmd("nicksearch", do_nicksearch);
56a84a81 156 deregistercontrolcmd("chansearch", do_chansearch);
e8ad630b 157 deregistercontrolcmd("usersearch", do_usersearch);
c86edd1d
Q
158}
159
160void registersearchterm(char *term, parseFunc parsefunc) {
161 addcommandtotree(searchTree, term, 0, 0, (CommandHandler) parsefunc);
162}
163
164void deregistersearchterm(char *term, parseFunc parsefunc) {
165 deletecommandfromtree(searchTree, term, (CommandHandler) parsefunc);
166}
167
0da2a4ae
CP
168static void controlwallwrapper(int level, char *format, ...) {
169 char buf[1024];
170 va_list ap;
171
172 va_start(ap, format);
173 vsnprintf(buf, sizeof(buf), format, ap);
174 controlwall(NO_OPER, level, "%s", buf);
175 va_end(ap);
176}
177
e8ad630b 178static int parseopts(int cargc, char **cargv, int *arg, int *limit, void **display, CommandTree *tree, replyFunc reply, void *sender) {
c86edd1d 179 char *ch;
73cfc302 180 struct Command *cmd;
b697c21c 181
c86edd1d
Q
182 if (*cargv[0] == '-') {
183 /* options */
e8ad630b 184 (*arg)++;
c86edd1d
Q
185
186 for (ch=cargv[0]+1;*ch;ch++) {
187 switch(*ch) {
188 case 'l':
e8ad630b 189 if (cargc<*arg) {
0da2a4ae 190 reply(sender,"Error: -l switch requires an argument");
38cee035 191 return CMD_USAGE;
c86edd1d 192 }
e8ad630b 193 *limit=strtoul(cargv[(*arg)++],NULL,10);
c86edd1d
Q
194 break;
195
73cfc302 196 case 'd':
e8ad630b 197 if (cargc<*arg) {
0da2a4ae 198 reply(sender,"Error: -d switch requires an argument");
73cfc302 199 return CMD_USAGE;
200 }
e8ad630b 201 cmd=findcommandintree(tree, cargv[*arg], 1);
73cfc302 202 if (!cmd) {
e8ad630b 203 reply(sender,"Error: unknown output format %s",cargv[*arg]);
73cfc302 204 return CMD_USAGE;
205 }
e8ad630b
CP
206 *display=(void *)cmd->handler;
207 (*arg)++;
73cfc302 208 break;
209
c86edd1d 210 default:
0da2a4ae 211 reply(sender,"Unrecognised flag -%c.",*ch);
c86edd1d
Q
212 }
213 }
214 }
215
e8ad630b
CP
216 return CMD_OK;
217}
218
f33f3f52 219void newsearch_ctxinit(searchCtx *ctx, searchParseFunc searchfn, replyFunc replyfn, wallFunc wallfn, void *arg, int type) {
b697c21c
CP
220 memset(ctx, 0, sizeof(searchCtx));
221
222 ctx->reply = replyfn;
223 ctx->wall = wallfn;
224 ctx->parser = searchfn;
225 ctx->arg = arg;
f33f3f52 226 ctx->type = type;
b697c21c
CP
227}
228
e8ad630b 229int do_nicksearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv) {
b697c21c 230 nick *sender = source;
e8ad630b
CP
231 struct searchNode *search;
232 int limit=500;
233 int arg=0;
55dd7529 234 NickDisplayFunc display=defaultnickfn;
e8ad630b
CP
235 searchCtx ctx;
236 int ret;
237
238 if (cargc<1)
239 return CMD_USAGE;
240
241 ret = parseopts(cargc, cargv, &arg, &limit, (void **)&display, nickOutputTree, reply, sender);
242 if(ret != CMD_OK)
243 return ret;
244
c86edd1d 245 if (arg>=cargc) {
4156103f 246 reply(sender,"No search terms - aborting.");
c86edd1d
Q
247 return CMD_ERROR;
248 }
249
250 if (arg<(cargc-1)) {
251 rejoinline(cargv[arg],cargc-arg);
252 }
c8be5183 253
f33f3f52 254 newsearch_ctxinit(&ctx, search_parse, reply, wall, NULL, SEARCHTYPE_NICK);
c8be5183 255
f33f3f52 256 if (!(search = ctx.parser(&ctx, cargv[arg]))) {
0da2a4ae 257 reply(sender,"Parse error: %s",parseError);
c86edd1d
Q
258 return CMD_ERROR;
259 }
c2c414be 260
c8be5183
CP
261 nicksearch_exe(search, &ctx, sender, display, limit);
262
263 (search->free)(&ctx, search);
c2c414be
CP
264
265 return CMD_OK;
266}
267
0da2a4ae
CP
268int do_nicksearch(void *source, int cargc, char **cargv) {
269 return do_nicksearch_real(controlreply, controlwallwrapper, source, cargc, cargv);
270}
271
c8be5183 272void nicksearch_exe(struct searchNode *search, searchCtx *ctx, nick *sender, NickDisplayFunc display, int limit) {
c2c414be
CP
273 int i, j;
274 int matches = 0;
275 unsigned int cmarker;
276 unsigned int tchans=0,uchans=0;
277 struct channel **cs;
278 nick *np;
b697c21c
CP
279 senderNSExtern = sender;
280
7c02e111 281 /* Get a marker value to mark "seen" channels for unique count */
282 cmarker=nextchanmarker();
283
c7f7a584 284 /* The top-level node needs to return a BOOL */
c8be5183 285 search=coerceNode(ctx, search, RETURNTYPE_BOOL);
c7f7a584 286
c86edd1d
Q
287 for (i=0;i<NICKHASHSIZE;i++) {
288 for (np=nicktable[i];np;np=np->next) {
c8be5183 289 if ((search->exe)(ctx, search, np)) {
7c02e111 290 /* Add total channels */
291 tchans += np->channels->cursi;
292
293 /* Check channels for uniqueness */
294 cs=(channel **)np->channels->content;
295 for (j=0;j<np->channels->cursi;j++) {
296 if (cs[j]->index->marker != cmarker) {
297 cs[j]->index->marker=cmarker;
298 uchans++;
299 }
300 }
301
c86edd1d 302 if (matches<limit)
3d2bf13b 303 display(ctx, sender, np);
7c02e111 304
c86edd1d 305 if (matches==limit)
c8be5183 306 ctx->reply(sender, "--- More than %d matches, skipping the rest",limit);
c86edd1d
Q
307 matches++;
308 }
309 }
310 }
311
c8be5183 312 ctx->reply(sender,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
7c02e111 313 matches, tchans, uchans, (float)tchans/uchans);
c86edd1d
Q
314}
315
0da2a4ae 316int do_chansearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv) {
b697c21c 317 nick *sender = source;
56a84a81 318 struct searchNode *search;
c2c414be 319 int limit=500;
56a84a81 320 int arg=0;
55dd7529 321 ChanDisplayFunc display=defaultchanfn;
c8be5183 322 searchCtx ctx;
e8ad630b 323 int ret;
56a84a81
IB
324
325 if (cargc<1)
326 return CMD_USAGE;
327
e8ad630b
CP
328 ret = parseopts(cargc, cargv, &arg, &limit, (void **)&display, chanOutputTree, reply, sender);
329 if(ret != CMD_OK)
330 return ret;
56a84a81
IB
331
332 if (arg>=cargc) {
4156103f 333 reply(sender,"No search terms - aborting.");
56a84a81
IB
334 return CMD_ERROR;
335 }
336
337 if (arg<(cargc-1)) {
338 rejoinline(cargv[arg],cargc-arg);
339 }
c2c414be 340
f33f3f52
P
341 newsearch_ctxinit(&ctx, search_parse, reply, wall, NULL, SEARCHTYPE_CHANNEL);
342 if (!(search = ctx.parser(&ctx, cargv[arg]))) {
0da2a4ae 343 reply(sender,"Parse error: %s",parseError);
56a84a81
IB
344 return CMD_ERROR;
345 }
c7f7a584 346
c8be5183 347 chansearch_exe(search, &ctx, sender, display, limit);
c2c414be 348
c8be5183 349 (search->free)(&ctx, search);
c2c414be
CP
350
351 return CMD_OK;
352}
353
0da2a4ae
CP
354int do_chansearch(void *source, int cargc, char **cargv) {
355 return do_chansearch_real(controlreply, controlwallwrapper, source, cargc, cargv);
356}
357
c8be5183 358void chansearch_exe(struct searchNode *search, searchCtx *ctx, nick *sender, ChanDisplayFunc display, int limit) {
c2c414be
CP
359 int i;
360 chanindex *cip;
361 int matches = 0;
b697c21c 362 senderNSExtern = sender;
c2c414be 363
c8be5183 364 search=coerceNode(ctx, search, RETURNTYPE_BOOL);
56a84a81
IB
365
366 for (i=0;i<CHANNELHASHSIZE;i++) {
367 for (cip=chantable[i];cip;cip=cip->next) {
c8be5183 368 if ((search->exe)(ctx, search, cip)) {
56a84a81 369 if (matches<limit)
3d2bf13b 370 display(ctx, sender, cip);
56a84a81 371 if (matches==limit)
c8be5183 372 ctx->reply(sender, "--- More than %d matches, skipping the rest",limit);
56a84a81
IB
373 matches++;
374 }
375 }
376 }
377
c8be5183 378 ctx->reply(sender,"--- End of list: %d matches", matches);
56a84a81
IB
379}
380
e8ad630b 381int do_usersearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv) {
b697c21c 382 nick *sender = source;
e8ad630b
CP
383 struct searchNode *search;
384 int limit=500;
385 int arg=0;
55dd7529 386 UserDisplayFunc display=defaultuserfn;
e8ad630b
CP
387 searchCtx ctx;
388 int ret;
389
390 if (cargc<1)
391 return CMD_USAGE;
392
393 ret = parseopts(cargc, cargv, &arg, &limit, (void **)&display, userOutputTree, reply, sender);
394 if(ret != CMD_OK)
395 return ret;
396
397 if (arg>=cargc) {
4156103f 398 reply(sender,"No search terms - aborting.");
e8ad630b
CP
399 return CMD_ERROR;
400 }
401
402 if (arg<(cargc-1)) {
403 rejoinline(cargv[arg],cargc-arg);
404 }
405
f33f3f52
P
406 newsearch_ctxinit(&ctx, search_parse, reply, wall, NULL, SEARCHTYPE_USER);
407 if (!(search = ctx.parser(&ctx, cargv[arg]))) {
e8ad630b
CP
408 reply(sender,"Parse error: %s",parseError);
409 return CMD_ERROR;
410 }
411
412 usersearch_exe(search, &ctx, sender, display, limit);
413
414 (search->free)(&ctx, search);
415
416 return CMD_OK;
417}
418
419int do_usersearch(void *source, int cargc, char **cargv) {
420 return do_usersearch_real(controlreply, controlwallwrapper, source, cargc, cargv);
421}
422
423void usersearch_exe(struct searchNode *search, searchCtx *ctx, nick *sender, UserDisplayFunc display, int limit) {
424 int i;
425 authname *aup;
426 int matches = 0;
b697c21c 427 senderNSExtern = sender;
e8ad630b
CP
428
429 search=coerceNode(ctx, search, RETURNTYPE_BOOL);
430
431 for (i=0;i<AUTHNAMEHASHSIZE;i++) {
432 for (aup=authnametable[i];aup;aup=aup->next) {
433 if ((search->exe)(ctx, search, aup)) {
434 if (matches<limit)
435 display(ctx, sender, aup);
436 if (matches==limit)
437 ctx->reply(sender, "--- More than %d matches, skipping the rest",limit);
438 matches++;
439 }
440 }
441 }
442
443 ctx->reply(sender,"--- End of list: %d matches", matches);
444}
445
c7f7a584 446/* Free a coerce node */
c8be5183 447void free_coerce(searchCtx *ctx, struct searchNode *thenode) {
c7f7a584 448 struct coercedata *cd=thenode->localdata;
449
c8be5183 450 cd->child->free(ctx, cd->child);
c7f7a584 451 free(thenode->localdata);
452 free(thenode);
453}
c86edd1d 454
c7f7a584 455/* Free a coerce node with a stringbuf allocated */
c8be5183 456void free_coercestring(searchCtx *ctx, struct searchNode *thenode) {
c7f7a584 457 free(((struct coercedata *)thenode->localdata)->u.stringbuf);
c8be5183 458 free_coerce(ctx, thenode);
c7f7a584 459}
460
461/* exe_tostr_null: return the constant string */
c8be5183 462void *exe_tostr_null(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 463 struct coercedata *cd=thenode->localdata;
464
465 return cd->u.stringbuf;
466}
467
468/* exe_val_null: return the constant value */
c8be5183 469void *exe_val_null(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 470 struct coercedata *cd=thenode->localdata;
471
472 return (void *)cd->u.val;
473}
474
475/* Lots of very dull type conversion functions */
c8be5183 476void *exe_inttostr(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 477 struct coercedata *cd=thenode->localdata;
478
c8be5183 479 sprintf(cd->u.stringbuf, "%lu", (unsigned long)(cd->child->exe)(ctx, cd->child, theinput));
c7f7a584 480
481 return cd->u.stringbuf;
482}
483
c8be5183 484void *exe_booltostr(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 485 struct coercedata *cd=thenode->localdata;
486
c8be5183 487 if ((cd->child->exe)(ctx, cd->child, theinput)) {
c7f7a584 488 sprintf(cd->u.stringbuf,"1");
489 } else {
490 cd->u.stringbuf[0]='\0';
491 }
492
493 return cd->u.stringbuf;
494}
c86edd1d 495
c8be5183 496void *exe_strtoint(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 497 struct coercedata *cd=thenode->localdata;
498
c8be5183 499 return (void *)strtoul((cd->child->exe)(ctx,cd->child,theinput),NULL,10);
c7f7a584 500}
501
c8be5183 502void *exe_booltoint(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 503 struct coercedata *cd=thenode->localdata;
504
505 /* Don't need to do anything */
c8be5183 506 return (cd->child->exe)(ctx, cd->child, theinput);
c7f7a584 507}
508
c8be5183 509void *exe_strtobool(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 510 struct coercedata *cd=thenode->localdata;
c8be5183 511 char *ch=(cd->child->exe)(ctx, cd->child, theinput);
c7f7a584 512
513 if (!ch || *ch=='\0' || (*ch=='0' && ch[1]=='\0')) {
514 return (void *)0;
515 } else {
516 return (void *)1;
517 }
518}
519
c8be5183 520void *exe_inttobool(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 521 struct coercedata *cd=thenode->localdata;
522
c8be5183 523 if ((cd->child->exe)(ctx, cd->child, theinput)) {
c7f7a584 524 return (void *)1;
525 } else {
526 return (void *)0;
527 }
528}
529
c8be5183 530struct searchNode *coerceNode(searchCtx *ctx, struct searchNode *thenode, int type) {
c7f7a584 531 struct searchNode *anode;
532 struct coercedata *cd;
533
534 /* You can't coerce a NULL */
535 if (!thenode)
536 return NULL;
537
538 /* No effort required to coerce to the same type */
539 if (type==(thenode->returntype & RETURNTYPE_TYPE))
540 return thenode;
541
542 anode=(struct searchNode *)malloc(sizeof(struct searchNode));
543 anode->localdata=cd=(struct coercedata *)malloc(sizeof(struct coercedata));
544 cd->child=thenode;
545 anode->returntype=type; /* We'll return what they want, always */
546 anode->free=free_coerce;
547
548 switch(type) {
549 case RETURNTYPE_STRING:
550 /* For a string we'll need a buffer */
551 /* A 64-bit number prints out to 20 digits, this leaves some slack */
552 cd->u.stringbuf=malloc(25);
553 anode->free=free_coercestring;
554
555 switch(thenode->returntype & RETURNTYPE_TYPE) {
556 default:
557 case RETURNTYPE_INT:
558 if (thenode->returntype & RETURNTYPE_CONST) {
559 /* Constant node: sort it out now */
c8be5183 560 sprintf(cd->u.stringbuf, "%lu", (unsigned long)thenode->exe(ctx, thenode, NULL));
c7f7a584 561 anode->exe=exe_tostr_null;
562 anode->returntype |= RETURNTYPE_CONST;
563 } else {
564 /* Variable data */
565 anode->exe=exe_inttostr;
566 }
567 break;
568
569 case RETURNTYPE_BOOL:
570 if (thenode->returntype & RETURNTYPE_CONST) {
571 /* Constant bool value */
c8be5183 572 if (thenode->exe(ctx, thenode,NULL)) {
c7f7a584 573 /* True! */
574 sprintf(cd->u.stringbuf, "1");
575 } else {
576 cd->u.stringbuf[0] = '\0';
577 }
578 anode->exe=exe_tostr_null;
579 anode->returntype |= RETURNTYPE_CONST;
580 } else {
581 /* Variable bool value */
582 anode->exe=exe_booltostr;
583 }
584 break;
585 }
586 break;
587
588 case RETURNTYPE_INT:
589 /* we want an int */
590 switch (thenode->returntype & RETURNTYPE_TYPE) {
591 case RETURNTYPE_STRING:
592 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 593 cd->u.val=strtoul((thenode->exe)(ctx, thenode, NULL), NULL, 10);
c7f7a584 594 anode->exe=exe_val_null;
595 anode->returntype |= RETURNTYPE_CONST;
596 } else {
597 anode->exe=exe_strtoint;
598 }
599 break;
600
601 default:
602 case RETURNTYPE_BOOL:
603 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 604 if ((thenode->exe)(ctx, thenode,NULL))
c7f7a584 605 cd->u.val=1;
606 else
607 cd->u.val=0;
608
609 anode->exe=exe_val_null;
610 anode->returntype |= RETURNTYPE_CONST;
611 } else {
612 anode->exe=exe_booltoint;
613 }
614 break;
615 }
616 break;
617
618 default:
619 case RETURNTYPE_BOOL:
620 /* we want a bool */
621 switch (thenode->returntype & RETURNTYPE_TYPE) {
622 case RETURNTYPE_STRING:
623 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 624 char *rv=(char *)((thenode->exe)(ctx, thenode, NULL));
c7f7a584 625 if (!rv || *rv=='\0' || (*rv=='0' && rv[1]=='\0'))
626 cd->u.val=0;
627 else
628 cd->u.val=1;
629
630 anode->exe=exe_val_null;
631 anode->returntype |= RETURNTYPE_CONST;
632 } else {
633 anode->exe=exe_strtobool;
634 }
635 break;
636
637 default:
638 case RETURNTYPE_INT:
639 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 640 if ((thenode->exe)(ctx, thenode,NULL))
c7f7a584 641 cd->u.val=1;
642 else
643 cd->u.val=0;
644
645 anode->exe=exe_val_null;
646 anode->returntype |= RETURNTYPE_CONST;
647 } else {
648 anode->exe=exe_inttobool;
649 }
650 break;
651 }
652 break;
653 }
654
655 return anode;
656}
657
658/* Literals always return constant strings... */
c8be5183 659void *literal_exe(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
2e2a8c6a
P
660 if (thenode->localdata)
661 return ((sstring *)thenode->localdata)->content;
662 else
663 return "";
c7f7a584 664}
665
c8be5183 666void literal_free(searchCtx *ctx, struct searchNode *thenode) {
c7f7a584 667 freesstring(thenode->localdata);
668 free(thenode);
669}
c86edd1d
Q
670
671/* search_parse:
672 * Given an input string, return a searchNode.
673 */
674
f33f3f52 675struct searchNode *search_parse(searchCtx *ctx, char *input) {
c86edd1d
Q
676 /* OK, we need to split the input into chunks on spaces and brackets.. */
677 char *argvector[100];
1bf9cda6 678 char thestring[500];
c7f7a584 679 int i,j,q=0,e=0;
1bf9cda6 680 char *ch,*ch2;
c86edd1d
Q
681 struct Command *cmd;
682 struct searchNode *thenode;
c86edd1d
Q
683
684 /* If it starts with a bracket, it's a function call.. */
685 if (*input=='(') {
686 /* Skip past string */
687 for (ch=input;*ch;ch++);
688 if (*(ch-1) != ')') {
689 parseError = "Bracket mismatch!";
690 return NULL;
691 }
692 input++;
693 *(ch-1)='\0';
694
695 /* Split further args */
696 i=-1; /* i = -1 BoW, 0 = inword, 1 = bracket nest depth */
697 j=0; /* j = current arg */
1bf9cda6 698 e=0;
699 q=0;
a33e0d2b 700 argvector[0]="";
c86edd1d
Q
701 for (ch=input;*ch;ch++) {
702 if (i==-1) {
1bf9cda6 703 argvector[j]=ch;
704 if (*ch=='(') {
705 i=1;
706 } else if (*ch != ' ') {
707 i=0;
708 if (*ch=='\\') {
709 e=1;
710 } else if (*ch=='\"') {
711 q=1;
712 }
713 }
714 } else if (e==1) {
715 e=0;
716 } else if (q==1) {
717 if (*ch=='\"')
718 q=0;
c86edd1d 719 } else if (i==0) {
1bf9cda6 720 if (*ch=='\\') {
721 e=1;
722 } else if (*ch=='\"') {
723 q=1;
724 } else if (*ch==' ') {
725 *ch='\0';
726 j++;
4cd6347c
P
727 if(j >= (sizeof(argvector) / sizeof(*argvector))) {
728 parseError = "Too many arguments";
729 return NULL;
730 }
1bf9cda6 731 i=-1;
732 }
c86edd1d 733 } else {
1bf9cda6 734 if (*ch=='\\') {
735 e=1;
736 } else if (*ch=='\"') {
737 q=1;
738 } else if (*ch=='(') {
739 i++;
740 } else if (*ch==')') {
741 i--;
742 }
c86edd1d
Q
743 }
744 }
745
746 if (i>0) {
747 parseError = "Bracket mismatch!";
748 return NULL;
749 }
4cd6347c
P
750
751 if (*(ch-1) == 0) /* if the last character was a space */
752 j--; /* remove an argument */
c86edd1d
Q
753
754 if (!(cmd=findcommandintree(searchTree,argvector[0],1))) {
755 parseError = "Unknown command";
756 return NULL;
757 } else {
f33f3f52 758 return ((parseFunc)cmd->handler)(ctx, j, argvector+1);
c86edd1d
Q
759 }
760 } else {
761 /* Literal */
1bf9cda6 762 if (*input=='\"') {
763 for (ch=input;*ch;ch++);
764
765 if (*(ch-1) != '\"') {
766 parseError="Quote mismatch";
767 return NULL;
768 }
769
770 *(ch-1)='\0';
771 input++;
772 }
773
774 ch2=thestring;
775 for (ch=input;*ch;ch++) {
776 if (e) {
777 e=0;
778 *ch2++=*ch;
779 } else if (*ch=='\\') {
780 e=1;
781 } else {
782 *ch2++=*ch;
783 }
784 }
785 *ch2='\0';
786
9ce4f0be
IB
787 if (!(thenode=(struct searchNode *)malloc(sizeof(struct searchNode)))) {
788 parseError = "malloc: could not allocate memory for this search.";
789 return NULL;
1bf9cda6 790 }
791
c7f7a584 792 thenode->localdata = getsstring(thestring,512);
c86edd1d
Q
793 thenode->returntype = RETURNTYPE_CONST | RETURNTYPE_STRING;
794 thenode->exe = literal_exe;
795 thenode->free = literal_free;
796
797 return thenode;
798 }
799}
2ba836f2 800
2ba836f2 801void nssnprintf(char *buf, size_t size, const char *format, nick *np) {
ba8a65c4 802 StringBuf b;
2ba836f2
CP
803 const char *p;
804 char *c;
805 char hostbuf[512];
806
807 if(size == 0)
808 return;
809
810 b.buf = buf;
811 b.capacity = size;
812 b.len = 0;
813
814 for(p=format;*p;p++) {
815 if(*p != '%') {
ba8a65c4 816 if(!sbaddchar(&b, *p))
2ba836f2
CP
817 break;
818 continue;
819 }
820 p++;
821 if(*p == '\0')
822 break;
823 if(*p == '%') {
ba8a65c4 824 if(!sbaddchar(&b, *p))
2ba836f2
CP
825 break;
826 continue;
827 }
828
829 c = NULL;
830 switch(*p) {
831 case 'n':
832 c = np->nick; break;
833 case 'i':
834 c = np->ident; break;
835 case 'h':
836 c = np->host->name->content; break;
837 case 'I':
838 snprintf(hostbuf, sizeof(hostbuf), "%s", IPtostr(np->p_ipaddr));
839 c = hostbuf;
840 break;
841 case 'u':
842 snprintf(hostbuf, sizeof(hostbuf), "%s!%s@%s", np->nick, np->ident, IPtostr(np->p_ipaddr));
843 c = hostbuf;
844 break;
845 default:
846 c = "(bad format specifier)";
847 }
848 if(c)
ba8a65c4 849 if(!sbaddstr(&b, c))
2ba836f2
CP
850 break;
851 }
852
0be0b2d0 853 sbterminate(&b);
2ba836f2
CP
854
855 /* not required */
856 /*
857 buf[size-1] = '\0';
858 */
859}
860
f33f3f52
P
861static char *var_tochar(searchCtx *ctx, char *arg, searchNode **variable) {
862 *variable = ctx->parser(ctx, arg);
b697c21c
CP
863 if (!(*variable = coerceNode(ctx, *variable, RETURNTYPE_STRING)))
864 return NULL;
865
866 if(!((*variable)->returntype & RETURNTYPE_CONST)) {
867 parseError = "only constant variables allowed";
868 ((*variable)->free)(ctx, *variable);
869 return NULL;
870 }
871
872 return (char *)((*variable)->exe)(ctx, *variable, NULL);
873}
874
875void free_val_null(searchCtx *ctx, struct searchNode *thenode) {
876}
877
f33f3f52 878struct searchVariable *var_register(searchCtx *ctx, char *arg, int type) {
b697c21c
CP
879 searchNode *variable;
880 struct searchVariable *us;
881 char *var;
882 int i;
883
884 if(ctx->lastvar >= MAX_VARIABLES) {
885 parseError = "Maximum number of variables reached";
886 return NULL;
887 }
888
889 us = &ctx->vars[ctx->lastvar];
890
f33f3f52 891 var = var_tochar(ctx, arg, &variable);
b697c21c
CP
892 if(!var)
893 return NULL;
894
895 strlcpy(us->name, var, sizeof(us->name));
896 (variable->free)(ctx, variable);
897
898 for(i=0;i<ctx->lastvar;i++) {
899 if(!strcmp(us->name, ctx->vars[i].name)) {
900 parseError = "variable name already in use";
901 return NULL;
902 }
903 }
904
905 ctx->lastvar++;
906 us->data.returntype = type;
907 us->data.localdata = &us->cdata;
908 us->data.exe = exe_val_null;
909 us->data.free = free_val_null;
910
911 us->cdata.child = NULL;
912 return us;
913}
914
f33f3f52 915searchNode *var_get(searchCtx *ctx, char *arg) {
b697c21c
CP
916 searchNode *variable, *found = NULL;
917 int i;
f33f3f52 918 char *var = var_tochar(ctx, arg, &variable);
b697c21c
CP
919 if(!var)
920 return NULL;
921
922 for(i=0;i<ctx->lastvar;i++) {
923 if(!strcmp(var, ctx->vars[i].name)) {
924 found = &ctx->vars[i].data;
925 break;
926 }
927 }
928 (variable->free)(ctx, variable);
929
930 if(!found)
931 parseError = "variable not found";
932 return found;
933}
934
935void var_setstr(struct searchVariable *v, char *data) {
936 v->cdata.u.stringbuf = data;
937}