Split it_key stuff into it_key.c. For indexing, sort key chunks in a separate
[idzebra-moved-to-github.git] / index / kinput.c
1 /* $Id: kinput.c,v 1.78 2006-11-21 14:32:38 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22  
23 #include <fcntl.h>
24 #ifdef WIN32
25 #include <io.h>
26 #endif
27 #if HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <assert.h>
34
35 #include "index.h"
36
37 #define KEY_SIZE (1+sizeof(struct it_key))
38 #define INP_NAME_MAX 768
39 #define INP_BUF_START 60000
40 #define INP_BUF_ADD  400000
41
42 struct key_file {
43     int   no;            /* file no */
44     off_t offset;        /* file offset */
45     unsigned char *buf;  /* buffer block */
46     size_t buf_size;     /* number of read bytes in block */
47     size_t chunk;        /* number of bytes allocated */
48     size_t buf_ptr;      /* current position in buffer */
49     char *prev_name;     /* last word read */
50     void *decode_handle;
51     off_t length;        /* length of file */
52                          /* handler invoked in each read */
53     void (*readHandler)(struct key_file *keyp, void *rinfo);
54     void *readInfo;
55     Res res;
56 };
57
58 #if 0
59 static void pkey(const char *b, int mode)
60 {
61     key_logdump_txt(YLOG_LOG, b, mode ? "i" : "d");
62 }
63 #endif
64
65
66 void getFnameTmp (Res res, char *fname, int no)
67 {
68     const char *pre;
69     
70     pre = res_get_def (res, "keyTmpDir", ".");
71     sprintf (fname, "%s/key%d.tmp", pre, no);
72 }
73
74 void extract_get_fname_tmp (ZebraHandle zh, char *fname, int no)
75 {
76     const char *pre;
77     
78     pre = res_get_def (zh->res, "keyTmpDir", ".");
79     sprintf (fname, "%s/key%d.tmp", pre, no);
80 }
81
82 void key_file_chunk_read (struct key_file *f)
83 {
84     int nr = 0, r = 0, fd;
85     char fname[1024];
86     getFnameTmp (f->res, fname, f->no);
87     fd = open (fname, O_BINARY|O_RDONLY);
88
89     f->buf_ptr = 0;
90     f->buf_size = 0;
91     if (fd == -1)
92     {
93         yaz_log(YLOG_WARN|YLOG_ERRNO, "cannot open %s", fname);
94         return ;
95     }
96     if (!f->length)
97     {
98         if ((f->length = lseek (fd, 0L, SEEK_END)) == (off_t) -1)
99         {
100             yaz_log(YLOG_WARN|YLOG_ERRNO, "cannot seek %s", fname);
101             close (fd);
102             return ;
103         }
104     }
105     if (lseek (fd, f->offset, SEEK_SET) == -1)
106     {
107         yaz_log(YLOG_WARN|YLOG_ERRNO, "cannot seek %s", fname);
108         close(fd);
109         return ;
110     }
111     while (f->chunk - nr > 0)
112     {
113         r = read (fd, f->buf + nr, f->chunk - nr);
114         if (r <= 0)
115             break;
116         nr += r;
117     }
118     if (r == -1)
119     {
120         yaz_log(YLOG_WARN|YLOG_ERRNO, "read of %s", fname);
121         close (fd);
122         return;
123     }
124     f->buf_size = nr;
125     if (f->readHandler)
126         (*f->readHandler)(f, f->readInfo);
127     close (fd);
128 }
129
130 void key_file_destroy (struct key_file *f)
131 {
132     iscz1_stop(f->decode_handle);
133     xfree (f->buf);
134     xfree (f->prev_name);
135     xfree (f);
136 }
137
138 struct key_file *key_file_init (int no, int chunk, Res res)
139 {
140     struct key_file *f;
141
142     f = (struct key_file *) xmalloc (sizeof(*f));
143     f->res = res;
144     f->decode_handle = iscz1_start();
145     f->no = no;
146     f->chunk = chunk;
147     f->offset = 0;
148     f->length = 0;
149     f->readHandler = NULL;
150     f->buf = (unsigned char *) xmalloc (f->chunk);
151     f->prev_name = (char *) xmalloc (INP_NAME_MAX);
152     *f->prev_name = '\0';
153     key_file_chunk_read (f);
154     return f;
155 }
156
157 int key_file_getc (struct key_file *f)
158 {
159     if (f->buf_ptr < f->buf_size)
160         return f->buf[(f->buf_ptr)++];
161     if (f->buf_size < f->chunk)
162         return EOF;
163     f->offset += f->buf_size;
164     key_file_chunk_read (f);
165     if (f->buf_ptr < f->buf_size)
166         return f->buf[(f->buf_ptr)++];
167     else
168         return EOF;
169 }
170
171 int key_file_read (struct key_file *f, char *key)
172 {
173     int i, c;
174     char srcbuf[128];
175     const char *src = srcbuf;
176     char *dst;
177     int j;
178
179     c = key_file_getc (f);
180     if (c == 0)
181     {
182         strcpy (key, f->prev_name);
183         i = 1+strlen (key);
184     }
185     else if (c == EOF)
186         return 0;
187     else
188     {
189         i = 0;
190         key[i++] = c;
191         while ((key[i++] = key_file_getc (f)))
192             ;
193         strcpy (f->prev_name, key);
194         iscz1_reset(f->decode_handle);
195     }
196     c = key_file_getc(f); /* length +  insert/delete combined */
197     key[i++] = c & 128;
198     c = c & 127;
199     for (j = 0; j < c; j++)
200         srcbuf[j] = key_file_getc(f);
201     dst = key + i;
202     iscz1_decode(f->decode_handle, &dst, &src);
203
204 #if 0
205     /* debugging */
206     if (1)
207     {
208         struct it_key k;
209         memcpy(&k, key+i, sizeof(k));
210         if (!k.mem[1])
211             yaz_log(YLOG_LOG, "00 KEY");
212     }
213 #endif
214     return i + sizeof(struct it_key);
215 }
216
217 struct heap_info {
218     struct {
219         struct key_file **file;
220         char   **buf;
221     } info;
222     int    heapnum;
223     int    *ptr;
224     int    (*cmp)(const void *p1, const void *p2);
225     struct zebra_register *reg;
226     ZebraHandle zh;
227     int raw_reading; /* 1=raw /mem read. 0=file reading */
228     int no_diffs;
229     int no_updates;
230     int no_deletions;
231     int no_insertions;
232     int no_iterations;
233 };
234
235 static struct heap_info *key_heap_malloc(void)
236 {  /* malloc and clear it */
237     struct heap_info *hi;
238     hi = (struct heap_info *) xmalloc (sizeof(*hi));
239     hi->info.file = 0;
240     hi->info.buf = 0;
241     hi->heapnum = 0;
242     hi->ptr = 0;
243     hi->raw_reading = 0;
244     hi->no_diffs = 0;
245     hi->no_diffs = 0;
246     hi->no_updates = 0;
247     hi->no_deletions = 0;
248     hi->no_insertions = 0;
249     hi->no_iterations = 0;
250     return hi;
251 }
252
253 struct heap_info *key_heap_init_file(ZebraHandle zh,
254                                      int nkeys,
255                                      int (*cmp)(const void *p1, const void *p2))
256 {
257     struct heap_info *hi;
258     int i;
259
260     hi = key_heap_malloc();
261     hi->zh = zh;
262     hi->info.file = (struct key_file **)
263         xmalloc (sizeof(*hi->info.file) * (1+nkeys));
264     hi->info.buf = (char **) xmalloc (sizeof(*hi->info.buf) * (1+nkeys));
265     hi->ptr = (int *) xmalloc (sizeof(*hi->ptr) * (1+nkeys));
266     hi->cmp = cmp;
267     for (i = 0; i<= nkeys; i++)
268     {
269         hi->ptr[i] = i;
270         hi->info.buf[i] = (char *) xmalloc (INP_NAME_MAX);
271     }
272     return hi;
273 }
274
275 struct heap_info *key_heap_init_raw(ZebraHandle zh,
276                                     int (*cmp)(const void *p1, const void *p2))
277 {
278     struct heap_info *hi=key_heap_malloc();
279     hi->cmp = cmp;
280     hi->zh = zh;
281     hi->raw_reading = 1;
282     return hi;
283 }
284
285 void key_heap_destroy (struct heap_info *hi, int nkeys)
286 {
287     int i;
288     if (!hi->raw_reading)
289         for (i = 0; i<=nkeys; i++)
290             xfree (hi->info.buf[i]);
291     
292     xfree (hi->info.buf);
293     xfree (hi->ptr);
294     xfree (hi->info.file);
295     xfree (hi);
296 }
297
298 static void key_heap_swap (struct heap_info *hi, int i1, int i2)
299 {
300     int swap;
301
302     swap = hi->ptr[i1];
303     hi->ptr[i1] = hi->ptr[i2];
304     hi->ptr[i2] = swap;
305 }
306
307
308 static void key_heap_delete (struct heap_info *hi)
309 {
310     int cur = 1, child = 2;
311
312     assert (hi->heapnum > 0);
313
314     key_heap_swap (hi, 1, hi->heapnum);
315     hi->heapnum--;
316     while (child <= hi->heapnum) {
317         if (child < hi->heapnum &&
318             (*hi->cmp)(&hi->info.buf[hi->ptr[child]],
319                        &hi->info.buf[hi->ptr[child+1]]) > 0)
320             child++;
321         if ((*hi->cmp)(&hi->info.buf[hi->ptr[cur]],
322                        &hi->info.buf[hi->ptr[child]]) > 0)
323         {            
324             key_heap_swap (hi, cur, child);
325             cur = child;
326             child = 2*cur;
327         }
328         else
329             break;
330     }
331 }
332
333 static void key_heap_insert (struct heap_info *hi, const char *buf, int nbytes,
334                              struct key_file *kf)
335 {
336     int cur, parent;
337
338     cur = ++(hi->heapnum);
339     memcpy (hi->info.buf[hi->ptr[cur]], buf, nbytes);
340     hi->info.file[hi->ptr[cur]] = kf;
341
342     parent = cur/2;
343     while (parent && (*hi->cmp)(&hi->info.buf[hi->ptr[parent]],
344                                 &hi->info.buf[hi->ptr[cur]]) > 0)
345     {
346         key_heap_swap (hi, cur, parent);
347         cur = parent;
348         parent = cur/2;
349     }
350 }
351
352 static int heap_read_one (struct heap_info *hi, char *name, char *key)
353 {
354     int n, r;
355     char rbuf[INP_NAME_MAX];
356     struct key_file *kf;
357
358     if (!hi->heapnum)
359         return 0;
360     n = hi->ptr[1];
361     strcpy (name, hi->info.buf[n]);
362     kf = hi->info.file[n];
363     r = strlen(name);
364     memcpy (key, hi->info.buf[n] + r+1, KEY_SIZE);
365     key_heap_delete (hi);
366     if ((r = key_file_read (kf, rbuf)))
367         key_heap_insert (hi, rbuf, r, kf);
368     hi->no_iterations++;
369     return 1;
370 }
371
372 #define PR_KEY_LOW 0
373 #define PR_KEY_TOP 0
374
375 #if 0
376 /* for debugging only */
377 static void print_dict_item(ZebraHandle zh, const char *s)
378 {
379     char dst[IT_MAX_WORD+1];
380     int ord;
381     int len = key_SU_decode(&ord, (const unsigned char *) s);
382     int index_type;
383     const char *db = 0;
384
385     if (!zh)
386         yaz_log(YLOG_LOG, "ord=%d", ord);
387     else
388     {
389         zebraExplain_lookup_ord (zh->reg->zei,
390                              ord, &index_type, &db, 0, 0, 0);
391
392         zebra_term_untrans(zh, index_type, dst, s + len);
393
394         yaz_log(YLOG_LOG, "ord=%d term=%s", ord, dst);
395     }
396 }
397 #endif
398
399 struct heap_cread_info {
400     char prev_name[INP_NAME_MAX];
401     char cur_name[INP_NAME_MAX];
402     char *key;
403     char *key_1, *key_2;
404     int mode_1, mode_2;
405     int sz_1, sz_2;
406     struct heap_info *hi;
407     int first_in_list;
408     int more;
409     int ret;
410     int look_level;
411 };
412
413 static int heap_cread_item (void *vp, char **dst, int *insertMode);
414
415 int heap_cread_item2(void *vp, char **dst, int *insertMode)
416 {
417     struct heap_cread_info *p = (struct heap_cread_info *) vp;
418     int level = 0;
419
420     if (p->look_level)
421     {
422         if (p->look_level > 0)
423         {
424             *insertMode = 1;
425             p->look_level--;
426         }
427         else
428         {
429             *insertMode = 0;
430             p->look_level++;
431         }
432         memcpy (*dst, p->key_1, p->sz_1);
433 #if 0
434         yaz_log(YLOG_LOG, "DUP level=%d", p->look_level);
435         pkey(*dst, *insertMode);
436 #endif
437         (*dst) += p->sz_1;
438         return 1;
439     }
440     if (p->ret == 0)    /* lookahead was 0?. Return that in read next round */
441     {
442         p->ret = -1;
443         return 0;
444     }
445     else if (p->ret == -1) /* Must read new item ? */
446     {
447         char *dst_1 = p->key_1;
448         p->ret = heap_cread_item(vp, &dst_1, &p->mode_1);
449         p->sz_1 = dst_1 - p->key_1;
450     }
451     else
452     {        /* lookahead in 2 . Now in 1. */
453         p->sz_1 = p->sz_2;
454         p->mode_1 = p->mode_2;
455         memcpy (p->key_1, p->key_2, p->sz_2);
456     }
457     if (p->mode_1)
458         level = 1;     /* insert */
459     else
460         level = -1;    /* delete */
461     while(1)
462     {
463         char *dst_2 = p->key_2;
464         p->ret = heap_cread_item(vp, &dst_2, &p->mode_2);
465         if (!p->ret)
466         {
467             if (level)
468                 break;
469             p->ret = -1;
470             return 0;
471         }
472         p->sz_2 = dst_2 - p->key_2;
473
474         if (key_compare(p->key_1, p->key_2) == 0)
475         {
476             if (p->mode_2) /* adjust level according to deletes/inserts */
477                 level++;
478             else
479                 level--;
480         }
481         else
482         {
483             if (level)
484                 break;
485             /* all the same. new round .. */
486             p->sz_1 = p->sz_2;
487             p->mode_1 = p->mode_2;
488             memcpy (p->key_1, p->key_2, p->sz_1);
489             if (p->mode_1)
490                 level = 1;     /* insert */
491             else
492                 level = -1;    /* delete */
493         }
494     }
495     /* outcome is insert (1) or delete (0) depending on final level */
496     if (level > 0)
497     {
498         *insertMode = 1;
499         level--;
500     }
501     else
502     {
503         *insertMode = 0;
504         level++;
505     }
506     p->look_level = level;
507     memcpy (*dst, p->key_1, p->sz_1);
508 #if 0
509     pkey(*dst, *insertMode);
510 #endif
511     (*dst) += p->sz_1;
512     return 1;
513 }
514       
515 int heap_cread_item (void *vp, char **dst, int *insertMode)
516 {
517     struct heap_cread_info *p = (struct heap_cread_info *) vp;
518     struct heap_info *hi = p->hi;
519
520     if (p->first_in_list)
521     {
522         *insertMode = p->key[0];
523         memcpy (*dst, p->key+1, sizeof(struct it_key));
524 #if PR_KEY_LOW
525         pkey(*dst, *insertMode);
526 #endif
527         (*dst) += sizeof(struct it_key);
528         p->first_in_list = 0;
529         return 1;
530     }
531     strcpy (p->prev_name, p->cur_name);
532     if (!(p->more = heap_read_one (hi, p->cur_name, p->key)))
533         return 0;
534     if (*p->cur_name && strcmp (p->cur_name, p->prev_name))
535     {
536         p->first_in_list = 1;
537         return 0;
538     }
539     *insertMode = p->key[0];
540     memcpy (*dst, p->key+1, sizeof(struct it_key));
541 #if PR_KEY_LOW
542     pkey(*dst, *insertMode);
543 #endif
544     (*dst) += sizeof(struct it_key);
545     return 1;
546 }
547
548 int heap_inpc (struct heap_cread_info *hci, struct heap_info *hi)
549 {
550     ISAMC_I *isamc_i = (ISAMC_I *) xmalloc (sizeof(*isamc_i));
551
552     isamc_i->clientData = hci;
553     isamc_i->read_item = heap_cread_item2;
554
555     while (hci->more)
556     {
557         char this_name[INP_NAME_MAX];
558         ISAM_P isamc_p, isamc_p2;
559         char *dict_info;
560
561         strcpy (this_name, hci->cur_name);
562         assert (hci->cur_name[0]);
563         hi->no_diffs++;
564         if ((dict_info = dict_lookup (hi->reg->dict, hci->cur_name)))
565         {
566             memcpy (&isamc_p, dict_info+1, sizeof(ISAM_P));
567             isamc_p2 = isamc_p;
568             isamc_merge (hi->reg->isamc, &isamc_p2, isamc_i);
569             if (!isamc_p2)
570             {
571                 hi->no_deletions++;
572                 if (!dict_delete (hi->reg->dict, this_name))
573                     abort();
574             }
575             else 
576             {
577                 hi->no_updates++;
578                 if (isamc_p2 != isamc_p)
579                     dict_insert (hi->reg->dict, this_name,
580                                  sizeof(ISAM_P), &isamc_p2);
581             }
582         } 
583         else
584         {
585             isamc_p = 0;
586             isamc_merge (hi->reg->isamc, &isamc_p, isamc_i);
587             hi->no_insertions++;
588             if (isamc_p)
589                 dict_insert (hi->reg->dict, this_name,
590                              sizeof(ISAM_P), &isamc_p);
591         }
592     }
593     xfree (isamc_i);
594     return 0;
595
596
597 int heap_inp0(struct heap_cread_info *hci, struct heap_info *hi)
598 {
599     while (hci->more)
600     {
601         char this_name[INP_NAME_MAX];
602         char mybuf[1024];
603         char *dst = mybuf;
604         int mode;
605
606         strcpy (this_name, hci->cur_name);
607         assert (hci->cur_name[0]);
608         hi->no_diffs++;
609
610         while (heap_cread_item2(hci, &dst, &mode))
611             ;
612     }
613     return 0;
614
615
616
617 int heap_inpb(struct heap_cread_info *hci, struct heap_info *hi)
618 {
619     ISAMC_I *isamc_i = (ISAMC_I *) xmalloc (sizeof(*isamc_i));
620
621     isamc_i->clientData = hci;
622     isamc_i->read_item = heap_cread_item2;
623
624     while (hci->more)
625     {
626         char this_name[INP_NAME_MAX];
627         ISAM_P isamc_p, isamc_p2;
628         char *dict_info;
629
630         strcpy (this_name, hci->cur_name);
631         assert (hci->cur_name[0]);
632         hi->no_diffs++;
633
634 #if 0
635         assert(hi->zh);
636         print_dict_item(hi->zh, hci->cur_name);
637 #endif
638         if ((dict_info = dict_lookup (hi->reg->dict, hci->cur_name)))
639         {
640             memcpy (&isamc_p, dict_info+1, sizeof(ISAM_P));
641             isamc_p2 = isamc_p;
642             isamb_merge (hi->reg->isamb, &isamc_p2, isamc_i);
643             if (!isamc_p2)
644             {
645                 hi->no_deletions++;
646                 if (!dict_delete (hi->reg->dict, this_name))
647                     abort();
648             }
649             else 
650             {
651                 hi->no_updates++;
652                 if (isamc_p2 != isamc_p)
653                     dict_insert (hi->reg->dict, this_name,
654                                  sizeof(ISAM_P), &isamc_p2);
655             }
656         } 
657         else
658         {
659             isamc_p = 0;
660             isamb_merge (hi->reg->isamb, &isamc_p, isamc_i);
661             hi->no_insertions++;
662             if (isamc_p)
663                 dict_insert (hi->reg->dict, this_name,
664                              sizeof(ISAM_P), &isamc_p);
665         }
666     }
667     xfree(isamc_i);
668     return 0;
669
670
671 int heap_inps (struct heap_cread_info *hci, struct heap_info *hi)
672 {
673     ISAMS_I isams_i = (ISAMS_I) xmalloc (sizeof(*isams_i));
674
675     isams_i->clientData = hci;
676     isams_i->read_item = heap_cread_item;
677
678     while (hci->more)
679     {
680         char this_name[INP_NAME_MAX];
681         ISAM_P isams_p;
682         char *dict_info;
683
684         strcpy (this_name, hci->cur_name);
685         assert (hci->cur_name[0]);
686         hi->no_diffs++;
687         if (!(dict_info = dict_lookup (hi->reg->dict, hci->cur_name)))
688         {
689             isams_p = isams_merge (hi->reg->isams, isams_i);
690             hi->no_insertions++;
691             dict_insert (hi->reg->dict, this_name, sizeof(ISAM_P), &isams_p);
692         }
693         else
694         {
695             yaz_log(YLOG_FATAL, "isams doesn't support this kind of update");
696             break;
697         }
698     }
699     xfree (isams_i);
700     return 0;
701
702
703 struct progressInfo {
704     time_t   startTime;
705     time_t   lastTime;
706     off_t    totalBytes;
707     off_t    totalOffset;
708 };
709
710 void progressFunc (struct key_file *keyp, void *info)
711 {
712     struct progressInfo *p = (struct progressInfo *) info;
713     time_t now, remaining;
714
715     if (keyp->buf_size <= 0 || p->totalBytes <= 0)
716         return ;
717     time (&now);
718
719     if (now >= p->lastTime+10)
720     {
721         p->lastTime = now;
722         remaining = (time_t) ((now - p->startTime)*
723             ((double) p->totalBytes/p->totalOffset - 1.0));
724         if (remaining <= 130)
725             yaz_log(YLOG_LOG, "Merge %2.1f%% completed; %ld seconds remaining",
726                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining);
727         else
728             yaz_log(YLOG_LOG, "Merge %2.1f%% completed; %ld minutes remaining",
729                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining/60);
730     }
731     p->totalOffset += keyp->buf_size;
732 }
733
734 #ifndef R_OK
735 #define R_OK 4
736 #endif
737
738 void zebra_index_merge (ZebraHandle zh)
739 {
740     struct key_file **kf = 0;
741     char rbuf[1024];
742     int i, r;
743     struct heap_info *hi;
744     struct progressInfo progressInfo;
745     int nkeys = key_block_get_no_files(zh->reg->key_block);
746
747     if (nkeys == 0)
748         return;
749     
750     if (nkeys < 0)
751     {
752         char fname[1024];
753         nkeys = 0;
754         while (1)
755         {
756             extract_get_fname_tmp  (zh, fname, nkeys+1);
757             if (access (fname, R_OK) == -1)
758                 break;
759             nkeys++;
760         }
761         if (!nkeys)
762             return ;
763     }
764     kf = (struct key_file **) xmalloc ((1+nkeys) * sizeof(*kf));
765     progressInfo.totalBytes = 0;
766     progressInfo.totalOffset = 0;
767     time (&progressInfo.startTime);
768     time (&progressInfo.lastTime);
769     for (i = 1; i<=nkeys; i++)
770     {
771         kf[i] = key_file_init (i, 8192, zh->res);
772         kf[i]->readHandler = progressFunc;
773         kf[i]->readInfo = &progressInfo;
774         progressInfo.totalBytes += kf[i]->length;
775         progressInfo.totalOffset += kf[i]->buf_size;
776     }
777     hi = key_heap_init_file(zh, nkeys, key_qsort_compare);
778     hi->reg = zh->reg;
779     
780     for (i = 1; i<=nkeys; i++)
781         if ((r = key_file_read (kf[i], rbuf)))
782             key_heap_insert (hi, rbuf, r, kf[i]);
783
784     if (1)
785     {
786         struct heap_cread_info hci;
787         
788         hci.key = (char *) xmalloc (KEY_SIZE);
789         hci.key_1 = (char *) xmalloc (KEY_SIZE);
790         hci.key_2 = (char *) xmalloc (KEY_SIZE);
791         hci.ret = -1;
792         hci.first_in_list = 1;
793         hci.hi = hi;
794         hci.look_level = 0;
795         hci.more = heap_read_one (hi, hci.cur_name, hci.key);    
796         
797         if (zh->reg->isams)
798             heap_inps(&hci, hi);
799         if (zh->reg->isamc)
800             heap_inpc(&hci, hi);
801         if (zh->reg->isamb)
802             heap_inpb(&hci, hi);
803         
804         xfree (hci.key);
805         xfree (hci.key_1);
806         xfree (hci.key_2);
807     }
808         
809     for (i = 1; i<=nkeys; i++)
810     {
811         extract_get_fname_tmp  (zh, rbuf, i);
812         unlink (rbuf);
813     }
814     for (i = 1; i<=nkeys; i++)
815         key_file_destroy (kf[i]);
816     xfree (kf);
817     if (hi->no_iterations)
818     { /* do not log if nothing happened */
819         yaz_log(YLOG_LOG, "Iterations . . .%7d", hi->no_iterations);
820         yaz_log(YLOG_LOG, "Distinct words .%7d", hi->no_diffs);
821         yaz_log(YLOG_LOG, "Updates. . . . .%7d", hi->no_updates);
822         yaz_log(YLOG_LOG, "Deletions. . . .%7d", hi->no_deletions);
823         yaz_log(YLOG_LOG, "Insertions . . .%7d", hi->no_insertions);
824     }
825     key_block_destroy(&zh->reg->key_block);
826     key_heap_destroy(hi, nkeys);
827 }
828 /*
829  * Local variables:
830  * c-basic-offset: 4
831  * indent-tabs-mode: nil
832  * End:
833  * vim: shiftwidth=4 tabstop=8 expandtab
834  */
835