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