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