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