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