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