C++ compilation.
[idzebra-moved-to-github.git] / index / kinput.c
1 /*
2  * Copyright (C) 1994-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: kinput.c,v $
7  * Revision 1.34  1999-05-26 07:49:13  adam
8  * C++ compilation.
9  *
10  * Revision 1.33  1999/05/15 14:36:38  adam
11  * Updated dictionary. Implemented "compression" of dictionary.
12  *
13  * Revision 1.32  1999/05/12 13:08:06  adam
14  * First version of ISAMS.
15  *
16  * Revision 1.31  1999/02/02 14:50:56  adam
17  * Updated WIN32 code specific sections. Changed header.
18  *
19  * Revision 1.30  1998/10/28 10:53:57  adam
20  * Added type cast to prevent warning.
21  *
22  * Revision 1.29  1998/06/11 15:41:39  adam
23  * Minor changes.
24  *
25  * Revision 1.28  1998/03/05 08:45:12  adam
26  * New result set model and modular ranking system. Moved towards
27  * descent server API. System information stored as "SGML" records.
28  *
29  * Revision 1.27  1998/02/17 10:32:52  adam
30  * Fixed bug: binary files weren't opened with flag b on NT.
31  *
32  * Revision 1.26  1998/01/29 13:39:13  adam
33  * Compress ISAM is default.
34  *
35  * Revision 1.25  1997/09/17 12:19:14  adam
36  * Zebra version corresponds to YAZ version 1.4.
37  * Changed Zebra server so that it doesn't depend on global common_resource.
38  *
39  * Revision 1.24  1997/09/09 13:38:07  adam
40  * Partial port to WIN95/NT.
41  *
42  * Revision 1.23  1997/09/04 13:57:39  adam
43  * Added O_BINARY for open calls.
44  *
45  * Revision 1.22  1997/02/12 20:39:45  adam
46  * Implemented options -f <n> that limits the log to the first <n>
47  * records.
48  * Changed some log messages also.
49  *
50  * Revision 1.21  1996/11/08 11:10:23  adam
51  * Buffers used during file match got bigger.
52  * Compressed ISAM support everywhere.
53  * Bug fixes regarding masking characters in queries.
54  * Redesigned Regexp-2 queries.
55  *
56  * Revision 1.20  1996/11/01 08:58:41  adam
57  * Interface to isamc system now includes update and delete.
58  *
59  * Revision 1.19  1996/10/29 14:09:46  adam
60  * Use of cisam system - enabled if setting isamc is 1.
61  *
62  * Revision 1.18  1996/06/04 10:18:59  adam
63  * Minor changes - removed include of ctype.h.
64  *
65  * Revision 1.17  1996/05/14  15:47:07  adam
66  * Cleanup of various buffer size entities.
67  *
68  * Revision 1.16  1996/04/09  10:05:20  adam
69  * Bug fix: prev_name buffer possibly too small; allocated in key_file_init.
70  *
71  * Revision 1.15  1996/03/21  14:50:09  adam
72  * File update uses modify-time instead of change-time.
73  *
74  * Revision 1.14  1996/02/07  14:06:37  adam
75  * Better progress report during register merge.
76  * New command: clean - removes temporary shadow files.
77  *
78  * Revision 1.13  1996/02/05  12:30:00  adam
79  * Logging reduced a bit.
80  * The remaining running time is estimated during register merge.
81  *
82  * Revision 1.12  1995/12/06  17:49:19  adam
83  * Uses dict_delete now.
84  *
85  * Revision 1.11  1995/12/06  16:06:43  adam
86  * Better diagnostics. Work on 'real' dictionary deletion.
87  *
88  * Revision 1.10  1995/12/06  12:41:22  adam
89  * New command 'stat' for the index program.
90  * Filenames can be read from stdin by specifying '-'.
91  * Bug fix/enhancement of the transformation from terms to regular
92  * expressons in the search engine.
93  *
94  * Revision 1.9  1995/10/10  12:24:39  adam
95  * Temporary sort files are compressed.
96  *
97  * Revision 1.8  1995/10/04  16:57:19  adam
98  * Key input and merge sort in one pass.
99  *
100  * Revision 1.7  1995/10/02  15:18:52  adam
101  * New member in recRetrieveCtrl: diagnostic.
102  *
103  * Revision 1.6  1995/09/29  15:51:56  adam
104  * First work on multi-way read.
105  *
106  * Revision 1.5  1995/09/29  14:01:43  adam
107  * Bug fixes.
108  *
109  * Revision 1.4  1995/09/28  14:22:57  adam
110  * Sort uses smaller temporary files.
111  *
112  * Revision 1.3  1995/09/06  16:11:17  adam
113  * Option: only one word key per file.
114  *
115  * Revision 1.2  1995/09/04  12:33:42  adam
116  * Various cleanup. YAZ util used instead.
117  *
118  * Revision 1.1  1995/09/04  09:10:37  adam
119  * More work on index add/del/update.
120  * Merge sort implemented.
121  * Initial work on z39 server.
122  *
123  */
124
125 #include <fcntl.h>
126 #ifdef WIN32
127 #include <io.h>
128 #else
129 #include <unistd.h>
130 #endif
131 #include <stdlib.h>
132 #include <string.h>
133 #include <stdio.h>
134 #include <assert.h>
135
136 #include "index.h"
137
138 #define KEY_SIZE (1+sizeof(struct it_key))
139 #define INP_NAME_MAX 768
140 #define INP_BUF_START 60000
141 #define INP_BUF_ADD  400000
142
143 static int no_diffs   = 0;
144 static int no_updates = 0;
145 static int no_deletions = 0;
146 static int no_insertions = 0;
147 static int no_iterations = 0;
148
149 struct key_file {
150     int   no;            /* file no */
151     off_t offset;        /* file offset */
152     unsigned char *buf;  /* buffer block */
153     size_t buf_size;     /* number of read bytes in block */
154     size_t chunk;        /* number of bytes allocated */
155     size_t buf_ptr;      /* current position in buffer */
156     char *prev_name;     /* last word read */
157     int   sysno;         /* last sysno */
158     int   seqno;         /* last seqno */
159     off_t length;        /* length of file */
160                          /* handler invoked in each read */
161     void (*readHandler)(struct key_file *keyp, void *rinfo);
162     void *readInfo;
163 };
164
165 void getFnameTmp (char *fname, int no)
166 {
167     const char *pre;
168     
169     pre = res_get_def (common_resource, "keyTmpDir", ".");
170     sprintf (fname, "%s/key%d.tmp", pre, no);
171 }
172
173 void key_file_chunk_read (struct key_file *f)
174 {
175     int nr = 0, r = 0, fd;
176     char fname[1024];
177     getFnameTmp (fname, f->no);
178     fd = open (fname, O_BINARY|O_RDONLY);
179     if (fd == -1)
180     {
181         logf (LOG_FATAL|LOG_ERRNO, "cannot open %s", fname);
182         exit (1);
183     }
184     if (!f->length)
185     {
186         if ((f->length = lseek (fd, 0L, SEEK_END)) == (off_t) -1)
187         {
188             logf (LOG_FATAL|LOG_ERRNO, "cannot seek %s", fname);
189             exit (1);
190         }
191     }
192     if (lseek (fd, f->offset, SEEK_SET) == -1)
193     {
194         logf (LOG_FATAL|LOG_ERRNO, "cannot seek %s", fname);
195         exit (1);
196     }
197     while (f->chunk - nr > 0)
198     {
199         r = read (fd, f->buf + nr, f->chunk - nr);
200         if (r <= 0)
201             break;
202         nr += r;
203     }
204     if (r == -1)
205     {
206         logf (LOG_FATAL|LOG_ERRNO, "read of %s", fname);
207         exit (1);
208     }
209     f->buf_size = nr;
210     f->buf_ptr = 0;
211     if (f->readHandler)
212         (*f->readHandler)(f, f->readInfo);
213     close (fd);
214 }
215
216 struct key_file *key_file_init (int no, int chunk)
217 {
218     struct key_file *f;
219
220     f = (struct key_file *) xmalloc (sizeof(*f));
221     f->sysno = 0;
222     f->seqno = 0;
223     f->no = no;
224     f->chunk = chunk;
225     f->offset = 0;
226     f->length = 0;
227     f->readHandler = NULL;
228     f->buf = (unsigned char *) xmalloc (f->chunk);
229     f->prev_name = (char *) xmalloc (INP_NAME_MAX);
230     *f->prev_name = '\0';
231     key_file_chunk_read (f);
232     return f;
233 }
234
235 int key_file_getc (struct key_file *f)
236 {
237     if (f->buf_ptr < f->buf_size)
238         return f->buf[(f->buf_ptr)++];
239     if (f->buf_size < f->chunk)
240         return EOF;
241     f->offset += f->buf_size;
242     key_file_chunk_read (f);
243     if (f->buf_ptr < f->buf_size)
244         return f->buf[(f->buf_ptr)++];
245     else
246         return EOF;
247 }
248
249 int key_file_decode (struct key_file *f)
250 {
251     int c, d;
252
253     c = key_file_getc (f);
254     switch (c & 192) 
255     {
256     case 0:
257         d = c;
258         break;
259     case 64:
260         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
261         break;
262     case 128:
263         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
264         d = (d << 8) + (key_file_getc (f) & 0xff);
265         break;
266     case 192:
267         d = ((c&63) << 8) + (key_file_getc (f) & 0xff);
268         d = (d << 8) + (key_file_getc (f) & 0xff);
269         d = (d << 8) + (key_file_getc (f) & 0xff);
270         break;
271     }
272     return d;
273 }
274
275 int key_file_read (struct key_file *f, char *key)
276 {
277     int i, d, c;
278     struct it_key itkey;
279
280     c = key_file_getc (f);
281     if (c == 0)
282     {
283         strcpy (key, f->prev_name);
284         i = 1+strlen (key);
285     }
286     else if (c == EOF)
287         return 0;
288     else
289     {
290         i = 0;
291         key[i++] = c;
292         while ((key[i++] = key_file_getc (f)))
293             ;
294         strcpy (f->prev_name, key);
295         f->sysno = 0;
296     }
297     d = key_file_decode (f);
298     key[i++] = d & 1;
299     d = d >> 1;
300     itkey.sysno = d + f->sysno;
301     if (d) 
302     {
303         f->sysno = itkey.sysno;
304         f->seqno = 0;
305     }
306     d = key_file_decode (f);
307     itkey.seqno = d + f->seqno;
308     f->seqno = itkey.seqno;
309     memcpy (key + i, &itkey, sizeof(struct it_key));
310     return i + sizeof (struct it_key);
311 }
312
313 struct heap_info {
314     struct {
315         struct key_file **file;
316         char   **buf;
317     } info;
318     int    heapnum;
319     int    *ptr;
320     int    (*cmp)(const void *p1, const void *p2);
321     Dict dict;
322     ISAM isam;
323     ISAMC isamc;
324     ISAMS isams;
325 };
326
327 struct heap_info *key_heap_init (int nkeys,
328                                  int (*cmp)(const void *p1, const void *p2))
329 {
330     struct heap_info *hi;
331     int i;
332
333     hi = (struct heap_info *) xmalloc (sizeof(*hi));
334     hi->info.file = (struct key_file **)
335         xmalloc (sizeof(*hi->info.file) * (1+nkeys));
336     hi->info.buf = (char **) xmalloc (sizeof(*hi->info.buf) * (1+nkeys));
337     hi->heapnum = 0;
338     hi->ptr = (int *) xmalloc (sizeof(*hi->ptr) * (1+nkeys));
339     hi->cmp = cmp;
340     for (i = 0; i<= nkeys; i++)
341     {
342         hi->ptr[i] = i;
343         hi->info.buf[i] = (char *) xmalloc (INP_NAME_MAX);
344     }
345     return hi;
346 }
347
348 static void key_heap_swap (struct heap_info *hi, int i1, int i2)
349 {
350     int swap;
351
352     swap = hi->ptr[i1];
353     hi->ptr[i1] = hi->ptr[i2];
354     hi->ptr[i2] = swap;
355 }
356
357
358 static void key_heap_delete (struct heap_info *hi)
359 {
360     int cur = 1, child = 2;
361
362     assert (hi->heapnum > 0);
363
364     key_heap_swap (hi, 1, hi->heapnum);
365     hi->heapnum--;
366     while (child <= hi->heapnum) {
367         if (child < hi->heapnum &&
368             (*hi->cmp)(&hi->info.buf[hi->ptr[child]],
369                        &hi->info.buf[hi->ptr[child+1]]) > 0)
370             child++;
371         if ((*hi->cmp)(&hi->info.buf[hi->ptr[cur]],
372                        &hi->info.buf[hi->ptr[child]]) > 0)
373         {            
374             key_heap_swap (hi, cur, child);
375             cur = child;
376             child = 2*cur;
377         }
378         else
379             break;
380     }
381 }
382
383 static void key_heap_insert (struct heap_info *hi, const char *buf, int nbytes,
384                              struct key_file *kf)
385 {
386     int cur, parent;
387
388     cur = ++(hi->heapnum);
389     memcpy (hi->info.buf[hi->ptr[cur]], buf, nbytes);
390     hi->info.file[hi->ptr[cur]] = kf;
391
392     parent = cur/2;
393     while (parent && (*hi->cmp)(&hi->info.buf[hi->ptr[parent]],
394                                 &hi->info.buf[hi->ptr[cur]]) > 0)
395     {
396         key_heap_swap (hi, cur, parent);
397         cur = parent;
398         parent = cur/2;
399     }
400 }
401
402 static int heap_read_one (struct heap_info *hi, char *name, char *key)
403 {
404     int n, r;
405     char rbuf[INP_NAME_MAX];
406     struct key_file *kf;
407
408     if (!hi->heapnum)
409         return 0;
410     n = hi->ptr[1];
411     strcpy (name, hi->info.buf[n]);
412     kf = hi->info.file[n];
413     r = strlen(name);
414     memcpy (key, hi->info.buf[n] + r+1, KEY_SIZE);
415     key_heap_delete (hi);
416     if ((r = key_file_read (kf, rbuf)))
417         key_heap_insert (hi, rbuf, r, kf);
418     no_iterations++;
419     return 1;
420 }
421
422 struct heap_cread_info {
423     char prev_name[INP_NAME_MAX];
424     char cur_name[INP_NAME_MAX];
425     char *key;
426     struct heap_info *hi;
427     int mode;
428     int more;
429 };
430       
431 int heap_cread_item (void *vp, char **dst, int *insertMode)
432 {
433     struct heap_cread_info *p = (struct heap_cread_info *) vp;
434     struct heap_info *hi = p->hi;
435
436     if (p->mode == 1)
437     {
438         *insertMode = p->key[0];
439         memcpy (*dst, p->key+1, sizeof(struct it_key));
440         (*dst) += sizeof(struct it_key);
441         p->mode = 2;
442         return 1;
443     }
444     strcpy (p->prev_name, p->cur_name);
445     if (!(p->more = heap_read_one (hi, p->cur_name, p->key)))
446         return 0;
447     if (*p->cur_name && strcmp (p->cur_name, p->prev_name))
448     {
449         p->mode = 1;
450         return 0;
451     }
452     *insertMode = p->key[0];
453     memcpy (*dst, p->key+1, sizeof(struct it_key));
454     (*dst) += sizeof(struct it_key);
455     return 1;
456 }
457
458 int heap_inpc (struct heap_info *hi)
459 {
460     struct heap_cread_info hci;
461     ISAMC_I isamc_i = (ISAMC_I) xmalloc (sizeof(*isamc_i));
462
463     hci.key = (char *) xmalloc (KEY_SIZE);
464     hci.mode = 1;
465     hci.hi = hi;
466     hci.more = heap_read_one (hi, hci.cur_name, hci.key);
467
468     isamc_i->clientData = &hci;
469     isamc_i->read_item = heap_cread_item;
470
471     while (hci.more)
472     {
473         char this_name[INP_NAME_MAX];
474         ISAMC_P isamc_p, isamc_p2;
475         char *dict_info;
476
477         strcpy (this_name, hci.cur_name);
478         assert (hci.cur_name[1]);
479         no_diffs++;
480         if ((dict_info = dict_lookup (hi->dict, hci.cur_name)))
481         {
482             memcpy (&isamc_p, dict_info+1, sizeof(ISAMC_P));
483             isamc_p2 = isc_merge (hi->isamc, isamc_p, isamc_i);
484             if (!isamc_p2)
485             {
486                 no_deletions++;
487                 if (!dict_delete (hi->dict, this_name))
488                     abort();
489             }
490             else 
491             {
492                 no_updates++;
493                 if (isamc_p2 != isamc_p)
494                     dict_insert (hi->dict, this_name,
495                                  sizeof(ISAMC_P), &isamc_p2);
496             }
497         } 
498         else
499         {
500             isamc_p = isc_merge (hi->isamc, 0, isamc_i);
501             no_insertions++;
502             dict_insert (hi->dict, this_name, sizeof(ISAMC_P), &isamc_p);
503         }
504     }
505     xfree (isamc_i);
506     return 0;
507
508
509 int heap_inps (struct heap_info *hi)
510 {
511     struct heap_cread_info hci;
512     ISAMS_I isams_i = (ISAMS_I) xmalloc (sizeof(*isams_i));
513
514     hci.key = (char *) xmalloc (KEY_SIZE);
515     hci.mode = 1;
516     hci.hi = hi;
517     hci.more = heap_read_one (hi, hci.cur_name, hci.key);
518
519     isams_i->clientData = &hci;
520     isams_i->read_item = heap_cread_item;
521
522     while (hci.more)
523     {
524         char this_name[INP_NAME_MAX];
525         ISAMS_P isams_p;
526         char *dict_info;
527
528         strcpy (this_name, hci.cur_name);
529         assert (hci.cur_name[1]);
530         no_diffs++;
531         if (!(dict_info = dict_lookup (hi->dict, hci.cur_name)))
532         {
533             isams_p = isams_merge (hi->isams, isams_i);
534             no_insertions++;
535             dict_insert (hi->dict, this_name, sizeof(ISAMS_P), &isams_p);
536         }
537         else
538             abort();
539     }
540     xfree (isams_i);
541     return 0;
542
543
544
545 int heap_inp (struct heap_info *hi)
546 {
547     char *info;
548     char next_name[INP_NAME_MAX];
549     char cur_name[INP_NAME_MAX];
550     int key_buf_size = INP_BUF_START;
551     int key_buf_ptr;
552     char *next_key;
553     char *key_buf;
554     int more;
555     
556     next_key = (char *) xmalloc (KEY_SIZE);
557     key_buf = (char *) xmalloc (key_buf_size);
558     more = heap_read_one (hi, cur_name, key_buf);
559     while (more)                   /* EOF ? */
560     {
561         int nmemb;
562         key_buf_ptr = KEY_SIZE;
563         while (1)
564         {
565             if (!(more = heap_read_one (hi, next_name, next_key)))
566                 break;
567             if (*next_name && strcmp (next_name, cur_name))
568                 break;
569             memcpy (key_buf + key_buf_ptr, next_key, KEY_SIZE);
570             key_buf_ptr += KEY_SIZE;
571             if (key_buf_ptr+(int) KEY_SIZE >= key_buf_size)
572             {
573                 char *new_key_buf;
574                 new_key_buf = (char *) xmalloc (key_buf_size + INP_BUF_ADD);
575                 memcpy (new_key_buf, key_buf, key_buf_size);
576                 key_buf_size += INP_BUF_ADD;
577                 xfree (key_buf);
578                 key_buf = new_key_buf;
579             }
580         }
581         no_diffs++;
582         nmemb = key_buf_ptr / KEY_SIZE;
583         assert (nmemb * (int) KEY_SIZE == key_buf_ptr);
584         if ((info = dict_lookup (hi->dict, cur_name)))
585         {
586             ISAM_P isam_p, isam_p2;
587             memcpy (&isam_p, info+1, sizeof(ISAM_P));
588             isam_p2 = is_merge (hi->isam, isam_p, nmemb, key_buf);
589             if (!isam_p2)
590             {
591                 no_deletions++;
592                 if (!dict_delete (hi->dict, cur_name))
593                     abort ();
594             }
595             else 
596             {
597                 no_updates++;
598                 if (isam_p2 != isam_p)
599                     dict_insert (hi->dict, cur_name, sizeof(ISAM_P), &isam_p2);
600             }
601         }
602         else
603         {
604             ISAM_P isam_p;
605             no_insertions++;
606             isam_p = is_merge (hi->isam, 0, nmemb, key_buf);
607             dict_insert (hi->dict, cur_name, sizeof(ISAM_P), &isam_p);
608         }
609         memcpy (key_buf, next_key, KEY_SIZE);
610         strcpy (cur_name, next_name);
611     }
612     return 0;
613 }
614
615 struct progressInfo {
616     time_t   startTime;
617     time_t   lastTime;
618     off_t    totalBytes;
619     off_t    totalOffset;
620 };
621
622 void progressFunc (struct key_file *keyp, void *info)
623 {
624     struct progressInfo *p = (struct progressInfo *) info;
625     time_t now, remaining;
626
627     if (keyp->buf_size <= 0 || p->totalBytes <= 0)
628         return ;
629     time (&now);
630
631     if (now >= p->lastTime+10)
632     {
633         p->lastTime = now;
634         remaining = (time_t) ((now - p->startTime)*
635             ((double) p->totalBytes/p->totalOffset - 1.0));
636         if (remaining <= 130)
637             logf (LOG_LOG, "Merge %2.1f%% completed; %ld seconds remaining",
638                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining);
639         else
640             logf (LOG_LOG, "Merge %2.1f%% completed; %ld minutes remaining",
641                  (100.0*p->totalOffset) / p->totalBytes, (long) remaining/60);
642     }
643     p->totalOffset += keyp->buf_size;
644 }
645
646 #ifndef R_OK
647 #define R_OK 4
648 #endif
649
650 void key_input (BFiles bfs, int nkeys, int cache)
651                 
652 {
653     Dict dict;
654     ISAM isam = NULL;
655     ISAMC isamc = NULL;
656     ISAMS isams = NULL;
657     struct key_file **kf;
658     char rbuf[1024];
659     int i, r;
660     struct heap_info *hi;
661     struct progressInfo progressInfo;
662
663     if (nkeys < 0)
664     {
665         char fname[1024];
666         nkeys = 0;
667         while (1)
668         {
669             getFnameTmp (fname, nkeys+1);
670             if (access (fname, R_OK) == -1)
671                 break;
672             nkeys++;
673         }
674         if (!nkeys)
675             return ;
676     }
677     dict = dict_open (bfs, FNAME_DICT, cache, 1, 0);
678     if (!dict)
679     {
680         logf (LOG_FATAL, "dict_open fail");
681         exit (1);
682     }
683     if (res_get_match (common_resource, "isam", "s", NULL))
684     {
685         isams = isams_open (bfs, FNAME_ISAMS, 1,
686                             key_isams_m (common_resource));
687         if (!isams)
688         {
689             logf (LOG_FATAL, "isams_open fail");
690             exit (1);
691         }
692     }
693     else if (res_get_match (common_resource, "isam", "i", NULL))
694     {
695         isam = is_open (bfs, FNAME_ISAM, key_compare, 1,
696                         sizeof(struct it_key), common_resource);
697         if (!isam)
698         {
699             logf (LOG_FATAL, "is_open fail");
700             exit (1);
701         }
702     }
703     else
704     {
705         isamc = isc_open (bfs, FNAME_ISAMC, 1,
706                           key_isamc_m (common_resource));
707         if (!isamc)
708         {
709             logf (LOG_FATAL, "isc_open fail");
710             exit (1);
711         }
712     }
713     kf = (struct key_file **) xmalloc ((1+nkeys) * sizeof(*kf));
714     progressInfo.totalBytes = 0;
715     progressInfo.totalOffset = 0;
716     time (&progressInfo.startTime);
717     time (&progressInfo.lastTime);
718     for (i = 1; i<=nkeys; i++)
719     {
720         kf[i] = key_file_init (i, 32768);
721         kf[i]->readHandler = progressFunc;
722         kf[i]->readInfo = &progressInfo;
723         progressInfo.totalBytes += kf[i]->length;
724         progressInfo.totalOffset += kf[i]->buf_size;
725     }
726     hi = key_heap_init (nkeys, key_qsort_compare);
727     hi->dict = dict;
728     hi->isam = isam;
729     hi->isamc = isamc;
730     hi->isams = isams;
731
732     for (i = 1; i<=nkeys; i++)
733         if ((r = key_file_read (kf[i], rbuf)))
734             key_heap_insert (hi, rbuf, r, kf[i]);
735     if (isamc)
736         heap_inpc (hi);
737     else if (isams)
738         heap_inps (hi);
739     else if (isam)
740         heap_inp (hi);
741     dict_close (dict);
742     if (isam)
743         is_close (isam);
744     if (isamc)
745         isc_close (isamc);
746     if (isams)
747         isams_close (isams);
748    
749     for (i = 1; i<=nkeys; i++)
750     {
751         getFnameTmp (rbuf, i);
752         unlink (rbuf);
753     }
754     logf (LOG_LOG, "Iterations . . .%7d", no_iterations);
755     logf (LOG_LOG, "Distinct words .%7d", no_diffs);
756     logf (LOG_LOG, "Updates. . . . .%7d", no_updates);
757     logf (LOG_LOG, "Deletions. . . .%7d", no_deletions);
758     logf (LOG_LOG, "Insertions . . .%7d", no_insertions);
759 }
760
761