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