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