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