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