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