8d5da192e68cf52847e9d80a766f3c71c6c0458c
[idzebra-moved-to-github.git] / index / zrpn.c
1 /*
2  * Copyright (C) 1994-1996, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zrpn.c,v $
7  * Revision 1.55  1996-11-04 14:07:44  adam
8  * Moved truncation code to trunc.c.
9  *
10  * Revision 1.54  1996/10/29 14:09:52  adam
11  * Use of cisam system - enabled if setting isamc is 1.
12  *
13  * Revision 1.53  1996/06/26 09:21:43  adam
14  * Bug fix: local attribute set wasn't obeyed in scan.
15  *
16  * Revision 1.52  1996/06/17  14:26:20  adam
17  * Function gen_regular_rel changed to handle negative numbers.
18  *
19  * Revision 1.51  1996/06/11 10:54:15  quinn
20  * Relevance work
21  *
22  * Revision 1.50  1996/06/07  08:51:53  adam
23  * Bug fix: Character mapping was broken (introducued by last revision).
24  *
25  * Revision 1.49  1996/06/04  10:18:11  adam
26  * Search/scan uses character mapping module.
27  *
28  * Revision 1.48  1996/05/28  15:15:01  adam
29  * Bug fix: Didn't handle unknown database correctly.
30  *
31  * Revision 1.47  1996/05/15  18:36:28  adam
32  * Function trans_term transforms unsearchable characters to blanks.
33  *
34  * Revision 1.46  1996/05/15  11:57:56  adam
35  * Fixed bug introduced by set/field mapping in search operations.
36  *
37  * Revision 1.45  1996/05/14  11:34:00  adam
38  * Scan support in multiple registers/databases.
39  *
40  * Revision 1.44  1996/05/14  06:16:44  adam
41  * Compact use/set bytes used in search service.
42  *
43  * Revision 1.43  1996/05/09 09:54:43  adam
44  * Server supports maps from one logical attributes to a list of physical
45  * attributes.
46  * The extraction process doesn't make space consuming 'any' keys.
47  *
48  * Revision 1.42  1996/05/09  07:28:56  quinn
49  * Work towards phrases and multiple registers
50  *
51  * Revision 1.41  1996/03/20  09:36:43  adam
52  * Function dict_lookup_grep got extra parameter, init_pos, which marks
53  * from which position in pattern approximate pattern matching should occur.
54  * Approximate pattern matching is used in relevance=re-2.
55  *
56  * Revision 1.40  1996/02/02  13:44:44  adam
57  * The public dictionary functions simply use char instead of Dict_char
58  * to represent search strings. Dict_char is used internally only.
59  *
60  * Revision 1.39  1996/01/03  16:22:13  quinn
61  * operator->roperator
62  *
63  * Revision 1.38  1995/12/11  09:12:55  adam
64  * The rec_get function returns NULL if record doesn't exist - will
65  * happen in the server if the result set records have been deleted since
66  * the creation of the set (i.e. the search).
67  * The server saves a result temporarily if it is 'volatile', i.e. the
68  * set is register dependent.
69  *
70  * Revision 1.37  1995/12/06  15:05:28  adam
71  * More verbose in count_set.
72  *
73  * Revision 1.36  1995/12/06  12:41:27  adam
74  * New command 'stat' for the index program.
75  * Filenames can be read from stdin by specifying '-'.
76  * Bug fix/enhancement of the transformation from terms to regular
77  * expressons in the search engine.
78  *
79  * Revision 1.35  1995/11/27  09:29:00  adam
80  * Bug fixes regarding conversion to regular expressions.
81  *
82  * Revision 1.34  1995/11/16  17:00:56  adam
83  * Better logging of rpn query.
84  *
85  * Revision 1.33  1995/11/01  13:58:28  quinn
86  * Moving data1 to yaz/retrieval
87  *
88  * Revision 1.32  1995/10/27  14:00:11  adam
89  * Implemented detection of database availability.
90  *
91  * Revision 1.31  1995/10/17  18:02:10  adam
92  * New feature: databases. Implemented as prefix to words in dictionary.
93  *
94  * Revision 1.30  1995/10/16  09:32:38  adam
95  * More work on relational op.
96  *
97  * Revision 1.29  1995/10/13  16:01:49  adam
98  * Work on relations.
99  *
100  * Revision 1.28  1995/10/13  12:26:43  adam
101  * Optimization of truncation.
102  *
103  * Revision 1.27  1995/10/12  17:07:22  adam
104  * Truncation works.
105  *
106  * Revision 1.26  1995/10/12  12:40:54  adam
107  * Bug fixes in rpn_prox.
108  *
109  * Revision 1.25  1995/10/10  13:59:24  adam
110  * Function rset_open changed its wflag parameter to general flags.
111  *
112  * Revision 1.24  1995/10/09  16:18:37  adam
113  * Function dict_lookup_grep got extra client data parameter.
114  *
115  * Revision 1.23  1995/10/06  16:33:37  adam
116  * Use attribute mappings.
117  *
118  * Revision 1.22  1995/10/06  15:07:39  adam
119  * Structure 'local-number' handled.
120  *
121  * Revision 1.21  1995/10/06  13:52:06  adam
122  * Bug fixes. Handler may abort further scanning.
123  *
124  * Revision 1.20  1995/10/06  11:06:33  adam
125  * Scan entries include 'occurrences' now.
126  *
127  * Revision 1.19  1995/10/06  10:43:56  adam
128  * Scan added. 'occurrences' in scan entries not set yet.
129  *
130  * Revision 1.18  1995/10/04  16:57:20  adam
131  * Key input and merge sort in one pass.
132  *
133  * Revision 1.17  1995/10/04  12:55:17  adam
134  * Bug fix in ranked search. Use=Any keys inserted.
135  *
136  * Revision 1.16  1995/10/02  16:24:40  adam
137  * Use attribute actually used in search requests.
138  *
139  * Revision 1.15  1995/10/02  15:18:52  adam
140  * New member in recRetrieveCtrl: diagnostic.
141  *
142  * Revision 1.14  1995/09/28  12:10:32  adam
143  * Bug fixes. Field prefix used in queries.
144  *
145  * Revision 1.13  1995/09/18  14:17:50  adam
146  * Minor changes.
147  *
148  * Revision 1.12  1995/09/15  14:45:21  adam
149  * Retrieve control.
150  * Work on truncation.
151  *
152  * Revision 1.11  1995/09/14  11:53:27  adam
153  * First work on regular expressions/truncations.
154  *
155  * Revision 1.10  1995/09/11  15:23:26  adam
156  * More work on relevance search.
157  *
158  * Revision 1.9  1995/09/11  13:09:35  adam
159  * More work on relevance feedback.
160  *
161  * Revision 1.8  1995/09/08  14:52:27  adam
162  * Minor changes. Dictionary is lower case now.
163  *
164  * Revision 1.7  1995/09/07  13:58:36  adam
165  * New parameter: result-set file descriptor (RSFD) to support multiple
166  * positions within the same result-set.
167  * Boolean operators: and, or, not implemented.
168  * Result-set references.
169  *
170  * Revision 1.6  1995/09/06  16:11:18  adam
171  * Option: only one word key per file.
172  *
173  * Revision 1.5  1995/09/06  10:33:04  adam
174  * More work on present. Some log messages removed.
175  *
176  * Revision 1.4  1995/09/05  15:28:40  adam
177  * More work on search engine.
178  *
179  * Revision 1.3  1995/09/04  15:20:22  adam
180  * Minor changes.
181  *
182  * Revision 1.2  1995/09/04  12:33:43  adam
183  * Various cleanup. YAZ util used instead.
184  *
185  * Revision 1.1  1995/09/04  09:10:40  adam
186  * More work on index add/del/update.
187  * Merge sort implemented.
188  * Initial work on z39 server.
189  *
190  */
191 #include <stdio.h>
192 #include <assert.h>
193 #include <unistd.h>
194 #include <ctype.h>
195
196 #include "zserver.h"
197 #include "attribute.h"
198
199 #include <charmap.h>
200 #include <rstemp.h>
201 #include <rsnull.h>
202 #include <rsbool.h>
203 #include <rsrel.h>
204
205 typedef struct {
206     int type;
207     int major;
208     int minor;
209     Z_AttributesPlusTerm *zapt;
210 } AttrType;
211
212 static int attr_find (AttrType *src, oid_value *attributeSetP)
213 {
214     while (src->major < src->zapt->num_attributes)
215     {
216         Z_AttributeElement *element;
217
218         element = src->zapt->attributeList[src->major];
219         if (src->type == *element->attributeType)
220         {
221             switch (element->which) 
222             {
223             case Z_AttributeValue_numeric:
224                 ++(src->major);
225                 if (element->attributeSet && attributeSetP)
226                 {
227                     oident *attrset;
228
229                     attrset = oid_getentbyoid (element->attributeSet);
230                     *attributeSetP = attrset->value;
231                 }
232                 return *element->value.numeric;
233                 break;
234             case Z_AttributeValue_complex:
235                 if (src->minor >= element->value.complex->num_list ||
236                     element->value.complex->list[src->minor]->which !=  
237                     Z_StringOrNumeric_numeric)
238                     break;
239                 ++(src->minor);
240                 if (element->attributeSet && attributeSetP)
241                 {
242                     oident *attrset;
243
244                     attrset = oid_getentbyoid (element->attributeSet);
245                     *attributeSetP = attrset->value;
246                 }
247                 return *element->value.complex->list[src->minor-1]->u.numeric;
248             default:
249                 assert (0);
250             }
251         }
252         ++(src->major);
253     }
254     return -1;
255 }
256
257 static void attr_init (AttrType *src, Z_AttributesPlusTerm *zapt,
258                        int type)
259 {
260     src->zapt = zapt;
261     src->type = type;
262     src->major = 0;
263     src->minor = 0;
264 }
265
266 #define TERM_COUNT        
267        
268 struct grep_info {        
269 #ifdef TERM_COUNT        
270     int *term_no;        
271 #endif        
272     ISAM_P *isam_p_buf;        
273     int isam_p_size;        
274     int isam_p_indx;        
275 };        
276
277 static void add_isam_p (const char *info, struct grep_info *p)
278 {
279     if (p->isam_p_indx == p->isam_p_size)
280     {
281         ISAM_P *new_isam_p_buf;
282 #ifdef TERM_COUNT        
283         int *new_term_no;        
284 #endif        
285         
286         p->isam_p_size = 2*p->isam_p_size + 100;
287         new_isam_p_buf = xmalloc (sizeof(*new_isam_p_buf) *
288                                   p->isam_p_size);
289         if (p->isam_p_buf)
290         {
291             memcpy (new_isam_p_buf, p->isam_p_buf,
292                     p->isam_p_indx * sizeof(*p->isam_p_buf));
293             xfree (p->isam_p_buf);
294         }
295         p->isam_p_buf = new_isam_p_buf;
296
297 #ifdef TERM_COUNT
298         new_term_no = xmalloc (sizeof(*new_term_no) *
299                                   p->isam_p_size);
300         if (p->term_no)
301         {
302             memcpy (new_term_no, p->isam_p_buf,
303                     p->isam_p_indx * sizeof(*p->term_no));
304             xfree (p->term_no);
305         }
306         p->term_no = new_term_no;
307 #endif
308     }
309     assert (*info == sizeof(*p->isam_p_buf));
310     memcpy (p->isam_p_buf + p->isam_p_indx, info+1, sizeof(*p->isam_p_buf));
311     (p->isam_p_indx)++;
312 }
313
314 static int grep_handle (char *name, const char *info, void *p)
315 {
316     add_isam_p (info, p);
317     return 0;
318 }
319
320 /* gen_regular_rel - generate regular expression from relation
321  *  val:     border value (inclusive)
322  *  islt:    1 if <=; 0 if >=.
323  */
324 static void gen_regular_rel (char *dst, int val, int islt)
325 {
326     int dst_p;
327     int w, d, i;
328     int pos = 0;
329     char numstr[20];
330
331     logf (LOG_DEBUG, "gen_regular_rel. val=%d, islt=%d", val, islt);
332     if (val >= 0)
333     {
334         if (islt)
335             strcpy (dst, "(-[0-9]+|");
336         else
337             strcpy (dst, "(");
338     } 
339     else
340     {
341         if (!islt)
342         {
343             strcpy (dst, "([0-9]+|-");
344             dst_p = strlen (dst);
345             islt = 1;
346         }
347         else
348         {
349             strcpy (dst, "(-");
350             islt = 0;
351         }
352         val = -val;
353     }
354     dst_p = strlen (dst);
355     sprintf (numstr, "%d", val);
356     for (w = strlen(numstr); --w >= 0; pos++)
357     {
358         d = numstr[w];
359         if (pos > 0)
360         {
361             if (islt)
362             {
363                 if (d == '0')
364                     continue;
365                 d--;
366             } 
367             else
368             {
369                 if (d == '9')
370                     continue;
371                 d++;
372             }
373         }
374         
375         strcpy (dst + dst_p, numstr);
376         dst_p = strlen(dst) - pos - 1;
377
378         if (islt)
379         {
380             if (d != '0')
381             {
382                 dst[dst_p++] = '[';
383                 dst[dst_p++] = '0';
384                 dst[dst_p++] = '-';
385                 dst[dst_p++] = d;
386                 dst[dst_p++] = ']';
387             }
388             else
389                 dst[dst_p++] = d;
390         }
391         else
392         {
393             if (d != '9')
394             { 
395                 dst[dst_p++] = '[';
396                 dst[dst_p++] = d;
397                 dst[dst_p++] = '-';
398                 dst[dst_p++] = '9';
399                 dst[dst_p++] = ']';
400             }
401             else
402                 dst[dst_p++] = d;
403         }
404         for (i = 0; i<pos; i++)
405         {
406             dst[dst_p++] = '[';
407             dst[dst_p++] = '0';
408             dst[dst_p++] = '-';
409             dst[dst_p++] = '9';
410             dst[dst_p++] = ']';
411         }
412         dst[dst_p++] = '|';
413     }
414     dst[dst_p] = '\0';
415     if (islt)
416     {
417         for (i=1; i<pos; i++)
418             strcat (dst, "[0-9]?");
419     }
420     else
421     {
422         for (i = 0; i <= pos; i++)
423             strcat (dst, "[0-9]");
424         strcat (dst, "[0-9]*");
425     }
426     strcat (dst, ")");
427 }
428
429 static int relational_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
430                             const char *term_sub,
431                             char *term_dict,
432                             oid_value attributeSet,
433                             struct grep_info *grep_info,
434                             int *max_pos)
435 {
436     AttrType relation;
437     int relation_value;
438     int term_value;
439     int r;
440
441     attr_init (&relation, zapt, 2);
442     relation_value = attr_find (&relation, NULL);
443     term_value = atoi (term_sub);
444
445     switch (relation_value)
446     {
447     case 1:
448         if (term_value <= 0)
449             return 1;
450         logf (LOG_DEBUG, "Relation <");
451         gen_regular_rel (term_dict + strlen(term_dict), term_value-1, 1);
452         break;
453     case 2:
454         if (term_value < 0)
455             return 1;
456         logf (LOG_DEBUG, "Relation <=");
457         gen_regular_rel (term_dict + strlen(term_dict), term_value, 1);
458         break;
459     case 4:
460         if (term_value < 0)
461             term_value = 0;
462         logf (LOG_DEBUG, "Relation >=");
463         gen_regular_rel (term_dict + strlen(term_dict), term_value, 0);
464         break;
465     case 5:
466         if (term_value < 0)
467             term_value = 0;
468         logf (LOG_DEBUG, "Relation >");
469         gen_regular_rel (term_dict + strlen(term_dict), term_value+1, 0);
470         break;
471     default:
472         return 0;
473     }
474     logf (LOG_DEBUG, "dict_lookup_grep: %s", term_dict);
475     r = dict_lookup_grep (zi->dict, term_dict, 0, grep_info, max_pos,
476                           0, grep_handle);
477     if (r)
478         logf (LOG_WARN, "dict_lookup_grep fail, rel=gt: %d", r);
479     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
480     return 1;
481 }
482
483 static void verbatim_char (int ch, int *indx, char *dst)
484 {
485     if (!isalnum (ch))
486         dst[(*indx)++] = '\\';
487     dst[(*indx)++] = ch;
488 }
489
490 static int field_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
491                        const char *term_sub, int regType,
492                        oid_value attributeSet, struct grep_info *grep_info,
493                        int num_bases, char **basenames)
494 {
495     char term_dict[2*IT_MAX_WORD+2];
496     int i, j, r, base_no;
497     AttrType truncation;
498     int truncation_value;
499     AttrType use;
500     int use_value;
501     oid_value curAttributeSet = attributeSet;
502
503     attr_init (&use, zapt, 1);
504     use_value = attr_find (&use, &curAttributeSet);
505     logf (LOG_DEBUG, "use value %d", use_value);
506     attr_init (&truncation, zapt, 5);
507     truncation_value = attr_find (&truncation, NULL);
508     logf (LOG_DEBUG, "truncation value %d", truncation_value);
509
510     if (use_value == -1)
511         use_value = 1016;
512
513     for (base_no = 0; base_no < num_bases; base_no++)
514     {
515         attent *attp;
516         data1_local_attribute *local_attr;
517         int max_pos, prefix_len = 0;
518
519         attp = att_getentbyatt (curAttributeSet, use_value);
520         if (!attp)
521         {
522             logf (LOG_DEBUG, "att_getentbyatt fail. set=%d use=%d",
523                   curAttributeSet, use_value);
524             zi->errCode = 114;
525             return -1;
526         }
527         if (zebTargetInfo_curDatabase (zi->zti, basenames[base_no]))
528         {
529             zi->errCode = 109; /* Database unavailable */
530             zi->errString = basenames[base_no];
531             return -1;
532         }
533         for (local_attr = attp->local_attributes; local_attr;
534              local_attr = local_attr->next)
535         {
536             int ord;
537
538             ord = zebTargetInfo_lookupSU (zi->zti, attp->attset_ordinal,
539                                           local_attr->local);
540             if (ord < 0)
541                 continue;
542             if (prefix_len)
543                 term_dict[prefix_len++] = '|';
544             else
545                 term_dict[prefix_len++] = '(';
546             term_dict[prefix_len++] = 1;
547             term_dict[prefix_len++] = ord;
548         }
549         if (!prefix_len)
550         {
551             zi->errCode = 114;
552             return -1;
553         }
554         term_dict[prefix_len++] = ')';        
555         term_dict[prefix_len++] = 1;
556         term_dict[prefix_len++] = regType;
557         term_dict[prefix_len] = '\0';
558         if (!relational_term (zi, zapt, term_sub, term_dict,
559                               attributeSet, grep_info, &max_pos))
560         {
561             const char *cp;
562
563             j = prefix_len;
564             switch (truncation_value)
565             {
566             case -1:         /* not specified */
567             case 100:        /* do not truncate */
568                 term_dict[j++] = '(';
569                 for (i = 0; term_sub[i]; i++)
570                     verbatim_char (term_sub[i], &j, term_dict);
571                 strcpy (term_dict+j, ")");
572                 r = dict_lookup_grep (zi->dict, term_dict, 0, grep_info,
573                                       &max_pos, 0, grep_handle);
574                 if (r)
575                     logf (LOG_WARN, "dict_lookup_grep err, trunc=none:%d", r);
576                 break;
577             case 1:          /* right truncation */
578                 term_dict[j++] = '(';
579                 for (i = 0; term_sub[i]; i++)
580                     verbatim_char (term_sub[i], &j, term_dict);
581                 strcpy (term_dict+j, ".*)");
582                 dict_lookup_grep (zi->dict, term_dict, 0, grep_info,
583                                   &max_pos, 0, grep_handle);
584                 break;
585             case 2:          /* left truncation */
586             case 3:          /* left&right truncation */
587                 zi->errCode = 120;
588                 return -1;
589             case 101:        /* process # in term */
590                 term_dict[j++] = '(';
591                 for (i=0; term_sub[i]; i++)
592                     if (term_sub[i] == '#' && i > 2)
593                     {
594                         term_dict[j++] = '.';
595                         term_dict[j++] = '*';
596                     }
597                     else
598                         verbatim_char (term_sub[i], &j, term_dict);
599                 strcpy (term_dict+j, ")");
600                 r = dict_lookup_grep (zi->dict, term_dict, 0, grep_info,
601                                       &max_pos, 0, grep_handle);
602                 if (r)
603                     logf (LOG_WARN, "dict_lookup_grep err, trunc=#: %d",
604                           r);
605                 break;
606             case 102:        /* regular expression */
607                 sprintf (term_dict + j, "(%s)", term_sub);
608                 r = dict_lookup_grep (zi->dict, term_dict, 0, grep_info,
609                                       &max_pos, 0, grep_handle);
610                 if (r)
611                     logf (LOG_WARN, "dict_lookup_grep err, trunc=regular: %d",
612                           r);
613                 break;
614             case 103:        /* regular expression with error correction */
615                 cp = term_sub;
616                 r = 0;
617                 if (*cp == '*' && cp[1] && cp[2])
618                 {
619                     r = atoi (cp+1);
620                     cp += 2;
621                 }
622                 sprintf (term_dict + j, "(%s)", cp);
623                 r = dict_lookup_grep (zi->dict, term_dict, r, grep_info,
624                                       &max_pos, j, grep_handle);
625                 if (r)
626                     logf (LOG_WARN, "dict_lookup_grep err, trunc=eregular: %d",
627                           r);
628                 break;
629             }
630         }
631     }
632     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
633     return 0;
634 }
635
636 static void trans_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
637                         char *termz)
638 {
639     size_t sizez;
640     Z_Term *term = zapt->term;
641
642     sizez = term->u.general->len;
643     if (sizez > IT_MAX_WORD-1)
644         sizez = IT_MAX_WORD-1;
645     memcpy (termz, term->u.general->buf, sizez);
646     termz[sizez] = '\0';
647 }
648
649 static void trans_scan_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
650                              char *termz)
651 {
652     Z_Term *term = zapt->term;
653     char **map;
654     char *cp = (char*) term->u.general->buf;
655     const char *cp_end = cp + term->u.general->len;
656     const char *src;
657     int i = 0;
658     int prev_space = 0;
659     int len;
660     
661     while ((len = (cp_end - cp)) > 0)
662     {
663         map = map_chrs_input (&cp, len);
664         if (**map == *CHR_SPACE)
665         {
666             if (prev_space)
667                 continue;
668             prev_space = 1;
669         } 
670         else
671             prev_space = 0;
672         for (src = *map; *src; src++)
673             termz[i++] = *src;
674     }
675     termz[i] = '\0';
676 }
677
678 static RSET rpn_search_APT_relevance (ZServerInfo *zi, 
679                                       Z_AttributesPlusTerm *zapt,
680                                       oid_value attributeSet,
681                                       int num_bases, char **basenames)
682 {
683     rset_relevance_parms parms;
684     char termz[IT_MAX_WORD+1];
685     char term_sub[IT_MAX_WORD+1];
686     struct grep_info grep_info;
687     char *p0 = termz;
688     RSET result;
689     int term_index = 0;
690
691     parms.key_size = sizeof(struct it_key);
692     parms.max_rec = 100;
693     parms.cmp = key_compare;
694     parms.is = zi->isam;
695     parms.no_terms = 0;
696
697     if (zapt->term->which != Z_Term_general)
698     {
699         zi->errCode = 124;
700         return NULL;
701     }
702     trans_term (zi, zapt, termz);
703
704 #ifdef TERM_COUNT
705     grep_info.term_no = 0;
706 #endif
707     grep_info.isam_p_indx = 0;
708     grep_info.isam_p_size = 0;
709     grep_info.isam_p_buf = NULL;
710     while (1)
711     {
712         char **map;
713         char *p2, *p1;
714
715         p1 = p0;
716         while (*(p0 = p1))
717         {
718             map = map_chrs_input (&p1, strlen(p1));
719             if (**map != *CHR_SPACE)
720                 break;
721         }
722         if (!*p0)
723             break;
724         
725         p1 = p0;
726         while (*(p2 = p1))
727         {
728             map = map_chrs_input (&p1, strlen(p1));
729             if (**map == *CHR_SPACE)
730                 break;
731         }
732         if (p2 == p0)
733             break;
734         memcpy (term_sub, p0, p2-p0);
735         term_sub[p2-p0] = '\0';
736         p0 = p2;
737         if (field_term (zi, zapt, term_sub, 'w', attributeSet, &grep_info,
738                         num_bases, basenames))
739             return NULL;
740 #ifdef TERM_COUNT
741         for (; term_index < grep_info.isam_p_indx; term_index++)
742             grep_info.term_no[term_index] = parms.no_terms;
743         parms.no_terms++;
744 #endif
745     }
746     parms.term_no = grep_info.term_no;
747     parms.isam_positions = grep_info.isam_p_buf;
748     parms.no_isam_positions = grep_info.isam_p_indx;
749     if (grep_info.isam_p_indx > 0)
750         result = rset_create (rset_kind_relevance, &parms);
751     else
752         result = rset_create (rset_kind_null, NULL);
753 #ifdef TERM_COUNT
754     xfree(grep_info.term_no);
755 #endif
756     xfree (grep_info.isam_p_buf);
757     return result;
758 }
759
760 static RSET rpn_search_APT_cphrase (ZServerInfo *zi,
761                                     Z_AttributesPlusTerm *zapt,
762                                     oid_value attributeSet,
763                                     int num_bases, char **basenames)
764 {
765     char termz[IT_MAX_WORD+1];
766     struct grep_info grep_info;
767     RSET result;
768
769     if (zapt->term->which != Z_Term_general)
770     {
771         zi->errCode = 124;
772         return NULL;
773     }
774     trans_term (zi, zapt, termz);
775
776 #ifdef TERM_COUNT
777     grep_info.term_no = 0;
778 #endif
779     grep_info.isam_p_indx = 0;
780     grep_info.isam_p_size = 0;
781     grep_info.isam_p_buf = NULL;
782
783     if (field_term (zi, zapt, termz, 'p', attributeSet, &grep_info,
784                     num_bases, basenames))
785         return NULL;
786     result = rset_trunc (zi, grep_info.isam_p_buf, grep_info.isam_p_indx);
787 #ifdef TERM_COUNT
788     xfree(grep_info.term_no);
789 #endif
790     xfree (grep_info.isam_p_buf);
791     return result;
792 }
793
794 static RSET rpn_prox (RSET *rset, int rset_no)
795 {
796     int i;
797     RSFD *rsfd;
798     int  *more;
799     struct it_key **buf;
800     RSFD rsfd_result;
801     RSET result;
802     rset_temp_parms parms;
803     
804     rsfd = xmalloc (sizeof(*rsfd)*rset_no);
805     more = xmalloc (sizeof(*more)*rset_no);
806     buf = xmalloc (sizeof(*buf)*rset_no);
807
808     for (i = 0; i<rset_no; i++)
809     {
810         buf[i] = xmalloc (sizeof(**buf));
811         rsfd[i] = rset_open (rset[i], RSETF_READ|RSETF_SORT_SYSNO);
812         if (!(more[i] = rset_read (rset[i], rsfd[i], buf[i])))
813         {
814             while (i >= 0)
815             {
816                 rset_close (rset[i], rsfd[i]);
817                 xfree (buf[i]);
818                 --i;
819             }
820             xfree (rsfd);
821             xfree (more);
822             xfree (buf);
823             return rset_create (rset_kind_null, NULL);
824         }
825     }
826     parms.key_size = sizeof (struct it_key);
827     result = rset_create (rset_kind_temp, &parms);
828     rsfd_result = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
829     
830     while (*more)
831     {
832         for (i = 1; i<rset_no; i++)
833         {
834             int cmp;
835             
836             if (!more[i])
837             {
838                 *more = 0;
839                 break;
840             }
841             cmp = key_compare (buf[i], buf[i-1]);
842             if (cmp > 1)
843             {
844                 more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
845                 break;
846             }
847             else if (cmp == 1)
848             {
849                 if (buf[i-1]->seqno+1 != buf[i]->seqno)
850                 {
851                     more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
852                     break;
853                 }
854             }
855             else
856             {
857                 more[i] = rset_read (rset[i], rsfd[i], buf[i]);
858                 break;
859             }
860         }
861         if (i == rset_no)
862         {
863             rset_write (result, rsfd_result, buf[0]);
864             more[0] = rset_read (*rset, *rsfd, *buf);
865         }
866     }
867     
868     for (i = 0; i<rset_no; i++)
869     {
870         rset_close (rset[i], rsfd[i]);
871         xfree (buf[i]);
872     }
873     rset_close (result, rsfd_result);
874     xfree (buf);
875     xfree (more);
876     xfree (rsfd);
877     return result;
878 }
879
880 static RSET rpn_search_APT_phrase (ZServerInfo *zi,
881                                    Z_AttributesPlusTerm *zapt,
882                                    oid_value attributeSet,
883                                    int num_bases, char **basenames)
884 {
885     char termz[IT_MAX_WORD+1];
886     char term_sub[IT_MAX_WORD+1];
887     char *p0 = termz;
888     RSET rset[60], result;
889     int i, rset_no = 0;
890     struct grep_info grep_info;
891
892     if (zapt->term->which != Z_Term_general)
893     {
894         zi->errCode = 124;
895         return NULL;
896     }
897     trans_term (zi, zapt, termz);
898
899 #ifdef TERM_COUNT
900     grep_info.term_no = 0;
901 #endif
902     grep_info.isam_p_size = 0;
903     grep_info.isam_p_buf = NULL;
904
905     while (1)
906     {
907         char **map;
908         char *p2, *p1;
909
910         p1 = p0;
911         while (*(p0 = p1))
912         {
913             map = map_chrs_input (&p1, strlen(p1));
914             if (**map != *CHR_SPACE)
915                 break;
916         }
917         if (!*p0)
918             break;
919         
920         p1 = p0;
921         while (*(p2 = p1))
922         {
923             map = map_chrs_input (&p1, strlen(p1));
924             if (**map == *CHR_SPACE)
925                 break;
926         }
927         if (p2 == p0)
928             break;
929
930         memcpy (term_sub, p0, p2-p0);
931         term_sub[p2-p0] = '\0';
932         p0 = p2;
933
934         grep_info.isam_p_indx = 0;
935         if (field_term (zi, zapt, term_sub, 'w', attributeSet, &grep_info,
936                         num_bases, basenames))
937             return NULL;
938         rset[rset_no] = rset_trunc (zi, grep_info.isam_p_buf,
939                                     grep_info.isam_p_indx);
940         assert (rset[rset_no]);
941         if (++rset_no >= sizeof(rset)/sizeof(*rset))
942             break;
943     }
944 #ifdef TERM_COUNT
945     xfree(grep_info.term_no);
946 #endif
947     xfree (grep_info.isam_p_buf);
948     if (rset_no == 0)
949         return rset_create (rset_kind_null, NULL);
950     else if (rset_no == 1)
951         return (rset[0]);
952
953     result = rpn_prox (rset, rset_no);
954     for (i = 0; i<rset_no; i++)
955         rset_delete (rset[i]);
956     return result;
957 }
958
959 static RSET rpn_search_APT_local (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
960                                   oid_value attributeSet)
961 {
962     RSET result;
963     RSFD rsfd;
964     struct it_key key;
965     rset_temp_parms parms;
966     char termz[IT_MAX_WORD+1];
967
968     if (zapt->term->which != Z_Term_general)
969     {
970         zi->errCode = 124;
971         return NULL;
972     }
973     parms.key_size = sizeof (struct it_key);
974     result = rset_create (rset_kind_temp, &parms);
975     rsfd = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
976
977     trans_term (zi, zapt, termz);
978
979     key.sysno = atoi (termz);
980     if (key.sysno <= 0)
981         key.sysno = 1;
982     rset_write (result, rsfd, &key);
983     rset_close (result, rsfd);
984     return result;
985 }
986
987 static RSET rpn_search_APT (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
988                             oid_value attributeSet,
989                             int num_bases, char **basenames)
990 {
991     AttrType relation;
992     AttrType structure;
993     AttrType completeness;
994     int relation_value, structure_value, completeness_value;
995
996     attr_init (&relation, zapt, 2);
997     attr_init (&structure, zapt, 4);
998     attr_init (&completeness, zapt, 6);
999     
1000     relation_value = attr_find (&relation, NULL);
1001     structure_value = attr_find (&structure, NULL);
1002     completeness_value = attr_find (&completeness, NULL);
1003     switch (structure_value)
1004     {
1005     case -1:
1006         if (relation_value == 102) /* relevance relation */
1007             return rpn_search_APT_relevance (zi, zapt, attributeSet,
1008                                              num_bases, basenames);
1009         if (completeness_value == 2 || completeness_value == 3)
1010             return rpn_search_APT_cphrase (zi, zapt, attributeSet,
1011                                            num_bases, basenames);
1012         return rpn_search_APT_phrase (zi, zapt, attributeSet,
1013                                       num_bases, basenames);
1014     case 1: /* phrase */
1015         if (relation_value == 102) /* relevance relation */
1016             return rpn_search_APT_relevance (zi, zapt, attributeSet,
1017                                              num_bases, basenames);
1018         if (completeness_value == 2 || completeness_value == 3)
1019             return rpn_search_APT_cphrase (zi, zapt, attributeSet,
1020                                            num_bases, basenames);
1021         return rpn_search_APT_phrase (zi, zapt, attributeSet,
1022                                       num_bases, basenames);
1023         break;
1024     case 2: /* word */
1025         if (relation_value == 102) /* relevance relation */
1026             return rpn_search_APT_relevance (zi, zapt, attributeSet,
1027                                              num_bases, basenames);
1028         if (completeness_value == 2 || completeness_value == 3)
1029             return rpn_search_APT_cphrase (zi, zapt, attributeSet,
1030                                            num_bases, basenames);
1031         return rpn_search_APT_phrase (zi, zapt, attributeSet,
1032                                       num_bases, basenames);
1033     case 3: /* key */
1034         break;
1035     case 4: /* year */
1036         break;
1037     case 5: /* date - normalized */
1038         break;
1039     case 6: /* word list */
1040         return rpn_search_APT_relevance (zi, zapt, attributeSet,
1041                                          num_bases, basenames);
1042     case 100: /* date - un-normalized */
1043         break;
1044     case 101: /* name - normalized */
1045         break;
1046     case 102: /* date - un-normalized */
1047         break;
1048     case 103: /* structure */
1049         break;
1050     case 104: /* urx */
1051         break;
1052     case 105: /* free-form-text */
1053         return rpn_search_APT_relevance (zi, zapt, attributeSet,
1054                                          num_bases, basenames);
1055     case 106: /* document-text */
1056         return rpn_search_APT_relevance (zi, zapt, attributeSet,
1057                                          num_bases, basenames);
1058     case 107: /* local-number */
1059         return rpn_search_APT_local (zi, zapt, attributeSet);
1060     case 108: /* string */ 
1061         return rpn_search_APT_phrase (zi, zapt, attributeSet,
1062                                       num_bases, basenames);
1063     case 109: /* numeric string */
1064         break;
1065     }
1066     zi->errCode = 118;
1067     return NULL;
1068 }
1069
1070 static RSET rpn_search_ref (ZServerInfo *zi, Z_ResultSetId *resultSetId)
1071 {
1072     ZServerSet *s;
1073
1074     if (!(s = resultSetGet (zi, resultSetId)))
1075         return rset_create (rset_kind_null, NULL);
1076     return s->rset;
1077 }
1078
1079 static RSET rpn_search_structure (ZServerInfo *zi, Z_RPNStructure *zs,
1080                                   oid_value attributeSet,
1081                                   int num_bases, char **basenames)
1082 {
1083     RSET r = NULL;
1084     if (zs->which == Z_RPNStructure_complex)
1085     {
1086         rset_bool_parms bool_parms;
1087         int soft = 0;
1088
1089         bool_parms.rset_l = rpn_search_structure (zi, zs->u.complex->s1,
1090                                                   attributeSet,
1091                                                   num_bases, basenames);
1092         if (bool_parms.rset_l == NULL)
1093             return NULL;
1094         if (rset_is_ranked(bool_parms.rset_l))
1095             soft = 1;
1096         bool_parms.rset_r = rpn_search_structure (zi, zs->u.complex->s2,
1097                                                   attributeSet,
1098                                                   num_bases, basenames);
1099         if (bool_parms.rset_r == NULL)
1100         {
1101             rset_delete (bool_parms.rset_l);
1102             return NULL;
1103         }
1104         if (rset_is_ranked(bool_parms.rset_r))
1105             soft = 1;
1106         bool_parms.key_size = sizeof(struct it_key);
1107         bool_parms.cmp = key_compare;
1108
1109         switch (zs->u.complex->roperator->which)
1110         {
1111         case Z_Operator_and:
1112             r = rset_create (soft ? rset_kind_sand:rset_kind_and, &bool_parms);
1113             break;
1114         case Z_Operator_or:
1115             r = rset_create (soft ? rset_kind_sor:rset_kind_or, &bool_parms);
1116             break;
1117         case Z_Operator_and_not:
1118             r = rset_create (soft ? rset_kind_snot:rset_kind_not, &bool_parms);
1119             break;
1120         default:
1121             assert (0);
1122         }
1123     }
1124     else if (zs->which == Z_RPNStructure_simple)
1125     {
1126         if (zs->u.simple->which == Z_Operand_APT)
1127         {
1128             logf (LOG_DEBUG, "rpn_search_APT");
1129             r = rpn_search_APT (zi, zs->u.simple->u.attributesPlusTerm,
1130                                 attributeSet, num_bases, basenames);
1131         }
1132         else if (zs->u.simple->which == Z_Operand_resultSetId)
1133         {
1134             logf (LOG_DEBUG, "rpn_search_ref");
1135             r = rpn_search_ref (zi, zs->u.simple->u.resultSetId);
1136         }
1137         else
1138         {
1139             assert (0);
1140         }
1141     }
1142     else
1143     {
1144         assert (0);
1145     }
1146     return r;
1147 }
1148
1149 void count_set_save (RSET *r, int *count)
1150 {
1151     int psysno = 0;
1152     int kno = 0;
1153     struct it_key key;
1154     RSFD rfd, wfd;
1155     RSET w;
1156     rset_temp_parms parms;
1157
1158     logf (LOG_DEBUG, "count_set_save");
1159     *count = 0;
1160     parms.key_size = sizeof(struct it_key);
1161     w = rset_create (rset_kind_temp, &parms);
1162     wfd = rset_open (w, RSETF_WRITE|RSETF_SORT_SYSNO);
1163     rfd = rset_open (*r, RSETF_READ|RSETF_SORT_SYSNO);
1164     while (rset_read (*r, rfd, &key))
1165     {
1166         logf (LOG_DEBUG, "sysno=%-7d seqno=%d", key.sysno, key.seqno);
1167         if (key.sysno != psysno)
1168         {
1169             rset_write (w, wfd, &key);
1170             psysno = key.sysno;
1171             (*count)++;
1172         }
1173         kno++;
1174     }
1175     rset_close (*r, rfd);
1176     rset_delete (*r);
1177     rset_close (w, wfd);
1178     *r = w;
1179     logf (LOG_DEBUG, "%d keys, %d distinct sysnos", kno, *count);
1180 }
1181
1182 static void count_set (RSET r, int *count)
1183 {
1184     int psysno = 0;
1185     int kno = 0;
1186     struct it_key key;
1187     RSFD rfd;
1188
1189     logf (LOG_DEBUG, "count_set");
1190     *count = 0;
1191     rfd = rset_open (r, RSETF_READ|RSETF_SORT_SYSNO);
1192     while (rset_read (r, rfd, &key))
1193     {
1194         if (key.sysno != psysno)
1195         {
1196             psysno = key.sysno;
1197             (*count)++;
1198         }
1199         kno++;
1200     }
1201     rset_close (r, rfd);
1202     logf (LOG_DEBUG, "%d keys, %d distinct sysnos", kno, *count);
1203 }
1204
1205 int rpn_search (ZServerInfo *zi,
1206                 Z_RPNQuery *rpn, int num_bases, char **basenames, 
1207                 const char *setname, int *hits)
1208 {
1209     RSET rset;
1210     oident *attrset;
1211     oid_value attributeSet;
1212
1213     dict_grep_cmap (zi->dict, map_chrs_input);
1214     zlog_rpn (rpn);
1215
1216     zi->errCode = 0;
1217     zi->errString = NULL;
1218
1219     attrset = oid_getentbyoid (rpn->attributeSetId);
1220     attributeSet = attrset->value;
1221     rset = rpn_search_structure (zi, rpn->RPNStructure, attributeSet,
1222                                  num_bases, basenames);
1223     if (!rset)
1224         return zi->errCode;
1225     if (rset_is_volatile(rset))
1226         count_set_save(&rset,hits);
1227     else
1228         count_set (rset, hits);
1229     resultSetAdd (zi, setname, 1, rset);
1230     if (zi->errCode)
1231         logf (LOG_DEBUG, "search error: %d", zi->errCode);
1232     return zi->errCode;
1233 }
1234
1235 struct scan_info_entry {
1236     char *term;
1237     ISAM_P isam_p;
1238 };
1239
1240 struct scan_info {
1241     struct scan_info_entry *list;
1242     ODR odr;
1243     int before, after;
1244     char prefix[20];
1245 };
1246
1247 static int scan_handle (char *name, const char *info, int pos, void *client)
1248 {
1249     int len_prefix, idx;
1250     struct scan_info *scan_info = client;
1251
1252     len_prefix = strlen(scan_info->prefix);
1253     if (memcmp (name, scan_info->prefix, len_prefix))
1254         return 1;
1255     if (pos > 0)
1256         idx = scan_info->after - pos + scan_info->before;
1257     else
1258         idx = - pos - 1;
1259     logf (LOG_DEBUG, "%-3d %s", idx, name+len_prefix);
1260     scan_info->list[idx].term = odr_malloc (scan_info->odr,
1261                                             strlen(name + len_prefix)+1);
1262     strcpy (scan_info->list[idx].term, name + len_prefix);
1263     assert (*info == sizeof(ISAM_P));
1264     memcpy (&scan_info->list[idx].isam_p, info+1, sizeof(ISAM_P));
1265     return 0;
1266 }
1267
1268
1269 static void scan_term_untrans (ODR odr, char **dstp, const char *src)
1270 {    
1271     char *dst = odr_malloc (odr, strlen(src)*2+1);
1272     *dstp = dst;
1273
1274     while (*src)
1275     {
1276         const char *cp = map_chrs_output (&src);
1277         while (*cp)
1278             *dst++ = *cp++;
1279     }
1280     *dst = '\0';
1281 }
1282
1283 int rpn_scan (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
1284               oid_value attributeset,
1285               int num_bases, char **basenames,
1286               int *position, int *num_entries, struct scan_entry **list,
1287               int *status)
1288 {
1289     int i;
1290     int pos = *position;
1291     int num = *num_entries;
1292     int before;
1293     int after;
1294     int base_no;
1295     char termz[IT_MAX_WORD+20];
1296     AttrType use;
1297     int use_value;
1298     AttrType completeness;
1299     int completeness_value;
1300     struct scan_info *scan_info_array;
1301     struct scan_entry *glist;
1302     int ords[32], ord_no = 0;
1303     int ptr[32];
1304
1305     logf (LOG_DEBUG, "scan, position = %d, num = %d", pos, num);
1306
1307     if (attributeset == VAL_NONE)
1308         attributeset = VAL_BIB1;
1309         
1310     attr_init (&use, zapt, 1);
1311     use_value = attr_find (&use, &attributeset);
1312     logf (LOG_DEBUG, "use value %d", use_value);
1313
1314     attr_init (&completeness, zapt, 6);
1315     completeness_value = attr_find (&completeness, NULL);
1316     logf (LOG_DEBUG, "completeness value %d", completeness_value);
1317
1318     if (use_value == -1)
1319         use_value = 1016;
1320     for (base_no = 0; base_no < num_bases && ord_no < 32; base_no++)
1321     {
1322         attent *attp;
1323         data1_local_attribute *local_attr;
1324
1325         attp = att_getentbyatt (attributeset, use_value);
1326         if (!attp)
1327         {
1328             logf (LOG_DEBUG, "att_getentbyatt fail. set=%d use=%d",
1329                   attributeset, use_value);
1330             return zi->errCode = 114;
1331         }
1332         if (zebTargetInfo_curDatabase (zi->zti, basenames[base_no]))
1333         {
1334             zi->errString = basenames[base_no];
1335             return zi->errCode = 109; /* Database unavailable */
1336         }
1337         for (local_attr = attp->local_attributes; local_attr && ord_no < 32;
1338              local_attr = local_attr->next)
1339         {
1340             int ord;
1341
1342             ord = zebTargetInfo_lookupSU (zi->zti, attp->attset_ordinal,
1343                                           local_attr->local);
1344             if (ord > 0)
1345                 ords[ord_no++] = ord;
1346         }
1347     }
1348     if (ord_no == 0)
1349         return zi->errCode = 113;
1350     before = pos-1;
1351     after = 1+num-pos;
1352     scan_info_array = odr_malloc (zi->odr, ord_no * sizeof(*scan_info_array));
1353     for (i = 0; i < ord_no; i++)
1354     {
1355         int j, prefix_len = 0;
1356         int before_tmp = before, after_tmp = after;
1357         struct scan_info *scan_info = scan_info_array + i;
1358
1359         scan_info->before = before;
1360         scan_info->after = after;
1361         scan_info->odr = zi->odr;
1362
1363         scan_info->list = odr_malloc (zi->odr, (before+after)*
1364                                       sizeof(*scan_info->list));
1365         for (j = 0; j<before+after; j++)
1366             scan_info->list[j].term = NULL;
1367         termz[prefix_len++] = ords[i];
1368         termz[prefix_len++] =
1369             (completeness_value==2 || completeness_value==3) ? 'p': 'w';
1370         termz[prefix_len] = 0;
1371         strcpy (scan_info->prefix, termz);
1372
1373         trans_scan_term (zi, zapt, termz+prefix_len);
1374                     
1375         dict_scan (zi->dict, termz, &before_tmp, &after_tmp, scan_info,
1376                    scan_handle);
1377     }
1378     glist = odr_malloc (zi->odr, (before+after)*sizeof(*glist));
1379     for (i = 0; i < ord_no; i++)
1380         ptr[i] = before;
1381     
1382     *status = BEND_SCAN_SUCCESS;
1383     for (i = 0; i<after; i++)
1384     {
1385         int j, j0 = -1;
1386         const char *mterm = NULL;
1387         const char *tst;
1388         RSET rset;
1389         
1390         for (j = 0; j < ord_no; j++)
1391         {
1392             if (ptr[j] < before+after &&
1393                 (tst=scan_info_array[j].list[ptr[j]].term) &&
1394                 (!mterm || strcmp (tst, mterm) < 0))
1395             {
1396                 j0 = j;
1397                 mterm = tst;
1398             }
1399         }
1400         if (j0 == -1)
1401             break;
1402         scan_term_untrans (zi->odr, &glist[i+before].term, mterm);
1403         rset = rset_trunc (zi, &scan_info_array[j0].list[ptr[j0]].isam_p, 1);
1404
1405         ptr[j0]++;
1406         for (j = j0+1; j<ord_no; j++)
1407         {
1408             if (ptr[j] < before+after &&
1409                 (tst=scan_info_array[j].list[ptr[j]].term) &&
1410                 !strcmp (tst, mterm))
1411             {
1412                 rset_bool_parms bool_parms;
1413                 RSET rset2;
1414
1415                 rset2 =
1416                    rset_trunc (zi, &scan_info_array[j].list[ptr[j]].isam_p, 1);
1417
1418                 bool_parms.key_size = sizeof(struct it_key);
1419                 bool_parms.cmp = key_compare;
1420                 bool_parms.rset_l = rset;
1421                 bool_parms.rset_r = rset2;
1422               
1423                 rset = rset_create (rset_kind_or, &bool_parms);
1424
1425                 ptr[j]++;
1426             }
1427         }
1428         count_set (rset, &glist[i+before].occurrences);
1429         rset_delete (rset);
1430     }
1431     if (i < after)
1432     {
1433         *num_entries -= (after-i);
1434         *status = BEND_SCAN_PARTIAL;
1435     }
1436
1437     for (i = 0; i<ord_no; i++)
1438         ptr[i] = 0;
1439
1440     for (i = 0; i<before; i++)
1441     {
1442         int j, j0 = -1;
1443         const char *mterm = NULL;
1444         const char *tst;
1445         RSET rset;
1446         
1447         for (j = 0; j <ord_no; j++)
1448         {
1449             if (ptr[j] < before &&
1450                 (tst=scan_info_array[j].list[before-1-ptr[j]].term) &&
1451                 (!mterm || strcmp (tst, mterm) > 0))
1452             {
1453                 j0 = j;
1454                 mterm = tst;
1455             }
1456         }
1457         if (j0 == -1)
1458             break;
1459
1460         scan_term_untrans (zi->odr, &glist[before-1-i].term, mterm);
1461
1462         rset = rset_trunc
1463                (zi, &scan_info_array[j0].list[before-1-ptr[j0]].isam_p, 1);
1464
1465         ptr[j0]++;
1466
1467         for (j = j0+1; j<ord_no; j++)
1468         {
1469             if (ptr[j] < before &&
1470                 (tst=scan_info_array[j].list[before-1-ptr[j]].term) &&
1471                 !strcmp (tst, mterm))
1472             {
1473                 rset_bool_parms bool_parms;
1474                 RSET rset2;
1475
1476                 rset2 = rset_trunc (zi,
1477                          &scan_info_array[j].list[before-1-ptr[j]].isam_p, 1);
1478
1479                 bool_parms.key_size = sizeof(struct it_key);
1480                 bool_parms.cmp = key_compare;
1481                 bool_parms.rset_l = rset;
1482                 bool_parms.rset_r = rset2;
1483               
1484                 rset = rset_create (rset_kind_or, &bool_parms);
1485
1486                 ptr[j]++;
1487             }
1488         }
1489         count_set (rset, &glist[before-1-i].occurrences);
1490         rset_delete (rset);
1491     }
1492     i = before-i;
1493     if (i)
1494     {
1495         *status = BEND_SCAN_PARTIAL;
1496         *position -= i;
1497         *num_entries -= i;
1498     }
1499     *list = glist + i;               /* list is set to first 'real' entry */
1500     
1501     logf (LOG_DEBUG, "position = %d, num_entries = %d",
1502           *position, *num_entries);
1503     if (zi->errCode)
1504         logf (LOG_DEBUG, "scan error: %d", zi->errCode);
1505     return zi->errCode;
1506 }
1507