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