]> jfr.im git - irc/quakenet/newserv.git/blame - newsearch/newsearch.c
Add iterators to newsearch, along with an example channel iterator function.
[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
b697c21c
CP
219void newsearch_ctxinit(searchCtx *ctx, searchParseFunc searchfn, replyFunc replyfn, wallFunc wallfn, void *arg) {
220 memset(ctx, 0, sizeof(searchCtx));
221
222 ctx->reply = replyfn;
223 ctx->wall = wallfn;
224 ctx->parser = searchfn;
225 ctx->arg = arg;
226}
227
e8ad630b 228int do_nicksearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv) {
b697c21c 229 nick *sender = source;
e8ad630b
CP
230 struct searchNode *search;
231 int limit=500;
232 int arg=0;
55dd7529 233 NickDisplayFunc display=defaultnickfn;
e8ad630b
CP
234 searchCtx ctx;
235 int ret;
236
237 if (cargc<1)
238 return CMD_USAGE;
239
240 ret = parseopts(cargc, cargv, &arg, &limit, (void **)&display, nickOutputTree, reply, sender);
241 if(ret != CMD_OK)
242 return ret;
243
c86edd1d 244 if (arg>=cargc) {
4156103f 245 reply(sender,"No search terms - aborting.");
c86edd1d
Q
246 return CMD_ERROR;
247 }
248
249 if (arg<(cargc-1)) {
250 rejoinline(cargv[arg],cargc-arg);
251 }
c8be5183 252
b697c21c 253 newsearch_ctxinit(&ctx, search_parse, reply, wall, NULL);
c8be5183
CP
254
255 if (!(search = ctx.parser(&ctx, SEARCHTYPE_NICK, cargv[arg]))) {
0da2a4ae 256 reply(sender,"Parse error: %s",parseError);
c86edd1d
Q
257 return CMD_ERROR;
258 }
c2c414be 259
c8be5183
CP
260 nicksearch_exe(search, &ctx, sender, display, limit);
261
262 (search->free)(&ctx, search);
c2c414be
CP
263
264 return CMD_OK;
265}
266
0da2a4ae
CP
267int do_nicksearch(void *source, int cargc, char **cargv) {
268 return do_nicksearch_real(controlreply, controlwallwrapper, source, cargc, cargv);
269}
270
c8be5183 271void nicksearch_exe(struct searchNode *search, searchCtx *ctx, nick *sender, NickDisplayFunc display, int limit) {
c2c414be
CP
272 int i, j;
273 int matches = 0;
274 unsigned int cmarker;
275 unsigned int tchans=0,uchans=0;
276 struct channel **cs;
277 nick *np;
b697c21c
CP
278 senderNSExtern = sender;
279
7c02e111 280 /* Get a marker value to mark "seen" channels for unique count */
281 cmarker=nextchanmarker();
282
c7f7a584 283 /* The top-level node needs to return a BOOL */
c8be5183 284 search=coerceNode(ctx, search, RETURNTYPE_BOOL);
c7f7a584 285
c86edd1d
Q
286 for (i=0;i<NICKHASHSIZE;i++) {
287 for (np=nicktable[i];np;np=np->next) {
c8be5183 288 if ((search->exe)(ctx, search, np)) {
7c02e111 289 /* Add total channels */
290 tchans += np->channels->cursi;
291
292 /* Check channels for uniqueness */
293 cs=(channel **)np->channels->content;
294 for (j=0;j<np->channels->cursi;j++) {
295 if (cs[j]->index->marker != cmarker) {
296 cs[j]->index->marker=cmarker;
297 uchans++;
298 }
299 }
300
c86edd1d 301 if (matches<limit)
3d2bf13b 302 display(ctx, sender, np);
7c02e111 303
c86edd1d 304 if (matches==limit)
c8be5183 305 ctx->reply(sender, "--- More than %d matches, skipping the rest",limit);
c86edd1d
Q
306 matches++;
307 }
308 }
309 }
310
c8be5183 311 ctx->reply(sender,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
7c02e111 312 matches, tchans, uchans, (float)tchans/uchans);
c86edd1d
Q
313}
314
0da2a4ae 315int do_chansearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv) {
b697c21c 316 nick *sender = source;
56a84a81 317 struct searchNode *search;
c2c414be 318 int limit=500;
56a84a81 319 int arg=0;
55dd7529 320 ChanDisplayFunc display=defaultchanfn;
c8be5183 321 searchCtx ctx;
e8ad630b 322 int ret;
56a84a81
IB
323
324 if (cargc<1)
325 return CMD_USAGE;
326
e8ad630b
CP
327 ret = parseopts(cargc, cargv, &arg, &limit, (void **)&display, chanOutputTree, reply, sender);
328 if(ret != CMD_OK)
329 return ret;
56a84a81
IB
330
331 if (arg>=cargc) {
4156103f 332 reply(sender,"No search terms - aborting.");
56a84a81
IB
333 return CMD_ERROR;
334 }
335
336 if (arg<(cargc-1)) {
337 rejoinline(cargv[arg],cargc-arg);
338 }
c2c414be 339
b697c21c 340 newsearch_ctxinit(&ctx, search_parse, reply, wall, NULL);
c8be5183 341 if (!(search = ctx.parser(&ctx, SEARCHTYPE_CHANNEL, cargv[arg]))) {
0da2a4ae 342 reply(sender,"Parse error: %s",parseError);
56a84a81
IB
343 return CMD_ERROR;
344 }
c7f7a584 345
c8be5183 346 chansearch_exe(search, &ctx, sender, display, limit);
c2c414be 347
c8be5183 348 (search->free)(&ctx, search);
c2c414be
CP
349
350 return CMD_OK;
351}
352
0da2a4ae
CP
353int do_chansearch(void *source, int cargc, char **cargv) {
354 return do_chansearch_real(controlreply, controlwallwrapper, source, cargc, cargv);
355}
356
c8be5183 357void chansearch_exe(struct searchNode *search, searchCtx *ctx, nick *sender, ChanDisplayFunc display, int limit) {
c2c414be
CP
358 int i;
359 chanindex *cip;
360 int matches = 0;
b697c21c 361 senderNSExtern = sender;
c2c414be 362
c8be5183 363 search=coerceNode(ctx, search, RETURNTYPE_BOOL);
56a84a81
IB
364
365 for (i=0;i<CHANNELHASHSIZE;i++) {
366 for (cip=chantable[i];cip;cip=cip->next) {
c8be5183 367 if ((search->exe)(ctx, search, cip)) {
56a84a81 368 if (matches<limit)
3d2bf13b 369 display(ctx, sender, cip);
56a84a81 370 if (matches==limit)
c8be5183 371 ctx->reply(sender, "--- More than %d matches, skipping the rest",limit);
56a84a81
IB
372 matches++;
373 }
374 }
375 }
376
c8be5183 377 ctx->reply(sender,"--- End of list: %d matches", matches);
56a84a81
IB
378}
379
e8ad630b 380int do_usersearch_real(replyFunc reply, wallFunc wall, void *source, int cargc, char **cargv) {
b697c21c 381 nick *sender = source;
e8ad630b
CP
382 struct searchNode *search;
383 int limit=500;
384 int arg=0;
55dd7529 385 UserDisplayFunc display=defaultuserfn;
e8ad630b
CP
386 searchCtx ctx;
387 int ret;
388
389 if (cargc<1)
390 return CMD_USAGE;
391
392 ret = parseopts(cargc, cargv, &arg, &limit, (void **)&display, userOutputTree, reply, sender);
393 if(ret != CMD_OK)
394 return ret;
395
396 if (arg>=cargc) {
4156103f 397 reply(sender,"No search terms - aborting.");
e8ad630b
CP
398 return CMD_ERROR;
399 }
400
401 if (arg<(cargc-1)) {
402 rejoinline(cargv[arg],cargc-arg);
403 }
404
b697c21c 405 newsearch_ctxinit(&ctx, search_parse, reply, wall, NULL);
e8ad630b
CP
406 if (!(search = ctx.parser(&ctx, SEARCHTYPE_USER, cargv[arg]))) {
407 reply(sender,"Parse error: %s",parseError);
408 return CMD_ERROR;
409 }
410
411 usersearch_exe(search, &ctx, sender, display, limit);
412
413 (search->free)(&ctx, search);
414
415 return CMD_OK;
416}
417
418int do_usersearch(void *source, int cargc, char **cargv) {
419 return do_usersearch_real(controlreply, controlwallwrapper, source, cargc, cargv);
420}
421
422void usersearch_exe(struct searchNode *search, searchCtx *ctx, nick *sender, UserDisplayFunc display, int limit) {
423 int i;
424 authname *aup;
425 int matches = 0;
b697c21c 426 senderNSExtern = sender;
e8ad630b
CP
427
428 search=coerceNode(ctx, search, RETURNTYPE_BOOL);
429
430 for (i=0;i<AUTHNAMEHASHSIZE;i++) {
431 for (aup=authnametable[i];aup;aup=aup->next) {
432 if ((search->exe)(ctx, search, aup)) {
433 if (matches<limit)
434 display(ctx, sender, aup);
435 if (matches==limit)
436 ctx->reply(sender, "--- More than %d matches, skipping the rest",limit);
437 matches++;
438 }
439 }
440 }
441
442 ctx->reply(sender,"--- End of list: %d matches", matches);
443}
444
c7f7a584 445/* Free a coerce node */
c8be5183 446void free_coerce(searchCtx *ctx, struct searchNode *thenode) {
c7f7a584 447 struct coercedata *cd=thenode->localdata;
448
c8be5183 449 cd->child->free(ctx, cd->child);
c7f7a584 450 free(thenode->localdata);
451 free(thenode);
452}
c86edd1d 453
c7f7a584 454/* Free a coerce node with a stringbuf allocated */
c8be5183 455void free_coercestring(searchCtx *ctx, struct searchNode *thenode) {
c7f7a584 456 free(((struct coercedata *)thenode->localdata)->u.stringbuf);
c8be5183 457 free_coerce(ctx, thenode);
c7f7a584 458}
459
460/* exe_tostr_null: return the constant string */
c8be5183 461void *exe_tostr_null(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 462 struct coercedata *cd=thenode->localdata;
463
464 return cd->u.stringbuf;
465}
466
467/* exe_val_null: return the constant value */
c8be5183 468void *exe_val_null(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 469 struct coercedata *cd=thenode->localdata;
470
471 return (void *)cd->u.val;
472}
473
474/* Lots of very dull type conversion functions */
c8be5183 475void *exe_inttostr(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 476 struct coercedata *cd=thenode->localdata;
477
c8be5183 478 sprintf(cd->u.stringbuf, "%lu", (unsigned long)(cd->child->exe)(ctx, cd->child, theinput));
c7f7a584 479
480 return cd->u.stringbuf;
481}
482
c8be5183 483void *exe_booltostr(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 484 struct coercedata *cd=thenode->localdata;
485
c8be5183 486 if ((cd->child->exe)(ctx, cd->child, theinput)) {
c7f7a584 487 sprintf(cd->u.stringbuf,"1");
488 } else {
489 cd->u.stringbuf[0]='\0';
490 }
491
492 return cd->u.stringbuf;
493}
c86edd1d 494
c8be5183 495void *exe_strtoint(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 496 struct coercedata *cd=thenode->localdata;
497
c8be5183 498 return (void *)strtoul((cd->child->exe)(ctx,cd->child,theinput),NULL,10);
c7f7a584 499}
500
c8be5183 501void *exe_booltoint(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 502 struct coercedata *cd=thenode->localdata;
503
504 /* Don't need to do anything */
c8be5183 505 return (cd->child->exe)(ctx, cd->child, theinput);
c7f7a584 506}
507
c8be5183 508void *exe_strtobool(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 509 struct coercedata *cd=thenode->localdata;
c8be5183 510 char *ch=(cd->child->exe)(ctx, cd->child, theinput);
c7f7a584 511
512 if (!ch || *ch=='\0' || (*ch=='0' && ch[1]=='\0')) {
513 return (void *)0;
514 } else {
515 return (void *)1;
516 }
517}
518
c8be5183 519void *exe_inttobool(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
c7f7a584 520 struct coercedata *cd=thenode->localdata;
521
c8be5183 522 if ((cd->child->exe)(ctx, cd->child, theinput)) {
c7f7a584 523 return (void *)1;
524 } else {
525 return (void *)0;
526 }
527}
528
c8be5183 529struct searchNode *coerceNode(searchCtx *ctx, struct searchNode *thenode, int type) {
c7f7a584 530 struct searchNode *anode;
531 struct coercedata *cd;
532
533 /* You can't coerce a NULL */
534 if (!thenode)
535 return NULL;
536
537 /* No effort required to coerce to the same type */
538 if (type==(thenode->returntype & RETURNTYPE_TYPE))
539 return thenode;
540
541 anode=(struct searchNode *)malloc(sizeof(struct searchNode));
542 anode->localdata=cd=(struct coercedata *)malloc(sizeof(struct coercedata));
543 cd->child=thenode;
544 anode->returntype=type; /* We'll return what they want, always */
545 anode->free=free_coerce;
546
547 switch(type) {
548 case RETURNTYPE_STRING:
549 /* For a string we'll need a buffer */
550 /* A 64-bit number prints out to 20 digits, this leaves some slack */
551 cd->u.stringbuf=malloc(25);
552 anode->free=free_coercestring;
553
554 switch(thenode->returntype & RETURNTYPE_TYPE) {
555 default:
556 case RETURNTYPE_INT:
557 if (thenode->returntype & RETURNTYPE_CONST) {
558 /* Constant node: sort it out now */
c8be5183 559 sprintf(cd->u.stringbuf, "%lu", (unsigned long)thenode->exe(ctx, thenode, NULL));
c7f7a584 560 anode->exe=exe_tostr_null;
561 anode->returntype |= RETURNTYPE_CONST;
562 } else {
563 /* Variable data */
564 anode->exe=exe_inttostr;
565 }
566 break;
567
568 case RETURNTYPE_BOOL:
569 if (thenode->returntype & RETURNTYPE_CONST) {
570 /* Constant bool value */
c8be5183 571 if (thenode->exe(ctx, thenode,NULL)) {
c7f7a584 572 /* True! */
573 sprintf(cd->u.stringbuf, "1");
574 } else {
575 cd->u.stringbuf[0] = '\0';
576 }
577 anode->exe=exe_tostr_null;
578 anode->returntype |= RETURNTYPE_CONST;
579 } else {
580 /* Variable bool value */
581 anode->exe=exe_booltostr;
582 }
583 break;
584 }
585 break;
586
587 case RETURNTYPE_INT:
588 /* we want an int */
589 switch (thenode->returntype & RETURNTYPE_TYPE) {
590 case RETURNTYPE_STRING:
591 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 592 cd->u.val=strtoul((thenode->exe)(ctx, thenode, NULL), NULL, 10);
c7f7a584 593 anode->exe=exe_val_null;
594 anode->returntype |= RETURNTYPE_CONST;
595 } else {
596 anode->exe=exe_strtoint;
597 }
598 break;
599
600 default:
601 case RETURNTYPE_BOOL:
602 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 603 if ((thenode->exe)(ctx, thenode,NULL))
c7f7a584 604 cd->u.val=1;
605 else
606 cd->u.val=0;
607
608 anode->exe=exe_val_null;
609 anode->returntype |= RETURNTYPE_CONST;
610 } else {
611 anode->exe=exe_booltoint;
612 }
613 break;
614 }
615 break;
616
617 default:
618 case RETURNTYPE_BOOL:
619 /* we want a bool */
620 switch (thenode->returntype & RETURNTYPE_TYPE) {
621 case RETURNTYPE_STRING:
622 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 623 char *rv=(char *)((thenode->exe)(ctx, thenode, NULL));
c7f7a584 624 if (!rv || *rv=='\0' || (*rv=='0' && rv[1]=='\0'))
625 cd->u.val=0;
626 else
627 cd->u.val=1;
628
629 anode->exe=exe_val_null;
630 anode->returntype |= RETURNTYPE_CONST;
631 } else {
632 anode->exe=exe_strtobool;
633 }
634 break;
635
636 default:
637 case RETURNTYPE_INT:
638 if (thenode->returntype & RETURNTYPE_CONST) {
c8be5183 639 if ((thenode->exe)(ctx, thenode,NULL))
c7f7a584 640 cd->u.val=1;
641 else
642 cd->u.val=0;
643
644 anode->exe=exe_val_null;
645 anode->returntype |= RETURNTYPE_CONST;
646 } else {
647 anode->exe=exe_inttobool;
648 }
649 break;
650 }
651 break;
652 }
653
654 return anode;
655}
656
657/* Literals always return constant strings... */
c8be5183 658void *literal_exe(searchCtx *ctx, struct searchNode *thenode, void *theinput) {
2e2a8c6a
P
659 if (thenode->localdata)
660 return ((sstring *)thenode->localdata)->content;
661 else
662 return "";
c7f7a584 663}
664
c8be5183 665void literal_free(searchCtx *ctx, struct searchNode *thenode) {
c7f7a584 666 freesstring(thenode->localdata);
667 free(thenode);
668}
c86edd1d
Q
669
670/* search_parse:
671 * Given an input string, return a searchNode.
672 */
673
c8be5183 674struct searchNode *search_parse(searchCtx *ctx, int type, char *input) {
c86edd1d
Q
675 /* OK, we need to split the input into chunks on spaces and brackets.. */
676 char *argvector[100];
1bf9cda6 677 char thestring[500];
c7f7a584 678 int i,j,q=0,e=0;
1bf9cda6 679 char *ch,*ch2;
c86edd1d
Q
680 struct Command *cmd;
681 struct searchNode *thenode;
c86edd1d
Q
682
683 /* If it starts with a bracket, it's a function call.. */
684 if (*input=='(') {
685 /* Skip past string */
686 for (ch=input;*ch;ch++);
687 if (*(ch-1) != ')') {
688 parseError = "Bracket mismatch!";
689 return NULL;
690 }
691 input++;
692 *(ch-1)='\0';
693
694 /* Split further args */
695 i=-1; /* i = -1 BoW, 0 = inword, 1 = bracket nest depth */
696 j=0; /* j = current arg */
1bf9cda6 697 e=0;
698 q=0;
a33e0d2b 699 argvector[0]="";
c86edd1d
Q
700 for (ch=input;*ch;ch++) {
701 if (i==-1) {
1bf9cda6 702 argvector[j]=ch;
703 if (*ch=='(') {
704 i=1;
705 } else if (*ch != ' ') {
706 i=0;
707 if (*ch=='\\') {
708 e=1;
709 } else if (*ch=='\"') {
710 q=1;
711 }
712 }
713 } else if (e==1) {
714 e=0;
715 } else if (q==1) {
716 if (*ch=='\"')
717 q=0;
c86edd1d 718 } else if (i==0) {
1bf9cda6 719 if (*ch=='\\') {
720 e=1;
721 } else if (*ch=='\"') {
722 q=1;
723 } else if (*ch==' ') {
724 *ch='\0';
725 j++;
4cd6347c
P
726 if(j >= (sizeof(argvector) / sizeof(*argvector))) {
727 parseError = "Too many arguments";
728 return NULL;
729 }
1bf9cda6 730 i=-1;
731 }
c86edd1d 732 } else {
1bf9cda6 733 if (*ch=='\\') {
734 e=1;
735 } else if (*ch=='\"') {
736 q=1;
737 } else if (*ch=='(') {
738 i++;
739 } else if (*ch==')') {
740 i--;
741 }
c86edd1d
Q
742 }
743 }
744
745 if (i>0) {
746 parseError = "Bracket mismatch!";
747 return NULL;
748 }
4cd6347c
P
749
750 if (*(ch-1) == 0) /* if the last character was a space */
751 j--; /* remove an argument */
c86edd1d
Q
752
753 if (!(cmd=findcommandintree(searchTree,argvector[0],1))) {
754 parseError = "Unknown command";
755 return NULL;
756 } else {
c8be5183 757 return ((parseFunc)cmd->handler)(ctx, type, j, argvector+1);
c86edd1d
Q
758 }
759 } else {
760 /* Literal */
1bf9cda6 761 if (*input=='\"') {
762 for (ch=input;*ch;ch++);
763
764 if (*(ch-1) != '\"') {
765 parseError="Quote mismatch";
766 return NULL;
767 }
768
769 *(ch-1)='\0';
770 input++;
771 }
772
773 ch2=thestring;
774 for (ch=input;*ch;ch++) {
775 if (e) {
776 e=0;
777 *ch2++=*ch;
778 } else if (*ch=='\\') {
779 e=1;
780 } else {
781 *ch2++=*ch;
782 }
783 }
784 *ch2='\0';
785
9ce4f0be
IB
786 if (!(thenode=(struct searchNode *)malloc(sizeof(struct searchNode)))) {
787 parseError = "malloc: could not allocate memory for this search.";
788 return NULL;
1bf9cda6 789 }
790
c7f7a584 791 thenode->localdata = getsstring(thestring,512);
c86edd1d
Q
792 thenode->returntype = RETURNTYPE_CONST | RETURNTYPE_STRING;
793 thenode->exe = literal_exe;
794 thenode->free = literal_free;
795
796 return thenode;
797 }
798}
2ba836f2 799
2ba836f2 800void nssnprintf(char *buf, size_t size, const char *format, nick *np) {
ba8a65c4 801 StringBuf b;
2ba836f2
CP
802 const char *p;
803 char *c;
804 char hostbuf[512];
805
806 if(size == 0)
807 return;
808
809 b.buf = buf;
810 b.capacity = size;
811 b.len = 0;
812
813 for(p=format;*p;p++) {
814 if(*p != '%') {
ba8a65c4 815 if(!sbaddchar(&b, *p))
2ba836f2
CP
816 break;
817 continue;
818 }
819 p++;
820 if(*p == '\0')
821 break;
822 if(*p == '%') {
ba8a65c4 823 if(!sbaddchar(&b, *p))
2ba836f2
CP
824 break;
825 continue;
826 }
827
828 c = NULL;
829 switch(*p) {
830 case 'n':
831 c = np->nick; break;
832 case 'i':
833 c = np->ident; break;
834 case 'h':
835 c = np->host->name->content; break;
836 case 'I':
837 snprintf(hostbuf, sizeof(hostbuf), "%s", IPtostr(np->p_ipaddr));
838 c = hostbuf;
839 break;
840 case 'u':
841 snprintf(hostbuf, sizeof(hostbuf), "%s!%s@%s", np->nick, np->ident, IPtostr(np->p_ipaddr));
842 c = hostbuf;
843 break;
844 default:
845 c = "(bad format specifier)";
846 }
847 if(c)
ba8a65c4 848 if(!sbaddstr(&b, c))
2ba836f2
CP
849 break;
850 }
851
0be0b2d0 852 sbterminate(&b);
2ba836f2
CP
853
854 /* not required */
855 /*
856 buf[size-1] = '\0';
857 */
858}
859
b697c21c
CP
860static char *var_tochar(searchCtx *ctx, int nstype, char *arg, searchNode **variable) {
861 *variable = ctx->parser(ctx, nstype, arg);
862 if (!(*variable = coerceNode(ctx, *variable, RETURNTYPE_STRING)))
863 return NULL;
864
865 if(!((*variable)->returntype & RETURNTYPE_CONST)) {
866 parseError = "only constant variables allowed";
867 ((*variable)->free)(ctx, *variable);
868 return NULL;
869 }
870
871 return (char *)((*variable)->exe)(ctx, *variable, NULL);
872}
873
874void free_val_null(searchCtx *ctx, struct searchNode *thenode) {
875}
876
877struct searchVariable *var_register(searchCtx *ctx, int nstype, char *arg, int type) {
878 searchNode *variable;
879 struct searchVariable *us;
880 char *var;
881 int i;
882
883 if(ctx->lastvar >= MAX_VARIABLES) {
884 parseError = "Maximum number of variables reached";
885 return NULL;
886 }
887
888 us = &ctx->vars[ctx->lastvar];
889
890 var = var_tochar(ctx, nstype, arg, &variable);
891 if(!var)
892 return NULL;
893
894 strlcpy(us->name, var, sizeof(us->name));
895 (variable->free)(ctx, variable);
896
897 for(i=0;i<ctx->lastvar;i++) {
898 if(!strcmp(us->name, ctx->vars[i].name)) {
899 parseError = "variable name already in use";
900 return NULL;
901 }
902 }
903
904 ctx->lastvar++;
905 us->data.returntype = type;
906 us->data.localdata = &us->cdata;
907 us->data.exe = exe_val_null;
908 us->data.free = free_val_null;
909
910 us->cdata.child = NULL;
911 return us;
912}
913
914searchNode *var_get(searchCtx *ctx, int nstype, char *arg) {
915 searchNode *variable, *found = NULL;
916 int i;
917 char *var = var_tochar(ctx, nstype, arg, &variable);
918 if(!var)
919 return NULL;
920
921 for(i=0;i<ctx->lastvar;i++) {
922 if(!strcmp(var, ctx->vars[i].name)) {
923 found = &ctx->vars[i].data;
924 break;
925 }
926 }
927 (variable->free)(ctx, variable);
928
929 if(!found)
930 parseError = "variable not found";
931 return found;
932}
933
934void var_setstr(struct searchVariable *v, char *data) {
935 v->cdata.u.stringbuf = data;
936}