Record management uses the bfile system.
[idzebra-moved-to-github.git] / index / recindex.c
1 /*
2  * Copyright (C) 1994-1995, Index Data I/S 
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: recindex.c,v $
7  * Revision 1.5  1995-11-22 17:19:18  adam
8  * Record management uses the bfile system.
9  *
10  * Revision 1.4  1995/11/20  16:59:46  adam
11  * New update method: the 'old' keys are saved for each records.
12  *
13  * Revision 1.3  1995/11/16  15:34:55  adam
14  * Uses new record management system in both indexer and server.
15  *
16  * Revision 1.2  1995/11/15  19:13:08  adam
17  * Work on record management.
18  *
19  * Revision 1.1  1995/11/15  14:46:20  adam
20  * Started work on better record management system.
21  *
22  */
23 #include <stdio.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30
31 #include "recindex.h"
32
33 #define USE_BF 1
34
35 #if USE_BF
36 #include <bfile.h>
37
38 #define REC_BLOCK_TYPES 2
39 #define REC_HEAD_MAGIC "recindx"
40
41 struct records_info {
42     int rw;
43
44     char *index_fname;
45     BFile index_BFile;
46
47
48     char *data_fname[REC_BLOCK_TYPES];
49     BFile data_BFile[REC_BLOCK_TYPES];
50
51     char *tmp_buf;
52     int tmp_size;
53
54     struct record_cache_entry *record_cache;
55     int cache_size;
56     int cache_cur;
57     int cache_max;
58
59     struct records_head {
60         char magic[8];
61         int block_size[REC_BLOCK_TYPES];
62         int block_free[REC_BLOCK_TYPES];
63         int block_last[REC_BLOCK_TYPES];
64         int block_used[REC_BLOCK_TYPES];
65         int block_move[REC_BLOCK_TYPES];
66
67         int index_last;
68         int index_free;
69         int no_records;
70
71     } head;
72 };
73
74 enum recordCacheFlag { recordFlagNop, recordFlagWrite, recordFlagDelete };
75
76 struct record_cache_entry {
77     Record rec;
78     enum recordCacheFlag flag;
79 };
80
81 struct record_index_entry {
82     union {
83         struct {
84             int next;
85             int size;
86         } used;
87         struct {
88             int next;
89         } free;
90     } u;
91 };
92
93
94 static void rec_write_head (Records p)
95 {
96     int r;
97
98     assert (p);
99     assert (p->index_BFile);
100
101     r = bf_write (p->index_BFile, 0, 0, sizeof(p->head), &p->head);    
102     if (r)
103     {
104         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
105         exit (1);
106     }
107 }
108
109 static void rec_tmp_expand (Records p, int size, int dst_type)
110 {
111     if (p->tmp_size < size + 256 ||
112         p->tmp_size < p->head.block_size[dst_type]*2)
113     {
114         free (p->tmp_buf);
115         p->tmp_size = size + p->head.block_size[dst_type]*2 +
116             256;
117         if (!(p->tmp_buf = malloc (p->tmp_size)))
118         {
119             logf (LOG_FATAL|LOG_ERRNO, "malloc");
120             exit (1);
121         }
122     }
123 }
124
125 static int read_indx (Records p, int sysno, void *buf, int itemsize, 
126                       int ignoreError)
127 {
128     int r;
129     int pos = (sysno-1)*itemsize;
130
131     r = bf_read (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
132     if (r != 1 && !ignoreError)
133     {
134         logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
135               p->index_fname, (long) pos);
136         abort ();
137         exit (1);
138     }
139     return r;
140 }
141
142 static void write_indx (Records p, int sysno, void *buf, int itemsize)
143 {
144     int pos = (sysno-1)*itemsize;
145
146     bf_write (p->index_BFile, 1+pos/128, pos%128, itemsize, buf);
147 }
148
149 static void rec_release_blocks (Records p, int sysno)
150 {
151     struct record_index_entry entry;
152     int freeblock, freenext;
153     int dst_type;
154
155     if (read_indx (p, sysno, &entry, sizeof(entry), 1) != 1)
156         return ;
157     freeblock = entry.u.used.next;
158     assert (freeblock > 0);
159     dst_type = freeblock & 7;
160     freeblock = freeblock / 8;
161     while (freeblock)
162     {
163         if (bf_read (p->data_BFile[dst_type], freeblock, 0, sizeof(freenext),
164                      &freenext) != 1)
165         {
166             logf (LOG_FATAL|LOG_ERRNO, "read in rec_del_single");
167             exit (1);
168         }
169         if (bf_write (p->data_BFile[dst_type], freeblock, 0, sizeof(freenext),
170                       &p->head.block_free[dst_type]))
171         {
172             logf (LOG_FATAL|LOG_ERRNO, "write in rec_del_single");
173             exit (1);
174         }
175         p->head.block_free[dst_type] = freeblock;
176         freeblock = freenext;
177         p->head.block_used[dst_type]--;
178     }
179 }
180
181 static void rec_delete_single (Records p, Record rec)
182 {
183     struct record_index_entry entry;
184
185     rec_release_blocks (p, rec->sysno);
186
187     entry.u.free.next = p->head.index_free;
188     p->head.index_free = rec->sysno;
189     write_indx (p, rec->sysno, &entry, sizeof(entry));
190 }
191
192 static void rec_write_single (Records p, Record rec)
193 {
194     int i, size = 0;
195     char *cptr;
196     int dst_type = 0;
197     int no_written = 0;
198     int block_prev = -1, block_free;
199     struct record_index_entry entry;
200
201     rec_release_blocks (p, rec->sysno);
202
203     for (i = 0; i < REC_NO_INFO; i++)
204         if (!rec->info[i])
205             size += sizeof(*rec->size);
206         else
207             size += sizeof(*rec->size) + rec->size[i];
208
209     for (i = 1; i<REC_BLOCK_TYPES; i++)
210         if (size >= p->head.block_move[i])
211             dst_type = i;
212
213     rec_tmp_expand (p, size, dst_type);
214
215     cptr = p->tmp_buf + sizeof(int);           /* a hack! */
216     for (i = 0; i < REC_NO_INFO; i++)
217     {
218         memcpy (cptr, &rec->size[i], sizeof(*rec->size));
219         cptr += sizeof(*rec->size);
220         if (rec->info[i])
221         {
222             memcpy (cptr, rec->info[i], rec->size[i]);
223             cptr += rec->size[i];
224         }
225     }
226     cptr = p->tmp_buf;
227     while (no_written < size)
228     {
229         block_free = p->head.block_free[dst_type];
230         if (block_free)
231         {
232             if (bf_read (p->data_BFile[dst_type],
233                          block_free, 0, sizeof(*p->head.block_free),
234                          &p->head.block_free[dst_type]) != 1)
235             {
236                 logf (LOG_FATAL|LOG_ERRNO, "read in %s at free block %d",
237                       p->data_fname[dst_type], block_free);
238             }
239         }
240         else
241             block_free = p->head.block_last[dst_type]++;
242         if (block_prev == -1)
243         {
244             entry.u.used.next = block_free*8 + dst_type;
245             entry.u.used.size = size;
246
247             write_indx (p, rec->sysno, &entry, sizeof(entry));
248         }
249         else
250         {
251             memcpy (cptr, &block_free, sizeof(int));
252             bf_write (p->data_BFile[dst_type], block_prev, 0, 0, cptr);
253             cptr = p->tmp_buf + no_written;
254         }
255         block_prev = block_free;
256         no_written += p->head.block_size[dst_type] - sizeof(int);
257         p->head.block_used[dst_type]++;
258     }
259     assert (block_prev != -1);
260     block_free = 0;
261     memcpy (cptr, &block_free, sizeof(int));
262     bf_write (p->data_BFile[dst_type], block_prev, 0,
263               sizeof(int) + (p->tmp_buf+size) - cptr, cptr);
264 }
265
266
267 Records rec_open (int rw)
268 {
269     Records p;
270     int i, r;
271
272     if (!(p = malloc (sizeof(*p))))
273     {
274         logf (LOG_FATAL|LOG_ERRNO, "malloc");
275         exit (1);
276     }
277     p->rw = rw;
278     p->tmp_size = 1024;
279     p->tmp_buf = malloc (p->tmp_size);
280     if (!p->tmp_buf)
281     {
282         logf (LOG_FATAL|LOG_ERRNO, "malloc");
283         exit (1);
284     }
285     p->index_fname = "recindex";
286     p->index_BFile = bf_open (p->index_fname, 128, rw);
287     if (p->index_BFile == NULL)
288     {
289         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
290         exit (1);
291     }
292     r = bf_read (p->index_BFile, 0, 0, 0, p->tmp_buf);
293     switch (r)
294     {
295     case 0:
296         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
297         p->head.index_free = 0;
298         p->head.index_last = 1;
299         p->head.no_records = 0;
300         for (i = 0; i<REC_BLOCK_TYPES; i++)
301         {
302             p->head.block_free[i] = 0;
303             p->head.block_last[i] = 1;
304             p->head.block_used[i] = 0;
305         }
306         p->head.block_size[0] = 128;
307         p->head.block_move[0] = 0;
308         for (i = 1; i<REC_BLOCK_TYPES; i++)
309         {
310             p->head.block_size[i] = p->head.block_size[i-1] * 4;
311             p->head.block_move[i] = p->head.block_size[i] * 3;
312         }
313         if (rw)
314             rec_write_head (p);
315         break;
316     case 1:
317         memcpy (&p->head, p->tmp_buf, sizeof(p->head));
318         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
319         {
320             logf (LOG_FATAL, "read %s. bad header", p->index_fname);
321             exit (1);
322         }
323         break;
324     }
325     for (i = 0; i<REC_BLOCK_TYPES; i++)
326     {
327         char str[80];
328         sprintf (str, "recdata%d", i);
329         p->data_fname[i] = malloc (strlen(str)+1);
330         strcpy (p->data_fname[i], str);
331         p->data_BFile[i] = NULL;
332     }
333     for (i = 0; i<REC_BLOCK_TYPES; i++)
334     {
335         if (!(p->data_BFile[i] = bf_open (p->data_fname[i],
336                                           p->head.block_size[i],
337                                           rw)))
338         {
339             logf (LOG_FATAL|LOG_ERRNO, "bf_open %s", p->data_fname[i]);
340             exit (1);
341         }
342     }
343     p->cache_max = 10;
344     p->cache_cur = 0;
345     if (!(p->record_cache = malloc (sizeof(*p->record_cache)*p->cache_max)))
346     {
347         logf (LOG_FATAL|LOG_ERRNO, "malloc");
348         exit (1);
349     }
350     return p;
351 }
352
353 static void rec_cache_flush (Records p)
354 {
355     int i;
356     for (i = 0; i<p->cache_cur; i++)
357     {
358         struct record_cache_entry *e = p->record_cache + i;
359         switch (e->flag)
360         {
361         case recordFlagNop:
362             break;
363         case recordFlagWrite:
364             rec_write_single (p, e->rec);
365             break;
366         case recordFlagDelete:
367             rec_delete_single (p, e->rec);
368             break;
369         }
370         rec_rm (&e->rec);
371     }
372     p->cache_cur = 0;
373 }
374
375 static Record *rec_cache_lookup (Records p, int sysno,
376                                  enum recordCacheFlag flag)
377 {
378     int i;
379     for (i = 0; i<p->cache_cur; i++)
380     {
381         struct record_cache_entry *e = p->record_cache + i;
382         if (e->rec->sysno == sysno)
383         {
384             if (flag != recordFlagNop)
385                 e->flag = flag;
386             return &e->rec;
387         }
388     }
389     return NULL;
390 }
391
392 static void rec_cache_insert (Records p, Record rec, enum recordCacheFlag flag)
393 {
394     struct record_cache_entry *e;
395
396     if (p->cache_cur == p->cache_max)
397         rec_cache_flush (p);
398     assert (p->cache_cur < p->cache_max);
399
400     e = p->record_cache + (p->cache_cur)++;
401     e->flag = flag;
402     e->rec = rec_cp (rec);
403 }
404
405 void rec_close (Records *pp)
406 {
407     Records p = *pp;
408     int i;
409
410     assert (p);
411
412     rec_cache_flush (p);
413     free (p->record_cache);
414
415     if (p->rw)
416         rec_write_head (p);
417
418     if (p->index_BFile)
419         bf_close (p->index_BFile);
420
421     for (i = 0; i<REC_BLOCK_TYPES; i++)
422     {
423         if (p->data_BFile[i])
424             bf_close (p->data_BFile[i]);
425         free (p->data_fname[i]);
426     }
427     free (p->tmp_buf);
428     free (p);
429     *pp = NULL;
430 }
431
432
433 Record rec_get (Records p, int sysno)
434 {
435     int i;
436     Record rec, *recp;
437     struct record_index_entry entry;
438     int freeblock, dst_type;
439     char *nptr, *cptr;
440
441     assert (sysno > 0);
442     assert (p);
443
444     if ((recp = rec_cache_lookup (p, sysno, recordFlagNop)))
445         return rec_cp (*recp);
446
447     read_indx (p, sysno, &entry, sizeof(entry), 0);
448
449     dst_type = entry.u.used.next & 7;
450     freeblock = entry.u.used.next / 8;
451
452     assert (freeblock > 0);
453     
454     if (!(rec = malloc (sizeof(*rec))))
455     {
456         logf (LOG_FATAL|LOG_ERRNO, "malloc");
457         exit (1);
458     }
459     rec_tmp_expand (p, entry.u.used.size, dst_type);
460
461     cptr = p->tmp_buf;
462     bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
463     memcpy (&freeblock, cptr, sizeof(freeblock));
464
465     while (freeblock)
466     {
467         int tmp;
468
469         cptr += p->head.block_size[dst_type] - sizeof(freeblock);
470         
471         memcpy (&tmp, cptr, sizeof(tmp));
472         bf_read (p->data_BFile[dst_type], freeblock, 0, 0, cptr);
473         memcpy (&freeblock, cptr, sizeof(freeblock));
474         memcpy (cptr, &tmp, sizeof(tmp));
475     }
476
477     rec->sysno = sysno;
478     nptr = p->tmp_buf + sizeof(freeblock);
479     for (i = 0; i < REC_NO_INFO; i++)
480     {
481         memcpy (&rec->size[i], nptr, sizeof(*rec->size));
482         nptr += sizeof(*rec->size);
483         if (rec->size[i])
484         {
485             rec->info[i] = malloc (rec->size[i]);
486             memcpy (rec->info[i], nptr, rec->size[i]);
487             nptr += rec->size[i];
488         }
489         else
490             rec->info[i] = NULL;
491     }
492     rec_cache_insert (p, rec, recordFlagNop);
493     return rec;
494 }
495
496 Record rec_new (Records p)
497 {
498     int sysno, i;
499     Record rec;
500
501     assert (p);
502     if (!(rec = malloc (sizeof(*rec))))
503     {
504         logf (LOG_FATAL|LOG_ERRNO, "malloc");
505         exit (1);
506     }
507     if (p->head.index_free == 0)
508         sysno = (p->head.index_last)++;
509     else
510     {
511         struct record_index_entry entry;
512
513         read_indx (p, p->head.index_free, &entry, sizeof(entry), 0);
514         sysno = p->head.index_free;
515         p->head.index_free = entry.u.free.next;
516     }
517     (p->head.no_records)++;
518     rec->sysno = sysno;
519     for (i = 0; i < REC_NO_INFO; i++)
520     {
521         rec->info[i] = NULL;
522         rec->size[i] = 0;
523     }
524     rec_cache_insert (p, rec, recordFlagWrite);
525     return rec;
526 }
527
528 void rec_del (Records p, Record *recpp)
529 {
530     Record *recp;
531
532     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagDelete)))
533     {
534         rec_rm (recp);
535         *recp = *recpp;
536     }
537     else
538     {
539         rec_cache_insert (p, *recpp, recordFlagDelete);
540         rec_rm (recpp);
541     }
542     *recpp = NULL;
543 }
544
545 void rec_put (Records p, Record *recpp)
546 {
547     Record *recp;
548
549     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, recordFlagWrite)))
550     {
551         rec_rm (recp);
552         *recp = *recpp;
553     }
554     else
555     {
556         rec_cache_insert (p, *recpp, recordFlagWrite);
557         rec_rm (recpp);
558     }
559     *recpp = NULL;
560 }
561
562 void rec_rm (Record *recpp)
563 {
564     int i;
565     for (i = 0; i < REC_NO_INFO; i++)
566         free ((*recpp)->info[i]);
567     free (*recpp);
568     *recpp = NULL;
569 }
570
571 Record rec_cp (Record rec)
572 {
573     Record n;
574     int i;
575
576     if (!(n = malloc (sizeof(*n))))
577     {
578         logf (LOG_FATAL|LOG_ERRNO, "malloc");
579         exit (1);
580     }
581     n->sysno = rec->sysno;
582     for (i = 0; i < REC_NO_INFO; i++)
583         if (!rec->info[i])
584         {
585             n->info[i] = NULL;
586             n->size[i] = 0;
587         }
588         else
589         {
590             n->size[i] = rec->size[i];
591             if (!(n->info[i] = malloc (rec->size[i])))
592             {
593                 logf (LOG_FATAL|LOG_ERRNO, "malloc. rec_cp");
594                 exit (1);
595             }
596             memcpy (n->info[i], rec->info[i], rec->size[i]);
597         }
598     return n;
599 }
600
601 /* no BF --------------------------------------------------- */
602 #else
603
604 struct records_info {
605     int rw;
606     int index_fd;
607     char *index_fname;
608     int data_fd;
609     char *data_fname;
610     struct records_head {
611         char magic[8];
612         int no_records;
613         int index_free;
614         int index_last;
615         int data_size;
616         int data_slack;
617         int data_used;
618     } head;
619     char *tmp_buf;
620     int tmp_size;
621     int cache_size;
622     int cache_cur;
623     int cache_max;
624     struct record_cache_entry *record_cache;
625 };
626
627 struct record_cache_entry {
628     Record rec;
629     int dirty;
630 };
631
632 struct record_index_entry {
633     union {
634         struct {
635             int offset;
636             int size;
637         } used;
638         struct {
639             int next;
640         } free;
641     } u;
642 };
643
644 #define REC_HEAD_MAGIC "rechead"
645
646 static void rec_write_head (Records p)
647 {
648     int r;
649
650     assert (p);
651     assert (p->index_fd != -1);
652     if (lseek (p->index_fd, (off_t) 0, SEEK_SET) == -1)
653     {
654         logf (LOG_FATAL|LOG_ERRNO, "lseek to 0 in %s", p->index_fname);
655         exit (1);
656     }
657     r = write (p->index_fd, &p->head, sizeof(p->head));    
658     switch (r)
659     {
660     case -1:
661         logf (LOG_FATAL|LOG_ERRNO, "write head of %s", p->index_fname);
662         exit (1);
663     case sizeof(p->head):
664         break;
665     default:
666         logf (LOG_FATAL, "write head of %s. wrote %d", p->index_fname, r);
667         exit (1);
668     }
669 }
670
671 Records rec_open (int rw)
672 {
673     Records p;
674     int r;
675
676     if (!(p = malloc (sizeof(*p))))
677     {
678         logf (LOG_FATAL|LOG_ERRNO, "malloc");
679         exit (1);
680     }
681     p->rw = rw;
682     p->tmp_buf = NULL;
683     p->tmp_size = 0;
684     p->data_fname = "recdata";
685     p->data_fd = -1;
686     p->index_fname = "recindex";
687     p->index_fd = open (p->index_fname,
688                         rw ? (O_RDWR|O_CREAT) : O_RDONLY, 0666);
689     if (p->index_fd == -1)
690     {
691         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->index_fname);
692         exit (1);
693     }
694     r = read (p->index_fd, &p->head, sizeof(p->head));
695     switch (r)
696     {
697     case -1:
698         logf (LOG_FATAL|LOG_ERRNO, "read %s", p->index_fname);
699         exit (1);
700     case 0:
701         memcpy (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic));
702         p->head.index_free = 0;
703         p->head.index_last = 1;
704         p->head.no_records = 0;
705         p->head.data_size = 0;
706         p->head.data_slack = 0;
707         p->head.data_used = 0;
708         if (rw)
709             rec_write_head (p);
710         break;
711     case sizeof(p->head):
712         if (memcmp (p->head.magic, REC_HEAD_MAGIC, sizeof(p->head.magic)))
713         {
714             logf (LOG_FATAL, "read %s. bad header", p->index_fname);
715             exit (1);
716         }
717         break;
718     default:
719         logf (LOG_FATAL, "read head of %s. expected %d. got %d",
720               p->index_fname, sizeof(p->head), r);
721         exit (1);
722     }
723     p->data_fd = open (p->data_fname,
724                        rw ? (O_RDWR|O_CREAT) : O_RDONLY, 0666);
725     if (p->data_fd == -1)
726     {
727         logf (LOG_FATAL|LOG_ERRNO, "open %s", p->data_fname);
728         exit (1);
729     }
730     p->cache_max = 10;
731     p->cache_cur = 0;
732     if (!(p->record_cache = malloc (sizeof(*p->record_cache)*p->cache_max)))
733     {
734         logf (LOG_FATAL|LOG_ERRNO, "malloc");
735         exit (1);
736     }
737     return p;
738 }
739
740 static void read_indx (Records p, int sysno, void *buf, int itemsize)
741 {
742     int r;
743     off_t pos = (sysno-1)*itemsize + sizeof(p->head);
744
745     if (lseek (p->index_fd, pos, SEEK_SET) == (pos) -1)
746     {
747         logf (LOG_FATAL|LOG_ERRNO, "seek in %s to pos %ld",
748               p->index_fname, (long) pos);
749         exit (1);
750     }
751     r = read (p->index_fd, buf, itemsize);
752     if (r != itemsize)
753     {
754         if (r == -1)
755             logf (LOG_FATAL|LOG_ERRNO, "read in %s at pos %ld",
756                   p->index_fname, (long) pos);
757         else
758             logf (LOG_FATAL, "read in %s at pos %ld",
759                   p->index_fname, (long) pos);
760         exit (1);
761     }
762 }
763
764 static void rec_write_single (Records p, Record rec)
765 {
766     struct record_index_entry entry;
767     int r, i, size = 0, got;
768     char *cptr;
769     off_t pos = (rec->sysno-1)*sizeof(entry) + sizeof(p->head);
770
771     for (i = 0; i < REC_NO_INFO; i++)
772         if (!rec->info[i])
773             size += sizeof(*rec->size);
774         else
775             size += sizeof(*rec->size) + rec->size[i];
776     
777     entry.u.used.offset = p->head.data_size;
778     entry.u.used.size = size;
779     p->head.data_size += size;
780     p->head.data_used += size;
781
782     if (lseek (p->index_fd, pos, SEEK_SET) == (pos) -1)
783     {
784         logf (LOG_FATAL|LOG_ERRNO, "seek in %s to pos %ld",
785               p->index_fname, (long) pos);
786         exit (1);
787     }
788     r = write (p->index_fd, &entry, sizeof(entry));
789     if (r != sizeof(entry))
790     {
791         if (r == -1)
792             logf (LOG_FATAL|LOG_ERRNO, "write of %s at pos %ld",
793                   p->index_fname, (long) pos);
794         else
795             logf (LOG_FATAL, "write of %s at pos %ld",
796                   p->index_fname, (long) pos);
797         exit (1);
798     }
799     if (lseek (p->data_fd, entry.u.used.offset, SEEK_SET) == -1) 
800     {
801         logf (LOG_FATAL|LOG_ERRNO, "lseek in %s to pos %ld",
802               p->data_fname, entry.u.used.offset);
803         exit (1);
804     }
805     if (p->tmp_size < entry.u.used.size) 
806     {
807         free (p->tmp_buf);
808         p->tmp_size = entry.u.used.size + 16384;
809         if (!(p->tmp_buf = malloc (p->tmp_size)))
810         {
811             logf (LOG_FATAL|LOG_ERRNO, "malloc");
812             exit (1);
813         }
814     }
815     cptr = p->tmp_buf;
816     for (i = 0; i < REC_NO_INFO; i++)
817     {
818         memcpy (cptr, &rec->size[i], sizeof(*rec->size));
819         cptr += sizeof(*rec->size);
820         if (rec->info[i])
821         {
822             memcpy (cptr, rec->info[i], rec->size[i]);
823             cptr += rec->size[i];
824         }
825     }
826     for (got = 0; got < entry.u.used.size; got += r)
827     {
828         r = write (p->data_fd, p->tmp_buf + got, entry.u.used.size - got);
829         if (r <= 0)
830         {
831             logf (LOG_FATAL|LOG_ERRNO, "write of %s", p->data_fname);
832             exit (1);
833         }
834     }
835 }
836
837 static void rec_cache_flush (Records p)
838 {
839     int i;
840     for (i = 0; i<p->cache_cur; i++)
841     {
842         struct record_cache_entry *e = p->record_cache + i;
843         if (e->dirty)
844             rec_write_single (p, e->rec);
845         rec_rm (&e->rec);
846     }
847     p->cache_cur = 0;
848 }
849
850 static Record *rec_cache_lookup (Records p, int sysno, int dirty)
851 {
852     int i;
853     for (i = 0; i<p->cache_cur; i++)
854     {
855         struct record_cache_entry *e = p->record_cache + i;
856         if (e->rec->sysno == sysno)
857         {
858             if (dirty)
859                 e->dirty = 1;
860             return &e->rec;
861         }
862     }
863     return NULL;
864 }
865
866 static void rec_cache_insert (Records p, Record rec, int dirty)
867 {
868     struct record_cache_entry *e;
869
870     if (p->cache_cur == p->cache_max)
871         rec_cache_flush (p);
872     assert (p->cache_cur < p->cache_max);
873
874     e = p->record_cache + (p->cache_cur)++;
875     e->dirty = dirty;
876     e->rec = rec_cp (rec);
877 }
878
879 void rec_close (Records *p)
880 {
881     assert (*p);
882
883     rec_cache_flush (*p);
884     free ((*p)->record_cache);
885
886     if ((*p)->rw)
887         rec_write_head (*p);
888
889     if ((*p)->index_fd != -1)
890         close ((*p)->index_fd);
891
892     if ((*p)->data_fd != -1)
893         close ((*p)->data_fd);
894
895     free ((*p)->tmp_buf);
896
897     free (*p);
898     *p = NULL;
899 }
900
901 Record rec_get (Records p, int sysno)
902 {
903     int i;
904     Record rec, *recp;
905     struct record_index_entry entry;
906     int r, got;
907     char *nptr;
908
909     assert (sysno > 0);
910     assert (p);
911
912     if ((recp = rec_cache_lookup (p, sysno, 0)))
913         return rec_cp (*recp);
914
915     read_indx (p, sysno, &entry, sizeof(entry));
916     
917     if (!(rec = malloc (sizeof(*rec))))
918     {
919         logf (LOG_FATAL|LOG_ERRNO, "malloc");
920         exit (1);
921     }
922     if (lseek (p->data_fd, entry.u.used.offset, SEEK_SET) == -1) 
923     {
924         logf (LOG_FATAL|LOG_ERRNO, "lseek in %s to pos %ld",
925               p->data_fname, entry.u.used.offset);
926         exit (1);
927     }
928     if (p->tmp_size < entry.u.used.size) 
929     {
930         free (p->tmp_buf);
931         p->tmp_size = entry.u.used.size + 16384;
932         if (!(p->tmp_buf = malloc (p->tmp_size)))
933         {
934             logf (LOG_FATAL|LOG_ERRNO, "malloc");
935             exit (1);
936         }
937     }
938     for (got = 0; got < entry.u.used.size; got += r)
939     {
940         r = read (p->data_fd, p->tmp_buf + got, entry.u.used.size - got);
941         if (r <= 0)
942         {
943             logf (LOG_FATAL|LOG_ERRNO, "read of %s", p->data_fname);
944             exit (1);
945         }
946     }
947     rec->sysno = sysno;
948
949     nptr = p->tmp_buf;
950     for (i = 0; i < REC_NO_INFO; i++)
951     {
952         memcpy (&rec->size[i], nptr, sizeof(*rec->size));
953         nptr += sizeof(*rec->size);
954         if (rec->size[i])
955         {
956             rec->info[i] = malloc (rec->size[i]);
957             memcpy (rec->info[i], nptr, rec->size[i]);
958             nptr += rec->size[i];
959         }
960         else
961             rec->info[i] = NULL;
962     }
963     rec_cache_insert (p, rec, 0);
964     return rec;
965 }
966
967 Record rec_new (Records p)
968 {
969     int sysno, i;
970     Record rec;
971
972     assert (p);
973     if (!(rec = malloc (sizeof(*rec))))
974     {
975         logf (LOG_FATAL|LOG_ERRNO, "malloc");
976         exit (1);
977     }
978     if (p->head.index_free == 0)
979         sysno = (p->head.index_last)++;
980     else
981     {
982         struct record_index_entry entry;
983
984         read_indx (p, p->head.index_free, &entry, sizeof(entry));
985         sysno = p->head.index_free;
986         p->head.index_free = entry.u.free.next;
987     }
988     (p->head.no_records)++;
989     rec->sysno = sysno;
990     for (i = 0; i < REC_NO_INFO; i++)
991     {
992         rec->info[i] = NULL;
993         rec->size[i] = 0;
994     }
995     rec_cache_insert (p, rec, 1);
996     return rec;
997 }
998
999 void rec_put (Records p, Record *recpp)
1000 {
1001     Record *recp;
1002
1003     if ((recp = rec_cache_lookup (p, (*recpp)->sysno, 1)))
1004     {
1005         rec_rm (recp);
1006         *recp = *recpp;
1007     }
1008     else
1009     {
1010         rec_cache_insert (p, *recpp, 1);
1011         rec_rm (recpp);
1012     }
1013     *recpp = NULL;
1014 }
1015
1016 void rec_rm (Record *recpp)
1017 {
1018     int i;
1019     for (i = 0; i < REC_NO_INFO; i++)
1020         free ((*recpp)->info[i]);
1021     free (*recpp);
1022     *recpp = NULL;
1023 }
1024
1025 Record rec_cp (Record rec)
1026 {
1027     Record n;
1028     int i;
1029
1030     if (!(n = malloc (sizeof(*n))))
1031     {
1032         logf (LOG_FATAL|LOG_ERRNO, "malloc");
1033         exit (1);
1034     }
1035     n->sysno = rec->sysno;
1036     for (i = 0; i < REC_NO_INFO; i++)
1037         if (!rec->info[i])
1038         {
1039             n->info[i] = NULL;
1040             n->size[i] = 0;
1041         }
1042         else
1043         {
1044             n->size[i] = rec->size[i];
1045             if (!(n->info[i] = malloc (rec->size[i])))
1046             {
1047                 logf (LOG_FATAL|LOG_ERRNO, "malloc. rec_cp");
1048                 exit (1);
1049             }
1050             memcpy (n->info[i], rec->info[i], rec->size[i]);
1051         }
1052     return n;
1053 }
1054
1055 void rec_del (Records p, Record *recpp)
1056 {
1057     assert (0);
1058 }
1059
1060
1061 #endif
1062
1063 char *rec_strdup (const char *s, size_t *len)
1064 {
1065     char *p;
1066
1067     if (!s)
1068     {
1069         *len = 0;
1070         return NULL;
1071     }
1072     *len = strlen(s)+1;
1073     p = malloc (*len);
1074     if (!p)
1075     {
1076         logf (LOG_FATAL|LOG_ERRNO, "malloc");
1077         exit (1);
1078     }
1079     strcpy (p, s);
1080     return p;
1081 }
1082