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