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