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