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