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