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