Fixed scan: server could break if bad attribute/database was selected.
[idzebra-moved-to-github.git] / index / zrpn.c
1 /*
2  * Copyright (C) 1995-2000, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zrpn.c,v $
7  * Revision 1.105  2000-11-08 13:46:59  adam
8  * Fixed scan: server could break if bad attribute/database was selected.
9  * Work on remote update.
10  *
11  * Revision 1.104  2000/04/05 09:49:35  adam
12  * On Unix, zebra/z'mbol uses automake.
13  *
14  * Revision 1.103  2000/03/20 19:08:36  adam
15  * Added remote record import using Z39.50 extended services and Segment
16  * Requests.
17  *
18  * Revision 1.102  2000/03/15 15:00:31  adam
19  * First work on threaded version.
20  *
21  * Revision 1.101  2000/03/02 14:35:03  adam
22  * Fixed proximity handling.
23  *
24  * Revision 1.100  1999/12/28 15:48:12  adam
25  * Minor Fix.
26  *
27  * Revision 1.99  1999/12/23 09:03:32  adam
28  * Changed behaviour of trunc=105 so that * is regular .* and ! is regular .
29  *
30  * Revision 1.98  1999/11/30 13:48:04  adam
31  * Improved installation. Updated for inclusion of YAZ header files.
32  *
33  * Revision 1.97  1999/10/14 14:33:50  adam
34  * Added truncation 5=106.
35  *
36  * Revision 1.96  1999/09/23 10:05:05  adam
37  * Implemented structure=105 searching.
38  *
39  * Revision 1.95  1999/09/07 07:19:21  adam
40  * Work on character mapping. Implemented replace rules.
41  *
42  * Revision 1.94  1999/07/20 13:59:18  adam
43  * Fixed bug that occurred when phrases had 0 hits.
44  *
45  * Revision 1.93  1999/06/17 14:38:40  adam
46  * Bug fix: Scan SEGV'ed when getting unknown use attribute.
47  *
48  * Revision 1.92  1999/05/26 07:49:13  adam
49  * C++ compilation.
50  *
51  * Revision 1.91  1999/02/02 14:51:13  adam
52  * Updated WIN32 code specific sections. Changed header.
53  *
54  * Revision 1.90  1998/11/16 16:03:43  adam
55  * Moved loggin utilities to Yaz. Was implemented in file zlogs.c.
56  *
57  * Revision 1.89  1998/11/16 10:11:55  adam
58  * Added addtional info for error 114 - unsupported use attribute.
59  *
60  * Revision 1.88  1998/10/18 07:54:52  adam
61  * Additional info added for diagnostics 114 (Unsupported use attribute) and
62  * 121 (Unsupported attribute set).
63  *
64  * Revision 1.87  1998/09/28 11:19:12  adam
65  * Fix for Compiled ASN.1.
66  *
67  * Revision 1.86  1998/09/22 10:48:20  adam
68  * Minor changes in search API.
69  *
70  * Revision 1.85  1998/09/22 10:03:43  adam
71  * Changed result sets to be persistent in the sense that they can
72  * be re-searched if needed.
73  * Fixed memory leak in rsm_or.
74  *
75  * Revision 1.84  1998/09/18 12:41:00  adam
76  * Fixed bug with numerical relations.
77  *
78  * Revision 1.83  1998/09/02 13:53:19  adam
79  * Extra parameter decode added to search routines to implement
80  * persistent queries.
81  *
82  * Revision 1.82  1998/06/26 11:16:40  quinn
83  * Added support (un-optimised) for left and left/right truncation
84  *
85  * Revision 1.81  1998/06/24 12:16:14  adam
86  * Support for relations on text operands. Open range support in
87  * DFA module (i.e. [-j], [g-]).
88  *
89  * Revision 1.80  1998/06/23 15:33:34  adam
90  * Added feature to specify sort criteria in query (type 7 specifies
91  * sort flags).
92  *
93  * Revision 1.79  1998/06/22 11:35:09  adam
94  * Minor changes.
95  *
96  * Revision 1.78  1998/06/08 14:43:17  adam
97  * Added suport for EXPLAIN Proxy servers - added settings databasePath
98  * and explainDatabase to facilitate this. Increased maximum number
99  * of databases and attributes in one register.
100  *
101  * Revision 1.77  1998/05/20 10:12:22  adam
102  * Implemented automatic EXPLAIN database maintenance.
103  * Modified Zebra to work with ASN.1 compiled version of YAZ.
104  *
105  * Revision 1.76  1998/04/02 14:35:29  adam
106  * First version of Zebra that works with compiled ASN.1.
107  *
108  * Revision 1.75  1998/03/05 08:45:13  adam
109  * New result set model and modular ranking system. Moved towards
110  * descent server API. System information stored as "SGML" records.
111  *
112  * Revision 1.74  1998/02/10 12:03:06  adam
113  * Implemented Sort.
114  *
115  * Revision 1.73  1998/01/29 13:40:11  adam
116  * Better logging for scan service.
117  *
118  * Revision 1.72  1998/01/07 13:53:41  adam
119  * Queries using simple ranked operands returns right number of hits.
120  *
121  * Revision 1.71  1997/12/18 10:54:24  adam
122  * New method result set method rs_hits that returns the number of
123  * hits in result-set (if known). The ranked result set returns real
124  * number of hits but only when not combined with other operands.
125  *
126  * Revision 1.70  1997/10/31 12:34:43  adam
127  * Changed a few log statements.
128  *
129  * Revision 1.69  1997/10/29 12:05:02  adam
130  * Server produces diagnostic "Unsupported Attribute Set" when appropriate.
131  *
132  * Revision 1.68  1997/10/27 14:33:06  adam
133  * Moved towards generic character mapping depending on "structure"
134  * field in abstract syntax file. Fixed a few memory leaks. Fixed
135  * bug with negative integers when doing searches with relational
136  * operators.
137  *
138  * Revision 1.67  1997/09/29 09:06:10  adam
139  * Removed one static var in order to make this module thread safe.
140  *
141  * Revision 1.66  1997/09/25 14:58:03  adam
142  * Windows NT port.
143  *
144  * Revision 1.65  1997/09/22 12:39:06  adam
145  * Added get_pos method for the ranked result sets.
146  *
147  * Revision 1.64  1997/09/18 08:59:20  adam
148  * Extra generic handle for the character mapping routines.
149  *
150  * Revision 1.63  1997/09/17 12:19:18  adam
151  * Zebra version corresponds to YAZ version 1.4.
152  * Changed Zebra server so that it doesn't depend on global common_resource.
153  *
154  * Revision 1.62  1997/09/05 15:30:09  adam
155  * Changed prototype for chr_map_input - added const.
156  * Added support for C++, headers uses extern "C" for public definitions.
157  *
158  * Revision 1.61  1997/02/10 10:21:14  adam
159  * Bug fix: in search terms character (^) wasn't observed.
160  *
161  * Revision 1.60  1997/01/31 11:10:34  adam
162  * Bug fix: Leading and trailing white space weren't removed in scan tokens.
163  *
164  * Revision 1.59  1997/01/17 11:31:46  adam
165  * Bug fix: complete phrase search didn't work.
166  *
167  * Revision 1.58  1996/12/23 15:30:45  adam
168  * Work on truncation.
169  * Bug fix: result sets weren't deleted after server shut down.
170  *
171  * Revision 1.57  1996/11/11 13:38:02  adam
172  * Added proximity support in search.
173  *
174  * Revision 1.56  1996/11/08 11:10:32  adam
175  * Buffers used during file match got bigger.
176  * Compressed ISAM support everywhere.
177  * Bug fixes regarding masking characters in queries.
178  * Redesigned Regexp-2 queries.
179  *
180  * Revision 1.55  1996/11/04 14:07:44  adam
181  * Moved truncation code to trunc.c.
182  *
183  * Revision 1.54  1996/10/29 14:09:52  adam
184  * Use of cisam system - enabled if setting isamc is 1.
185  *
186  * Revision 1.53  1996/06/26 09:21:43  adam
187  * Bug fix: local attribute set wasn't obeyed in scan.
188  *
189  * Revision 1.52  1996/06/17  14:26:20  adam
190  * Function gen_regular_rel changed to handle negative numbers.
191  *
192  * Revision 1.51  1996/06/11 10:54:15  quinn
193  * Relevance work
194  *
195  * Revision 1.50  1996/06/07  08:51:53  adam
196  * Bug fix: Character mapping was broken (introducued by last revision).
197  *
198  * Revision 1.49  1996/06/04  10:18:11  adam
199  * Search/scan uses character mapping module.
200  *
201  * Revision 1.48  1996/05/28  15:15:01  adam
202  * Bug fix: Didn't handle unknown database correctly.
203  *
204  * Revision 1.47  1996/05/15  18:36:28  adam
205  * Function trans_term transforms unsearchable characters to blanks.
206  *
207  * Revision 1.46  1996/05/15  11:57:56  adam
208  * Fixed bug introduced by set/field mapping in search operations.
209  *
210  * Revision 1.45  1996/05/14  11:34:00  adam
211  * Scan support in multiple registers/databases.
212  *
213  * Revision 1.44  1996/05/14  06:16:44  adam
214  * Compact use/set bytes used in search service.
215  *
216  * Revision 1.43  1996/05/09 09:54:43  adam
217  * Server supports maps from one logical attributes to a list of physical
218  * attributes.
219  * The extraction process doesn't make space consuming 'any' keys.
220  *
221  * Revision 1.42  1996/05/09  07:28:56  quinn
222  * Work towards phrases and multiple registers
223  *
224  * Revision 1.41  1996/03/20  09:36:43  adam
225  * Function dict_lookup_grep got extra parameter, init_pos, which marks
226  * from which position in pattern approximate pattern matching should occur.
227  * Approximate pattern matching is used in relevance=re-2.
228  *
229  * Revision 1.40  1996/02/02  13:44:44  adam
230  * The public dictionary functions simply use char instead of Dict_char
231  * to represent search strings. Dict_char is used internally only.
232  *
233  * Revision 1.39  1996/01/03  16:22:13  quinn
234  * operator->roperator
235  *
236  * Revision 1.38  1995/12/11  09:12:55  adam
237  * The rec_get function returns NULL if record doesn't exist - will
238  * happen in the server if the result set records have been deleted since
239  * the creation of the set (i.e. the search).
240  * The server saves a result temporarily if it is 'volatile', i.e. the
241  * set is register dependent.
242  *
243  * Revision 1.37  1995/12/06  15:05:28  adam
244  * More verbose in count_set.
245  *
246  * Revision 1.36  1995/12/06  12:41:27  adam
247  * New command 'stat' for the index program.
248  * Filenames can be read from stdin by specifying '-'.
249  * Bug fix/enhancement of the transformation from terms to regular
250  * expressons in the search engine.
251  *
252  * Revision 1.35  1995/11/27  09:29:00  adam
253  * Bug fixes regarding conversion to regular expressions.
254  *
255  * Revision 1.34  1995/11/16  17:00:56  adam
256  * Better logging of rpn query.
257  *
258  * Revision 1.33  1995/11/01  13:58:28  quinn
259  * Moving data1 to yaz/retrieval
260  *
261  * Revision 1.32  1995/10/27  14:00:11  adam
262  * Implemented detection of database availability.
263  *
264  * Revision 1.31  1995/10/17  18:02:10  adam
265  * New feature: databases. Implemented as prefix to words in dictionary.
266  *
267  * Revision 1.30  1995/10/16  09:32:38  adam
268  * More work on relational op.
269  *
270  * Revision 1.29  1995/10/13  16:01:49  adam
271  * Work on relations.
272  *
273  * Revision 1.28  1995/10/13  12:26:43  adam
274  * Optimization of truncation.
275  *
276  * Revision 1.27  1995/10/12  17:07:22  adam
277  * Truncation works.
278  *
279  * Revision 1.26  1995/10/12  12:40:54  adam
280  * Bug fixes in rpn_prox.
281  *
282  * Revision 1.25  1995/10/10  13:59:24  adam
283  * Function rset_open changed its wflag parameter to general flags.
284  *
285  * Revision 1.24  1995/10/09  16:18:37  adam
286  * Function dict_lookup_grep got extra client data parameter.
287  *
288  * Revision 1.23  1995/10/06  16:33:37  adam
289  * Use attribute mappings.
290  *
291  * Revision 1.22  1995/10/06  15:07:39  adam
292  * Structure 'local-number' handled.
293  *
294  * Revision 1.21  1995/10/06  13:52:06  adam
295  * Bug fixes. Handler may abort further scanning.
296  *
297  * Revision 1.20  1995/10/06  11:06:33  adam
298  * Scan entries include 'occurrences' now.
299  *
300  * Revision 1.19  1995/10/06  10:43:56  adam
301  * Scan added. 'occurrences' in scan entries not set yet.
302  *
303  * Revision 1.18  1995/10/04  16:57:20  adam
304  * Key input and merge sort in one pass.
305  *
306  * Revision 1.17  1995/10/04  12:55:17  adam
307  * Bug fix in ranked search. Use=Any keys inserted.
308  *
309  * Revision 1.16  1995/10/02  16:24:40  adam
310  * Use attribute actually used in search requests.
311  *
312  * Revision 1.15  1995/10/02  15:18:52  adam
313  * New member in recRetrieveCtrl: diagnostic.
314  *
315  * Revision 1.14  1995/09/28  12:10:32  adam
316  * Bug fixes. Field prefix used in queries.
317  *
318  * Revision 1.13  1995/09/18  14:17:50  adam
319  * Minor changes.
320  *
321  * Revision 1.12  1995/09/15  14:45:21  adam
322  * Retrieve control.
323  * Work on truncation.
324  *
325  * Revision 1.11  1995/09/14  11:53:27  adam
326  * First work on regular expressions/truncations.
327  *
328  * Revision 1.10  1995/09/11  15:23:26  adam
329  * More work on relevance search.
330  *
331  * Revision 1.9  1995/09/11  13:09:35  adam
332  * More work on relevance feedback.
333  *
334  * Revision 1.8  1995/09/08  14:52:27  adam
335  * Minor changes. Dictionary is lower case now.
336  *
337  * Revision 1.7  1995/09/07  13:58:36  adam
338  * New parameter: result-set file descriptor (RSFD) to support multiple
339  * positions within the same result-set.
340  * Boolean operators: and, or, not implemented.
341  * Result-set references.
342  *
343  * Revision 1.6  1995/09/06  16:11:18  adam
344  * Option: only one word key per file.
345  *
346  * Revision 1.5  1995/09/06  10:33:04  adam
347  * More work on present. Some log messages removed.
348  *
349  * Revision 1.4  1995/09/05  15:28:40  adam
350  * More work on search engine.
351  *
352  * Revision 1.3  1995/09/04  15:20:22  adam
353  * Minor changes.
354  *
355  * Revision 1.2  1995/09/04  12:33:43  adam
356  * Various cleanup. YAZ util used instead.
357  *
358  * Revision 1.1  1995/09/04  09:10:40  adam
359  * More work on index add/del/update.
360  * Merge sort implemented.
361  * Initial work on z39 server.
362  *
363  */
364 #include <stdio.h>
365 #include <assert.h>
366 #ifdef WIN32
367 #include <io.h>
368 #else
369 #include <unistd.h>
370 #endif
371 #include <ctype.h>
372
373 #include "zserver.h"
374
375 #include <charmap.h>
376 #include <rstemp.h>
377 #include <rsnull.h>
378 #include <rsbool.h>
379
380 struct rpn_char_map_info {
381     ZebraMaps zm;
382     int reg_type;
383 };
384
385 static const char **rpn_char_map_handler (void *vp, const char **from, int len)
386 {
387     struct rpn_char_map_info *p = (struct rpn_char_map_info *) vp;
388     return zebra_maps_input (p->zm, p->reg_type, from, len);
389 }
390
391 static void rpn_char_map_prepare (ZebraHandle zh, int reg_type,
392                                   struct rpn_char_map_info *map_info)
393 {
394     map_info->zm = zh->service->zebra_maps;
395     map_info->reg_type = reg_type;
396     dict_grep_cmap (zh->service->dict, map_info, rpn_char_map_handler);
397 }
398
399 typedef struct {
400     int type;
401     int major;
402     int minor;
403     Z_AttributesPlusTerm *zapt;
404 } AttrType;
405
406 static int attr_find (AttrType *src, oid_value *attributeSetP)
407 {
408     int num_attributes;
409
410 #ifdef ASN_COMPILED
411     num_attributes = src->zapt->attributes->num_attributes;
412 #else
413     num_attributes = src->zapt->num_attributes;
414 #endif
415     while (src->major < num_attributes)
416     {
417         Z_AttributeElement *element;
418
419 #ifdef ASN_COMPILED
420         element = src->zapt->attributes->attributes[src->major];
421 #else
422         element = src->zapt->attributeList[src->major];
423 #endif
424         if (src->type == *element->attributeType)
425         {
426             switch (element->which) 
427             {
428             case Z_AttributeValue_numeric:
429                 ++(src->major);
430                 if (element->attributeSet && attributeSetP)
431                 {
432                     oident *attrset;
433
434                     attrset = oid_getentbyoid (element->attributeSet);
435                     *attributeSetP = attrset->value;
436                 }
437                 return *element->value.numeric;
438                 break;
439             case Z_AttributeValue_complex:
440                 if (src->minor >= element->value.complex->num_list ||
441                     element->value.complex->list[src->minor]->which !=  
442                     Z_StringOrNumeric_numeric)
443                     break;
444                 ++(src->minor);
445                 if (element->attributeSet && attributeSetP)
446                 {
447                     oident *attrset;
448
449                     attrset = oid_getentbyoid (element->attributeSet);
450                     *attributeSetP = attrset->value;
451                 }
452                 return *element->value.complex->list[src->minor-1]->u.numeric;
453             default:
454                 assert (0);
455             }
456         }
457         ++(src->major);
458     }
459     return -1;
460 }
461
462 static void attr_init (AttrType *src, Z_AttributesPlusTerm *zapt,
463                        int type)
464 {
465     src->zapt = zapt;
466     src->type = type;
467     src->major = 0;
468     src->minor = 0;
469 }
470
471 #define TERM_COUNT        
472        
473 struct grep_info {        
474 #ifdef TERM_COUNT        
475     int *term_no;        
476 #endif        
477     ISAMS_P *isam_p_buf;
478     int isam_p_size;        
479     int isam_p_indx;
480     ZebraHandle zh;
481     int reg_type;
482 };        
483
484 static void term_untrans  (ZebraHandle zh, int reg_type,
485                            char *dst, const char *src)
486 {
487     while (*src)
488     {
489         const char *cp = zebra_maps_output (zh->service->zebra_maps,
490                                             reg_type, &src);
491         if (!cp)
492             *dst++ = *src++;
493         else
494             while (*cp)
495                 *dst++ = *cp++;
496     }
497     *dst = '\0';
498 }
499
500 static void add_isam_p (const char *name, const char *info,
501                         struct grep_info *p)
502 {
503     if (p->isam_p_indx == p->isam_p_size)
504     {
505         ISAMS_P *new_isam_p_buf;
506 #ifdef TERM_COUNT        
507         int *new_term_no;        
508 #endif
509         p->isam_p_size = 2*p->isam_p_size + 100;
510         new_isam_p_buf = (ISAMS_P *) xmalloc (sizeof(*new_isam_p_buf) *
511                                              p->isam_p_size);
512         if (p->isam_p_buf)
513         {
514             memcpy (new_isam_p_buf, p->isam_p_buf,
515                     p->isam_p_indx * sizeof(*p->isam_p_buf));
516             xfree (p->isam_p_buf);
517         }
518         p->isam_p_buf = new_isam_p_buf;
519
520 #ifdef TERM_COUNT
521         new_term_no = (int *) xmalloc (sizeof(*new_term_no) *
522                                        p->isam_p_size);
523         if (p->term_no)
524         {
525             memcpy (new_term_no, p->isam_p_buf,
526                     p->isam_p_indx * sizeof(*p->term_no));
527             xfree (p->term_no);
528         }
529         p->term_no = new_term_no;
530 #endif
531     }
532     assert (*info == sizeof(*p->isam_p_buf));
533     memcpy (p->isam_p_buf + p->isam_p_indx, info+1, sizeof(*p->isam_p_buf));
534
535 #if 0
536     term_untrans  (p->zh, p->reg_type, term_tmp, name+2);
537     logf (LOG_DEBUG, "grep: %s", term_tmp);
538 #endif
539     (p->isam_p_indx)++;
540 }
541
542 static int grep_handle (char *name, const char *info, void *p)
543 {
544     add_isam_p (name, info, (struct grep_info *) p);
545     return 0;
546 }
547
548 static int term_pre (ZebraMaps zebra_maps, int reg_type, const char **src,
549                      const char *ct1, const char *ct2)
550 {
551     const char *s1, *s0 = *src;
552     const char **map;
553
554     /* skip white space */
555     while (*s0)
556     {
557         if (ct1 && strchr (ct1, *s0))
558             break;
559         if (ct2 && strchr (ct2, *s0))
560             break;
561         s1 = s0;
562         map = zebra_maps_input (zebra_maps, reg_type, &s1, strlen(s1));
563         if (**map != *CHR_SPACE)
564             break;
565         s0 = s1;
566     }
567     *src = s0;
568     return *s0;
569 }
570
571 /* term_100: handle term, where trunc=none (no operators at all) */
572 static int term_100 (ZebraMaps zebra_maps, int reg_type,
573                      const char **src, char *dst, int space_split,
574                      char *dst_term)
575 {
576     const char *s0, *s1;
577     const char **map;
578     int i = 0;
579     int j = 0;
580
581     if (!term_pre (zebra_maps, reg_type, src, NULL, NULL))
582         return 0;
583     s0 = *src;
584     while (*s0)
585     {
586         s1 = s0;
587         map = zebra_maps_input (zebra_maps, reg_type, &s0, strlen(s0));
588         if (space_split && **map == *CHR_SPACE)
589             break;
590         while (s1 < s0)
591         {
592             if (!isalnum (*s1) && *s1 != '-')
593                 dst[i++] = '\\';
594             dst_term[j++] = *s1;
595             dst[i++] = *s1++;
596         }
597     }
598     dst[i] = '\0';
599     dst_term[j] = '\0';
600     *src = s0;
601     return i;
602 }
603
604 /* term_101: handle term, where trunc=Process # */
605 static int term_101 (ZebraMaps zebra_maps, int reg_type,
606                      const char **src, char *dst, int space_split,
607                      char *dst_term)
608 {
609     const char *s0, *s1;
610     const char **map;
611     int i = 0;
612     int j = 0;
613
614     if (!term_pre (zebra_maps, reg_type, src, "#", "#"))
615         return 0;
616     s0 = *src;
617     while (*s0)
618     {
619         if (*s0 == '#')
620         {
621             dst[i++] = '.';
622             dst[i++] = '*';
623             dst_term[j++] = *s0++;
624         }
625         else
626         {
627             s1 = s0;
628             map = zebra_maps_input (zebra_maps, reg_type, &s0, strlen(s0));
629             if (space_split && **map == *CHR_SPACE)
630                 break;
631             while (s1 < s0)
632             {
633                 if (!isalnum (*s1))
634                     dst[i++] = '\\';
635                 dst_term[j++] = *s1;
636                 dst[i++] = *s1++;
637             }
638         }
639     }
640     dst[i] = '\0';
641     dst_term[j++] = '\0';
642     *src = s0;
643     return i;
644 }
645
646 /* term_103: handle term, where trunc=re-2 (regular expressions) */
647 static int term_103 (ZebraMaps zebra_maps, int reg_type, const char **src,
648                      char *dst, int *errors, int space_split,
649                      char *dst_term)
650 {
651     int i = 0;
652     int j = 0;
653     const char *s0, *s1;
654     const char **map;
655
656     if (!term_pre (zebra_maps, reg_type, src, "^\\()[].*+?|", "("))
657         return 0;
658     s0 = *src;
659     if (errors && *s0 == '+' && s0[1] && s0[2] == '+' && s0[3] &&
660         isdigit (s0[1]))
661     {
662         *errors = s0[1] - '0';
663         s0 += 3;
664         if (*errors > 3)
665             *errors = 3;
666     }
667     while (*s0)
668     {
669         if (strchr ("^\\()[].*+?|-", *s0))
670         {
671             dst_term[j++] = *s0;
672             dst[i++] = *s0++;
673         }
674         else
675         {
676             s1 = s0;
677             map = zebra_maps_input (zebra_maps, reg_type, &s0, strlen(s0));
678             if (**map == *CHR_SPACE)
679                 break;
680             while (s1 < s0)
681             {
682                 if (!isalnum (*s1))
683                     dst[i++] = '\\';
684                 dst_term[j++] = *s1;
685                 dst[i++] = *s1++;
686             }
687         }
688     }
689     dst[i] = '\0';
690     dst_term[j] = '\0';
691     *src = s0;
692     return i;
693 }
694
695 /* term_103: handle term, where trunc=re-1 (regular expressions) */
696 static int term_102 (ZebraMaps zebra_maps, int reg_type, const char **src,
697                      char *dst, int space_split, char *dst_term)
698 {
699     return term_103 (zebra_maps, reg_type, src, dst, NULL, space_split,
700                      dst_term);
701 }
702
703
704 /* term_104: handle term, where trunc=Process # and ! */
705 static int term_104 (ZebraMaps zebra_maps, int reg_type,
706                      const char **src, char *dst, int space_split,
707                      char *dst_term)
708 {
709     const char *s0, *s1;
710     const char **map;
711     int i = 0;
712     int j = 0;
713
714     if (!term_pre (zebra_maps, reg_type, src, "#!", "#!"))
715         return 0;
716     s0 = *src;
717     while (*s0)
718     {
719         if (*s0 == '#')
720         {
721             dst[i++] = '.';
722             dst[i++] = '*';
723             dst_term[j++] = *s0++;
724         }
725         else if (*s0 == '!')
726         {
727             dst[i++] = '.';
728             dst_term[j++] = *s0++;
729         }
730         {
731             s1 = s0;
732             map = zebra_maps_input (zebra_maps, reg_type, &s0, strlen(s0));
733             if (space_split && **map == *CHR_SPACE)
734                 break;
735             while (s1 < s0)
736             {
737                 if (!isalnum (*s1))
738                     dst[i++] = '\\';
739                 dst_term[j++] = *s1;
740                 dst[i++] = *s1++;
741             }
742         }
743     }
744     dst[i] = '\0';
745     dst_term[j++] = '\0';
746     *src = s0;
747     return i;
748 }
749
750 /* term_105/106: handle term, where trunc=Process * and ! and right trunc */
751 static int term_105 (ZebraMaps zebra_maps, int reg_type,
752                      const char **src, char *dst, int space_split,
753                      char *dst_term, int right_truncate)
754 {
755     const char *s0, *s1;
756     const char **map;
757     int i = 0;
758     int j = 0;
759
760     if (!term_pre (zebra_maps, reg_type, src, "*!", "*!"))
761         return 0;
762     s0 = *src;
763     while (*s0)
764     {
765         if (*s0 == '*')
766         {
767             dst[i++] = '.';
768             dst[i++] = '*';
769             dst_term[j++] = *s0++;
770         }
771         else if (*s0 == '!')
772         {
773             dst[i++] = '.';
774             dst_term[j++] = *s0++;
775         }
776         {
777             s1 = s0;
778             map = zebra_maps_input (zebra_maps, reg_type, &s0, strlen(s0));
779             if (space_split && **map == *CHR_SPACE)
780                 break;
781             while (s1 < s0)
782             {
783                 if (!isalnum (*s1))
784                     dst[i++] = '\\';
785                 dst_term[j++] = *s1;
786                 dst[i++] = *s1++;
787             }
788         }
789     }
790     if (right_truncate)
791     {
792         dst[i++] = '.';
793         dst[i++] = '*';
794     }
795     dst[i] = '\0';
796     
797     dst_term[j++] = '\0';
798     *src = s0;
799     return i;
800 }
801
802
803 /* gen_regular_rel - generate regular expression from relation
804  *  val:     border value (inclusive)
805  *  islt:    1 if <=; 0 if >=.
806  */
807 static void gen_regular_rel (char *dst, int val, int islt)
808 {
809     int dst_p;
810     int w, d, i;
811     int pos = 0;
812     char numstr[20];
813
814     logf (LOG_DEBUG, "gen_regular_rel. val=%d, islt=%d", val, islt);
815     if (val >= 0)
816     {
817         if (islt)
818             strcpy (dst, "(-[0-9]+|(");
819         else
820             strcpy (dst, "((");
821     } 
822     else
823     {
824         if (!islt)
825         {
826             strcpy (dst, "([0-9]+|-(");
827             dst_p = strlen (dst);
828             islt = 1;
829         }
830         else
831         {
832             strcpy (dst, "(-(");
833             islt = 0;
834         }
835         val = -val;
836     }
837     dst_p = strlen (dst);
838     sprintf (numstr, "%d", val);
839     for (w = strlen(numstr); --w >= 0; pos++)
840     {
841         d = numstr[w];
842         if (pos > 0)
843         {
844             if (islt)
845             {
846                 if (d == '0')
847                     continue;
848                 d--;
849             } 
850             else
851             {
852                 if (d == '9')
853                     continue;
854                 d++;
855             }
856         }
857         
858         strcpy (dst + dst_p, numstr);
859         dst_p = strlen(dst) - pos - 1;
860
861         if (islt)
862         {
863             if (d != '0')
864             {
865                 dst[dst_p++] = '[';
866                 dst[dst_p++] = '0';
867                 dst[dst_p++] = '-';
868                 dst[dst_p++] = d;
869                 dst[dst_p++] = ']';
870             }
871             else
872                 dst[dst_p++] = d;
873         }
874         else
875         {
876             if (d != '9')
877             { 
878                 dst[dst_p++] = '[';
879                 dst[dst_p++] = d;
880                 dst[dst_p++] = '-';
881                 dst[dst_p++] = '9';
882                 dst[dst_p++] = ']';
883             }
884             else
885                 dst[dst_p++] = d;
886         }
887         for (i = 0; i<pos; i++)
888         {
889             dst[dst_p++] = '[';
890             dst[dst_p++] = '0';
891             dst[dst_p++] = '-';
892             dst[dst_p++] = '9';
893             dst[dst_p++] = ']';
894         }
895         dst[dst_p++] = '|';
896     }
897     dst[dst_p] = '\0';
898     if (islt)
899     {
900         /* match everything less than 10^(pos-1) */
901         strcat (dst, "0*");
902         for (i=1; i<pos; i++)
903             strcat (dst, "[0-9]?");
904     }
905     else
906     {
907         /* match everything greater than 10^pos */
908         for (i = 0; i <= pos; i++)
909             strcat (dst, "[0-9]");
910         strcat (dst, "[0-9]*");
911     }
912     strcat (dst, "))");
913 }
914
915 void string_rel_add_char (char **term_p, const char *src, int *indx)
916 {
917     if (src[*indx] == '\\')
918         *(*term_p)++ = src[(*indx)++];
919     *(*term_p)++ = src[(*indx)++];
920 }
921
922 /*
923  *   >  abc     ([b-].*|a[c-].*|ab[d-].*|abc.+)
924  *              ([^-a].*|a[^-b].*ab[^-c].*|abc.+)
925  *   >= abc     ([b-].*|a[c-].*|ab[c-].*)
926  *              ([^-a].*|a[^-b].*|ab[c-].*)
927  *   <  abc     ([-0].*|a[-a].*|ab[-b].*)
928  *              ([^a-].*|a[^b-].*|ab[^c-].*)
929  *   <= abc     ([-0].*|a[-a].*|ab[-b].*|abc)
930  *              ([^a-].*|a[^b-].*|ab[^c-].*|abc)
931  */
932 static int string_relation (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
933                             const char **term_sub, char *term_dict,
934                             oid_value attributeSet,
935                             int reg_type, int space_split, char *term_dst)
936 {
937     AttrType relation;
938     int relation_value;
939     int i;
940     char *term_tmp = term_dict + strlen(term_dict);
941     char term_component[256];
942
943     attr_init (&relation, zapt, 2);
944     relation_value = attr_find (&relation, NULL);
945
946     logf (LOG_DEBUG, "string relation value=%d", relation_value);
947     switch (relation_value)
948     {
949     case 1:
950         if (!term_100 (zh->service->zebra_maps, reg_type,
951                        term_sub, term_component,
952                        space_split, term_dst))
953             return 0;
954         logf (LOG_DEBUG, "Relation <");
955         
956         *term_tmp++ = '(';
957         for (i = 0; term_component[i]; )
958         {
959             int j = 0;
960
961             if (i)
962                 *term_tmp++ = '|';
963             while (j < i)
964                 string_rel_add_char (&term_tmp, term_component, &j);
965
966             *term_tmp++ = '[';
967
968             *term_tmp++ = '^';
969             string_rel_add_char (&term_tmp, term_component, &i);
970             *term_tmp++ = '-';
971
972             *term_tmp++ = ']';
973             *term_tmp++ = '.';
974             *term_tmp++ = '*';
975         }
976         *term_tmp++ = ')';
977         *term_tmp = '\0';
978         break;
979     case 2:
980         if (!term_100 (zh->service->zebra_maps, reg_type,
981                        term_sub, term_component,
982                        space_split, term_dst))
983             return 0;
984         logf (LOG_DEBUG, "Relation <=");
985
986         *term_tmp++ = '(';
987         for (i = 0; term_component[i]; )
988         {
989             int j = 0;
990
991             while (j < i)
992                 string_rel_add_char (&term_tmp, term_component, &j);
993             *term_tmp++ = '[';
994
995             *term_tmp++ = '^';
996             string_rel_add_char (&term_tmp, term_component, &i);
997             *term_tmp++ = '-';
998
999             *term_tmp++ = ']';
1000             *term_tmp++ = '.';
1001             *term_tmp++ = '*';
1002
1003             *term_tmp++ = '|';
1004         }
1005         for (i = 0; term_component[i]; )
1006             string_rel_add_char (&term_tmp, term_component, &i);
1007         *term_tmp++ = ')';
1008         *term_tmp = '\0';
1009         break;
1010     case 5:
1011         if (!term_100 (zh->service->zebra_maps, reg_type,
1012                        term_sub, term_component, space_split, term_dst))
1013             return 0;
1014         logf (LOG_DEBUG, "Relation >");
1015
1016         *term_tmp++ = '(';
1017         for (i = 0; term_component[i];)
1018         {
1019             int j = 0;
1020
1021             while (j < i)
1022                 string_rel_add_char (&term_tmp, term_component, &j);
1023             *term_tmp++ = '[';
1024             
1025             *term_tmp++ = '^';
1026             *term_tmp++ = '-';
1027             string_rel_add_char (&term_tmp, term_component, &i);
1028
1029             *term_tmp++ = ']';
1030             *term_tmp++ = '.';
1031             *term_tmp++ = '*';
1032
1033             *term_tmp++ = '|';
1034         }
1035         for (i = 0; term_component[i];)
1036             string_rel_add_char (&term_tmp, term_component, &i);
1037         *term_tmp++ = '.';
1038         *term_tmp++ = '+';
1039         *term_tmp++ = ')';
1040         *term_tmp = '\0';
1041         break;
1042     case 4:
1043         if (!term_100 (zh->service->zebra_maps, reg_type, term_sub,
1044                        term_component, space_split, term_dst))
1045             return 0;
1046         logf (LOG_DEBUG, "Relation >=");
1047
1048         *term_tmp++ = '(';
1049         for (i = 0; term_component[i];)
1050         {
1051             int j = 0;
1052
1053             if (i)
1054                 *term_tmp++ = '|';
1055             while (j < i)
1056                 string_rel_add_char (&term_tmp, term_component, &j);
1057             *term_tmp++ = '[';
1058
1059             if (term_component[i+1])
1060             {
1061                 *term_tmp++ = '^';
1062                 *term_tmp++ = '-';
1063                 string_rel_add_char (&term_tmp, term_component, &i);
1064             }
1065             else
1066             {
1067                 string_rel_add_char (&term_tmp, term_component, &i);
1068                 *term_tmp++ = '-';
1069             }
1070             *term_tmp++ = ']';
1071             *term_tmp++ = '.';
1072             *term_tmp++ = '*';
1073         }
1074         *term_tmp++ = ')';
1075         *term_tmp = '\0';
1076         break;
1077     case 3:
1078     default:
1079         logf (LOG_DEBUG, "Relation =");
1080         if (!term_100 (zh->service->zebra_maps, reg_type, term_sub,
1081                        term_component, space_split, term_dst))
1082             return 0;
1083         strcat (term_tmp, "(");
1084         strcat (term_tmp, term_component);
1085         strcat (term_tmp, ")");
1086     }
1087     return 1;
1088 }
1089
1090 static int string_term (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
1091                         const char **term_sub, 
1092                         oid_value attributeSet, NMEM stream,
1093                         struct grep_info *grep_info,
1094                         int reg_type, int complete_flag,
1095                         int num_bases, char **basenames,
1096                         char *term_dst)
1097 {
1098     char term_dict[2*IT_MAX_WORD+4000];
1099     int j, r, base_no;
1100     AttrType truncation;
1101     int truncation_value;
1102     AttrType use;
1103     int use_value;
1104     oid_value curAttributeSet = attributeSet;
1105     const char *termp;
1106     struct rpn_char_map_info rcmi;
1107     int space_split = complete_flag ? 0 : 1;
1108
1109     rpn_char_map_prepare (zh, reg_type, &rcmi);
1110     attr_init (&use, zapt, 1);
1111     use_value = attr_find (&use, &curAttributeSet);
1112     logf (LOG_DEBUG, "string_term, use value %d", use_value);
1113     attr_init (&truncation, zapt, 5);
1114     truncation_value = attr_find (&truncation, NULL);
1115     logf (LOG_DEBUG, "truncation value %d", truncation_value);
1116
1117     if (use_value == -1)
1118         use_value = 1016;
1119
1120     for (base_no = 0; base_no < num_bases; base_no++)
1121     {
1122         attent attp;
1123         data1_local_attribute *local_attr;
1124         int max_pos, prefix_len = 0;
1125
1126         termp = *term_sub;
1127         if ((r=att_getentbyatt (zh, &attp, curAttributeSet, use_value)))
1128         {
1129             logf (LOG_DEBUG, "att_getentbyatt fail. set=%d use=%d r=%d",
1130                   curAttributeSet, use_value, r);
1131             if (r == -1)
1132             {
1133                 char val_str[32];
1134                 sprintf (val_str, "%d", use_value);
1135                 zh->errCode = 114;
1136                 zh->errString = nmem_strdup (stream, val_str);
1137             }
1138             else
1139             {
1140                 int oid[OID_SIZE];
1141                 struct oident oident;
1142
1143                 oident.proto = PROTO_Z3950;
1144                 oident.oclass = CLASS_ATTSET;
1145                 oident.value = curAttributeSet;
1146                 oid_ent_to_oid (&oident, oid);
1147
1148                 zh->errCode = 121;
1149                 zh->errString = nmem_strdup (stream, oident.desc);
1150             }
1151             return -1;
1152         }
1153         if (zebraExplain_curDatabase (zh->service->zei, basenames[base_no]))
1154         {
1155             zh->errCode = 109; /* Database unavailable */
1156             zh->errString = basenames[base_no];
1157             return -1;
1158         }
1159         for (local_attr = attp.local_attributes; local_attr;
1160              local_attr = local_attr->next)
1161         {
1162             int ord;
1163             char ord_buf[32];
1164             int i, ord_len;
1165
1166             ord = zebraExplain_lookupSU (zh->service->zei, attp.attset_ordinal,
1167                                           local_attr->local);
1168             if (ord < 0)
1169                 continue;
1170             if (prefix_len)
1171                 term_dict[prefix_len++] = '|';
1172             else
1173                 term_dict[prefix_len++] = '(';
1174
1175             ord_len = key_SU_code (ord, ord_buf);
1176             for (i = 0; i<ord_len; i++)
1177             {
1178                 term_dict[prefix_len++] = 1;
1179                 term_dict[prefix_len++] = ord_buf[i];
1180             }
1181         }
1182         if (!prefix_len)
1183         {
1184             char val_str[32];
1185             sprintf (val_str, "%d", use_value);
1186             zh->errCode = 114;
1187             zh->errString = nmem_strdup (stream, val_str);
1188             return -1;
1189         }
1190         term_dict[prefix_len++] = ')';        
1191         term_dict[prefix_len++] = 1;
1192         term_dict[prefix_len++] = reg_type;
1193         logf (LOG_DEBUG, "reg_type = %d", term_dict[prefix_len-1]);
1194         term_dict[prefix_len] = '\0';
1195         j = prefix_len;
1196         switch (truncation_value)
1197         {
1198         case -1:         /* not specified */
1199         case 100:        /* do not truncate */
1200             if (!string_relation (zh, zapt, &termp, term_dict,
1201                                   attributeSet,
1202                                   reg_type, space_split, term_dst))
1203                 return 0;
1204             logf (LOG_DEBUG, "dict_lookup_grep: %s", term_dict+prefix_len);
1205             r = dict_lookup_grep (zh->service->dict, term_dict, 0,
1206                                   grep_info, &max_pos, 0, grep_handle);
1207             if (r)
1208                 logf (LOG_WARN, "dict_lookup_grep fail, rel=gt: %d", r);
1209             break;
1210         case 1:          /* right truncation */
1211             term_dict[j++] = '(';
1212             if (!term_100 (zh->service->zebra_maps, reg_type,
1213                            &termp, term_dict + j, space_split, term_dst))
1214                 return 0;
1215             strcat (term_dict, ".*)");
1216             dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1217                               &max_pos, 0, grep_handle);
1218             break;
1219         case 2:          /* keft truncation */
1220             term_dict[j++] = '('; term_dict[j++] = '.'; term_dict[j++] = '*';
1221             if (!term_100 (zh->service->zebra_maps, reg_type,
1222                            &termp, term_dict + j, space_split, term_dst))
1223                 return 0;
1224             strcat (term_dict, ")");
1225             dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1226                               &max_pos, 0, grep_handle);
1227             break;
1228         case 3:          /* left&right truncation */
1229             term_dict[j++] = '('; term_dict[j++] = '.'; term_dict[j++] = '*';
1230             if (!term_100 (zh->service->zebra_maps, reg_type,
1231                            &termp, term_dict + j, space_split, term_dst))
1232                 return 0;
1233             strcat (term_dict, ".*)");
1234             dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1235                               &max_pos, 0, grep_handle);
1236             break;
1237             zh->errCode = 120;
1238             return -1;
1239         case 101:        /* process # in term */
1240             term_dict[j++] = '(';
1241             if (!term_101 (zh->service->zebra_maps, reg_type,
1242                            &termp, term_dict + j, space_split, term_dst))
1243                 return 0;
1244             strcat (term_dict, ")");
1245             r = dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1246                                   &max_pos, 0, grep_handle);
1247             if (r)
1248                 logf (LOG_WARN, "dict_lookup_grep err, trunc=#: %d", r);
1249             break;
1250         case 102:        /* Regexp-1 */
1251             term_dict[j++] = '(';
1252             if (!term_102 (zh->service->zebra_maps, reg_type,
1253                            &termp, term_dict + j, space_split, term_dst))
1254                 return 0;
1255             strcat (term_dict, ")");
1256             logf (LOG_DEBUG, "Regexp-1 tolerance=%d", r);
1257             r = dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1258                                   &max_pos, 0, grep_handle);
1259             if (r)
1260                 logf (LOG_WARN, "dict_lookup_grep err, trunc=regular: %d",
1261                       r);
1262             break;
1263         case 103:       /* Regexp-2 */
1264             r = 1;
1265             term_dict[j++] = '(';
1266             if (!term_103 (zh->service->zebra_maps, reg_type,
1267                            &termp, term_dict + j, &r, space_split, term_dst))
1268                 return 0;
1269             strcat (term_dict, ")");
1270             logf (LOG_DEBUG, "Regexp-2 tolerance=%d", r);
1271             r = dict_lookup_grep (zh->service->dict, term_dict, r, grep_info,
1272                                   &max_pos, 2, grep_handle);
1273             if (r)
1274                 logf (LOG_WARN, "dict_lookup_grep err, trunc=eregular: %d",
1275                       r);
1276             break;
1277         case 104:        /* process # and ! in term */
1278             term_dict[j++] = '(';
1279             if (!term_104 (zh->service->zebra_maps, reg_type,
1280                            &termp, term_dict + j, space_split, term_dst))
1281                 return 0;
1282             strcat (term_dict, ")");
1283             r = dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1284                                   &max_pos, 0, grep_handle);
1285             if (r)
1286                 logf (LOG_WARN, "dict_lookup_grep err, trunc=#/!: %d", r);
1287             break;
1288         case 105:        /* process * and ! in term */
1289             term_dict[j++] = '(';
1290             if (!term_105 (zh->service->zebra_maps, reg_type,
1291                            &termp, term_dict + j, space_split, term_dst, 1))
1292                 return 0;
1293             strcat (term_dict, ")");
1294             r = dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1295                                   &max_pos, 0, grep_handle);
1296             if (r)
1297                 logf (LOG_WARN, "dict_lookup_grep err, trunc=*/!: %d", r);
1298             break;
1299         case 106:        /* process * and ! in term */
1300             term_dict[j++] = '(';
1301             if (!term_105 (zh->service->zebra_maps, reg_type,
1302                            &termp, term_dict + j, space_split, term_dst, 0))
1303                 return 0;
1304             strcat (term_dict, ")");
1305             r = dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info,
1306                                   &max_pos, 0, grep_handle);
1307             if (r)
1308                 logf (LOG_WARN, "dict_lookup_grep err, trunc=*/!: %d", r);
1309             break;
1310         }
1311     }
1312     *term_sub = termp;
1313     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
1314     return 1;
1315 }
1316
1317 static void trans_term (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
1318                         char *termz)
1319 {
1320     size_t sizez;
1321     Z_Term *term = zapt->term;
1322
1323     sizez = term->u.general->len;
1324     if (sizez > IT_MAX_WORD-1)
1325         sizez = IT_MAX_WORD-1;
1326     memcpy (termz, term->u.general->buf, sizez);
1327     termz[sizez] = '\0';
1328 }
1329
1330 static void trans_scan_term (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
1331                              char *termz, int reg_type)
1332 {
1333     Z_Term *term = zapt->term;
1334     const char **map;
1335     const char *cp = (const char *) term->u.general->buf;
1336     const char *cp_end = cp + term->u.general->len;
1337     const char *src;
1338     int i = 0;
1339     const char *space_map = NULL;
1340     int len;
1341     
1342     while ((len = (cp_end - cp)) > 0)
1343     {
1344         map = zebra_maps_input (zh->service->zebra_maps, reg_type, &cp, len);
1345         if (**map == *CHR_SPACE)
1346             space_map = *map;
1347         else
1348         {
1349             if (i && space_map)
1350                 for (src = space_map; *src; src++)
1351                     termz[i++] = *src;
1352             space_map = NULL;
1353             for (src = *map; *src; src++)
1354                 termz[i++] = *src;
1355         }
1356     }
1357     termz[i] = '\0';
1358 }
1359
1360 static RSET rpn_prox (ZebraHandle zh, RSET *rset, int rset_no,
1361                       int ordered, int exclusion, int relation, int distance)
1362 {
1363     int i;
1364     RSFD *rsfd;
1365     int  *more;
1366     struct it_key **buf;
1367     RSET result;
1368     char prox_term[1024];
1369     int length_prox_term = 0;
1370     int min_nn = 10000000;
1371     int term_index;
1372     const char *flags = NULL;
1373     
1374     rsfd = (RSFD *) xmalloc (sizeof(*rsfd)*rset_no);
1375     more = (int *) xmalloc (sizeof(*more)*rset_no);
1376     buf = (struct it_key **) xmalloc (sizeof(*buf)*rset_no);
1377
1378     *prox_term = '\0';
1379     for (i = 0; i<rset_no; i++)
1380     {
1381         int j;
1382         for (j = 0; j<rset[i]->no_rset_terms; j++)
1383         {
1384             const char *nflags = rset[i]->rset_terms[j]->flags;
1385             char *term = rset[i]->rset_terms[j]->name;
1386             int lterm = strlen(term);
1387             if (lterm + length_prox_term < sizeof(prox_term)-1)
1388             {
1389                 if (length_prox_term)
1390                     prox_term[length_prox_term++] = ' ';
1391                 strcpy (prox_term + length_prox_term, term);
1392                 length_prox_term += lterm;
1393             }
1394             if (min_nn > rset[i]->rset_terms[j]->nn)
1395                 min_nn = rset[i]->rset_terms[j]->nn;
1396             flags = nflags;
1397         }
1398     }
1399     for (i = 0; i<rset_no; i++)
1400     {
1401         buf[i] = 0;
1402         rsfd[i] = 0;
1403     }
1404     for (i = 0; i<rset_no; i++)
1405     {
1406         buf[i] = (struct it_key *) xmalloc (sizeof(**buf));
1407         rsfd[i] = rset_open (rset[i], RSETF_READ);
1408         if (!(more[i] = rset_read (rset[i], rsfd[i], buf[i], &term_index)))
1409             break;
1410     }
1411     if (i != rset_no)
1412     {
1413         /* at least one is empty ... return null set */
1414         rset_null_parms parms;
1415         
1416         parms.rset_term = rset_term_create (prox_term, length_prox_term,
1417                                             flags);
1418         parms.rset_term->nn = 0;
1419         result = rset_create (rset_kind_null, &parms);
1420     }
1421     else if (ordered && relation == 3 && exclusion == 0 && distance == 1)
1422     {
1423         /* special proximity case = phrase search ... */
1424         rset_temp_parms parms;
1425         RSFD rsfd_result;
1426
1427         parms.rset_term = rset_term_create (prox_term, length_prox_term,
1428                                             flags);
1429         parms.rset_term->nn = min_nn;
1430         parms.key_size = sizeof (struct it_key);
1431         parms.temp_path = res_get (zh->service->res, "setTmpDir");
1432         result = rset_create (rset_kind_temp, &parms);
1433         rsfd_result = rset_open (result, RSETF_WRITE);
1434         
1435         while (*more)
1436         {
1437             for (i = 1; i<rset_no; i++)
1438             {
1439                 int cmp;
1440                 
1441                 if (!more[i])
1442                 {
1443                     *more = 0;
1444                     break;
1445                 }
1446                 cmp = key_compare_it (buf[i], buf[i-1]);
1447                 if (cmp > 1)
1448                 {
1449                     more[i-1] = rset_read (rset[i-1], rsfd[i-1],
1450                                            buf[i-1], &term_index);
1451                     break;
1452                 }
1453                 else if (cmp == 1)
1454                 {
1455                     if (buf[i-1]->seqno+1 != buf[i]->seqno)
1456                     {
1457                         more[i-1] = rset_read (rset[i-1], rsfd[i-1],
1458                                                buf[i-1], &term_index);
1459                         break;
1460                     }
1461                 }
1462                 else
1463                 {
1464                     more[i] = rset_read (rset[i], rsfd[i], buf[i],
1465                                          &term_index);
1466                     break;
1467                 }
1468             }
1469             if (i == rset_no)
1470             {
1471                 rset_write (result, rsfd_result, buf[0]);
1472                 more[0] = rset_read (*rset, *rsfd, *buf, &term_index);
1473             }
1474         }
1475         rset_close (result, rsfd_result);
1476     }
1477     else if (rset_no == 2)
1478     {
1479         /* generic proximity case (two input sets only) ... */
1480         rset_temp_parms parms;
1481         RSFD rsfd_result;
1482
1483         logf (LOG_LOG, "generic prox, dist = %d, relation = %d, ordered =%d, exclusion=%d",
1484               distance, relation, ordered, exclusion);
1485         parms.rset_term = rset_term_create (prox_term, length_prox_term,
1486                                             flags);
1487         parms.rset_term->nn = min_nn;
1488         parms.key_size = sizeof (struct it_key);
1489         parms.temp_path = res_get (zh->service->res, "setTmpDir");
1490         result = rset_create (rset_kind_temp, &parms);
1491         rsfd_result = rset_open (result, RSETF_WRITE);
1492
1493         while (more[0] && more[1]) 
1494         {
1495             int cmp = key_compare_it (buf[0], buf[1]);
1496             if (cmp < -1)
1497                 more[0] = rset_read (rset[0], rsfd[0], buf[0], &term_index);
1498             else if (cmp > 1)
1499                 more[1] = rset_read (rset[1], rsfd[1], buf[1], &term_index);
1500             else
1501             {
1502                 int sysno = buf[0]->sysno;
1503                 int seqno[500];
1504                 int n = 0;
1505                 
1506                 seqno[n++] = buf[0]->seqno;
1507                 while ((more[0] = rset_read (rset[0], rsfd[0], buf[0],
1508                                              &term_index)) &&
1509                        sysno == buf[0]->sysno)
1510                     if (n < 500)
1511                         seqno[n++] = buf[0]->seqno;
1512                 do
1513                 {
1514                     for (i = 0; i<n; i++)
1515                     {
1516                         int diff = buf[1]->seqno - seqno[i];
1517                         int excl = exclusion;
1518                         if (!ordered && diff < 0)
1519                             diff = -diff;
1520                         switch (relation)
1521                         {
1522                         case 1:      /* < */
1523                             if (diff < distance && diff >= 0)
1524                                 excl = !excl;
1525                             break;
1526                         case 2:      /* <= */
1527                             if (diff <= distance && diff >= 0)
1528                                 excl = !excl;
1529                             break;
1530                         case 3:      /* == */
1531                             if (diff == distance && diff >= 0)
1532                                 excl = !excl;
1533                             break;
1534                         case 4:      /* >= */
1535                             if (diff >= distance && diff >= 0)
1536                                 excl = !excl;
1537                             break;
1538                         case 5:      /* > */
1539                             if (diff > distance && diff >= 0)
1540                                 excl = !excl;
1541                             break;
1542                         case 6:      /* != */
1543                             if (diff != distance && diff >= 0)
1544                                 excl = !excl;
1545                             break;
1546                         }
1547                         if (excl)
1548                         {
1549                             rset_write (result, rsfd_result, buf[1]);
1550                             break;
1551                         }
1552                     }
1553                 } while ((more[1] = rset_read (rset[1], rsfd[1], buf[1],
1554                                                &term_index)) &&
1555                          sysno == buf[1]->sysno);
1556             }
1557         }
1558         rset_close (result, rsfd_result);
1559     }
1560     else
1561     {
1562         rset_null_parms parms;
1563         
1564         parms.rset_term = rset_term_create (prox_term, length_prox_term,
1565                                             flags);
1566         parms.rset_term->nn = 0;
1567         result = rset_create (rset_kind_null, &parms);
1568     }
1569     for (i = 0; i<rset_no; i++)
1570     {
1571         if (rsfd[i])
1572             rset_close (rset[i], rsfd[i]);
1573         xfree (buf[i]);
1574     }
1575     xfree (buf);
1576     xfree (more);
1577     xfree (rsfd);
1578     return result;
1579 }
1580
1581
1582 char *normalize_term(ZebraHandle zh, Z_AttributesPlusTerm *zapt,
1583                      const char *termz, NMEM stream, unsigned reg_id)
1584 {
1585     WRBUF wrbuf = 0;
1586     AttrType truncation;
1587     int truncation_value;
1588     char *ex_list = 0;
1589
1590     attr_init (&truncation, zapt, 5);
1591     truncation_value = attr_find (&truncation, NULL);
1592
1593     switch (truncation_value)
1594     {
1595     default:
1596         ex_list = "";
1597         break;
1598     case 101:
1599         ex_list = "#";
1600         break;
1601     case 102:
1602     case 103:
1603         ex_list = 0;
1604         break;
1605     case 104:
1606         ex_list = "!#";
1607         break;
1608     case 105:
1609         ex_list = "!*";
1610         break;
1611     }
1612     if (ex_list)
1613         wrbuf = zebra_replace(zh->service->zebra_maps, reg_id, ex_list,
1614                               termz, strlen(termz));
1615     if (!wrbuf)
1616         return nmem_strdup(stream, termz);
1617     else
1618     {
1619         char *buf = (char*) nmem_malloc (stream, wrbuf_len(wrbuf)+1);
1620         memcpy (buf, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
1621         buf[wrbuf_len(wrbuf)] = '\0';
1622         return buf;
1623     }
1624 }
1625
1626 static RSET rpn_search_APT_phrase (ZebraHandle zh,
1627                                    Z_AttributesPlusTerm *zapt,
1628                                    const char *termz_org,
1629                                    oid_value attributeSet,
1630                                    NMEM stream,
1631                                    int reg_type, int complete_flag,
1632                                    const char *rank_type,
1633                                    int num_bases, char **basenames)
1634 {
1635     char term_dst[IT_MAX_WORD+1];
1636     RSET rset[60], result;
1637     int i, r, rset_no = 0;
1638     struct grep_info grep_info;
1639     char *termz = normalize_term(zh, zapt, termz_org, stream, reg_type);
1640     const char *termp = termz;
1641
1642 #ifdef TERM_COUNT
1643     grep_info.term_no = 0;
1644 #endif
1645     grep_info.isam_p_size = 0;
1646     grep_info.isam_p_buf = NULL;
1647     grep_info.zh = zh;
1648     grep_info.reg_type = reg_type;
1649
1650     while (1)
1651     { 
1652         logf (LOG_DEBUG, "APT_phrase termp=%s", termp);
1653         grep_info.isam_p_indx = 0;
1654         r = string_term (zh, zapt, &termp, attributeSet, stream, &grep_info,
1655                         reg_type, complete_flag, num_bases, basenames,
1656                         term_dst);
1657         if (r < 1)
1658             break;
1659         logf (LOG_DEBUG, "term: %s", term_dst);
1660         rset[rset_no] = rset_trunc (zh, grep_info.isam_p_buf,
1661                                     grep_info.isam_p_indx, term_dst,
1662                                     strlen(term_dst), rank_type);
1663         assert (rset[rset_no]);
1664         if (++rset_no >= (int) (sizeof(rset)/sizeof(*rset)))
1665             break;
1666     }
1667 #ifdef TERM_COUNT
1668     xfree(grep_info.term_no);
1669 #endif
1670     xfree (grep_info.isam_p_buf);
1671     if (rset_no == 0)
1672     {
1673         rset_null_parms parms;
1674         
1675         parms.rset_term = rset_term_create (term_dst, -1, rank_type);
1676         return rset_create (rset_kind_null, &parms);
1677     }
1678     else if (rset_no == 1)
1679         return (rset[0]);
1680     result = rpn_prox (zh, rset, rset_no, 1, 0, 3, 1);
1681     for (i = 0; i<rset_no; i++)
1682         rset_delete (rset[i]);
1683     return result;
1684 }
1685
1686 static RSET rpn_search_APT_or_list (ZebraHandle zh,
1687                                     Z_AttributesPlusTerm *zapt,
1688                                     const char *termz_org,
1689                                     oid_value attributeSet,
1690                                     NMEM stream,
1691                                     int reg_type, int complete_flag,
1692                                     const char *rank_type,
1693                                     int num_bases, char **basenames)
1694 {
1695     char term_dst[IT_MAX_WORD+1];
1696     RSET rset[60], result;
1697     int i, r, rset_no = 0;
1698     struct grep_info grep_info;
1699     char *termz = normalize_term(zh, zapt, termz_org, stream, reg_type);
1700     const char *termp = termz;
1701 #ifdef TERM_COUNT
1702     grep_info.term_no = 0;
1703 #endif
1704     grep_info.isam_p_size = 0;
1705     grep_info.isam_p_buf = NULL;
1706     grep_info.zh = zh;
1707     grep_info.reg_type = reg_type;
1708
1709     while (1)
1710     { 
1711         logf (LOG_DEBUG, "APT_or_list termp=%s", termp);
1712         grep_info.isam_p_indx = 0;
1713         r = string_term (zh, zapt, &termp, attributeSet, stream, &grep_info,
1714                         reg_type, complete_flag, num_bases, basenames,
1715                         term_dst);
1716         if (r < 1)
1717             break;
1718         logf (LOG_DEBUG, "term: %s", term_dst);
1719         rset[rset_no] = rset_trunc (zh, grep_info.isam_p_buf,
1720                                     grep_info.isam_p_indx, term_dst,
1721                                     strlen(term_dst), rank_type);
1722         assert (rset[rset_no]);
1723         if (++rset_no >= (int) (sizeof(rset)/sizeof(*rset)))
1724             break;
1725     }
1726 #ifdef TERM_COUNT
1727     xfree(grep_info.term_no);
1728 #endif
1729     xfree (grep_info.isam_p_buf);
1730     if (rset_no == 0)
1731     {
1732         rset_null_parms parms;
1733         
1734         parms.rset_term = rset_term_create (term_dst, -1, rank_type);
1735         return rset_create (rset_kind_null, &parms);
1736     }
1737     result = rset[0];
1738     for (i = 1; i<rset_no; i++)
1739     {
1740         rset_bool_parms bool_parms;
1741
1742         bool_parms.rset_l = result;
1743         bool_parms.rset_r = rset[i];
1744         bool_parms.key_size = sizeof(struct it_key);
1745         bool_parms.cmp = key_compare_it;
1746         result = rset_create (rset_kind_or, &bool_parms);
1747     }
1748     return result;
1749 }
1750
1751 static RSET rpn_search_APT_and_list (ZebraHandle zh,
1752                                      Z_AttributesPlusTerm *zapt,
1753                                      const char *termz_org,
1754                                      oid_value attributeSet,
1755                                      NMEM stream,
1756                                      int reg_type, int complete_flag,
1757                                      const char *rank_type,
1758                                      int num_bases, char **basenames)
1759 {
1760     char term_dst[IT_MAX_WORD+1];
1761     RSET rset[60], result;
1762     int i, r, rset_no = 0;
1763     struct grep_info grep_info;
1764     char *termz = normalize_term(zh, zapt, termz_org, stream, reg_type);
1765     const char *termp = termz;
1766
1767 #ifdef TERM_COUNT
1768     grep_info.term_no = 0;
1769 #endif
1770     grep_info.isam_p_size = 0;
1771     grep_info.isam_p_buf = NULL;
1772     grep_info.zh = zh;
1773     grep_info.reg_type = reg_type;
1774
1775     while (1)
1776     { 
1777         logf (LOG_DEBUG, "APT_and_list termp=%s", termp);
1778         grep_info.isam_p_indx = 0;
1779         r = string_term (zh, zapt, &termp, attributeSet, stream, &grep_info,
1780                         reg_type, complete_flag, num_bases, basenames,
1781                         term_dst);
1782         if (r < 1)
1783             break;
1784         logf (LOG_DEBUG, "term: %s", term_dst);
1785         rset[rset_no] = rset_trunc (zh, grep_info.isam_p_buf,
1786                                     grep_info.isam_p_indx, term_dst,
1787                                     strlen(term_dst), rank_type);
1788         assert (rset[rset_no]);
1789         if (++rset_no >= (int) (sizeof(rset)/sizeof(*rset)))
1790             break;
1791     }
1792 #ifdef TERM_COUNT
1793     xfree(grep_info.term_no);
1794 #endif
1795     xfree (grep_info.isam_p_buf);
1796     if (rset_no == 0)
1797     {
1798         rset_null_parms parms;
1799         
1800         parms.rset_term = rset_term_create (term_dst, -1, rank_type);
1801         return rset_create (rset_kind_null, &parms);
1802     }
1803     result = rset[0];
1804     for (i = 1; i<rset_no; i++)
1805     {
1806         rset_bool_parms bool_parms;
1807
1808         bool_parms.rset_l = result;
1809         bool_parms.rset_r = rset[i];
1810         bool_parms.key_size = sizeof(struct it_key);
1811         bool_parms.cmp = key_compare_it;
1812         result = rset_create (rset_kind_and, &bool_parms);
1813     }
1814     return result;
1815 }
1816
1817 static int numeric_relation (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
1818                              const char **term_sub,
1819                              char *term_dict,
1820                              oid_value attributeSet,
1821                              struct grep_info *grep_info,
1822                              int *max_pos,
1823                              int reg_type,
1824                              char *term_dst)
1825 {
1826     AttrType relation;
1827     int relation_value;
1828     int term_value;
1829     int r;
1830     char *term_tmp = term_dict + strlen(term_dict);
1831
1832     attr_init (&relation, zapt, 2);
1833     relation_value = attr_find (&relation, NULL);
1834
1835     logf (LOG_DEBUG, "numeric relation value=%d", relation_value);
1836
1837     if (!term_100 (zh->service->zebra_maps, reg_type, term_sub, term_tmp, 1,
1838                    term_dst))
1839         return 0;
1840     term_value = atoi (term_tmp);
1841     switch (relation_value)
1842     {
1843     case 1:
1844         logf (LOG_DEBUG, "Relation <");
1845         gen_regular_rel (term_tmp, term_value-1, 1);
1846         break;
1847     case 2:
1848         logf (LOG_DEBUG, "Relation <=");
1849         gen_regular_rel (term_tmp, term_value, 1);
1850         break;
1851     case 4:
1852         logf (LOG_DEBUG, "Relation >=");
1853         gen_regular_rel (term_tmp, term_value, 0);
1854         break;
1855     case 5:
1856         logf (LOG_DEBUG, "Relation >");
1857         gen_regular_rel (term_tmp, term_value+1, 0);
1858         break;
1859     case 3:
1860     default:
1861         logf (LOG_DEBUG, "Relation =");
1862         sprintf (term_tmp, "(0*%d)", term_value);
1863     }
1864     logf (LOG_DEBUG, "dict_lookup_grep: %s", term_tmp);
1865     r = dict_lookup_grep (zh->service->dict, term_dict, 0, grep_info, max_pos,
1866                           0, grep_handle);
1867     if (r)
1868         logf (LOG_WARN, "dict_lookup_grep fail, rel=gt: %d", r);
1869     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
1870     return 1;
1871 }
1872
1873 static int numeric_term (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
1874                          const char **term_sub, 
1875                          oid_value attributeSet, struct grep_info *grep_info,
1876                          int reg_type, int complete_flag,
1877                          int num_bases, char **basenames,
1878                          char *term_dst)
1879 {
1880     char term_dict[2*IT_MAX_WORD+2];
1881     int r, base_no;
1882     AttrType use;
1883     int use_value;
1884     oid_value curAttributeSet = attributeSet;
1885     const char *termp;
1886     struct rpn_char_map_info rcmi;
1887
1888     rpn_char_map_prepare (zh, reg_type, &rcmi);
1889     attr_init (&use, zapt, 1);
1890     use_value = attr_find (&use, &curAttributeSet);
1891     logf (LOG_DEBUG, "numeric_term, use value %d", use_value);
1892
1893     if (use_value == -1)
1894         use_value = 1016;
1895
1896     for (base_no = 0; base_no < num_bases; base_no++)
1897     {
1898         attent attp;
1899         data1_local_attribute *local_attr;
1900         int max_pos, prefix_len = 0;
1901
1902         termp = *term_sub;
1903         if ((r=att_getentbyatt (zh, &attp, curAttributeSet, use_value)))
1904         {
1905             logf (LOG_DEBUG, "att_getentbyatt fail. set=%d use=%d r=%d",
1906                   curAttributeSet, use_value, r);
1907             if (r == -1)
1908                 zh->errCode = 114;
1909             else
1910                 zh->errCode = 121;
1911             return -1;
1912         }
1913         if (zebraExplain_curDatabase (zh->service->zei, basenames[base_no]))
1914         {
1915             zh->errCode = 109; /* Database unavailable */
1916             zh->errString = basenames[base_no];
1917             return -1;
1918         }
1919         for (local_attr = attp.local_attributes; local_attr;
1920              local_attr = local_attr->next)
1921         {
1922             int ord;
1923             char ord_buf[32];
1924             int i, ord_len;
1925
1926             ord = zebraExplain_lookupSU (zh->service->zei, attp.attset_ordinal,
1927                                           local_attr->local);
1928             if (ord < 0)
1929                 continue;
1930             if (prefix_len)
1931                 term_dict[prefix_len++] = '|';
1932             else
1933                 term_dict[prefix_len++] = '(';
1934
1935             ord_len = key_SU_code (ord, ord_buf);
1936             for (i = 0; i<ord_len; i++)
1937             {
1938                 term_dict[prefix_len++] = 1;
1939                 term_dict[prefix_len++] = ord_buf[i];
1940             }
1941         }
1942         if (!prefix_len)
1943         {
1944             zh->errCode = 114;
1945             return -1;
1946         }
1947         term_dict[prefix_len++] = ')';        
1948         term_dict[prefix_len++] = 1;
1949         term_dict[prefix_len++] = reg_type;
1950         logf (LOG_DEBUG, "reg_type = %d", term_dict[prefix_len-1]);
1951         term_dict[prefix_len] = '\0';
1952         if (!numeric_relation (zh, zapt, &termp, term_dict,
1953                                attributeSet, grep_info, &max_pos, reg_type,
1954                                term_dst))
1955             return 0;
1956     }
1957     *term_sub = termp;
1958     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
1959     return 1;
1960 }
1961
1962 static RSET rpn_search_APT_numeric (ZebraHandle zh,
1963                                     Z_AttributesPlusTerm *zapt,
1964                                     const char *termz,
1965                                     oid_value attributeSet,
1966                                     NMEM stream,
1967                                     int reg_type, int complete_flag,
1968                                     const char *rank_type,
1969                                     int num_bases, char **basenames)
1970 {
1971     char term_dst[IT_MAX_WORD+1];
1972     const char *termp = termz;
1973     RSET rset[60], result;
1974     int i, r, rset_no = 0;
1975     struct grep_info grep_info;
1976
1977 #ifdef TERM_COUNT
1978     grep_info.term_no = 0;
1979 #endif
1980     grep_info.isam_p_size = 0;
1981     grep_info.isam_p_buf = NULL;
1982     grep_info.zh = zh;
1983     grep_info.reg_type = reg_type;
1984
1985     while (1)
1986     { 
1987         logf (LOG_DEBUG, "APT_numeric termp=%s", termp);
1988         grep_info.isam_p_indx = 0;
1989         r = numeric_term (zh, zapt, &termp, attributeSet, &grep_info,
1990                           reg_type, complete_flag, num_bases, basenames,
1991                           term_dst);
1992         if (r < 1)
1993             break;
1994         logf (LOG_DEBUG, "term: %s", term_dst);
1995         rset[rset_no] = rset_trunc (zh, grep_info.isam_p_buf,
1996                                     grep_info.isam_p_indx, term_dst,
1997                                     strlen(term_dst), rank_type);
1998         assert (rset[rset_no]);
1999         if (++rset_no >= (int) (sizeof(rset)/sizeof(*rset)))
2000             break;
2001     }
2002 #ifdef TERM_COUNT
2003     xfree(grep_info.term_no);
2004 #endif
2005     xfree (grep_info.isam_p_buf);
2006     if (rset_no == 0)
2007     {
2008         rset_null_parms parms;
2009         
2010         parms.rset_term = rset_term_create (term_dst, -1, rank_type);
2011         return rset_create (rset_kind_null, &parms);
2012     }
2013     result = rset[0];
2014     for (i = 1; i<rset_no; i++)
2015     {
2016         rset_bool_parms bool_parms;
2017
2018         bool_parms.rset_l = result;
2019         bool_parms.rset_r = rset[i];
2020         bool_parms.key_size = sizeof(struct it_key);
2021         bool_parms.cmp = key_compare_it;
2022         result = rset_create (rset_kind_and, &bool_parms);
2023     }
2024     return result;
2025 }
2026
2027 static RSET rpn_search_APT_local (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
2028                                   const char *termz,
2029                                   oid_value attributeSet,
2030                                   NMEM stream,
2031                                   const char *rank_type)
2032 {
2033     RSET result;
2034     RSFD rsfd;
2035     struct it_key key;
2036     rset_temp_parms parms;
2037
2038     parms.rset_term = rset_term_create (termz, -1, rank_type);
2039     parms.key_size = sizeof (struct it_key);
2040     parms.temp_path = res_get (zh->service->res, "setTmpDir");
2041     result = rset_create (rset_kind_temp, &parms);
2042     rsfd = rset_open (result, RSETF_WRITE);
2043
2044     key.sysno = atoi (termz);
2045     key.seqno = 1;
2046     if (key.sysno <= 0)
2047         key.sysno = 1;
2048     rset_write (result, rsfd, &key);
2049     rset_close (result, rsfd);
2050     return result;
2051 }
2052
2053 static RSET rpn_sort_spec (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
2054                            oid_value attributeSet, NMEM stream,
2055                            Z_SortKeySpecList *sort_sequence,
2056                            const char *rank_type)
2057 {
2058     rset_null_parms parms;    
2059     int i;
2060     int sort_relation_value;
2061     AttrType sort_relation_type;
2062     int use_value;
2063     AttrType use_type;
2064     Z_SortKeySpec *sks;
2065     Z_SortKey *sk;
2066     Z_AttributeElement *ae;
2067     int oid[OID_SIZE];
2068     oident oe;
2069     
2070     attr_init (&sort_relation_type, zapt, 7);
2071     sort_relation_value = attr_find (&sort_relation_type, &attributeSet);
2072
2073     attr_init (&use_type, zapt, 1);
2074     use_value = attr_find (&use_type, &attributeSet);
2075
2076     if (!sort_sequence->specs)
2077     {
2078         sort_sequence->num_specs = 10;
2079         sort_sequence->specs = (Z_SortKeySpec **)
2080             nmem_malloc (stream, sort_sequence->num_specs *
2081                          sizeof(*sort_sequence->specs));
2082         for (i = 0; i<sort_sequence->num_specs; i++)
2083             sort_sequence->specs[i] = 0;
2084     }
2085     if (zapt->term->which != Z_Term_general)
2086         i = 0;
2087     else
2088         i = atoi_n ((char *) zapt->term->u.general->buf,
2089                     zapt->term->u.general->len);
2090     if (i >= sort_sequence->num_specs)
2091         i = 0;
2092
2093     oe.proto = PROTO_Z3950;
2094     oe.oclass = CLASS_ATTSET;
2095     oe.value = attributeSet;
2096     if (!oid_ent_to_oid (&oe, oid))
2097         return 0;
2098
2099     sks = (Z_SortKeySpec *) nmem_malloc (stream, sizeof(*sks));
2100     sks->sortElement = (Z_SortElement *)
2101         nmem_malloc (stream, sizeof(*sks->sortElement));
2102     sks->sortElement->which = Z_SortElement_generic;
2103     sk = sks->sortElement->u.generic = (Z_SortKey *)
2104         nmem_malloc (stream, sizeof(*sk));
2105     sk->which = Z_SortKey_sortAttributes;
2106     sk->u.sortAttributes = (Z_SortAttributes *)
2107         nmem_malloc (stream, sizeof(*sk->u.sortAttributes));
2108
2109     sk->u.sortAttributes->id = oid;
2110     sk->u.sortAttributes->list = (Z_AttributeList *)
2111         nmem_malloc (stream, sizeof(*sk->u.sortAttributes->list));
2112     sk->u.sortAttributes->list->num_attributes = 1;
2113     sk->u.sortAttributes->list->attributes = (Z_AttributeElement **)
2114         nmem_malloc (stream, sizeof(*sk->u.sortAttributes->list->attributes));
2115     ae = *sk->u.sortAttributes->list->attributes = (Z_AttributeElement *)
2116         nmem_malloc (stream, sizeof(**sk->u.sortAttributes->list->attributes));
2117     ae->attributeSet = 0;
2118     ae->attributeType = (int *)
2119         nmem_malloc (stream, sizeof(*ae->attributeType));
2120     *ae->attributeType = 1;
2121     ae->which = Z_AttributeValue_numeric;
2122     ae->value.numeric = (int *)
2123         nmem_malloc (stream, sizeof(*ae->value.numeric));
2124     *ae->value.numeric = use_value;
2125
2126     sks->sortRelation = (int *)
2127         nmem_malloc (stream, sizeof(*sks->sortRelation));
2128     if (sort_relation_value == 1)
2129         *sks->sortRelation = Z_SortRelation_ascending;
2130     else if (sort_relation_value == 2)
2131         *sks->sortRelation = Z_SortRelation_descending;
2132     else 
2133         *sks->sortRelation = Z_SortRelation_ascending;
2134
2135     sks->caseSensitivity = (int *)
2136         nmem_malloc (stream, sizeof(*sks->caseSensitivity));
2137     *sks->caseSensitivity = 0;
2138
2139 #ifdef ASN_COMPILED
2140     sks->which = Z_SortKeySpec_null;
2141     sks->u.null = odr_nullval ();
2142 #else
2143     sks->missingValueAction = 0;
2144 #endif
2145
2146     sort_sequence->specs[i] = sks;
2147
2148     parms.rset_term = rset_term_create ("", -1, rank_type);
2149     return rset_create (rset_kind_null, &parms);
2150 }
2151
2152
2153 static RSET rpn_search_APT (ZebraHandle zh, Z_AttributesPlusTerm *zapt,
2154                             oid_value attributeSet, NMEM stream,
2155                             Z_SortKeySpecList *sort_sequence,
2156                             int num_bases, char **basenames)
2157 {
2158     unsigned reg_id;
2159     char *search_type = NULL;
2160     char *rank_type = NULL;
2161     int complete_flag;
2162     int sort_flag;
2163     char termz[IT_MAX_WORD+1];
2164
2165     zebra_maps_attr (zh->service->zebra_maps, zapt, &reg_id, &search_type,
2166                      &rank_type, &complete_flag, &sort_flag);
2167     
2168     logf (LOG_DEBUG, "reg_id=%c", reg_id);
2169     logf (LOG_DEBUG, "complete_flag=%d", complete_flag);
2170     logf (LOG_DEBUG, "search_type=%s", search_type);
2171     logf (LOG_DEBUG, "rank_type=%s", rank_type);
2172
2173     if (zapt->term->which != Z_Term_general)
2174     {
2175         zh->errCode = 124;
2176         return NULL;
2177     }
2178     trans_term (zh, zapt, termz);
2179
2180     if (sort_flag)
2181         return rpn_sort_spec (zh, zapt, attributeSet, stream, sort_sequence,
2182                               rank_type);
2183
2184     if (!strcmp (search_type, "phrase"))
2185     {
2186         return rpn_search_APT_phrase (zh, zapt, termz, attributeSet, stream,
2187                                       reg_id, complete_flag, rank_type,
2188                                       num_bases, basenames);
2189     }
2190     else if (!strcmp (search_type, "and-list"))
2191     {
2192         return rpn_search_APT_and_list (zh, zapt, termz, attributeSet, stream,
2193                                         reg_id, complete_flag, rank_type,
2194                                         num_bases, basenames);
2195     }
2196     else if (!strcmp (search_type, "or-list"))
2197     {
2198         return rpn_search_APT_or_list (zh, zapt, termz, attributeSet, stream,
2199                                        reg_id, complete_flag, rank_type,
2200                                        num_bases, basenames);
2201     }
2202     else if (!strcmp (search_type, "local"))
2203     {
2204         return rpn_search_APT_local (zh, zapt, termz, attributeSet, stream,
2205                                      rank_type);
2206     }
2207     else if (!strcmp (search_type, "numeric"))
2208     {
2209         return rpn_search_APT_numeric (zh, zapt, termz, attributeSet, stream,
2210                                        reg_id, complete_flag, rank_type,
2211                                        num_bases, basenames);
2212     }
2213     zh->errCode = 118;
2214     return NULL;
2215 }
2216
2217 static RSET rpn_search_structure (ZebraHandle zh, Z_RPNStructure *zs,
2218                                   oid_value attributeSet, NMEM stream,
2219                                   Z_SortKeySpecList *sort_sequence,
2220                                   int num_bases, char **basenames)
2221 {
2222     RSET r = NULL;
2223     if (zs->which == Z_RPNStructure_complex)
2224     {
2225         Z_Operator *zop = zs->u.complex->roperator;
2226         rset_bool_parms bool_parms;
2227
2228         bool_parms.rset_l = rpn_search_structure (zh, zs->u.complex->s1,
2229                                                   attributeSet, stream,
2230                                                   sort_sequence,
2231                                                   num_bases, basenames);
2232         if (bool_parms.rset_l == NULL)
2233             return NULL;
2234         bool_parms.rset_r = rpn_search_structure (zh, zs->u.complex->s2,
2235                                                   attributeSet, stream,
2236                                                   sort_sequence,
2237                                                   num_bases, basenames);
2238         if (bool_parms.rset_r == NULL)
2239         {
2240             rset_delete (bool_parms.rset_l);
2241             return NULL;
2242         }
2243         bool_parms.key_size = sizeof(struct it_key);
2244         bool_parms.cmp = key_compare_it;
2245
2246         switch (zop->which)
2247         {
2248         case Z_Operator_and:
2249             r = rset_create (rset_kind_and, &bool_parms);
2250             break;
2251         case Z_Operator_or:
2252             r = rset_create (rset_kind_or, &bool_parms);
2253             break;
2254         case Z_Operator_and_not:
2255             r = rset_create (rset_kind_not, &bool_parms);
2256             break;
2257         case Z_Operator_prox:
2258 #ifdef ASN_COMPILED
2259             if (zop->u.prox->which != Z_ProximityOperator_known)
2260             {
2261                 zh->errCode = 132;
2262                 return NULL;
2263             }
2264 #else
2265             if (zop->u.prox->which != Z_ProxCode_known)
2266             {
2267                 zh->errCode = 132;
2268                 return NULL;
2269             }
2270 #endif
2271
2272 #ifdef ASN_COMPILED
2273             if (*zop->u.prox->u.known != Z_ProxUnit_word)
2274             {
2275                 char *val = (char *) nmem_malloc (stream, 16);
2276                 zh->errCode = 132;
2277                 zh->errString = val;
2278                 sprintf (val, "%d", *zop->u.prox->u.known);
2279                 return NULL;
2280             }
2281 #else
2282             if (*zop->u.prox->proximityUnitCode != Z_ProxUnit_word)
2283             {
2284                 char *val = (char *) nmem_malloc (stream, 16);
2285                 zh->errCode = 132;
2286                 zh->errString = val;
2287                 sprintf (val, "%d", *zop->u.prox->proximityUnitCode);
2288                 return NULL;
2289             }
2290 #endif
2291             else
2292             {
2293                 RSET rsets[2];
2294
2295                 rsets[0] = bool_parms.rset_l;
2296                 rsets[1] = bool_parms.rset_r;
2297                 
2298                 r = rpn_prox (zh, rsets, 2, 
2299                               *zop->u.prox->ordered,
2300                               (!zop->u.prox->exclusion ? 0 :
2301                                *zop->u.prox->exclusion),
2302                               *zop->u.prox->relationType,
2303                               *zop->u.prox->distance);
2304                 rset_delete (rsets[0]);
2305                 rset_delete (rsets[1]);
2306             }
2307             break;
2308         default:
2309             zh->errCode = 110;
2310             return NULL;
2311         }
2312     }
2313     else if (zs->which == Z_RPNStructure_simple)
2314     {
2315         if (zs->u.simple->which == Z_Operand_APT)
2316         {
2317             logf (LOG_DEBUG, "rpn_search_APT");
2318             r = rpn_search_APT (zh, zs->u.simple->u.attributesPlusTerm,
2319                                 attributeSet, stream, sort_sequence,
2320                                 num_bases, basenames);
2321         }
2322         else if (zs->u.simple->which == Z_Operand_resultSetId)
2323         {
2324             logf (LOG_DEBUG, "rpn_search_ref");
2325             r = resultSetRef (zh, zs->u.simple->u.resultSetId);
2326             if (!r)
2327                 r = rset_create (rset_kind_null, NULL);
2328         }
2329         else
2330         {
2331             zh->errCode = 3;
2332             return NULL;
2333         }
2334     }
2335     else
2336     {
2337         zh->errCode = 3;
2338         return NULL;
2339     }
2340     return r;
2341 }
2342
2343
2344 RSET rpn_search (ZebraHandle zh, NMEM nmem,
2345                  Z_RPNQuery *rpn, int num_bases, char **basenames, 
2346                  const char *setname,
2347                  ZebraSet sset)
2348 {
2349     RSET rset;
2350     oident *attrset;
2351     oid_value attributeSet;
2352     Z_SortKeySpecList *sort_sequence;
2353     int sort_status, i;
2354
2355     zh->errCode = 0;
2356     zh->errString = NULL;
2357     zh->hits = 0;
2358
2359     sort_sequence = (Z_SortKeySpecList *)
2360         nmem_malloc (nmem, sizeof(*sort_sequence));
2361     sort_sequence->num_specs = 10;
2362     sort_sequence->specs = (Z_SortKeySpec **)
2363         nmem_malloc (nmem, sort_sequence->num_specs *
2364                      sizeof(*sort_sequence->specs));
2365     for (i = 0; i<sort_sequence->num_specs; i++)
2366         sort_sequence->specs[i] = 0;
2367     
2368     attrset = oid_getentbyoid (rpn->attributeSetId);
2369     attributeSet = attrset->value;
2370     rset = rpn_search_structure (zh, rpn->RPNStructure, attributeSet,
2371                                  nmem, sort_sequence, num_bases, basenames);
2372     if (!rset)
2373         return 0;
2374
2375     if (zh->errCode)
2376         logf (LOG_DEBUG, "search error: %d", zh->errCode);
2377     
2378     for (i = 0; sort_sequence->specs[i]; i++)
2379         ;
2380     sort_sequence->num_specs = i;
2381     if (!i)
2382         resultSetRank (zh, sset, rset);
2383     else
2384     {
2385         logf (LOG_DEBUG, "resultSetSortSingle in rpn_search");
2386         resultSetSortSingle (zh, nmem, sset, rset,
2387                              sort_sequence, &sort_status);
2388         if (zh->errCode)
2389         {
2390             logf (LOG_DEBUG, "resultSetSortSingle status = %d", zh->errCode);
2391         }
2392     }
2393     return rset;
2394 }
2395
2396 struct scan_info_entry {
2397     char *term;
2398     ISAMS_P isam_p;
2399 };
2400
2401 struct scan_info {
2402     struct scan_info_entry *list;
2403     ODR odr;
2404     int before, after;
2405     char prefix[20];
2406 };
2407
2408 static int scan_handle (char *name, const char *info, int pos, void *client)
2409 {
2410     int len_prefix, idx;
2411     struct scan_info *scan_info = (struct scan_info *) client;
2412
2413     len_prefix = strlen(scan_info->prefix);
2414     if (memcmp (name, scan_info->prefix, len_prefix))
2415         return 1;
2416     if (pos > 0)        idx = scan_info->after - pos + scan_info->before;
2417     else
2418         idx = - pos - 1;
2419     scan_info->list[idx].term = (char *)
2420         odr_malloc (scan_info->odr, strlen(name + len_prefix)+1);
2421     strcpy (scan_info->list[idx].term, name + len_prefix);
2422     assert (*info == sizeof(ISAMS_P));
2423     memcpy (&scan_info->list[idx].isam_p, info+1, sizeof(ISAMS_P));
2424     return 0;
2425 }
2426
2427 static void scan_term_untrans (ZebraHandle zh, NMEM stream, int reg_type,
2428                                char **dst, const char *src)
2429 {
2430     char term_dst[1024];
2431     
2432     term_untrans (zh, reg_type, term_dst, src);
2433     
2434     *dst = (char *) nmem_malloc (stream, strlen(term_dst)+1);
2435     strcpy (*dst, term_dst);
2436 }
2437
2438 static void count_set (RSET r, int *count)
2439 {
2440     int psysno = 0;
2441     int kno = 0;
2442     struct it_key key;
2443     RSFD rfd;
2444     int term_index;
2445
2446     logf (LOG_DEBUG, "count_set");
2447
2448     *count = 0;
2449     rfd = rset_open (r, RSETF_READ);
2450     while (rset_read (r, rfd, &key, &term_index))
2451     {
2452         if (key.sysno != psysno)
2453         {
2454             psysno = key.sysno;
2455             (*count)++;
2456         }
2457         kno++;
2458     }
2459     rset_close (r, rfd);
2460     logf (LOG_DEBUG, "%d keys, %d records", kno, *count);
2461 }
2462
2463 void rpn_scan (ZebraHandle zh, ODR stream, Z_AttributesPlusTerm *zapt,
2464                oid_value attributeset,
2465                int num_bases, char **basenames,
2466                int *position, int *num_entries, ZebraScanEntry **list,
2467                int *is_partial)
2468 {
2469     int i;
2470     int pos = *position;
2471     int num = *num_entries;
2472     int before;
2473     int after;
2474     int base_no;
2475     char termz[IT_MAX_WORD+20];
2476     AttrType use;
2477     int use_value;
2478     struct scan_info *scan_info_array;
2479     ZebraScanEntry *glist;
2480     int ords[32], ord_no = 0;
2481     int ptr[32];
2482
2483     unsigned reg_id;
2484     char *search_type = NULL;
2485     char *rank_type = NULL;
2486     int complete_flag;
2487     int sort_flag;
2488     *list = 0;
2489
2490     if (attributeset == VAL_NONE)
2491         attributeset = VAL_BIB1;
2492
2493     logf (LOG_DEBUG, "position = %d, num = %d", pos, num);
2494         
2495     attr_init (&use, zapt, 1);
2496     use_value = attr_find (&use, &attributeset);
2497
2498     if (zebra_maps_attr (zh->service->zebra_maps, zapt, &reg_id, &search_type,
2499                          &rank_type, &complete_flag, &sort_flag))
2500     {
2501         *num_entries = 0;
2502         zh->errCode = 113;
2503         return ;
2504     }
2505
2506     if (use_value == -1)
2507         use_value = 1016;
2508     for (base_no = 0; base_no < num_bases && ord_no < 32; base_no++)
2509     {
2510         int r;
2511         attent attp;
2512         data1_local_attribute *local_attr;
2513
2514         if ((r=att_getentbyatt (zh, &attp, attributeset, use_value)))
2515         {
2516             logf (LOG_DEBUG, "att_getentbyatt fail. set=%d use=%d",
2517                   attributeset, use_value);
2518             if (r == -1)
2519                 zh->errCode = 114;
2520             else
2521                 zh->errCode = 121;
2522             *num_entries = 0;
2523             return;
2524         }
2525         if (zebraExplain_curDatabase (zh->service->zei, basenames[base_no]))
2526         {
2527             zh->errString = basenames[base_no];
2528             zh->errCode = 109; /* Database unavailable */
2529             *num_entries = 0;
2530             return;
2531         }
2532         for (local_attr = attp.local_attributes; local_attr && ord_no < 32;
2533              local_attr = local_attr->next)
2534         {
2535             int ord;
2536
2537             ord = zebraExplain_lookupSU (zh->service->zei, attp.attset_ordinal,
2538                                          local_attr->local);
2539             if (ord > 0)
2540                 ords[ord_no++] = ord;
2541         }
2542     }
2543     if (ord_no == 0)
2544     {
2545         *num_entries = 0;
2546         zh->errCode = 113;
2547         return;
2548     }
2549     /* prepare dictionary scanning */
2550     before = pos-1;
2551     after = 1+num-pos;
2552     scan_info_array = (struct scan_info *)
2553         odr_malloc (stream, ord_no * sizeof(*scan_info_array));
2554     for (i = 0; i < ord_no; i++)
2555     {
2556         int j, prefix_len = 0;
2557         int before_tmp = before, after_tmp = after;
2558         struct scan_info *scan_info = scan_info_array + i;
2559         struct rpn_char_map_info rcmi;
2560
2561         rpn_char_map_prepare (zh, reg_id, &rcmi);
2562
2563         scan_info->before = before;
2564         scan_info->after = after;
2565         scan_info->odr = stream;
2566
2567         scan_info->list = (struct scan_info_entry *)
2568             odr_malloc (stream, (before+after) * sizeof(*scan_info->list));
2569         for (j = 0; j<before+after; j++)
2570             scan_info->list[j].term = NULL;
2571
2572         prefix_len += key_SU_code (ords[i], termz + prefix_len);
2573         termz[prefix_len++] = reg_id;
2574         termz[prefix_len] = 0;
2575         strcpy (scan_info->prefix, termz);
2576
2577         trans_scan_term (zh, zapt, termz+prefix_len, reg_id);
2578                     
2579         dict_scan (zh->service->dict, termz, &before_tmp, &after_tmp,
2580                    scan_info, scan_handle);
2581     }
2582     glist = (ZebraScanEntry *)
2583         odr_malloc (stream, (before+after)*sizeof(*glist));
2584
2585     /* consider terms after main term */
2586     for (i = 0; i < ord_no; i++)
2587         ptr[i] = before;
2588     
2589     *is_partial = 0;
2590     for (i = 0; i<after; i++)
2591     {
2592         int j, j0 = -1;
2593         const char *mterm = NULL;
2594         const char *tst;
2595         RSET rset;
2596         
2597         for (j = 0; j < ord_no; j++)
2598         {
2599             if (ptr[j] < before+after &&
2600                 (tst=scan_info_array[j].list[ptr[j]].term) &&
2601                 (!mterm || strcmp (tst, mterm) < 0))
2602             {
2603                 j0 = j;
2604                 mterm = tst;
2605             }
2606         }
2607         if (j0 == -1)
2608             break;
2609         scan_term_untrans (zh, stream->mem, reg_id,
2610                            &glist[i+before].term, mterm);
2611         rset = rset_trunc (zh, &scan_info_array[j0].list[ptr[j0]].isam_p, 1,
2612                            glist[i+before].term, strlen(glist[i+before].term),
2613                            NULL);
2614
2615         ptr[j0]++;
2616         for (j = j0+1; j<ord_no; j++)
2617         {
2618             if (ptr[j] < before+after &&
2619                 (tst=scan_info_array[j].list[ptr[j]].term) &&
2620                 !strcmp (tst, mterm))
2621             {
2622                 rset_bool_parms bool_parms;
2623                 RSET rset2;
2624
2625                 rset2 =
2626                    rset_trunc (zh, &scan_info_array[j].list[ptr[j]].isam_p, 1,
2627                                glist[i+before].term,
2628                                strlen(glist[i+before].term), NULL);
2629
2630                 bool_parms.key_size = sizeof(struct it_key);
2631                 bool_parms.cmp = key_compare_it;
2632                 bool_parms.rset_l = rset;
2633                 bool_parms.rset_r = rset2;
2634               
2635                 rset = rset_create (rset_kind_or, &bool_parms);
2636
2637                 ptr[j]++;
2638             }
2639         }
2640         count_set (rset, &glist[i+before].occurrences);
2641         rset_delete (rset);
2642     }
2643     if (i < after)
2644     {
2645         *num_entries -= (after-i);
2646         *is_partial = 1;
2647     }
2648
2649     /* consider terms before main term */
2650     for (i = 0; i<ord_no; i++)
2651         ptr[i] = 0;
2652
2653     for (i = 0; i<before; i++)
2654     {
2655         int j, j0 = -1;
2656         const char *mterm = NULL;
2657         const char *tst;
2658         RSET rset;
2659         
2660         for (j = 0; j <ord_no; j++)
2661         {
2662             if (ptr[j] < before &&
2663                 (tst=scan_info_array[j].list[before-1-ptr[j]].term) &&
2664                 (!mterm || strcmp (tst, mterm) > 0))
2665             {
2666                 j0 = j;
2667                 mterm = tst;
2668             }
2669         }
2670         if (j0 == -1)
2671             break;
2672
2673         scan_term_untrans (zh, stream->mem, reg_id,
2674                            &glist[before-1-i].term, mterm);
2675
2676         rset = rset_trunc
2677                (zh, &scan_info_array[j0].list[before-1-ptr[j0]].isam_p, 1,
2678                 glist[before-1-i].term, strlen(glist[before-1-i].term),
2679                 NULL);
2680
2681         ptr[j0]++;
2682
2683         for (j = j0+1; j<ord_no; j++)
2684         {
2685             if (ptr[j] < before &&
2686                 (tst=scan_info_array[j].list[before-1-ptr[j]].term) &&
2687                 !strcmp (tst, mterm))
2688             {
2689                 rset_bool_parms bool_parms;
2690                 RSET rset2;
2691
2692                 rset2 = rset_trunc (zh,
2693                          &scan_info_array[j].list[before-1-ptr[j]].isam_p, 1,
2694                                     glist[before-1-i].term,
2695                                     strlen(glist[before-1-i].term), NULL);
2696
2697                 bool_parms.key_size = sizeof(struct it_key);
2698                 bool_parms.cmp = key_compare_it;
2699                 bool_parms.rset_l = rset;
2700                 bool_parms.rset_r = rset2;
2701               
2702                 rset = rset_create (rset_kind_or, &bool_parms);
2703
2704                 ptr[j]++;
2705             }
2706         }
2707         count_set (rset, &glist[before-1-i].occurrences);
2708         rset_delete (rset);
2709     }
2710     i = before-i;
2711     if (i)
2712     {
2713         *is_partial = 1;
2714         *position -= i;
2715         *num_entries -= i;
2716     }
2717     *list = glist + i;               /* list is set to first 'real' entry */
2718     
2719     logf (LOG_DEBUG, "position = %d, num_entries = %d",
2720           *position, *num_entries);
2721     if (zh->errCode)
2722         logf (LOG_DEBUG, "scan error: %d", zh->errCode);
2723 }
2724