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