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