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