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