72e8259ce00f3ae3c4f97096a3857b8d89dc880e
[idzebra-moved-to-github.git] / index / recindex.c
1 /*
2  * Copyright (C) 1994-1999, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recindex.c,v $
7  * Revision 1.31  2001-02-26 22:14:59  adam
8  * Updated for BZIP2 1.0.X. Configure script doesn't enable 64 bit LFS
9  * on broken glibc on Redhat 7.0.
10  *
11  * Revision 1.30  2000/07/13 10:14:20  heikki
12  * Removed compiler warnings when making zebra
13  *
14  * Revision 1.29  2000/04/05 09:49:35  adam
15  * On Unix, zebra/z'mbol uses automake.
16  *
17  * Revision 1.28  1999/12/08 22:44:45  adam
18  * Zebra/Z'mbol dependencies added.
19  *
20  * Revision 1.27  1999/10/29 10:02:33  adam
21  * Fixed decompression buffer overflow.
22  *
23  * Revision 1.26  1999/07/06 13:34:57  adam
24  * Fixed bug (introduced by previous commit).
25  *
26  * Revision 1.25  1999/07/06 12:28:04  adam
27  * Updated record index structure. Format includes version ID. Compression
28  * algorithm ID is stored for each record block.
29  *
30  * Revision 1.24  1999/06/25 13:48:02  adam
31  * Updated MSVC project files.
32  * Added BZIP2 record compression (not very well tested).
33  *
34  * Revision 1.23  1999/05/26 07:49:13  adam
35  * C++ compilation.
36  *
37  * Revision 1.22  1999/02/18 12:49:34  adam
38  * Changed file naming scheme for register files as well as record
39  * store/index files.
40  *
41  * Revision 1.21  1999/02/02 14:51:03  adam
42  * Updated WIN32 code specific sections. Changed header.
43  *
44  * Revision 1.20  1998/01/12 15:04:08  adam
45  * The test option (-s) only uses read-lock (and not write lock).
46  *
47  * Revision 1.19  1997/09/17 12:19:16  adam
48  * Zebra version corresponds to YAZ version 1.4.
49  * Changed Zebra server so that it doesn't depend on global common_resource.
50  *
51  * Revision 1.18  1997/07/15 16:28:42  adam
52  * Bug fix: storeData didn't work with files with multiple records.
53  * Bug fix: fixed memory management with records; not really well
54  *  thought through.
55  *
56  * Revision 1.17  1997/02/12 20:39:46  adam
57  * Implemented options -f <n> that limits the log to the first <n>
58  * records.
59  * Changed some log messages also.
60  *
61  * Revision 1.16  1996/06/04 10:19:00  adam
62  * Minor changes - removed include of ctype.h.
63  *
64  * Revision 1.15  1996/05/13  14:23:06  adam
65  * Work on compaction of set/use bytes in dictionary.
66  *
67  * Revision 1.14  1996/02/01  20:48:15  adam
68  * The total size of records are always checked in rec_cache_insert to
69  * reduce memory usage.
70  *
71  * Revision 1.13  1995/12/11  09:12:49  adam
72  * The rec_get function returns NULL if record doesn't exist - will
73  * happen in the server if the result set records have been deleted since
74  * the creation of the set (i.e. the search).
75  * The server saves a result temporarily if it is 'volatile', i.e. the
76  * set is register dependent.
77  *
78  * Revision 1.12  1995/12/07  17:38:47  adam
79  * Work locking mechanisms for concurrent updates/commit.
80  *
81  * Revision 1.11  1995/12/06  13:58:26  adam
82  * Improved flushing of records - all flushes except the last one
83  * don't write the last accessed. Also flush takes place if record
84  * info occupy more than about 256k.
85  *
86  * Revision 1.10  1995/12/06  12:41:24  adam
87  * New command 'stat' for the index program.
88  * Filenames can be read from stdin by specifying '-'.
89  * Bug fix/enhancement of the transformation from terms to regular
90  * expressons in the search engine.
91  *
92  * Revision 1.9  1995/11/30  08:34:33  adam
93  * Started work on commit facility.
94  * Changed a few malloc/free to xmalloc/xfree.
95  *
96  * Revision 1.8  1995/11/28  14:26:21  adam
97  * Bug fix: recordId with constant wasn't right.
98  * Bug fix: recordId dictionary entry wasn't deleted when needed.
99  *
100  * Revision 1.7  1995/11/28  09:09:43  adam
101  * Zebra config renamed.
102  * Use setting 'recordId' to identify record now.
103  * Bug fix in recindex.c: rec_release_blocks was invokeded even
104  * though the blocks were already released.
105  * File traversal properly deletes records when needed.
106  *
107  * Revision 1.6  1995/11/25  10:24:06  adam
108  * More record fields - they are enumerated now.
109  * New options: flagStoreData flagStoreKey.
110  *
111  * Revision 1.5  1995/11/22  17:19:18  adam
112  * Record management uses the bfile system.
113  *
114  * Revision 1.4  1995/11/20  16:59:46  adam
115  * New update method: the 'old' keys are saved for each records.
116  *
117  * Revision 1.3  1995/11/16  15:34:55  adam
118  * Uses new record management system in both indexer and server.
119  *
120  * Revision 1.2  1995/11/15  19:13:08  adam
121  * Work on record management.
122  *
123  * Revision 1.1  1995/11/15  14:46:20  adam
124  * Started work on better record management system.
125  *
126  */
127
128
129 /*
130  *  Format of first block
131  *      next       (4 bytes)
132  *      ref_count  (4 bytes)
133  *      block      (504 bytes)
134  *
135  *  Format of subsequent blocks 
136  *      next  (4 bytes)
137  *      block (508 bytes)
138  *
139  *  Format of each record
140  *      sysno
141  *      (length, data) - pairs
142  *      length = 0 if same as previous
143  */
144 #include <stdio.h>
145 #include <assert.h>
146 #include <string.h>
147
148 #include "recindxp.h"
149
150 #if HAVE_BZLIB_H
151 #include <bzlib.h>
152 #endif
153 static void rec_write_head (Records p)
154 {
155     int r;
156
157     assert (p);
158     assert (p->index_BFile);
159
160     r = bf_write (p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
161     if (r)
162     {
163         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
164         exit (1);
165     }
166 }
167
168 static void rec_tmp_expand (Records p, int size)
169 {
170     if (p->tmp_size < size + 2048 ||
171         p->tmp_size < p->head.block_size[REC_BLOCK_TYPES-1]*2)
172     {
173         xfree (p->tmp_buf);
174         p->tmp_size = size + p->head.block_size[REC_BLOCK_TYPES-1]*2 + 2048;
175         p->tmp_buf = (char *) xmalloc (p->tmp_size);
176     }
177 }
178
179 static int read_indx (Records p, int sysno, void *buf, int itemsize, 
180                       int ignoreError)
181 {
182     int r;
183     int pos = (sysno-1)*itemsize;
184
185     r = bf_read (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
186     if (r != 1 && !ignoreError)
187     {
188         logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
189               p->index_fname, (long) pos);
190         exit (1);
191     }
192     return r;
193 }
194
195 static void write_indx (Records p, int sysno, void *buf, int itemsize)
196 {
197     int pos = (sysno-1)*itemsize;
198
199     bf_write (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
200 }
201
202 static void rec_release_blocks (Records p, int sysno)
203 {
204     struct record_index_entry entry;
205     int freeblock;
206     char block_and_ref[sizeof(short) + sizeof(int)];
207     int dst_type;
208     int first = 1;
209
210     if (read_indx (p, sysno, &entry, sizeof(entry), 1) != 1)
211         return ;
212
213     freeblock = entry.next;
214     assert (freeblock > 0);
215     dst_type = freeblock & 7;
216     assert (dst_type < REC_BLOCK_TYPES);
217     freeblock = freeblock / 8;
218     while (freeblock)
219     {
220         if (bf_read (p->data_BFile[dst_type], freeblock, 0,
221                      sizeof(block_and_ref), block_and_ref) != 1)
222         {
223             logf (LOG_FATAL|LOG_ERRNO, "read in rec_del_single");
224             exit (1);
225         }
226         if (first)
227         {
228             short ref;
229             memcpy (&ref, block_and_ref + sizeof(int), sizeof(ref));
230             --ref;
231             memcpy (block_and_ref + sizeof(int), &ref, sizeof(ref));
232             if (ref)
233             {
234                 if (bf_write (p->data_BFile[dst_type], freeblock, 0,
235                               sizeof(block_and_ref), block_and_ref))
236                 {
237                     logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
238                     exit (1);
239                 }
240                 return;
241             }
242             first = 0;
243         }
244         
245         if (bf_write (p->data_BFile[dst_type], freeblock, 0, sizeof(freeblock),
246                       &p->head.block_free[dst_type]))
247         {
248             logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
249             exit (1);
250         }
251         p->head.block_free[dst_type] = freeblock;
252         memcpy (&freeblock, block_and_ref, sizeof(int));
253
254         p->head.block_used[dst_type]--;
255     }
256     p->head.total_bytes -= entry.size;
257 }
258
259 static void rec_delete_single (Records p, Record rec)
260 {
261     struct record_index_entry entry;
262
263     rec_release_blocks (p, rec->sysno);
264
265     entry.next = p->head.index_free;
266     entry.size = 0;
267     p->head.index_free = rec->sysno;
268     write_indx (p, rec->sysno, &entry, sizeof(entry));
269 }
270
271 static void rec_write_tmp_buf (Records p, int size, int *sysnos)
272 {
273     struct record_index_entry entry;
274     int no_written = 0;
275     char *cptr = p->tmp_buf;
276     int block_prev = -1, block_free;
277     int dst_type = 0;
278     int i;
279
280     for (i = 1; i<REC_BLOCK_TYPES; i++)
281         if (size >= p->head.block_move[i])
282             dst_type = i;
283     while (no_written < size)
284     {
285         block_free = p->head.block_free[dst_type];
286         if (block_free)
287         {
288             if (bf_read (p->data_BFile[dst_type],
289                          block_free, 0, sizeof(*p->head.block_free),
290                          &p->head.block_free[dst_type]) != 1)
291             {
292                 logf (LOG_FATAL|LOG_ERRNO, "read in %s at free block %d",
293                       p->data_fname[dst_type], block_free);
294                 exit (1);
295             }
296         }
297         else
298             block_free = p->head.block_last[dst_type]++;
299         if (block_prev == -1)
300         {
301             entry.next = block_free*8 + dst_type;
302             entry.size = size;
303             p->head.total_bytes += size;
304             while (*sysnos > 0)
305             {
306                 write_indx (p, *sysnos, &entry, sizeof(entry));
307                 sysnos++;
308             }
309         }
310         else
311         {
312             memcpy (cptr, &block_free, sizeof(int));
313             bf_write (p->data_BFile[dst_type], block_prev, 0, 0, cptr);
314             cptr = p->tmp_buf + no_written;
315         }
316         block_prev = block_free;
317         no_written += p->head.block_size[dst_type] - sizeof(int);
318         p->head.block_used[dst_type]++;
319     }
320     assert (block_prev != -1);
321     block_free = 0;
322     memcpy (cptr, &block_free, sizeof(int));
323     bf_write (p->data_BFile[dst_type], block_prev, 0,
324               sizeof(int) + (p->tmp_buf+size) - cptr, cptr);
325 }
326
327 Records rec_open (BFiles bfs, int rw, int compression_method)
328 {
329     Records p;
330     int i, r;
331     int version;
332
333     p = (Records) xmalloc (sizeof(*p));
334     p->compression_method = compression_method;
335     p->rw = rw;
336     p->tmp_size = 1024;
337     p->tmp_buf = (char *) xmalloc (p->tmp_size);
338     p->index_fname = "reci";
339     p->index_BFile = bf_open (bfs, p->index_fname, 128, rw);
340     if (p->index_BFile == NULL)
341     {
342         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
343         exit (1);
344     }
345     r = bf_read (p->index_BFile, 0, 0, 0, p->tmp_buf);
346     switch (r)
347     {
348     case 0:
349         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
350         sprintf (p->head.version, "%3d", REC_VERSION);
351         p->head.index_free = 0;
352         p->head.index_last = 1;
353         p->head.no_records = 0;
354         p->head.total_bytes = 0;
355         for (i = 0; i<REC_BLOCK_TYPES; i++)
356         {
357             p->head.block_free[i] = 0;
358             p->head.block_last[i] = 1;
359             p->head.block_used[i] = 0;
360         }
361         p->head.block_size[0] = 128;
362         p->head.block_move[0] = 0;
363         for (i = 1; i<REC_BLOCK_TYPES; i++)
364         {
365             p->head.block_size[i] = p->head.block_size[i-1] * 4;
366             p->head.block_move[i] = p->head.block_size[i] * 24;
367         }
368         if (rw)
369             rec_write_head (p);
370         break;
371     case 1:
372         memcpy (&p->head, p->tmp_buf, sizeof(p->head));
373         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
374         {
375             logf (LOG_FATAL, "file %s has bad format", p->index_fname);
376             exit (1);
377         }
378         version = atoi (p->head.version);
379         if (version != REC_VERSION)
380         {
381             logf (LOG_FATAL, "file %s is version %d, but version"
382                   " %d is required", p->index_fname, version, REC_VERSION);
383             exit (1);
384         }
385         break;
386     }
387     for (i = 0; i<REC_BLOCK_TYPES; i++)
388     {
389         char str[80];
390         sprintf (str, "recd%c", i + 'A');
391         p->data_fname[i] = (char *) xmalloc (strlen(str)+1);
392         strcpy (p->data_fname[i], str);
393         p->data_BFile[i] = NULL;
394     }
395     for (i = 0; i<REC_BLOCK_TYPES; i++)
396     {
397         if (!(p->data_BFile[i] = bf_open (bfs, p->data_fname[i],
398                                           p->head.block_size[i],
399                                           rw)))
400         {
401             logf (LOG_FATAL|LOG_ERRNO, "bf_open %s", p->data_fname[i]);
402             exit (1);
403         }
404     }
405     p->cache_max = 400;
406     p->cache_cur = 0;
407     p->record_cache = (struct record_cache_entry *)
408         xmalloc (sizeof(*p->record_cache)*p->cache_max);
409     zebra_mutex_init (&p->mutex);
410     return p;
411 }
412
413 static void rec_encode_unsigned (unsigned n, unsigned char *buf, int *len)
414 {
415     (*len) = 0;
416     while (n > 127)
417     {
418         buf[*len] = 128 + (n & 127);
419         n = n >> 7;
420         (*len)++;
421     }
422     buf[*len] = n;
423     (*len)++;
424 }
425
426 static void rec_decode_unsigned(unsigned *np, unsigned char *buf, int *len)
427 {
428     unsigned n = 0;
429     unsigned w = 1;
430     (*len) = 0;
431
432     while (buf[*len] > 127)
433     {
434         n += w*(buf[*len] & 127);
435         w = w << 7;
436         (*len)++;
437     }
438     n += w * buf[*len];
439     (*len)++;
440     *np = n;
441 }
442
443 static void rec_cache_flush_block1 (Records p, Record rec, Record last_rec,
444                                     char **out_buf, int *out_size,
445                                     int *out_offset)
446 {
447     int i;
448     int len;
449
450     for (i = 0; i<REC_NO_INFO; i++)
451     {
452         if (*out_offset + (int) rec->size[i] + 20 > *out_size)
453         {
454             int new_size = *out_offset + rec->size[i] + 65536;
455             char *np = (char *) xmalloc (new_size);
456             if (*out_offset)
457                 memcpy (np, *out_buf, *out_offset);
458             xfree (*out_buf);
459             *out_size = new_size;
460             *out_buf = np;
461         }
462         if (i == 0)
463         {
464             rec_encode_unsigned (rec->sysno, *out_buf + *out_offset, &len);
465             (*out_offset) += len;
466         }
467         if (rec->size[i] == 0)
468         {
469             rec_encode_unsigned (1, *out_buf + *out_offset, &len);
470             (*out_offset) += len;
471         }
472         else if (last_rec && rec->size[i] == last_rec->size[i] &&
473                  !memcmp (rec->info[i], last_rec->info[i], rec->size[i]))
474         {
475             rec_encode_unsigned (0, *out_buf + *out_offset, &len);
476             (*out_offset) += len;
477         }
478         else
479         {
480             rec_encode_unsigned (rec->size[i]+1, *out_buf + *out_offset, &len);
481             (*out_offset) += len;
482             memcpy (*out_buf + *out_offset, rec->info[i], rec->size[i]);
483             (*out_offset) += rec->size[i];
484         }
485     }
486 }
487
488 static void rec_write_multiple (Records p, int saveCount)
489 {
490     int i;
491     short ref_count = 0;
492     char compression_method;
493     Record last_rec = 0;
494     int out_size = 1000;
495     int out_offset = 0;
496     char *out_buf = (char *) xmalloc (out_size);
497     int *sysnos = (int *) xmalloc (sizeof(*sysnos) * (p->cache_cur + 1));
498     int *sysnop = sysnos;
499
500     for (i = 0; i<p->cache_cur - saveCount; i++)
501     {
502         struct record_cache_entry *e = p->record_cache + i;
503         switch (e->flag)
504         {
505         case recordFlagNew:
506             rec_cache_flush_block1 (p, e->rec, last_rec, &out_buf,
507                                     &out_size, &out_offset);
508             *sysnop++ = e->rec->sysno;
509             ref_count++;
510             e->flag = recordFlagNop;
511             last_rec = e->rec;
512             break;
513         case recordFlagWrite:
514             rec_release_blocks (p, e->rec->sysno);
515             rec_cache_flush_block1 (p, e->rec, last_rec, &out_buf,
516                                     &out_size, &out_offset);
517             *sysnop++ = e->rec->sysno;
518             ref_count++;
519             e->flag = recordFlagNop;
520             last_rec = e->rec;
521             break;
522         case recordFlagDelete:
523             rec_delete_single (p, e->rec);
524             e->flag = recordFlagNop;
525             break;
526         default:
527             break;
528         }
529     }
530
531     *sysnop = -1;
532     if (ref_count)
533     {
534         int csize = 0;  /* indicate compression "not performed yet" */
535         compression_method = p->compression_method;
536         switch (compression_method)
537         {
538         case REC_COMPRESS_BZIP2:
539 #if HAVE_BZLIB_H        
540             csize = out_offset + (out_offset >> 6) + 620;
541             rec_tmp_expand (p, csize);
542 #ifdef BZ_CONFIG_ERROR
543             i = BZ2_bzBuffToBuffCompress 
544 #else
545             i = bzBuffToBuffCompress 
546 #endif
547                                      (p->tmp_buf+sizeof(int)+sizeof(short)+
548                                       sizeof(char),
549                                       &csize, out_buf, out_offset, 1, 0, 30);
550             if (i != BZ_OK)
551             {
552                 logf (LOG_WARN, "bzBuffToBuffCompress error code=%d", i);
553                 csize = 0;
554             }
555             logf (LOG_LOG, "compress %4d %5d %5d", ref_count, out_offset,
556                   csize);
557 #endif
558             break;
559         case REC_COMPRESS_NONE:
560             break;
561         }
562         if (!csize)  
563         {
564             /* either no compression or compression not supported ... */
565             csize = out_offset;
566             rec_tmp_expand (p, csize);
567             memcpy (p->tmp_buf + sizeof(int) + sizeof(short) + sizeof(char),
568                     out_buf, out_offset);
569             csize = out_offset;
570             compression_method = REC_COMPRESS_NONE;
571         }
572         memcpy (p->tmp_buf + sizeof(int), &ref_count, sizeof(ref_count));
573         memcpy (p->tmp_buf + sizeof(int)+sizeof(short),
574                 &compression_method, sizeof(compression_method));
575                 
576         /* -------- compression */
577         rec_write_tmp_buf (p, csize + sizeof(short) + sizeof(char), sysnos);
578     }
579     xfree (out_buf);
580     xfree (sysnos);
581 }
582
583 static void rec_cache_flush (Records p, int saveCount)
584 {
585     int i, j;
586
587     if (saveCount >= p->cache_cur)
588         saveCount = 0;
589
590     rec_write_multiple (p, saveCount);
591
592     for (i = 0; i<p->cache_cur - saveCount; i++)
593     {
594         struct record_cache_entry *e = p->record_cache + i;
595         rec_rm (&e->rec);
596     } 
597     /* i still being used ... */
598     for (j = 0; j<saveCount; j++, i++)
599         memcpy (p->record_cache+j, p->record_cache+i,
600                 sizeof(*p->record_cache));
601     p->cache_cur = saveCount;
602 }
603
604 static Record *rec_cache_lookup (Records p, int sysno,
605                                  enum recordCacheFlag flag)
606 {
607     int i;
608     for (i = 0; i<p->cache_cur; i++)
609     {
610         struct record_cache_entry *e = p->record_cache + i;
611         if (e->rec->sysno == sysno)
612         {
613             if (flag != recordFlagNop && e->flag == recordFlagNop)
614                 e->flag = flag;
615             return &e->rec;
616         }
617     }
618     return NULL;
619 }
620
621 static void rec_cache_insert (Records p, Record rec, enum recordCacheFlag flag)
622 {
623     struct record_cache_entry *e;
624
625     if (p->cache_cur == p->cache_max)
626         rec_cache_flush (p, 1);
627     else if (p->cache_cur > 0)
628     {
629         int i, j;
630         int used = 0;
631         for (i = 0; i<p->cache_cur; i++)
632         {
633             Record r = (p->record_cache + i)->rec;
634             for (j = 0; j<REC_NO_INFO; j++)
635                 used += r->size[j];
636         }
637         if (used > 90000)
638             rec_cache_flush (p, 1);
639     }
640     assert (p->cache_cur < p->cache_max);
641
642     e = p->record_cache + (p->cache_cur)++;
643     e->flag = flag;
644     e->rec = rec_cp (rec);
645 }
646
647 void rec_close (Records *pp)
648 {
649     Records p = *pp;
650     int i;
651
652     assert (p);
653
654     zebra_mutex_destroy (&p->mutex);
655     rec_cache_flush (p, 0);
656     xfree (p->record_cache);
657
658     if (p->rw)
659         rec_write_head (p);
660
661     if (p->index_BFile)
662         bf_close (p->index_BFile);
663
664     for (i = 0; i<REC_BLOCK_TYPES; i++)
665     {
666         if (p->data_BFile[i])
667             bf_close (p->data_BFile[i]);
668         xfree (p->data_fname[i]);
669     }
670     xfree (p->tmp_buf);
671     xfree (p);
672     *pp = NULL;
673 }
674
675 static Record rec_get_int (Records p, int sysno)
676 {
677     int i, in_size, r;
678     Record rec, *recp;
679     struct record_index_entry entry;
680     int freeblock, dst_type;
681     char *nptr, *cptr;
682     char *in_buf = 0;
683     char *bz_buf = 0;
684 #if HAVE_BZLIB_H
685     int bz_size;
686 #endif
687     char compression_method;
688
689     assert (sysno > 0);
690     assert (p);
691
692     if ((recp = rec_cache_lookup (p, sysno, recordFlagNop)))
693         return rec_cp (*recp);
694
695     if (read_indx (p, sysno, &entry, sizeof(entry), 1) < 1)
696         return NULL;       /* record is not there! */
697
698     if (!entry.size)
699         return NULL;       /* record is deleted */
700
701     dst_type = entry.next & 7;
702     assert (dst_type < REC_BLOCK_TYPES);
703     freeblock = entry.next / 8;
704
705     assert (freeblock > 0);
706     
707     rec_tmp_expand (p, entry.size);
708
709     cptr = p->tmp_buf;
710     r = bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
711     if (r < 0)
712         return 0;
713     memcpy (&freeblock, cptr, sizeof(freeblock));
714
715     while (freeblock)
716     {
717         int tmp;
718
719         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
720         
721         memcpy (&tmp, cptr, sizeof(tmp));
722         r = bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
723         if (r < 0)
724             return 0;
725         memcpy (&freeblock, cptr, sizeof(freeblock));
726         memcpy (cptr, &tmp, sizeof(tmp));
727     }
728
729     rec = (Record) xmalloc (sizeof(*rec));
730     rec->sysno = sysno;
731     memcpy (&compression_method, p->tmp_buf + sizeof(int) + sizeof(short),
732             sizeof(compression_method));
733     in_buf = p->tmp_buf + sizeof(int) + sizeof(short) + sizeof(char);
734     in_size = entry.size - sizeof(short) - sizeof(char);
735     switch (compression_method)
736     {
737     case REC_COMPRESS_BZIP2:
738 #if HAVE_BZLIB_H
739         bz_size = entry.size * 20 + 100;
740         while (1)
741         {
742             bz_buf = (char *) xmalloc (bz_size);
743 #ifdef BZ_CONFIG_ERROR
744             i = BZ2_bzBuffToBuffDecompress
745 #else
746             i = bzBuffToBuffDecompress
747 #endif
748                  (bz_buf, &bz_size, in_buf, in_size, 0, 0);
749             logf (LOG_LOG, "decompress %5d %5d", in_size, bz_size);
750             if (i == BZ_OK)
751                 break;
752             logf (LOG_LOG, "failed");
753             xfree (bz_buf);
754             bz_size *= 2;
755         }
756         in_buf = bz_buf;
757         in_size = bz_size;
758 #else
759         logf (LOG_FATAL, "cannot decompress record(s) in BZIP2 format");
760         exit (1);
761 #endif
762         break;
763     case REC_COMPRESS_NONE:
764         break;
765     }
766     for (i = 0; i<REC_NO_INFO; i++)
767         rec->info[i] = 0;
768
769     nptr = in_buf;                /* skip ref count */
770     while (nptr < in_buf + in_size)
771     {
772         int this_sysno;
773         int len;
774         rec_decode_unsigned (&this_sysno, nptr, &len);
775         nptr += len;
776
777         for (i = 0; i < REC_NO_INFO; i++)
778         {
779             int this_size;
780             rec_decode_unsigned (&this_size, nptr, &len);
781             nptr += len;
782
783             if (this_size == 0)
784                 continue;
785             rec->size[i] = this_size-1;
786
787             if (rec->size[i])
788             {
789                 rec->info[i] = nptr;
790                 nptr += rec->size[i];
791             }
792             else
793                 rec->info[i] = NULL;
794         }
795         if (this_sysno == sysno)
796             break;
797     }
798     for (i = 0; i<REC_NO_INFO; i++)
799     {
800         if (rec->info[i] && rec->size[i])
801         {
802             char *np = xmalloc (rec->size[i]);
803             memcpy (np, rec->info[i], rec->size[i]);
804             rec->info[i] = np;
805         }
806         else
807         {
808             assert (rec->info[i] == 0);
809             assert (rec->size[i] == 0);
810         }
811     }
812     xfree (bz_buf);
813     rec_cache_insert (p, rec, recordFlagNop);
814     return rec;
815 }
816
817 Record rec_get (Records p, int sysno)
818 {
819     Record rec;
820     zebra_mutex_lock (&p->mutex);
821
822     rec = rec_get_int (p, sysno);
823     zebra_mutex_unlock (&p->mutex);
824     return rec;
825 }
826
827 static Record rec_new_int (Records p)
828 {
829     int sysno, i;
830     Record rec;
831
832     assert (p);
833     rec = (Record) xmalloc (sizeof(*rec));
834     if (1 || p->head.index_free == 0)
835         sysno = (p->head.index_last)++;
836     else
837     {
838         struct record_index_entry entry;
839
840         read_indx (p, p->head.index_free, &entry, sizeof(entry), 0);
841         sysno = p->head.index_free;
842         p->head.index_free = entry.next;
843     }
844 #if ZMBOL
845 #else
846     if (sysno > 100000)
847     {
848         logf (LOG_FATAL, "100,000 record limit reached");
849         exit (1);
850     }
851 #endif
852     (p->head.no_records)++;
853     rec->sysno = sysno;
854     for (i = 0; i < REC_NO_INFO; i++)
855     {
856         rec->info[i] = NULL;
857         rec->size[i] = 0;
858     }
859     rec_cache_insert (p, rec, recordFlagNew);
860     return rec;
861 }
862
863 Record rec_new (Records p)
864 {
865     Record rec;
866     zebra_mutex_lock (&p->mutex);
867
868     rec = rec_new_int (p);
869     zebra_mutex_unlock (&p->mutex);
870     return rec;
871 }
872
873 void rec_del (Records p, Record *recpp)
874 {
875     Record *recp;
876
877     zebra_mutex_lock (&p->mutex);
878     (p->head.no_records)--;
879     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagDelete)))
880     {
881         rec_rm (recp);
882         *recp = *recpp;
883     }
884     else
885     {
886         rec_cache_insert (p, *recpp, recordFlagDelete);
887         rec_rm (recpp);
888     }
889     zebra_mutex_unlock (&p->mutex);
890     *recpp = NULL;
891 }
892
893 void rec_put (Records p, Record *recpp)
894 {
895     Record *recp;
896
897     zebra_mutex_lock (&p->mutex);
898     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagWrite)))
899     {
900         rec_rm (recp);
901         *recp = *recpp;
902     }
903     else
904     {
905         rec_cache_insert (p, *recpp, recordFlagWrite);
906         rec_rm (recpp);
907     }
908     zebra_mutex_unlock (&p->mutex);
909     *recpp = NULL;
910 }
911
912 void rec_rm (Record *recpp)
913 {
914     int i;
915
916     if (!*recpp)
917         return ;
918     for (i = 0; i < REC_NO_INFO; i++)
919         xfree ((*recpp)->info[i]);
920     xfree (*recpp);
921     *recpp = NULL;
922 }
923
924 Record rec_cp (Record rec)
925 {
926     Record n;
927     int i;
928
929     n = (Record) xmalloc (sizeof(*n));
930     n->sysno = rec->sysno;
931     for (i = 0; i < REC_NO_INFO; i++)
932         if (!rec->info[i])
933         {
934             n->info[i] = NULL;
935             n->size[i] = 0;
936         }
937         else
938         {
939             n->size[i] = rec->size[i];
940             n->info[i] = (char *) xmalloc (rec->size[i]);
941             memcpy (n->info[i], rec->info[i], rec->size[i]);
942         }
943     return n;
944 }
945
946
947 char *rec_strdup (const char *s, size_t *len)
948 {
949     char *p;
950
951     if (!s)
952     {
953         *len = 0;
954         return NULL;
955     }
956     *len = strlen(s)+1;
957     p = (char *) xmalloc (*len);
958     strcpy (p, s);
959     return p;
960 }
961