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