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