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