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