More work on relational op.
[idzebra-moved-to-github.git] / index / zrpn.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zrpn.c,v $
7  * Revision 1.30  1995-10-16 09:32:38  adam
8  * More work on relational op.
9  *
10  * Revision 1.29  1995/10/13  16:01:49  adam
11  * Work on relations.
12  *
13  * Revision 1.28  1995/10/13  12:26:43  adam
14  * Optimization of truncation.
15  *
16  * Revision 1.27  1995/10/12  17:07:22  adam
17  * Truncation works.
18  *
19  * Revision 1.26  1995/10/12  12:40:54  adam
20  * Bug fixes in rpn_prox.
21  *
22  * Revision 1.25  1995/10/10  13:59:24  adam
23  * Function rset_open changed its wflag parameter to general flags.
24  *
25  * Revision 1.24  1995/10/09  16:18:37  adam
26  * Function dict_lookup_grep got extra client data parameter.
27  *
28  * Revision 1.23  1995/10/06  16:33:37  adam
29  * Use attribute mappings.
30  *
31  * Revision 1.22  1995/10/06  15:07:39  adam
32  * Structure 'local-number' handled.
33  *
34  * Revision 1.21  1995/10/06  13:52:06  adam
35  * Bug fixes. Handler may abort further scanning.
36  *
37  * Revision 1.20  1995/10/06  11:06:33  adam
38  * Scan entries include 'occurrences' now.
39  *
40  * Revision 1.19  1995/10/06  10:43:56  adam
41  * Scan added. 'occurrences' in scan entries not set yet.
42  *
43  * Revision 1.18  1995/10/04  16:57:20  adam
44  * Key input and merge sort in one pass.
45  *
46  * Revision 1.17  1995/10/04  12:55:17  adam
47  * Bug fix in ranked search. Use=Any keys inserted.
48  *
49  * Revision 1.16  1995/10/02  16:24:40  adam
50  * Use attribute actually used in search requests.
51  *
52  * Revision 1.15  1995/10/02  15:18:52  adam
53  * New member in recRetrieveCtrl: diagnostic.
54  *
55  * Revision 1.14  1995/09/28  12:10:32  adam
56  * Bug fixes. Field prefix used in queries.
57  *
58  * Revision 1.13  1995/09/18  14:17:50  adam
59  * Minor changes.
60  *
61  * Revision 1.12  1995/09/15  14:45:21  adam
62  * Retrieve control.
63  * Work on truncation.
64  *
65  * Revision 1.11  1995/09/14  11:53:27  adam
66  * First work on regular expressions/truncations.
67  *
68  * Revision 1.10  1995/09/11  15:23:26  adam
69  * More work on relevance search.
70  *
71  * Revision 1.9  1995/09/11  13:09:35  adam
72  * More work on relevance feedback.
73  *
74  * Revision 1.8  1995/09/08  14:52:27  adam
75  * Minor changes. Dictionary is lower case now.
76  *
77  * Revision 1.7  1995/09/07  13:58:36  adam
78  * New parameter: result-set file descriptor (RSFD) to support multiple
79  * positions within the same result-set.
80  * Boolean operators: and, or, not implemented.
81  * Result-set references.
82  *
83  * Revision 1.6  1995/09/06  16:11:18  adam
84  * Option: only one word key per file.
85  *
86  * Revision 1.5  1995/09/06  10:33:04  adam
87  * More work on present. Some log messages removed.
88  *
89  * Revision 1.4  1995/09/05  15:28:40  adam
90  * More work on search engine.
91  *
92  * Revision 1.3  1995/09/04  15:20:22  adam
93  * Minor changes.
94  *
95  * Revision 1.2  1995/09/04  12:33:43  adam
96  * Various cleanup. YAZ util used instead.
97  *
98  * Revision 1.1  1995/09/04  09:10:40  adam
99  * More work on index add/del/update.
100  * Merge sort implemented.
101  * Initial work on z39 server.
102  *
103  */
104 #include <stdio.h>
105 #include <assert.h>
106 #include <unistd.h>
107
108 #include "zserver.h"
109 #include <attribute.h>
110
111 #include <rsisam.h>
112 #include <rstemp.h>
113 #include <rsnull.h>
114 #include <rsbool.h>
115 #include <rsrel.h>
116
117 int index_word_prefix_map (char *string, oid_value attrSet, int attrUse)
118 {
119     attent *attp;
120
121     logf (LOG_DEBUG, "oid_value attrSet = %d, attrUse = %d", attrSet, attrUse);
122     attp = att_getentbyatt (attrSet, attrUse);
123     if (!attp)
124         return -1;
125     logf (LOG_DEBUG, "ord=%d", attp->attset_ordinal);
126     return index_word_prefix (string, attp->attset_ordinal,
127                               attp->local_attribute);
128 }
129
130 /*
131  * attr_print: log attributes
132  */
133 static void attr_print (Z_AttributesPlusTerm *t)
134 {
135     int of, i;
136     for (of = 0; of < t->num_attributes; of++)
137     {
138         Z_AttributeElement *element;
139         element = t->attributeList[of];
140
141         switch (element->which) 
142         {
143         case Z_AttributeValue_numeric:
144             logf (LOG_DEBUG, "attributeType=%d value=%d", 
145                   *element->attributeType,
146                   *element->value.numeric);
147             break;
148         case Z_AttributeValue_complex:
149             logf (LOG_DEBUG, "attributeType=%d complex", 
150                   *element->attributeType);
151             for (i = 0; i<element->value.complex->num_list; i++)
152             {
153                 if (element->value.complex->list[i]->which ==
154                     Z_StringOrNumeric_string)
155                     logf (LOG_DEBUG, "   string: '%s'",
156                           element->value.complex->list[i]->u.string);
157                 else if (element->value.complex->list[i]->which ==
158                          Z_StringOrNumeric_numeric)
159                     logf (LOG_DEBUG, "   numeric: '%d'",
160                           *element->value.complex->list[i]->u.numeric);
161             }
162             break;
163         default:
164             assert (0);
165         }
166     }
167 }
168
169 typedef struct {
170     int type;
171     int major;
172     int minor;
173     Z_AttributesPlusTerm *zapt;
174 } AttrType;
175
176 static int attr_find (AttrType *src, oid_value *attributeSetP)
177 {
178     while (src->major < src->zapt->num_attributes)
179     {
180         Z_AttributeElement *element;
181
182         element = src->zapt->attributeList[src->major];
183         if (src->type == *element->attributeType)
184         {
185             switch (element->which) 
186             {
187             case Z_AttributeValue_numeric:
188                 ++(src->major);
189                 if (element->attributeSet && attributeSetP)
190                 {
191                     oident *attrset;
192
193                     attrset = oid_getentbyoid (element->attributeSet);
194                     *attributeSetP = attrset->value;
195                 }
196                 return *element->value.numeric;
197                 break;
198             case Z_AttributeValue_complex:
199                 if (src->minor >= element->value.complex->num_list ||
200                     element->value.complex->list[src->minor]->which !=  
201                     Z_StringOrNumeric_numeric)
202                     break;
203                 ++(src->minor);
204                 if (element->attributeSet && attributeSetP)
205                 {
206                     oident *attrset;
207
208                     attrset = oid_getentbyoid (element->attributeSet);
209                     *attributeSetP = attrset->value;
210                 }
211                 return *element->value.complex->list[src->minor-1]->u.numeric;
212             default:
213                 assert (0);
214             }
215         }
216         ++(src->major);
217     }
218     return -1;
219 }
220
221 static void attr_init (AttrType *src, Z_AttributesPlusTerm *zapt,
222                        int type)
223 {
224     src->zapt = zapt;
225     src->type = type;
226     src->major = 0;
227     src->minor = 0;
228 }
229
230 struct trunc_info {
231     int  *ptr;
232     int  *indx;
233     char **heap;
234     int  heapnum;
235     int  (*cmp)(const void *p1, const void *p2);
236     int  keysize;
237     char *swapbuf;
238     char *tmpbuf;
239     char *buf;
240 };
241
242 static void heap_swap (struct trunc_info *ti, int i1, int i2)
243 {
244     int swap;
245
246     swap = ti->ptr[i1];
247     ti->ptr[i1] = ti->ptr[i2];
248     ti->ptr[i2] = swap;
249 }
250
251 static void heap_delete (struct trunc_info *ti)
252 {
253     int cur = 1, child = 2;
254
255     heap_swap (ti, 1, ti->heapnum--);
256     while (child <= ti->heapnum) {
257         if (child < ti->heapnum &&
258             (*ti->cmp)(ti->heap[ti->ptr[child]],
259                        ti->heap[ti->ptr[1+child]]) > 0)
260             child++;
261         if ((*ti->cmp)(ti->heap[ti->ptr[cur]],
262                        ti->heap[ti->ptr[child]]) > 0)
263         {
264             heap_swap (ti, cur, child);
265             cur = child;
266             child = 2*cur;
267         }
268         else
269             break;
270     }
271 }
272
273 static void heap_insert (struct trunc_info *ti, const char *buf, int indx)
274 {
275     int cur, parent;
276
277     cur = ++(ti->heapnum);
278     memcpy (ti->heap[ti->ptr[cur]], buf, ti->keysize);
279     ti->indx[ti->ptr[cur]] = indx;
280     parent = cur/2;
281     while (parent && (*ti->cmp)(ti->heap[ti->ptr[parent]],
282                                 ti->heap[ti->ptr[cur]]) > 0)
283     {
284         heap_swap (ti, cur, parent);
285         cur = parent;
286         parent = cur/2;
287     }
288 }
289
290 static
291 struct trunc_info *heap_init (int size, int key_size,
292                               int (*cmp)(const void *p1, const void *p2))
293 {
294     struct trunc_info *ti = xmalloc (sizeof(*ti));
295     int i;
296
297     ++size;
298     ti->heapnum = 0;
299     ti->keysize = key_size;
300     ti->cmp = cmp;
301     ti->indx = xmalloc (size * sizeof(*ti->indx));
302     ti->heap = xmalloc (size * sizeof(*ti->heap));
303     ti->ptr = xmalloc (size * sizeof(*ti->ptr));
304     ti->swapbuf = xmalloc (ti->keysize);
305     ti->tmpbuf = xmalloc (ti->keysize);
306     ti->buf = xmalloc (size * ti->keysize);
307     for (i = size; --i >= 0; )
308     {
309         ti->ptr[i] = i;
310         ti->heap[i] = ti->buf + ti->keysize * i;
311     }
312     return ti;
313 }
314
315 static void heap_close (struct trunc_info *ti)
316 {
317     xfree (ti->ptr);
318     xfree (ti->indx);
319     xfree (ti->heap);
320     xfree (ti->swapbuf);
321     xfree (ti->tmpbuf);
322     xfree (ti);
323 }
324
325 static RSET rset_trunc_r (ISAM isam, ISAM_P *isam_p, int from, int to,
326                          int merge_chunk)
327 {
328     RSET result; 
329     RSFD result_rsfd;
330     rset_temp_parms parms;
331
332     parms.key_size = sizeof(struct it_key);
333     result = rset_create (rset_kind_temp, &parms);
334     result_rsfd = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
335
336     if (to - from > merge_chunk)
337     {
338         RSFD *rsfd;
339         RSET *rset;
340         int i, i_add = (to-from)/merge_chunk + 1;
341         struct trunc_info *ti;
342         int rscur = 0;
343         int rsmax = (to-from)/i_add + 1;
344         
345         rset = xmalloc (sizeof(*rset) * rsmax);
346         rsfd = xmalloc (sizeof(*rsfd) * rsmax);
347         
348         for (i = from; i < to; i += i_add)
349         {
350             if (i_add <= to - i)
351                 rset[rscur] = rset_trunc_r (isam, isam_p, i, i+i_add,
352                                             merge_chunk);
353             else
354                 rset[rscur] = rset_trunc_r (isam, isam_p, i, to,
355                                             merge_chunk);
356             rscur++;
357         }
358         ti = heap_init (rscur, sizeof(struct it_key), key_compare);
359         for (i = rscur; --i >= 0; )
360         {
361             rsfd[i] = rset_open (rset[i], RSETF_READ|RSETF_SORT_SYSNO);
362             if (rset_read (rset[i], rsfd[i], ti->tmpbuf))
363                 heap_insert (ti, ti->tmpbuf, i);
364             else
365             {
366                 rset_close (rset[i], rsfd[i]);
367                 rset_delete (rset[i]);
368             }
369         }
370         while (ti->heapnum)
371         {
372             int n = ti->indx[ti->ptr[1]];
373
374             rset_write (result, result_rsfd, ti->heap[ti->ptr[1]]);
375
376             while (1)
377             {
378                 if (!rset_read (rset[n], rsfd[n], ti->tmpbuf))
379                 {
380                     heap_delete (ti);
381                     rset_close (rset[n], rsfd[n]);
382                     rset_delete (rset[n]);
383                     break;
384                 }
385                 if ((*ti->cmp)(ti->tmpbuf, ti->heap[ti->ptr[1]]) > 1)
386                 {
387                     heap_delete (ti);
388                     heap_insert (ti, ti->tmpbuf, n);
389                     break;
390                 }
391             }
392         }
393         xfree (rset);
394         xfree (rsfd);
395         heap_close (ti);
396     }
397     else
398     {
399         ISPT *ispt;
400         int i;
401         struct trunc_info *ti;
402
403         ispt = xmalloc (sizeof(*ispt) * (to-from));
404
405         ti = heap_init (to-from, sizeof(struct it_key),
406                         key_compare);
407         for (i = to-from; --i >= 0; )
408         {
409             ispt[i] = is_position (isam, isam_p[from+i]);
410             if (is_readkey (ispt[i], ti->tmpbuf))
411                 heap_insert (ti, ti->tmpbuf, i);
412             else
413                 is_pt_free (ispt[i]);
414         }
415         while (ti->heapnum)
416         {
417             int n = ti->indx[ti->ptr[1]];
418
419             rset_write (result, result_rsfd, ti->heap[ti->ptr[1]]);
420 #if 0
421 /* section that preserve all keys */
422             heap_delete (ti);
423             if (is_readkey (ispt[n], ti->tmpbuf))
424                 heap_insert (ti, ti->tmpbuf, n);
425             else
426                 is_pt_free (ispt[n]);
427 #else
428 /* section that preserve all keys with unique sysnos */
429             while (1)
430             {
431                 if (!is_readkey (ispt[n], ti->tmpbuf))
432                 {
433                     heap_delete (ti);
434                     is_pt_free (ispt[n]);
435                     break;
436                 }
437                 if ((*ti->cmp)(ti->tmpbuf, ti->heap[ti->ptr[1]]) > 1)
438                 {
439                     heap_delete (ti);
440                     heap_insert (ti, ti->tmpbuf, n);
441                     break;
442                 }
443             }
444 #endif
445         }
446         heap_close (ti);
447         xfree (ispt);
448     }
449     rset_close (result, result_rsfd);
450     return result;
451 }
452
453 static int isam_trunc_cmp (const void *p1, const void *p2)
454 {
455     ISAM_P i1 = *(ISAM_P*) p1;
456     ISAM_P i2 = *(ISAM_P*) p2;
457     int d;
458
459     d = is_type (i1) - is_type (i2);
460     if (d)
461         return d;
462     return is_block (i1) - is_block (i2);
463 }
464
465 static RSET rset_trunc (ISAM isam, ISAM_P *isam_p, int no)
466 {
467
468     qsort (isam_p, no, sizeof(*isam_p), isam_trunc_cmp);
469     return rset_trunc_r (isam, isam_p, 0, no, 100);
470 }
471
472 struct grep_info {
473     ISAM_P *isam_p_buf;
474     int isam_p_size;
475     int isam_p_indx;
476 };
477
478 static void add_isam_p (const char *info, struct grep_info *p)
479 {
480     if (p->isam_p_indx == p->isam_p_size)
481     {
482         ISAM_P *new_isam_p_buf;
483         
484         p->isam_p_size = 2*p->isam_p_size + 100;
485         new_isam_p_buf = xmalloc (sizeof(*new_isam_p_buf) *
486                                   p->isam_p_size);
487         if (p->isam_p_buf)
488         {
489             memcpy (new_isam_p_buf, p->isam_p_buf,
490                     p->isam_p_indx * sizeof(*p->isam_p_buf));
491             xfree (p->isam_p_buf);
492         }
493         p->isam_p_buf = new_isam_p_buf;
494     }
495     assert (*info == sizeof(*p->isam_p_buf));
496     memcpy (p->isam_p_buf + p->isam_p_indx, info+1, sizeof(*p->isam_p_buf));
497     (p->isam_p_indx)++;
498 }
499
500 static int grep_handle (Dict_char *name, const char *info, void *p)
501 {
502     logf (LOG_DEBUG, "dict name: %s", name);
503     add_isam_p (info, p);
504     return 0;
505 }
506
507 static void gen_regular_ge (char *dst, int val)
508 {
509     int dst_p = 1;
510     int w, d, i;
511     int pos = 0;
512     char numstr[20];
513
514     *dst = '(';
515     if (val < 0) 
516         val = 0;
517     sprintf (numstr, "%d", val);
518     for (w = strlen(numstr); --w >= 0; pos++)
519     {
520         d = numstr[w];
521         if (pos > 0)
522         {
523             if (d == '9')
524                 continue;
525             d++;
526         }
527         
528         strcpy (dst + dst_p, numstr);
529         dst_p = strlen(dst) - pos - 1;
530
531         if (d <= '8')
532         { 
533             dst[dst_p++] = '[';
534             dst[dst_p++] = d;
535             dst[dst_p++] = '-';
536             dst[dst_p++] = '9';
537             dst[dst_p++] = ']';
538         }
539         else
540             dst[dst_p++] = d;
541
542         for (i = 0; i<pos; i++)
543         {
544             dst[dst_p++] = '[';
545             dst[dst_p++] = '0';
546             dst[dst_p++] = '-';
547             dst[dst_p++] = '9';
548             dst[dst_p++] = ']';
549         }
550         dst[dst_p++] = '|';
551     }
552     dst[dst_p] = '\0';
553     for (i = 0; i <= pos; i++)
554         strcat (dst, "[0-9]");
555     strcat (dst, "[0-9]*)");
556 }
557
558 static int trunc_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
559                        const char *term_sub,
560                        oid_value attributeSet, struct grep_info *grep_info)
561 {
562     char term_dict[10*IT_MAX_WORD+2];
563     int i, j, r;
564     const char *info;    
565     AttrType truncation;
566     int truncation_value;
567     AttrType use;
568     int use_value;
569     AttrType relation;
570     int relation_value;
571     oid_value curAttributeSet = attributeSet;
572
573     attr_init (&use, zapt, 1);
574     use_value = attr_find (&use, &curAttributeSet);
575     logf (LOG_DEBUG, "use value %d", use_value);
576     attr_init (&truncation, zapt, 5);
577     truncation_value = attr_find (&truncation, NULL);
578     logf (LOG_DEBUG, "truncation value %d", truncation_value);
579
580     attr_init (&relation, zapt, 2);
581     relation_value = attr_find (&relation, NULL);
582
583     if (use_value == -1)
584         use_value = 1016;
585     i = index_word_prefix_map (term_dict, curAttributeSet, use_value);
586     if (i < 0)
587     {
588         zi->errCode = 114;
589         return -1;
590     }
591     switch (relation_value)
592     {
593     case 1:
594     case 2:
595         break;
596     case 4:
597         logf (LOG_LOG, "Relation ge");
598         gen_regular_ge (term_dict + strlen(term_dict), atoi(term_sub));
599         logf (LOG_LOG, "dict_lookup_grep: %s", term_dict);
600         r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, 
601                               grep_handle);
602         if (r)
603             logf (LOG_WARN, "dict_lookup_grep fail, rel=ge: %d", r);
604         logf (LOG_LOG, "%d positions", grep_info->isam_p_indx);
605         return 0;
606     case 5:
607         logf (LOG_LOG, "Relation gt");
608         gen_regular_ge (term_dict + strlen(term_dict), atoi(term_sub)+1);
609         logf (LOG_LOG, "dict_lookup_grep: %s", term_dict);
610         r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, 
611                               grep_handle);
612         if (r)
613             logf (LOG_WARN, "dict_lookup_grep fail, rel=gt: %d", r);
614         logf (LOG_LOG, "%d positions", grep_info->isam_p_indx);
615         return 0;
616     }
617     switch (truncation_value)
618     {
619     case -1:         /* not specified */
620     case 100:        /* do not truncate */
621         strcat (term_dict, term_sub);
622         logf (LOG_DEBUG, "dict_lookup: %s", term_dict);
623         if ((info = dict_lookup (zi->wordDict, term_dict)))
624             add_isam_p (info, grep_info);
625         break;
626     case 1:          /* right truncation */
627         strcat (term_dict, term_sub);
628         strcat (term_dict, ".*");
629         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, grep_handle);
630         break;
631     case 2:          /* left truncation */
632     case 3:          /* left&right truncation */
633         zi->errCode = 120;
634         return -1;
635     case 101:        /* process # in term */
636         for (j = strlen(term_dict), i = 0; term_sub[i] && i < 2; i++)
637             term_dict[j++] = term_sub[i];
638         for (; term_sub[i]; i++)
639             if (term_sub[i] == '#')
640             {
641                 term_dict[j++] = '.';
642                 term_dict[j++] = '*';
643             }
644             else
645                 term_dict[j++] = term_sub[i];
646         term_dict[j] = '\0';
647         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, grep_handle);
648         break;
649     case 102:        /* regular expression */
650         strcat (term_dict, "(");
651         strcat (term_dict, term_sub);
652         strcat (term_dict, ")");
653         logf (LOG_LOG, "dict_lookup_grep: %s", term_dict);
654         r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info,
655                               grep_handle);
656         if (r)
657             logf (LOG_WARN, "dict_lookup_grep fail, truncation=regular: %d", r);
658         break;
659     }
660     logf (LOG_LOG, "%d positions", grep_info->isam_p_indx);
661     return 0;
662 }
663
664 static void trans_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
665                         char *termz)
666 {
667     size_t i, sizez;
668     Z_Term *term = zapt->term;
669
670     sizez = term->u.general->len;
671     if (sizez > IT_MAX_WORD)
672         sizez = IT_MAX_WORD;
673     for (i = 0; i < sizez; i++)
674         termz[i] = index_char_cvt (term->u.general->buf[i]);
675     termz[i] = '\0';
676 }
677
678 static RSET rpn_search_APT_relevance (ZServerInfo *zi, 
679                                       Z_AttributesPlusTerm *zapt,
680                                       oid_value attributeSet)
681 {
682     rset_relevance_parms parms;
683     char termz[IT_MAX_WORD+1];
684     char term_sub[IT_MAX_WORD+1];
685     struct grep_info grep_info;
686     char *p0 = termz, *p1 = NULL;
687     RSET result;
688
689     parms.key_size = sizeof(struct it_key);
690     parms.max_rec = 100;
691     parms.cmp = key_compare;
692     parms.is = zi->wordIsam;
693
694     if (zapt->term->which != Z_Term_general)
695     {
696         zi->errCode = 124;
697         return NULL;
698     }
699     trans_term (zi, zapt, termz);
700     grep_info.isam_p_indx = 0;
701     grep_info.isam_p_size = 0;
702     grep_info.isam_p_buf = NULL;
703     while (1)
704     {
705         if ((p1 = strchr (p0, ' ')))
706         {
707             memcpy (term_sub, p0, p1-p0);
708             term_sub[p1-p0] = '\0';
709         }
710         else
711             strcpy (term_sub, p0);
712         if (trunc_term (zi, zapt, term_sub, attributeSet, &grep_info))
713             return NULL;
714         if (!p1)
715             break;
716         p0 = p1;
717         while (*++p0 == ' ')
718             ;
719     }
720     parms.isam_positions = grep_info.isam_p_buf;
721     parms.no_isam_positions = grep_info.isam_p_indx;
722     if (grep_info.isam_p_indx > 0)
723         result = rset_create (rset_kind_relevance, &parms);
724     else
725         result = rset_create (rset_kind_null, NULL);
726     xfree (grep_info.isam_p_buf);
727     return result;
728 }
729
730 static RSET rpn_search_APT_word (ZServerInfo *zi,
731                                  Z_AttributesPlusTerm *zapt,
732                                  oid_value attributeSet)
733 {
734     rset_isam_parms parms;
735     char termz[IT_MAX_WORD+1];
736     struct grep_info grep_info;
737     RSET result;
738
739     if (zapt->term->which != Z_Term_general)
740     {
741         zi->errCode = 124;
742         return NULL;
743     }
744     trans_term (zi, zapt, termz);
745
746     grep_info.isam_p_indx = 0;
747     grep_info.isam_p_size = 0;
748     grep_info.isam_p_buf = NULL;
749
750     if (trunc_term (zi, zapt, termz, attributeSet, &grep_info))
751         return NULL;
752     if (grep_info.isam_p_indx < 1)
753         result = rset_create (rset_kind_null, NULL);
754     else if (grep_info.isam_p_indx == 1)
755     {
756         parms.is = zi->wordIsam;
757         parms.pos = *grep_info.isam_p_buf;
758         result = rset_create (rset_kind_isam, &parms);
759     }
760     else
761         result = rset_trunc (zi->wordIsam, grep_info.isam_p_buf,
762                              grep_info.isam_p_indx);
763     xfree (grep_info.isam_p_buf);
764     return result;
765 }
766
767 static RSET rpn_prox (RSET *rset, int rset_no)
768 {
769     int i;
770     RSFD *rsfd;
771     int  *more;
772     struct it_key **buf;
773     RSFD rsfd_result;
774     RSET result;
775     rset_temp_parms parms;
776     
777     rsfd = xmalloc (sizeof(*rsfd)*rset_no);
778     more = xmalloc (sizeof(*more)*rset_no);
779     buf = xmalloc (sizeof(*buf)*rset_no);
780
781     for (i = 0; i<rset_no; i++)
782     {
783         buf[i] = xmalloc (sizeof(**buf));
784         rsfd[i] = rset_open (rset[i], RSETF_READ|RSETF_SORT_SYSNO);
785         if (!(more[i] = rset_read (rset[i], rsfd[i], buf[i])))
786         {
787             while (i >= 0)
788             {
789                 rset_close (rset[i], rsfd[i]);
790                 xfree (buf[i]);
791                 --i;
792             }
793             xfree (rsfd);
794             xfree (more);
795             xfree (buf);
796             return rset_create (rset_kind_null, NULL);
797         }
798     }
799     parms.key_size = sizeof (struct it_key);
800     result = rset_create (rset_kind_temp, &parms);
801     rsfd_result = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
802     
803     while (*more)
804     {
805         for (i = 1; i<rset_no; i++)
806         {
807             int cmp;
808             
809             if (!more[i])
810             {
811                 *more = 0;
812                 break;
813             }
814             cmp = key_compare (buf[i], buf[i-1]);
815             if (cmp > 1)
816             {
817                 more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
818                 break;
819             }
820             else if (cmp == 1)
821             {
822                 if (buf[i-1]->seqno+1 != buf[i]->seqno)
823                 {
824                     more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
825                     break;
826                 }
827             }
828             else
829             {
830                 more[i] = rset_read (rset[i], rsfd[i], buf[i]);
831                 break;
832             }
833         }
834         if (i == rset_no)
835         {
836             rset_write (result, rsfd_result, buf[0]);
837             more[0] = rset_read (*rset, *rsfd, *buf);
838         }
839     }
840     
841     for (i = 0; i<rset_no; i++)
842     {
843         rset_close (rset[i], rsfd[i]);
844         xfree (buf[i]);
845     }
846     rset_close (result, rsfd_result);
847     xfree (buf);
848     xfree (more);
849     xfree (rsfd);
850     return result;
851 }
852
853 static RSET rpn_search_APT_phrase (ZServerInfo *zi,
854                                    Z_AttributesPlusTerm *zapt,
855                                    oid_value attributeSet)
856 {
857     char termz[IT_MAX_WORD+1];
858     char term_sub[IT_MAX_WORD+1];
859     char *p0 = termz, *p1 = NULL;
860     RSET rset[60], result;
861     int i, rset_no = 0;
862     struct grep_info grep_info;
863
864     if (zapt->term->which != Z_Term_general)
865     {
866         zi->errCode = 124;
867         return NULL;
868     }
869     trans_term (zi, zapt, termz);
870
871     grep_info.isam_p_size = 0;
872     grep_info.isam_p_buf = NULL;
873
874     while (1)
875     {
876         if ((p1 = strchr (p0, ' ')))
877         {
878             memcpy (term_sub, p0, p1-p0);
879             term_sub[p1-p0] = '\0';
880         }
881         else
882             strcpy (term_sub, p0);
883
884         grep_info.isam_p_indx = 0;
885         if (trunc_term (zi, zapt, term_sub, attributeSet, &grep_info))
886             return NULL;
887         if (grep_info.isam_p_indx == 0)
888             rset[rset_no] = rset_create (rset_kind_null, NULL);
889         else if (grep_info.isam_p_indx > 1)
890             rset[rset_no] = rset_trunc (zi->wordIsam,
891                                         grep_info.isam_p_buf,
892                                         grep_info.isam_p_indx);
893         else
894         {
895             rset_isam_parms parms;
896             
897             parms.is = zi->wordIsam;
898             parms.pos = *grep_info.isam_p_buf;
899             rset[rset_no] = rset_create (rset_kind_isam, &parms);
900         }
901         assert (rset[rset_no]);
902         if (++rset_no >= sizeof(rset)/sizeof(*rset))
903             break;
904         if (!p1)
905             break;
906         p0 = p1;
907         while (*++p0 == ' ')
908             ;
909     }
910     xfree (grep_info.isam_p_buf);
911     if (rset_no == 0)
912         return rset_create (rset_kind_null, NULL);
913     else if (rset_no == 1)
914         return (rset[0]);
915
916     result = rpn_prox (rset, rset_no);
917     for (i = 0; i<rset_no; i++)
918         rset_delete (rset[i]);
919     return result;
920 }
921
922 static RSET rpn_search_APT_local (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
923                                   oid_value attributeSet)
924 {
925     RSET result;
926     RSFD rsfd;
927     struct it_key key;
928     rset_temp_parms parms;
929     char termz[IT_MAX_WORD+1];
930
931     if (zapt->term->which != Z_Term_general)
932     {
933         zi->errCode = 124;
934         return NULL;
935     }
936     parms.key_size = sizeof (struct it_key);
937     result = rset_create (rset_kind_temp, &parms);
938     rsfd = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
939
940     trans_term (zi, zapt, termz);
941     key.sysno = atoi (termz);
942     if (key.sysno <= 0)
943         key.sysno = 1;
944     rset_write (result, rsfd, &key);
945     rset_close (result, rsfd);
946     return result;
947 }
948
949 static RSET rpn_search_APT (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
950                             oid_value attributeSet)
951 {
952     AttrType relation;
953     AttrType structure;
954     int relation_value, structure_value;
955
956     attr_init (&relation, zapt, 2);
957     attr_init (&structure, zapt, 4);
958     
959     relation_value = attr_find (&relation, NULL);
960     structure_value = attr_find (&structure, NULL);
961     switch (structure_value)
962     {
963     case -1:
964         if (relation_value == 102) /* relevance relation */
965             return rpn_search_APT_relevance (zi, zapt, attributeSet);
966         return rpn_search_APT_phrase (zi, zapt, attributeSet);
967     case 1: /* phrase */
968         if (relation_value == 102) /* relevance relation */
969             return rpn_search_APT_relevance (zi, zapt, attributeSet);
970         return rpn_search_APT_phrase (zi, zapt, attributeSet);
971         break;
972     case 2: /* word */
973         if (relation_value == 102) /* relevance relation */
974             return rpn_search_APT_relevance (zi, zapt, attributeSet);
975         return rpn_search_APT_word (zi, zapt, attributeSet);
976     case 3: /* key */
977         break;
978     case 4: /* year */
979         break;
980     case 5: /* date - normalized */
981         break;
982     case 6: /* word list */
983         return rpn_search_APT_relevance (zi, zapt, attributeSet);
984     case 100: /* date - un-normalized */
985         break;
986     case 101: /* name - normalized */
987         break;
988     case 102: /* date - un-normalized */
989         break;
990     case 103: /* structure */
991         break;
992     case 104: /* urx */
993         break;
994     case 105: /* free-form-text */
995         return rpn_search_APT_relevance (zi, zapt, attributeSet);
996     case 106: /* document-text */
997         return rpn_search_APT_relevance (zi, zapt, attributeSet);
998     case 107: /* local-number */
999         return rpn_search_APT_local (zi, zapt, attributeSet);
1000     case 108: /* string */ 
1001         return rpn_search_APT_word (zi, zapt, attributeSet);
1002     case 109: /* numeric string */
1003         break;
1004     }
1005     zi->errCode = 118;
1006     return NULL;
1007 }
1008
1009 static RSET rpn_search_ref (ZServerInfo *zi, Z_ResultSetId *resultSetId)
1010 {
1011     ZServerSet *s;
1012
1013     if (!(s = resultSetGet (zi, resultSetId)))
1014         return rset_create (rset_kind_null, NULL);
1015     return s->rset;
1016 }
1017
1018 static RSET rpn_search_structure (ZServerInfo *zi, Z_RPNStructure *zs,
1019                                   oid_value attributeSet)
1020 {
1021     RSET r = NULL;
1022     if (zs->which == Z_RPNStructure_complex)
1023     {
1024         rset_bool_parms bool_parms;
1025
1026         bool_parms.rset_l = rpn_search_structure (zi, zs->u.complex->s1,
1027                                                   attributeSet);
1028         if (bool_parms.rset_l == NULL)
1029             return NULL;
1030         bool_parms.rset_r = rpn_search_structure (zi, zs->u.complex->s2,
1031                                                   attributeSet);
1032         if (bool_parms.rset_r == NULL)
1033         {
1034             rset_delete (bool_parms.rset_l);
1035             return NULL;
1036         }
1037         bool_parms.key_size = sizeof(struct it_key);
1038         bool_parms.cmp = key_compare;
1039
1040         switch (zs->u.complex->operator->which)
1041         {
1042         case Z_Operator_and:
1043             r = rset_create (rset_kind_and, &bool_parms);
1044             break;
1045         case Z_Operator_or:
1046             r = rset_create (rset_kind_or, &bool_parms);
1047             break;
1048         case Z_Operator_and_not:
1049             r = rset_create (rset_kind_not, &bool_parms);
1050             break;
1051         default:
1052             assert (0);
1053         }
1054     }
1055     else if (zs->which == Z_RPNStructure_simple)
1056     {
1057         if (zs->u.simple->which == Z_Operand_APT)
1058         {
1059             logf (LOG_DEBUG, "rpn_search_APT");
1060             r = rpn_search_APT (zi, zs->u.simple->u.attributesPlusTerm,
1061                                 attributeSet);
1062         }
1063         else if (zs->u.simple->which == Z_Operand_resultSetId)
1064         {
1065             logf (LOG_DEBUG, "rpn_search_ref");
1066             r = rpn_search_ref (zi, zs->u.simple->u.resultSetId);
1067         }
1068         else
1069         {
1070             assert (0);
1071         }
1072     }
1073     else
1074     {
1075         assert (0);
1076     }
1077     return r;
1078 }
1079
1080 static void count_set (RSET r, int *count)
1081 {
1082     int psysno = 0;
1083     struct it_key key;
1084     RSFD rfd;
1085
1086     logf (LOG_DEBUG, "rpn_save_set");
1087     *count = 0;
1088     rfd = rset_open (r, RSETF_READ|RSETF_SORT_SYSNO);
1089     while (rset_read (r, rfd, &key))
1090     {
1091         if (key.sysno != psysno)
1092         {
1093             psysno = key.sysno;
1094             (*count)++;
1095         }
1096     }
1097     rset_close (r, rfd);
1098     logf (LOG_DEBUG, "%d distinct sysnos", *count);
1099 }
1100
1101 int rpn_search (ZServerInfo *zi,
1102                 Z_RPNQuery *rpn, int num_bases, char **basenames, 
1103                 const char *setname, int *hits)
1104 {
1105     RSET rset;
1106     oident *attrset;
1107     oid_value attributeSet;
1108
1109     zi->errCode = 0;
1110     zi->errString = NULL;
1111     
1112     attrset = oid_getentbyoid (rpn->attributeSetId);
1113     attributeSet = attrset->value;
1114
1115     rset = rpn_search_structure (zi, rpn->RPNStructure, attributeSet);
1116     if (!rset)
1117         return zi->errCode;
1118     count_set (rset, hits);
1119     resultSetAdd (zi, setname, 1, rset);
1120     if (zi->errCode)
1121         logf (LOG_DEBUG, "search error: %d", zi->errCode);
1122     return zi->errCode;
1123 }
1124
1125 struct scan_info {
1126     struct scan_entry *list;
1127     ODR odr;
1128     int before, after;
1129     ISAM isam;
1130     char prefix[20];
1131 };
1132
1133 static int scan_handle (Dict_char *name, const char *info, int pos, 
1134                         void *client)
1135 {
1136     int len_prefix, idx;
1137     ISAM_P isam_p;
1138     RSET rset;
1139     struct scan_info *scan_info = client;
1140
1141     rset_isam_parms parms;
1142
1143     len_prefix = strlen(scan_info->prefix);
1144     if (memcmp (name, scan_info->prefix, len_prefix))
1145         return 1;
1146     if (pos > 0)
1147         idx = scan_info->after - pos + scan_info->before;
1148     else
1149         idx = - pos - 1;
1150     scan_info->list[idx].term = odr_malloc (scan_info->odr,
1151                                             strlen(name + len_prefix)+1);
1152     strcpy (scan_info->list[idx].term, name + len_prefix);
1153     assert (*info == sizeof(isam_p));
1154     memcpy (&isam_p, info+1, sizeof(isam_p));
1155     parms.is = scan_info->isam;
1156     parms.pos = isam_p;
1157 #if 1
1158     rset = rset_create (rset_kind_isam, &parms);
1159     count_set (rset, &scan_info->list[idx].occurrences);
1160     rset_delete (rset);
1161 #else
1162     scan_info->list[idx].occurrences = 1;
1163 #endif
1164     logf (LOG_DEBUG, "pos=%3d idx=%3d name=%s", pos, idx, name);
1165     return 0;
1166 }
1167
1168 int rpn_scan (ZServerInfo *zi, ODR odr, Z_AttributesPlusTerm *zapt,
1169               int *position, int *num_entries, struct scan_entry **list,
1170               int *status)
1171 {
1172     int i, j, sizez;
1173     int pos = *position;
1174     int num = *num_entries;
1175     int before;
1176     int after;
1177     char termz[IT_MAX_WORD+20];
1178     AttrType use;
1179     int use_value;
1180     Z_Term *term = zapt->term;
1181     struct scan_info scan_info;
1182
1183     logf (LOG_DEBUG, "scan, position = %d, num = %d", pos, num);
1184     scan_info.before = before = pos-1;
1185     scan_info.after = after = 1+num-pos;
1186     scan_info.odr = odr;
1187
1188     logf (LOG_DEBUG, "scan, before = %d, after = %d", before, after);
1189     
1190     scan_info.isam = zi->wordIsam;
1191     scan_info.list = odr_malloc (odr, (before+after)*sizeof(*scan_info.list));
1192     for (j = 0; j<before+after; j++)
1193         scan_info.list[j].term = NULL;
1194     attr_init (&use, zapt, 1);
1195     use_value = attr_find (&use, NULL);
1196     logf (LOG_DEBUG, "use value %d", use_value);
1197
1198     if (use_value == -1)
1199         use_value = 1016;
1200     i = index_word_prefix (termz, 1, use_value);
1201     strcpy (scan_info.prefix, termz);
1202     sizez = term->u.general->len;
1203     if (sizez > IT_MAX_WORD)
1204         sizez = IT_MAX_WORD;
1205     for (j = 0; j<sizez; j++)
1206         termz[j+i] = index_char_cvt (term->u.general->buf[j]);
1207     termz[j+i] = '\0';
1208     
1209     dict_scan (zi->wordDict, termz, &before, &after, &scan_info, scan_handle);
1210
1211     *status = BEND_SCAN_SUCCESS;
1212
1213     for (i = 0; i<scan_info.after; i++)
1214         if (scan_info.list[scan_info.before+scan_info.after-i-1].term)
1215             break;
1216     *num_entries -= i;
1217     if (i)
1218         *status = BEND_SCAN_PARTIAL;
1219
1220     for (i = 0; i<scan_info.before; i++)
1221         if (scan_info.list[i].term)
1222             break;
1223     if (i)
1224         *status = BEND_SCAN_PARTIAL;
1225     *position -= i;
1226     *num_entries -= i;
1227
1228     *list = scan_info.list+i;       /* list is set to first 'real' entry */
1229
1230     if (*num_entries == 0)          /* signal 'unsupported use-attribute' */
1231         zi->errCode = 114;          /* if no entries was found */
1232     logf (LOG_DEBUG, "position = %d, num_entries = %d",
1233           *position, *num_entries);
1234     if (zi->errCode)
1235         logf (LOG_DEBUG, "scan error: %d", zi->errCode);
1236     return 0;
1237 }
1238               
1239
1240