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