Removed code no longer in use.
[idzebra-moved-to-github.git] / index / kinput.c
1 /* $Id: kinput.c,v 1.88 2008-01-09 14:57:07 adam Exp $
2    Copyright (C) 1995-2007
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 ((c = key_file_getc(f)))
192         {
193             if (i < IT_MAX_WORD)
194                 key[i++] = c;
195         }
196         key[i++] = '\0';
197         strcpy(f->prev_name, key);
198         iscz1_reset(f->decode_handle);
199     }
200     c = key_file_getc(f); /* length +  insert/delete combined */
201     key[i++] = c & 128;
202     c = c & 127;
203     for (j = 0; j < c; j++)
204         srcbuf[j] = key_file_getc(f);
205     dst = key + i;
206     iscz1_decode(f->decode_handle, &dst, &src);
207
208 #if 0
209     /* debugging */
210     if (1)
211     {
212         struct it_key k;
213         memcpy(&k, key+i, sizeof(k));
214         if (!k.mem[1])
215             yaz_log(YLOG_LOG, "00 KEY");
216     }
217 #endif
218     return i + sizeof(struct it_key);
219 }
220
221 struct heap_info {
222     struct {
223         struct key_file **file;
224         char   **buf;
225     } info;
226     int    heapnum;
227     int    *ptr;
228     int    (*cmp)(const void *p1, const void *p2);
229     struct zebra_register *reg;
230     ZebraHandle zh;
231     zint no_diffs;
232     zint no_updates;
233     zint no_deletions;
234     zint no_insertions;
235     zint no_iterations;
236 };
237
238 static struct heap_info *key_heap_malloc(void)
239 {  /* malloc and clear it */
240     struct heap_info *hi;
241     hi = (struct heap_info *) xmalloc(sizeof(*hi));
242     hi->info.file = 0;
243     hi->info.buf = 0;
244     hi->heapnum = 0;
245     hi->ptr = 0;
246     hi->no_diffs = 0;
247     hi->no_diffs = 0;
248     hi->no_updates = 0;
249     hi->no_deletions = 0;
250     hi->no_insertions = 0;
251     hi->no_iterations = 0;
252     return hi;
253 }
254
255 struct heap_info *key_heap_init_file(ZebraHandle zh,
256                                      int nkeys,
257                                      int (*cmp)(const void *p1, const void *p2))
258 {
259     struct heap_info *hi;
260     int i;
261
262     hi = key_heap_malloc();
263     hi->zh = zh;
264     hi->info.file = (struct key_file **)
265         xmalloc(sizeof(*hi->info.file) * (1+nkeys));
266     hi->info.buf = (char **) xmalloc(sizeof(*hi->info.buf) * (1+nkeys));
267     hi->ptr = (int *) xmalloc(sizeof(*hi->ptr) * (1+nkeys));
268     hi->cmp = cmp;
269     for (i = 0; i<= nkeys; i++)
270     {
271         hi->ptr[i] = i;
272         hi->info.buf[i] = (char *) xmalloc(INP_NAME_MAX);
273     }
274     return hi;
275 }
276
277 void key_heap_destroy(struct heap_info *hi, int nkeys)
278 {
279     int i;
280     for (i = 0; i<=nkeys; i++)
281         xfree(hi->info.buf[i]);
282     xfree(hi->info.buf);
283     xfree(hi->ptr);
284     xfree(hi->info.file);
285     xfree(hi);
286 }
287
288 static void key_heap_swap(struct heap_info *hi, int i1, int i2)
289 {
290     int swap;
291
292     swap = hi->ptr[i1];
293     hi->ptr[i1] = hi->ptr[i2];
294     hi->ptr[i2] = swap;
295 }
296
297
298 static void key_heap_delete(struct heap_info *hi)
299 {
300     int cur = 1, child = 2;
301
302     assert(hi->heapnum > 0);
303
304     key_heap_swap(hi, 1, hi->heapnum);
305     hi->heapnum--;
306     while (child <= hi->heapnum) {
307         if (child < hi->heapnum &&
308             (*hi->cmp)(&hi->info.buf[hi->ptr[child]],
309                        &hi->info.buf[hi->ptr[child+1]]) > 0)
310             child++;
311         if ((*hi->cmp)(&hi->info.buf[hi->ptr[cur]],
312                        &hi->info.buf[hi->ptr[child]]) > 0)
313         {            
314             key_heap_swap(hi, cur, child);
315             cur = child;
316             child = 2*cur;
317         }
318         else
319             break;
320     }
321 }
322
323 static void key_heap_insert(struct heap_info *hi, const char *buf, int nbytes,
324                              struct key_file *kf)
325 {
326     int cur, parent;
327
328     cur = ++(hi->heapnum);
329     memcpy(hi->info.buf[hi->ptr[cur]], buf, nbytes);
330     hi->info.file[hi->ptr[cur]] = kf;
331
332     parent = cur/2;
333     while (parent && (*hi->cmp)(&hi->info.buf[hi->ptr[parent]],
334                                 &hi->info.buf[hi->ptr[cur]]) > 0)
335     {
336         key_heap_swap(hi, cur, parent);
337         cur = parent;
338         parent = cur/2;
339     }
340 }
341
342 static int heap_read_one(struct heap_info *hi, char *name, char *key)
343 {
344     int n, r;
345     char rbuf[INP_NAME_MAX];
346     struct key_file *kf;
347
348     if (!hi->heapnum)
349         return 0;
350     n = hi->ptr[1];
351     strcpy(name, hi->info.buf[n]);
352     kf = hi->info.file[n];
353     r = strlen(name);
354     memcpy(key, hi->info.buf[n] + r+1, KEY_SIZE);
355     key_heap_delete(hi);
356     if ((r = key_file_read(kf, rbuf)))
357         key_heap_insert(hi, rbuf, r, kf);
358     hi->no_iterations++;
359     return 1;
360 }
361
362 #define PR_KEY_LOW 0
363 #define PR_KEY_TOP 0
364
365 /* for debugging only */
366 void zebra_log_dict_entry(ZebraHandle zh, const char *s)
367 {
368     char dst[IT_MAX_WORD+1];
369     int ord;
370     int len = key_SU_decode(&ord, (const unsigned char *) s);
371     const char *index_type;
372
373     if (!zh)
374         yaz_log(YLOG_LOG, "ord=%d", ord);
375     else
376     {
377         const char *string_index;
378         const char *db = 0;
379         zebraExplain_lookup_ord(zh->reg->zei,
380                                 ord, &index_type, &db, &string_index);
381
382         zebra_term_untrans(zh, index_type, dst, s + len);
383
384         yaz_log(YLOG_LOG, "ord=%d index_type=%s index=%s term=%s",
385                 ord, index_type, string_index, dst);
386     }
387 }
388
389 struct heap_cread_info {
390     char prev_name[INP_NAME_MAX];
391     char cur_name[INP_NAME_MAX];
392     char *key;
393     char *key_1, *key_2;
394     int mode_1, mode_2;
395     int sz_1, sz_2;
396     struct heap_info *hi;
397     int first_in_list;
398     int more;
399     int ret;
400     int look_level;
401 };
402
403 static int heap_cread_item(void *vp, char **dst, int *insertMode);
404
405 int heap_cread_item2(void *vp, char **dst, int *insertMode)
406 {
407     struct heap_cread_info *p = (struct heap_cread_info *) vp;
408     int level = 0;
409
410     if (p->look_level)
411     {
412         if (p->look_level > 0)
413         {
414             *insertMode = 1;
415             p->look_level--;
416         }
417         else
418         {
419             *insertMode = 0;
420             p->look_level++;
421         }
422         memcpy(*dst, p->key_1, p->sz_1);
423 #if 0
424         yaz_log(YLOG_LOG, "DUP level=%d", p->look_level);
425         pkey(*dst, *insertMode);
426 #endif
427         (*dst) += p->sz_1;
428         return 1;
429     }
430     if (p->ret == 0)    /* lookahead was 0?. Return that in read next round */
431     {
432         p->ret = -1;
433         return 0;
434     }
435     else if (p->ret == -1) /* Must read new item ? */
436     {
437         char *dst_1 = p->key_1;
438         p->ret = heap_cread_item(vp, &dst_1, &p->mode_1);
439         p->sz_1 = dst_1 - p->key_1;
440     }
441     else
442     {        /* lookahead in 2 . Now in 1. */
443         p->sz_1 = p->sz_2;
444         p->mode_1 = p->mode_2;
445         memcpy(p->key_1, p->key_2, p->sz_2);
446     }
447     if (p->mode_1)
448         level = 1;     /* insert */
449     else
450         level = -1;    /* delete */
451     while(1)
452     {
453         char *dst_2 = p->key_2;
454         p->ret = heap_cread_item(vp, &dst_2, &p->mode_2);
455         if (!p->ret)
456         {
457             if (level)
458                 break;
459             p->ret = -1;
460             return 0;
461         }
462         p->sz_2 = dst_2 - p->key_2;
463
464         if (key_compare(p->key_1, p->key_2) == 0)
465         {
466             if (p->mode_2) /* adjust level according to deletes/inserts */
467                 level++;
468             else
469                 level--;
470         }
471         else
472         {
473             if (level)
474                 break;
475             /* all the same. new round .. */
476             p->sz_1 = p->sz_2;
477             p->mode_1 = p->mode_2;
478             memcpy(p->key_1, p->key_2, p->sz_1);
479             if (p->mode_1)
480                 level = 1;     /* insert */
481             else
482                 level = -1;    /* delete */
483         }
484     }
485     /* outcome is insert (1) or delete (0) depending on final level */
486     if (level > 0)
487     {
488         *insertMode = 1;
489         level--;
490     }
491     else
492     {
493         *insertMode = 0;
494         level++;
495     }
496     p->look_level = level;
497     memcpy(*dst, p->key_1, p->sz_1);
498 #if 0
499     pkey(*dst, *insertMode);
500 #endif
501     (*dst) += p->sz_1;
502     return 1;
503 }
504       
505 int heap_cread_item(void *vp, char **dst, int *insertMode)
506 {
507     struct heap_cread_info *p = (struct heap_cread_info *) vp;
508     struct heap_info *hi = p->hi;
509
510     if (p->first_in_list)
511     {
512         *insertMode = p->key[0];
513         memcpy(*dst, p->key+1, sizeof(struct it_key));
514 #if PR_KEY_LOW
515         zebra_log_dict_entry(hi->zh, p->cur_name);
516         pkey(*dst, *insertMode);
517 #endif
518         (*dst) += sizeof(struct it_key);
519         p->first_in_list = 0;
520         return 1;
521     }
522     strcpy(p->prev_name, p->cur_name);
523     if (!(p->more = heap_read_one(hi, p->cur_name, p->key)))
524         return 0;
525     if (*p->cur_name && strcmp(p->cur_name, p->prev_name))
526     {
527         p->first_in_list = 1;
528         return 0;
529     }
530     *insertMode = p->key[0];
531     memcpy(*dst, p->key+1, sizeof(struct it_key));
532 #if PR_KEY_LOW
533     zebra_log_dict_entry(hi->zh, p->cur_name);
534     pkey(*dst, *insertMode);
535 #endif
536     (*dst) += sizeof(struct it_key);
537     return 1;
538 }
539
540 int heap_inpc(struct heap_cread_info *hci, struct heap_info *hi)
541 {
542     ISAMC_I *isamc_i = (ISAMC_I *) xmalloc(sizeof(*isamc_i));
543
544     isamc_i->clientData = hci;
545     isamc_i->read_item = heap_cread_item2;
546
547     while (hci->more)
548     {
549         char this_name[INP_NAME_MAX];
550         ISAM_P isamc_p, isamc_p2;
551         char *dict_info;
552
553         strcpy(this_name, hci->cur_name);
554         assert(hci->cur_name[0]);
555         hi->no_diffs++;
556         if ((dict_info = dict_lookup(hi->reg->dict, hci->cur_name)))
557         {
558             memcpy(&isamc_p, dict_info+1, sizeof(ISAM_P));
559             isamc_p2 = isamc_p;
560             isamc_merge(hi->reg->isamc, &isamc_p2, isamc_i);
561             if (!isamc_p2)
562             {
563                 hi->no_deletions++;
564                 if (!dict_delete(hi->reg->dict, this_name))
565                     abort();
566             }
567             else 
568             {
569                 hi->no_updates++;
570                 if (isamc_p2 != isamc_p)
571                     dict_insert(hi->reg->dict, this_name,
572                                  sizeof(ISAM_P), &isamc_p2);
573             }
574         } 
575         else
576         {
577             isamc_p = 0;
578             isamc_merge(hi->reg->isamc, &isamc_p, isamc_i);
579             hi->no_insertions++;
580             if (isamc_p)
581                 dict_insert(hi->reg->dict, this_name,
582                              sizeof(ISAM_P), &isamc_p);
583         }
584     }
585     xfree(isamc_i);
586     return 0;
587
588
589 int heap_inpb(struct heap_cread_info *hci, struct heap_info *hi)
590 {
591     ISAMC_I *isamc_i = (ISAMC_I *) xmalloc(sizeof(*isamc_i));
592
593     isamc_i->clientData = hci;
594     isamc_i->read_item = heap_cread_item2;
595
596     while (hci->more)
597     {
598         char this_name[INP_NAME_MAX];
599         ISAM_P isamc_p, isamc_p2;
600         char *dict_info;
601
602         strcpy(this_name, hci->cur_name);
603         assert(hci->cur_name[0]);
604         hi->no_diffs++;
605
606 #if 0
607         assert(hi->zh);
608         zebra_log_dict_entry(hi->zh, hci->cur_name);
609 #endif
610         if ((dict_info = dict_lookup(hi->reg->dict, hci->cur_name)))
611         {
612             memcpy(&isamc_p, dict_info+1, sizeof(ISAM_P));
613             isamc_p2 = isamc_p;
614             isamb_merge(hi->reg->isamb, &isamc_p2, isamc_i);
615             if (!isamc_p2)
616             {
617                 hi->no_deletions++;
618                 if (!dict_delete(hi->reg->dict, this_name))
619                     abort();
620             }
621             else 
622             {
623                 hi->no_updates++;
624                 if (isamc_p2 != isamc_p)
625                     dict_insert(hi->reg->dict, this_name,
626                                  sizeof(ISAM_P), &isamc_p2);
627             }
628         } 
629         else
630         {
631             isamc_p = 0;
632             isamb_merge(hi->reg->isamb, &isamc_p, isamc_i);
633             hi->no_insertions++;
634             if (isamc_p)
635                 dict_insert(hi->reg->dict, this_name,
636                              sizeof(ISAM_P), &isamc_p);
637         }
638     }
639     xfree(isamc_i);
640     return 0;
641
642
643 int heap_inps(struct heap_cread_info *hci, struct heap_info *hi)
644 {
645     ISAMS_I isams_i = (ISAMS_I) xmalloc(sizeof(*isams_i));
646
647     isams_i->clientData = hci;
648     isams_i->read_item = heap_cread_item;
649
650     while (hci->more)
651     {
652         char this_name[INP_NAME_MAX];
653         ISAM_P isams_p;
654         char *dict_info;
655
656         strcpy(this_name, hci->cur_name);
657         assert(hci->cur_name[0]);
658         hi->no_diffs++;
659         if (!(dict_info = dict_lookup(hi->reg->dict, hci->cur_name)))
660         {
661             isams_p = isams_merge(hi->reg->isams, isams_i);
662             hi->no_insertions++;
663             dict_insert(hi->reg->dict, this_name, sizeof(ISAM_P), &isams_p);
664         }
665         else
666         {
667             yaz_log(YLOG_FATAL, "isams doesn't support this kind of update");
668             break;
669         }
670     }
671     xfree(isams_i);
672     return 0;
673
674
675 struct progressInfo {
676     time_t   startTime;
677     time_t   lastTime;
678     off_t    totalBytes;
679     off_t    totalOffset;
680 };
681
682 void progressFunc(struct key_file *keyp, void *info)
683 {
684     struct progressInfo *p = (struct progressInfo *) info;
685     time_t now, remaining;
686
687     if (keyp->buf_size <= 0 || p->totalBytes <= 0)
688         return ;
689     time (&now);
690
691     if (now >= p->lastTime+10)
692     {
693         p->lastTime = now;
694         remaining = (time_t) ((now - p->startTime)*
695             ((double) p->totalBytes/p->totalOffset - 1.0));
696         if (remaining <= 130)
697             yaz_log(YLOG_LOG, "Merge %2.1f%% completed; %ld seconds remaining",
698                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining);
699         else
700             yaz_log(YLOG_LOG, "Merge %2.1f%% completed; %ld minutes remaining",
701                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining/60);
702     }
703     p->totalOffset += keyp->buf_size;
704 }
705
706 #ifndef R_OK
707 #define R_OK 4
708 #endif
709
710 void zebra_index_merge(ZebraHandle zh)
711 {
712     struct key_file **kf = 0;
713     char rbuf[1024];
714     int i, r;
715     struct heap_info *hi;
716     struct progressInfo progressInfo;
717     int nkeys = key_block_get_no_files(zh->reg->key_block);
718
719     if (nkeys == 0)
720         return;
721     
722     if (nkeys < 0)
723     {
724         char fname[1024];
725         nkeys = 0;
726         while (1)
727         {
728             extract_get_fname_tmp (zh, fname, nkeys+1);
729             if (access(fname, R_OK) == -1)
730                 break;
731             nkeys++;
732         }
733         if (!nkeys)
734             return ;
735     }
736     kf = (struct key_file **) xmalloc((1+nkeys) * sizeof(*kf));
737     progressInfo.totalBytes = 0;
738     progressInfo.totalOffset = 0;
739     time(&progressInfo.startTime);
740     time(&progressInfo.lastTime);
741     for (i = 1; i<=nkeys; i++)
742     {
743         kf[i] = key_file_init(i, 8192, zh->res);
744         kf[i]->readHandler = progressFunc;
745         kf[i]->readInfo = &progressInfo;
746         progressInfo.totalBytes += kf[i]->length;
747         progressInfo.totalOffset += kf[i]->buf_size;
748     }
749     hi = key_heap_init_file(zh, nkeys, key_qsort_compare);
750     hi->reg = zh->reg;
751     
752     for (i = 1; i<=nkeys; i++)
753         if ((r = key_file_read(kf[i], rbuf)))
754             key_heap_insert(hi, rbuf, r, kf[i]);
755
756     if (1)
757     {
758         struct heap_cread_info hci;
759         
760         hci.key = (char *) xmalloc(KEY_SIZE);
761         hci.key_1 = (char *) xmalloc(KEY_SIZE);
762         hci.key_2 = (char *) xmalloc(KEY_SIZE);
763         hci.ret = -1;
764         hci.first_in_list = 1;
765         hci.hi = hi;
766         hci.look_level = 0;
767         hci.more = heap_read_one(hi, hci.cur_name, hci.key);    
768         
769         if (zh->reg->isams)
770             heap_inps(&hci, hi);
771         if (zh->reg->isamc)
772             heap_inpc(&hci, hi);
773         if (zh->reg->isamb)
774             heap_inpb(&hci, hi);
775         
776         xfree(hci.key);
777         xfree(hci.key_1);
778         xfree(hci.key_2);
779     }
780         
781     for (i = 1; i<=nkeys; i++)
782     {
783         extract_get_fname_tmp (zh, rbuf, i);
784         unlink(rbuf);
785     }
786     for (i = 1; i<=nkeys; i++)
787         key_file_destroy(kf[i]);
788     xfree(kf);
789     if (hi->no_iterations)
790     { /* do not log if nothing happened */
791         yaz_log(YLOG_LOG, "Iterations: isam/dict " 
792                 ZINT_FORMAT "/" ZINT_FORMAT,
793                 hi->no_iterations, hi->no_diffs);
794         yaz_log(YLOG_LOG, "Dict: inserts/updates/deletions: "
795                 ZINT_FORMAT "/" ZINT_FORMAT "/" ZINT_FORMAT,
796                 hi->no_insertions, hi->no_updates, hi->no_deletions);
797     }
798     key_block_destroy(&zh->reg->key_block);
799     key_heap_destroy(hi, nkeys);
800 }
801 /*
802  * Local variables:
803  * c-basic-offset: 4
804  * indent-tabs-mode: nil
805  * End:
806  * vim: shiftwidth=4 tabstop=8 expandtab
807  */
808