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