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