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