The rec_get function returns NULL if record doesn't exist - will
[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.38  1995-12-11 09:12:55  adam
8  * The rec_get function returns NULL if record doesn't exist - will
9  * happen in the server if the result set records have been deleted since
10  * the creation of the set (i.e. the search).
11  * The server saves a result temporarily if it is 'volatile', i.e. the
12  * set is register dependent.
13  *
14  * Revision 1.37  1995/12/06  15:05:28  adam
15  * More verbose in count_set.
16  *
17  * Revision 1.36  1995/12/06  12:41:27  adam
18  * New command 'stat' for the index program.
19  * Filenames can be read from stdin by specifying '-'.
20  * Bug fix/enhancement of the transformation from terms to regular
21  * expressons in the search engine.
22  *
23  * Revision 1.35  1995/11/27  09:29:00  adam
24  * Bug fixes regarding conversion to regular expressions.
25  *
26  * Revision 1.34  1995/11/16  17:00:56  adam
27  * Better logging of rpn query.
28  *
29  * Revision 1.33  1995/11/01  13:58:28  quinn
30  * Moving data1 to yaz/retrieval
31  *
32  * Revision 1.32  1995/10/27  14:00:11  adam
33  * Implemented detection of database availability.
34  *
35  * Revision 1.31  1995/10/17  18:02:10  adam
36  * New feature: databases. Implemented as prefix to words in dictionary.
37  *
38  * Revision 1.30  1995/10/16  09:32:38  adam
39  * More work on relational op.
40  *
41  * Revision 1.29  1995/10/13  16:01:49  adam
42  * Work on relations.
43  *
44  * Revision 1.28  1995/10/13  12:26:43  adam
45  * Optimization of truncation.
46  *
47  * Revision 1.27  1995/10/12  17:07:22  adam
48  * Truncation works.
49  *
50  * Revision 1.26  1995/10/12  12:40:54  adam
51  * Bug fixes in rpn_prox.
52  *
53  * Revision 1.25  1995/10/10  13:59:24  adam
54  * Function rset_open changed its wflag parameter to general flags.
55  *
56  * Revision 1.24  1995/10/09  16:18:37  adam
57  * Function dict_lookup_grep got extra client data parameter.
58  *
59  * Revision 1.23  1995/10/06  16:33:37  adam
60  * Use attribute mappings.
61  *
62  * Revision 1.22  1995/10/06  15:07:39  adam
63  * Structure 'local-number' handled.
64  *
65  * Revision 1.21  1995/10/06  13:52:06  adam
66  * Bug fixes. Handler may abort further scanning.
67  *
68  * Revision 1.20  1995/10/06  11:06:33  adam
69  * Scan entries include 'occurrences' now.
70  *
71  * Revision 1.19  1995/10/06  10:43:56  adam
72  * Scan added. 'occurrences' in scan entries not set yet.
73  *
74  * Revision 1.18  1995/10/04  16:57:20  adam
75  * Key input and merge sort in one pass.
76  *
77  * Revision 1.17  1995/10/04  12:55:17  adam
78  * Bug fix in ranked search. Use=Any keys inserted.
79  *
80  * Revision 1.16  1995/10/02  16:24:40  adam
81  * Use attribute actually used in search requests.
82  *
83  * Revision 1.15  1995/10/02  15:18:52  adam
84  * New member in recRetrieveCtrl: diagnostic.
85  *
86  * Revision 1.14  1995/09/28  12:10:32  adam
87  * Bug fixes. Field prefix used in queries.
88  *
89  * Revision 1.13  1995/09/18  14:17:50  adam
90  * Minor changes.
91  *
92  * Revision 1.12  1995/09/15  14:45:21  adam
93  * Retrieve control.
94  * Work on truncation.
95  *
96  * Revision 1.11  1995/09/14  11:53:27  adam
97  * First work on regular expressions/truncations.
98  *
99  * Revision 1.10  1995/09/11  15:23:26  adam
100  * More work on relevance search.
101  *
102  * Revision 1.9  1995/09/11  13:09:35  adam
103  * More work on relevance feedback.
104  *
105  * Revision 1.8  1995/09/08  14:52:27  adam
106  * Minor changes. Dictionary is lower case now.
107  *
108  * Revision 1.7  1995/09/07  13:58:36  adam
109  * New parameter: result-set file descriptor (RSFD) to support multiple
110  * positions within the same result-set.
111  * Boolean operators: and, or, not implemented.
112  * Result-set references.
113  *
114  * Revision 1.6  1995/09/06  16:11:18  adam
115  * Option: only one word key per file.
116  *
117  * Revision 1.5  1995/09/06  10:33:04  adam
118  * More work on present. Some log messages removed.
119  *
120  * Revision 1.4  1995/09/05  15:28:40  adam
121  * More work on search engine.
122  *
123  * Revision 1.3  1995/09/04  15:20:22  adam
124  * Minor changes.
125  *
126  * Revision 1.2  1995/09/04  12:33:43  adam
127  * Various cleanup. YAZ util used instead.
128  *
129  * Revision 1.1  1995/09/04  09:10:40  adam
130  * More work on index add/del/update.
131  * Merge sort implemented.
132  * Initial work on z39 server.
133  *
134  */
135 #include <stdio.h>
136 #include <assert.h>
137 #include <unistd.h>
138 #include <ctype.h>
139
140 #include "zserver.h"
141 #include "attribute.h"
142
143 #include <rsisam.h>
144 #include <rstemp.h>
145 #include <rsnull.h>
146 #include <rsbool.h>
147 #include <rsrel.h>
148
149 int index_word_prefix_map (char *string, oid_value attrSet, int attrUse,
150                            char *basename)
151 {
152     attent *attp;
153
154     logf (LOG_DEBUG, "oid_value attrSet = %d, attrUse = %d", attrSet, attrUse);
155     attp = att_getentbyatt (attrSet, attrUse);
156     if (!attp)
157         return -1;
158     logf (LOG_DEBUG, "ord=%d", attp->attset_ordinal);
159     return index_word_prefix (string, attp->attset_ordinal,
160                               attp->local_attribute, basename);
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 /* section that preserve all keys */
416             heap_delete (ti);
417             if (is_readkey (ispt[n], ti->tmpbuf))
418                 heap_insert (ti, ti->tmpbuf, n);
419             else
420                 is_pt_free (ispt[n]);
421 #else
422 /* section that preserve all keys with unique sysnos */
423             while (1)
424             {
425                 if (!is_readkey (ispt[n], ti->tmpbuf))
426                 {
427                     heap_delete (ti);
428                     is_pt_free (ispt[n]);
429                     break;
430                 }
431                 if ((*ti->cmp)(ti->tmpbuf, ti->heap[ti->ptr[1]]) > 1)
432                 {
433                     heap_delete (ti);
434                     heap_insert (ti, ti->tmpbuf, n);
435                     break;
436                 }
437             }
438 #endif
439         }
440         heap_close (ti);
441         xfree (ispt);
442     }
443     rset_close (result, result_rsfd);
444     return result;
445 }
446
447 static int isam_trunc_cmp (const void *p1, const void *p2)
448 {
449     ISAM_P i1 = *(ISAM_P*) p1;
450     ISAM_P i2 = *(ISAM_P*) p2;
451     int d;
452
453     d = is_type (i1) - is_type (i2);
454     if (d)
455         return d;
456     return is_block (i1) - is_block (i2);
457 }
458
459 static RSET rset_trunc (ISAM isam, ISAM_P *isam_p, int no)
460 {
461
462     qsort (isam_p, no, sizeof(*isam_p), isam_trunc_cmp);
463     return rset_trunc_r (isam, isam_p, 0, no, 100);
464 }
465
466 struct grep_info {
467     ISAM_P *isam_p_buf;
468     int isam_p_size;
469     int isam_p_indx;
470 };
471
472 static void add_isam_p (const char *info, struct grep_info *p)
473 {
474     if (p->isam_p_indx == p->isam_p_size)
475     {
476         ISAM_P *new_isam_p_buf;
477         
478         p->isam_p_size = 2*p->isam_p_size + 100;
479         new_isam_p_buf = xmalloc (sizeof(*new_isam_p_buf) *
480                                   p->isam_p_size);
481         if (p->isam_p_buf)
482         {
483             memcpy (new_isam_p_buf, p->isam_p_buf,
484                     p->isam_p_indx * sizeof(*p->isam_p_buf));
485             xfree (p->isam_p_buf);
486         }
487         p->isam_p_buf = new_isam_p_buf;
488     }
489     assert (*info == sizeof(*p->isam_p_buf));
490     memcpy (p->isam_p_buf + p->isam_p_indx, info+1, sizeof(*p->isam_p_buf));
491     (p->isam_p_indx)++;
492 }
493
494 static int grep_handle (Dict_char *name, const char *info, void *p)
495 {
496     logf (LOG_DEBUG, "dict name: %s", name);
497     add_isam_p (info, p);
498     return 0;
499 }
500
501 static void gen_regular_rel (char *dst, int val, int islt)
502 {
503     int dst_p = 1;
504     int w, d, i;
505     int pos = 0;
506     char numstr[20];
507
508     *dst = '(';
509     sprintf (numstr, "%d", val);
510     for (w = strlen(numstr); --w >= 0; pos++)
511     {
512         d = numstr[w];
513         if (pos > 0)
514         {
515             if (islt)
516             {
517                 if (d == '0')
518                     continue;
519                 d--;
520             } 
521             else
522             {
523                 if (d == '9')
524                     continue;
525                 d++;
526             }
527         }
528         
529         strcpy (dst + dst_p, numstr);
530         dst_p = strlen(dst) - pos - 1;
531
532         if (islt)
533         {
534             if (d != '0')
535             {
536                 dst[dst_p++] = '[';
537                 dst[dst_p++] = '0';
538                 dst[dst_p++] = '-';
539                 dst[dst_p++] = d;
540                 dst[dst_p++] = ']';
541             }
542             else
543                 dst[dst_p++] = d;
544         }
545         else
546         {
547             if (d != '9')
548             { 
549                 dst[dst_p++] = '[';
550                 dst[dst_p++] = d;
551                 dst[dst_p++] = '-';
552                 dst[dst_p++] = '9';
553                 dst[dst_p++] = ']';
554             }
555             else
556                 dst[dst_p++] = d;
557         }
558         for (i = 0; i<pos; i++)
559         {
560             dst[dst_p++] = '[';
561             dst[dst_p++] = '0';
562             dst[dst_p++] = '-';
563             dst[dst_p++] = '9';
564             dst[dst_p++] = ']';
565         }
566         dst[dst_p++] = '|';
567     }
568     dst[dst_p] = '\0';
569     if (islt)
570     {
571         for (i=1; i<pos; i++)
572             strcat (dst, "[0-9]?");
573     }
574     else
575     {
576         for (i = 0; i <= pos; i++)
577             strcat (dst, "[0-9]");
578         strcat (dst, "[0-9]*");
579     }
580     strcat (dst, ")");
581 }
582
583 static int relational_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
584                             const char *term_sub,
585                             char *term_dict,
586                             oid_value attributeSet,
587                             struct grep_info *grep_info,
588                             int *max_pos)
589 {
590     AttrType relation;
591     int relation_value;
592     int term_value;
593     int r;
594
595     attr_init (&relation, zapt, 2);
596     relation_value = attr_find (&relation, NULL);
597     term_value = atoi (term_sub);
598
599     switch (relation_value)
600     {
601     case 1:
602         if (term_value <= 0)
603             return 1;
604         logf (LOG_DEBUG, "Relation <");
605         gen_regular_rel (term_dict + strlen(term_dict), term_value-1, 1);
606         break;
607     case 2:
608         if (term_value < 0)
609             return 1;
610         logf (LOG_DEBUG, "Relation <=");
611         gen_regular_rel (term_dict + strlen(term_dict), term_value, 1);
612         break;
613     case 4:
614         if (term_value < 0)
615             term_value = 0;
616         logf (LOG_DEBUG, "Relation >=");
617         gen_regular_rel (term_dict + strlen(term_dict), term_value, 0);
618         break;
619     case 5:
620         if (term_value < 0)
621             term_value = 0;
622         logf (LOG_DEBUG, "Relation >");
623         gen_regular_rel (term_dict + strlen(term_dict), term_value+1, 0);
624         break;
625     default:
626         return 0;
627     }
628     logf (LOG_DEBUG, "dict_lookup_grep: %s", term_dict);
629     r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info, max_pos,
630                           grep_handle);
631     if (r)
632         logf (LOG_WARN, "dict_lookup_grep fail, rel=gt: %d", r);
633     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
634     return 1;
635 }
636
637 static void verbatim_char (int ch, int *indx, char *dst)
638 {
639     if (!isalnum (ch))
640         dst[(*indx)++] = '\\';
641     dst[(*indx)++] = ch;
642 }
643
644 static int trunc_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
645                        const char *term_sub,
646                        oid_value attributeSet, struct grep_info *grep_info,
647                        int num_bases, char **basenames)
648 {
649     char term_dict[2*IT_MAX_WORD+2];
650     int i, j, r, base_no;
651     AttrType truncation;
652     int truncation_value;
653     AttrType use;
654     int use_value;
655     oid_value curAttributeSet = attributeSet;
656
657     attr_init (&use, zapt, 1);
658     use_value = attr_find (&use, &curAttributeSet);
659     logf (LOG_DEBUG, "use value %d", use_value);
660     attr_init (&truncation, zapt, 5);
661     truncation_value = attr_find (&truncation, NULL);
662     logf (LOG_DEBUG, "truncation value %d", truncation_value);
663
664     if (use_value == -1)
665         use_value = 1016;
666
667     for (base_no = 0; base_no < num_bases; base_no++)
668     {
669         int max_pos;
670         int prefix_len = index_word_prefix_map (term_dict, curAttributeSet,
671                                                 use_value,
672                                                 basenames[base_no]);
673         if (prefix_len < 0)
674         {
675             zi->errCode = 114;
676             return -1;
677         }
678         if (!relational_term (zi, zapt, term_sub, term_dict,
679                               attributeSet, grep_info, &max_pos))
680         {
681             switch (truncation_value)
682             {
683             case -1:         /* not specified */
684             case 100:        /* do not truncate */
685                 j = strlen(term_dict);
686                 term_dict[j++] = '(';
687                 for (i = 0; term_sub[i]; i++)
688                     verbatim_char (term_sub[i], &j, term_dict);
689                 strcpy (term_dict+j, ")");
690                 r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info,
691                                       &max_pos, grep_handle);
692                 if (r)
693                     logf (LOG_WARN, "dict_lookup_grep err, trunc=none:%d", r);
694                 break;
695             case 1:          /* right truncation */
696                 j = strlen(term_dict);
697                 term_dict[j++] = '(';
698                 for (i = 0; term_sub[i]; i++)
699                     verbatim_char (term_sub[i], &j, term_dict);
700                 strcpy (term_dict+j, ".*)");
701                 dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info,
702                                   &max_pos, grep_handle);
703                 break;
704             case 2:          /* left truncation */
705             case 3:          /* left&right truncation */
706                 zi->errCode = 120;
707                 return -1;
708             case 101:        /* process # in term */
709                 j = strlen(term_dict);
710                 term_dict[j++] = '(';
711                 for (i=0; term_sub[i]; i++)
712                     if (term_sub[i] == '#' && i > 2)
713                     {
714                         term_dict[j++] = '.';
715                         term_dict[j++] = '*';
716                     }
717                     else
718                         verbatim_char (term_sub[i], &j, term_dict);
719                 strcpy (term_dict+j, ")");
720                 r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info,
721                                       &max_pos, grep_handle);
722                 if (r)
723                     logf (LOG_WARN, "dict_lookup_grep err, trunc=#: %d",
724                           r);
725                 break;
726             case 102:        /* regular expression */
727                 sprintf (term_dict + strlen(term_dict), "(%s)", term_sub);
728                 r = dict_lookup_grep (zi->wordDict, term_dict, 0, grep_info,
729                                       &max_pos, grep_handle);
730                 if (r)
731                     logf (LOG_WARN, "dict_lookup_grep err, trunc=regular: %d",
732                           r);
733                 break;
734             }
735         }
736         if (max_pos <= strlen(basenames[base_no]))
737         {
738             zi->errCode = 109; /* Database unavailable */
739             zi->errString = basenames[base_no];
740             return -1;
741         }
742     }
743     logf (LOG_DEBUG, "%d positions", grep_info->isam_p_indx);
744     return 0;
745 }
746
747 static void trans_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
748                         char *termz)
749 {
750     size_t i, sizez;
751     Z_Term *term = zapt->term;
752
753     sizez = term->u.general->len;
754     if (sizez > IT_MAX_WORD)
755         sizez = IT_MAX_WORD;
756     for (i = 0; i < sizez; i++)
757         termz[i] = index_char_cvt (term->u.general->buf[i]);
758     termz[i] = '\0';
759 }
760
761 static RSET rpn_search_APT_relevance (ZServerInfo *zi, 
762                                       Z_AttributesPlusTerm *zapt,
763                                       oid_value attributeSet,
764                                       int num_bases, char **basenames)
765 {
766     rset_relevance_parms parms;
767     char termz[IT_MAX_WORD+1];
768     char term_sub[IT_MAX_WORD+1];
769     struct grep_info grep_info;
770     char *p0 = termz, *p1 = NULL;
771     RSET result;
772
773     parms.key_size = sizeof(struct it_key);
774     parms.max_rec = 100;
775     parms.cmp = key_compare;
776     parms.is = zi->wordIsam;
777
778     if (zapt->term->which != Z_Term_general)
779     {
780         zi->errCode = 124;
781         return NULL;
782     }
783     trans_term (zi, zapt, termz);
784     grep_info.isam_p_indx = 0;
785     grep_info.isam_p_size = 0;
786     grep_info.isam_p_buf = NULL;
787     while (1)
788     {
789         if ((p1 = strchr (p0, ' ')))
790         {
791             memcpy (term_sub, p0, p1-p0);
792             term_sub[p1-p0] = '\0';
793         }
794         else
795             strcpy (term_sub, p0);
796         if (trunc_term (zi, zapt, term_sub, attributeSet, &grep_info,
797                         num_bases, basenames))
798             return NULL;
799         if (!p1)
800             break;
801         p0 = p1;
802         while (*++p0 == ' ')
803             ;
804     }
805     parms.isam_positions = grep_info.isam_p_buf;
806     parms.no_isam_positions = grep_info.isam_p_indx;
807     if (grep_info.isam_p_indx > 0)
808         result = rset_create (rset_kind_relevance, &parms);
809     else
810         result = rset_create (rset_kind_null, NULL);
811     xfree (grep_info.isam_p_buf);
812     return result;
813 }
814
815 static RSET rpn_search_APT_word (ZServerInfo *zi,
816                                  Z_AttributesPlusTerm *zapt,
817                                  oid_value attributeSet,
818                                  int num_bases, char **basenames)
819 {
820     rset_isam_parms parms;
821     char termz[IT_MAX_WORD+1];
822     struct grep_info grep_info;
823     RSET result;
824
825     if (zapt->term->which != Z_Term_general)
826     {
827         zi->errCode = 124;
828         return NULL;
829     }
830     trans_term (zi, zapt, termz);
831
832     grep_info.isam_p_indx = 0;
833     grep_info.isam_p_size = 0;
834     grep_info.isam_p_buf = NULL;
835
836     if (trunc_term (zi, zapt, termz, attributeSet, &grep_info,
837                     num_bases, basenames))
838         return NULL;
839     if (grep_info.isam_p_indx < 1)
840         result = rset_create (rset_kind_null, NULL);
841     else if (grep_info.isam_p_indx == 1)
842     {
843         parms.is = zi->wordIsam;
844         parms.pos = *grep_info.isam_p_buf;
845         result = rset_create (rset_kind_isam, &parms);
846     }
847     else
848         result = rset_trunc (zi->wordIsam, grep_info.isam_p_buf,
849                              grep_info.isam_p_indx);
850     xfree (grep_info.isam_p_buf);
851     return result;
852 }
853
854 static RSET rpn_prox (RSET *rset, int rset_no)
855 {
856     int i;
857     RSFD *rsfd;
858     int  *more;
859     struct it_key **buf;
860     RSFD rsfd_result;
861     RSET result;
862     rset_temp_parms parms;
863     
864     rsfd = xmalloc (sizeof(*rsfd)*rset_no);
865     more = xmalloc (sizeof(*more)*rset_no);
866     buf = xmalloc (sizeof(*buf)*rset_no);
867
868     for (i = 0; i<rset_no; i++)
869     {
870         buf[i] = xmalloc (sizeof(**buf));
871         rsfd[i] = rset_open (rset[i], RSETF_READ|RSETF_SORT_SYSNO);
872         if (!(more[i] = rset_read (rset[i], rsfd[i], buf[i])))
873         {
874             while (i >= 0)
875             {
876                 rset_close (rset[i], rsfd[i]);
877                 xfree (buf[i]);
878                 --i;
879             }
880             xfree (rsfd);
881             xfree (more);
882             xfree (buf);
883             return rset_create (rset_kind_null, NULL);
884         }
885     }
886     parms.key_size = sizeof (struct it_key);
887     result = rset_create (rset_kind_temp, &parms);
888     rsfd_result = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
889     
890     while (*more)
891     {
892         for (i = 1; i<rset_no; i++)
893         {
894             int cmp;
895             
896             if (!more[i])
897             {
898                 *more = 0;
899                 break;
900             }
901             cmp = key_compare (buf[i], buf[i-1]);
902             if (cmp > 1)
903             {
904                 more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
905                 break;
906             }
907             else if (cmp == 1)
908             {
909                 if (buf[i-1]->seqno+1 != buf[i]->seqno)
910                 {
911                     more[i-1] = rset_read (rset[i-1], rsfd[i-1], buf[i-1]);
912                     break;
913                 }
914             }
915             else
916             {
917                 more[i] = rset_read (rset[i], rsfd[i], buf[i]);
918                 break;
919             }
920         }
921         if (i == rset_no)
922         {
923             rset_write (result, rsfd_result, buf[0]);
924             more[0] = rset_read (*rset, *rsfd, *buf);
925         }
926     }
927     
928     for (i = 0; i<rset_no; i++)
929     {
930         rset_close (rset[i], rsfd[i]);
931         xfree (buf[i]);
932     }
933     rset_close (result, rsfd_result);
934     xfree (buf);
935     xfree (more);
936     xfree (rsfd);
937     return result;
938 }
939
940 static RSET rpn_search_APT_phrase (ZServerInfo *zi,
941                                    Z_AttributesPlusTerm *zapt,
942                                    oid_value attributeSet,
943                                    int num_bases, char **basenames)
944 {
945     char termz[IT_MAX_WORD+1];
946     char term_sub[IT_MAX_WORD+1];
947     char *p0 = termz, *p1 = NULL;
948     RSET rset[60], result;
949     int i, rset_no = 0;
950     struct grep_info grep_info;
951
952     if (zapt->term->which != Z_Term_general)
953     {
954         zi->errCode = 124;
955         return NULL;
956     }
957     trans_term (zi, zapt, termz);
958
959     grep_info.isam_p_size = 0;
960     grep_info.isam_p_buf = NULL;
961
962     while (1)
963     {
964         if ((p1 = strchr (p0, ' ')))
965         {
966             memcpy (term_sub, p0, p1-p0);
967             term_sub[p1-p0] = '\0';
968         }
969         else
970             strcpy (term_sub, p0);
971
972         grep_info.isam_p_indx = 0;
973         if (trunc_term (zi, zapt, term_sub, attributeSet, &grep_info,
974                         num_bases, basenames))
975             return NULL;
976         if (grep_info.isam_p_indx == 0)
977             rset[rset_no] = rset_create (rset_kind_null, NULL);
978         else if (grep_info.isam_p_indx > 1)
979             rset[rset_no] = rset_trunc (zi->wordIsam,
980                                         grep_info.isam_p_buf,
981                                         grep_info.isam_p_indx);
982         else
983         {
984             rset_isam_parms parms;
985             
986             parms.is = zi->wordIsam;
987             parms.pos = *grep_info.isam_p_buf;
988             rset[rset_no] = rset_create (rset_kind_isam, &parms);
989         }
990         assert (rset[rset_no]);
991         if (++rset_no >= sizeof(rset)/sizeof(*rset))
992             break;
993         if (!p1)
994             break;
995         p0 = p1;
996         while (*++p0 == ' ')
997             ;
998     }
999     xfree (grep_info.isam_p_buf);
1000     if (rset_no == 0)
1001         return rset_create (rset_kind_null, NULL);
1002     else if (rset_no == 1)
1003         return (rset[0]);
1004
1005     result = rpn_prox (rset, rset_no);
1006     for (i = 0; i<rset_no; i++)
1007         rset_delete (rset[i]);
1008     return result;
1009 }
1010
1011 static RSET rpn_search_APT_local (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
1012                                   oid_value attributeSet)
1013 {
1014     RSET result;
1015     RSFD rsfd;
1016     struct it_key key;
1017     rset_temp_parms parms;
1018     char termz[IT_MAX_WORD+1];
1019
1020     if (zapt->term->which != Z_Term_general)
1021     {
1022         zi->errCode = 124;
1023         return NULL;
1024     }
1025     parms.key_size = sizeof (struct it_key);
1026     result = rset_create (rset_kind_temp, &parms);
1027     rsfd = rset_open (result, RSETF_WRITE|RSETF_SORT_SYSNO);
1028
1029     trans_term (zi, zapt, termz);
1030     key.sysno = atoi (termz);
1031     if (key.sysno <= 0)
1032         key.sysno = 1;
1033     rset_write (result, rsfd, &key);
1034     rset_close (result, rsfd);
1035     return result;
1036 }
1037
1038 static RSET rpn_search_APT (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
1039                             oid_value attributeSet,
1040                             int num_bases, char **basenames)
1041 {
1042     AttrType relation;
1043     AttrType structure;
1044     int relation_value, structure_value;
1045
1046     attr_init (&relation, zapt, 2);
1047     attr_init (&structure, zapt, 4);
1048     
1049     relation_value = attr_find (&relation, NULL);
1050     structure_value = attr_find (&structure, NULL);
1051     switch (structure_value)
1052     {
1053     case -1:
1054         if (relation_value == 102) /* relevance relation */
1055             return rpn_search_APT_relevance (zi, zapt, attributeSet,
1056                                              num_bases, basenames);
1057         return rpn_search_APT_phrase (zi, zapt, attributeSet,
1058                                       num_bases, basenames);
1059     case 1: /* phrase */
1060         if (relation_value == 102) /* relevance relation */
1061             return rpn_search_APT_relevance (zi, zapt, attributeSet,
1062                                              num_bases, basenames);
1063         return rpn_search_APT_phrase (zi, zapt, attributeSet,
1064                                       num_bases, basenames);
1065         break;
1066     case 2: /* word */
1067         if (relation_value == 102) /* relevance relation */
1068             return rpn_search_APT_relevance (zi, zapt, attributeSet,
1069                                              num_bases, basenames);
1070         return rpn_search_APT_word (zi, zapt, attributeSet,
1071                                     num_bases, basenames);
1072     case 3: /* key */
1073         break;
1074     case 4: /* year */
1075         break;
1076     case 5: /* date - normalized */
1077         break;
1078     case 6: /* word list */
1079         return rpn_search_APT_relevance (zi, zapt, attributeSet,
1080                                          num_bases, basenames);
1081     case 100: /* date - un-normalized */
1082         break;
1083     case 101: /* name - normalized */
1084         break;
1085     case 102: /* date - un-normalized */
1086         break;
1087     case 103: /* structure */
1088         break;
1089     case 104: /* urx */
1090         break;
1091     case 105: /* free-form-text */
1092         return rpn_search_APT_relevance (zi, zapt, attributeSet,
1093                                          num_bases, basenames);
1094     case 106: /* document-text */
1095         return rpn_search_APT_relevance (zi, zapt, attributeSet,
1096                                          num_bases, basenames);
1097     case 107: /* local-number */
1098         return rpn_search_APT_local (zi, zapt, attributeSet);
1099     case 108: /* string */ 
1100         return rpn_search_APT_word (zi, zapt, attributeSet,
1101                                     num_bases, basenames);
1102     case 109: /* numeric string */
1103         break;
1104     }
1105     zi->errCode = 118;
1106     return NULL;
1107 }
1108
1109 static RSET rpn_search_ref (ZServerInfo *zi, Z_ResultSetId *resultSetId)
1110 {
1111     ZServerSet *s;
1112
1113     if (!(s = resultSetGet (zi, resultSetId)))
1114         return rset_create (rset_kind_null, NULL);
1115     return s->rset;
1116 }
1117
1118 static RSET rpn_search_structure (ZServerInfo *zi, Z_RPNStructure *zs,
1119                                   oid_value attributeSet,
1120                                   int num_bases, char **basenames)
1121 {
1122     RSET r = NULL;
1123     if (zs->which == Z_RPNStructure_complex)
1124     {
1125         rset_bool_parms bool_parms;
1126         int soft = 0;
1127
1128         bool_parms.rset_l = rpn_search_structure (zi, zs->u.complex->s1,
1129                                                   attributeSet,
1130                                                   num_bases, basenames);
1131         if (bool_parms.rset_l == NULL)
1132             return NULL;
1133         if (rset_is_ranked(bool_parms.rset_l))
1134             soft = 1;
1135         bool_parms.rset_r = rpn_search_structure (zi, zs->u.complex->s2,
1136                                                   attributeSet,
1137                                                   num_bases, basenames);
1138         if (bool_parms.rset_r == NULL)
1139         {
1140             rset_delete (bool_parms.rset_l);
1141             return NULL;
1142         }
1143         if (rset_is_ranked(bool_parms.rset_r))
1144             soft = 1;
1145         bool_parms.key_size = sizeof(struct it_key);
1146         bool_parms.cmp = key_compare;
1147
1148         switch (zs->u.complex->operator->which)
1149         {
1150         case Z_Operator_and:
1151             r = rset_create (soft ? rset_kind_sand:rset_kind_and, &bool_parms);
1152             break;
1153         case Z_Operator_or:
1154             r = rset_create (soft ? rset_kind_sor:rset_kind_or, &bool_parms);
1155             break;
1156         case Z_Operator_and_not:
1157             r = rset_create (soft ? rset_kind_snot:rset_kind_not, &bool_parms);
1158             break;
1159         default:
1160             assert (0);
1161         }
1162     }
1163     else if (zs->which == Z_RPNStructure_simple)
1164     {
1165         if (zs->u.simple->which == Z_Operand_APT)
1166         {
1167             logf (LOG_DEBUG, "rpn_search_APT");
1168             r = rpn_search_APT (zi, zs->u.simple->u.attributesPlusTerm,
1169                                 attributeSet, num_bases, basenames);
1170         }
1171         else if (zs->u.simple->which == Z_Operand_resultSetId)
1172         {
1173             logf (LOG_DEBUG, "rpn_search_ref");
1174             r = rpn_search_ref (zi, zs->u.simple->u.resultSetId);
1175         }
1176         else
1177         {
1178             assert (0);
1179         }
1180     }
1181     else
1182     {
1183         assert (0);
1184     }
1185     return r;
1186 }
1187
1188 void count_set_save (RSET *r, int *count)
1189 {
1190     int psysno = 0;
1191     int kno = 0;
1192     struct it_key key;
1193     RSFD rfd, wfd;
1194     RSET w;
1195     rset_temp_parms parms;
1196
1197     logf (LOG_DEBUG, "count_set_save");
1198     *count = 0;
1199     parms.key_size = sizeof(struct it_key);
1200     w = rset_create (rset_kind_temp, &parms);
1201     wfd = rset_open (w, RSETF_WRITE|RSETF_SORT_SYSNO);
1202     rfd = rset_open (*r, RSETF_READ|RSETF_SORT_SYSNO);
1203     while (rset_read (*r, rfd, &key))
1204     {
1205         if (key.sysno != psysno)
1206         {
1207             rset_write (w, wfd, &key);
1208             psysno = key.sysno;
1209             (*count)++;
1210         }
1211         kno++;
1212     }
1213     rset_close (*r, rfd);
1214     rset_delete (*r);
1215     rset_close (w, wfd);
1216     *r = w;
1217     logf (LOG_DEBUG, "%d keys, %d distinct sysnos", kno, *count);
1218 }
1219
1220 static void count_set (RSET r, int *count)
1221 {
1222     int psysno = 0;
1223     int kno = 0;
1224     struct it_key key;
1225     RSFD rfd;
1226
1227     logf (LOG_DEBUG, "count_set");
1228     *count = 0;
1229     rfd = rset_open (r, RSETF_READ|RSETF_SORT_SYSNO);
1230     while (rset_read (r, rfd, &key))
1231     {
1232         if (key.sysno != psysno)
1233         {
1234             psysno = key.sysno;
1235             (*count)++;
1236         }
1237         kno++;
1238     }
1239     rset_close (r, rfd);
1240     logf (LOG_DEBUG, "%d keys, %d distinct sysnos", kno, *count);
1241 }
1242
1243 int rpn_search (ZServerInfo *zi,
1244                 Z_RPNQuery *rpn, int num_bases, char **basenames, 
1245                 const char *setname, int *hits)
1246 {
1247     RSET rset;
1248     oident *attrset;
1249     oid_value attributeSet;
1250
1251     zlog_rpn (rpn);
1252
1253     zi->errCode = 0;
1254     zi->errString = NULL;
1255
1256     attrset = oid_getentbyoid (rpn->attributeSetId);
1257     attributeSet = attrset->value;
1258     rset = rpn_search_structure (zi, rpn->RPNStructure, attributeSet,
1259                                  num_bases, basenames);
1260     if (!rset)
1261         return zi->errCode;
1262     if (rset_is_volatile(rset))
1263         count_set_save(&rset,hits);
1264     else
1265         count_set (rset, hits);
1266     resultSetAdd (zi, setname, 1, rset);
1267     if (zi->errCode)
1268         logf (LOG_DEBUG, "search error: %d", zi->errCode);
1269     return zi->errCode;
1270 }
1271
1272 struct scan_info {
1273     struct scan_entry *list;
1274     ODR odr;
1275     int before, after;
1276     ISAM isam;
1277     char prefix[20];
1278 };
1279
1280 static int scan_handle (Dict_char *name, const char *info, int pos, 
1281                         void *client)
1282 {
1283     int len_prefix, idx;
1284     ISAM_P isam_p;
1285     RSET rset;
1286     struct scan_info *scan_info = client;
1287
1288     rset_isam_parms parms;
1289
1290     len_prefix = strlen(scan_info->prefix);
1291     if (memcmp (name, scan_info->prefix, len_prefix))
1292         return 1;
1293     if (pos > 0)
1294         idx = scan_info->after - pos + scan_info->before;
1295     else
1296         idx = - pos - 1;
1297     scan_info->list[idx].term = odr_malloc (scan_info->odr,
1298                                             strlen(name + len_prefix)+1);
1299     strcpy (scan_info->list[idx].term, name + len_prefix);
1300     assert (*info == sizeof(isam_p));
1301     memcpy (&isam_p, info+1, sizeof(isam_p));
1302     parms.is = scan_info->isam;
1303     parms.pos = isam_p;
1304 #if 1
1305     rset = rset_create (rset_kind_isam, &parms);
1306     count_set (rset, &scan_info->list[idx].occurrences);
1307     rset_delete (rset);
1308 #else
1309     scan_info->list[idx].occurrences = 1;
1310 #endif
1311     logf (LOG_DEBUG, "pos=%3d idx=%3d name=%s", pos, idx, name);
1312     return 0;
1313 }
1314
1315
1316 static int dummy_handle (Dict_char *name, const char *info, void *p)
1317 {
1318     return 0;
1319 }
1320
1321 int rpn_scan (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
1322               int num_bases, char **basenames,
1323               int *position, int *num_entries, struct scan_entry **list,
1324               int *status)
1325 {
1326     int i, j, sizez, max_pos;
1327     int pos = *position;
1328     int num = *num_entries;
1329     int before;
1330     int after;
1331     char termz[IT_MAX_WORD+20];
1332     AttrType use;
1333     int use_value;
1334     Z_Term *term = zapt->term;
1335     struct scan_info scan_info;
1336
1337     logf (LOG_DEBUG, "scan, position = %d, num = %d", pos, num);
1338
1339     if (num_bases != 1)
1340         return 111;
1341     scan_info.before = before = pos-1;
1342     scan_info.after = after = 1+num-pos;
1343     scan_info.odr = zi->odr;
1344
1345     logf (LOG_DEBUG, "scan, before = %d, after = %d", before, after);
1346     
1347     scan_info.isam = zi->wordIsam;
1348     scan_info.list = odr_malloc (zi->odr, (before+after)*
1349                                  sizeof(*scan_info.list));
1350     for (j = 0; j<before+after; j++)
1351         scan_info.list[j].term = NULL;
1352     attr_init (&use, zapt, 1);
1353     use_value = attr_find (&use, NULL);
1354     logf (LOG_DEBUG, "use value %d", use_value);
1355
1356     if (use_value == -1)
1357         use_value = 1016;
1358     i = index_word_prefix (termz, 1, use_value, *basenames);
1359
1360     dict_lookup_grep (zi->wordDict, termz, 0, NULL, &max_pos,
1361                       dummy_handle);
1362     if (max_pos <= strlen(*basenames))
1363     {
1364         zi->errString = *basenames;
1365         return zi->errCode = 109; /* Database unavailable */
1366     }
1367     strcpy (scan_info.prefix, termz);
1368     sizez = term->u.general->len;
1369     if (sizez > IT_MAX_WORD)
1370         sizez = IT_MAX_WORD;
1371     for (j = 0; j<sizez; j++)
1372         termz[j+i] = index_char_cvt (term->u.general->buf[j]);
1373     termz[j+i] = '\0';
1374     
1375     dict_scan (zi->wordDict, termz, &before, &after, &scan_info, scan_handle);
1376
1377     *status = BEND_SCAN_SUCCESS;
1378
1379     for (i = 0; i<scan_info.after; i++)
1380         if (scan_info.list[scan_info.before+scan_info.after-i-1].term)
1381             break;
1382     *num_entries -= i;
1383     if (i)
1384         *status = BEND_SCAN_PARTIAL;
1385
1386     for (i = 0; i<scan_info.before; i++)
1387         if (scan_info.list[i].term)
1388             break;
1389     if (i)
1390         *status = BEND_SCAN_PARTIAL;
1391     *position -= i;
1392     *num_entries -= i;
1393
1394     *list = scan_info.list+i;       /* list is set to first 'real' entry */
1395
1396     if (*num_entries == 0)          /* signal 'unsupported use-attribute' */
1397         zi->errCode = 114;          /* if no entries was found */
1398     logf (LOG_DEBUG, "position = %d, num_entries = %d",
1399           *position, *num_entries);
1400     if (zi->errCode)
1401         logf (LOG_DEBUG, "scan error: %d", zi->errCode);
1402     return 0;
1403 }
1404               
1405
1406