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