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