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