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