Add version-info to libdcui.
[doldaconnect.git] / daemon / search.c
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"
35 #include "client.h"
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
43 struct srchlist
44 {
45     struct srchlist *next, *prev;
46     struct search *srch;
47 };
48
49 struct tok
50 {
51     struct tok *next;
52     int type;
53     union
54     {
55         wchar_t *str;
56         struct sexpr *se;
57     } d;
58 };
59
60 struct reinfo
61 {
62     wchar_t *begstr, *endstr, *onestr;
63     struct wcslist *strs;
64 };
65
66 struct bound
67 {
68     int min, max;
69 };
70
71 static void trycommit(void);
72
73 struct search *searches = NULL;
74 static struct srchlist *searchqueue = NULL;
75 static struct timer *committimer = NULL;
76 GCBCHAIN(newsrchcb, struct search *);
77
78 wchar_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
109 static 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
121 void freesl(struct wcslist **list)
122 {
123     while(*list != NULL)
124         freesln(*list, list);
125 }
126
127 static 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
143 static 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
164 void slmergemax(struct wcslist **dest, struct wcslist *src)
165 {
166     for(; src != NULL; src = src->next)
167         slmerge1(dest, src->str);
168 }
169
170 static 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
194 struct 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
211 static 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
265 static 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++);
399                 if(i == 0) {
400                     free(ret.begstr);
401                     ret.begstr = NULL;
402                 } else {
403                     ret.begstr[i] = L'\0';
404                 }
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++);
429                 if(i == 1) {
430                     free(ret.endstr);
431                     ret.endstr = NULL;
432                 } else if(i <= maxlen) {
433                     wmemmove(ret.endstr, ret.endstr + (len1 - i) + 1, i);
434                 }
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
473 struct 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
487 static 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
497 void getsexpr(struct sexpr *sexpr)
498 {
499     sexpr->refcount++;
500 }
501
502 void 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     }
522     if(sexpr->op == SOP_HASHIS)
523     {
524         if(sexpr->d.hash != NULL)
525             freehash(sexpr->d.hash);
526     }
527     free(sexpr);
528 }
529
530 static 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
540 static 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
549 static void pushtok(struct tok *tok, struct tok **st)
550 {
551     tok->next = *st;
552     *st = tok;
553 }
554
555 static struct tok *poptok(struct tok **st)
556 {
557     struct tok *tok;
558     
559     tok = *st;
560     *st = (*st)->next;
561     return(tok);
562 }
563
564 int 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
574 struct 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;
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;
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
732 void 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
751 struct 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;
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
785 static 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
796 static void ctexpire(int cancelled, void *data)
797 {
798     committimer = NULL;
799     if(!cancelled)
800         trycommit();
801 }
802
803 static 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
844 struct 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
856 struct 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
879 static void srchexpire(int cancelled, struct search *srch)
880 {
881     srch->freetimer = NULL;
882     if(!cancelled)
883         freesearch(srch);
884 }
885
886 static 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
914 void 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
959 void 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
976 static 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  */
1016 void 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
1060 static 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);
1110     case SOP_HASHIS:
1111         if(sr->hash == NULL)
1112             return(0);
1113         return(hashcmp(sr->hash, sexpr->d.hash));
1114     }
1115     return(0);
1116 }
1117
1118 struct 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
1132 void 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     }
1144     if(sr->hash != NULL)
1145         freehash(sr->hash);
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
1157 struct 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);
1174     if(sr->hash != NULL)
1175         new->hash = duphash(sr->hash);
1176     return(new);
1177 }
1178
1179 static 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
1191 void 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 }