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