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