Truncation works.
[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.27  1995-10-12 17:07:22  adam
8  * Truncation works.
9  *
10  * Revision 1.26  1995/10/12  12:40:54  adam
11  * Bug fixes in rpn_prox.
12  *
13  * Revision 1.25  1995/10/10  13:59:24  adam
14  * Function rset_open changed its wflag parameter to general flags.
15  *
16  * Revision 1.24  1995/10/09  16:18:37  adam
17  * Function dict_lookup_grep got extra client data parameter.
18  *
19  * Revision 1.23  1995/10/06  16:33:37  adam
20  * Use attribute mappings.
21  *
22  * Revision 1.22  1995/10/06  15:07:39  adam
23  * Structure 'local-number' handled.
24  *
25  * Revision 1.21  1995/10/06  13:52:06  adam
26  * Bug fixes. Handler may abort further scanning.
27  *
28  * Revision 1.20  1995/10/06  11:06:33  adam
29  * Scan entries include 'occurrences' now.
30  *
31  * Revision 1.19  1995/10/06  10:43:56  adam
32  * Scan added. 'occurrences' in scan entries not set yet.
33  *
34  * Revision 1.18  1995/10/04  16:57:20  adam
35  * Key input and merge sort in one pass.
36  *
37  * Revision 1.17  1995/10/04  12:55:17  adam
38  * Bug fix in ranked search. Use=Any keys inserted.
39  *
40  * Revision 1.16  1995/10/02  16:24:40  adam
41  * Use attribute actually used in search requests.
42  *
43  * Revision 1.15  1995/10/02  15:18:52  adam
44  * New member in recRetrieveCtrl: diagnostic.
45  *
46  * Revision 1.14  1995/09/28  12:10:32  adam
47  * Bug fixes. Field prefix used in queries.
48  *
49  * Revision 1.13  1995/09/18  14:17:50  adam
50  * Minor changes.
51  *
52  * Revision 1.12  1995/09/15  14:45:21  adam
53  * Retrieve control.
54  * Work on truncation.
55  *
56  * Revision 1.11  1995/09/14  11:53:27  adam
57  * First work on regular expressions/truncations.
58  *
59  * Revision 1.10  1995/09/11  15:23:26  adam
60  * More work on relevance search.
61  *
62  * Revision 1.9  1995/09/11  13:09:35  adam
63  * More work on relevance feedback.
64  *
65  * Revision 1.8  1995/09/08  14:52:27  adam
66  * Minor changes. Dictionary is lower case now.
67  *
68  * Revision 1.7  1995/09/07  13:58:36  adam
69  * New parameter: result-set file descriptor (RSFD) to support multiple
70  * positions within the same result-set.
71  * Boolean operators: and, or, not implemented.
72  * Result-set references.
73  *
74  * Revision 1.6  1995/09/06  16:11:18  adam
75  * Option: only one word key per file.
76  *
77  * Revision 1.5  1995/09/06  10:33:04  adam
78  * More work on present. Some log messages removed.
79  *
80  * Revision 1.4  1995/09/05  15:28:40  adam
81  * More work on search engine.
82  *
83  * Revision 1.3  1995/09/04  15:20:22  adam
84  * Minor changes.
85  *
86  * Revision 1.2  1995/09/04  12:33:43  adam
87  * Various cleanup. YAZ util used instead.
88  *
89  * Revision 1.1  1995/09/04  09:10:40  adam
90  * More work on index add/del/update.
91  * Merge sort implemented.
92  * Initial work on z39 server.
93  *
94  */
95 #include <stdio.h>
96 #include <assert.h>
97 #include <unistd.h>
98
99 #include "zserver.h"
100 #include <attribute.h>
101
102 #include <rsisam.h>
103 #include <rstemp.h>
104 #include <rsnull.h>
105 #include <rsbool.h>
106 #include <rsrel.h>
107
108 int index_word_prefix_map (char *string, oid_value attrSet, int attrUse)
109 {
110     attent *attp;
111
112     logf (LOG_DEBUG, "oid_value attrSet = %d, attrUse = %d", attrSet, attrUse);
113     attp = att_getentbyatt (attrSet, attrUse);
114     if (!attp)
115         return -1;
116     logf (LOG_DEBUG, "ord=%d", attp->attset_ordinal);
117     return index_word_prefix (string, attp->attset_ordinal,
118                               attp->local_attribute);
119 }
120
121 /*
122  * attr_print: log attributes
123  */
124 static void attr_print (Z_AttributesPlusTerm *t)
125 {
126     int of, i;
127     for (of = 0; of < t->num_attributes; of++)
128     {
129         Z_AttributeElement *element;
130         element = t->attributeList[of];
131
132         switch (element->which) 
133         {
134         case Z_AttributeValue_numeric:
135             logf (LOG_DEBUG, "attributeType=%d value=%d", 
136                   *element->attributeType,
137                   *element->value.numeric);
138             break;
139         case Z_AttributeValue_complex:
140             logf (LOG_DEBUG, "attributeType=%d complex", 
141                   *element->attributeType);
142             for (i = 0; i<element->value.complex->num_list; i++)
143             {
144                 if (element->value.complex->list[i]->which ==
145                     Z_StringOrNumeric_string)
146                     logf (LOG_DEBUG, "   string: '%s'",
147                           element->value.complex->list[i]->u.string);
148                 else if (element->value.complex->list[i]->which ==
149                          Z_StringOrNumeric_numeric)
150                     logf (LOG_DEBUG, "   numeric: '%d'",
151                           *element->value.complex->list[i]->u.numeric);
152             }
153             break;
154         default:
155             assert (0);
156         }
157     }
158 }
159
160 typedef struct {
161     int type;
162     int major;
163     int minor;
164     Z_AttributesPlusTerm *zapt;
165 } AttrType;
166
167 static int attr_find (AttrType *src, oid_value *attributeSetP)
168 {
169     while (src->major < src->zapt->num_attributes)
170     {
171         Z_AttributeElement *element;
172
173         element = src->zapt->attributeList[src->major];
174         if (src->type == *element->attributeType)
175         {
176             switch (element->which) 
177             {
178             case Z_AttributeValue_numeric:
179                 ++(src->major);
180                 if (element->attributeSet && attributeSetP)
181                 {
182                     oident *attrset;
183
184                     attrset = oid_getentbyoid (element->attributeSet);
185                     *attributeSetP = attrset->value;
186                 }
187                 return *element->value.numeric;
188                 break;
189             case Z_AttributeValue_complex:
190                 if (src->minor >= element->value.complex->num_list ||
191                     element->value.complex->list[src->minor]->which !=  
192                     Z_StringOrNumeric_numeric)
193                     break;
194                 ++(src->minor);
195                 if (element->attributeSet && attributeSetP)
196                 {
197                     oident *attrset;
198
199                     attrset = oid_getentbyoid (element->attributeSet);
200                     *attributeSetP = attrset->value;
201                 }
202                 return *element->value.complex->list[src->minor-1]->u.numeric;
203             default:
204                 assert (0);
205             }
206         }
207         ++(src->major);
208     }
209     return -1;
210 }
211
212 static void attr_init (AttrType *src, Z_AttributesPlusTerm *zapt,
213                        int type)
214 {
215     src->zapt = zapt;
216     src->type = type;
217     src->major = 0;
218     src->minor = 0;
219 }
220
221 struct trunc_info {
222     int  *ptr;
223     int  *indx;
224     char **heap;
225     int  heapnum;
226     int  (*cmp)(const void *p1, const void *p2);
227     int  keysize;
228     char *swapbuf;
229     char *tmpbuf;
230     char *buf;
231 };
232
233 static void heap_swap (struct trunc_info *ti, int i1, int i2)
234 {
235     int swap;
236
237     swap = ti->ptr[i1];
238     ti->ptr[i1] = ti->ptr[i2];
239     ti->ptr[i2] = swap;
240 }
241
242 static void heap_delete (struct trunc_info *ti)
243 {
244     int cur = 1, child = 2;
245
246     assert (ti->heapnum > 0);
247
248     heap_swap (ti, 1, ti->heapnum);
249     ti->heapnum--;
250     while (child <= ti->heapnum) {
251         if (child < ti->heapnum &&
252             (*ti->cmp)(ti->heap[ti->ptr[child]],
253                        ti->heap[ti->ptr[1+child]]) > 0)
254             child++;
255         if ((*ti->cmp)(ti->heap[ti->ptr[cur]],
256                        ti->heap[ti->ptr[child]]) > 0)
257         {
258             heap_swap (ti, cur, child);
259             cur = child;
260             child = 2*cur;
261         }
262         else
263             break;
264     }
265 }
266
267 static void heap_insert (struct trunc_info *ti, const char *buf, int indx)
268 {
269     int cur, parent;
270
271     cur = ++(ti->heapnum);
272     memcpy (ti->heap[ti->ptr[cur]], buf, ti->keysize);
273     ti->indx[ti->ptr[cur]] = indx;
274     parent = cur/2;
275     while (parent && (*ti->cmp)(ti->heap[ti->ptr[parent]],
276                                 ti->heap[ti->ptr[cur]]) > 0)
277     {
278         heap_swap (ti, cur, parent);
279         cur = parent;
280         parent = cur/2;
281     }
282 }
283
284 static
285 struct trunc_info *heap_init (int size, int key_size,
286                               int (*cmp)(const void *p1, const void *p2))
287 {
288     struct trunc_info *ti = xmalloc (sizeof(*ti));
289     int i;
290
291     ++size;
292     ti->heapnum = 0;
293     ti->keysize = key_size;
294     ti->cmp = cmp;
295     ti->indx = xmalloc (size * sizeof(*ti->indx));
296     ti->heap = xmalloc (size * sizeof(*ti->heap));
297     ti->ptr = xmalloc (size * sizeof(*ti->ptr));
298     ti->swapbuf = xmalloc (ti->keysize);
299     ti->tmpbuf = xmalloc (ti->keysize);
300     ti->buf = xmalloc (size * ti->keysize);
301     for (i = size; --i >= 0; )
302     {
303         ti->ptr[i] = i;
304         ti->heap[i] = ti->buf + ti->keysize * i;
305     }
306     return ti;
307 }
308
309 static void heap_close (struct trunc_info *ti)
310 {
311     xfree (ti->ptr);
312     xfree (ti->indx);
313     xfree (ti->heap);
314     xfree (ti->swapbuf);
315     xfree (ti->tmpbuf);
316     xfree (ti);
317 }
318
319 static RSET rset_trunc (ISAM isam, ISAM_P *isam_p, int from, int to,
320                         int merge_chunk)
321 {
322     RSET result; 
323     RSFD result_rsfd;
324     rset_temp_parms parms;
325
326     logf (LOG_DEBUG, "rset_trunc, range=%d-%d", from, to-1);
327
328     parms.key_size = sizeof(struct it_key);
329     result = rset_create (rset_kind_temp, &parms);
330     result_rsfd = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
331
332     if (to - from > merge_chunk)
333     {
334         RSFD *rsfd;
335         RSET *rset;
336         int i, i_add = (to-from)/merge_chunk + 1;
337         struct trunc_info *ti;
338         int rscur = 0;
339
340         rset = xmalloc (sizeof(*rset) * merge_chunk);
341         rsfd = xmalloc (sizeof(*rsfd) * merge_chunk);
342         
343         for (i = from; i < to; i += i_add)
344         {
345             if (i_add <= to - i)
346                 rset[rscur] = rset_trunc (isam, isam_p, i, i+i_add, i_add);
347             else
348                 rset[rscur] = rset_trunc (isam, isam_p, i, to,      to-i);
349             rscur++;
350         }
351         ti = heap_init (rscur, sizeof(struct it_key), key_compare);
352         for (i = rscur; --i >= 0; )
353         {
354             rsfd[i] = rset_open (rset[i], RSETF_READ|RSETF_SORT_SYSNO);
355             if (rset_read (rset[i], rsfd[i], ti->tmpbuf))
356                 heap_insert (ti, ti->tmpbuf, i);
357         }
358         while (ti->heapnum)
359         {
360             int n = ti->indx[ti->ptr[1]];
361
362             rset_write (result, result_rsfd, ti->heap[ti->ptr[1]]);
363             heap_delete (ti);
364             if (rset_read (rset[n], rsfd[n], ti->tmpbuf))
365                 heap_insert (ti, ti->tmpbuf, n);
366         }
367         for (i = rscur; --i >= 0; )
368         {
369             rset_close (rset[i], rsfd[i]);
370             rset_delete (rset[i]);
371         }
372         xfree (rset);
373         xfree (rsfd);
374         heap_close (ti);
375     }
376     else
377     {
378         ISPT *ispt;
379         int i;
380         struct trunc_info *ti;
381
382         ispt = xmalloc (sizeof(*ispt) * (to-from));
383
384         ti = heap_init (to-from, sizeof(struct it_key),
385                         key_compare);
386         for (i = to-from; --i >= 0; )
387         {
388             ispt[i] = is_position (isam, isam_p[from+i]);
389             if (is_readkey (ispt[i], ti->tmpbuf))
390                 heap_insert (ti, ti->tmpbuf, i);
391         }
392         while (ti->heapnum)
393         {
394             int n = ti->indx[ti->ptr[1]];
395
396             rset_write (result, result_rsfd, ti->heap[ti->ptr[1]]);
397             heap_delete (ti);
398             if (is_readkey (ispt[n], ti->tmpbuf))
399                 heap_insert (ti, ti->tmpbuf, n);
400         }
401         for (i = to-from; --i >= 0; )
402             is_pt_free (ispt[i]);
403         heap_close (ti);
404         xfree (ispt);
405     }
406     rset_close (result, result_rsfd);
407     return result;
408 }
409
410 struct grep_info {
411     ISAM_P *isam_p_buf;
412     int isam_p_size;
413     int isam_p_indx;
414 };
415
416 static void add_isam_p (const char *info, struct grep_info *p)
417 {
418     if (p->isam_p_indx == p->isam_p_size)
419     {
420         ISAM_P *new_isam_p_buf;
421         
422         p->isam_p_size = 2*p->isam_p_size + 100;
423         new_isam_p_buf = xmalloc (sizeof(*new_isam_p_buf) *
424                                   p->isam_p_size);
425         if (p->isam_p_buf)
426         {
427             memcpy (new_isam_p_buf, p->isam_p_buf,
428                     p->isam_p_indx * sizeof(*p->isam_p_buf));
429             xfree (p->isam_p_buf);
430         }
431         p->isam_p_buf = new_isam_p_buf;
432     }
433     assert (*info == sizeof(*p->isam_p_buf));
434     memcpy (p->isam_p_buf + p->isam_p_indx, info+1, sizeof(*p->isam_p_buf));
435     (p->isam_p_indx)++;
436 }
437
438 static int grep_handle (Dict_char *name, const char *info, void *p)
439 {
440     logf (LOG_DEBUG, "dict name: %s", name);
441     add_isam_p (info, p);
442     return 0;
443 }
444
445 static int trunc_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
446                        const char *term_sub,
447                        oid_value attributeSet, struct grep_info *grep_info)
448 {
449     char term_dict[2*IT_MAX_WORD+2];
450     int i, j;
451     const char *info;    
452     AttrType truncation;
453     int truncation_value;
454     AttrType use;
455     int use_value;
456     oid_value curAttributeSet = attributeSet;
457
458     attr_init (&use, zapt, 1);
459     use_value = attr_find (&use, &curAttributeSet);
460     logf (LOG_DEBUG, "use value %d", use_value);
461     attr_init (&truncation, zapt, 5);
462     truncation_value = attr_find (&truncation, NULL);
463     logf (LOG_DEBUG, "truncation value %d", truncation_value);
464
465     if (use_value == -1)
466         use_value = 1016;
467     i = index_word_prefix_map (term_dict, curAttributeSet, use_value);
468     if (i < 0)
469     {
470         zi->errCode = 114;
471         return -1;
472     }
473     
474     switch (truncation_value)
475     {
476     case -1:         /* not specified */
477     case 100:        /* do not truncate */
478         strcat (term_dict, term_sub);
479         logf (LOG_DEBUG, "dict_lookup: %s", term_dict);
480         if ((info = dict_lookup (zi->wordDict, term_dict)))
481             add_isam_p (info, grep_info);
482         break;
483     case 1:          /* right truncation */
484         strcat (term_dict, term_sub);
485         strcat (term_dict, ".*");
486         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, grep_handle);
487         break;
488     case 2:          /* left truncation */
489     case 3:          /* left&right truncation */
490         zi->errCode = 120;
491         return -1;
492     case 101:        /* process # in term */
493         for (j = strlen(term_dict), i = 0; term_sub[i] && i < 2; i++)
494             term_dict[j++] = term_sub[i];
495         for (; term_sub[i]; i++)
496             if (term_sub[i] == '#')
497             {
498                 term_dict[j++] = '.';
499                 term_dict[j++] = '*';
500             }
501             else
502                 term_dict[j++] = term_sub[i];
503         term_dict[j] = '\0';
504         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, grep_handle);
505         break;
506     case 102:        /* regular expression */
507         strcat (term_dict, term_sub);
508         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, grep_handle);
509         break;
510     }
511     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
512     return 0;
513 }
514
515 static void trans_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
516                         char *termz)
517 {
518     size_t i, sizez;
519     Z_Term *term = zapt->term;
520
521     sizez = term->u.general->len;
522     if (sizez > IT_MAX_WORD)
523         sizez = IT_MAX_WORD;
524     for (i = 0; i < sizez; i++)
525         termz[i] = index_char_cvt (term->u.general->buf[i]);
526     termz[i] = '\0';
527 }
528
529 static RSET rpn_search_APT_relevance (ZServerInfo *zi, 
530                                       Z_AttributesPlusTerm *zapt,
531                                       oid_value attributeSet)
532 {
533     rset_relevance_parms parms;
534     char termz[IT_MAX_WORD+1];
535     char term_sub[IT_MAX_WORD+1];
536     struct grep_info grep_info;
537     char *p0 = termz, *p1 = NULL;
538     RSET result;
539
540     parms.key_size = sizeof(struct it_key);
541     parms.max_rec = 100;
542     parms.cmp = key_compare;
543     parms.is = zi->wordIsam;
544
545     if (zapt->term->which != Z_Term_general)
546     {
547         zi->errCode = 124;
548         return NULL;
549     }
550     trans_term (zi, zapt, termz);
551     grep_info.isam_p_indx = 0;
552     grep_info.isam_p_size = 0;
553     grep_info.isam_p_buf = NULL;
554     while (1)
555     {
556         if ((p1 = strchr (p0, ' ')))
557         {
558             memcpy (term_sub, p0, p1-p0);
559             term_sub[p1-p0] = '\0';
560         }
561         else
562             strcpy (term_sub, p0);
563         if (trunc_term (zi, zapt, term_sub, attributeSet, &grep_info))
564             return NULL;
565         if (!p1)
566             break;
567         p0 = p1;
568         while (*++p0 == ' ')
569             ;
570     }
571     parms.isam_positions = grep_info.isam_p_buf;
572     parms.no_isam_positions = grep_info.isam_p_indx;
573     if (grep_info.isam_p_indx > 0)
574         result = rset_create (rset_kind_relevance, &parms);
575     else
576         result = rset_create (rset_kind_null, NULL);
577     xfree (grep_info.isam_p_buf);
578     return result;
579 }
580
581 static RSET rpn_search_APT_word (ZServerInfo *zi,
582                                  Z_AttributesPlusTerm *zapt,
583                                  oid_value attributeSet)
584 {
585     rset_isam_parms parms;
586     char termz[IT_MAX_WORD+1];
587     struct grep_info grep_info;
588     RSET result;
589
590     if (zapt->term->which != Z_Term_general)
591     {
592         zi->errCode = 124;
593         return NULL;
594     }
595     trans_term (zi, zapt, termz);
596
597     grep_info.isam_p_indx = 0;
598     grep_info.isam_p_size = 0;
599     grep_info.isam_p_buf = NULL;
600
601     if (trunc_term (zi, zapt, termz, attributeSet, &grep_info))
602         return NULL;
603     if (grep_info.isam_p_indx < 1)
604         result = rset_create (rset_kind_null, NULL);
605     else if (grep_info.isam_p_indx == 1)
606     {
607         parms.is = zi->wordIsam;
608         parms.pos = *grep_info.isam_p_buf;
609         result = rset_create (rset_kind_isam, &parms);
610     }
611     else
612         result = rset_trunc (zi->wordIsam, grep_info.isam_p_buf, 0,
613                              grep_info.isam_p_indx, 400);
614     xfree (grep_info.isam_p_buf);
615     return result;
616 }
617
618 static RSET rpn_prox (RSET *rset, int rset_no)
619 {
620     int i;
621     RSFD *rsfd;
622     int  *more;
623     struct it_key **buf;
624     RSFD rsfd_result;
625     RSET result;
626     rset_temp_parms parms;
627     
628     rsfd = xmalloc (sizeof(*rsfd)*rset_no);
629     more = xmalloc (sizeof(*more)*rset_no);
630     buf = xmalloc (sizeof(*buf)*rset_no);
631
632     for (i = 0; i<rset_no; i++)
633     {
634         buf[i] = xmalloc (sizeof(**buf));
635         rsfd[i] = rset_open (rset[i], RSETF_READ|RSETF_SORT_SYSNO);
636         if (!(more[i] = rset_read (rset[i], rsfd[i], buf[i])))
637         {
638             while (i >= 0)
639             {
640                 rset_close (rset[i], rsfd[i]);
641                 xfree (buf[i]);
642                 --i;
643             }
644             xfree (rsfd);
645             xfree (more);
646             xfree (buf);
647             return rset_create (rset_kind_null, NULL);
648         }
649     }
650     parms.key_size = sizeof (struct it_key);
651     result = rset_create (rset_kind_temp, &parms);
652     rsfd_result = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
653     
654     while (*more)
655     {
656         for (i = 1; i<rset_no; i++)
657         {
658             int cmp;
659             
660             if (!more[i])
661             {
662                 *more = 0;
663                 break;
664             }
665             cmp = key_compare (buf[i], buf[i-1]);
666             if (cmp > 1)
667             {
668                 more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
669                 break;
670             }
671             else if (cmp == 1)
672             {
673                 if (buf[i-1]->seqno+1 != buf[i]->seqno)
674                 {
675                     more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
676                     break;
677                 }
678             }
679             else
680             {
681                 more[i] = rset_read (rset[i], rsfd[i], buf[i]);
682                 break;
683             }
684         }
685         if (i == rset_no)
686         {
687             rset_write (result, rsfd_result, buf[0]);
688             more[0] = rset_read (*rset, *rsfd, *buf);
689         }
690     }
691     
692     for (i = 0; i<rset_no; i++)
693     {
694         rset_close (rset[i], rsfd[i]);
695         xfree (buf[i]);
696     }
697     rset_close (result, rsfd_result);
698     xfree (buf);
699     xfree (more);
700     xfree (rsfd);
701     return result;
702 }
703
704 static RSET rpn_search_APT_phrase (ZServerInfo *zi,
705                                    Z_AttributesPlusTerm *zapt,
706                                    oid_value attributeSet)
707 {
708     char termz[IT_MAX_WORD+1];
709     char term_sub[IT_MAX_WORD+1];
710     char *p0 = termz, *p1 = NULL;
711     RSET rset[60], result;
712     int i, rset_no = 0;
713     struct grep_info grep_info;
714
715     if (zapt->term->which != Z_Term_general)
716     {
717         zi->errCode = 124;
718         return NULL;
719     }
720     trans_term (zi, zapt, termz);
721
722     grep_info.isam_p_size = 0;
723     grep_info.isam_p_buf = NULL;
724
725     while (1)
726     {
727         if ((p1 = strchr (p0, ' ')))
728         {
729             memcpy (term_sub, p0, p1-p0);
730             term_sub[p1-p0] = '\0';
731         }
732         else
733             strcpy (term_sub, p0);
734
735         grep_info.isam_p_indx = 0;
736         if (trunc_term (zi, zapt, term_sub, attributeSet, &grep_info))
737             return NULL;
738         if (grep_info.isam_p_indx == 0)
739             rset[rset_no] = rset_create (rset_kind_null, NULL);
740         else if (grep_info.isam_p_indx > 1)
741             rset[rset_no] = rset_trunc (zi->wordIsam,
742                                         grep_info.isam_p_buf, 0,
743                                         grep_info.isam_p_indx, 400);
744         else
745         {
746             rset_isam_parms parms;
747             
748             parms.is = zi->wordIsam;
749             parms.pos = *grep_info.isam_p_buf;
750             rset[rset_no] = rset_create (rset_kind_isam, &parms);
751         }
752         assert (rset[rset_no]);
753         if (++rset_no >= sizeof(rset)/sizeof(*rset))
754             break;
755         if (!p1)
756             break;
757         p0 = p1;
758         while (*++p0 == ' ')
759             ;
760     }
761     xfree (grep_info.isam_p_buf);
762     if (rset_no == 0)
763         return rset_create (rset_kind_null, NULL);
764     else if (rset_no == 1)
765         return (rset[0]);
766
767     result = rpn_prox (rset, rset_no);
768     for (i = 0; i<rset_no; i++)
769         rset_delete (rset[i]);
770     return result;
771 }
772
773 static RSET rpn_search_APT_local (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
774                                   oid_value attributeSet)
775 {
776     RSET result;
777     RSFD rsfd;
778     struct it_key key;
779     rset_temp_parms parms;
780     char termz[IT_MAX_WORD+1];
781
782     if (zapt->term->which != Z_Term_general)
783     {
784         zi->errCode = 124;
785         return NULL;
786     }
787     parms.key_size = sizeof (struct it_key);
788     result = rset_create (rset_kind_temp, &parms);
789     rsfd = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
790
791     trans_term (zi, zapt, termz);
792     key.sysno = atoi (termz);
793     if (key.sysno <= 0)
794         key.sysno = 1;
795     rset_write (result, rsfd, &key);
796     rset_close (result, rsfd);
797     return result;
798 }
799
800
801 static RSET rpn_search_APT (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
802                             oid_value attributeSet)
803 {
804     AttrType relation;
805     AttrType structure;
806     int relation_value, structure_value;
807
808     attr_init (&relation, zapt, 2);
809     attr_init (&structure, zapt, 4);
810     
811     relation_value = attr_find (&relation, NULL);
812     structure_value = attr_find (&structure, NULL);
813     switch (structure_value)
814     {
815     case -1:
816         if (relation_value == 102) /* relevance relation */
817             return rpn_search_APT_relevance (zi, zapt, attributeSet);
818         return rpn_search_APT_phrase (zi, zapt, attributeSet);
819     case 1: /* phrase */
820         if (relation_value == 102) /* relevance relation */
821             return rpn_search_APT_relevance (zi, zapt, attributeSet);
822         return rpn_search_APT_phrase (zi, zapt, attributeSet);
823         break;
824     case 2: /* word */
825         if (relation_value == 102) /* relevance relation */
826             return rpn_search_APT_relevance (zi, zapt, attributeSet);
827         return rpn_search_APT_word (zi, zapt, attributeSet);
828     case 3: /* key */
829         break;
830     case 4: /* year */
831         break;
832     case 5: /* date - normalized */
833         break;
834     case 6: /* word list */
835         return rpn_search_APT_relevance (zi, zapt, attributeSet);
836     case 100: /* date - un-normalized */
837         break;
838     case 101: /* name - normalized */
839         break;
840     case 102: /* date - un-normalized */
841         break;
842     case 103: /* structure */
843         break;
844     case 104: /* urx */
845         break;
846     case 105: /* free-form-text */
847         return rpn_search_APT_relevance (zi, zapt, attributeSet);
848     case 106: /* document-text */
849         return rpn_search_APT_relevance (zi, zapt, attributeSet);
850     case 107: /* local-number */
851         return rpn_search_APT_local (zi, zapt, attributeSet);
852     case 108: /* string */ 
853         return rpn_search_APT_word (zi, zapt, attributeSet);
854     case 109: /* numeric string */
855         break;
856     }
857     zi->errCode = 118;
858     return NULL;
859 }
860
861 static RSET rpn_search_ref (ZServerInfo *zi, Z_ResultSetId *resultSetId)
862 {
863     ZServerSet *s;
864
865     if (!(s = resultSetGet (zi, resultSetId)))
866         return rset_create (rset_kind_null, NULL);
867     return s->rset;
868 }
869
870 static RSET rpn_search_structure (ZServerInfo *zi, Z_RPNStructure *zs,
871                                   oid_value attributeSet)
872 {
873     RSET r = NULL;
874     if (zs->which == Z_RPNStructure_complex)
875     {
876         rset_bool_parms bool_parms;
877
878         bool_parms.rset_l = rpn_search_structure (zi, zs->u.complex->s1,
879                                                   attributeSet);
880         if (bool_parms.rset_l == NULL)
881             return NULL;
882         bool_parms.rset_r = rpn_search_structure (zi, zs->u.complex->s2,
883                                                   attributeSet);
884         if (bool_parms.rset_r == NULL)
885         {
886             rset_delete (bool_parms.rset_l);
887             return NULL;
888         }
889         bool_parms.key_size = sizeof(struct it_key);
890         bool_parms.cmp = key_compare;
891
892         switch (zs->u.complex->operator->which)
893         {
894         case Z_Operator_and:
895             r = rset_create (rset_kind_and, &bool_parms);
896             break;
897         case Z_Operator_or:
898             r = rset_create (rset_kind_or, &bool_parms);
899             break;
900         case Z_Operator_and_not:
901             r = rset_create (rset_kind_not, &bool_parms);
902             break;
903         default:
904             assert (0);
905         }
906     }
907     else if (zs->which == Z_RPNStructure_simple)
908     {
909         if (zs->u.simple->which == Z_Operand_APT)
910         {
911             logf (LOG_DEBUG, "rpn_search_APT");
912             r = rpn_search_APT (zi, zs->u.simple->u.attributesPlusTerm,
913                                 attributeSet);
914         }
915         else if (zs->u.simple->which == Z_Operand_resultSetId)
916         {
917             logf (LOG_DEBUG, "rpn_search_ref");
918             r = rpn_search_ref (zi, zs->u.simple->u.resultSetId);
919         }
920         else
921         {
922             assert (0);
923         }
924     }
925     else
926     {
927         assert (0);
928     }
929     return r;
930 }
931
932 static void count_set (RSET r, int *count)
933 {
934     int psysno = 0;
935     struct it_key key;
936     RSFD rfd;
937
938     logf (LOG_DEBUG, "rpn_save_set");
939     *count = 0;
940     rfd = rset_open (r, RSETF_READ|RSETF_SORT_SYSNO);
941     while (rset_read (r, rfd, &key))
942     {
943         if (key.sysno != psysno)
944         {
945             psysno = key.sysno;
946             (*count)++;
947         }
948     }
949     rset_close (r, rfd);
950     logf (LOG_DEBUG, "%d distinct sysnos", *count);
951 }
952
953 int rpn_search (ZServerInfo *zi,
954                 Z_RPNQuery *rpn, int num_bases, char **basenames, 
955                 const char *setname, int *hits)
956 {
957     RSET rset;
958     oident *attrset;
959     oid_value attributeSet;
960
961     zi->errCode = 0;
962     zi->errString = NULL;
963     
964     attrset = oid_getentbyoid (rpn->attributeSetId);
965     attributeSet = attrset->value;
966
967     rset = rpn_search_structure (zi, rpn->RPNStructure, attributeSet);
968     if (!rset)
969         return zi->errCode;
970     count_set (rset, hits);
971     resultSetAdd (zi, setname, 1, rset);
972     if (zi->errCode)
973         logf (LOG_DEBUG, "search error: %d", zi->errCode);
974     return zi->errCode;
975 }
976
977 struct scan_info {
978     struct scan_entry *list;
979     ODR odr;
980     int before, after;
981     ISAM isam;
982     char prefix[20];
983 };
984
985 static int scan_handle (Dict_char *name, const char *info, int pos, 
986                         void *client)
987 {
988     int len_prefix, idx;
989     ISAM_P isam_p;
990     RSET rset;
991     struct scan_info *scan_info = client;
992
993     rset_isam_parms parms;
994
995     len_prefix = strlen(scan_info->prefix);
996     if (memcmp (name, scan_info->prefix, len_prefix))
997         return 1;
998     if (pos > 0)
999         idx = scan_info->after - pos + scan_info->before;
1000     else
1001         idx = - pos - 1;
1002     scan_info->list[idx].term = odr_malloc (scan_info->odr,
1003                                             strlen(name + len_prefix)+1);
1004     strcpy (scan_info->list[idx].term, name + len_prefix);
1005     assert (*info == sizeof(isam_p));
1006     memcpy (&isam_p, info+1, sizeof(isam_p));
1007     parms.is = scan_info->isam;
1008     parms.pos = isam_p;
1009 #if 1
1010     rset = rset_create (rset_kind_isam, &parms);
1011     count_set (rset, &scan_info->list[idx].occurrences);
1012     rset_delete (rset);
1013 #else
1014     scan_info->list[idx].occurrences = 1;
1015 #endif
1016     logf (LOG_DEBUG, "pos=%3d idx=%3d name=%s", pos, idx, name);
1017     return 0;
1018 }
1019
1020 int rpn_scan (ZServerInfo *zi, ODR odr, Z_AttributesPlusTerm *zapt,
1021               int *position, int *num_entries, struct scan_entry **list,
1022               int *status)
1023 {
1024     int i, j, sizez;
1025     int pos = *position;
1026     int num = *num_entries;
1027     int before;
1028     int after;
1029     char termz[IT_MAX_WORD+20];
1030     AttrType use;
1031     int use_value;
1032     Z_Term *term = zapt->term;
1033     struct scan_info scan_info;
1034
1035     logf (LOG_DEBUG, "scan, position = %d, num = %d", pos, num);
1036     scan_info.before = before = pos-1;
1037     scan_info.after = after = 1+num-pos;
1038     scan_info.odr = odr;
1039
1040     logf (LOG_DEBUG, "scan, before = %d, after = %d", before, after);
1041     
1042     scan_info.isam = zi->wordIsam;
1043     scan_info.list = odr_malloc (odr, (before+after)*sizeof(*scan_info.list));
1044     for (j = 0; j<before+after; j++)
1045         scan_info.list[j].term = NULL;
1046     attr_init (&use, zapt, 1);
1047     use_value = attr_find (&use, NULL);
1048     logf (LOG_DEBUG, "use value %d", use_value);
1049
1050     if (use_value == -1)
1051         use_value = 1016;
1052     i = index_word_prefix (termz, 1, use_value);
1053     strcpy (scan_info.prefix, termz);
1054     sizez = term->u.general->len;
1055     if (sizez > IT_MAX_WORD)
1056         sizez = IT_MAX_WORD;
1057     for (j = 0; j<sizez; j++)
1058         termz[j+i] = index_char_cvt (term->u.general->buf[j]);
1059     termz[j+i] = '\0';
1060     
1061     dict_scan (zi->wordDict, termz, &before, &after, &scan_info, scan_handle);
1062
1063     *status = BEND_SCAN_SUCCESS;
1064
1065     for (i = 0; i<scan_info.after; i++)
1066         if (scan_info.list[scan_info.before+scan_info.after-i-1].term)
1067             break;
1068     *num_entries -= i;
1069     if (i)
1070         *status = BEND_SCAN_PARTIAL;
1071
1072     for (i = 0; i<scan_info.before; i++)
1073         if (scan_info.list[i].term)
1074             break;
1075     if (i)
1076         *status = BEND_SCAN_PARTIAL;
1077     *position -= i;
1078     *num_entries -= i;
1079
1080     *list = scan_info.list+i;       /* list is set to first 'real' entry */
1081
1082     if (*num_entries == 0)          /* signal 'unsupported use-attribute' */
1083         zi->errCode = 114;          /* if no entries was found */
1084     logf (LOG_DEBUG, "position = %d, num_entries = %d",
1085           *position, *num_entries);
1086     if (zi->errCode)
1087         logf (LOG_DEBUG, "scan error: %d", zi->errCode);
1088     return 0;
1089 }
1090               
1091
1092