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