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