Add version-info to libdcui.
[doldaconnect.git] / daemon / search.c
CommitLineData
d3372da9 1/*
2 * Dolda Connect - Modular multiuser Direct Connect-style client
3 * Copyright (C) 2004 Fredrik Tolf (fredrik@dolda2000.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18*/
19#include <stdlib.h>
20#include <malloc.h>
21#include <wchar.h>
22#include <wctype.h>
23#include <errno.h>
24#include <regex.h>
25#include <string.h>
26#include <time.h>
27
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31#include "utils.h"
32#include "log.h"
33#include "sysevents.h"
34#include "filenet.h"
7dd2a79b 35#include "client.h"
d3372da9 36#include "search.h"
37
38#define TOK_STR 0
39#define TOK_SE 1
40#define TOK_OP 2
41#define TOK_CP 3
42
43struct srchlist
44{
45 struct srchlist *next, *prev;
46 struct search *srch;
47};
48
49struct tok
50{
51 struct tok *next;
52 int type;
53 union
54 {
55 wchar_t *str;
56 struct sexpr *se;
57 } d;
58};
59
60struct reinfo
61{
62 wchar_t *begstr, *endstr, *onestr;
63 struct wcslist *strs;
64};
65
66struct bound
67{
68 int min, max;
69};
70
71static void trycommit(void);
72
73struct search *searches = NULL;
74static struct srchlist *searchqueue = NULL;
75static struct timer *committimer = NULL;
76GCBCHAIN(newsrchcb, struct search *);
77
78wchar_t *regexunquotesimple(wchar_t *re)
79{
80 wchar_t *specials, *buf, *p;
81
82 specials = L"\\^$.*+?[{()|";
83 buf = smalloc((wcslen(re) + 1) * sizeof(wchar_t));
84 p = buf;
85 for(; *re != L'\0'; re++)
86 {
87 if(*re == L'\\')
88 {
89 re++;
90 if(!*re)
91 {
92 *p = L'\0';
93 return(buf);
94 }
95 *(p++) = *re;
96 } else {
97 if(wcschr(specials, *re) != NULL)
98 {
99 free(buf);
100 return(NULL);
101 }
102 *(p++) = *re;
103 }
104 }
105 *p = L'\0';
106 return(buf);
107}
108
109static void freesln(struct wcslist *ln, struct wcslist **list)
110{
111 if(ln->prev != NULL)
112 ln->prev->next = ln->next;
113 if(ln->next != NULL)
114 ln->next->prev = ln->prev;
115 if(ln == *list)
116 *list = ln->next;
117 free(ln->str);
118 free(ln);
119}
120
121void freesl(struct wcslist **list)
122{
123 while(*list != NULL)
124 freesln(*list, list);
125}
126
127static struct wcslist *newsl(struct wcslist **list, wchar_t *str)
128{
129 struct wcslist *ln;
130
131 ln = smalloc(sizeof(*ln));
132 memset(ln, 0, sizeof(*ln));
133 ln->str = swcsdup(str);
134 ln->len = wcslen(str);
135 ln->next = *list;
136 ln->prev = NULL;
137 if(*list != NULL)
138 (*list)->prev = ln;
139 *list = ln;
140 return(ln);
141}
142
143static void slmerge1(struct wcslist **list, wchar_t *str)
144{
145 size_t len;
146 struct wcslist *cur, *next;
147
148 len = wcslen(str);
149 for(cur = *list; cur != NULL; cur = next)
150 {
151 next = cur->next;
152 if(len <= cur->len)
153 {
154 if(((len < cur->len) && wcsexists(cur->str, str)) || !wcscmp(str, cur->str))
155 return;
156 } else if(len > cur->len) {
157 if(wcsexists(str, cur->str))
158 freesln(cur, list);
159 }
160 }
161 newsl(list, str);
162}
163
164void slmergemax(struct wcslist **dest, struct wcslist *src)
165{
166 for(; src != NULL; src = src->next)
167 slmerge1(dest, src->str);
168}
169
170static struct wcslist *makeminlist1(wchar_t *s1, wchar_t *s2)
171{
172 int i;
173 wchar_t *p1, *p2, c;
174 struct wcslist *list;
175
176 list = NULL;
177 for(p1 = s1; *p1 != L'\0'; p1++)
178 {
179 for(p2 = s2; *p2 != L'\0'; p2++)
180 {
181 for(i = 0; (p1[i] != L'\0') && (p2[i] != L'\0') && (towlower(p1[i]) == towlower(p2[i])); i++);
182 if(i > 0)
183 {
184 c = p2[i];
185 p2[i] = L'\0';
186 slmerge1(&list, p2);
187 p2[i] = c;
188 }
189 }
190 }
191 return(list);
192}
193
194struct wcslist *slmergemin(struct wcslist *l1, struct wcslist *l2)
195{
196 struct wcslist *cur1, *cur2, *list, *rlist;
197
198 list = NULL;
199 for(cur1 = l1; cur1 != NULL; cur1 = cur1->next)
200 {
201 for(cur2 = l2; cur2 != NULL; cur2 = cur2->next)
202 {
203 rlist = makeminlist1(cur1->str, cur2->str);
204 slmergemax(&list, rlist);
205 freesl(&rlist);
206 }
207 }
208 return(list);
209}
210
211static struct bound readbound(wchar_t *p, wchar_t **endret)
212{
213 struct bound ret;
214
215 switch(*p)
216 {
217 case L'?':
218 ret.min = 0;
219 ret.max = 1;
220 p++;
221 break;
222 case L'*':
223 ret.min = 0;
224 ret.max = -1;
225 p++;
226 break;
227 case L'+':
228 ret.min = 1;
229 ret.max = -1;
230 p++;
231 break;
232 case L'{':
233 p++;
234 ret.min = wcstol(p, &p, 10);
235 if(*p == L',')
236 {
237 p++;
238 if(*p == L'}')
239 ret.max = -1;
240 else
241 ret.max = wcstol(p, &p, 10);
242 } else {
243 ret.max = ret.min;
244 }
245 if(*p != L'}')
246 {
247 /* Cannot happen in a validated regex... */
248 flog(LOG_WARNING, "BUG? \"Impossible\" case encountered in search.c:readbound() (No `}' after `{'-type bound!");
249 } else {
250 p++;
251 }
252 break;
253 default:
254 ret.min = 1;
255 ret.max = 1;
256 break;
257 }
258 if(endret != NULL)
259 *endret = p;
260 return(ret);
261}
262
263#define fnc(p) do {if((p) != NULL) free(p); (p) = NULL; p ## size = 0; p ## data = 0;} while(0)
264
265static struct reinfo analyzere(wchar_t *re, wchar_t **endret, wchar_t endc)
266{
267 int i, commit, parsealt;
268 struct reinfo ret, sinf;
269 struct bound b;
270 wchar_t *cs, *ns;
271 size_t cssize, csdata, nssize, nsdata, len1, len2, maxlen, minlen;
272 wchar_t beg, c;
273 struct wcslist *list;
274
275 memset(&ret, 0, sizeof(ret));
276 commit = parsealt = 0;
277 beg = 1;
278 cs = ns = NULL;
279 cssize = csdata = nssize = nsdata = 0;
280 while((*re != endc) && !parsealt)
281 {
282 switch(*re)
283 {
284 case L'$':
285 case L'^':
286 re++;
287 commit = 1;
288 break;
289 case L'.':
290 re++;
291 b = readbound(re, &re);
292 if(b.max != 0)
293 commit = 1;
294 break;
295 case L'(':
296 re++;
297 sinf = analyzere(re, &re, L')');
298 re++;
299 b = readbound(re, &re);
300 if(sinf.onestr != NULL)
301 {
302 for(i = 0; i < b.min; i++)
303 {
304 bufcat(cs, sinf.onestr, wcslen(sinf.onestr));
305 bufcat(ns, sinf.onestr, wcslen(sinf.onestr));
306 }
307 if((b.max == -1) || (b.max > b.min))
308 commit = 1;
309 } else {
310 commit = 1;
311 if(b.min > 0)
312 {
313 if(sinf.begstr != NULL)
314 bufcat(cs, sinf.begstr, wcslen(sinf.begstr));
315 if(sinf.endstr != NULL)
316 bufcat(ns, sinf.endstr, wcslen(sinf.endstr));
317 }
318 }
319 if(sinf.begstr != NULL)
320 free(sinf.begstr);
321 if(sinf.endstr != NULL)
322 free(sinf.endstr);
323 if(sinf.onestr != NULL)
324 free(sinf.onestr);
325 if(b.min > 0)
326 slmergemax(&ret.strs, sinf.strs);
327 freesl(&sinf.strs);
328 break;
329 case L'[':
330 c = L'\0';
331 re += 2;
332 /* Must exist in a validated RE... */
333 while(*(re++) != L']');
334 readbound(re, &re);
335 commit = 1;
336 break;
337 case L'|':
338 re++;
339 parsealt = 1;
340 break;
341 case L'\\':
342 re++;
343 /* A validated RE cannot end in a backslash, so fall
344 * through */
345 default:
346 c = *(re++);
347 b = readbound(re, &re);
348 for(i = 0; i < b.min; i++)
349 addtobuf(cs, c);
350 if((b.max == -1) || (b.max > b.min))
351 commit = 1;
352 break;
353 }
354 if(commit)
355 {
356 if(cs != NULL)
357 addtobuf(cs, L'\0');
358 if(beg)
359 {
360 if(cs != NULL)
361 ret.begstr = swcsdup(cs);
362 beg = 0;
363 }
364 if(cs != NULL)
365 slmerge1(&ret.strs, cs);
366 fnc(cs);
367 commit = 0;
368 if(ns != NULL)
369 {
370 cs = swcsdup(ns);
371 cssize = nssize;
372 csdata = nsdata;
373 fnc(ns);
374 }
375 }
376 }
377 if(cs != NULL)
378 {
379 addtobuf(cs, L'\0');
380 if(beg)
381 ret.onestr = swcsdup(cs);
382 else
383 ret.endstr = swcsdup(cs);
384 slmerge1(&ret.strs, cs);
385 fnc(cs);
386 }
387 if(parsealt)
388 {
389 sinf = analyzere(re, &re, endc);
390 list = slmergemin(ret.strs, sinf.strs);
391 freesl(&ret.strs);
392 freesl(&sinf.strs);
393 ret.strs = list;
394 if(sinf.begstr != NULL)
395 {
396 if(ret.begstr != NULL)
397 {
398 for(i = 0; (sinf.begstr[i] != L'\0') && (ret.begstr != L'\0') && (ret.begstr[i] == sinf.begstr[i]); i++);
c74516fb 399 if(i == 0) {
d3372da9 400 free(ret.begstr);
c74516fb 401 ret.begstr = NULL;
402 } else {
d3372da9 403 ret.begstr[i] = L'\0';
c74516fb 404 }
d3372da9 405 }
406 free(sinf.begstr);
407 } else {
408 if(ret.begstr != NULL)
409 {
410 free(ret.begstr);
411 ret.begstr = NULL;
412 }
413 }
414 if(sinf.endstr != NULL)
415 {
416 if(ret.endstr != NULL)
417 {
418 len1 = wcslen(ret.endstr);
419 len2 = wcslen(sinf.endstr);
420 if(len1 < len2)
421 {
422 minlen = len1;
423 maxlen = len2;
424 } else {
425 minlen = len2;
426 maxlen = len1;
427 }
428 for(i = 1; (i <= minlen) && (ret.endstr[len1 - i] == sinf.endstr[len2 - i]); i++);
c74516fb 429 if(i == 1) {
d3372da9 430 free(ret.endstr);
c74516fb 431 ret.endstr = NULL;
432 } else if(i <= maxlen) {
d3372da9 433 wmemmove(ret.endstr, ret.endstr + (len1 - i) + 1, i);
c74516fb 434 }
d3372da9 435 }
436 free(sinf.endstr);
437 } else {
438 if(ret.endstr != NULL)
439 {
440 free(ret.endstr);
441 ret.endstr = NULL;
442 }
443 }
444 if(sinf.onestr != NULL)
445 {
446 if(ret.onestr != NULL)
447 {
448 /* XXX: Comparing beginning and end of the onestrs and
449 * create begstr and endstr if there isn't an exact
450 * match.*/
451 if(wcscmp(ret.onestr, sinf.onestr))
452 {
453 free(ret.onestr);
454 ret.onestr = NULL;
455 }
456 }
457 free(sinf.onestr);
458 } else {
459 if(ret.onestr != NULL)
460 {
461 free(ret.onestr);
462 ret.onestr = NULL;
463 }
464 }
465 }
466 if(endret != NULL)
467 *endret = re;
468 return(ret);
469}
470
471#undef fnc
472
473struct wcslist *regexfindstrings(wchar_t *re)
474{
475 struct reinfo i;
476
477 i = analyzere(re, NULL, L'\0');
478 if(i.begstr != NULL)
479 free(i.begstr);
480 if(i.endstr != NULL)
481 free(i.endstr);
482 if(i.onestr != NULL)
483 free(i.onestr);
484 return(i.strs);
485}
486
487static struct sexpr *newsexpr(void)
488{
489 struct sexpr *sexpr;
490
491 sexpr = smalloc(sizeof(*sexpr));
492 memset(sexpr, 0, sizeof(*sexpr));
493 sexpr->refcount = 1;
494 return(sexpr);
495}
496
497void getsexpr(struct sexpr *sexpr)
498{
499 sexpr->refcount++;
500}
501
502void putsexpr(struct sexpr *sexpr)
503{
504 if(--sexpr->refcount != 0)
505 return;
506 if(sexpr->l != NULL)
507 putsexpr(sexpr->l);
508 if(sexpr->r != NULL)
509 putsexpr(sexpr->r);
510 if((sexpr->op == SOP_NAMERE) || (sexpr->op == SOP_LINKRE))
511 {
512 if(sexpr->d.re.sre != NULL)
513 free(sexpr->d.re.sre);
514 if(sexpr->d.re.inited)
515 regfree(&sexpr->d.re.cre);
516 }
517 if((sexpr->op == SOP_NAMESS) || (sexpr->op == SOP_LINKSS))
518 {
519 if(sexpr->d.s != NULL)
520 free(sexpr->d.s);
521 }
9d8cf743 522 if(sexpr->op == SOP_HASHIS)
523 {
524 if(sexpr->d.hash != NULL)
525 freehash(sexpr->d.hash);
526 }
d3372da9 527 free(sexpr);
528}
529
530static struct tok *newtok(void)
531{
532 struct tok *tok;
533
534 tok = smalloc(sizeof(*tok));
535 memset(tok, 0, sizeof(*tok));
536 tok->next = NULL;
537 return(tok);
538}
539
540static void freetok(struct tok *tok)
541{
542 if((tok->type == TOK_STR) && (tok->d.str != NULL))
543 free(tok->d.str);
544 if((tok->type == TOK_SE) && (tok->d.se != NULL))
545 putsexpr(tok->d.se);
546 free(tok);
547}
548
549static void pushtok(struct tok *tok, struct tok **st)
550{
551 tok->next = *st;
552 *st = tok;
553}
554
555static struct tok *poptok(struct tok **st)
556{
557 struct tok *tok;
558
559 tok = *st;
560 *st = (*st)->next;
561 return(tok);
562}
563
564int calccost(struct sexpr *sexpr)
565{
566 sexpr->tcost = sexpr->cost;
567 if(sexpr->l != NULL)
568 sexpr->tcost += calccost(sexpr->l);
569 if(sexpr->r != NULL)
570 sexpr->tcost += calccost(sexpr->r);
571 return(sexpr->tcost);
572}
573
574struct sexpr *parsesexpr(int argc, wchar_t **argv)
575{
576 int i, done, std;
577 struct tok *st, *tok, *tok2;
578 struct sexpr *sexpr;
579 char *buf;
580 wchar_t *wbuf;
581
582 std = 0;
583 st = NULL;
584 for(i = 0; i < argc; i++)
585 {
586 pushtok(tok = newtok(), &st);
587 tok->type = TOK_STR;
588 tok->d.str = swcsdup(argv[i]);
589 std++;
590 do
591 {
592 done = 1;
593 if((st->type == TOK_STR) && !wcscmp(st->d.str, L"("))
594 {
595 freetok(poptok(&st));
596 pushtok(tok = newtok(), &st);
597 tok->type = TOK_OP;
598 done = 0;
599 } else if((st->type == TOK_STR) && !wcscmp(st->d.str, L")")) {
600 freetok(poptok(&st));
601 pushtok(tok = newtok(), &st);
602 tok->type = TOK_CP;
603 done = 0;
604 } else if((st->type == TOK_STR) && (!wcsncmp(st->d.str, L"N~", 2) || !wcsncmp(st->d.str, L"L~", 2))) {
605 tok2 = poptok(&st);
606 pushtok(tok = newtok(), &st);
607 tok->type = TOK_SE;
608 sexpr = newsexpr();
609 if((wbuf = regexunquotesimple(tok2->d.str + 2)) != NULL)
610 {
611 if(tok2->d.str[0] == L'N')
612 sexpr->op = SOP_NAMESS;
613 else
614 sexpr->op = SOP_LINKSS;
615 sexpr->d.s = wbuf;
616 sexpr->cost = 5;
617 } else {
618 if(tok2->d.str[0] == L'N')
619 sexpr->op = SOP_NAMERE;
620 else
621 sexpr->op = SOP_LINKRE;
622 sexpr->cost = 20;
623 if((buf = icwcstombs(tok2->d.str + 2, "UTF-8")) == NULL)
624 {
625 freetok(tok2);
626 putsexpr(sexpr);
627 goto out_err;
628 }
629 if(regcomp(&sexpr->d.re.cre, buf, REG_EXTENDED | REG_ICASE | REG_NOSUB))
630 {
631 freetok(tok2);
632 free(buf);
633 putsexpr(sexpr);
634 goto out_err;
635 }
636 free(buf);
637 sexpr->d.re.inited = 1;
638 sexpr->d.re.sre = swcsdup(tok2->d.str + 2);
639 }
640 getsexpr(tok->d.se = sexpr);
641 freetok(tok2);
642 putsexpr(sexpr);
643 done = 0;
644 } else if((st->type == TOK_STR) && (!wcsncmp(st->d.str, L"S<", 2) || !wcsncmp(st->d.str, L"S=", 2) || !wcsncmp(st->d.str, L"S>", 2))) {
645 tok2 = poptok(&st);
646 pushtok(tok = newtok(), &st);
647 tok->type = TOK_SE;
648 sexpr = newsexpr();
649 if(tok2->d.str[1] == L'<')
650 sexpr->op = SOP_SIZELT;
651 else if(tok2->d.str[1] == L'=')
652 sexpr->op = SOP_SIZEEQ;
653 else
654 sexpr->op = SOP_SIZEGT;
655 sexpr->d.n = wcstol(tok2->d.str + 2, NULL, 0);
656 sexpr->cost = 0;
657 getsexpr(tok->d.se = sexpr);
658 freetok(tok2);
659 putsexpr(sexpr);
660 done = 0;
9d8cf743 661 } else if((st->type == TOK_STR) && !wcsncmp(st->d.str, L"H=", 2)) {
662 tok2 = poptok(&st);
663 pushtok(tok = newtok(), &st);
664 tok->type = TOK_SE;
665 sexpr = newsexpr();
666 sexpr->op = SOP_HASHIS;
667 if((sexpr->d.hash = parsehash(tok2->d.str + 2)) == NULL)
668 {
669 freetok(tok2);
670 putsexpr(sexpr);
671 goto out_err;
672 }
673 sexpr->cost = 2;
674 getsexpr(tok->d.se = sexpr);
675 freetok(tok2);
676 putsexpr(sexpr);
677 done = 0;
d3372da9 678 } else if((std >= 3) && (st->type == TOK_CP) && (st->next->type == TOK_SE) && (st->next->next->type == TOK_OP)) {
679 freetok(poptok(&st));
680 tok = poptok(&st);
681 freetok(poptok(&st));
682 pushtok(tok, &st);
683 std -= 2;
684 done = 0;
685 } else if((std >= 2) && (st->type == TOK_SE) && (st->next->type == TOK_STR) && !wcscmp(st->next->d.str, L"!")) {
686 sexpr = newsexpr();
687 sexpr->op = SOP_NOT;
688 sexpr->cost = 0;
689 getsexpr(sexpr->l = st->d.se);
690 freetok(poptok(&st));
691 freetok(poptok(&st));
692 pushtok(tok = newtok(), &st);
693 tok->type = TOK_SE;
694 getsexpr(tok->d.se = sexpr);
695 putsexpr(sexpr);
696 std -= 1;
697 done = 0;
698 } else if((std >= 3) && (st->type == TOK_SE) && (st->next->type == TOK_STR) && (!wcscmp(st->next->d.str, L"&") || !wcscmp(st->next->d.str, L"|")) && (st->next->next->type == TOK_SE)) {
699 sexpr = newsexpr();
700 if(!wcscmp(st->next->d.str, L"&"))
701 sexpr->op = SOP_AND;
702 else
703 sexpr->op = SOP_OR;
704 sexpr->cost = 0;
705 getsexpr(sexpr->l = st->next->next->d.se);
706 getsexpr(sexpr->r = st->d.se);
707 freetok(poptok(&st));
708 freetok(poptok(&st));
709 freetok(poptok(&st));
710 pushtok(tok = newtok(), &st);
711 tok->type = TOK_SE;
712 getsexpr(tok->d.se = sexpr);
713 putsexpr(sexpr);
714 std -= 2;
715 done = 0;
716 }
717 } while(!done);
718 }
719 if((st == NULL) || (st->next != NULL) || (st->type != TOK_SE))
720 goto out_err;
721 getsexpr(sexpr = st->d.se);
722 freetok(st);
723 calccost(sexpr);
724 return(sexpr);
725
726 out_err:
727 while(st != NULL)
728 freetok(poptok(&st));
729 return(NULL);
730}
731
732void optsexpr(struct sexpr *sexpr)
733{
734 struct sexpr *buf;
735
736 if((sexpr->l != NULL) && (sexpr->r != NULL))
737 {
738 if(sexpr->l->tcost > sexpr->r->tcost)
739 {
740 buf = sexpr->r;
741 sexpr->r = sexpr->l;
742 sexpr->l = buf;
743 }
744 }
745 if(sexpr->l != NULL)
746 optsexpr(sexpr->l);
747 if(sexpr->r != NULL)
748 optsexpr(sexpr->r);
749}
750
751struct wcslist *findsexprstrs(struct sexpr *sexpr)
752{
753 struct wcslist *list, *l1, *l2;
754
755 list = NULL;
756 switch(sexpr->op)
757 {
758 case SOP_AND:
759 list = findsexprstrs(sexpr->l);
760 l1 = findsexprstrs(sexpr->r);
761 slmergemax(&list, l1);
762 freesl(&l1);
763 break;
764 case SOP_OR:
765 l1 = findsexprstrs(sexpr->l);
766 l2 = findsexprstrs(sexpr->r);
767 list = slmergemin(l1, l2);
768 freesl(&l1);
769 freesl(&l2);
770 break;
d3372da9 771 case SOP_NAMERE:
772 case SOP_LINKRE:
773 list = regexfindstrings(sexpr->d.re.sre);
774 break;
775 case SOP_NAMESS:
776 case SOP_LINKSS:
777 slmerge1(&list, sexpr->d.s);
778 break;
779 default:
780 break;
781 }
782 return(list);
783}
784
785static void unlinksqueue(struct srchlist *ln)
786{
787 if(ln->prev != NULL)
788 ln->prev->next = ln->next;
789 if(ln->next != NULL)
790 ln->next->prev = ln->prev;
791 if(ln == searchqueue)
792 searchqueue = ln->next;
793 free(ln);
794}
795
796static void ctexpire(int cancelled, void *data)
797{
798 committimer = NULL;
799 if(!cancelled)
800 trycommit();
801}
802
803static void estimatequeue(void)
804{
805 struct srchlist *cur;
806 struct srchfnnlist *ln;
807 time_t now, start;
808
809 if(searchqueue == NULL)
810 return;
811 start = now = time(NULL);
812 for(ln = searchqueue->srch->fnl; ln != NULL; ln = ln->next)
813 {
814 if((ln->fn->lastsrch != 0) && (ln->fn->lastsrch + ln->fn->srchwait > start))
815 start = ln->fn->lastsrch + ln->fn->srchwait;
816 }
817 if(start != searchqueue->srch->eta)
818 {
819 searchqueue->srch->eta = start;
820 CBCHAINDOCB(searchqueue->srch, search_eta, searchqueue->srch);
821 }
822 for(cur = searchqueue->next; cur != NULL; cur = cur->next)
823 {
824 now = start;
825 for(ln = cur->srch->fnl; ln != NULL; ln = ln->next)
826 {
827 if(now + ln->fn->srchwait > start)
828 start = now + ln->fn->srchwait;
829 }
830 if(start != cur->srch->eta)
831 {
832 cur->srch->eta = start;
833 CBCHAINDOCB(cur->srch, search_eta, cur->srch);
834 }
835 }
836 if((committimer == NULL) || ((time_t)committimer->at != searchqueue->srch->eta))
837 {
838 if(committimer != NULL)
839 canceltimer(committimer);
840 committimer = timercallback(searchqueue->srch->eta, ctexpire, NULL);
841 }
842}
843
844struct search *findsearch(int id)
845{
846 struct search *srch;
847
848 for(srch = searches; srch != NULL; srch = srch->next)
849 {
850 if(srch->id == id)
851 break;
852 }
853 return(srch);
854}
855
856struct search *newsearch(wchar_t *owner, struct sexpr *sexpr)
857{
858 struct search *srch;
859 static int id = 0;
860
861 srch = smalloc(sizeof(*srch));
862 memset(srch, 0, sizeof(*srch));
863 srch->id = id++;
864 srch->owner = swcsdup(owner);
865 if((srch->sexpr = sexpr) != NULL)
866 getsexpr(srch->sexpr);
867 CBCHAININIT(srch, search_eta);
868 CBCHAININIT(srch, search_commit);
869 CBCHAININIT(srch, search_result);
870 CBCHAININIT(srch, search_destroy);
871 srch->next = searches;
872 srch->prev = NULL;
873 if(searches != NULL)
874 searches->prev = srch;
875 searches = srch;
876 return(srch);
877}
878
879static void srchexpire(int cancelled, struct search *srch)
880{
881 srch->freetimer = NULL;
882 if(!cancelled)
883 freesearch(srch);
884}
885
886static void trycommit(void)
887{
888 struct srchfnnlist *ln;
889 struct search *srch;
890 time_t now;
891
892 if(searchqueue == NULL)
893 return;
894 srch = searchqueue->srch;
895 now = time(NULL);
896 for(ln = srch->fnl; ln != NULL; ln = ln->next)
897 {
898 if(now < ln->fn->lastsrch + ln->fn->srchwait)
899 break;
900 }
901 if(ln != NULL)
902 return;
903 unlinksqueue(searchqueue);
904 srch->state = SRCH_RUN;
905 srch->eta = time(NULL);
906 srch->committime = ntime();
907 srch->freetimer = timercallback(ntime() + 300, (void (*)(int, void *))srchexpire, srch);
908 CBCHAINDOCB(srch, search_commit, srch);
909 for(ln = srch->fnl; ln != NULL; ln = ln->next)
910 fnetsearch(ln->fn, srch, ln);
911 estimatequeue();
912}
913
914void freesearch(struct search *srch)
915{
916 struct srchfnnlist *ln;
917 struct srchlist *sln;
918
919 if(srch->prev != NULL)
920 srch->prev->next = srch->next;
921 if(srch->next != NULL)
922 srch->next->prev = srch->prev;
923 if(srch == searches)
924 searches = srch->next;
925 estimatequeue();
926 if(srch->freetimer != NULL)
927 canceltimer(srch->freetimer);
928 CBCHAINDOCB(srch, search_destroy, srch);
929 CBCHAINFREE(srch, search_eta);
930 CBCHAINFREE(srch, search_commit);
931 CBCHAINFREE(srch, search_result);
932 CBCHAINFREE(srch, search_destroy);
933 while(srch->results != NULL)
934 freesrchres(srch->results);
935 for(sln = searchqueue; sln != NULL; sln = sln->next)
936 {
937 if(sln->srch == srch)
938 {
939 unlinksqueue(sln);
940 break;
941 }
942 }
943 while(srch->fnl != NULL)
944 {
945 ln = srch->fnl;
946 srch->fnl = ln->next;
947 CBCHAINDOCB(ln, searchfnl_destroy, ln);
948 CBCHAINFREE(ln, searchfnl_destroy);
949 putfnetnode(ln->fn);
950 free(ln);
951 }
952 if(srch->sexpr != NULL)
953 putsexpr(srch->sexpr);
954 if(srch->owner != NULL)
955 free(srch->owner);
956 free(srch);
957}
958
959void searchaddfn(struct search *srch, struct fnetnode *fn)
960{
961 struct srchfnnlist *ln;
962
963 for(ln = srch->fnl; ln != NULL; ln = ln->next)
964 {
965 if(ln->fn == fn)
966 return;
967 }
968 ln = smalloc(sizeof(*ln));
969 memset(ln, 0, sizeof(*ln));
970 getfnetnode(ln->fn = fn);
971 CBCHAININIT(ln, searchfnl_destroy);
972 ln->next = srch->fnl;
973 srch->fnl = ln;
974}
975
976static void linksearch(struct search *srch, struct srchlist *prev)
977{
978 struct srchlist *new;
979
980 new = smalloc(sizeof(*new));
981 new->srch = srch;
982 if(prev == NULL)
983 {
984 new->prev = NULL;
985 new->next = searchqueue;
986 if(searchqueue != NULL)
987 searchqueue->prev = new;
988 searchqueue = new;
989 } else {
990 new->prev = prev;
991 if((new->next = prev->next) != NULL)
992 new->next->prev = new;
993 prev->next = new;
994 }
995 GCBCHAINDOCB(newsrchcb, srch);
996 estimatequeue();
997}
998
999/*
1000 * queuesearch is also the "scheduler" function - it finds a suitable
1001 * place in the queue for the new search. I'll make a weak attempt at
1002 * describing the algorithm:
1003 * First, we find the first search that doesn't have a lower priority
1004 * than this one. If there is no such, we just link this one onto the
1005 * end of the queue.
1006 * Then, if we have a search of this priority in the queue with the
1007 * same owner as the new search, we set lastmine to the search after
1008 * that one, otherwise, lastmine is the first search of this
1009 * priority. If lastmine is discovered either to not exist (that is,
1010 * our last search is at the end of the queue), or to be of lower
1011 * priority (higher number), we link it in at the appropriate end.
1012 * Then, we find the next search of the same priority and owner as
1013 * lastmine, and link this search in before it. That should yield a
1014 * 'round-robin-like' scheduling within priority boundaries. I think.
1015 */
1016void queuesearch(struct search *srch)
1017{
1018 struct srchlist *cur, *lastmine, *prev;
1019 wchar_t *nexto;
1020
1021 for(prev = NULL, cur = searchqueue; cur != NULL; prev = cur, cur = cur->next)
1022 {
1023 if(cur->srch->prio >= srch->prio)
1024 break;
1025 }
1026 if(cur == NULL)
1027 {
1028 linksearch(srch, prev);
1029 return;
1030 }
1031 lastmine = cur;
1032 for(; cur != NULL; prev = cur, cur = cur->next)
1033 {
1034 if(!wcscmp(cur->srch->owner, srch->owner))
1035 lastmine = cur->next;
1036 if(cur->srch->prio > srch->prio)
1037 break;
1038 }
1039 if((lastmine == NULL) || (lastmine->srch->prio > srch->prio))
1040 {
1041 linksearch(srch, prev);
1042 return;
1043 }
1044 nexto = lastmine->srch->owner;
1045 for(cur = lastmine->next; cur != NULL; prev = cur, cur = cur->next)
1046 {
1047 if(!wcscmp(cur->srch->owner, nexto))
1048 break;
1049 if(cur->srch->prio > srch->prio)
1050 break;
1051 }
1052 if(cur == NULL)
1053 {
1054 linksearch(srch, prev);
1055 return;
1056 }
1057 linksearch(srch, prev);
1058}
1059
1060static int srisvalid(struct srchres *sr, struct sexpr *sexpr)
1061{
1062 int ret;
1063 char *buf;
1064 wchar_t *p;
1065
1066 switch(sexpr->op)
1067 {
1068 case SOP_FALSE:
1069 return(0);
1070 case SOP_TRUE:
1071 return(1);
1072 case SOP_AND:
1073 if(!srisvalid(sr, sexpr->l))
1074 return(0);
1075 return(srisvalid(sr, sexpr->r));
1076 case SOP_OR:
1077 if(srisvalid(sr, sexpr->l))
1078 return(1);
1079 return(srisvalid(sr, sexpr->r));
1080 case SOP_NOT:
1081 return(!srisvalid(sr, sexpr->l));
1082 case SOP_NAMERE:
1083 if((buf = icwcstombs(sr->filename, "UTF-8")) == NULL)
1084 return(0);
1085 ret = regexec(&sexpr->d.re.cre, buf, 0, NULL, 0);
1086 free(buf);
1087 return(!ret);
1088 case SOP_LINKRE:
1089 p = sr->filename;
1090 if(sr->fnet->filebasename != NULL)
1091 p = sr->fnet->filebasename(p);
1092 if((buf = icwcstombs(p, "UTF-8")) == NULL)
1093 return(0);
1094 ret = regexec(&sexpr->d.re.cre, buf, 0, NULL, 0);
1095 free(buf);
1096 return(!ret);
1097 case SOP_NAMESS:
1098 return(wcsexists(sr->filename, sexpr->d.s));
1099 case SOP_LINKSS:
1100 p = sr->filename;
1101 if(sr->fnet->filebasename != NULL)
1102 p = sr->fnet->filebasename(p);
1103 return(wcsexists(p, sexpr->d.s));
1104 case SOP_SIZELT:
1105 return(sr->size < sexpr->d.n);
1106 case SOP_SIZEEQ:
1107 return(sr->size == sexpr->d.n);
1108 case SOP_SIZEGT:
1109 return(sr->size > sexpr->d.n);
9d8cf743 1110 case SOP_HASHIS:
1111 if(sr->hash == NULL)
1112 return(0);
1113 return(hashcmp(sr->hash, sexpr->d.hash));
d3372da9 1114 }
1115 return(0);
1116}
1117
1118struct srchres *newsrchres(struct fnet *fnet, wchar_t *filename, wchar_t *peerid)
1119{
1120 struct srchres *sr;
1121
1122 sr = smalloc(sizeof(*sr));
1123 memset(sr, 0, sizeof(*sr));
1124 sr->size = -1;
1125 sr->slots = -1;
1126 sr->fnet = fnet;
1127 sr->filename = swcsdup(filename);
1128 sr->peerid = swcsdup(peerid);
1129 return(sr);
1130}
1131
1132void freesrchres(struct srchres *sr)
1133{
1134 if(sr->next != NULL)
1135 sr->next->prev = sr->prev;
1136 if(sr->prev != NULL)
1137 sr->prev->next = sr->next;
1138 if(sr->srch != NULL)
1139 {
1140 if(sr->srch->results == sr)
1141 sr->srch->results = sr->next;
1142 sr->srch->numres--;
1143 }
7dd2a79b 1144 if(sr->hash != NULL)
1145 freehash(sr->hash);
d3372da9 1146 if(sr->filename != NULL)
1147 free(sr->filename);
1148 if(sr->peerid != NULL)
1149 free(sr->peerid);
1150 if(sr->peernick != NULL)
1151 free(sr->peernick);
1152 if(sr->fn != NULL)
1153 putfnetnode(sr->fn);
1154 free(sr);
1155}
1156
1157struct srchres *dupsrchres(struct srchres *sr)
1158{
1159 struct srchres *new;
1160
1161 new = smalloc(sizeof(*new));
1162 memset(new, 0, sizeof(*new));
1163 new->size = sr->size;
1164 new->slots = sr->slots;
1165 new->fnet = sr->fnet;
1166 if(sr->peerid != NULL)
1167 new->peerid = swcsdup(sr->peerid);
1168 if(sr->peernick != NULL)
1169 new->peernick = swcsdup(sr->peernick);
1170 if(sr->filename != NULL)
1171 new->filename = swcsdup(sr->filename);
1172 if(sr->fn != NULL)
1173 getfnetnode(new->fn = sr->fn);
7dd2a79b 1174 if(sr->hash != NULL)
1175 new->hash = duphash(sr->hash);
d3372da9 1176 return(new);
1177}
1178
1179static void linksr(struct search *srch, struct srchres *sr)
1180{
1181 sr->prev = NULL;
1182 sr->next = srch->results;
1183 if(srch->results != NULL)
1184 srch->results->prev = sr;
1185 srch->results = sr;
1186 sr->srch = srch;
1187 sr->time = ntime() - srch->committime;
1188 srch->numres++;
1189}
1190
1191void submitsrchres(struct srchres *sr)
1192{
1193 struct search *srch;
1194 struct srchfnnlist *ln;
1195 struct srchres *dsr;
1196
1197 for(srch = searches; srch != NULL; srch = srch->next)
1198 {
1199 if(srch->state == SRCH_RUN)
1200 {
1201 if(!srisvalid(sr, srch->sexpr))
1202 continue;
1203 for(ln = srch->fnl; ln != NULL; ln = ln->next)
1204 {
1205 if(((sr->fn != NULL) && (ln->fn == sr->fn)) || ((sr->fn == NULL) && (sr->fnet == ln->fn->fnet)))
1206 break;
1207 }
1208 if(ln != NULL)
1209 {
1210 dsr = dupsrchres(sr);
1211 linksr(srch, dsr);
1212 CBCHAINDOCB(srch, search_result, srch, dsr);
1213 }
1214 }
1215 }
1216}