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