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