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