]>
jfr.im git - irc/quakenet/newserv.git/blob - newsearch/newsearch.c
5 #include "../irc/irc_config.h"
6 #include "../lib/irc_string.h"
7 #include "../parser/parser.h"
8 #include "../control/control.h"
9 #include "../lib/splitline.h"
10 #include "../lib/version.h"
14 CommandTree
*searchTree
;
15 CommandTree
*chanOutputTree
;
16 CommandTree
*nickOutputTree
;
18 int do_nicksearch(void *source
, int cargc
, char **cargv
);
19 int do_chansearch(void *source
, int cargc
, char **cargv
);
20 struct searchNode
*search_parse(int type
, char *input
);
22 void printnick(nick
*, nick
*);
23 void printnick_channels(nick
*, nick
*);
24 void printchannel(nick
*, chanindex
*);
25 void printchannel_topic(nick
*, chanindex
*);
26 void printchannel_services(nick
*, chanindex
*);
28 void registersearchterm(char *term
, parseFunc parsefunc
);
29 void deregistersearchterm(char *term
, parseFunc parsefunc
);
31 void regchandisp(const char *name
, ChanDisplayFunc handler
) {
32 addcommandtotree(chanOutputTree
, name
, 0, 0, (CommandHandler
)handler
);
35 void unregchandisp(const char *name
, ChanDisplayFunc handler
) {
36 deletecommandfromtree(chanOutputTree
, name
, (CommandHandler
)handler
);
39 void regnickdisp(const char *name
, NickDisplayFunc handler
) {
40 addcommandtotree(nickOutputTree
, name
, 0, 0, (CommandHandler
)handler
);
43 void unregnickdisp(const char *name
, NickDisplayFunc handler
) {
44 deletecommandfromtree(nickOutputTree
, name
, (CommandHandler
)handler
);
47 const char *parseError
;
48 /* used for *_free functions that need to warn users of certain things
49 i.e. hitting too many users in a (kill) or (gline) */
53 searchTree
=newcommandtree();
54 chanOutputTree
=newcommandtree();
55 nickOutputTree
=newcommandtree();
57 /* Boolean operations */
58 registersearchterm("and",and_parse
);
59 registersearchterm("not",not_parse
);
60 registersearchterm("or",or_parse
);
62 registersearchterm("eq",eq_parse
);
64 registersearchterm("lt",lt_parse
);
65 registersearchterm("gt",gt_parse
);
67 /* String operations */
68 registersearchterm("match",match_parse
);
69 registersearchterm("regex",regex_parse
);
70 registersearchterm("length",length_parse
);
72 /* Nickname operations */
73 registersearchterm("hostmask",hostmask_parse
);
74 registersearchterm("realname",realname_parse
);
75 registersearchterm("authname",authname_parse
);
76 registersearchterm("authts",authts_parse
);
77 registersearchterm("ident",ident_parse
);
78 registersearchterm("host",host_parse
);
79 registersearchterm("channel",channel_parse
);
80 registersearchterm("timestamp",timestamp_parse
);
81 registersearchterm("country",country_parse
);
82 registersearchterm("ip",ip_parse
);
84 /* Channel operations */
85 registersearchterm("exists",exists_parse
);
86 registersearchterm("services",services_parse
);
87 registersearchterm("size",size_parse
);
88 registersearchterm("name",name_parse
);
89 registersearchterm("topic",topic_parse
);
90 registersearchterm("oppct",oppct_parse
);
91 registersearchterm("uniquehostpct",hostpct_parse
);
92 registersearchterm("authedpct",authedpct_parse
);
93 registersearchterm("kick",kick_parse
);
95 /* Nickname / channel operations */
96 registersearchterm("modes",modes_parse
);
97 registersearchterm("nick",nick_parse
);
99 /* Kill / gline parameters */
100 registersearchterm("kill",kill_parse
);
101 registersearchterm("gline",gline_parse
);
103 /* Nick output filters */
104 regnickdisp("default",printnick
);
105 regnickdisp("channels",printnick_channels
);
107 /* Channel output filters */
108 regchandisp("default",printchannel
);
109 regchandisp("topic",printchannel_topic
);
110 regchandisp("services",printchannel_services
);
112 registercontrolhelpcmd("nicksearch",NO_OPER
,4,do_nicksearch
, "Usage: nicksearch <criteria>\nSearches for nicknames with the given criteria.");
113 registercontrolhelpcmd("chansearch",NO_OPER
,4,do_chansearch
, "Usage: chansearch <criteria>\nSearches for channels with the given criteria.");
117 destroycommandtree(searchTree
);
118 destroycommandtree(chanOutputTree
);
119 destroycommandtree(nickOutputTree
);
120 deregistercontrolcmd("nicksearch", do_nicksearch
);
121 deregistercontrolcmd("chansearch", do_chansearch
);
124 void registersearchterm(char *term
, parseFunc parsefunc
) {
125 addcommandtotree(searchTree
, term
, 0, 0, (CommandHandler
) parsefunc
);
128 void deregistersearchterm(char *term
, parseFunc parsefunc
) {
129 deletecommandfromtree(searchTree
, term
, (CommandHandler
) parsefunc
);
132 int do_nicksearch(void *source
, int cargc
, char **cargv
) {
133 nick
*sender
= senderNSExtern
= source
, *np
;
135 struct searchNode
*search
;
136 int limit
=500,matches
=0;
140 NickDisplayFunc display
=printnick
;
141 unsigned int cmarker
;
142 unsigned int tchans
=0,uchans
=0;
148 if (*cargv
[0] == '-') {
152 for (ch
=cargv
[0]+1;*ch
;ch
++) {
156 controlreply(sender
,"Error: -l switch requires an argument");
159 limit
=strtoul(cargv
[arg
++],NULL
,10);
164 controlreply(sender
,"Error: -d switch requires an argument");
167 cmd
=findcommandintree(nickOutputTree
, cargv
[arg
], 1);
169 controlreply(sender
,"Error: unknown output format %s",cargv
[arg
]);
172 display
=(NickDisplayFunc
)cmd
->handler
;
177 controlreply(sender
,"Unrecognised flag -%c.",*ch
);
183 controlreply(sender
,"No search terms - aborting.");
188 rejoinline(cargv
[arg
],cargc
-arg
);
191 if (!(search
= search_parse(SEARCHTYPE_NICK
, cargv
[arg
]))) {
192 controlreply(sender
,"Parse error: %s",parseError
);
196 /* Get a marker value to mark "seen" channels for unique count */
197 cmarker
=nextchanmarker();
199 /* The top-level node needs to return a BOOL */
200 search
=coerceNode(search
, RETURNTYPE_BOOL
);
202 for (i
=0;i
<NICKHASHSIZE
;i
++) {
203 for (np
=nicktable
[i
];np
;np
=np
->next
) {
204 if ((search
->exe
)(search
, np
)) {
205 /* Add total channels */
206 tchans
+= np
->channels
->cursi
;
208 /* Check channels for uniqueness */
209 cs
=(channel
**)np
->channels
->content
;
210 for (j
=0;j
<np
->channels
->cursi
;j
++) {
211 if (cs
[j
]->index
->marker
!= cmarker
) {
212 cs
[j
]->index
->marker
=cmarker
;
221 controlreply(sender
, "--- More than %d matches, skipping the rest",limit
);
227 (search
->free
)(search
);
229 controlreply(sender
,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
230 matches
, tchans
, uchans
, (float)tchans
/uchans
);
235 int do_chansearch(void *source
, int cargc
, char **cargv
) {
236 nick
*sender
= senderNSExtern
= source
;
239 struct searchNode
*search
;
240 int limit
=500,matches
=0;
244 ChanDisplayFunc display
=printchannel
;
249 if (*cargv
[0] == '-') {
253 for (ch
=cargv
[0]+1;*ch
;ch
++) {
257 controlreply(sender
,"Error: -l switch requires an argument");
260 limit
=strtoul(cargv
[arg
++],NULL
,10);
265 controlreply(sender
,"Error: -d switch requires an argument");
268 cmd
=findcommandintree(chanOutputTree
, cargv
[arg
], 1);
270 controlreply(sender
,"Error: unknown output format %s",cargv
[arg
]);
273 display
=(ChanDisplayFunc
)cmd
->handler
;
278 controlreply(sender
,"Unrecognised flag -%c.",*ch
);
284 controlreply(sender
,"No search terms - aborting.");
289 rejoinline(cargv
[arg
],cargc
-arg
);
292 if (!(search
= search_parse(SEARCHTYPE_CHANNEL
, cargv
[arg
]))) {
293 controlreply(sender
,"Parse error: %s",parseError
);
297 search
=coerceNode(search
, RETURNTYPE_BOOL
);
299 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
300 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
301 if ((search
->exe
)(search
, cip
)) {
303 display(sender
, cip
);
305 controlreply(sender
, "--- More than %d matches, skipping the rest",limit
);
311 (search
->free
)(search
);
313 controlreply(sender
,"--- End of list: %d matches", matches
);
319 struct searchNode
*child
;
326 /* Free a coerce node */
327 void free_coerce(struct searchNode
*thenode
) {
328 struct coercedata
*cd
=thenode
->localdata
;
330 cd
->child
->free(cd
->child
);
331 free(thenode
->localdata
);
335 /* Free a coerce node with a stringbuf allocated */
336 void free_coercestring(struct searchNode
*thenode
) {
337 free(((struct coercedata
*)thenode
->localdata
)->u
.stringbuf
);
338 free_coerce(thenode
);
341 /* exe_tostr_null: return the constant string */
342 void *exe_tostr_null(struct searchNode
*thenode
, void *theinput
) {
343 struct coercedata
*cd
=thenode
->localdata
;
345 return cd
->u
.stringbuf
;
348 /* exe_val_null: return the constant value */
349 void *exe_val_null(struct searchNode
*thenode
, void *theinput
) {
350 struct coercedata
*cd
=thenode
->localdata
;
352 return (void *)cd
->u
.val
;
355 /* Lots of very dull type conversion functions */
356 void *exe_inttostr(struct searchNode
*thenode
, void *theinput
) {
357 struct coercedata
*cd
=thenode
->localdata
;
359 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)(cd
->child
->exe
)(cd
->child
, theinput
));
361 return cd
->u
.stringbuf
;
364 void *exe_booltostr(struct searchNode
*thenode
, void *theinput
) {
365 struct coercedata
*cd
=thenode
->localdata
;
367 if ((cd
->child
->exe
)(cd
->child
, theinput
)) {
368 sprintf(cd
->u
.stringbuf
,"1");
370 cd
->u
.stringbuf
[0]='\0';
373 return cd
->u
.stringbuf
;
376 void *exe_strtoint(struct searchNode
*thenode
, void *theinput
) {
377 struct coercedata
*cd
=thenode
->localdata
;
379 return (void *)strtoul((cd
->child
->exe
)(cd
->child
,theinput
),NULL
,10);
382 void *exe_booltoint(struct searchNode
*thenode
, void *theinput
) {
383 struct coercedata
*cd
=thenode
->localdata
;
385 /* Don't need to do anything */
386 return (cd
->child
->exe
)(cd
->child
, theinput
);
389 void *exe_strtobool(struct searchNode
*thenode
, void *theinput
) {
390 struct coercedata
*cd
=thenode
->localdata
;
391 char *ch
=(cd
->child
->exe
)(cd
->child
, theinput
);
393 if (!ch
|| *ch
=='\0' || (*ch
=='0' && ch
[1]=='\0')) {
400 void *exe_inttobool(struct searchNode
*thenode
, void *theinput
) {
401 struct coercedata
*cd
=thenode
->localdata
;
403 if ((cd
->child
->exe
)(cd
->child
, theinput
)) {
410 struct searchNode
*coerceNode(struct searchNode
*thenode
, int type
) {
411 struct searchNode
*anode
;
412 struct coercedata
*cd
;
414 /* You can't coerce a NULL */
418 /* No effort required to coerce to the same type */
419 if (type
==(thenode
->returntype
& RETURNTYPE_TYPE
))
422 anode
=(struct searchNode
*)malloc(sizeof(struct searchNode
));
423 anode
->localdata
=cd
=(struct coercedata
*)malloc(sizeof(struct coercedata
));
425 anode
->returntype
=type
; /* We'll return what they want, always */
426 anode
->free
=free_coerce
;
429 case RETURNTYPE_STRING
:
430 /* For a string we'll need a buffer */
431 /* A 64-bit number prints out to 20 digits, this leaves some slack */
432 cd
->u
.stringbuf
=malloc(25);
433 anode
->free
=free_coercestring
;
435 switch(thenode
->returntype
& RETURNTYPE_TYPE
) {
438 if (thenode
->returntype
& RETURNTYPE_CONST
) {
439 /* Constant node: sort it out now */
440 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)thenode
->exe(thenode
, NULL
));
441 anode
->exe
=exe_tostr_null
;
442 anode
->returntype
|= RETURNTYPE_CONST
;
445 anode
->exe
=exe_inttostr
;
449 case RETURNTYPE_BOOL
:
450 if (thenode
->returntype
& RETURNTYPE_CONST
) {
451 /* Constant bool value */
452 if (thenode
->exe(thenode
,NULL
)) {
454 sprintf(cd
->u
.stringbuf
, "1");
456 cd
->u
.stringbuf
[0] = '\0';
458 anode
->exe
=exe_tostr_null
;
459 anode
->returntype
|= RETURNTYPE_CONST
;
461 /* Variable bool value */
462 anode
->exe
=exe_booltostr
;
470 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
471 case RETURNTYPE_STRING
:
472 if (thenode
->returntype
& RETURNTYPE_CONST
) {
473 cd
->u
.val
=strtoul((thenode
->exe
)(thenode
, NULL
), NULL
, 10);
474 anode
->exe
=exe_val_null
;
475 anode
->returntype
|= RETURNTYPE_CONST
;
477 anode
->exe
=exe_strtoint
;
482 case RETURNTYPE_BOOL
:
483 if (thenode
->returntype
& RETURNTYPE_CONST
) {
484 if ((thenode
->exe
)(thenode
,NULL
))
489 anode
->exe
=exe_val_null
;
490 anode
->returntype
|= RETURNTYPE_CONST
;
492 anode
->exe
=exe_booltoint
;
499 case RETURNTYPE_BOOL
:
501 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
502 case RETURNTYPE_STRING
:
503 if (thenode
->returntype
& RETURNTYPE_CONST
) {
504 char *rv
=(char *)((thenode
->exe
)(thenode
, NULL
));
505 if (!rv
|| *rv
=='\0' || (*rv
=='0' && rv
[1]=='\0'))
510 anode
->exe
=exe_val_null
;
511 anode
->returntype
|= RETURNTYPE_CONST
;
513 anode
->exe
=exe_strtobool
;
519 if (thenode
->returntype
& RETURNTYPE_CONST
) {
520 if ((thenode
->exe
)(thenode
,NULL
))
525 anode
->exe
=exe_val_null
;
526 anode
->returntype
|= RETURNTYPE_CONST
;
528 anode
->exe
=exe_inttobool
;
538 /* Literals always return constant strings... */
539 void *literal_exe(struct searchNode
*thenode
, void *theinput
) {
540 return ((sstring
*)thenode
->localdata
)->content
;
543 void literal_free(struct searchNode
*thenode
) {
544 freesstring(thenode
->localdata
);
549 * Given an input string, return a searchNode.
552 struct searchNode
*search_parse(int type
, char *input
) {
553 /* OK, we need to split the input into chunks on spaces and brackets.. */
554 char *argvector
[100];
559 struct searchNode
*thenode
;
561 /* If it starts with a bracket, it's a function call.. */
563 /* Skip past string */
564 for (ch
=input
;*ch
;ch
++);
565 if (*(ch
-1) != ')') {
566 parseError
= "Bracket mismatch!";
572 /* Split further args */
573 i
=-1; /* i = -1 BoW, 0 = inword, 1 = bracket nest depth */
574 j
=0; /* j = current arg */
578 for (ch
=input
;*ch
;ch
++) {
583 } else if (*ch
!= ' ') {
587 } else if (*ch
=='\"') {
599 } else if (*ch
=='\"') {
601 } else if (*ch
==' ') {
604 if(j
>= (sizeof(argvector
) / sizeof(*argvector
))) {
605 parseError
= "Too many arguments";
613 } else if (*ch
=='\"') {
615 } else if (*ch
=='(') {
617 } else if (*ch
==')') {
624 parseError
= "Bracket mismatch!";
628 if (*(ch
-1) == 0) /* if the last character was a space */
629 j
--; /* remove an argument */
631 if (!(cmd
=findcommandintree(searchTree
,argvector
[0],1))) {
632 parseError
= "Unknown command";
635 return ((parseFunc
)cmd
->handler
)(type
, j
, argvector
+1);
640 for (ch
=input
;*ch
;ch
++);
642 if (*(ch
-1) != '\"') {
643 parseError
="Quote mismatch";
652 for (ch
=input
;*ch
;ch
++) {
656 } else if (*ch
=='\\') {
664 if (!(thenode
=(struct searchNode
*)malloc(sizeof(struct searchNode
)))) {
665 parseError
= "malloc: could not allocate memory for this search.";
669 thenode
->localdata
= getsstring(thestring
,512);
670 thenode
->returntype
= RETURNTYPE_CONST
| RETURNTYPE_STRING
;
671 thenode
->exe
= literal_exe
;
672 thenode
->free
= literal_free
;