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