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