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