e60cb1a61206ebbc574ff0a5d37bb55472e95c88
[idzebra-moved-to-github.git] / index / recindex.c
1 /* $Id: recindex.c,v 1.34 2002-08-02 19:26:55 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24
25
26 /*
27  *  Format of first block
28  *      next       (4 bytes)
29  *      ref_count  (4 bytes)
30  *      block      (504 bytes)
31  *
32  *  Format of subsequent blocks 
33  *      next  (4 bytes)
34  *      block (508 bytes)
35  *
36  *  Format of each record
37  *      sysno
38  *      (length, data) - pairs
39  *      length = 0 if same as previous
40  */
41 #include <stdio.h>
42 #include <assert.h>
43 #include <string.h>
44
45 #include "recindxp.h"
46
47 #if HAVE_BZLIB_H
48 #include <bzlib.h>
49 #endif
50 static void rec_write_head (Records p)
51 {
52     int r;
53
54     assert (p);
55     assert (p->index_BFile);
56
57     r = bf_write (p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
58     if (r)
59     {
60         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
61         exit (1);
62     }
63 }
64
65 static void rec_tmp_expand (Records p, int size)
66 {
67     if (p->tmp_size < size + 2048 ||
68         p->tmp_size < p->head.block_size[REC_BLOCK_TYPES-1]*2)
69     {
70         xfree (p->tmp_buf);
71         p->tmp_size = size + p->head.block_size[REC_BLOCK_TYPES-1]*2 + 2048;
72         p->tmp_buf = (char *) xmalloc (p->tmp_size);
73     }
74 }
75
76 static int read_indx (Records p, int sysno, void *buf, int itemsize, 
77                       int ignoreError)
78 {
79     int r;
80     int pos = (sysno-1)*itemsize;
81
82     r = bf_read (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
83     if (r != 1 && !ignoreError)
84     {
85         logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
86               p->index_fname, (long) pos);
87         exit (1);
88     }
89     return r;
90 }
91
92 static void write_indx (Records p, int sysno, void *buf, int itemsize)
93 {
94     int pos = (sysno-1)*itemsize;
95
96     bf_write (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
97 }
98
99 static void rec_release_blocks (Records p, int sysno)
100 {
101     struct record_index_entry entry;
102     int freeblock;
103     char block_and_ref[sizeof(short) + sizeof(int)];
104     int dst_type;
105     int first = 1;
106
107     if (read_indx (p, sysno, &entry, sizeof(entry), 1) != 1)
108         return ;
109
110     freeblock = entry.next;
111     assert (freeblock > 0);
112     dst_type = freeblock & 7;
113     assert (dst_type < REC_BLOCK_TYPES);
114     freeblock = freeblock / 8;
115     while (freeblock)
116     {
117         if (bf_read (p->data_BFile[dst_type], freeblock, 0,
118                      sizeof(block_and_ref), block_and_ref) != 1)
119         {
120             logf (LOG_FATAL|LOG_ERRNO, "read in rec_del_single");
121             exit (1);
122         }
123         if (first)
124         {
125             short ref;
126             memcpy (&ref, block_and_ref + sizeof(int), sizeof(ref));
127             --ref;
128             memcpy (block_and_ref + sizeof(int), &ref, sizeof(ref));
129             if (ref)
130             {
131                 if (bf_write (p->data_BFile[dst_type], freeblock, 0,
132                               sizeof(block_and_ref), block_and_ref))
133                 {
134                     logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
135                     exit (1);
136                 }
137                 return;
138             }
139             first = 0;
140         }
141         
142         if (bf_write (p->data_BFile[dst_type], freeblock, 0, sizeof(freeblock),
143                       &p->head.block_free[dst_type]))
144         {
145             logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
146             exit (1);
147         }
148         p->head.block_free[dst_type] = freeblock;
149         memcpy (&freeblock, block_and_ref, sizeof(int));
150
151         p->head.block_used[dst_type]--;
152     }
153     p->head.total_bytes -= entry.size;
154 }
155
156 static void rec_delete_single (Records p, Record rec)
157 {
158     struct record_index_entry entry;
159
160     rec_release_blocks (p, rec->sysno);
161
162     entry.next = p->head.index_free;
163     entry.size = 0;
164     p->head.index_free = rec->sysno;
165     write_indx (p, rec->sysno, &entry, sizeof(entry));
166 }
167
168 static void rec_write_tmp_buf (Records p, int size, int *sysnos)
169 {
170     struct record_index_entry entry;
171     int no_written = 0;
172     char *cptr = p->tmp_buf;
173     int block_prev = -1, block_free;
174     int dst_type = 0;
175     int i;
176
177     for (i = 1; i<REC_BLOCK_TYPES; i++)
178         if (size >= p->head.block_move[i])
179             dst_type = i;
180     while (no_written < size)
181     {
182         block_free = p->head.block_free[dst_type];
183         if (block_free)
184         {
185             if (bf_read (p->data_BFile[dst_type],
186                          block_free, 0, sizeof(*p->head.block_free),
187                          &p->head.block_free[dst_type]) != 1)
188             {
189                 logf (LOG_FATAL|LOG_ERRNO, "read in %s at free block %d",
190                       p->data_fname[dst_type], block_free);
191                 exit (1);
192             }
193         }
194         else
195             block_free = p->head.block_last[dst_type]++;
196         if (block_prev == -1)
197         {
198             entry.next = block_free*8 + dst_type;
199             entry.size = size;
200             p->head.total_bytes += size;
201             while (*sysnos > 0)
202             {
203                 write_indx (p, *sysnos, &entry, sizeof(entry));
204                 sysnos++;
205             }
206         }
207         else
208         {
209             memcpy (cptr, &block_free, sizeof(int));
210             bf_write (p->data_BFile[dst_type], block_prev, 0, 0, cptr);
211             cptr = p->tmp_buf + no_written;
212         }
213         block_prev = block_free;
214         no_written += p->head.block_size[dst_type] - sizeof(int);
215         p->head.block_used[dst_type]++;
216     }
217     assert (block_prev != -1);
218     block_free = 0;
219     memcpy (cptr, &block_free, sizeof(int));
220     bf_write (p->data_BFile[dst_type], block_prev, 0,
221               sizeof(int) + (p->tmp_buf+size) - cptr, cptr);
222 }
223
224 Records rec_open (BFiles bfs, int rw, int compression_method)
225 {
226     Records p;
227     int i, r;
228     int version;
229
230     p = (Records) xmalloc (sizeof(*p));
231     p->compression_method = compression_method;
232     p->rw = rw;
233     p->tmp_size = 1024;
234     p->tmp_buf = (char *) xmalloc (p->tmp_size);
235     p->index_fname = "reci";
236     p->index_BFile = bf_open (bfs, p->index_fname, 128, rw);
237     if (p->index_BFile == NULL)
238     {
239         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
240         exit (1);
241     }
242     r = bf_read (p->index_BFile, 0, 0, 0, p->tmp_buf);
243     switch (r)
244     {
245     case 0:
246         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
247         sprintf (p->head.version, "%3d", REC_VERSION);
248         p->head.index_free = 0;
249         p->head.index_last = 1;
250         p->head.no_records = 0;
251         p->head.total_bytes = 0;
252         for (i = 0; i<REC_BLOCK_TYPES; i++)
253         {
254             p->head.block_free[i] = 0;
255             p->head.block_last[i] = 1;
256             p->head.block_used[i] = 0;
257         }
258         p->head.block_size[0] = 128;
259         p->head.block_move[0] = 0;
260         for (i = 1; i<REC_BLOCK_TYPES; i++)
261         {
262             p->head.block_size[i] = p->head.block_size[i-1] * 4;
263             p->head.block_move[i] = p->head.block_size[i] * 24;
264         }
265         if (rw)
266             rec_write_head (p);
267         break;
268     case 1:
269         memcpy (&p->head, p->tmp_buf, sizeof(p->head));
270         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
271         {
272             logf (LOG_FATAL, "file %s has bad format", p->index_fname);
273             exit (1);
274         }
275         version = atoi (p->head.version);
276         if (version != REC_VERSION)
277         {
278             logf (LOG_FATAL, "file %s is version %d, but version"
279                   " %d is required", p->index_fname, version, REC_VERSION);
280             exit (1);
281         }
282         break;
283     }
284     for (i = 0; i<REC_BLOCK_TYPES; i++)
285     {
286         char str[80];
287         sprintf (str, "recd%c", i + 'A');
288         p->data_fname[i] = (char *) xmalloc (strlen(str)+1);
289         strcpy (p->data_fname[i], str);
290         p->data_BFile[i] = NULL;
291     }
292     for (i = 0; i<REC_BLOCK_TYPES; i++)
293     {
294         if (!(p->data_BFile[i] = bf_open (bfs, p->data_fname[i],
295                                           p->head.block_size[i],
296                                           rw)))
297         {
298             logf (LOG_FATAL|LOG_ERRNO, "bf_open %s", p->data_fname[i]);
299             exit (1);
300         }
301     }
302     p->cache_max = 400;
303     p->cache_cur = 0;
304     p->record_cache = (struct record_cache_entry *)
305         xmalloc (sizeof(*p->record_cache)*p->cache_max);
306     zebra_mutex_init (&p->mutex);
307     return p;
308 }
309
310 static void rec_encode_unsigned (unsigned n, unsigned char *buf, int *len)
311 {
312     (*len) = 0;
313     while (n > 127)
314     {
315         buf[*len] = 128 + (n & 127);
316         n = n >> 7;
317         (*len)++;
318     }
319     buf[*len] = n;
320     (*len)++;
321 }
322
323 static void rec_decode_unsigned(unsigned *np, unsigned char *buf, int *len)
324 {
325     unsigned n = 0;
326     unsigned w = 1;
327     (*len) = 0;
328
329     while (buf[*len] > 127)
330     {
331         n += w*(buf[*len] & 127);
332         w = w << 7;
333         (*len)++;
334     }
335     n += w * buf[*len];
336     (*len)++;
337     *np = n;
338 }
339
340 static void rec_cache_flush_block1 (Records p, Record rec, Record last_rec,
341                                     char **out_buf, int *out_size,
342                                     int *out_offset)
343 {
344     int i;
345     int len;
346
347     for (i = 0; i<REC_NO_INFO; i++)
348     {
349         if (*out_offset + (int) rec->size[i] + 20 > *out_size)
350         {
351             int new_size = *out_offset + rec->size[i] + 65536;
352             char *np = (char *) xmalloc (new_size);
353             if (*out_offset)
354                 memcpy (np, *out_buf, *out_offset);
355             xfree (*out_buf);
356             *out_size = new_size;
357             *out_buf = np;
358         }
359         if (i == 0)
360         {
361             rec_encode_unsigned (rec->sysno, *out_buf + *out_offset, &len);
362             (*out_offset) += len;
363         }
364         if (rec->size[i] == 0)
365         {
366             rec_encode_unsigned (1, *out_buf + *out_offset, &len);
367             (*out_offset) += len;
368         }
369         else if (last_rec && rec->size[i] == last_rec->size[i] &&
370                  !memcmp (rec->info[i], last_rec->info[i], rec->size[i]))
371         {
372             rec_encode_unsigned (0, *out_buf + *out_offset, &len);
373             (*out_offset) += len;
374         }
375         else
376         {
377             rec_encode_unsigned (rec->size[i]+1, *out_buf + *out_offset, &len);
378             (*out_offset) += len;
379             memcpy (*out_buf + *out_offset, rec->info[i], rec->size[i]);
380             (*out_offset) += rec->size[i];
381         }
382     }
383 }
384
385 static void rec_write_multiple (Records p, int saveCount)
386 {
387     int i;
388     short ref_count = 0;
389     char compression_method;
390     Record last_rec = 0;
391     int out_size = 1000;
392     int out_offset = 0;
393     char *out_buf = (char *) xmalloc (out_size);
394     int *sysnos = (int *) xmalloc (sizeof(*sysnos) * (p->cache_cur + 1));
395     int *sysnop = sysnos;
396
397     for (i = 0; i<p->cache_cur - saveCount; i++)
398     {
399         struct record_cache_entry *e = p->record_cache + i;
400         switch (e->flag)
401         {
402         case recordFlagNew:
403             rec_cache_flush_block1 (p, e->rec, last_rec, &out_buf,
404                                     &out_size, &out_offset);
405             *sysnop++ = e->rec->sysno;
406             ref_count++;
407             e->flag = recordFlagNop;
408             last_rec = e->rec;
409             break;
410         case recordFlagWrite:
411             rec_release_blocks (p, e->rec->sysno);
412             rec_cache_flush_block1 (p, e->rec, last_rec, &out_buf,
413                                     &out_size, &out_offset);
414             *sysnop++ = e->rec->sysno;
415             ref_count++;
416             e->flag = recordFlagNop;
417             last_rec = e->rec;
418             break;
419         case recordFlagDelete:
420             rec_delete_single (p, e->rec);
421             e->flag = recordFlagNop;
422             break;
423         default:
424             break;
425         }
426     }
427
428     *sysnop = -1;
429     if (ref_count)
430     {
431         int csize = 0;  /* indicate compression "not performed yet" */
432         compression_method = p->compression_method;
433         switch (compression_method)
434         {
435         case REC_COMPRESS_BZIP2:
436 #if HAVE_BZLIB_H        
437             csize = out_offset + (out_offset >> 6) + 620;
438             rec_tmp_expand (p, csize);
439 #ifdef BZ_CONFIG_ERROR
440             i = BZ2_bzBuffToBuffCompress 
441 #else
442             i = bzBuffToBuffCompress 
443 #endif
444                                      (p->tmp_buf+sizeof(int)+sizeof(short)+
445                                       sizeof(char),
446                                       &csize, out_buf, out_offset, 1, 0, 30);
447             if (i != BZ_OK)
448             {
449                 logf (LOG_WARN, "bzBuffToBuffCompress error code=%d", i);
450                 csize = 0;
451             }
452             logf (LOG_LOG, "compress %4d %5d %5d", ref_count, out_offset,
453                   csize);
454 #endif
455             break;
456         case REC_COMPRESS_NONE:
457             break;
458         }
459         if (!csize)  
460         {
461             /* either no compression or compression not supported ... */
462             csize = out_offset;
463             rec_tmp_expand (p, csize);
464             memcpy (p->tmp_buf + sizeof(int) + sizeof(short) + sizeof(char),
465                     out_buf, out_offset);
466             csize = out_offset;
467             compression_method = REC_COMPRESS_NONE;
468         }
469         memcpy (p->tmp_buf + sizeof(int), &ref_count, sizeof(ref_count));
470         memcpy (p->tmp_buf + sizeof(int)+sizeof(short),
471                 &compression_method, sizeof(compression_method));
472                 
473         /* -------- compression */
474         rec_write_tmp_buf (p, csize + sizeof(short) + sizeof(char), sysnos);
475     }
476     xfree (out_buf);
477     xfree (sysnos);
478 }
479
480 static void rec_cache_flush (Records p, int saveCount)
481 {
482     int i, j;
483
484     if (saveCount >= p->cache_cur)
485         saveCount = 0;
486
487     rec_write_multiple (p, saveCount);
488
489     for (i = 0; i<p->cache_cur - saveCount; i++)
490     {
491         struct record_cache_entry *e = p->record_cache + i;
492         rec_rm (&e->rec);
493     } 
494     /* i still being used ... */
495     for (j = 0; j<saveCount; j++, i++)
496         memcpy (p->record_cache+j, p->record_cache+i,
497                 sizeof(*p->record_cache));
498     p->cache_cur = saveCount;
499 }
500
501 static Record *rec_cache_lookup (Records p, int sysno,
502                                  enum recordCacheFlag flag)
503 {
504     int i;
505     for (i = 0; i<p->cache_cur; i++)
506     {
507         struct record_cache_entry *e = p->record_cache + i;
508         if (e->rec->sysno == sysno)
509         {
510             if (flag != recordFlagNop && e->flag == recordFlagNop)
511                 e->flag = flag;
512             return &e->rec;
513         }
514     }
515     return NULL;
516 }
517
518 static void rec_cache_insert (Records p, Record rec, enum recordCacheFlag flag)
519 {
520     struct record_cache_entry *e;
521
522     if (p->cache_cur == p->cache_max)
523         rec_cache_flush (p, 1);
524     else if (p->cache_cur > 0)
525     {
526         int i, j;
527         int used = 0;
528         for (i = 0; i<p->cache_cur; i++)
529         {
530             Record r = (p->record_cache + i)->rec;
531             for (j = 0; j<REC_NO_INFO; j++)
532                 used += r->size[j];
533         }
534         if (used > 90000)
535             rec_cache_flush (p, 1);
536     }
537     assert (p->cache_cur < p->cache_max);
538
539     e = p->record_cache + (p->cache_cur)++;
540     e->flag = flag;
541     e->rec = rec_cp (rec);
542 }
543
544 void rec_close (Records *pp)
545 {
546     Records p = *pp;
547     int i;
548
549     assert (p);
550
551     zebra_mutex_destroy (&p->mutex);
552     rec_cache_flush (p, 0);
553     xfree (p->record_cache);
554
555     if (p->rw)
556         rec_write_head (p);
557
558     if (p->index_BFile)
559         bf_close (p->index_BFile);
560
561     for (i = 0; i<REC_BLOCK_TYPES; i++)
562     {
563         if (p->data_BFile[i])
564             bf_close (p->data_BFile[i]);
565         xfree (p->data_fname[i]);
566     }
567     xfree (p->tmp_buf);
568     xfree (p);
569     *pp = NULL;
570 }
571
572 static Record rec_get_int (Records p, int sysno)
573 {
574     int i, in_size, r;
575     Record rec, *recp;
576     struct record_index_entry entry;
577     int freeblock, dst_type;
578     char *nptr, *cptr;
579     char *in_buf = 0;
580     char *bz_buf = 0;
581 #if HAVE_BZLIB_H
582     int bz_size;
583 #endif
584     char compression_method;
585
586     assert (sysno > 0);
587     assert (p);
588
589     if ((recp = rec_cache_lookup (p, sysno, recordFlagNop)))
590         return rec_cp (*recp);
591
592     if (read_indx (p, sysno, &entry, sizeof(entry), 1) < 1)
593         return NULL;       /* record is not there! */
594
595     if (!entry.size)
596         return NULL;       /* record is deleted */
597
598     dst_type = entry.next & 7;
599     assert (dst_type < REC_BLOCK_TYPES);
600     freeblock = entry.next / 8;
601
602     assert (freeblock > 0);
603     
604     rec_tmp_expand (p, entry.size);
605
606     cptr = p->tmp_buf;
607     r = bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
608     if (r < 0)
609         return 0;
610     memcpy (&freeblock, cptr, sizeof(freeblock));
611
612     while (freeblock)
613     {
614         int tmp;
615
616         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
617         
618         memcpy (&tmp, cptr, sizeof(tmp));
619         r = bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
620         if (r < 0)
621             return 0;
622         memcpy (&freeblock, cptr, sizeof(freeblock));
623         memcpy (cptr, &tmp, sizeof(tmp));
624     }
625
626     rec = (Record) xmalloc (sizeof(*rec));
627     rec->sysno = sysno;
628     memcpy (&compression_method, p->tmp_buf + sizeof(int) + sizeof(short),
629             sizeof(compression_method));
630     in_buf = p->tmp_buf + sizeof(int) + sizeof(short) + sizeof(char);
631     in_size = entry.size - sizeof(short) - sizeof(char);
632     switch (compression_method)
633     {
634     case REC_COMPRESS_BZIP2:
635 #if HAVE_BZLIB_H
636         bz_size = entry.size * 20 + 100;
637         while (1)
638         {
639             bz_buf = (char *) xmalloc (bz_size);
640 #ifdef BZ_CONFIG_ERROR
641             i = BZ2_bzBuffToBuffDecompress
642 #else
643             i = bzBuffToBuffDecompress
644 #endif
645                  (bz_buf, &bz_size, in_buf, in_size, 0, 0);
646             logf (LOG_LOG, "decompress %5d %5d", in_size, bz_size);
647             if (i == BZ_OK)
648                 break;
649             logf (LOG_LOG, "failed");
650             xfree (bz_buf);
651             bz_size *= 2;
652         }
653         in_buf = bz_buf;
654         in_size = bz_size;
655 #else
656         logf (LOG_FATAL, "cannot decompress record(s) in BZIP2 format");
657         exit (1);
658 #endif
659         break;
660     case REC_COMPRESS_NONE:
661         break;
662     }
663     for (i = 0; i<REC_NO_INFO; i++)
664         rec->info[i] = 0;
665
666     nptr = in_buf;                /* skip ref count */
667     while (nptr < in_buf + in_size)
668     {
669         int this_sysno;
670         int len;
671         rec_decode_unsigned (&this_sysno, nptr, &len);
672         nptr += len;
673
674         for (i = 0; i < REC_NO_INFO; i++)
675         {
676             int this_size;
677             rec_decode_unsigned (&this_size, nptr, &len);
678             nptr += len;
679
680             if (this_size == 0)
681                 continue;
682             rec->size[i] = this_size-1;
683
684             if (rec->size[i])
685             {
686                 rec->info[i] = nptr;
687                 nptr += rec->size[i];
688             }
689             else
690                 rec->info[i] = NULL;
691         }
692         if (this_sysno == sysno)
693             break;
694     }
695     for (i = 0; i<REC_NO_INFO; i++)
696     {
697         if (rec->info[i] && rec->size[i])
698         {
699             char *np = xmalloc (rec->size[i]+1);
700             memcpy (np, rec->info[i], rec->size[i]);
701             np[rec->size[i]] = '\0';
702             rec->info[i] = np;
703         }
704         else
705         {
706             assert (rec->info[i] == 0);
707             assert (rec->size[i] == 0);
708         }
709     }
710     xfree (bz_buf);
711     rec_cache_insert (p, rec, recordFlagNop);
712     return rec;
713 }
714
715 Record rec_get (Records p, int sysno)
716 {
717     Record rec;
718     zebra_mutex_lock (&p->mutex);
719
720     rec = rec_get_int (p, sysno);
721     zebra_mutex_unlock (&p->mutex);
722     return rec;
723 }
724
725 static Record rec_new_int (Records p)
726 {
727     int sysno, i;
728     Record rec;
729
730     assert (p);
731     rec = (Record) xmalloc (sizeof(*rec));
732     if (1 || p->head.index_free == 0)
733         sysno = (p->head.index_last)++;
734     else
735     {
736         struct record_index_entry entry;
737
738         read_indx (p, p->head.index_free, &entry, sizeof(entry), 0);
739         sysno = p->head.index_free;
740         p->head.index_free = entry.next;
741     }
742     (p->head.no_records)++;
743     rec->sysno = sysno;
744     for (i = 0; i < REC_NO_INFO; i++)
745     {
746         rec->info[i] = NULL;
747         rec->size[i] = 0;
748     }
749     rec_cache_insert (p, rec, recordFlagNew);
750     return rec;
751 }
752
753 Record rec_new (Records p)
754 {
755     Record rec;
756     zebra_mutex_lock (&p->mutex);
757
758     rec = rec_new_int (p);
759     zebra_mutex_unlock (&p->mutex);
760     return rec;
761 }
762
763 void rec_del (Records p, Record *recpp)
764 {
765     Record *recp;
766
767     zebra_mutex_lock (&p->mutex);
768     (p->head.no_records)--;
769     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagDelete)))
770     {
771         rec_rm (recp);
772         *recp = *recpp;
773     }
774     else
775     {
776         rec_cache_insert (p, *recpp, recordFlagDelete);
777         rec_rm (recpp);
778     }
779     zebra_mutex_unlock (&p->mutex);
780     *recpp = NULL;
781 }
782
783 void rec_put (Records p, Record *recpp)
784 {
785     Record *recp;
786
787     zebra_mutex_lock (&p->mutex);
788     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagWrite)))
789     {
790         rec_rm (recp);
791         *recp = *recpp;
792     }
793     else
794     {
795         rec_cache_insert (p, *recpp, recordFlagWrite);
796         rec_rm (recpp);
797     }
798     zebra_mutex_unlock (&p->mutex);
799     *recpp = NULL;
800 }
801
802 void rec_rm (Record *recpp)
803 {
804     int i;
805
806     if (!*recpp)
807         return ;
808     for (i = 0; i < REC_NO_INFO; i++)
809         xfree ((*recpp)->info[i]);
810     xfree (*recpp);
811     *recpp = NULL;
812 }
813
814 Record rec_cp (Record rec)
815 {
816     Record n;
817     int i;
818
819     n = (Record) xmalloc (sizeof(*n));
820     n->sysno = rec->sysno;
821     for (i = 0; i < REC_NO_INFO; i++)
822         if (!rec->info[i])
823         {
824             n->info[i] = NULL;
825             n->size[i] = 0;
826         }
827         else
828         {
829             n->size[i] = rec->size[i];
830             n->info[i] = (char *) xmalloc (rec->size[i]);
831             memcpy (n->info[i], rec->info[i], rec->size[i]);
832         }
833     return n;
834 }
835
836
837 char *rec_strdup (const char *s, size_t *len)
838 {
839     char *p;
840
841     if (!s)
842     {
843         *len = 0;
844         return NULL;
845     }
846     *len = strlen(s)+1;
847     p = (char *) xmalloc (*len);
848     strcpy (p, s);
849     return p;
850 }
851