~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Frontier Kernel
Frontier/Common/source/db.c

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.