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