Bug fixes. Handler may abort further scanning.
[idzebra-moved-to-github.git] / index / zrpn.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zrpn.c,v $
7  * Revision 1.21  1995-10-06 13:52:06  adam
8  * Bug fixes. Handler may abort further scanning.
9  *
10  * Revision 1.20  1995/10/06  11:06:33  adam
11  * Scan entries include 'occurrences' now.
12  *
13  * Revision 1.19  1995/10/06  10:43:56  adam
14  * Scan added. 'occurrences' in scan entries not set yet.
15  *
16  * Revision 1.18  1995/10/04  16:57:20  adam
17  * Key input and merge sort in one pass.
18  *
19  * Revision 1.17  1995/10/04  12:55:17  adam
20  * Bug fix in ranked search. Use=Any keys inserted.
21  *
22  * Revision 1.16  1995/10/02  16:24:40  adam
23  * Use attribute actually used in search requests.
24  *
25  * Revision 1.15  1995/10/02  15:18:52  adam
26  * New member in recRetrieveCtrl: diagnostic.
27  *
28  * Revision 1.14  1995/09/28  12:10:32  adam
29  * Bug fixes. Field prefix used in queries.
30  *
31  * Revision 1.13  1995/09/18  14:17:50  adam
32  * Minor changes.
33  *
34  * Revision 1.12  1995/09/15  14:45:21  adam
35  * Retrieve control.
36  * Work on truncation.
37  *
38  * Revision 1.11  1995/09/14  11:53:27  adam
39  * First work on regular expressions/truncations.
40  *
41  * Revision 1.10  1995/09/11  15:23:26  adam
42  * More work on relevance search.
43  *
44  * Revision 1.9  1995/09/11  13:09:35  adam
45  * More work on relevance feedback.
46  *
47  * Revision 1.8  1995/09/08  14:52:27  adam
48  * Minor changes. Dictionary is lower case now.
49  *
50  * Revision 1.7  1995/09/07  13:58:36  adam
51  * New parameter: result-set file descriptor (RSFD) to support multiple
52  * positions within the same result-set.
53  * Boolean operators: and, or, not implemented.
54  * Result-set references.
55  *
56  * Revision 1.6  1995/09/06  16:11:18  adam
57  * Option: only one word key per file.
58  *
59  * Revision 1.5  1995/09/06  10:33:04  adam
60  * More work on present. Some log messages removed.
61  *
62  * Revision 1.4  1995/09/05  15:28:40  adam
63  * More work on search engine.
64  *
65  * Revision 1.3  1995/09/04  15:20:22  adam
66  * Minor changes.
67  *
68  * Revision 1.2  1995/09/04  12:33:43  adam
69  * Various cleanup. YAZ util used instead.
70  *
71  * Revision 1.1  1995/09/04  09:10:40  adam
72  * More work on index add/del/update.
73  * Merge sort implemented.
74  * Initial work on z39 server.
75  *
76  */
77 #include <stdio.h>
78 #include <assert.h>
79 #include <unistd.h>
80
81 #include "zserver.h"
82
83 #include <rsisam.h>
84 #include <rstemp.h>
85 #include <rsnull.h>
86 #include <rsbool.h>
87 #include <rsrel.h>
88
89 /*
90  * attr_print: log attributes
91  */
92 static void attr_print (Z_AttributesPlusTerm *t)
93 {
94     int of, i;
95     for (of = 0; of < t->num_attributes; of++)
96     {
97         Z_AttributeElement *element;
98         element = t->attributeList[of];
99
100         switch (element->which) 
101         {
102         case Z_AttributeValue_numeric:
103             logf (LOG_DEBUG, "attributeType=%d value=%d", 
104                   *element->attributeType,
105                   *element->value.numeric);
106             break;
107         case Z_AttributeValue_complex:
108             logf (LOG_DEBUG, "attributeType=%d complex", 
109                   *element->attributeType);
110             for (i = 0; i<element->value.complex->num_list; i++)
111             {
112                 if (element->value.complex->list[i]->which ==
113                     Z_StringOrNumeric_string)
114                     logf (LOG_DEBUG, "   string: '%s'",
115                           element->value.complex->list[i]->u.string);
116                 else if (element->value.complex->list[i]->which ==
117                          Z_StringOrNumeric_numeric)
118                     logf (LOG_DEBUG, "   numeric: '%d'",
119                           *element->value.complex->list[i]->u.numeric);
120             }
121             break;
122         default:
123             assert (0);
124         }
125     }
126 }
127
128 typedef struct {
129     int type;
130     int major;
131     int minor;
132     Z_AttributesPlusTerm *zapt;
133 } AttrType;
134
135 static int attr_find (AttrType *src)
136 {
137     while (src->major < src->zapt->num_attributes)
138     {
139         Z_AttributeElement *element;
140         element = src->zapt->attributeList[src->major];
141
142         if (src->type == *element->attributeType)
143         {
144             switch (element->which) 
145             {
146             case Z_AttributeValue_numeric:
147                 ++(src->major);
148                 return *element->value.numeric;
149                 break;
150             case Z_AttributeValue_complex:
151                 if (src->minor >= element->value.complex->num_list ||
152                     element->value.complex->list[src->minor]->which !=  
153                     Z_StringOrNumeric_numeric)
154                     break;
155                 ++(src->minor);
156                 return *element->value.complex->list[src->minor-1]->u.numeric;
157             default:
158                 assert (0);
159             }
160         }
161         ++(src->major);
162     }
163     return -1;
164 }
165
166 static void attr_init (AttrType *src, Z_AttributesPlusTerm *zapt,
167                        int type)
168 {
169     src->zapt = zapt;
170     src->type = type;
171     src->major = 0;
172     src->minor = 0;
173 }
174
175 struct trunc_info {
176     int  *indx;
177     char **heap;
178     int  heapnum;
179     int  (*cmp)(const void *p1, const void *p2);
180     int  keysize;
181     char *swapbuf;
182     char *tmpbuf;
183     char *buf;
184 };
185
186 static void heap_swap (struct trunc_info *ti, int i1, int i2)
187 {
188     int swap;
189
190     memcpy (ti->swapbuf, ti->heap[i1], ti->keysize);
191     memcpy (ti->heap[i1], ti->heap[i2], ti->keysize);
192     memcpy (ti->heap[i2], ti->swapbuf, ti->keysize);
193
194     swap = ti->indx[i1];
195     ti->indx[i1] = ti->indx[i2];
196     ti->indx[i2] = swap;
197 }
198
199 static void heap_delete (struct trunc_info *ti)
200 {
201     int cur = 1, child = 2;
202
203     assert (ti->heapnum > 0);
204     memcpy (ti->heap[1], ti->heap[ti->heapnum], ti->keysize);
205     ti->indx[1] = ti->indx[ti->heapnum--];
206     while (child <= ti->heapnum) {
207         if (child < ti->heapnum &&
208             (*ti->cmp)(ti->heap[child], ti->heap[1+child]) > 0)
209             child++;
210         if ((*ti->cmp)(ti->heap[cur], ti->heap[child]) > 0)
211         {
212             heap_swap (ti, cur, child);
213             cur = child;
214             child = 2*cur;
215         }
216         else
217             break;
218     }
219 }
220
221 static void heap_insert (struct trunc_info *ti, const char *buf, int indx)
222 {
223     int cur, parent;
224
225     cur = ++(ti->heapnum);
226     memcpy (ti->heap[cur], buf, ti->keysize);
227     ti->indx[cur] = indx;
228     parent = cur/2;
229     while (parent && (*ti->cmp)(ti->heap[parent], ti->heap[cur]) > 0)
230     {
231         heap_swap (ti, cur, parent);
232         cur = parent;
233         parent = cur/2;
234     }
235 }
236
237 static
238 struct trunc_info *heap_init (int size, int key_size,
239                               int (*cmp)(const void *p1, const void *p2))
240 {
241     struct trunc_info *ti = xmalloc (sizeof(*ti));
242     int i;
243
244     ++size;
245     ti->heapnum = 0;
246     ti->keysize = key_size;
247     ti->cmp = cmp;
248     ti->indx = xmalloc (size * sizeof(*ti->indx));
249     ti->heap = xmalloc (size * sizeof(*ti->heap));
250     ti->swapbuf = xmalloc (ti->keysize);
251     ti->tmpbuf = xmalloc (ti->keysize);
252     ti->buf = xmalloc (size * ti->keysize);
253     for (i = size; --i >= 0; )
254         ti->heap[i] = ti->buf + ti->keysize * i;
255     return ti;
256 }
257
258 static void heap_close (struct trunc_info *ti)
259 {
260     xfree (ti->indx);
261     xfree (ti->heap);
262     xfree (ti->swapbuf);
263     xfree (ti->tmpbuf);
264     xfree (ti);
265 }
266
267 static RSET rset_trunc (ISAM isam, ISAM_P *isam_p, int from, int to,
268                         int merge_chunk)
269 {
270     logf (LOG_DEBUG, "rset_trunc, range=%d-%d", from, to-1);
271     if (to - from > merge_chunk)
272     {
273         return NULL;
274     }
275     else
276     {
277         ISPT *ispt;
278         int i;
279         struct trunc_info *ti;
280         RSET result;
281         RSFD rsfd;
282         rset_temp_parms parms;
283
284         ispt = xmalloc (sizeof(*ispt) * (to-from));
285         parms.key_size = sizeof (struct it_key);
286         result = rset_create (rset_kind_temp, &parms);
287         rsfd = rset_open (result, 1);
288
289         ti = heap_init (to-from, sizeof(struct it_key),
290                         key_compare);
291         for (i = to-from; --i >= 0; )
292         {
293             ispt[i] = is_position (isam, isam_p[from+i]);
294             if (is_readkey (ispt[i], ti->tmpbuf))
295                 heap_insert (ti, ti->tmpbuf, i);
296         }
297         while (ti->heapnum)
298         {
299             int n = ti->indx[1];
300
301             rset_write (result, rsfd, ti->heap[1]);
302             heap_delete (ti);
303             if (is_readkey (ispt[n], ti->tmpbuf))
304                 heap_insert (ti, ti->tmpbuf, n);
305         }
306         for (i = to-from; --i >= 0; )
307             is_pt_free (ispt[i]);
308         rset_close (result, rsfd);
309         heap_close (ti);
310         xfree (ispt);
311         return result;
312     }
313 }
314
315 static ISAM_P *isam_p_buf = NULL;
316 static int isam_p_size = 0;
317 static int isam_p_indx;
318
319 static void add_isam_p (const char *info)
320 {
321     if (isam_p_indx == isam_p_size)
322     {
323         ISAM_P *new_isam_p_buf;
324         
325         isam_p_size = 2*isam_p_size + 100;
326         new_isam_p_buf = xmalloc (sizeof(*new_isam_p_buf) *
327                                   isam_p_size);
328         if (isam_p_buf)
329         {
330             memcpy (new_isam_p_buf, isam_p_buf,
331                     isam_p_indx * sizeof(*isam_p_buf));
332             xfree (isam_p_buf);
333         }
334         isam_p_buf = new_isam_p_buf;
335     }
336     assert (*info == sizeof(*isam_p_buf));
337     memcpy (isam_p_buf + isam_p_indx, info+1, sizeof(*isam_p_buf));
338     isam_p_indx++;
339 }
340
341 static int grep_handle (Dict_char *name, const char *info)
342 {
343     logf (LOG_DEBUG, "dict name: %s", name);
344     add_isam_p (info);
345     return 0;
346 }
347
348 static int trunc_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
349                        const char *term_sub, ISAM_P **isam_ps)
350 {
351     char term_dict[2*IT_MAX_WORD+2];
352     int i, j;
353     const char *info;    
354     AttrType truncation;
355     int truncation_value;
356     AttrType use;
357     int use_value;
358
359     attr_init (&use, zapt, 1);
360     use_value = attr_find (&use);
361     logf (LOG_DEBUG, "use value %d", use_value);
362     attr_init (&truncation, zapt, 5);
363     truncation_value = attr_find (&truncation);
364     logf (LOG_DEBUG, "truncation value %d", truncation_value);
365
366     if (use_value == -1)
367         use_value = 1016;
368     i = index_word_prefix (term_dict, 1, use_value);
369
370     switch (truncation_value)
371     {
372     case -1:         /* not specified */
373     case 100:        /* do not truncate */
374         strcat (term_dict, term_sub);
375         logf (LOG_DEBUG, "dict_lookup: %s", term_dict);
376         if ((info = dict_lookup (zi->wordDict, term_dict)))
377             add_isam_p (info);
378         break;
379     case 1:          /* right truncation */
380         strcat (term_dict, term_sub);
381         strcat (term_dict, ".*");
382         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_handle);
383         break;
384     case 2:          /* left truncation */
385     case 3:          /* left&right truncation */
386         zi->errCode = 120;
387         return -1;
388     case 101:        /* process # in term */
389         for (j = strlen(term_dict), i = 0; term_sub[i] && i < 2; i++)
390             term_dict[j++] = term_sub[i];
391         for (; term_sub[i]; i++)
392             if (term_sub[i] == '#')
393             {
394                 term_dict[j++] = '.';
395                 term_dict[j++] = '*';
396             }
397             else
398                 term_dict[j++] = term_sub[i];
399         term_dict[j] = '\0';
400         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_handle);
401         break;
402     case 102:        /* regular expression */
403         strcat (term_dict, term_sub);
404         dict_lookup_grep (zi->wordDict, term_dict, 0, grep_handle);
405         break;
406     }
407     *isam_ps = isam_p_buf;
408     logf (LOG_DEBUG, "%d positions", isam_p_indx);
409     return 0;
410 }
411
412 static void trans_term (ZServerInfo *zi, Z_AttributesPlusTerm *zapt,
413                         char *termz)
414 {
415     size_t i, sizez;
416     Z_Term *term = zapt->term;
417
418     sizez = term->u.general->len;
419     if (sizez > IT_MAX_WORD)
420         sizez = IT_MAX_WORD;
421     for (i = 0; i < sizez; i++)
422         termz[i] = index_char_cvt (term->u.general->buf[i]);
423     termz[i] = '\0';
424 }
425
426 static RSET rpn_search_APT_relevance (ZServerInfo *zi, 
427                                       Z_AttributesPlusTerm *zapt)
428 {
429     rset_relevance_parms parms;
430     char termz[IT_MAX_WORD+1];
431     char term_sub[IT_MAX_WORD+1];
432     char *p0 = termz, *p1 = NULL;
433     Z_Term *term = zapt->term;
434
435     parms.key_size = sizeof(struct it_key);
436     parms.max_rec = 100;
437     parms.cmp = key_compare;
438     parms.is = zi->wordIsam;
439
440     if (term->which != Z_Term_general)
441     {
442         zi->errCode = 124;
443         return NULL;
444     }
445     trans_term (zi, zapt, termz);
446     isam_p_indx = 0;  /* global, set by trunc_term - see below */
447     while (1)
448     {
449         if ((p1 = strchr (p0, ' ')))
450         {
451             memcpy (term_sub, p0, p1-p0);
452             term_sub[p1-p0] = '\0';
453         }
454         else
455             strcpy (term_sub, p0);
456         if (trunc_term (zi, zapt, term_sub, &parms.isam_positions))
457             return NULL;
458         if (!p1)
459             break;
460         p0 = p1+1;
461     }
462     parms.no_isam_positions = isam_p_indx;
463     if (isam_p_indx > 0)
464         return rset_create (rset_kind_relevance, &parms);
465     else
466         return rset_create (rset_kind_null, NULL);
467 }
468
469 static RSET rpn_search_APT_word (ZServerInfo *zi,
470                                  Z_AttributesPlusTerm *zapt)
471 {
472     ISAM_P *isam_positions;
473     rset_isam_parms parms;
474
475     char termz[IT_MAX_WORD+1];
476     Z_Term *term = zapt->term;
477
478     if (term->which != Z_Term_general)
479     {
480         zi->errCode = 124;
481         return NULL;
482     }
483     trans_term (zi, zapt, termz);
484     isam_p_indx = 0;  /* global, set by trunc_term - see below */
485     if (trunc_term (zi, zapt, termz, &isam_positions))
486         return NULL;
487     if (isam_p_indx < 1)
488         return rset_create (rset_kind_null, NULL);
489     else if (isam_p_indx == 1)
490     {
491         parms.is = zi->wordIsam;
492         parms.pos = *isam_positions;
493         return rset_create (rset_kind_isam, &parms);
494     }
495     else
496         return rset_trunc (zi->wordIsam, isam_positions, 0, isam_p_indx, 400);
497 }
498
499 static RSET rpn_search_APT_phrase (ZServerInfo *zi,
500                                    Z_AttributesPlusTerm *zapt)
501 {
502     ISAM_P *isam_positions;
503     rset_isam_parms parms;
504
505     char termz[IT_MAX_WORD+1];
506     Z_Term *term = zapt->term;
507
508     if (term->which != Z_Term_general)
509     {
510         zi->errCode = 124;
511         return NULL;
512     }
513     trans_term (zi, zapt, termz);
514     isam_p_indx = 0;  /* global, set by trunc_term - see below */
515     if (trunc_term (zi, zapt, termz, &isam_positions))
516         return NULL;
517     if (isam_p_indx != 1)
518         return rset_create (rset_kind_null, NULL);
519     parms.is = zi->wordIsam;
520     parms.pos = *isam_positions;
521     return rset_create (rset_kind_isam, &parms);
522 }
523
524 static RSET rpn_search_APT (ZServerInfo *zi, Z_AttributesPlusTerm *zapt)
525 {
526     AttrType relation;
527     AttrType structure;
528     int relation_value, structure_value;
529
530     attr_init (&relation, zapt, 2);
531     attr_init (&structure, zapt, 4);
532     
533     relation_value = attr_find (&relation);
534     structure_value = attr_find (&structure);
535     switch (structure_value)
536     {
537     case -1:
538         if (relation_value == 102) /* relevance relation */
539             return rpn_search_APT_relevance (zi, zapt);
540         return rpn_search_APT_word (zi, zapt);
541     case 1: /* phrase */
542         if (relation_value == 102) /* relevance relation */
543             return rpn_search_APT_relevance (zi, zapt);
544         return rpn_search_APT_phrase (zi, zapt);
545         break;
546     case 2: /* word */
547         if (relation_value == 102) /* relevance relation */
548             return rpn_search_APT_relevance (zi, zapt);
549         return rpn_search_APT_word (zi, zapt);
550     case 3: /* key */
551         break;
552     case 4: /* year */
553         break;
554     case 5: /* date - normalized */
555         break;
556     case 6: /* word list */
557         return rpn_search_APT_relevance (zi, zapt);
558     case 100: /* date - un-normalized */
559         break;
560     case 101: /* name - normalized */
561         break;
562     case 102: /* date - un-normalized */
563         break;
564     case 103: /* structure */
565         break;
566     case 104: /* urx */
567         break;
568     case 105: /* free-form-text */
569         return rpn_search_APT_relevance (zi, zapt);
570     case 106: /* document-text */
571         return rpn_search_APT_relevance (zi, zapt);
572     case 107: /* local-number */
573         break;
574     case 108: /* string */ 
575         return rpn_search_APT_word (zi, zapt);
576     case 109: /* numeric string */
577         break;
578     }
579     zi->errCode = 118;
580     return NULL;
581 }
582
583 static RSET rpn_search_ref (ZServerInfo *zi, Z_ResultSetId *resultSetId)
584 {
585     ZServerSet *s;
586
587     if (!(s = resultSetGet (zi, resultSetId)))
588         return rset_create (rset_kind_null, NULL);
589     return s->rset;
590 }
591
592 static RSET rpn_search_structure (ZServerInfo *zi, Z_RPNStructure *zs)
593 {
594     RSET r = NULL;
595     if (zs->which == Z_RPNStructure_complex)
596     {
597         rset_bool_parms bool_parms;
598
599         bool_parms.rset_l = rpn_search_structure (zi, zs->u.complex->s1);
600         if (bool_parms.rset_l == NULL)
601             return NULL;
602         bool_parms.rset_r = rpn_search_structure (zi, zs->u.complex->s2);
603         if (bool_parms.rset_r == NULL)
604         {
605             rset_delete (bool_parms.rset_l);
606             return NULL;
607         }
608         bool_parms.key_size = sizeof(struct it_key);
609         bool_parms.cmp = key_compare;
610
611         switch (zs->u.complex->operator->which)
612         {
613         case Z_Operator_and:
614             r = rset_create (rset_kind_and, &bool_parms);
615             break;
616         case Z_Operator_or:
617             r = rset_create (rset_kind_or, &bool_parms);
618             break;
619         case Z_Operator_and_not:
620             r = rset_create (rset_kind_not, &bool_parms);
621             break;
622         default:
623             assert (0);
624         }
625     }
626     else if (zs->which == Z_RPNStructure_simple)
627     {
628         if (zs->u.simple->which == Z_Operand_APT)
629         {
630             logf (LOG_DEBUG, "rpn_search_APT");
631             r = rpn_search_APT (zi, zs->u.simple->u.attributesPlusTerm);
632         }
633         else if (zs->u.simple->which == Z_Operand_resultSetId)
634         {
635             logf (LOG_DEBUG, "rpn_search_ref");
636             r = rpn_search_ref (zi, zs->u.simple->u.resultSetId);
637         }
638         else
639         {
640             assert (0);
641         }
642     }
643     else
644     {
645         assert (0);
646     }
647     return r;
648 }
649
650 static void count_set (RSET r, int *count)
651 {
652     int psysno = 0;
653     struct it_key key;
654     RSFD rfd;
655
656     logf (LOG_DEBUG, "rpn_save_set");
657     *count = 0;
658     rfd = rset_open (r, 0);
659     while (rset_read (r, rfd, &key))
660     {
661         if (key.sysno != psysno)
662         {
663             psysno = key.sysno;
664             (*count)++;
665         }
666     }
667     rset_close (r, rfd);
668     logf (LOG_DEBUG, "%d distinct sysnos", *count);
669 }
670
671 int rpn_search (ZServerInfo *zi,
672                 Z_RPNQuery *rpn, int num_bases, char **basenames, 
673                 const char *setname, int *hits)
674 {
675     RSET rset;
676
677     zi->errCode = 0;
678     zi->errString = NULL;
679     rset = rpn_search_structure (zi, rpn->RPNStructure);
680     if (!rset)
681         return zi->errCode;
682     count_set (rset, hits);
683     resultSetAdd (zi, setname, 1, rset);
684     if (zi->errCode)
685         logf (LOG_DEBUG, "search error: %d", zi->errCode);
686     return zi->errCode;
687 }
688
689 static struct scan_entry *scan_list;
690 static ODR scan_odr;
691 static int scan_before, scan_after;
692 static ISAM scan_isam;
693 static char scan_prefix[20];
694
695 static int scan_handle (Dict_char *name, const char *info, int pos)
696 {
697     int len_prefix, idx;
698     ISAM_P isam_p;
699     RSET rset;
700
701     rset_isam_parms parms;
702
703     len_prefix = strlen(scan_prefix);
704     if (memcmp (name, scan_prefix, len_prefix))
705         return 1;
706     if (pos > 0)
707         idx = scan_after - pos + scan_before;
708     else
709         idx = - pos - 1;
710     scan_list[idx].term = odr_malloc (scan_odr, strlen(name + len_prefix)+1);
711     strcpy (scan_list[idx].term, name + len_prefix);
712     assert (*info == sizeof(isam_p));
713     memcpy (&isam_p, info+1, sizeof(isam_p));
714     parms.is = scan_isam;
715     parms.pos = isam_p;
716 #if 1
717     rset = rset_create (rset_kind_isam, &parms);
718     count_set (rset, &scan_list[idx].occurrences);
719     rset_delete (rset);
720 #else
721     scan_list[idx].occurrences = 1;
722 #endif
723     logf (LOG_DEBUG, "pos=%3d idx=%3d name=%s", pos, idx, name);
724     return 0;
725 }
726
727 int rpn_scan (ZServerInfo *zi, ODR odr, Z_AttributesPlusTerm *zapt,
728               int *position, int *num_entries, struct scan_entry **list)
729 {
730     int i, j, sizez;
731     int pos = *position;
732     int num = *num_entries;
733     int before;
734     int after;
735     char termz[IT_MAX_WORD+20];
736     AttrType use;
737     int use_value;
738     Z_Term *term = zapt->term;
739
740     logf (LOG_DEBUG, "scan, position = %d, num = %d", pos, num);
741     scan_before = before = pos-1;
742     scan_after = after = 1+num-pos;
743     scan_odr = odr;
744
745     logf (LOG_DEBUG, "scan, before = %d, after = %d", before, after);
746     
747     scan_isam = zi->wordIsam;
748     scan_list = *list = odr_malloc (odr, (before+after)*sizeof(**list));
749     for (j = 0; j<before+after; j++)
750         scan_list[j].term = "------";
751     attr_init (&use, zapt, 1);
752     use_value = attr_find (&use);
753     logf (LOG_DEBUG, "use value %d", use_value);
754
755     if (use_value == -1)
756         use_value = 1016;
757     i = index_word_prefix (termz, 1, use_value);
758     strcpy (scan_prefix, termz);
759     sizez = term->u.general->len;
760     if (sizez > IT_MAX_WORD)
761         sizez = IT_MAX_WORD;
762     for (j = 0; j<sizez; j++)
763         termz[j+i] = index_char_cvt (term->u.general->buf[j]);
764     termz[j+i] = '\0';
765     
766     dict_scan (zi->wordDict, termz, &before, &after, scan_handle);
767
768     if (zi->errCode)
769         logf (LOG_DEBUG, "search error: %d", zi->errCode);
770     return 0;
771 }
772