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