Version:
~ [ 10.0 ] ~
** Warning: Cannot open xref database.
1
2 /* $Id: db.c,v 1.6 2005/01/11 22:48:05 andreradke Exp $ */
3
4 /******************************************************************************
5
6 UserLand Frontier(tm) -- High performance Web content management,
7 object database, system-level and Internet scripting environment,
8 including source code editing and debugging.
9
10 Copyright (C) 1992-2004 UserLand Software, Inc.
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program; if not, write to the Free Software
24 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
26 ******************************************************************************/
27
28 #include "frontier.h"
29 #include "standard.h"
30
31 #include "memory.h"
32 #include "cursor.h"
33 #include "dialogs.h"
34 #include "error.h"
35 #include "file.h"
36 #include "resources.h"
37 #include "strings.h"
38 #include "shell.h"
39 #include "db.h"
40 #include "dbinternal.h"
41 #include "ops.h" //6.2b3 AR: for numbertostring
42
43 #include "frontierdebug.h" //6.2b7 AR
44
45 #define dberrorlist 256
46
47 #define setdirty(hdb) ((**hdb).flags |= dbdirtymask)
48 #define cleardirty(hdb) ((**hdb).flags &= ~dbdirtymask)
49 #define isdirty(hdb) ((**hdb).flags & dbdirtymask)
50
51 #define majorversion(v) (v & 0x00f0)
52 #define minorversion(v) (v & 0x000f)
53
54 typedef enum {
55
56 dbnoerror,
57
58 dbwrongversionerror,
59
60 dbfreeblockerror,
61
62 dbfreelisterror,
63
64 dbinconsistentavaillisterror,
65
66 dbassignfreeblockerror,
67
68 dbfilesizeerror,
69
70 dbreleasefreeblockerror,
71
72 dbreleaseinvalidblockerror,
73
74 dbmergeinvalidblockerror
75 } tydberror;
76
77
78 hdldatabaserecord databasedata; /*the global database handle*/
79
80 #if flruntime
81
82 #define fldatabasesaveas false
83
84 #else
85
86 boolean fldatabasesaveas = false; /*only true during Save As operation*/
87
88 #endif
89
90
91 static hdldatabaserecord databasedestination; /*for Save As*/
92
93 #if fldebug
94
95 static long leftmerges = 0, rightmerges = 0; /*statistics*/
96
97 static long splits = 0, nonsplits = 0; /*more statistics*/
98
99 static long allocs = 0, newallocs = 0, allocloops = 0; /*more statistics*/
100
101 #endif
102
103
104 #ifdef DATABASE_DEBUG
105
106 #pragma message ("*********************** DATABASE_DEBUG is ON: output to dblog.txt ***********************")
107
108 #define dberror(num) debug_dberror(num, __LINE__, true)
109 #define dblogerror(num) debug_dberror(num, __LINE__, false)
110 #define dbseteof(eof) debug_dbseteof(eof, __LINE__)
111
112
113 static boolean DBTRACKERGETROOTVISIT (WindowPtr w, bigstring bsfile) {
114
115 hdlwindowinfo hinfo;
116
117 if (getwindowinfo (w, &hinfo))
118 if ((long) (**hinfo).fnum == (**databasedata).fnumdatabase) {
119
120 copystring (fsname(&(**hinfo).fspec), bsfile);
121
122 return (false); /*terminate visiting*/
123 }
124
125 return (true);
126 }/*getrootnamevisit*/
127
128
129 static void DBTRACKERGETFILENAME (bigstring bsfile) {
130
131 if (shellvisittypedwindows (idcancoonconfig, &DBTRACKERGETROOTVISIT, bsfile))
132 setemptystring (bsfile); /*nothing found*/
133
134 }/*DBTRACKERGETFILENAME*/
135
136
137 static void debug_dberror (short errnum, int line, boolean flthrow) {
138
139 bigstring bs, bsfile;
140 char str[1024];
141
142 getstringlist (dberrorlist, errnum, bs);
143
144 DBTRACKERGETFILENAME (bsfile);
145
146 sprintf (str, "%s | %s [db.c,%ld]", stringbaseaddress (bsfile), stringbaseaddress (bs), line);
147
148 DB_MSG_1 (str);
149
150 if (flthrow)
151 shellerrormessage (bs);
152 } /*debug_dberror*/
153
154
155 static boolean debug_dbseteof (long eof, long line) {
156
157 const long onegigabyte = 1024L * 1024L * 1024L;
158
159 if (eof >= onegigabyte) {
160
161 long oldeof = nil;
162
163 if (dbgeteof (&oldeof) && (oldeof < onegigabyte)) {
164
165 char str[1024];
166 bigstring bsfile;
167
168 DBTRACKERGETFILENAME (bsfile);
169
170 sprintf (str, "%s | WARNING -- Growing database from %ld to %ld bytes. [db.c,%ld]", stringbaseaddress (bsfile), oldeof, eof, line);
171
172 DB_MSG_1 (str);
173 }
174 }
175
176 if ((eof & 0x80000000L) != 0x00000000L) {
177
178 dberror (dbfilesizeerror);
179
180 return (false); //trying to grow the file beyond 2 GB
181 }
182
183 return (fileseteof ((hdlfilenum)((**databasedata).fnumdatabase), eof));
184 } /*dbseteof*/
185
186 #else
187
188 #define dblogerror(errnum)
189
190 static void dberror (short errnum) {
191
192 bigstring bs;
193
194 getstringlist (dberrorlist, errnum, bs);
195
196 shellerrormessage (bs);
197 } /*dberror*/
198
199 static boolean dbseteof (long eof) {
200
201 if ((eof & 0x80000000L) != 0x00000000L) {
202
203 dberror (dbfilesizeerror);
204
205 return (false); //trying to grow the file beyond 2 GB
206 }
207
208 return (fileseteof ((hdlfilenum)((**databasedata).fnumdatabase), eof));
209 } /*dbseteof*/
210
211 #endif
212
213
214 #define ctdatabasestack 10
215
216 short topdatabasestack = 0;
217
218 hdldatabaserecord databasestack [ctdatabasestack];
219
220
221 static boolean dbrelease (dbaddress); /*6.2b2: Dropped from db.h and declared static*/
222 static boolean dballocate (long databytes, ptrvoid pdata, dbaddress *paddress); /*6.2b14 AR: forward declaration for dbwriteshadowavaillist*/
223
224 boolean dbpushdatabase (hdldatabaserecord hdatabase) {
225
226 /*
227 when you want to temporarily work with a different databaserecord, call this
228 routine, do your stuff and then call dbpopdatabase.
229 */
230
231 if (topdatabasestack >= ctdatabasestack) {
232
233 DebugStr (STR_database_stack_overflow);
234
235 return (false);
236 }
237
238 databasestack [topdatabasestack++] = databasedata;
239
240 if (hdatabase != nil)
241 databasedata = hdatabase;
242
243 return (true);
244 } /*dbpushdatabase*/
245
246
247 boolean dbpopdatabase (void) {
248
249 if (topdatabasestack <= 0)
250 return (false);
251
252 databasedata = databasestack [--topdatabasestack];
253
254 return (true);
255 } /*dbpopdatabase*/
256
257
258 static void dbswapglobals (void) {
259
260 /*
261 used to temporarily set and unset databasedestination as the databasehandle
262 */
263
264 if (fldatabasesaveas) {
265
266 hdldatabaserecord htemp = databasedata;
267
268 databasedata = databasedestination;
269
270 databasedestination = htemp;
271 }
272 } /*dbswapglobals*/
273
274
275 static boolean dbseek (dbaddress adr) {
276
277 return (filesetposition((hdlfilenum)((**databasedata).fnumdatabase), adr));
278 } /*dbseek*/
279
280
281 static boolean dbwrite (dbaddress adr, long ctbytes, ptrvoid pdata) {
282
283 if (!dbseek (adr))
284 return (false);
285
286 return (filewrite ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata));
287 } /*dbwrite*/
288
289
290 static boolean dbread (dbaddress adr, long ctbytes, ptrvoid pdata) {
291
292 if (!dbseek (adr))
293 return (false);
294
295 return (fileread ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata));
296 } /*dbread*/
297
298
299 static boolean dbwriteswap (dbaddress adr, long ctbytes, ptrvoid pdata) {
300 boolean res;
301
302 if (!dbseek (adr))
303 return (false);
304
305 #ifdef WIN32
306 if (ctbytes == sizeof (long))
307 {
308 longswap (*((long *)pdata));
309 }
310
311 if (ctbytes == sizeof(short))
312 {
313 shortswap (*((short*)pdata));
314 }
315 #endif
316
317 res = filewrite ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata);
318
319 #ifdef WIN32
320 if (ctbytes == sizeof (long))
321 {
322 longswap (*((long *)pdata));
323 }
324
325 if (ctbytes == sizeof(short))
326 {
327 shortswap (*((short*)pdata));
328 }
329 #endif
330
331 return (res);
332 } /*dbwrite*/
333
334
335 static boolean dbreadswap (dbaddress adr, long ctbytes, ptrvoid pdata) {
336 boolean res;
337
338 if (!dbseek (adr))
339 return (false);
340
341 res = fileread ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata);
342
343 #ifdef WIN32
344 if (ctbytes == sizeof (long))
345 {
346 longswap (*((long *)pdata));
347 }
348 else
349 {
350 if (ctbytes == sizeof(short))
351 {
352 shortswap (*((short*)pdata));
353 }
354 }
355 #endif
356
357 return (res);
358 } /*dbread*/
359
360
361 boolean dbgeteof (long *eof) {
362
363 return (filegeteof ((hdlfilenum)((**databasedata).fnumdatabase), eof));
364 } /*dbgeteof*/
365
366
367 static void dbheaderdirty (void) {
368
369 /*
370 5.0.1 dmb: was ^= instead of |=. Caused avail list database corruption
371 */
372
373 (**databasedata).flags |= dbdirtymask;
374 } /*dbheaderdirty*/
375
376
377 static boolean dbflushheader (void) {
378
379 /*
380 5.1.5 dmb: copy databasedata to local record for writing
381 */
382
383 register hdldatabaserecord hdb = databasedata;
384 boolean fl;
385 tydatabaserecord diskrec;
386
387 assert (sizeof (diskrec.u.growthspace) >= sizeof (diskrec.u.extensions));
388
389 if (isdirty (hdb)) { /*changes made to header*/
390
391 cleardirty (hdb); /*clear it*/
392
393 diskrec = **hdb;
394
395 #ifdef SMART_DB_OPENING
396 clearbytes (&diskrec.u.extensions.availlistshadow, sizeof (diskrec.u.extensions.availlistshadow)); /*in-memory structure only*/
397
398 clearbytes (&diskrec.u.extensions.flreadonly, sizeof (diskrec.u.extensions.flreadonly)); /*in-memory structure only*/
399 #else
400 clearbytes (&diskrec.u.growthspace, sizeof (diskrec.u.growthspace)); /*in-memory structure only*/
401 #endif
402
403 #ifdef WIN32
404 {
405 short i;
406 longswap (diskrec.availlist);
407 longswap (diskrec.u.extensions.availlistblock);
408 shortswap (diskrec.flags);
409 for (i = 0; i < ctviews; i++)
410 {
411 longswap (diskrec.views[i]);
412 }
413 // longswap (diskrec.fnumdatabase);
414 longswap (diskrec.headerLength);
415 shortswap (diskrec.longversionMajor);
416 shortswap (diskrec.longversionMinor);
417 }
418 #endif
419
420 fl = dbwrite ((dbaddress) 0, sizeof (tydatabaserecord), &diskrec);
421
422 #ifdef WIN95VERSION
423 if (fl)
424 flushvolumechanges (nil, (hdlfilenum)((**databasedata).fnumdatabase));
425 #else
426 /*flush file buffers*/ {
427 IOParam pb;
428
429 clearbytes (&pb, sizeof (pb));
430
431 pb.ioRefNum = (hdlfilenum)((**databasedata).fnumdatabase);
432
433 PBFlushFile ((ParmBlkPtr) &pb, false);
434 }
435 #endif
436
437 return (fl);
438 } /*changes made to header*/
439
440 return (true);
441 } /*dbflushheader*/
442
443
444 boolean dbreadheader (dbaddress adr, boolean *flfree, long *ctbytes, tyvariance *variance) {
445
446 tyheader header;
447
448 if (!dbread (adr, sizeheader, &header))
449 return (false);
450
451 #ifdef WIN32
452 longswap(header.variance);
453 longswap(header.sizefreeword.size);
454 #endif
455
456 *flfree = (header.sizefreeword.size & 0x80000000L) == 0x80000000L ? true : false;
457
458 *ctbytes = header.sizefreeword.size & 0x7FFFFFFFL;
459
460 *variance = header.variance;
461
462 return (true);
463 } /*dbreadheader*/
464
465
466 boolean dbreadtrailer (dbaddress adr, boolean *flfree, long *ctbytes) {
467
468 tytrailer trailer;
469
470 if (!dbread (adr, sizetrailer, &trailer))
471 return (false);
472
473 #ifdef WIN32
474 longswap(trailer.sizefreeword.size);
475 #endif
476
477 *flfree = (trailer.sizefreeword.size & 0x80000000L) == 0x80000000L ? true : false;
478
479 *ctbytes = trailer.sizefreeword.size & 0x7FFFFFFFL;
480
481 return (true);
482 } /*dbreadtrailer*/
483
484
485 static boolean dbwriteheader (dbaddress adr, boolean flfree, long ctbytes, tyvariance variance) {
486
487 tyheader header;
488
489 header.sizefreeword.size = ctbytes;
490
491 if (flfree)
492 header.sizefreeword.size |= 0x80000000L;
493
494 header.variance = variance;
495
496 #ifdef WIN32
497 longswap(header.variance);
498 longswap(header.sizefreeword.size);
499 #endif
500
501 return (dbwrite (adr, sizeheader, &header));
502 } /*dbwriteheader*/
503
504
505 static boolean dbwritetrailer (dbaddress adr, boolean flfree, long ctbytes) {
506
507 tytrailer trailer;
508
509 trailer.sizefreeword.size = ctbytes;
510
511 if (flfree)
512 trailer.sizefreeword.size |= 0x80000000L;
513
514 #ifdef WIN32
515 longswap(trailer.sizefreeword.size);
516 #endif
517
518 return (dbwrite (adr, sizetrailer, &trailer));
519 } /*dbwritetrailer*/
520
521
522 static boolean dbwriteheaderandtrailer (dbaddress adr, boolean flfree, long ctbytes, tyvariance variance) {
523
524 if (!dbwriteheader (adr, flfree, ctbytes, variance))
525 return (false);
526
527 return (dbwritetrailer (adr + sizeheader + ctbytes, flfree, ctbytes));
528 } /*dbwriteheaderandtrailer*/
529
530
531 boolean dbreadavailnode (dbaddress adr, boolean *flfree, long *ctbytes, dbaddress *link) {
532
533 /*
534 each node on the avail list has a link stored in its data field, we get
535 all the usual data from the node and return that link in the nextnomad
536 parameter.
537 */
538
539 tyvariance variance; /*variance is irrelevent in avail nodes*/
540
541 if (!dbreadheader (adr, flfree, ctbytes, &variance))
542 return (false);
543
544 return (dbreadswap (adr + sizeheader, sizeof (dbaddress), link));
545 } /*dbreadavailnode*/
546
547
548 static boolean dbwriteavailnode (dbaddress adr, long ctbytes, dbaddress nextlink) {
549
550 assert (adr != nil);
551
552 assert ((**databasedata).u.extensions.availlistblock == nildbaddress);
553
554 if (!dbwriteheader (adr, true, ctbytes, 0L))
555 return (false);
556
557 if (!dbwriteswap (adr + sizeheader, sizeof (dbaddress), &nextlink))
558 return (false);
559
560 if (!dbwritetrailer (adr + sizeheader + ctbytes, true, ctbytes))
561 return (false);
562
563 return (true);
564 } /*dbwriteavailnode*/
565
566
567 static boolean dbsetavaillink (dbaddress adr, dbaddress link) {
568
569 /*
570 adr points to a record in the database file. move past the header and
571 write the link address in the first four bytes of the block's space.
572 */
573
574 assert ((**databasedata).u.extensions.availlistblock == nildbaddress);
575
576 if (adr == nildbaddress) { /*special case, set link in file header*/
577
578 (**databasedata).availlist = link;
579
580 dbheaderdirty ();
581
582 return (true);
583 }
584
585 return (dbwriteswap (adr + sizeheader, sizeof (dbaddress), &link));
586 } /*dbsetavaillink*/
587
588
589 static boolean dbwritedatablock (dbaddress adr, long databytes, long nodebytes, ptrvoid pdata) {
590
591 /*
592 there might be less data to write than there is room in the
593 block, so we only write as much as is necessary, but we size
594 the block according to its logical size.
595 */
596
597 if (!dbwriteheader (adr, false, nodebytes, (tyvariance) nodebytes - databytes))
598 return (false);
599
600 if (pdata != nil) /*write data bytes*/
601
602 if (!dbwrite (adr + sizeheader, databytes, pdata))
603 return (false);
604
605 return (dbwritetrailer (adr + nodebytes + sizeheader, false, nodebytes));
606 } /*dbwritedatablock*/
607
608
609 static boolean dbfindpreviousavail (dbaddress adr, dbaddress *prev, long *ixshadow) {
610
611 /*
612 the available list is not doubly-linked. this is where we pay the price.
613
614 the caller wants to know which node in the avail list points at it. if
615 its the list header, we return nildbaddress.
616
617 returns true if *prev was correctly set, false otherwise.
618
619 5.1.5 dmb: use in-memory shadow
620 */
621
622 #ifdef dbshadow
623 hdldatabaserecord hdb = databasedata;
624 hdlavaillistshadow havailshadow = (hdlavaillistshadow) (**hdb).u.extensions.availlistshadow.data;
625 long i, ctavail = (**hdb).u.extensions.availlistshadow.eof / sizeof (tyavailnodeshadow);
626
627 for (i = 0; i < ctavail; ++i) {
628
629 if ((*havailshadow) [i].adr == adr) {
630
631 if (i > 0)
632 *prev = (*havailshadow) [i - 1].adr;
633 else
634 *prev = nildbaddress;
635
636 *ixshadow = i;
637
638 return (true);
639 }
640 }
641
642 dblogerror (dbfreelisterror); /*something fishy is going on*/
643
644 return (false);
645 #else
646 dbaddress nomad;
647 boolean flfree;
648 long ctbytes;
649 dbaddress nextnomad;
650
651 nomad = (**databasedata).availlist;
652
653 if (nomad == adr) { /*he's the first guy on the list*/
654
655 *prev = nildbaddress;
656
657 return (true);
658 }
659
660 while (true) {
661
662 if (nomad == nildbaddress) /*reached end of list, no one points at the node*/
663 return (false);
664
665 if (!dbreadavailnode (nomad, &flfree, &ctbytes, &nextnomad))
666 return (false);
667
668 if (nextnomad == adr) { /*found the guy that points at our friend*/
669
670 *prev = nomad;
671
672 return (true);
673 }
674
675 nomad = nextnomad; /*advance to next node*/
676 } /*while*/
677 #endif
678 } /*dbfindpreviousavail*/
679
680
681
682 #ifdef SMART_DB_OPENING
683
684 static void dbclearshadowavaillist (void) {
685
686 /*
687 6.2b12 AR: This function MUST be called before modifying the linked list
688 of available blocks on disk. We reset the pointer to the cached shadow avail list
689 in the database header, set the header's dirty bit, and flush the header to disk.
690
691 Finally, we also release the block allocated for the cached shadow avail list.
692
693 Do nothing if we're performing a Save A Copy or if the db was opened read-only.
694 */
695
696 register hdldatabaserecord hdb = databasedata;
697
698 if (!fldatabasesaveas && !(**hdb).u.extensions.flreadonly)
699
700 if ((**hdb).u.extensions.availlistblock != nildbaddress) {
701
702 dbaddress adrblock = (**hdb).u.extensions.availlistblock;
703
704 (**hdb).u.extensions.availlistblock = nildbaddress;
705
706 dbheaderdirty ();
707
708 dbflushheader ();
709
710 if (!dbrelease (adrblock)) {
711 #ifdef DATABASE_DEBUG
712 char str[256];
713
714 sprintf (str, "dbrelease failed for address %ld.", (**hdb).u.extensions.availlistblock);
715
716 DB_MSG_2 (str);
717 #endif
718 }
719 }
720
721 return;
722 } /*dbclearshadowavaillist*/
723
724
725 static void dbdisposeshadowavaillist (void) {
726
727 register hdldatabaserecord hdb = databasedata;
728 handlestream s;
729
730 assert (hdb != nil);
731
732 s = (**hdb).u.extensions.availlistshadow;
733
734 disposehandlestream (&s);
735
736 clearbytes (&s, sizeof (s));
737
738 (**hdb).u.extensions.availlistshadow = s;
739
740 return;
741 } /*dbdisposeshadowavaillist*/
742
743 #endif
744
745
746 static boolean dbwriteshadowavaillist (void) {
747
748 /*
749 6.2a9 AR: If there's an in-memory shadow avail list, write it to a faked new data block
750 at the end of the database and store a pointer to the block in the file header.
751
752 What happens if the database is opened by a version of Frontier that doesn't know about
753 this convention? When it first saves the database, it will write the db header, thereby
754 destroying the pointer to the on-disk shadow avail list. We end up with an orphaned block
755 in the database, typically a few dozen kB in size. Next time the user saves a copy,
756 the orphaned block is dropped from the database. Not a big deal.
757
758 Also see dbreadshadowavaillist.
759
760 If we don't have write permission, just dispose of everything. Don't even flush the db header.
761
762 6.2b14 AR: Before we call dbwritedatablock, we have to determine the actual size
763 of the allocated block. It could be larger than what we asked for. So we call
764 dbreadheader to update the value of nodebytes to the real thing.
765 */
766
767 register hdldatabaserecord hdb = databasedata;
768 dbaddress adrblock = nil;
769 boolean fl = false;
770 boolean flfree;
771
772 assert (hdb != nil);
773
774 if ((**hdb).u.extensions.flreadonly || fldatabasesaveas)
775 return (true); /*we're done already*/
776
777 dbclearshadowavaillist ();
778
779 if ((**hdb).u.extensions.availlistshadow.data == nil)
780 return (true); /*we're done already*/
781
782 if ((**hdb).u.extensions.availlistshadow.eof > 0) { /*there's something to be saved*/
783
784 long nodebytes = (**hdb).u.extensions.availlistshadow.eof;
785 long databytes, dummy;
786 Handle h = nil;
787
788 if (!dballocate (nodebytes, nil, &adrblock))
789 goto error;
790
791 assert (adrblock != nil);
792
793 closehandlestream (&(**hdb).u.extensions.availlistshadow);
794
795 if (!copyhandle ((**hdb).u.extensions.availlistshadow.data, &h))
796 goto error;
797
798 databytes = gethandlesize (h);
799
800 assert (databytes == nodebytes || databytes == nodebytes - (long) sizeof (tyavailnodeshadow));
801
802 #if 0 //DATABASE_DEBUG //6.2b7 AR: debugging check disabled
803
804 /*verify cached version of avail list*/ {
805
806 tyavailnodeshadow diskavailrec;
807 tyavailnodeshadow memavailrec;
808 dbaddress nextavail;
809 boolean flfree;
810 long dbeof;
811 long ix = 0;
812
813 if (!dbgeteof (&dbeof))
814 goto error;
815
816 diskavailrec.adr = (**hdb).availlist;
817
818 while (diskavailrec.adr != nildbaddress) {
819
820 if (!dbreadavailnode (diskavailrec.adr, &flfree, &diskavailrec.size, &nextavail) ||
821 !flfree ||
822 diskavailrec.adr + diskavailrec.size > dbeof) {
823
824 diskavailrec.adr = nildbaddress;
825
826 assert (false);
827
828 break;
829 }
830
831 memavailrec = ((tyavailnodeshadow*)(*h)) [ix++];
832
833 assert (diskavailrec.adr == memavailrec.adr);
834
835 assert (diskavailrec.size == memavailrec.size);
836
837 assert (ix * sizeof (tyavailnodeshadow) < databytes);
838
839 diskavailrec.adr = nextavail;
840
841 rollbeachball ();
842 } /*while*/
843
844 diskavailrec.size = 0;
845
846 memavailrec = ((tyavailnodeshadow*)(*h)) [ix++];
847
848 assert (diskavailrec.adr == memavailrec.adr);
849
850 assert (diskavailrec.size == memavailrec.size);
851
852 assert (ix * sizeof (tyavailnodeshadow) == databytes);
853 }
854 #endif
855
856 #ifdef WIN32
857 /*switch byte order*/ {
858 long ix;
859 long ct = databytes / sizeof (tyavailnodeshadow);
860 register tyavailnodeshadow* p = (tyavailnodeshadow *) *h;
861
862 for (ix = 0; ix < ct; ix++) {
863 longswap (p[ix].adr);
864 longswap (p[ix].size);
865 }
866 }
867 #endif
868
869 lockhandle (h);
870
871 fl = dbreadheader (adrblock, &flfree, &nodebytes, &dummy);
872
873 assert (databytes <= nodebytes);
874
875 fl = fl && dbwritedatablock (adrblock, databytes, nodebytes, *h);
876
877 unlockhandle (h);
878
879 disposehandle (h);
880
881 if (!fl)
882 goto error;
883 }
884
885 fl = true;
886
887 error:
888
889 (**hdb).u.extensions.availlistblock = fl ? adrblock : nildbaddress;
890
891 setdirty (hdb);
892
893 dbflushheader ();
894
895 return (fl);
896 }/*dbwriteshadowavaillist*/
897
898
899 static boolean dbreadshadowavaillist (void) {
900
901 /*
902 6.2a9 AR: If the availlistblock was set in the database header, go in and
903 read the avail list block from the end of the database instead of chaining
904 thru the linked list of free blocks. Nuke the reference to the availlistblock
905 in the db header asap, so if we crash somewhere down the road, we won't read
906 an inconsistent availlistblock the next time we open the db.
907
908 Also see dbwriteshadowavaillist.
909 */
910
911 register hdldatabaserecord hdb = databasedata;
912 dbaddress adrblock;
913 hdlavaillistshadow h;
914 handlestream s;
915 long dbeof;
916
917 assert (hdb != nil);
918
919 if (!dbgeteof (&dbeof))
920 return (false);
921
922 adrblock = (**hdb).u.extensions.availlistblock;
923
924 if (adrblock == nildbaddress) /*for safety*/
925 return (false);
926
927 if (!dbrefhandle (adrblock, (Handle*) &h))
928 return (false);
929
930 #ifdef WIN32
931 /*switch byte order*/ {
932 long ix;
933 long ct = gethandlesize ((Handle) h) / sizeof (tyavailnodeshadow);
934 register tyavailnodeshadow* p = *h;
935
936 for (ix = 0; ix < ct; ix++) {
937 longswap (p[ix].adr);
938 longswap (p[ix].size);
939 }
940 }
941 #endif
942
943 /*Test consistency of cached shadow avail list*/
944
945 if ((**h).adr != (**hdb).availlist) {
946
947 dblogerror (dbinconsistentavaillisterror);
948
949 disposehandle ((Handle) h);
950
951 return (false);
952 }
953
954 if ((**h).adr != nildbaddress) {
955
956 long availbytes;
957 dbaddress firstavail = (**h).adr;
958 dbaddress nextavail;
959 boolean flfree;
960
961 if (!dbreadavailnode (firstavail, &flfree, &availbytes, &nextavail)
962 || !flfree || firstavail + availbytes > dbeof) {
963
964 dblogerror (dbinconsistentavaillisterror);
965
966 disposehandle ((Handle) h);
967
968 return (false);
969 }
970 }
971
972 openhandlestream ((Handle)h, &s);
973
974 #if 0 //DATABASE_DEBUG //6.2b7 AR: debugging code disabled
975
976 /*verify cached version of avail list*/ {
977
978 tyavailnodeshadow diskavailrec;
979 tyavailnodeshadow memavailrec;
980 dbaddress nextavail;
981 boolean flfree;
982 long ix = 0;
983
984 diskavailrec.adr = (**hdb).availlist;
985
986 while (diskavailrec.adr != nildbaddress) {
987
988 if (!dbreadavailnode (diskavailrec.adr, &flfree, &diskavailrec.size, &nextavail) ||
989 !flfree ||
990 diskavailrec.adr + diskavailrec.size > dbeof) {
991
992 diskavailrec.adr = nildbaddress;
993
994 assert (false);
995
996 break;
997 }
998
999 memavailrec = ((tyavailnodeshadow*)(*s.data)) [ix++];
1000
1001 assert (diskavailrec.adr == memavailrec.adr);
1002
1003 assert (diskavailrec.size == memavailrec.size);
1004
1005 assert (ix * sizeof (tyavailnodeshadow) < s.eof);
1006
1007 diskavailrec.adr = nextavail;
1008
1009 rollbeachball ();
1010 } /*while*/
1011
1012 diskavailrec.size = 0;
1013
1014 memavailrec = ((tyavailnodeshadow*)(*s.data)) [ix++];
1015
1016 assert (diskavailrec.adr == memavailrec.adr);
1017
1018 assert (diskavailrec.size == memavailrec.size);
1019
1020 assert (ix * sizeof (tyavailnodeshadow) == s.eof);
1021 }
1022 #endif
1023
1024 (**hdb).u.extensions.availlistshadow = s;
1025
1026 return (true);
1027 }/*dbreadshadowavaillist*/
1028
1029
1030 static boolean dbshadowavaillist (void) {
1031
1032 /*
1033 5.1.5 dmb: read the entire avail list into memory. the last record in
1034 the shadow array is {0, 0}
1035
1036 6.2a9 AR: streamlined usage of handlestream, no longer keep separate
1037 local havaillist handle around which would interfere with the new
1038 shadow avail list caching in the db. (see dbwriteshadowavaillist)
1039
1040 If dbreadschadowaviallist doesn't succeed, we try the old-fashioned way.
1041 */
1042
1043 handlestream s;
1044 tyavailnodeshadow availrec;
1045 dbaddress nextavail;
1046 boolean flfree;
1047 long dbeof;
1048
1049 #ifdef SMART_DB_OPENING
1050 if ((**databasedata).u.extensions.availlistblock != nildbaddress)
1051 if (dbreadshadowavaillist ())
1052 return (true);
1053 #endif
1054
1055 if (!dbgeteof (&dbeof))
1056 return (false);
1057
1058 openhandlestream (nil, &s);
1059
1060 availrec.adr = (**databasedata).availlist;
1061
1062 while (availrec.adr != nildbaddress) {
1063
1064 if (!dbreadavailnode (availrec.adr, &flfree, &availrec.size, &nextavail) ||
1065 !flfree ||
1066 availrec.adr + availrec.size > dbeof) {
1067
1068 availrec.adr = nildbaddress;
1069
1070 dberror (dbfreelisterror);
1071
1072 break;
1073 }
1074
1075 if (!writehandlestream (&s, &availrec, sizeof (availrec)))
1076 goto error;
1077
1078 availrec.adr = nextavail;
1079
1080 rollbeachball ();
1081 } /*while*/
1082
1083 availrec.size = 0;
1084
1085 if (!writehandlestream (&s, &availrec, sizeof (availrec)))
1086 goto error;
1087
1088 (**databasedata).u.extensions.availlistshadow = s;
1089
1090 return (true);
1091
1092 error:
1093 disposehandlestream (&s);
1094
1095 return (false);
1096 } /*dbshadowavaillist*/
1097
1098
1099 static boolean dbinsertavailshadow (long ixshadow, dbaddress adr, long ctbytes) {
1100
1101 handlestream s = (**databasedata).u.extensions.availlistshadow;
1102 tyavailnodeshadow avail;
1103
1104 assert ((ixshadow >= 0) && (ixshadow <= s.eof / (long) sizeof (tyavailnodeshadow)));
1105
1106 avail.adr = adr;
1107
1108 avail.size = ctbytes;
1109
1110 s.pos = ixshadow * sizeof (tyavailnodeshadow);
1111
1112 if (!mergehandlestreamdata (&s, 0L, &avail, sizeof (avail)))
1113 return (false);
1114
1115 (**databasedata).u.extensions.availlistshadow = s;
1116
1117 return (true);
1118 } /*dbinsertavailshadow*/
1119
1120
1121 static boolean dbdeleteavailshadow (long ixshadow) {
1122
1123 handlestream s = (**databasedata).u.extensions.availlistshadow;
1124
1125 assert ((ixshadow >= 0) && (ixshadow < s.eof / (long) sizeof (tyavailnodeshadow)));
1126
1127 s.pos = ixshadow * sizeof (tyavailnodeshadow);
1128
1129 if (!pullfromhandlestream (&s, sizeof (tyavailnodeshadow), nil))
1130 return (false);
1131
1132 (**databasedata).u.extensions.availlistshadow = s;
1133
1134 return (true);
1135 } /*dbdeleteavailshadow*/
1136
1137
1138 static boolean dbsetavailshadow (long ixshadow, dbaddress adr, long ctbytes) {
1139
1140 handlestream s = (**databasedata).u.extensions.availlistshadow;
1141 tyavailnodeshadow avail;
1142
1143 assert ((ixshadow >= 0) && (ixshadow < s.eof / (long) sizeof (tyavailnodeshadow)));
1144
1145 avail.adr = adr;
1146
1147 avail.size = ctbytes;
1148
1149 s.pos = ixshadow * sizeof (tyavailnodeshadow);
1150
1151 if (!mergehandlestreamdata (&s, sizeof (avail), &avail, sizeof (avail)))
1152 return (false);
1153
1154 (**databasedata).u.extensions.availlistshadow = s;
1155
1156 return (true);
1157 } /*dbsetavailshadow*/
1158
1159
1160 static boolean dbgetsizeandvariance (dbaddress adr, long *size, tyvariance *variance) {
1161
1162 /*
1163 give me the address of a database block and I'll return the number
1164 of bytes it has reserved. the variance is the number of unused bytes
1165 that are the result of block-splitting in allocate.
1166 */
1167
1168 boolean flfree;
1169
1170 return (dbreadheader (adr, &flfree, size, variance));
1171 } /*dbgetsizeandvariance*/
1172
1173
1174 static boolean dbsetsize (dbaddress adr, long size, tyvariance variance) {
1175
1176 return (dbwriteheader (adr, false, size, variance));
1177 } /*dbsetsize*/
1178
1179
1180 boolean dbreference (dbaddress adr, long maxbytes, ptrvoid pdata) {
1181
1182 /*
1183 copy into pdata the database block located at address. the number
1184 of bytes is found in the header/trailer word at the beginning of
1185 the block.
1186
1187 under no circumstances will we read in more than maxbytes. the caller
1188 should supply us with the size of pdata in this argument, it prevents
1189 disastrous overwriting of memory.
1190
1191 each block also records a variance -- the number of extra bytes
1192 due to block-splitting in allocate. we only copy the number of
1193 bytes that actually hold the caller's data, probably saves a little
1194 time, and keeps us from overwriting other important stuff!
1195 */
1196
1197 long ctbytes;
1198 boolean flfree;
1199 tyvariance variance;
1200
1201 if (!dbreadheader (adr, &flfree, &ctbytes, &variance))
1202 return (false);
1203
1204 if (flfree || (ctbytes < 0)) { /*referencing a free node -- probably a bad address*/
1205
1206 dberror (dbfreeblockerror);
1207
1208 return (false);
1209 }
1210
1211 return (dbread (adr + sizeheader, min (maxbytes, ctbytes - (long) variance), pdata));
1212 } /*dbreference*/
1213
1214
1215 boolean dbrefhandle (dbaddress adr, Handle *h) {
1216
1217 /*
1218 copy a block from the database into a handle which we allocate.
1219
1220 the caller must dispose of the handle.
1221
1222 5.0.1 dmb: added freeblock error; don't fail silently
1223 */
1224
1225 register dbaddress a = adr;
1226 register boolean fl;
1227 register Handle hregister;
1228 register long ct;
1229 long ctbytes;
1230 boolean flfree;
1231 tyvariance variance;
1232
1233 *h = nil;
1234
1235 if (a == nildbaddress) /*defensive driving*/
1236 return (false);
1237
1238 if (!dbreadheader (a, &flfree, &ctbytes, &variance))
1239 return (false);
1240
1241 ct = ctbytes - (long) variance;
1242
1243 if (flfree || (ct < 0)) { /*probably a bad address*/
1244
1245 dberror (dbfreeblockerror);
1246
1247 return (false);
1248 }
1249
1250 if (!newclearhandle (ct, h))
1251 return (false);
1252
1253 hregister = *h;
1254
1255 lockhandle (hregister);
1256
1257 fl = dbread (a + sizeheader, ct, *hregister);
1258
1259 unlockhandle (hregister);
1260
1261 return (fl);
1262 } /*dbrefhandle*/
1263
1264
1265 #if 0
1266
1267 static boolean dbrefbytes (dbaddress adr, long ctwanted, ptrvoid pdata) {
1268
1269 /*
1270 copy into pdata the database block located at address. this call
1271 is used when you want fewer than the "natural" number of bytes that
1272 dbreference returns.
1273
1274 5.0.1 dmb: added freeblock error; don't fail silently
1275 */
1276
1277 long ctbytes;
1278 boolean flfree;
1279 tyvariance variance;
1280
1281 if (!dbreadheader (adr, &flfree, &ctbytes, &variance))
1282 return (false);
1283
1284 if (flfree || (ctbytes < 0)) { /*referencing a free node -- probably a bad address*/
1285
1286 dberror (dbfreeblockerror);
1287
1288 return (false);
1289 }
1290
1291 ctwanted = min (ctwanted, ctbytes - (long) variance);
1292
1293 return (dbread (adr + sizeheader, ctwanted, pdata));
1294 } /*dbrefbytes*/
1295
1296 #endif
1297
1298
1299 static boolean dballocate (long databytes, ptrvoid pdata, dbaddress *paddress) {
1300
1301 /*
1302 allocate databytes space in the database. return the database address of the
1303 allocated space in paddress. if allocation error, paddress == nildbaddress.
1304
1305 the caller can supply the address of data to be saved in the database, if its
1306 not nil, we will copy the data into the file before returning.
1307
1308 4.1b9 dmb: removed special case check for nil prevnomad; dbsetavaillink handles
1309 that case.
1310
1311 5.1.5b1 dmb: use and maintain availlist shadow
1312 */
1313
1314 long origeof;
1315 long nodebytes, newnodebytes;
1316 //boolean flfree;
1317 dbaddress nomad, prevnomad, nextnomad;
1318 tyvariance variance;
1319 long smallestinterestingblock;
1320 long ctalloc;
1321
1322 #if fldebug
1323 allocs++;
1324 #endif
1325
1326 #ifdef SMART_DB_OPENING
1327 dbclearshadowavaillist (); /*6.2b12 AR*/
1328 #endif
1329
1330 dbswapglobals (); /*use databasedestination*/
1331
1332 smallestinterestingblock = max (databytes, (long) minblocksize);
1333
1334 #ifdef dbshadow
1335 {
1336 hdldatabaserecord hdb = databasedata;
1337 hdlavaillistshadow havailshadow = (hdlavaillistshadow) (**hdb).u.extensions.availlistshadow.data;
1338 long i, ctavail = (**hdb).u.extensions.availlistshadow.eof / sizeof (tyavailnodeshadow);
1339
1340 for (i = 0, prevnomad = nildbaddress; i < ctavail; ++i, prevnomad = nomad) {
1341
1342 #if fldebug
1343 allocloops++;
1344 #endif
1345
1346 nomad = (*havailshadow) [i].adr;
1347
1348 if (nomad == nildbaddress)
1349 break;
1350
1351 nodebytes = (*havailshadow) [i].size;
1352
1353 if (nodebytes < smallestinterestingblock) //too small to be of interest
1354 continue;
1355
1356 /*found a block to allocate off avail list*/
1357
1358 //if (!dbreadavailnode (nomad, &flfree, &nodebytes, &nextnomad))
1359 // goto failure;
1360
1361 nextnomad = (*havailshadow) [i + 1].adr;
1362
1363 //assert (nodebytes == (*havailshadow) [i].size);
1364
1365 variance = nodebytes - databytes; //how much more we got than what we asked for
1366
1367 if (variance >= (minblocksize + sizeheader + sizetrailer)) { //split into two blocks
1368
1369 newnodebytes = nodebytes - (databytes + sizeheader + sizetrailer);
1370
1371 if (!dbwriteheaderandtrailer (nomad, true, newnodebytes, (tyvariance) 0))
1372 goto failure;
1373
1374 dbsetavailshadow (i, nomad, newnodebytes);
1375
1376 nomad += sizeheader + newnodebytes + sizetrailer;
1377
1378 if (!dbwritedatablock (nomad, databytes, databytes, pdata))
1379 goto failure;
1380
1381 *paddress = nomad; /*use the newly split off block*/
1382
1383
1384 #if fldebug
1385 splits++;
1386 #endif
1387
1388 goto success;
1389 } /*splitting into two blocks*/
1390
1391 if (!dbwritedatablock (nomad, databytes, nodebytes, pdata))
1392 goto failure;
1393
1394
1395 #if fldebug
1396 nonsplits++;
1397 #endif
1398
1399 *paddress = nomad;
1400
1401 dbdeleteavailshadow (i);
1402
1403 if (!dbsetavaillink (prevnomad, nextnomad)) /*unlink node from avail list*/
1404 goto failure;
1405
1406 goto success;
1407 }
1408 }
1409 #else
1410 nomad = (**databasedata).availlist;
1411
1412 prevnomad = nildbaddress; /*no previous node*/
1413
1414 while (nomad != nildbaddress) { /*look at each element on the avail list, first-fit*/
1415
1416 if (!dbreadavailnode (nomad, &flfree, &nodebytes, &nextnomad))
1417 goto failure;
1418
1419 if (nodebytes < smallestinterestingblock) /*too small to be of interest*/
1420 goto nextloop;
1421
1422 /*found a block to allocate off avail list*/
1423
1424 variance = nodebytes - databytes; /*how much more we got than what we asked for*/
1425
1426 if (variance >= (minblocksize + sizeheader + sizetrailer)) { /*split into two blocks*/
1427
1428 newnodebytes = nodebytes - (databytes + sizeheader + sizetrailer);
1429
1430 if (!dbwriteheaderandtrailer (nomad, true, newnodebytes, (tyvariance) 0))
1431 goto failure;
1432
1433 nomad += sizeheader + newnodebytes + sizetrailer;
1434
1435 if (!dbwritedatablock (nomad, databytes, databytes, pdata))
1436 goto failure;
1437
1438 *paddress = nomad; /*use the newly split off block*/
1439
1440
1441 #if fldebug
1442 splits++;
1443 #endif
1444
1445 goto success;
1446 } /*splitting into two blocks*/
1447
1448 if (!dbwritedatablock (nomad, databytes, nodebytes, pdata))
1449 goto failure;
1450
1451 #if fldebug
1452 nonsplits++;
1453 #endif
1454
1455 *paddress = nomad;
1456
1457 if (!dbsetavaillink (prevnomad, nextnomad)) /*unlink node from avail list*/
1458 goto failure;
1459
1460 goto success;
1461
1462 nextloop:
1463
1464 prevnomad = nomad; /*remember in case we have to unlink the next one*/
1465
1466 nomad = nextnomad; /*advance to next node in the avail list*/
1467 } /*while*/
1468 #endif
1469
1470 #if fldebug
1471 newallocs++;
1472 #endif
1473
1474 if (!dbgeteof (&origeof))
1475 goto failure;
1476
1477 if (databytes < minblocksize) { /*we never alloc a block smaller than minblocksize*/
1478
1479 ctalloc = minblocksize;
1480
1481 variance = minblocksize - databytes;
1482 }
1483 else {
1484
1485 ctalloc = databytes;
1486
1487 variance = 0;
1488 }
1489
1490 if (!dbseteof (origeof + sizeheader + ctalloc + sizetrailer))
1491 goto failure;
1492
1493 if (!dbwritedatablock (origeof, databytes, ctalloc, pdata))
1494 goto failure;
1495
1496 *paddress = origeof; /*this is the address of the block we allocated*/
1497
1498
1499 success:
1500
1501 dbswapglobals (); /*restore*/
1502
1503 return (true); /*the allocation was successful*/
1504
1505
1506 failure:
1507
1508 dbswapglobals (); /*restore*/
1509
1510 return (false);
1511 } /*dballocate*/
1512
1513
1514 static boolean dbmergeleft (boolean flmerged, dbaddress adr, boolean* ptrflmergedleft) {
1515
1516 /*
1517 try to merge the database block pointed to by adr with the block to its
1518 left. this often may not be possible because the block to the left may
1519 or may not be free, or even may not exist. return true if it worked,
1520 false otherwise.
1521
1522 we do nothing to the avail list if we merge. we assume that the free
1523 block to the left is already on the avail list.
1524
1525 flmerged tells us whether a right-merge has already been performed. if
1526 so, the node at adr is on the available list and must be popped off the
1527 list if a left-merge takes place.
1528
1529 5.1.5b1 dmb: maintain availlist shadow
1530 */
1531
1532 dbaddress newadr;
1533 long newsize;
1534 boolean flfree, flleftfree;
1535 long ctbytes, ctleftbytes;
1536 dbaddress nextavail, prevavail;
1537 long ixshadow;
1538
1539 *ptrflmergedleft = false; /*default return value*/
1540
1541 if (adr == firstphysicaladdress) /*nothing to the left, other than the header!*/
1542 return (true);
1543
1544 if (adr < firstphysicaladdress) { /*nothing to the left, other than the header!*/
1545
1546 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1547
1548 return (false);
1549 }
1550
1551 if (!dbreadtrailer (adr - sizetrailer, &flleftfree, &ctleftbytes))
1552 return (false);
1553
1554 if (ctleftbytes < minblocksize) { /*probably an invalid block*/
1555
1556 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1557
1558 return (false);
1559 }
1560
1561 if (!flleftfree) /*can't merge if block to left is not free*/
1562 return (true);
1563
1564 #ifdef fldebug //DATABASE_DEBUG
1565 {
1566 long leftblockadr = adr - sizetrailer - ctleftbytes - sizeheader;
1567 long dbeof;
1568 boolean flfreeheader;
1569 long ctbytesheader;
1570 tyvariance variance;
1571
1572 if (!dbgeteof (&dbeof))
1573 return (false);
1574
1575 if (leftblockadr < firstphysicaladdress) {
1576
1577 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1578
1579 return (false);
1580 }
1581
1582 if (leftblockadr > dbeof) {
1583
1584 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1585
1586 return (false);
1587 }
1588
1589 if (!dbreadheader (leftblockadr, &flfreeheader, &ctbytesheader, &variance))
1590 return (false);
1591
1592 if (!flfreeheader) { /*the trailer said otherwise!*/
1593
1594 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1595
1596 return (false);
1597 }
1598
1599 if (ctbytesheader != ctleftbytes) {
1600
1601 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1602
1603 return (false);
1604 }
1605 }
1606 #endif
1607
1608 if (!dbreadavailnode (adr, &flfree, &ctbytes, &nextavail)) /*get data about our block*/
1609 return (false);
1610
1611 if (flmerged) { /*the node we're releasing is already on the avail list, pop him!*/
1612
1613 if (!dbfindpreviousavail (adr, &prevavail, &ixshadow))
1614 return (false); /*damaged free list*/
1615
1616 assert ((*(hdlavaillistshadow)(**databasedata).u.extensions.availlistshadow.data) [ixshadow + 1].adr == nextavail);
1617
1618 if (!dbsetavaillink (prevavail, nextavail)) /*point around the soon-to-be-defunct node*/
1619 return (false);
1620
1621 if (!dbdeleteavailshadow (ixshadow))
1622 return (false);
1623 }
1624
1625 newsize = ctleftbytes + ctbytes + sizeheader + sizetrailer; /*start merging*/
1626
1627 newadr = adr - sizetrailer - ctleftbytes - sizeheader;
1628
1629 if (!dbwriteheaderandtrailer (newadr, true, newsize, 0L)) //avail link already set
1630 return (false);
1631
1632 if (!dbfindpreviousavail (newadr, &prevavail, &ixshadow)) // don't need prev, just index
1633 return (false);
1634
1635 dbsetavailshadow (ixshadow, newadr, newsize);
1636
1637 #if fldebug
1638 leftmerges++;
1639 #endif
1640
1641 *ptrflmergedleft = true; /*actually merged*/
1642
1643 return (true);
1644 } /*dbmergeleft*/
1645
1646
1647 static boolean dbmergeright (dbaddress adr, long ctbytes, boolean* ptrflmergedright) {
1648
1649 /*
1650 try to merge the database block pointed to by adr with the block to its
1651 right. this often may not be possible because the block to the right may
1652 or may not be free, or even may not exist. return true if we merged,
1653 false otherwise.
1654
1655 we also adjust the available list if we merge. it must point at the
1656 beginning of the two merged blocks.
1657
1658 we don't need to change the address because even if we merge, the address
1659 of the merged blocks is the same as adr.
1660
1661 5.1.5b1 dmb: take ctbytes parameter so we don't need to re-read header;
1662 maintain availlist shadow
1663
1664 6.2b5 AR: Do writes for merged block sequentially
1665 */
1666
1667 long eof;
1668 dbaddress rightblockadr;
1669 boolean flrightfree;
1670 long ctrightbytes;
1671 dbaddress prevavail, nextavail;
1672 long ixshadow;
1673
1674 *ptrflmergedright = false;
1675
1676 rightblockadr = adr + sizeheader + ctbytes + sizetrailer;
1677
1678 if (!dbgeteof (&eof))
1679 return (false);
1680
1681 if (rightblockadr == eof) /*there is no block to the right*/
1682 return (true);
1683
1684 if (rightblockadr > eof) { /*reached the end of the file*/
1685
1686 dblogerror (dbmergeinvalidblockerror);
1687
1688 return (false);
1689 }
1690
1691 if (!dbreadavailnode (rightblockadr, &flrightfree, &ctrightbytes, &nextavail))
1692 return (false);
1693
1694 if (ctrightbytes < minblocksize) {
1695
1696 dblogerror (dbmergeinvalidblockerror);
1697
1698 return (false); //not likely to be a valid block, probably just a stream of nil bytes
1699 }
1700
1701 if (!flrightfree) /*the block to the right is in use*/
1702 return (true);
1703
1704 #ifdef fldebug //DATABASE_DEBUG
1705 {
1706 long traileradr = rightblockadr + sizeheader + ctrightbytes;
1707 boolean flfreetrailer;
1708 long ctbytestrailer;
1709
1710 if (traileradr < firstphysicaladdress) {
1711
1712 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1713
1714 return (false);
1715 }
1716
1717 if (traileradr > eof) {
1718
1719 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1720
1721 return (false);
1722 }
1723
1724 if (!dbreadtrailer (traileradr, &flfreetrailer, &ctbytestrailer))
1725 return (false);
1726
1727 if (!flfreetrailer) { /*the header said otherwise!*/
1728
1729 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1730
1731 return (false);
1732 }
1733
1734 if (ctbytestrailer != ctrightbytes) {
1735
1736 dblogerror (dbmergeinvalidblockerror); /*illegal address*/
1737
1738 return (false);
1739 }
1740 }
1741 #endif
1742
1743 if (!dbfindpreviousavail (rightblockadr, &prevavail, &ixshadow))
1744 return (false);
1745
1746 assert ((*(hdlavaillistshadow)(**databasedata).u.extensions.availlistshadow.data) [ixshadow + 1].adr == nextavail);
1747
1748 if (!dbsetavaillink (prevavail, adr)) /*point at beginning of two merged blocks*/
1749 return (false);
1750
1751 ctbytes += ctrightbytes + sizeheader + sizetrailer;
1752
1753 if (!dbwriteavailnode (adr, ctbytes, nextavail))
1754 return (false);
1755
1756 if (!dbsetavailshadow (ixshadow, adr, ctbytes))
1757 return (false);
1758
1759 #if fldebug
1760 rightmerges++;
1761 #endif
1762
1763 *ptrflmergedright = true; /*actually merged*/
1764
1765 return (true); /*actually merged*/
1766 } /*dbmergeright*/
1767
1768
1769 static boolean dbrelease (dbaddress adr) {
1770
1771 /*
1772 release the database block at adr.
1773
1774 try to merge it with the block to the left and then with the block to right.
1775
1776 push any new free block(s) on the available list.
1777
1778 5.1.4 dmb: do all three writes sequentially (write availlink before trailer)
1779
1780 6.2b3 AR: Change in our philosophy: We no longer consider it a big deal if releasing
1781 a block fails, but make absolutely sure we don't corrupt the database by releasing a non-existant
1782 block in the database. Most callers now ignore our return value.
1783 */
1784
1785 boolean flmergedleft, flmergedright;
1786 boolean flfree;
1787 long ctbytes;
1788 tyvariance variance;
1789
1790 if (adr == nildbaddress) /*its easy to release the nil node*/
1791 return (true);
1792
1793 #ifdef SMART_DB_OPENING
1794 dbclearshadowavaillist (); /*6.2b12 AR*/
1795 #endif
1796
1797 if (!dbreadheader (adr, &flfree, &ctbytes, &variance))
1798 return (false);
1799
1800 if (flfree) { /*nasty internal error - block is already free*/
1801
1802 dberror (dbreleasefreeblockerror);
1803
1804 return (false);
1805 }
1806
1807 #ifdef fldebug //DATABASE_DEBUG
1808 /*check header/trailer consistency*/ {
1809
1810 boolean flfreetrailer;
1811 long ctbytestrailer;
1812 dbaddress traileradr = adr + sizeheader + ctbytes;
1813 long dbeof;
1814
1815 if (!dbgeteof (&dbeof))
1816 return (false);
1817
1818 if (traileradr > dbeof) { /*nasty internal error - probably not a valid address*/
1819
1820 dblogerror (dbreleaseinvalidblockerror);
1821
1822 return (false);
1823 }
1824
1825 if (!dbreadtrailer (traileradr, &flfreetrailer, &ctbytestrailer))
1826 return (false);
1827
1828 if (flfreetrailer) { /*nasty internal error - probably not a valid address*/
1829
1830 dblogerror (dbreleaseinvalidblockerror);
1831
1832 return (false);
1833 }
1834
1835 if (ctbytes != ctbytestrailer) { /*nasty internal error - probably not a valid address*/
1836
1837 dblogerror (dbreleaseinvalidblockerror);
1838
1839 return (false);
1840 }
1841 }
1842 #endif
1843
1844 if (!dbmergeright (adr, ctbytes, &flmergedright))
1845 return (false);
1846
1847 if (!dbmergeleft (flmergedright, adr, &flmergedleft))
1848 return (false);
1849
1850 if (flmergedleft || flmergedright)
1851 return (true); /*we're done*/
1852
1853 /*no merging -- set free bits in header & trailer, insert at head of avail list*/
1854
1855 if (!dbwriteavailnode (adr, ctbytes, (**databasedata).availlist))
1856 return (false);
1857
1858 (**databasedata).availlist = adr;
1859
1860 if (!dbinsertavailshadow (0, adr, ctbytes))
1861 return (false);
1862
1863 dbheaderdirty ();
1864
1865 return (true);
1866 } /*dbrelease*/
1867
1868
1869 #if 0
1870
1871 static boolean dbreadbytes (dbaddress adr, long offset, long ctbytes, char *pdata) {
1872
1873 /*
1874 copy from the data part of the block at adr, at given offset into memory.
1875
1876 this is useful if you want to stream a known amount of data out of a database block
1877 but don't want to allocate a temporary buffer to hold it all.
1878 */
1879
1880 return (dbread (adr + sizeheader + offset, ctbytes, pdata));
1881 } /*dbreadbytes*/
1882
1883
1884 static boolean dbwritebytes (dbaddress adr, long offset, long ctbytes, char *pdata) {
1885
1886 /*
1887 copy the data from memory to the data part of the block at adr, at given offset.
1888
1889 this is useful if you want to stream a known amount of data into a database block
1890 but don't want to allocate a temporary buffer to hold it all.
1891 */
1892
1893 return (dbwrite (adr + sizeheader + offset, ctbytes, pdata));
1894 } /*dbwritebytes*/
1895
1896 #endif
1897
1898
1899 static boolean dbmove (ptrvoid pdata, long ctbytes, dbaddress adr) {
1900
1901 /*
1902 copy the data from memory (pdata) to the data part of the block at adr.
1903
1904 call this when you know that the size of the object you're writing is the
1905 same as the object this block was created to hold.
1906 */
1907
1908 return (dbwrite (adr + sizeheader, ctbytes, pdata));
1909 } /*dbmove*/
1910
1911
1912 boolean dbassign (dbaddress *padr, long newsize, ptrvoid pdata) {
1913
1914 /*
1915 we want to move new data into the database block whose address is adr.
1916
1917 maybe the size has changed? think about variable length strings. if so, the
1918 new size is given in newsize.
1919
1920 we get a pointer to the address because we might change the address if we have
1921 to allocate to fit new larger data. we never re-allocate a block if the data
1922 got smaller.
1923
1924 10/16/91 dmb: found longstanding bug. the variance must we updated any time the
1925 size changes, not just when the newsize is less than cttotal
1926
1927 6.2b2 AR: Improved error checking based on the assumption that we should
1928 never assign to a free block -- except when saving a copy, of course.
1929 */
1930
1931 register dbaddress adr;
1932 tyvariance ctunused;
1933 long cttotal;
1934 boolean flfree;
1935
1936 adr = *padr; /*copy into a register*/
1937
1938 if (fldatabasesaveas || (adr == nildbaddress)) /*no previous allocation, create a new one*/
1939 return (dballocate (newsize, pdata, padr));
1940
1941 if (!dbreadheader (adr, &flfree, &cttotal, &ctunused)) /*find out how much space we have in block*/
1942 return (false);
1943
1944 if (flfree) { /*6.2b2 AR: here's another chance to easily detect corruption, why not use it?*/
1945
1946 dberror (dbassignfreeblockerror);
1947
1948 return (false);
1949 }
1950
1951 if (newsize > cttotal) { /*there isn't enough room*/
1952
1953 if (!dbrelease (adr)) { //ignore return value, don't want to abort saving
1954 #ifdef DATABASE_DEBUG
1955 char str[256];
1956
1957 sprintf (str, "dbrelease failed for address %ld.", adr);
1958
1959 DB_MSG_2 (str);
1960 #endif
1961 }
1962
1963 return (dballocate (newsize, pdata, padr)); /*allocate the new, bigger block*/
1964 }
1965
1966 if (newsize != cttotal - ctunused) /*must update the variance*/
1967
1968 if (!dbsetsize (adr, cttotal, cttotal - newsize))
1969
1970 return (false);
1971
1972 return (dbmove (pdata, newsize, adr)); /*copy the data into a big-enough block*/
1973 } /*dbassign*/
1974
1975
1976 static boolean dbgetsize (dbaddress adr, long *logicalsize) {
1977
1978 /*
1979 give me the address of a database block and I'll return the number
1980 of logical bytes it is using.
1981 */
1982
1983 tyvariance size, variance;
1984
1985 *logicalsize = 0;
1986
1987 if (adr == nildbaddress)
1988 return (false);
1989
1990 if (!dbgetsizeandvariance (adr, &size, &variance))
1991 return (false);
1992
1993 *logicalsize = size - variance;
1994
1995 return (true);
1996 } /*dbgetsize*/
1997
1998
1999 boolean dbcopy (dbaddress adrorig, dbaddress *adrcopy) {
2000
2001 /*
2002 create a copy of the database block pointed to by adrorig. return
2003 true if adrcopy has the address of a new block, the same logical size
2004 as the original with a copy of the original's data.
2005 */
2006
2007 register boolean flreturned;
2008 Handle hnew;
2009 register Handle h;
2010 long size;
2011
2012 if (adrorig == nildbaddress) { /*it's very easy to copy the nil node*/
2013
2014 *adrcopy = nildbaddress;
2015
2016 return (true);
2017 }
2018
2019 if (!dbgetsize (adrorig, &size))
2020 return (false);
2021
2022 if (!newhandle (size, &hnew)) /*not enough room in the heap*/
2023 return (false);
2024
2025 h = hnew; /*copy into register*/
2026
2027 lockhandle (h);
2028
2029 flreturned = false; /*default*/
2030
2031 if (dbreference (adrorig, size, *h))
2032
2033 flreturned = dballocate (size, *h, adrcopy);
2034
2035 unlockhandle (h);
2036
2037 disposehandle (h);
2038
2039 return (flreturned);
2040 } /*dbcopy*/
2041
2042
2043 static boolean dballocstring (dbaddress *adr, bigstring bs) {
2044
2045 return (dballocate ((long) stringlength(bs) + 1, bs, adr));
2046 } /*dballocstring*/
2047
2048
2049 static boolean dbrefstring (dbaddress adr, bigstring bs) {
2050
2051 setstringlength (bs, 0);
2052
2053 if (adr == nildbaddress) /*nil adr represents an empty string, saves time & space*/
2054 return (true);
2055
2056 return (dbreference (adr, sizeof (bigstring), bs));
2057 } /*dbrefstring*/
2058
2059
2060 static boolean dbassignstring (dbaddress *adr, bigstring bs) {
2061
2062 if (*adr == nildbaddress)
2063 return (dballocstring (adr, bs));
2064 else
2065 return (dbassign (adr, (long) stringlength(bs) + 1, bs));
2066 } /*dbassignstring*/
2067
2068
2069 static boolean dbreleasestring (dbaddress adr) {
2070
2071 if (!dbrelease (adr)) {
2072 #ifdef DATABASE_DEBUG
2073 char str[256];
2074
2075 sprintf (str, "dbrelease failed for address %ld.", adr);
2076
2077 DB_MSG_2 (str);
2078 #endif
2079
2080 return (false);
2081 }
2082
2083 return (true);
2084 } /*dbreleasestring*/
2085
2086
2087 boolean dbrefheapstring (dbaddress adr, hdlstring *hstring) {
2088
2089 bigstring bs;
2090
2091 if (!dbrefstring (adr, bs))
2092 return (false);
2093
2094 return (newheapstring (bs, hstring));
2095 } /*dbrefheapstring*/
2096
2097
2098 boolean dbassignheapstring (dbaddress *adr, hdlstring hstring) {
2099
2100 bigstring bs;
2101
2102 copyheapstring (hstring, bs); /*checks for hstring == nil*/
2103
2104 if (isemptystring (bs)) {
2105
2106 if (!fldatabasesaveas)
2107 dbreleasestring (*adr);
2108
2109 *adr = nildbaddress; /*default return value, indicates empty string*/
2110
2111 return (true);
2112 }
2113
2114 return (dbassignstring (adr, bs));
2115 } /*dbassignheapstring*/
2116
2117
2118 boolean dballochandle (Handle halloc, dbaddress *adr) {
2119
2120 register Handle h = halloc;
2121 register boolean fl;
2122
2123 if (h == nil) { /*defensive driving, nil handles are represented by nil addresses*/
2124
2125 *adr = nildbaddress;
2126
2127 return (true);
2128 }
2129
2130 lockhandle (h);
2131
2132 fl = dballocate ((long) gethandlesize (h), *h, adr);
2133
2134 unlockhandle (h);
2135
2136 return (fl);
2137 } /*dballochandle*/
2138
2139
2140 boolean dbassignhandle (Handle h, dbaddress *adr) {
2141
2142 /*
2143 6/30/92 dmb: added check for nil handle
2144 */
2145
2146 register boolean fl;
2147
2148 if (*adr == nildbaddress) /*creating a new guy*/
2149
2150 return (dballochandle (h, adr));
2151
2152 if (h == nil)
2153 return (dbassign (adr, 0, nil));
2154
2155 lockhandle (h);
2156
2157 fl = dbassign (adr, (long) gethandlesize (h), *h);
2158
2159 unlockhandle (h);
2160
2161 return (fl);
2162 } /*dbassignhandle*/
2163
2164
2165 boolean dbsavehandle (Handle hsave, dbaddress *adr) {
2166
2167 /*
2168 xxx -- not sure why this is needed, looks like dbassignhandle, above,
2169 does the job fairly well.
2170 */
2171
2172 register Handle h = hsave;
2173 register long ctbytes;
2174 register boolean fl;
2175 dbaddress a = *adr;
2176
2177 ctbytes = gethandlesize (h);
2178
2179 lockhandle (h);
2180
2181 if (a == nildbaddress)
2182 fl = dballocate (ctbytes, *h, &a);
2183 else
2184 fl = dbassign (&a, ctbytes, *h);
2185
2186 unlockhandle (h);
2187
2188 *adr = a; /*copy into returned value*/
2189
2190 return (fl);
2191 } /*dbsavehandle*/
2192
2193
2194 /*
2195 boolean dbnewarray (ctelements, sizeelement, pdata, adr) short ctelements, sizeelement; ptrvoid pdata; dbaddress *adr; {
2196
2197 register long ctbytes;
2198
2199 ctbytes = ((long) ctelements * sizeelement) + sizeof (tydbarrayheader);
2200
2201 return (dballocate (ctbytes, pdata, adr));
2202 } /%dbnewarray%/
2203 */
2204
2205
2206 void dbsetview (short viewnumber, dbaddress adrtext) {
2207
2208 register hdldatabaserecord hdb;
2209
2210 dbswapglobals ();
2211
2212 hdb = databasedata; /*move into register*/
2213
2214 (**hdb).views [viewnumber] = adrtext;
2215
2216 setdirty (hdb);
2217
2218 dbflushheader ();
2219
2220 dbswapglobals ();
2221 } /*dbsetview*/
2222
2223
2224 void dbgetview (short viewnumber, dbaddress *adrtext) {
2225
2226 *adrtext = (**databasedata).views [viewnumber];
2227 } /*dbgetview*/
2228
2229
2230 void dbcurrentdatabase (hdldatabaserecord hdb) {
2231
2232 if (hdb != nil)
2233 databasedata = hdb;
2234 } /*dbcurrentdatabase*/
2235
2236
2237 void dbgetcurrentdatabase (hdldatabaserecord *hdb) {
2238
2239 *hdb = databasedata;
2240 } /*dbgetcurrentdatabase*/
2241
2242
2243 boolean dbfnumchanged (hdlfilenum newfnum) {
2244
2245 register hdldatabaserecord hdb = databasedata;
2246
2247 (**hdb).fnumdatabase = (long) newfnum;
2248
2249 setdirty (hdb);
2250
2251 return (dbflushheader ());
2252 } /*dbfnumchanged*/
2253
2254
2255 #ifdef DATABASE_DEBUG
2256
2257 boolean debug_dbpushreleasestack (dbaddress adr, long valtype, long line, char *sourcefile) {
2258
2259 /*
2260 the chunk of db space pointed to by adr is being logically released, but
2261 the caller is saying that he doesn't want to make the effects permanent
2262 until some time in the future. he indicates it's time to release all these
2263 guys by calling dbflushreleasestack, below.
2264
2265 if the user decides to not save changes, you should call dbzeroreleasestack.
2266 */
2267
2268 Handle hstack = (**databasedata).releasestack;
2269 tydbreleasestackframe info;
2270
2271 if (adr == nildbaddress) /*no need to waste space on a nil address*/
2272 return (true);
2273
2274 if (hstack == nil) {
2275
2276 if (!newclearhandle (0L, &hstack))
2277 return (false);
2278
2279 (**databasedata).releasestack = hstack;
2280 }
2281
2282 clearbytes (&info, sizeof (info));
2283
2284 info.adr = adr;
2285
2286 info.id = valtype;
2287
2288 info.line = line;
2289
2290 newfilledhandle (sourcefile, strlen (sourcefile), &info.file);
2291
2292 return (enlargehandle (hstack, sizeof (info), &info));
2293 } /*dbpushreleasestack*/
2294
2295
2296 boolean dbflushreleasestack (void) {
2297
2298 /*
2299 release all the chunks accumulated in the database's releasestack.
2300
2301 5.1.4 dmb: don't lock the handle
2302 */
2303
2304 Handle h = (**databasedata).releasestack;
2305 tydbreleasestackframe info;
2306 long i, ct;
2307 long hsize;
2308
2309 if (h != nil) {
2310
2311 hsize = gethandlesize (h);
2312
2313 ct = hsize / sizeof (info);
2314
2315 for (i = 0; i < ct; ++i) {
2316
2317 rollbeachball (); /*dmb 4.1b9*/
2318
2319 info = ((tydbreleasestackframe*)(*h)) [i];
2320
2321 if (!dbrelease (info.adr)) {
2322
2323 bigstring bsfile;
2324 char str[256];
2325
2326 texthandletostring (info.file, bsfile);
2327
2328 sprintf (str, "dbrelease failed for address %ld, type %ld, line %ld in %s.", info.adr, info.id, info.line, stringbaseaddress (bsfile));
2329
2330 DB_MSG_2 (str);
2331 }
2332 }
2333
2334 disposehandle (h);
2335
2336 (**databasedata).releasestack = nil;
2337 }
2338
2339 #ifdef SMART_DB_OPENING
2340 dbwriteshadowavaillist (); /*6.2b12 AR: this is a good place to do it since we're about done with saving*/
2341 #endif
2342
2343 return (true);
2344 } /*dbflushreleasestack*/
2345
2346 #else
2347
2348 boolean dbpushreleasestack (dbaddress adr, long valtype) {
2349
2350 /*
2351 the chunk of db space pointed to by adr is being logically released, but
2352 the caller is saying that he doesn't want to make the effects permanent
2353 until some time in the future. he indicates it's time to release all these
2354 guys by calling dbflushreleasestack, below.
2355
2356 if the user decides to not save changes, you should call dbzeroreleasestack.
2357
2358 6.2b3 AR: Added valtype parameter, only used in debug version (see above).
2359 */
2360
2361 Handle hstack = (**databasedata).releasestack;
2362
2363 if (adr == nildbaddress) /*no need to waste space on a nil address*/
2364 return (true);
2365
2366 if (hstack == nil) {
2367
2368 if (!newclearhandle (0L, &hstack))
2369 return (false);
2370
2371 (**databasedata).releasestack = hstack;
2372 }
2373
2374 return (enlargehandle (hstack, sizeof (adr), &adr));
2375 } /*dbpushreleasestack*/
2376
2377
2378 boolean dbflushreleasestack (void) {
2379
2380 /*
2381 release all the chunks accumulated in the database's releasestack.
2382
2383 5.1.4 dmb: don't lock the handle
2384 */
2385
2386 Handle h = (**databasedata).releasestack;
2387 long i, ct;
2388 long hsize;
2389
2390 if (h != nil) {
2391
2392 hsize = gethandlesize (h);
2393
2394 ct = hsize / sizeof (dbaddress);
2395
2396 for (i = 0; i < ct; ++i) {
2397
2398 rollbeachball (); /*dmb 4.1b9*/
2399
2400 dbrelease (((ptrdbaddress) (*h)) [i]);
2401 }
2402
2403 disposehandle (h);
2404
2405 (**databasedata).releasestack = nil;
2406 }
2407
2408 #ifdef SMART_DB_OPENING
2409 dbwriteshadowavaillist (); /*6.2b12 AR: this is a good place to do it since we're about done with saving*/
2410 #endif
2411
2412 return (true);
2413 } /*dbflushreleasestack*/
2414
2415 #endif
2416
2417
2418 static void dbzeroreleasestack (void) {
2419
2420 disposehandle ((**databasedata).releasestack);
2421
2422 (**databasedata).releasestack = nil;
2423 } /*dbzeroreleasestack*/
2424
2425
2426 boolean dbdispose (void) {
2427
2428 dbzeroreleasestack ();
2429
2430 #ifdef SMART_DB_OPENING
2431 dbdisposeshadowavaillist ();
2432 #else
2433 disposehandle ((**databasedata).u.extensions.availlistshadow.data);
2434 #endif
2435
2436 disposehandle ((Handle) databasedata);
2437
2438 databasedata = nil;
2439
2440 return (true);
2441 } /*dbdispose*/
2442
2443
2444 boolean dbnew (hdlfilenum fnum) {
2445
2446 /*
2447 2002-11-11 AR: Added assert to make sure the C compiler chose the
2448 proper byte alignment for the tydatabaserecord struct. If it did not,
2449 we would end up corrupting any database files we saved.
2450 */
2451
2452 register hdldatabaserecord hdb;
2453
2454 assert (sizeof (tydatabaserecord) == 88);
2455
2456 if (!newclearhandle (sizeof (tydatabaserecord), (Handle *) &databasedata))
2457 return (false);
2458
2459 hdb = databasedata; /*copy into register*/
2460
2461 (**hdb).fnumdatabase = (long) fnum;
2462
2463 #ifdef MACVERSION
2464 (**hdb).systemid = dbsystemidMac;
2465 #endif
2466
2467 #ifdef WIN95VERSION
2468 (**hdb).systemid = dbsystemidWin32;
2469 #endif
2470
2471 (**hdb).versionnumber = dbversionnumber;
2472
2473 (**hdb).headerLength = firstphysicaladdress;
2474 (**hdb).longversionMajor = dbversionnumber;
2475 (**hdb).longversionMinor = dbversionnumberminor;
2476
2477 dbshadowavaillist ();
2478
2479 setdirty (hdb);
2480
2481 if (dbflushheader ()) /*initial info written to disk*/
2482 return (true);
2483
2484 dbdispose (); /*error flushing the data out to disk*/
2485
2486 return (false);
2487 } /*dbnew*/
2488
2489
2490 boolean dbopenfile (hdlfilenum fnum, boolean flreadonly) {
2491
2492 /*
2493 4.1b9 dmb: allow opening of databases newer than us, as long as
2494 version number change is not major.
2495
2496 5.1.5 dmb: use diskrec instead of handle locking; shadow avail list
2497
2498 6.2a9 AR: To support the builtins.db verbs we need to know whether
2499 we have write permission, so we introduced the flreadonly param.
2500
2501 2002-11-11 AR: Added assert to make sure the C compiler chose the
2502 proper byte alignment for the tydatabaserecord struct. If it did not,
2503 we would end up corrupting any database files we saved.
2504 */
2505
2506 tydatabaserecord diskrec;
2507 register hdldatabaserecord hdb;
2508
2509 assert (sizeof (tydatabaserecord) == 88);
2510
2511 if (!newclearhandle (longsizeof (tydatabaserecord), (Handle *) &databasedata))
2512 return (false);
2513
2514 hdb = databasedata; /*copy into register*/
2515
2516 (**hdb).fnumdatabase = (long) fnum; /*set up so dbread will work*/
2517
2518 if (!dbread ((dbaddress) 0, sizeof (tydatabaserecord), &diskrec))
2519 goto error;
2520
2521 #ifdef WIN32
2522 {
2523 short i;
2524 longswap (diskrec.availlist);
2525 longswap (diskrec.u.extensions.availlistblock);
2526 shortswap (diskrec.flags);
2527 for (i = 0; i < ctviews; i++)
2528 {
2529 longswap (diskrec.views[i]);
2530 }
2531 // longswap (diskrec.fnumdatabase);
2532 longswap (diskrec.headerLength);
2533 shortswap (diskrec.longversionMajor);
2534 shortswap (diskrec.longversionMinor);
2535 }
2536 #endif
2537
2538 diskrec.fnumdatabase = (long) fnum; /*this just got overwritten*/
2539
2540 diskrec.releasestack = nil; /*this is an in-memory structure only*/
2541
2542 diskrec.u.extensions.flreadonly = flreadonly; /*this is an in-memory structure only*/
2543
2544 **hdb = diskrec;
2545
2546 if ((**hdb).versionnumber != dbversionnumber) {
2547
2548 if (majorversion ((**hdb).versionnumber) != majorversion (dbversionnumber)) {
2549
2550 dberror (dbwrongversionerror);
2551
2552 goto error;
2553 }
2554
2555 #ifdef SMART_DB_OPENING
2556 if ((**hdb).versionnumber < dbfirstversionwithcachedshadowavaillist)
2557 (**hdb).u.extensions.availlistblock = nildbaddress; /*don't count on old version to handle this one*/
2558 #endif
2559
2560 (**hdb).versionnumber = dbversionnumber; /*we can only write what we know*/
2561
2562 setdirty (hdb);
2563 }
2564
2565 if (!dbshadowavaillist ())
2566 goto error;
2567
2568 return (true);
2569
2570 error:
2571
2572 disposehandle ((Handle) hdb);
2573
2574 databasedata = nil;
2575
2576 return (false); /*error loading in header*/
2577 } /*dbopenfile*/
2578
2579
2580 boolean dbclose (void) {
2581
2582 dbzeroreleasestack (); /*don't release chunks accumulated in release stack*/
2583
2584 setdirty (databasedata);
2585
2586 return (dbflushheader ());
2587 } /*dbclose*/
2588
2589
2590 boolean dbstartsaveas (hdlfilenum fnum) {
2591
2592 register boolean fl;
2593
2594 fldatabasesaveas = true; /*set global; enables databasehandle swapping*/
2595
2596 dbswapglobals ();
2597
2598 fl = dbnew (fnum);
2599
2600 dbswapglobals ();
2601
2602 fldatabasesaveas = fl;
2603
2604 return (fl);
2605 } /*dbstartsaveas*/
2606
2607
2608 boolean dbendsaveas (void) {
2609
2610 register boolean fl;
2611
2612 if (!fldatabasesaveas)
2613 return (false);
2614
2615 dbswapglobals ();
2616
2617 fl = dbclose ();
2618
2619 dbdispose ();
2620
2621 dbswapglobals ();
2622
2623 fldatabasesaveas = false;
2624
2625 return (fl);
2626 } /*dbendsaveas*/
2627
2628
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.