Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / tt / mini_isam / isvarrec.c
1 /*%%  (c) Copyright 1993, 1994 Hewlett-Packard Company                   */
2 /*%%  (c) Copyright 1993, 1994 International Business Machines Corp.     */
3 /*%%  (c) Copyright 1993, 1994 Sun Microsystems, Inc.                    */
4 /*%%  (c) Copyright 1993, 1994 Novell, Inc.                              */
5 /*%%  $XConsortium: isvarrec.c /main/3 1995/10/23 11:45:36 rswiston $                                                    */
6 #ifndef lint
7 static char sccsid[] = "@(#)isvarrec.c 1.8 89/07/17 Copyr 1988 Sun Micro";
8 #endif
9 /*
10  * Copyright (c) 1988 by Sun Microsystems, Inc.
11  */
12
13 /*
14  * isvarrec.c
15  *
16  * Description:
17  *      Fixed length record access (VLRA) module.
18  */
19
20 #include "isam_impl.h"
21
22 /* Local functions */
23 long _vl_getpos();                           /* Get offset in .rec file */
24 int  _vl_deleted();                          /* 0/1 returns 1 if record is deleted */
25 static void remove_from_chain2();            /* used by _vlrec_wrrec() */
26 long _istail_insert();
27 static void _istail_delete();
28 static _istail_read();
29
30 /*
31  * _vlrec_write(fcb, record, recnum, reclen)
32  *
33  * Write a record.
34  *
35  * Input params:
36  *      FCB     File Control Block
37  *      record  record buffer
38  *      reclen  record length (NOT USED)
39  *
40  * Output params:
41  *      recnum  record number of the new record
42  *
43  * Returns 0 if record was written succesfully, or -1 if any error.
44  */
45
46 /*ARGSUSED*/
47 int
48 _vlrec_write(fcb, record, recnum, reclen)
49     register Fcb        *fcb;
50     char                *record;
51     Recno               *recnum;
52     int                 reclen;
53 {
54     Recno               recnum2;
55     long                rec_position;
56     long                tailoff = VL_RECNOTAIL;
57     char                recnobuf [RECNOSIZE];
58     char                tailoffbuf[LONGSIZE];
59
60     /*
61      * Reuse a deleted record if one exits.
62      * Otherwise, extend .rec file by a record.
63      */
64     if (fcb->freerecno != NULL_RECNO) {
65         recnum2 = fcb->freerecno;
66
67         /*
68          * Remove record from the chain of deleted records.
69          */
70         rec_position = _vl_getpos(fcb, recnum2); /* Offset in .rec file */
71         _cp_fromfile(fcb, fcb->datfd, recnobuf, rec_position + LONGSIZE, RECNOSIZE);
72         fcb->freerecno = ldrecno(recnobuf);
73     }
74     else {
75         recnum2 = ++(fcb->lastrecno);
76
77         /* 
78          * Extend .rec file size if necessary.
79          */
80         while (_vl_getpos(fcb, recnum2 + 1) > fcb->datsize * ISPAGESIZE) {
81             fcb->datsize = _extend_file(fcb, fcb->datfd, fcb->datsize);
82         }
83         rec_position = _vl_getpos(fcb, recnum2); /* Offset in .rec file */
84     }
85
86     /* 
87      * Store variable part of record (the 'tail') in .var file.
88      */
89     tailoff = _istail_insert(fcb, record + fcb->minreclen, 
90                              reclen - fcb->minreclen);
91
92     /*
93      * Copy record to the .rec file. Mark record as undeleted.
94      */
95     stlong(tailoff, tailoffbuf);
96     _cp_tofile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE); 
97     _cp_tofile(fcb, fcb->datfd, record, rec_position + LONGSIZE, fcb->minreclen); 
98
99     *recnum = recnum2;
100
101     return (ISOK);
102 }
103
104 /*
105  * _vlrec_read(fcb, record, recnum, reclen)
106  *
107  * Read a record.
108  *
109  * Input params:
110  *      FCB     File Control Block
111  *      recnum  record number of the record
112  *      reclen  filled with the record size for compatibilty with
113  *              variable length records
114  *
115  * Output params:
116  *      record  record buffer is filled with data
117  *
118  * Returns 0 if record was read succesfully, or error code if any error.
119  */
120
121 int
122 _vlrec_read(fcb, record, recnum, reclen)
123     register Fcb        *fcb;
124     char                *record;
125     Recno               recnum;
126     int                 *reclen;
127 {
128     long                rec_position;
129     long                tailoff;
130     char                tailoffbuf[LONGSIZE];
131
132     /*
133      * Check that recnum is within the range of existing record numbers.
134      */
135     if (recnum < 1 || recnum > fcb->lastrecno)
136         return (EENDFILE);
137
138     rec_position = _vl_getpos(fcb, recnum); /* Offset in .rec file */
139
140     /*
141      * Check that the record is not marked as deleted.
142      */
143     _cp_fromfile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE);
144     tailoff = ldlong(tailoffbuf);
145     if (tailoff == VL_RECDELETED) {
146         return (ENOREC);
147     }
148
149     /*
150      * Copy record from the .at file. 
151      */
152     _cp_fromfile(fcb, fcb->datfd, record, rec_position + LONGSIZE, fcb->minreclen); 
153
154     *reclen = fcb->minreclen;
155
156     /*
157      * Get the 'tail' of the record if any.
158      */
159     *reclen += _istail_read(fcb, tailoff, record + fcb->minreclen);
160
161     if (*reclen > fcb->maxreclen)
162         _isfatal_error("Corrupted file: too long variable length record");
163
164     return (ISOK);
165 }
166
167 /*
168  * pos = _vl_getpos(fcb, recnum)
169  *
170  * Calculate the position of record in .rec file.
171  */
172
173 long
174 _vl_getpos(fcb, recnum)
175     Fcb                 *fcb;
176     Recno               recnum;
177 {
178     return ((long)(ISCNTLSIZE + (fcb->minreclen + LONGSIZE) * (recnum -1)));
179 }
180
181 /*
182  * _vlrec_rewrite(fcb, record, recnum, reclen)
183  *
184  * Rewrite a record.
185  *
186  * Input params:
187  *      FCB     File Control Block
188  *      recnum  record number of the record
189  *      record  new record
190  *      int     reclen (NOT USED)
191  *
192  * Returns 0 if record was rewritten succesfully, or error code if any error.
193  */
194
195 /*ARGSUSED*/
196 int
197 _vlrec_rewrite(fcb, record, recnum, reclen)
198     register Fcb        *fcb;
199     char                *record;
200     Recno               recnum;
201     int                 reclen;
202 {
203     long                rec_position;
204     long                tailoff;
205     char                tailoffbuf[LONGSIZE];
206
207     /*
208      * Check that recnum is within the range of existing record numbers.
209      */
210     if (recnum < 1 || recnum > fcb->lastrecno)
211         return (EENDFILE);
212
213     rec_position = _vl_getpos(fcb, recnum); /* Offset in .rec file */
214
215     /*
216      * Check that the record is not marked as deleted.
217      */
218     _cp_fromfile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE); 
219     tailoff = ldlong(tailoffbuf);
220     if (tailoff == VL_RECDELETED) {
221         return (ENOREC);
222     }
223
224     /* 
225      * Store variable part of record (the 'tail') in .var file.
226      */
227     tailoff = _istail_modify(fcb, tailoff, record + fcb->minreclen, 
228                              reclen - fcb->minreclen);
229
230     /*
231      * Copy new record to the .rec file. 
232      */
233     stlong(tailoff, tailoffbuf);
234     _cp_tofile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE);
235     _cp_tofile(fcb, fcb->datfd, record, rec_position + LONGSIZE, fcb->minreclen); 
236
237     return (ISOK);
238 }
239
240 /*
241  * _vlrec_delete;(fcb, recnum)
242  *
243  * Rewrite a record.
244  *
245  * Input params:
246  *      FCB     File Control Block
247  *      recnum  record number of the record
248  *
249  * Returns 0 if record was rewritten succesfully, or error code if any error.
250  */
251
252 int
253 _vlrec_delete(fcb, recnum)
254     register Fcb        *fcb;
255     Recno               recnum;
256 {
257     long                rec_position;
258     long                tailoff;
259     char                tailoffbuf[LONGSIZE];
260     char                recnobuf [RECNOSIZE];
261
262     /*
263      * Check that recnum is within the range of existing record numbers.
264      */
265     if (recnum < 1 || recnum > fcb->lastrecno)
266         return (EENDFILE);
267
268     rec_position = _vl_getpos(fcb, recnum); /* Offset in .rec file */
269
270     /*
271      * Check that the record is not marked as deleted.
272      */
273     _cp_fromfile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE); 
274     tailoff = ldlong(tailoffbuf);
275     if (tailoff == VL_RECDELETED) {
276         return (ENOREC);
277     }
278
279     /*
280      * Set the delete flag to VL_RECDELETED.
281      */
282     tailoff = VL_RECDELETED;
283     stlong(tailoff, tailoffbuf);
284     _cp_tofile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE);
285
286     /*
287      * Insert record into chain of deleted records.
288      */
289     strecno(fcb->freerecno, recnobuf);
290     _cp_tofile(fcb, fcb->datfd, recnobuf, rec_position + LONGSIZE, RECNOSIZE);
291     fcb->freerecno = recnum;
292
293     /*
294      * Delete tail from .var file.
295      */
296
297     return (ISOK);
298 }
299
300 /*
301  * _vlrec_wrrec(fcb, record, recnum, reclen)
302  *
303  * Write a record by record number.
304  *
305  * Input params:
306  *      FCB     File Control Block
307  *      recnum  record number of the record
308  *      record  record buffer
309  *      int     reclen (NOT USED)
310  *
311  * Returns 0 if record was written succesfully, or error code if any error.
312  *
313  * Note that _vlrec_wrrec() commits updates and syncs the FCB to avoid
314  *      buffer pool overflow.
315  */
316
317 /*ARGSUSED*/
318 int
319 _vlrec_wrrec(fcb, record, recnum, reclen)
320     register Fcb        *fcb;
321     char                *record;
322     Recno               recnum;
323     int                 reclen;
324 {
325     long                rec_position;
326     long                tailoff;
327     char                tailoffbuf[LONGSIZE];
328     Recno               recnum2;
329     char                recnumbuf [RECNOSIZE];
330
331     /*
332      * Check that recnum is not negative.
333      */
334     if (recnum < 1)
335         return (EBADARG);
336
337     rec_position = _vl_getpos(fcb, recnum); /* Offset in .rec file */
338
339     if (recnum > fcb->lastrecno)  {
340
341         /*
342          * If the recnum is bigger than the highest record number in the .rec
343          * file, extend the .rec file.
344          */
345         while (_vl_getpos(fcb, recnum + 1) > fcb->datsize * ISPAGESIZE) {
346             fcb->datsize = _extend_file(fcb, fcb->datfd, fcb->datsize);
347             
348             /* Sync the updates to avoid buffer pool overflow. */
349             _isdisk_commit();
350             _isdisk_sync();
351             (void)_isfcb_cntlpg_w2(fcb);
352         }
353
354         /*
355          * Mark all records in the range <fcb->lastrecno+1, recnum>  as
356          * deleted.
357          */
358         tailoff = VL_RECDELETED;
359         stlong(tailoff, tailoffbuf);
360         for (recnum2 = fcb->lastrecno + 1; recnum2 <= recnum; recnum2++) {      
361             _cp_tofile(fcb, fcb->datfd, tailoffbuf, _vl_getpos(fcb, recnum2), LONGSIZE); 
362             strecno(fcb->freerecno, recnumbuf);
363             _cp_tofile(fcb, fcb->datfd, recnumbuf,
364                        _vl_getpos(fcb, recnum2) + LONGSIZE, RECNOSIZE); 
365             fcb->freerecno = recnum2;
366             
367             /* Sync the updates to avoid buffer pool overflow. */
368             _isdisk_commit();
369             _isdisk_sync();
370             fcb->lastrecno = recnum;
371             (void)_isfcb_cntlpg_w2(fcb);
372         }
373         
374         /*
375          * Note that the disk structures are in a consistent state now,
376          * the .rec was extended by a few records marked as 'deleted'.
377          * This is important for subsequent rollbacks.
378          */
379     } 
380
381     /*
382      * If recnum specifies a record that has existed, check whether it
383      * has been deleted. _vlrec_wrrec() does not override existing record.
384      */
385     _cp_fromfile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE); 
386     tailoff = ldlong(tailoffbuf);
387     if (tailoff != VL_RECDELETED) {
388         return (EDUPL);
389     }
390
391     /*
392      * Remove the record from the chain of deleted records.
393      */
394     remove_from_chain2(fcb, recnum);
395
396     /* 
397      * Store variable part of record (the 'tail') in .var file.
398      */
399     tailoff = _istail_insert(fcb, record + fcb->minreclen, reclen - fcb->minreclen);
400
401     /*
402      * Copy new record to the .rec file. 
403      */
404     stlong(tailoff, tailoffbuf);
405     _cp_tofile(fcb, fcb->datfd, tailoffbuf, rec_position, LONGSIZE); 
406     _cp_tofile(fcb, fcb->datfd, record, rec_position + LONGSIZE, fcb->minreclen); 
407
408     return (ISOK);
409 }
410
411 /*
412  * remove_from_chain(fcb, recnum)
413  *
414  * Remove record from the chain of deleted records.
415  */
416
417 static void
418 remove_from_chain2(fcb, recnum)
419     Fcb                 *fcb;
420     Recno               recnum;
421 {
422     char                recnobuf1 [RECNOSIZE] , recnobuf2 [RECNOSIZE];
423     long                pos1, pos2;
424     Recno               recnum2;
425
426     pos1 = _vl_getpos(fcb, recnum);
427     _cp_fromfile(fcb, fcb->datfd, recnobuf1, pos1 + LONGSIZE, RECNOSIZE); 
428
429     if (fcb->freerecno == recnum) {
430         fcb->freerecno = ldrecno(recnobuf1);
431     }
432     else {
433         recnum2 = fcb->freerecno;
434         do {
435             pos2 = _vl_getpos(fcb, recnum2);
436             _cp_fromfile(fcb, fcb->datfd, recnobuf2, pos2 + LONGSIZE, RECNOSIZE); 
437             recnum2 = ldrecno(recnobuf2);
438         } while (recnum2 != recnum && recnum2 != NULL_RECNO);
439
440         _cp_tofile(fcb, fcb->datfd, recnobuf1, pos2 + LONGSIZE, RECNOSIZE); 
441     }
442 }
443
444
445 /*
446  * The following are functions that manipulate the 'tails' of variable
447  * records.  The tail is the actual record with the fixed part removed
448  * (fixed part starts at offset zero and its length is minimum record 
449  * length.  The tails are stored in the .var file.
450  */
451
452
453 /* Insert tail into .var file. Return offset in .var file */
454
455 long _istail_insert(fcb, tailp, taillen)
456     Fcb                 *fcb;
457     char                *tailp;
458     int                 taillen;
459 {
460     char                frameheadbuf [2 * SHORTSIZE];
461     int                 framelen;
462     long                offset;
463
464 /*    printf ("_insert called, taillen %d\n", taillen); */
465
466     if (taillen == 0)
467         return (VL_RECNOTAIL);
468
469     framelen = taillen + 2 * SHORTSIZE;
470
471     /*
472      * Set up frame header.
473      */
474     stshort((short)taillen, frameheadbuf + VR_FRAMELEN_OFF);
475     stshort((short)taillen, frameheadbuf + VR_TAILLEN_OFF);
476
477     offset = fcb->varend;
478
479     /*
480      * Extend .var file if that is necesary.    
481      */
482     while (offset + framelen > fcb->varsize * ISPAGESIZE)
483         fcb->varsize = _extend_file(fcb, fcb->varfd, fcb->varsize);
484
485     /*
486      * Copy frame head and tail to .var file.
487      */
488     _cp_tofile(fcb, fcb->varfd, frameheadbuf, offset, 2 * SHORTSIZE);
489     _cp_tofile(fcb, fcb->varfd, tailp, offset + 2 * SHORTSIZE, taillen);
490  
491     fcb->varend += taillen + 2 * SHORTSIZE;
492
493     return (offset);
494 }
495
496 /* Remove tail from .var file */
497
498 /* ARGSUSED */
499 Static void _istail_delete(fcb, offset)
500     Fcb                 *fcb;
501     long                offset;
502 {
503     /* 
504      * Don't do anything in NetISAM 1.0. The tails are lost, the space
505      * will be re-used after next restructuring: "copy -c file" command
506      */
507     return;
508 }
509
510 /* Read tail from .var file */
511
512 Static _istail_read(fcb, offset, buffer)
513     Fcb                 *fcb;
514     long                offset;
515     char                *buffer;
516 {
517     char                frameheadbuf [2 * SHORTSIZE];
518     int                 taillen;
519
520 /*    printf ("_read called, offset %d\n", offset); */
521
522     if (offset == VL_RECNOTAIL)
523         return (0);
524
525     /* 
526      * Read frame header.
527      */
528      _cp_fromfile(fcb, fcb->varfd, frameheadbuf, offset, 2 * SHORTSIZE);
529     taillen = ldshort(frameheadbuf + VR_TAILLEN_OFF);
530      _cp_fromfile(fcb, fcb->varfd, buffer, offset + 2 * SHORTSIZE, taillen);
531     
532     return (taillen);
533 }
534     
535 /* Rewrite tail. Returns -1 if the new tail is longer than the original frame */
536
537 int _istail_modify(fcb, offset, tailp, taillen)
538     Fcb                 *fcb;
539     long                offset;
540     int                 taillen;
541     char                *tailp;
542 {
543     char                frameheadbuf [2 * SHORTSIZE];
544     int                 framelen;
545
546     /*
547      * Trivial case: no old frame, no new tail.
548      */
549     if (offset == VL_RECNOTAIL && taillen == 0)
550         return (offset);
551
552     if (offset != VL_RECNOTAIL) {
553         /* 
554          * Read frame header.
555          */
556         _cp_fromfile(fcb, fcb->varfd, frameheadbuf, offset, 2 * SHORTSIZE);
557         framelen = ldshort(frameheadbuf + VR_FRAMELEN_OFF);
558     }
559     else
560         framelen = 0;
561
562     if (taillen > framelen) {
563         /* 
564          * Delete the old frame if the new tail does not fit.
565          * Insert the new tail at the end of .var file.
566          */
567
568         _istail_delete(fcb, offset);
569         return (_istail_insert(fcb, tailp, taillen));
570     }
571     else {
572         /*
573          * The new tail fits in the existing frame.
574          */
575         stshort((short)taillen, frameheadbuf + VR_TAILLEN_OFF);
576         _cp_tofile(fcb, fcb->varfd, frameheadbuf, offset, 2 * SHORTSIZE);
577         _cp_tofile(fcb, fcb->varfd, tailp, offset + 2 * SHORTSIZE, taillen);
578
579         return (offset);
580     }
581 }