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