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