db.c

Go to the documentation of this file.
00001 
00002 /*  $Id: db.c 1245 2006-04-09 20:36:52Z andreradke $    */
00003 
00004 /******************************************************************************
00005 
00006     UserLand Frontier(tm) -- High performance Web content management,
00007     object database, system-level and Internet scripting environment,
00008     including source code editing and debugging.
00009 
00010     Copyright (C) 1992-2004 UserLand Software, Inc.
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00025 
00026 ******************************************************************************/
00027 
00028 #include "frontier.h"
00029 #include "standard.h"
00030 
00031 #include "memory.h"
00032 #include "cursor.h"
00033 #include "dialogs.h"
00034 #include "error.h"
00035 #include "file.h"
00036 #include "resources.h"
00037 #include "strings.h"
00038 #include "shell.h"
00039 #include "db.h"
00040 #include "dbinternal.h"
00041 #include "ops.h" //6.2b3 AR: for numbertostring
00042 #include "byteorder.h"  /* 2006-04-08 aradke: endianness conversion macros */
00043 
00044 #include "frontierdebug.h" //6.2b7 AR
00045 
00046 #define dberrorlist 256
00047 
00048 #define setdirty(hdb)       ((**hdb).flags |= dbdirtymask)
00049 #define cleardirty(hdb)     ((**hdb).flags &= ~dbdirtymask)
00050 #define isdirty(hdb)        ((**hdb).flags & dbdirtymask)
00051 
00052 #define majorversion(v)     (v & 0x00f0)
00053 #define minorversion(v)     (v & 0x000f)
00054 
00055 typedef enum {
00056     
00057     dbnoerror,
00058     
00059     dbwrongversionerror,
00060     
00061     dbfreeblockerror,
00062 
00063     dbfreelisterror,
00064 
00065     dbinconsistentavaillisterror,
00066 
00067     dbassignfreeblockerror,
00068 
00069     dbfilesizeerror,
00070 
00071     dbreleasefreeblockerror,
00072 
00073     dbreleaseinvalidblockerror,
00074 
00075     dbmergeinvalidblockerror
00076     } tydberror;
00077 
00078 
00079 hdldatabaserecord databasedata; /*the global database handle*/
00080 
00081 #if flruntime
00082 
00083     #define fldatabasesaveas false
00084 
00085 #else
00086 
00087     boolean fldatabasesaveas = false; /*only true during Save As operation*/
00088 
00089 #endif
00090 
00091 
00092 static hdldatabaserecord databasedestination; /*for Save As*/
00093 
00094 #if fldebug
00095 
00096 static long leftmerges = 0, rightmerges = 0; /*statistics*/
00097 
00098 static long splits = 0, nonsplits = 0; /*more statistics*/
00099 
00100 static long allocs = 0, newallocs = 0, allocloops = 0; /*more statistics*/
00101 
00102 #endif
00103 
00104 
00105 #ifdef DATABASE_DEBUG
00106 
00107 #pragma message ("*********************** DATABASE_DEBUG is ON: output to dblog.txt ***********************")
00108 
00109 #define dberror(num) debug_dberror(num, __LINE__, true)
00110 #define dblogerror(num) debug_dberror(num, __LINE__, false)
00111 #define dbseteof(eof) debug_dbseteof(eof, __LINE__)
00112 
00113 
00114 static boolean DBTRACKERGETROOTVISIT (WindowPtr w, bigstring bsfile) {
00115 
00116     hdlwindowinfo hinfo;
00117 
00118     if (getwindowinfo (w, &hinfo))
00119         if ((long) (**hinfo).fnum == (**databasedata).fnumdatabase) {
00120 
00121             copystring (fsname(&(**hinfo).fspec), bsfile);
00122 
00123             return (false); /*terminate visiting*/
00124             }
00125 
00126     return (true);
00127     }/*getrootnamevisit*/
00128 
00129 
00130 static void DBTRACKERGETFILENAME (bigstring bsfile) {
00131 
00132     if (shellvisittypedwindows (idcancoonconfig, &DBTRACKERGETROOTVISIT, bsfile))
00133         setemptystring (bsfile); /*nothing found*/
00134     
00135     }/*DBTRACKERGETFILENAME*/
00136 
00137 
00138 static void debug_dberror (short errnum, int line, boolean flthrow) {
00139     
00140     bigstring bs, bsfile;
00141     char str[1024];
00142     
00143     getstringlist (dberrorlist, errnum, bs);
00144 
00145     DBTRACKERGETFILENAME (bsfile);
00146     
00147     sprintf (str, "%s | %s [db.c,%ld]", stringbaseaddress (bsfile), stringbaseaddress (bs), line);
00148 
00149     DB_MSG_1 (str);
00150 
00151     if (flthrow)
00152         shellerrormessage (bs);
00153     } /*debug_dberror*/
00154 
00155 
00156 static boolean debug_dbseteof (long eof, long line) {
00157 
00158     const long onegigabyte = 1024L * 1024L * 1024L;
00159 
00160     if (eof >= onegigabyte) {
00161 
00162         long oldeof = nil;
00163 
00164         if (dbgeteof (&oldeof) && (oldeof < onegigabyte)) {
00165 
00166             char str[1024];
00167             bigstring bsfile;
00168             
00169             DBTRACKERGETFILENAME (bsfile);
00170             
00171             sprintf (str, "%s | WARNING -- Growing database from %ld to %ld bytes. [db.c,%ld]", stringbaseaddress (bsfile), oldeof, eof, line);
00172 
00173             DB_MSG_1 (str);
00174             }
00175         }
00176 
00177     if ((eof & 0x80000000L) != 0x00000000L) {
00178 
00179         dberror (dbfilesizeerror);
00180 
00181         return (false); //trying to grow the file beyond 2 GB
00182         }
00183 
00184     return (fileseteof ((hdlfilenum)((**databasedata).fnumdatabase), eof));
00185     } /*dbseteof*/
00186 
00187 #else
00188 
00189 #define dblogerror(errnum)
00190 
00191 static void dberror (short errnum) {
00192     
00193     bigstring bs;
00194     
00195     getstringlist (dberrorlist, errnum, bs);
00196     
00197     shellerrormessage (bs);
00198     } /*dberror*/
00199 
00200 static boolean dbseteof (long eof) {
00201 
00202     if ((eof & 0x80000000L) != 0x00000000L) {
00203 
00204         dberror (dbfilesizeerror);
00205 
00206         return (false); //trying to grow the file beyond 2 GB
00207         }
00208 
00209     return (fileseteof ((hdlfilenum)((**databasedata).fnumdatabase), eof));
00210     } /*dbseteof*/
00211 
00212 #endif
00213 
00214 
00215 #define ctdatabasestack 10
00216 
00217 short topdatabasestack = 0;
00218 
00219 hdldatabaserecord databasestack [ctdatabasestack];
00220 
00221 
00222 static boolean dbrelease (dbaddress); /*6.2b2: Dropped from db.h and declared static*/
00223 static boolean dballocate (long databytes, ptrvoid pdata, dbaddress *paddress); /*6.2b14 AR: forward declaration for dbwriteshadowavaillist*/
00224 
00225 boolean dbpushdatabase (hdldatabaserecord hdatabase) {
00226     
00227     /*
00228     when you want to temporarily work with a different databaserecord, call this
00229     routine, do your stuff and then call dbpopdatabase.
00230     */
00231     
00232     if (topdatabasestack >= ctdatabasestack) {
00233         
00234         DebugStr (STR_database_stack_overflow);
00235         
00236         return (false);
00237         }
00238     
00239     databasestack [topdatabasestack++] = databasedata;
00240     
00241     if (hdatabase != nil)
00242         databasedata = hdatabase;
00243     
00244     return (true);
00245     } /*dbpushdatabase*/
00246         
00247 
00248 boolean dbpopdatabase (void) {
00249     
00250     if (topdatabasestack <= 0)
00251         return (false);
00252     
00253     databasedata = databasestack [--topdatabasestack];
00254     
00255     return (true);
00256     } /*dbpopdatabase*/
00257 
00258 
00259 static void dbswapglobals (void) {
00260     
00261     /*
00262     used to temporarily set and unset databasedestination as the databasehandle
00263     */
00264     
00265     if (fldatabasesaveas) {
00266         
00267         hdldatabaserecord htemp = databasedata;
00268         
00269         databasedata = databasedestination;
00270         
00271         databasedestination = htemp;
00272         }
00273     } /*dbswapglobals*/
00274 
00275 
00276 static boolean dbseek (dbaddress adr) {
00277     
00278     return (filesetposition((hdlfilenum)((**databasedata).fnumdatabase), adr));
00279     } /*dbseek*/
00280         
00281     
00282 static boolean dbwrite (dbaddress adr, long ctbytes, ptrvoid pdata) {
00283     
00284     if (!dbseek (adr))
00285         return (false);
00286         
00287     return (filewrite ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata)); 
00288     } /*dbwrite*/
00289     
00290 
00291 static boolean dbread (dbaddress adr, long ctbytes, ptrvoid pdata) {
00292 
00293     if (!dbseek (adr))
00294         return (false);
00295                 
00296     return (fileread ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata)); 
00297     } /*dbread*/
00298     
00299 
00300 static boolean dbwriteswap (dbaddress adr, long ctbytes, ptrvoid pdata) {
00301     boolean res;
00302 
00303     if (!dbseek (adr))
00304         return (false);
00305 
00306 #ifdef SWAP_BYTE_ORDER
00307     if (ctbytes == sizeof (long))
00308         {
00309         memtodisklong (*((long *)pdata));
00310         }
00311 
00312     if (ctbytes == sizeof(short))
00313         {
00314         memtodiskshort (*((short*)pdata));
00315         }
00316 #endif
00317     
00318     res = filewrite ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata);
00319 
00320 #ifdef SWAP_BYTE_ORDER
00321     if (ctbytes == sizeof (long))
00322         {
00323         disktomemlong (*((long *)pdata));
00324         }
00325 
00326     if (ctbytes == sizeof(short))
00327         {
00328         disktomemshort (*((short*)pdata));
00329         }
00330 #endif
00331 
00332     return (res);
00333     } /*dbwriteswap*/
00334     
00335 
00336 static boolean dbreadswap (dbaddress adr, long ctbytes, ptrvoid pdata) {
00337     boolean res;
00338 
00339     if (!dbseek (adr))
00340         return (false);
00341                 
00342     res = fileread ((hdlfilenum)((**databasedata).fnumdatabase), ctbytes, pdata); 
00343 
00344 #ifdef SWAP_BYTE_ORDER
00345     if (ctbytes == sizeof (long))
00346         {
00347         disktomemlong (*((long *)pdata));
00348         }
00349     else
00350         {
00351         if (ctbytes == sizeof(short))
00352             {
00353             disktomemshort (*((short*)pdata));
00354             }
00355         }
00356 #endif
00357 
00358     return (res);
00359     } /*dbreadswap*/
00360     
00361 
00362 boolean dbgeteof (long *eof) {
00363 
00364     return (filegeteof ((hdlfilenum)((**databasedata).fnumdatabase), eof));
00365     } /*dbgeteof*/
00366 
00367         
00368 static void dbheaderdirty (void) {
00369     
00370     /*
00371     5.0.1 dmb: was ^= instead of |=. Caused avail list database corruption
00372     */
00373     
00374     (**databasedata).flags |= dbdirtymask;
00375     } /*dbheaderdirty*/
00376 
00377 
00378 static boolean dbflushheader (void) {
00379     
00380     /*
00381     5.1.5 dmb: copy databasedata to local record for writing
00382     */
00383     
00384     register hdldatabaserecord hdb = databasedata;
00385     boolean fl;
00386     tydatabaserecord diskrec;
00387     
00388     assert (sizeof (diskrec.u.growthspace) >= sizeof (diskrec.u.extensions));
00389     
00390     if (isdirty (hdb)) { /*changes made to header*/
00391         
00392         cleardirty (hdb); /*clear it*/
00393         
00394         diskrec = **hdb;
00395 
00396         #ifdef SMART_DB_OPENING     
00397             clearbytes (&diskrec.u.extensions.availlistshadow, sizeof (diskrec.u.extensions.availlistshadow)); /*in-memory structure only*/
00398             
00399             clearbytes (&diskrec.u.extensions.flreadonly, sizeof (diskrec.u.extensions.flreadonly)); /*in-memory structure only*/
00400         #else
00401             clearbytes (&diskrec.u.growthspace, sizeof (diskrec.u.growthspace)); /*in-memory structure only*/
00402         #endif
00403 
00404         #ifdef SWAP_BYTE_ORDER
00405             {
00406             short i;
00407             memtodisklong (diskrec.availlist);
00408             memtodisklong (diskrec.u.extensions.availlistblock);
00409             memtodiskshort (diskrec.flags);
00410             for (i = 0; i < ctviews; i++)
00411                 {
00412                 memtodisklong (diskrec.views[i]);
00413                 }
00414         //  memtodisklong (diskrec.fnumdatabase);
00415             memtodisklong (diskrec.headerLength);
00416             memtodiskshort (diskrec.longversionMajor);
00417             memtodiskshort (diskrec.longversionMinor);
00418             }
00419         #endif
00420         
00421         fl = dbwrite ((dbaddress) 0, sizeof (tydatabaserecord), &diskrec);
00422         
00423         #ifdef WIN95VERSION
00424             if (fl)
00425                 flushvolumechanges (nil, (hdlfilenum)((**databasedata).fnumdatabase));
00426         #else
00427         /*flush file buffers*/ {
00428             IOParam pb;
00429             
00430             clearbytes (&pb, sizeof (pb));
00431             
00432             pb.ioRefNum = (hdlfilenum)((**databasedata).fnumdatabase);
00433             
00434             PBFlushFile ((ParmBlkPtr) &pb, false);
00435             }
00436         #endif
00437 
00438         return (fl);
00439         } /*changes made to header*/
00440         
00441     return (true);
00442     } /*dbflushheader*/
00443     
00444 
00445 boolean dbreadheader (dbaddress adr, boolean *flfree, long *ctbytes, tyvariance *variance) {
00446     
00447     tyheader header;
00448     
00449     if (!dbread (adr, sizeheader, &header))
00450         return (false);
00451     
00452     disktomemlong (header.variance);
00453     disktomemlong (header.sizefreeword.size);
00454 
00455     *flfree = (header.sizefreeword.size & 0x80000000L) == 0x80000000L ? true : false;
00456     
00457     *ctbytes = header.sizefreeword.size & 0x7FFFFFFFL;
00458     
00459     *variance = header.variance;
00460     
00461     return (true);
00462     } /*dbreadheader*/
00463     
00464 
00465 boolean dbreadtrailer (dbaddress adr, boolean *flfree, long *ctbytes) {
00466 
00467     tytrailer trailer;
00468     
00469     if (!dbread (adr, sizetrailer, &trailer))
00470         return (false);
00471         
00472     disktomemlong (trailer.sizefreeword.size);
00473 
00474     *flfree = (trailer.sizefreeword.size & 0x80000000L) == 0x80000000L ? true : false;
00475     
00476     *ctbytes = trailer.sizefreeword.size & 0x7FFFFFFFL;
00477     
00478     return (true);
00479     } /*dbreadtrailer*/
00480     
00481 
00482 static boolean dbwriteheader (dbaddress adr, boolean flfree, long ctbytes, tyvariance variance) {
00483 
00484     tyheader header;
00485     
00486     header.sizefreeword.size = ctbytes;
00487 
00488     if (flfree)
00489         header.sizefreeword.size |= 0x80000000L;
00490     
00491     header.variance = variance;
00492     
00493     memtodisklong (header.variance);
00494     memtodisklong (header.sizefreeword.size);
00495 
00496     return (dbwrite (adr, sizeheader, &header));
00497     } /*dbwriteheader*/
00498     
00499     
00500 static boolean dbwritetrailer (dbaddress adr, boolean flfree, long ctbytes) {
00501 
00502     tytrailer trailer;
00503     
00504     trailer.sizefreeword.size = ctbytes; 
00505 
00506     if (flfree)
00507         trailer.sizefreeword.size |= 0x80000000L;
00508     
00509     memtodisklong (trailer.sizefreeword.size);
00510 
00511     return (dbwrite (adr, sizetrailer, &trailer));
00512     } /*dbwritetrailer*/
00513 
00514 
00515 static boolean dbwriteheaderandtrailer (dbaddress adr, boolean flfree, long ctbytes, tyvariance variance) {
00516     
00517     if (!dbwriteheader (adr, flfree, ctbytes, variance))
00518         return (false);
00519     
00520     return (dbwritetrailer (adr + sizeheader + ctbytes, flfree, ctbytes));
00521     } /*dbwriteheaderandtrailer*/
00522     
00523         
00524 boolean dbreadavailnode (dbaddress adr, boolean *flfree, long *ctbytes, dbaddress *link) {
00525 
00526     /*
00527     each node on the avail list has a link stored in its data field, we get
00528     all the usual data from the node and return that link in the nextnomad
00529     parameter.
00530     */
00531 
00532     tyvariance variance; /*variance is irrelevent in avail nodes*/
00533     
00534     if (!dbreadheader (adr, flfree, ctbytes, &variance))
00535         return (false);
00536             
00537     return (dbreadswap (adr + sizeheader, sizeof (dbaddress), link));
00538     } /*dbreadavailnode*/
00539     
00540 
00541 static boolean dbwriteavailnode (dbaddress adr, long ctbytes, dbaddress nextlink) {
00542 
00543     assert (adr != nil);
00544     
00545     assert ((**databasedata).u.extensions.availlistblock == nildbaddress);
00546     
00547     if (!dbwriteheader (adr, true, ctbytes, 0L))
00548         return (false);
00549     
00550     if (!dbwriteswap (adr + sizeheader, sizeof (dbaddress), &nextlink))
00551         return (false);
00552 
00553     if (!dbwritetrailer (adr + sizeheader + ctbytes, true, ctbytes))
00554         return (false);
00555     
00556     return (true);
00557     } /*dbwriteavailnode*/
00558     
00559 
00560 static boolean dbsetavaillink (dbaddress adr, dbaddress link) {
00561 
00562     /*
00563     adr points to a record in the database file.  move past the header and
00564     write the link address in the first four bytes of the block's space.
00565     */
00566     
00567     assert ((**databasedata).u.extensions.availlistblock == nildbaddress);
00568     
00569     if (adr == nildbaddress) { /*special case, set link in file header*/
00570         
00571         (**databasedata).availlist = link;
00572         
00573         dbheaderdirty ();
00574             
00575         return (true);
00576         }
00577         
00578     return (dbwriteswap (adr + sizeheader, sizeof (dbaddress), &link));
00579     } /*dbsetavaillink*/
00580     
00581     
00582 static boolean dbwritedatablock (dbaddress adr, long databytes, long nodebytes, ptrvoid pdata) {
00583 
00584     /*
00585     there might be less data to write than there is room in the 
00586     block, so we only write as much as is necessary, but we size 
00587     the block according to its logical size.
00588     */
00589     
00590     if (!dbwriteheader (adr, false, nodebytes, (tyvariance) nodebytes - databytes))
00591         return (false);
00592         
00593     if (pdata != nil) /*write data bytes*/
00594     
00595         if (!dbwrite (adr + sizeheader, databytes, pdata)) 
00596             return (false);
00597     
00598     return (dbwritetrailer (adr + nodebytes + sizeheader, false, nodebytes));
00599     } /*dbwritedatablock*/
00600     
00601 
00602 static boolean dbfindpreviousavail (dbaddress adr, dbaddress *prev, long *ixshadow) {
00603     
00604     /*
00605     the available list is not doubly-linked.  this is where we pay the price.
00606     
00607     the caller wants to know which node in the avail list points at it.  if
00608     its the list header, we return nildbaddress.
00609     
00610     returns true if *prev was correctly set, false otherwise.
00611     
00612     5.1.5 dmb: use in-memory shadow
00613     */
00614     
00615 #ifdef dbshadow
00616     hdldatabaserecord hdb = databasedata;
00617     hdlavaillistshadow havailshadow = (hdlavaillistshadow) (**hdb).u.extensions.availlistshadow.data;
00618     long i, ctavail = (**hdb).u.extensions.availlistshadow.eof / sizeof (tyavailnodeshadow);
00619     
00620     for (i = 0; i < ctavail; ++i) {
00621         
00622         if ((*havailshadow) [i].adr == adr) {
00623             
00624             if (i > 0)
00625                 *prev = (*havailshadow) [i - 1].adr;
00626             else
00627                 *prev = nildbaddress;
00628             
00629             *ixshadow = i;
00630             
00631             return (true);
00632             }
00633         }
00634     
00635     dblogerror (dbfreelisterror); /*something fishy is going on*/
00636     
00637     return (false);
00638 #else
00639     dbaddress nomad;
00640     boolean flfree;
00641     long ctbytes;
00642     dbaddress nextnomad;
00643     
00644     nomad = (**databasedata).availlist;
00645     
00646     if (nomad == adr) { /*he's the first guy on the list*/
00647     
00648         *prev = nildbaddress;
00649         
00650         return (true);
00651         }
00652         
00653     while (true) {
00654         
00655         if (nomad == nildbaddress) /*reached end of list, no one points at the node*/
00656             return (false);
00657             
00658         if (!dbreadavailnode (nomad, &flfree, &ctbytes, &nextnomad))
00659             return (false);
00660             
00661         if (nextnomad == adr) { /*found the guy that points at our friend*/
00662             
00663             *prev = nomad;
00664             
00665             return (true);
00666             }
00667         
00668         nomad = nextnomad; /*advance to next node*/
00669         } /*while*/
00670 #endif
00671     } /*dbfindpreviousavail*/
00672 
00673 
00674 
00675 #ifdef SMART_DB_OPENING 
00676 
00677 static void dbclearshadowavaillist (void) {
00678     
00679     /*
00680     6.2b12 AR: This function MUST be called before modifying the linked list
00681     of available blocks on disk. We reset the pointer to the cached shadow avail list
00682     in the database header, set the header's dirty bit, and flush the header to disk.
00683     
00684     Finally, we also release the block allocated for the cached shadow avail list.
00685     
00686     Do nothing if we're performing a Save A Copy or if the db was opened read-only.
00687     */
00688 
00689     register hdldatabaserecord hdb = databasedata;
00690     
00691     if (!fldatabasesaveas && !(**hdb).u.extensions.flreadonly)
00692 
00693         if ((**hdb).u.extensions.availlistblock != nildbaddress) {
00694     
00695             dbaddress adrblock = (**hdb).u.extensions.availlistblock;
00696             
00697             (**hdb).u.extensions.availlistblock = nildbaddress;
00698             
00699             dbheaderdirty ();
00700             
00701             dbflushheader ();       
00702             
00703             if (!dbrelease (adrblock)) {
00704                 #ifdef DATABASE_DEBUG
00705                     char str[256];
00706 
00707                     sprintf (str, "dbrelease failed for address %ld.", (**hdb).u.extensions.availlistblock);
00708 
00709                     DB_MSG_2 (str);
00710                 #endif
00711                 }
00712             }
00713             
00714     return;
00715     } /*dbclearshadowavaillist*/
00716 
00717 
00718 static void dbdisposeshadowavaillist (void) {
00719     
00720     register hdldatabaserecord hdb = databasedata;
00721     handlestream s;
00722     
00723     assert (hdb != nil);
00724 
00725     s = (**hdb).u.extensions.availlistshadow;
00726 
00727     disposehandlestream (&s);
00728         
00729     clearbytes (&s, sizeof (s));
00730         
00731     (**hdb).u.extensions.availlistshadow = s;
00732 
00733     return;
00734     } /*dbdisposeshadowavaillist*/
00735 
00736 #endif
00737 
00738 
00739 static boolean dbwriteshadowavaillist (void) {
00740 
00741     /*
00742     6.2a9 AR: If there's an in-memory shadow avail list, write it to a faked new data block
00743     at the end of the database and store a pointer to the block in the file header.
00744     
00745     What happens if the database is opened by a version of Frontier that doesn't know about
00746     this convention? When it first saves the database, it will write the db header, thereby
00747     destroying the pointer to the on-disk shadow avail list. We end up with an orphaned block
00748     in the database, typically a few dozen kB in size. Next time the user saves a copy,
00749     the orphaned block is dropped from the database. Not a big deal.
00750     
00751     Also see dbreadshadowavaillist.
00752 
00753     If we don't have write permission, just dispose of everything. Don't even flush the db header.
00754     
00755     6.2b14 AR: Before we call dbwritedatablock, we have to determine the actual size
00756     of the allocated block. It could be larger than what we asked for. So we call
00757     dbreadheader to update the value of nodebytes to the real thing.
00758     */
00759 
00760     register hdldatabaserecord hdb = databasedata;
00761     dbaddress adrblock = nil;
00762     boolean fl = false;
00763     boolean flfree;
00764     
00765     assert (hdb != nil);
00766 
00767     if ((**hdb).u.extensions.flreadonly || fldatabasesaveas)
00768         return (true); /*we're done already*/
00769     
00770     dbclearshadowavaillist ();
00771         
00772     if ((**hdb).u.extensions.availlistshadow.data == nil)
00773         return (true); /*we're done already*/
00774     
00775     if ((**hdb).u.extensions.availlistshadow.eof > 0) { /*there's something to be saved*/
00776     
00777         long nodebytes = (**hdb).u.extensions.availlistshadow.eof;
00778         long databytes, dummy;
00779         Handle h = nil;
00780         
00781         if (!dballocate (nodebytes, nil, &adrblock))
00782             goto error;
00783         
00784         assert (adrblock != nil);
00785 
00786         closehandlestream (&(**hdb).u.extensions.availlistshadow);
00787         
00788         if (!copyhandle ((**hdb).u.extensions.availlistshadow.data, &h))
00789             goto error;
00790         
00791         databytes = gethandlesize (h);
00792         
00793         assert (databytes == nodebytes || databytes == nodebytes - (long) sizeof (tyavailnodeshadow));
00794 
00795         #if 0 //DATABASE_DEBUG //6.2b7 AR: debugging check disabled
00796 
00797             /*verify cached version of avail list*/ {
00798 
00799                 tyavailnodeshadow diskavailrec;
00800                 tyavailnodeshadow memavailrec;
00801                 dbaddress nextavail;
00802                 boolean flfree;
00803                 long dbeof;
00804                 long ix = 0;
00805             
00806                 if (!dbgeteof (&dbeof))
00807                     goto error;
00808 
00809                 diskavailrec.adr = (**hdb).availlist;
00810 
00811                 while (diskavailrec.adr != nildbaddress) {
00812                     
00813                     if (!dbreadavailnode (diskavailrec.adr, &flfree, &diskavailrec.size, &nextavail) ||
00814                         !flfree ||
00815                         diskavailrec.adr + diskavailrec.size > dbeof) {
00816 
00817                         diskavailrec.adr = nildbaddress;
00818                         
00819                         assert (false);
00820 
00821                         break;
00822                         }
00823                     
00824                     memavailrec = ((tyavailnodeshadow*)(*h)) [ix++];
00825 
00826                     assert (diskavailrec.adr == memavailrec.adr);
00827                     
00828                     assert (diskavailrec.size == memavailrec.size);
00829 
00830                     assert (ix * sizeof (tyavailnodeshadow) < databytes);
00831 
00832                     diskavailrec.adr = nextavail;
00833                     
00834                     rollbeachball ();
00835                     } /*while*/ 
00836                 
00837                 diskavailrec.size = 0;
00838 
00839                 memavailrec = ((tyavailnodeshadow*)(*h)) [ix++];
00840 
00841                 assert (diskavailrec.adr == memavailrec.adr);
00842                     
00843                 assert (diskavailrec.size == memavailrec.size);
00844 
00845                 assert (ix * sizeof (tyavailnodeshadow) == databytes);
00846                 }
00847         #endif
00848         
00849         #ifdef SWAP_BYTE_ORDER
00850             /*switch byte order*/ {
00851             long ix;
00852             long ct = databytes / sizeof (tyavailnodeshadow);
00853             register tyavailnodeshadow* p = (tyavailnodeshadow *) *h;
00854 
00855             for (ix = 0; ix < ct; ix++) {
00856                 memtodisklong (p[ix].adr);
00857                 memtodisklong (p[ix].size);
00858                 }
00859             }
00860         #endif
00861 
00862         lockhandle (h);
00863         
00864         fl = dbreadheader (adrblock, &flfree, &nodebytes, &dummy);
00865         
00866         assert (databytes <= nodebytes);
00867         
00868         fl = fl && dbwritedatablock (adrblock, databytes, nodebytes, *h);
00869         
00870         unlockhandle (h);
00871 
00872         disposehandle (h);
00873         
00874         if (!fl)
00875             goto error;
00876         }
00877     
00878     fl = true;
00879 
00880 error:
00881 
00882     (**hdb).u.extensions.availlistblock = fl ? adrblock : nildbaddress;
00883 
00884     setdirty (hdb);
00885     
00886     dbflushheader ();
00887 
00888     return (fl);
00889     }/*dbwriteshadowavaillist*/
00890 
00891 
00892 static boolean dbreadshadowavaillist (void) {
00893 
00894     /*
00895     6.2a9 AR: If the availlistblock was set in the database header, go in and
00896     read the avail list block from the end of the database instead of chaining
00897     thru the linked list of free blocks. Nuke the reference to the availlistblock
00898     in the db header asap, so if we crash somewhere down the road, we won't read
00899     an inconsistent availlistblock the next time we open the db.
00900     
00901     Also see dbwriteshadowavaillist.
00902     */
00903 
00904     register hdldatabaserecord hdb = databasedata;
00905     dbaddress adrblock;
00906     hdlavaillistshadow h;
00907     handlestream s;
00908     long dbeof;
00909 
00910     assert (hdb != nil);
00911     
00912     if (!dbgeteof (&dbeof))
00913         return (false);
00914     
00915     adrblock = (**hdb).u.extensions.availlistblock;
00916 
00917     if (adrblock == nildbaddress) /*for safety*/
00918         return (false);
00919 
00920     if (!dbrefhandle (adrblock, (Handle*) &h))
00921         return (false);
00922 
00923 #ifdef SWAP_BYTE_ORDER
00924     /*switch byte order*/ {
00925         long ix;
00926         long ct = gethandlesize ((Handle) h) / sizeof (tyavailnodeshadow);
00927         register tyavailnodeshadow* p = *h;
00928 
00929         for (ix = 0; ix < ct; ix++) {
00930             disktomemlong (p[ix].adr);
00931             disktomemlong (p[ix].size);
00932             }
00933         }
00934 #endif
00935 
00936     /*Test consistency of cached shadow avail list*/
00937     
00938     if ((**h).adr != (**hdb).availlist) {
00939         
00940         dblogerror (dbinconsistentavaillisterror);
00941         
00942         disposehandle ((Handle) h);
00943     
00944         return (false);
00945         }
00946 
00947     if ((**h).adr != nildbaddress) {
00948 
00949         long availbytes;
00950         dbaddress firstavail = (**h).adr;
00951         dbaddress nextavail;
00952         boolean flfree;
00953     
00954         if (!dbreadavailnode (firstavail, &flfree, &availbytes, &nextavail)
00955                 || !flfree || firstavail + availbytes > dbeof) {
00956             
00957             dblogerror (dbinconsistentavaillisterror);
00958             
00959             disposehandle ((Handle) h);
00960         
00961             return (false);
00962             }
00963         }
00964 
00965     openhandlestream ((Handle)h, &s);
00966 
00967 #if 0 //DATABASE_DEBUG //6.2b7 AR: debugging code disabled
00968 
00969     /*verify cached version of avail list*/ {
00970 
00971         tyavailnodeshadow diskavailrec;
00972         tyavailnodeshadow memavailrec;
00973         dbaddress nextavail;
00974         boolean flfree;
00975         long ix = 0;
00976     
00977         diskavailrec.adr = (**hdb).availlist;
00978 
00979         while (diskavailrec.adr != nildbaddress) {
00980             
00981             if (!dbreadavailnode (diskavailrec.adr, &flfree, &diskavailrec.size, &nextavail) ||
00982                 !flfree ||
00983                 diskavailrec.adr + diskavailrec.size > dbeof) {
00984 
00985                 diskavailrec.adr = nildbaddress;
00986                 
00987                 assert (false);
00988 
00989                 break;
00990                 }
00991             
00992             memavailrec = ((tyavailnodeshadow*)(*s.data)) [ix++];
00993 
00994             assert (diskavailrec.adr == memavailrec.adr);
00995             
00996             assert (diskavailrec.size == memavailrec.size);
00997 
00998             assert (ix * sizeof (tyavailnodeshadow) < s.eof);
00999 
01000             diskavailrec.adr = nextavail;
01001             
01002             rollbeachball ();
01003             } /*while*/ 
01004         
01005         diskavailrec.size = 0;
01006 
01007         memavailrec = ((tyavailnodeshadow*)(*s.data)) [ix++];
01008 
01009         assert (diskavailrec.adr == memavailrec.adr);
01010             
01011         assert (diskavailrec.size == memavailrec.size);
01012 
01013         assert (ix * sizeof (tyavailnodeshadow) == s.eof);
01014         }
01015 #endif
01016         
01017     (**hdb).u.extensions.availlistshadow = s;
01018     
01019     return (true);
01020     }/*dbreadshadowavaillist*/
01021 
01022 
01023 static boolean dbshadowavaillist (void) {
01024     
01025     /*
01026     5.1.5 dmb: read the entire avail list into memory. the last record in 
01027     the shadow array is {0, 0}
01028     
01029     6.2a9 AR: streamlined usage of handlestream, no longer keep separate
01030     local havaillist handle around which would interfere with the new
01031     shadow avail list caching in the db. (see dbwriteshadowavaillist)
01032     
01033     If dbreadschadowaviallist doesn't succeed, we try the old-fashioned way.
01034     */
01035     
01036     handlestream s;
01037     tyavailnodeshadow availrec;
01038     dbaddress nextavail;
01039     boolean flfree;
01040     long dbeof;
01041 
01042 #ifdef SMART_DB_OPENING 
01043     if ((**databasedata).u.extensions.availlistblock != nildbaddress)
01044         if (dbreadshadowavaillist ())
01045             return (true);
01046 #endif
01047 
01048     if (!dbgeteof (&dbeof))
01049         return (false);
01050     
01051     openhandlestream (nil, &s);
01052     
01053     availrec.adr = (**databasedata).availlist;
01054     
01055     while (availrec.adr != nildbaddress) {
01056         
01057         if (!dbreadavailnode (availrec.adr, &flfree, &availrec.size, &nextavail) ||
01058             !flfree ||
01059             availrec.adr + availrec.size > dbeof) {
01060 
01061             availrec.adr = nildbaddress;
01062             
01063             dberror (dbfreelisterror);
01064 
01065             break;
01066             }
01067         
01068         if (!writehandlestream (&s, &availrec, sizeof (availrec)))
01069             goto error;
01070         
01071         availrec.adr = nextavail;
01072         
01073         rollbeachball ();
01074         } /*while*/ 
01075     
01076     availrec.size = 0;
01077     
01078     if (!writehandlestream (&s, &availrec, sizeof (availrec)))
01079         goto error;
01080     
01081     (**databasedata).u.extensions.availlistshadow = s;
01082     
01083     return (true);
01084     
01085     error:
01086         disposehandlestream (&s);
01087         
01088         return (false);
01089     } /*dbshadowavaillist*/
01090 
01091 
01092 static boolean dbinsertavailshadow (long ixshadow, dbaddress adr, long ctbytes) {
01093     
01094     handlestream s = (**databasedata).u.extensions.availlistshadow;
01095     tyavailnodeshadow avail;
01096     
01097     assert ((ixshadow >= 0) && (ixshadow <= s.eof / (long) sizeof (tyavailnodeshadow)));
01098     
01099     avail.adr = adr;
01100     
01101     avail.size = ctbytes;
01102     
01103     s.pos = ixshadow * sizeof (tyavailnodeshadow);
01104     
01105     if (!mergehandlestreamdata (&s, 0L, &avail, sizeof (avail)))
01106         return (false);
01107     
01108     (**databasedata).u.extensions.availlistshadow = s;
01109     
01110     return (true);
01111     } /*dbinsertavailshadow*/
01112 
01113 
01114 static boolean dbdeleteavailshadow (long ixshadow) {
01115 
01116     handlestream s = (**databasedata).u.extensions.availlistshadow;
01117     
01118     assert ((ixshadow >= 0) && (ixshadow < s.eof / (long) sizeof (tyavailnodeshadow)));
01119     
01120     s.pos = ixshadow * sizeof (tyavailnodeshadow);
01121     
01122     if (!pullfromhandlestream (&s, sizeof (tyavailnodeshadow), nil))
01123         return (false);
01124     
01125     (**databasedata).u.extensions.availlistshadow = s;
01126     
01127     return (true);
01128     } /*dbdeleteavailshadow*/
01129 
01130 
01131 static boolean dbsetavailshadow (long ixshadow, dbaddress adr, long ctbytes) {
01132 
01133     handlestream s = (**databasedata).u.extensions.availlistshadow;
01134     tyavailnodeshadow avail;
01135     
01136     assert ((ixshadow >= 0) && (ixshadow < s.eof / (long) sizeof (tyavailnodeshadow)));
01137     
01138     avail.adr = adr;
01139     
01140     avail.size = ctbytes;
01141     
01142     s.pos = ixshadow * sizeof (tyavailnodeshadow);
01143     
01144     if (!mergehandlestreamdata (&s, sizeof (avail), &avail, sizeof (avail)))
01145         return (false);
01146     
01147     (**databasedata).u.extensions.availlistshadow = s;
01148     
01149     return (true);
01150     } /*dbsetavailshadow*/
01151 
01152 
01153 static boolean dbgetsizeandvariance (dbaddress adr, long *size, tyvariance *variance) {
01154 
01155     /*
01156     give me the address of a database block and I'll return the number
01157     of bytes it has reserved.  the variance is the number of unused bytes
01158     that are the result of block-splitting in allocate.
01159     */
01160     
01161     boolean flfree;
01162     
01163     return (dbreadheader (adr, &flfree, size, variance));
01164     } /*dbgetsizeandvariance*/
01165     
01166 
01167 static boolean dbsetsize (dbaddress adr, long size, tyvariance variance) {
01168     
01169     return (dbwriteheader (adr, false, size, variance));
01170     } /*dbsetsize*/
01171     
01172 
01173 boolean dbreference (dbaddress adr, long maxbytes, ptrvoid pdata) {
01174 
01175     /*
01176     copy into pdata the database block located at address.  the number
01177     of bytes is found in the header/trailer word at the beginning of
01178     the block.
01179     
01180     under no circumstances will we read in more than maxbytes.  the caller
01181     should supply us with the size of pdata in this argument, it prevents
01182     disastrous overwriting of memory.
01183     
01184     each block also records a variance -- the number of extra bytes 
01185     due to block-splitting in allocate.  we only copy the number of
01186     bytes that actually hold the caller's data, probably saves a little
01187     time, and keeps us from overwriting other important stuff!
01188     */
01189     
01190     long ctbytes;
01191     boolean flfree;
01192     tyvariance variance;
01193     
01194     if (!dbreadheader (adr, &flfree, &ctbytes, &variance))
01195         return (false);
01196         
01197     if (flfree || (ctbytes < 0)) { /*referencing a free node -- probably a bad address*/
01198         
01199         dberror (dbfreeblockerror);
01200         
01201         return (false);
01202         }
01203     
01204     return (dbread (adr + sizeheader, min (maxbytes, ctbytes - (long) variance), pdata));
01205     } /*dbreference*/
01206     
01207 
01208 boolean dbrefhandle (dbaddress adr, Handle *h) {
01209 
01210     /*
01211     copy a block from the database into a handle which we allocate.
01212     
01213     the caller must dispose of the handle.
01214     
01215     5.0.1 dmb: added freeblock error; don't fail silently
01216     */
01217     
01218     register dbaddress a = adr;
01219     register boolean fl;
01220     register Handle hregister;
01221     register long ct;
01222     long ctbytes;
01223     boolean flfree;
01224     tyvariance variance;
01225         
01226     *h = nil;
01227         
01228     if (a == nildbaddress) /*defensive driving*/
01229         return (false);
01230     
01231     if (!dbreadheader (a, &flfree, &ctbytes, &variance))
01232         return (false);
01233         
01234     ct = ctbytes - (long) variance;
01235     
01236     if (flfree || (ct < 0)) { /*probably a bad address*/
01237         
01238         dberror (dbfreeblockerror);
01239         
01240         return (false);
01241         }
01242         
01243     if (!newclearhandle (ct, h))
01244         return (false);
01245     
01246     hregister = *h;
01247     
01248     lockhandle (hregister);
01249     
01250     fl = dbread (a + sizeheader, ct, *hregister);
01251     
01252     unlockhandle (hregister);
01253     
01254     return (fl);
01255     } /*dbrefhandle*/
01256     
01257 
01258 #if 0
01259 
01260 static boolean dbrefbytes (dbaddress adr, long ctwanted, ptrvoid pdata) {
01261 
01262     /*
01263     copy into pdata the database block located at address.  this call
01264     is used when you want fewer than the "natural" number of bytes that
01265     dbreference returns.
01266     
01267     5.0.1 dmb: added freeblock error; don't fail silently
01268     */
01269     
01270     long ctbytes;
01271     boolean flfree;
01272     tyvariance variance;
01273     
01274     if (!dbreadheader (adr, &flfree, &ctbytes, &variance))
01275         return (false);
01276         
01277     if (flfree || (ctbytes < 0)) { /*referencing a free node -- probably a bad address*/
01278         
01279         dberror (dbfreeblockerror);
01280         
01281         return (false);
01282         }
01283     
01284     ctwanted = min (ctwanted, ctbytes - (long) variance);
01285     
01286     return (dbread (adr + sizeheader, ctwanted, pdata));
01287     } /*dbrefbytes*/
01288 
01289 #endif  
01290 
01291 
01292 static boolean dballocate (long databytes, ptrvoid pdata, dbaddress *paddress) {
01293 
01294     /*
01295     allocate databytes space in the database.  return the database address of the
01296     allocated space in paddress.  if allocation error, paddress == nildbaddress.
01297     
01298     the caller can supply the address of data to be saved in the database, if its
01299     not nil, we will copy the data into the file before returning.
01300 
01301     4.1b9 dmb: removed special case check for nil prevnomad; dbsetavaillink handles
01302     that case.
01303     
01304     5.1.5b1 dmb: use and maintain availlist shadow
01305     */
01306 
01307     long origeof;
01308     long nodebytes, newnodebytes;
01309     //boolean flfree;
01310     dbaddress nomad, prevnomad, nextnomad;
01311     tyvariance variance;
01312     long smallestinterestingblock;
01313     long ctalloc;
01314     
01315 #if fldebug
01316     allocs++;
01317 #endif
01318 
01319 #ifdef SMART_DB_OPENING 
01320     dbclearshadowavaillist (); /*6.2b12 AR*/
01321 #endif
01322 
01323     dbswapglobals (); /*use databasedestination*/
01324     
01325     smallestinterestingblock = max (databytes, (long) minblocksize);
01326     
01327 #ifdef dbshadow
01328     {
01329     hdldatabaserecord hdb = databasedata;
01330     hdlavaillistshadow havailshadow = (hdlavaillistshadow) (**hdb).u.extensions.availlistshadow.data;
01331     long i, ctavail = (**hdb).u.extensions.availlistshadow.eof / sizeof (tyavailnodeshadow);
01332     
01333     for (i = 0, prevnomad = nildbaddress; i < ctavail; ++i, prevnomad = nomad) {
01334 
01335 #if fldebug
01336         allocloops++;
01337 #endif
01338 
01339         nomad = (*havailshadow) [i].adr;
01340         
01341         if (nomad == nildbaddress)
01342             break;
01343         
01344         nodebytes = (*havailshadow) [i].size;
01345         
01346         if (nodebytes < smallestinterestingblock) //too small to be of interest
01347             continue;
01348 
01349         /*found a block to allocate off avail list*/
01350         
01351         //if (!dbreadavailnode (nomad, &flfree, &nodebytes, &nextnomad))
01352         //  goto failure;
01353         
01354         nextnomad = (*havailshadow) [i + 1].adr;
01355         
01356         //assert (nodebytes == (*havailshadow) [i].size);
01357         
01358         variance = nodebytes - databytes; //how much more we got than what we asked for
01359         
01360         if (variance >= (minblocksize + sizeheader + sizetrailer)) { //split into two blocks
01361             
01362             newnodebytes = nodebytes - (databytes + sizeheader + sizetrailer);
01363             
01364             if (!dbwriteheaderandtrailer (nomad, true, newnodebytes, (tyvariance) 0))
01365                 goto failure;
01366             
01367             dbsetavailshadow (i, nomad, newnodebytes);
01368             
01369             nomad += sizeheader + newnodebytes + sizetrailer;
01370             
01371             if (!dbwritedatablock (nomad, databytes, databytes, pdata))
01372                 goto failure;
01373             
01374             *paddress = nomad; /*use the newly split off block*/
01375             
01376 
01377 #if fldebug
01378             splits++;
01379 #endif
01380 
01381             goto success;
01382             } /*splitting into two blocks*/
01383         
01384         if (!dbwritedatablock (nomad, databytes, nodebytes, pdata))
01385             goto failure;
01386         
01387 
01388 #if fldebug
01389         nonsplits++;
01390 #endif      
01391 
01392         *paddress = nomad;
01393         
01394         dbdeleteavailshadow (i);
01395         
01396         if (!dbsetavaillink (prevnomad, nextnomad)) /*unlink node from avail list*/
01397             goto failure;
01398         
01399         goto success;
01400         }
01401     }
01402 #else
01403     nomad = (**databasedata).availlist;
01404     
01405     prevnomad = nildbaddress; /*no previous node*/
01406     
01407     while (nomad != nildbaddress) { /*look at each element on the avail list, first-fit*/
01408         
01409         if (!dbreadavailnode (nomad, &flfree, &nodebytes, &nextnomad))
01410             goto failure;
01411         
01412         if (nodebytes < smallestinterestingblock) /*too small to be of interest*/
01413             goto nextloop;
01414         
01415         /*found a block to allocate off avail list*/
01416         
01417         variance = nodebytes - databytes; /*how much more we got than what we asked for*/
01418         
01419         if (variance >= (minblocksize + sizeheader + sizetrailer)) { /*split into two blocks*/
01420             
01421             newnodebytes = nodebytes - (databytes + sizeheader + sizetrailer);
01422             
01423             if (!dbwriteheaderandtrailer (nomad, true, newnodebytes, (tyvariance) 0))
01424                 goto failure;
01425                 
01426             nomad += sizeheader + newnodebytes + sizetrailer;
01427             
01428             if (!dbwritedatablock (nomad, databytes, databytes, pdata))
01429                 goto failure;
01430                 
01431             *paddress = nomad; /*use the newly split off block*/
01432             
01433 
01434 #if fldebug
01435             splits++;
01436 #endif          
01437 
01438             goto success;
01439             } /*splitting into two blocks*/
01440             
01441         if (!dbwritedatablock (nomad, databytes, nodebytes, pdata))
01442             goto failure;
01443 
01444 #if fldebug     
01445         nonsplits++;
01446 #endif
01447 
01448         *paddress = nomad;
01449         
01450         if (!dbsetavaillink (prevnomad, nextnomad)) /*unlink node from avail list*/
01451             goto failure;
01452         
01453         goto success;
01454         
01455         nextloop:
01456         
01457         prevnomad = nomad; /*remember in case we have to unlink the next one*/
01458         
01459         nomad = nextnomad; /*advance to next node in the avail list*/
01460         } /*while*/
01461 #endif
01462 
01463 #if fldebug 
01464     newallocs++;
01465 #endif
01466 
01467     if (!dbgeteof (&origeof))
01468         goto failure;
01469     
01470     if (databytes < minblocksize) { /*we never alloc a block smaller than minblocksize*/
01471         
01472         ctalloc = minblocksize;
01473         
01474         variance = minblocksize - databytes;
01475         }
01476     else {
01477     
01478         ctalloc = databytes;
01479         
01480         variance = 0;
01481         }
01482         
01483     if (!dbseteof (origeof + sizeheader + ctalloc + sizetrailer))
01484         goto failure;
01485         
01486     if (!dbwritedatablock (origeof, databytes, ctalloc, pdata))     
01487         goto failure;
01488     
01489     *paddress = origeof; /*this is the address of the block we allocated*/
01490     
01491     
01492     success:
01493     
01494     dbswapglobals (); /*restore*/
01495     
01496     return (true); /*the allocation was successful*/
01497     
01498     
01499     failure:
01500     
01501     dbswapglobals (); /*restore*/
01502     
01503     return (false);
01504     } /*dballocate*/
01505 
01506 
01507 static boolean dbmergeleft (boolean flmerged, dbaddress adr, boolean* ptrflmergedleft) {
01508 
01509     /*
01510     try to merge the database block pointed to by adr with the block to its
01511     left.  this often may not be possible because the block to the left may
01512     or may not be free, or even may not exist.  return true if it worked, 
01513     false otherwise.
01514     
01515     we do nothing to the avail list if we merge.  we assume that the free
01516     block to the left is already on the avail list.
01517     
01518     flmerged tells us whether a right-merge has already been performed.  if
01519     so, the node at adr is on the available list and must be popped off the
01520     list if a left-merge takes place.
01521     
01522     5.1.5b1 dmb: maintain availlist shadow
01523     */
01524     
01525     dbaddress newadr;
01526     long newsize;
01527     boolean flfree, flleftfree;
01528     long ctbytes, ctleftbytes;
01529     dbaddress nextavail, prevavail;
01530     long ixshadow;
01531     
01532     *ptrflmergedleft = false; /*default return value*/
01533     
01534     if (adr == firstphysicaladdress) /*nothing to the left, other than the header!*/
01535         return (true);
01536 
01537     if (adr < firstphysicaladdress) { /*nothing to the left, other than the header!*/
01538 
01539         dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01540 
01541         return (false);
01542         }
01543     
01544     if (!dbreadtrailer (adr - sizetrailer, &flleftfree, &ctleftbytes))
01545         return (false);
01546 
01547     if (ctleftbytes < minblocksize) { /*probably an invalid block*/
01548 
01549         dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01550 
01551         return (false);
01552         }
01553 
01554     if (!flleftfree) /*can't merge if block to left is not free*/
01555         return (true);
01556 
01557 #ifdef fldebug //DATABASE_DEBUG
01558     {
01559         long leftblockadr = adr - sizetrailer - ctleftbytes - sizeheader;
01560         long dbeof;
01561         boolean flfreeheader;
01562         long ctbytesheader;
01563         tyvariance variance;
01564 
01565         if (!dbgeteof (&dbeof))
01566             return (false);
01567 
01568         if (leftblockadr < firstphysicaladdress) {
01569 
01570             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01571 
01572             return (false);
01573             }
01574 
01575         if (leftblockadr > dbeof) {
01576 
01577             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01578 
01579             return (false);
01580             }
01581 
01582         if (!dbreadheader (leftblockadr, &flfreeheader, &ctbytesheader, &variance))
01583             return (false);
01584 
01585         if (!flfreeheader) { /*the trailer said otherwise!*/
01586 
01587             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01588 
01589             return (false);
01590             }
01591         
01592         if (ctbytesheader != ctleftbytes) {
01593 
01594             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01595 
01596             return (false);
01597             }
01598     }
01599 #endif
01600 
01601     if (!dbreadavailnode (adr, &flfree, &ctbytes, &nextavail)) /*get data about our block*/
01602         return (false);
01603     
01604     if (flmerged) { /*the node we're releasing is already on the avail list, pop him!*/
01605         
01606         if (!dbfindpreviousavail (adr, &prevavail, &ixshadow))
01607             return (false); /*damaged free list*/
01608         
01609         assert ((*(hdlavaillistshadow)(**databasedata).u.extensions.availlistshadow.data) [ixshadow + 1].adr == nextavail);
01610         
01611         if (!dbsetavaillink (prevavail, nextavail)) /*point around the soon-to-be-defunct node*/
01612             return (false);
01613             
01614         if (!dbdeleteavailshadow (ixshadow))
01615             return (false);
01616         }
01617     
01618     newsize = ctleftbytes + ctbytes + sizeheader + sizetrailer; /*start merging*/
01619     
01620     newadr = adr - sizetrailer - ctleftbytes - sizeheader;
01621     
01622     if (!dbwriteheaderandtrailer (newadr, true, newsize, 0L)) //avail link already set
01623         return (false);
01624     
01625     if (!dbfindpreviousavail (newadr, &prevavail, &ixshadow)) // don't need prev, just index
01626         return (false);
01627 
01628     dbsetavailshadow (ixshadow, newadr, newsize);
01629     
01630 #if fldebug
01631     leftmerges++;
01632 #endif
01633 
01634     *ptrflmergedleft = true; /*actually merged*/
01635     
01636     return (true);
01637     } /*dbmergeleft*/
01638 
01639 
01640 static boolean dbmergeright (dbaddress adr, long ctbytes, boolean* ptrflmergedright) {
01641 
01642     /*
01643     try to merge the database block pointed to by adr with the block to its
01644     right.  this often may not be possible because the block to the right may
01645     or may not be free, or even may not exist.  return true if we merged, 
01646     false otherwise.
01647     
01648     we also adjust the available list if we merge.  it must point at the 
01649     beginning of the two merged blocks.
01650     
01651     we don't need to change the address because even if we merge, the address
01652     of the merged blocks is the same as adr.
01653     
01654     5.1.5b1 dmb: take ctbytes parameter so we don't need to re-read header;
01655     maintain availlist shadow
01656 
01657     6.2b5 AR: Do writes for merged block sequentially
01658     */
01659     
01660     long eof;
01661     dbaddress rightblockadr;
01662     boolean flrightfree;
01663     long ctrightbytes;
01664     dbaddress prevavail, nextavail;
01665     long ixshadow;
01666     
01667     *ptrflmergedright = false;
01668     
01669     rightblockadr = adr + sizeheader + ctbytes + sizetrailer;
01670     
01671     if (!dbgeteof (&eof))
01672         return (false);
01673 
01674     if (rightblockadr == eof) /*there is no block to the right*/
01675         return (true);
01676 
01677     if (rightblockadr > eof) { /*reached the end of the file*/
01678 
01679         dblogerror (dbmergeinvalidblockerror);
01680 
01681         return (false);
01682         }
01683 
01684     if (!dbreadavailnode (rightblockadr, &flrightfree, &ctrightbytes, &nextavail))
01685         return (false);
01686 
01687     if (ctrightbytes < minblocksize) {
01688 
01689         dblogerror (dbmergeinvalidblockerror);
01690 
01691         return (false); //not likely to be a valid block, probably just a stream of nil bytes
01692         }
01693     
01694     if (!flrightfree) /*the block to the right is in use*/
01695         return (true);
01696     
01697 #ifdef fldebug //DATABASE_DEBUG
01698     {
01699         long traileradr = rightblockadr + sizeheader + ctrightbytes;
01700         boolean flfreetrailer;
01701         long ctbytestrailer;
01702 
01703         if (traileradr < firstphysicaladdress) {
01704 
01705             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01706 
01707             return (false);
01708             }
01709 
01710         if (traileradr > eof) {
01711 
01712             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01713 
01714             return (false);
01715             }
01716 
01717         if (!dbreadtrailer (traileradr, &flfreetrailer, &ctbytestrailer))
01718             return (false);
01719 
01720         if (!flfreetrailer) { /*the header said otherwise!*/
01721 
01722             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01723 
01724             return (false);
01725             }
01726         
01727         if (ctbytestrailer != ctrightbytes) {
01728 
01729             dblogerror (dbmergeinvalidblockerror); /*illegal address*/
01730 
01731             return (false);
01732             }
01733     }
01734 #endif
01735 
01736     if (!dbfindpreviousavail (rightblockadr, &prevavail, &ixshadow))
01737         return (false);
01738     
01739     assert ((*(hdlavaillistshadow)(**databasedata).u.extensions.availlistshadow.data) [ixshadow + 1].adr == nextavail);
01740 
01741     if (!dbsetavaillink (prevavail, adr)) /*point at beginning of two merged blocks*/
01742         return (false);
01743         
01744     ctbytes += ctrightbytes + sizeheader + sizetrailer;
01745 
01746     if (!dbwriteavailnode (adr, ctbytes, nextavail))
01747         return (false);
01748 
01749     if (!dbsetavailshadow (ixshadow, adr, ctbytes))
01750         return (false);
01751         
01752 #if fldebug
01753     rightmerges++;
01754 #endif
01755 
01756     *ptrflmergedright = true; /*actually merged*/
01757     
01758     return (true); /*actually merged*/
01759     } /*dbmergeright*/
01760     
01761     
01762 static boolean dbrelease (dbaddress adr) {
01763 
01764     /*
01765     release the database block at adr.
01766     
01767     try to merge it with the block to the left and then with the block to right.
01768     
01769     push any new free block(s) on the available list.
01770     
01771     5.1.4 dmb: do all three writes sequentially (write availlink before trailer)
01772 
01773     6.2b3 AR: Change in our philosophy: We no longer consider it a big deal if releasing
01774     a block fails, but make absolutely sure we don't corrupt the database by releasing a non-existant
01775     block in the database. Most callers now ignore our return value.
01776     */
01777     
01778     boolean flmergedleft, flmergedright;
01779     boolean flfree;
01780     long ctbytes;
01781     tyvariance variance;
01782      
01783     if (adr == nildbaddress) /*its easy to release the nil node*/
01784         return (true);
01785     
01786 #ifdef SMART_DB_OPENING 
01787     dbclearshadowavaillist (); /*6.2b12 AR*/
01788 #endif
01789 
01790     if (!dbreadheader (adr, &flfree, &ctbytes, &variance)) 
01791         return (false);
01792     
01793     if (flfree) { /*nasty internal error - block is already free*/
01794         
01795         dberror (dbreleasefreeblockerror);
01796         
01797         return (false);
01798         }
01799 
01800 #ifdef fldebug //DATABASE_DEBUG
01801     /*check header/trailer consistency*/ {
01802 
01803         boolean flfreetrailer;
01804         long ctbytestrailer;
01805         dbaddress traileradr = adr + sizeheader + ctbytes;
01806         long dbeof;
01807 
01808         if (!dbgeteof (&dbeof))
01809             return (false);
01810 
01811         if (traileradr > dbeof) { /*nasty internal error - probably not a valid address*/
01812             
01813             dblogerror (dbreleaseinvalidblockerror);
01814             
01815             return (false);
01816             }
01817 
01818         if (!dbreadtrailer (traileradr, &flfreetrailer, &ctbytestrailer))
01819             return (false);
01820 
01821         if (flfreetrailer) { /*nasty internal error - probably not a valid address*/
01822             
01823             dblogerror (dbreleaseinvalidblockerror);
01824             
01825             return (false);
01826             }
01827 
01828         if (ctbytes != ctbytestrailer) { /*nasty internal error - probably not a valid address*/
01829             
01830             dblogerror (dbreleaseinvalidblockerror);
01831             
01832             return (false);
01833             }
01834         }
01835 #endif
01836     
01837     if (!dbmergeright (adr, ctbytes, &flmergedright))
01838         return (false);
01839     
01840     if (!dbmergeleft (flmergedright, adr, &flmergedleft))
01841         return (false);
01842         
01843     if (flmergedleft || flmergedright)
01844         return (true); /*we're done*/
01845     
01846     /*no merging -- set free bits in header & trailer, insert at head of avail list*/
01847     
01848     if (!dbwriteavailnode (adr, ctbytes, (**databasedata).availlist))
01849         return (false);
01850     
01851     (**databasedata).availlist = adr;
01852     
01853     if (!dbinsertavailshadow (0, adr, ctbytes))
01854         return (false);
01855     
01856     dbheaderdirty ();
01857     
01858     return (true);
01859     } /*dbrelease*/
01860     
01861 
01862 #if 0
01863 
01864 static boolean dbreadbytes (dbaddress adr, long offset, long ctbytes, char *pdata) {
01865 
01866     /*
01867     copy from the data part of the block at adr, at given offset into memory.
01868     
01869     this is useful if you want to stream a known amount of data out of a database block
01870     but don't want to allocate a temporary buffer to hold it all.
01871     */
01872     
01873     return (dbread (adr + sizeheader + offset, ctbytes, pdata));
01874     } /*dbreadbytes*/
01875     
01876 
01877 static boolean dbwritebytes (dbaddress adr, long offset, long ctbytes, char *pdata) {
01878 
01879     /*
01880     copy the data from memory to the data part of the block at adr, at given offset.
01881     
01882     this is useful if you want to stream a known amount of data into a database block
01883     but don't want to allocate a temporary buffer to hold it all.
01884     */
01885     
01886     return (dbwrite (adr + sizeheader + offset, ctbytes, pdata));
01887     } /*dbwritebytes*/
01888 
01889 #endif
01890     
01891 
01892 static boolean dbmove (ptrvoid pdata, long ctbytes, dbaddress adr) {
01893     
01894     /*
01895     copy the data from memory (pdata) to the data part of the block at adr.
01896     
01897     call this when you know that the size of the object you're writing is the
01898     same as the object this block was created to hold.
01899     */
01900     
01901     return (dbwrite (adr + sizeheader, ctbytes, pdata)); 
01902     } /*dbmove*/
01903     
01904 
01905 boolean dbassign (dbaddress *padr, long newsize, ptrvoid pdata) {
01906     
01907     /*
01908     we want to move new data into the database block whose address is adr.
01909     
01910     maybe the size has changed?  think about variable length strings.  if so, the
01911     new size is given in newsize. 
01912     
01913     we get a pointer to the address because we might change the address if we have
01914     to allocate to fit new larger data.  we never re-allocate a block if the data
01915     got smaller.
01916     
01917     10/16/91 dmb: found longstanding bug.  the variance must we updated any time the 
01918     size changes, not just when the newsize is less than cttotal
01919 
01920     6.2b2 AR: Improved error checking based on the assumption that we should
01921     never assign to a free block -- except when saving a copy, of course.
01922     */
01923     
01924     register dbaddress adr;
01925     tyvariance ctunused;
01926     long cttotal;
01927     boolean flfree;
01928     
01929     adr = *padr; /*copy into a register*/
01930     
01931     if (fldatabasesaveas || (adr == nildbaddress)) /*no previous allocation, create a new one*/
01932         return (dballocate (newsize, pdata, padr)); 
01933     
01934     if (!dbreadheader (adr, &flfree, &cttotal, &ctunused)) /*find out how much space we have in block*/
01935         return (false);
01936 
01937     if (flfree) { /*6.2b2 AR: here's another chance to easily detect corruption, why not use it?*/
01938 
01939         dberror (dbassignfreeblockerror);
01940     
01941         return (false);
01942         }
01943     
01944     if (newsize > cttotal) { /*there isn't enough room*/
01945 
01946         if (!dbrelease (adr)) { //ignore return value, don't want to abort saving
01947             #ifdef DATABASE_DEBUG
01948                 char str[256];
01949 
01950                 sprintf (str, "dbrelease failed for address %ld.", adr);
01951 
01952                 DB_MSG_2 (str);
01953             #endif
01954             }
01955 
01956         return (dballocate (newsize, pdata, padr)); /*allocate the new, bigger block*/
01957         }
01958     
01959     if (newsize != cttotal - ctunused) /*must update the variance*/
01960     
01961         if (!dbsetsize (adr, cttotal, cttotal - newsize))
01962         
01963             return (false);
01964         
01965     return (dbmove (pdata, newsize, adr)); /*copy the data into a big-enough block*/
01966     } /*dbassign*/
01967     
01968     
01969 static boolean dbgetsize (dbaddress adr, long *logicalsize) {
01970 
01971     /*
01972     give me the address of a database block and I'll return the number
01973     of logical bytes it is using.
01974     */
01975     
01976     tyvariance size, variance;
01977     
01978     *logicalsize = 0;
01979     
01980     if (adr == nildbaddress) 
01981         return (false);
01982     
01983     if (!dbgetsizeandvariance (adr, &size, &variance))      
01984         return (false);
01985         
01986     *logicalsize = size - variance;
01987     
01988     return (true);
01989     } /*dbgetsize*/
01990 
01991 
01992 boolean dbcopy (dbaddress adrorig, dbaddress *adrcopy) {
01993     
01994     /*
01995     create a copy of the database block pointed to by adrorig.  return
01996     true if adrcopy has the address of a new block, the same logical size
01997     as the original with a copy of the original's data.
01998     */
01999     
02000     register boolean flreturned;
02001     Handle hnew;
02002     register Handle h;
02003     long size;
02004     
02005     if (adrorig == nildbaddress) { /*it's very easy to copy the nil node*/
02006         
02007         *adrcopy = nildbaddress;
02008         
02009         return (true);
02010         }
02011     
02012     if (!dbgetsize (adrorig, &size))
02013         return (false);
02014     
02015     if (!newhandle (size, &hnew)) /*not enough room in the heap*/
02016         return (false);
02017     
02018     h = hnew; /*copy into register*/
02019     
02020     lockhandle (h);
02021     
02022     flreturned = false; /*default*/
02023     
02024     if (dbreference (adrorig, size, *h))
02025     
02026         flreturned = dballocate (size, *h, adrcopy);
02027     
02028     unlockhandle (h);
02029     
02030     disposehandle (h);
02031     
02032     return (flreturned);
02033     } /*dbcopy*/
02034     
02035     
02036 static boolean dballocstring (dbaddress *adr, bigstring bs) {
02037     
02038     return (dballocate ((long) stringlength(bs) + 1, bs, adr));
02039     } /*dballocstring*/
02040     
02041 
02042 static boolean dbrefstring (dbaddress adr, bigstring bs) {
02043     
02044     setstringlength (bs, 0);
02045         
02046     if (adr == nildbaddress) /*nil adr represents an empty string, saves time & space*/
02047         return (true);
02048         
02049     return (dbreference (adr, sizeof (bigstring), bs));
02050     } /*dbrefstring*/
02051     
02052     
02053 static boolean dbassignstring (dbaddress *adr, bigstring bs) {
02054     
02055     if (*adr == nildbaddress) 
02056         return (dballocstring (adr, bs));
02057     else
02058         return (dbassign (adr, (long) stringlength(bs) + 1, bs));
02059     } /*dbassignstring*/
02060     
02061     
02062 static boolean dbreleasestring (dbaddress adr) {
02063 
02064     if (!dbrelease (adr)) {
02065         #ifdef DATABASE_DEBUG
02066             char str[256];
02067 
02068             sprintf (str, "dbrelease failed for address %ld.", adr);
02069 
02070             DB_MSG_2 (str);
02071         #endif
02072 
02073         return (false);
02074         }
02075 
02076     return (true);
02077     } /*dbreleasestring*/
02078     
02079 
02080 boolean dbrefheapstring (dbaddress adr, hdlstring *hstring) {
02081     
02082     bigstring bs;
02083     
02084     if (!dbrefstring (adr, bs))
02085         return (false);
02086         
02087     return (newheapstring (bs, hstring));
02088     } /*dbrefheapstring*/
02089     
02090 
02091 boolean dbassignheapstring (dbaddress *adr, hdlstring hstring) {
02092 
02093     bigstring bs;
02094     
02095     copyheapstring (hstring, bs); /*checks for hstring == nil*/
02096     
02097     if (isemptystring (bs)) {
02098     
02099         if (!fldatabasesaveas)
02100             dbreleasestring (*adr); 
02101         
02102         *adr = nildbaddress; /*default return value, indicates empty string*/
02103         
02104         return (true);
02105         }
02106     
02107     return (dbassignstring (adr, bs));
02108     } /*dbassignheapstring*/
02109     
02110     
02111 boolean dballochandle (Handle halloc, dbaddress *adr) {
02112     
02113     register Handle h = halloc;
02114     register boolean fl;
02115     
02116     if (h == nil) { /*defensive driving, nil handles are represented by nil addresses*/
02117         
02118         *adr = nildbaddress;
02119         
02120         return (true);
02121         }
02122         
02123     lockhandle (h);
02124     
02125     fl = dballocate ((long) gethandlesize (h), *h, adr);
02126     
02127     unlockhandle (h);
02128     
02129     return (fl);
02130     } /*dballochandle*/
02131     
02132     
02133 boolean dbassignhandle (Handle h, dbaddress *adr) {
02134     
02135     /*
02136     6/30/92 dmb: added check for nil handle
02137     */
02138     
02139     register boolean fl;
02140     
02141     if (*adr == nildbaddress) /*creating a new guy*/
02142     
02143         return (dballochandle (h, adr));
02144     
02145     if (h == nil)
02146         return (dbassign (adr, 0, nil));
02147     
02148     lockhandle (h);
02149     
02150     fl = dbassign (adr, (long) gethandlesize (h), *h);
02151     
02152     unlockhandle (h);
02153     
02154     return (fl);
02155     } /*dbassignhandle*/
02156     
02157     
02158 boolean dbsavehandle (Handle hsave, dbaddress *adr) {
02159     
02160     /*
02161     xxx -- not sure why this is needed, looks like dbassignhandle, above,  
02162     does the job fairly well.
02163     */
02164     
02165     register Handle h = hsave;
02166     register long ctbytes;
02167     register boolean fl;
02168     dbaddress a = *adr;
02169     
02170     ctbytes = gethandlesize (h);
02171     
02172     lockhandle (h);
02173     
02174     if (a == nildbaddress) 
02175         fl = dballocate (ctbytes, *h, &a);
02176     else
02177         fl = dbassign (&a, ctbytes, *h);
02178         
02179     unlockhandle (h);
02180     
02181     *adr = a; /*copy into returned value*/
02182     
02183     return (fl);
02184     } /*dbsavehandle*/
02185     
02186 
02187 /*
02188 boolean dbnewarray (ctelements, sizeelement, pdata, adr) short ctelements, sizeelement; ptrvoid pdata; dbaddress *adr; {
02189     
02190     register long ctbytes;
02191     
02192     ctbytes = ((long) ctelements * sizeelement) + sizeof (tydbarrayheader);
02193     
02194     return (dballocate (ctbytes, pdata, adr));
02195     } /%dbnewarray%/
02196 */
02197     
02198 
02199 void dbsetview (short viewnumber, dbaddress adrtext) {
02200 
02201     register hdldatabaserecord hdb;
02202     
02203     dbswapglobals ();
02204     
02205     hdb = databasedata; /*move into register*/
02206     
02207     (**hdb).views [viewnumber] = adrtext;
02208     
02209     setdirty (hdb);
02210     
02211     dbflushheader ();
02212     
02213     dbswapglobals ();
02214     } /*dbsetview*/
02215 
02216 
02217 void dbgetview (short viewnumber, dbaddress *adrtext) {
02218     
02219     *adrtext = (**databasedata).views [viewnumber];
02220     } /*dbgetview*/
02221 
02222 
02223 void dbcurrentdatabase (hdldatabaserecord hdb) {
02224     
02225     if (hdb != nil)
02226         databasedata = hdb; 
02227     } /*dbcurrentdatabase*/
02228     
02229 
02230 void dbgetcurrentdatabase (hdldatabaserecord *hdb) {
02231     
02232     *hdb = databasedata;
02233     } /*dbgetcurrentdatabase*/
02234     
02235     
02236 boolean dbfnumchanged (hdlfilenum newfnum) {
02237     
02238     register hdldatabaserecord hdb = databasedata;
02239     
02240     (**hdb).fnumdatabase = (long) newfnum;
02241     
02242     setdirty (hdb);
02243     
02244     return (dbflushheader ());
02245     } /*dbfnumchanged*/
02246 
02247 
02248 #ifdef DATABASE_DEBUG
02249 
02250 boolean debug_dbpushreleasestack (dbaddress adr, long valtype, long line, char *sourcefile) {
02251     
02252     /*
02253     the chunk of db space pointed to by adr is being logically released, but
02254     the caller is saying that he doesn't want to make the effects permanent
02255     until some time in the future.  he indicates it's time to release all these
02256     guys by calling dbflushreleasestack, below.
02257     
02258     if the user decides to not save changes, you should call dbzeroreleasestack.
02259     */
02260     
02261     Handle hstack = (**databasedata).releasestack;
02262     tydbreleasestackframe info;
02263 
02264     if (adr == nildbaddress) /*no need to waste space on a nil address*/
02265         return (true);
02266         
02267     if (hstack == nil) {
02268         
02269         if (!newclearhandle (0L, &hstack))
02270             return (false);
02271             
02272         (**databasedata).releasestack = hstack;
02273         }
02274 
02275     clearbytes (&info, sizeof (info));
02276 
02277     info.adr = adr;
02278 
02279     info.id = valtype;
02280 
02281     info.line = line;
02282 
02283     newfilledhandle (sourcefile, strlen (sourcefile), &info.file);
02284         
02285     return (enlargehandle (hstack, sizeof (info), &info));
02286     } /*dbpushreleasestack*/
02287 
02288 
02289 boolean dbflushreleasestack (void) {
02290     
02291     /*
02292     release all the chunks accumulated in the database's releasestack.
02293     
02294     5.1.4 dmb: don't lock the handle
02295     */
02296     
02297     Handle h = (**databasedata).releasestack;
02298     tydbreleasestackframe info;
02299     long i, ct;
02300     long hsize;
02301     
02302     if (h != nil) {
02303         
02304         hsize = gethandlesize (h);
02305         
02306         ct = hsize / sizeof (info);
02307         
02308         for (i = 0; i < ct; ++i) {
02309             
02310             rollbeachball (); /*dmb 4.1b9*/
02311 
02312             info = ((tydbreleasestackframe*)(*h)) [i];
02313             
02314             if (!dbrelease (info.adr)) {
02315 
02316                 bigstring bsfile;
02317                 char str[256];
02318 
02319                 texthandletostring (info.file, bsfile);
02320 
02321                 sprintf (str, "dbrelease failed for address %ld, type %ld, line %ld in %s.", info.adr, info.id, info.line, stringbaseaddress (bsfile));
02322                 
02323                 DB_MSG_2 (str);
02324                 }
02325             }
02326 
02327         disposehandle (h);
02328         
02329         (**databasedata).releasestack = nil;
02330         }
02331     
02332 #ifdef SMART_DB_OPENING 
02333     dbwriteshadowavaillist (); /*6.2b12 AR: this is a good place to do it since we're about done with saving*/
02334 #endif
02335      
02336     return (true);
02337     } /*dbflushreleasestack*/
02338 
02339 #else
02340     
02341 boolean dbpushreleasestack (dbaddress adr, long valtype) {
02342 #pragma unused(valtype)
02343 
02344     /*
02345     the chunk of db space pointed to by adr is being logically released, but
02346     the caller is saying that he doesn't want to make the effects permanent
02347     until some time in the future.  he indicates it's time to release all these
02348     guys by calling dbflushreleasestack, below.
02349     
02350     if the user decides to not save changes, you should call dbzeroreleasestack.
02351 
02352     6.2b3 AR: Added valtype parameter, only used in debug version (see above).
02353     */
02354     
02355     Handle hstack = (**databasedata).releasestack;
02356     
02357     if (adr == nildbaddress) /*no need to waste space on a nil address*/
02358         return (true);
02359         
02360     if (hstack == nil) {
02361         
02362         if (!newclearhandle (0L, &hstack))
02363             return (false);
02364             
02365         (**databasedata).releasestack = hstack;
02366         }
02367         
02368     return (enlargehandle (hstack, sizeof (adr), &adr));
02369     } /*dbpushreleasestack*/
02370 
02371 
02372 boolean dbflushreleasestack (void) {
02373     
02374     /*
02375     release all the chunks accumulated in the database's releasestack.
02376     
02377     5.1.4 dmb: don't lock the handle
02378     */
02379     
02380     Handle h = (**databasedata).releasestack;
02381     long i, ct;
02382     long hsize;
02383     
02384     if (h != nil) {
02385         
02386         hsize = gethandlesize (h);
02387         
02388         ct = hsize / sizeof (dbaddress);
02389         
02390         for (i = 0; i < ct; ++i) {
02391             
02392             rollbeachball (); /*dmb 4.1b9*/
02393             
02394             dbrelease (((ptrdbaddress) (*h)) [i]);
02395             }
02396 
02397         disposehandle (h);
02398         
02399         (**databasedata).releasestack = nil;
02400         }
02401     
02402 #ifdef SMART_DB_OPENING 
02403     dbwriteshadowavaillist (); /*6.2b12 AR: this is a good place to do it since we're about done with saving*/
02404 #endif
02405     
02406     return (true);
02407     } /*dbflushreleasestack*/
02408 
02409 #endif
02410 
02411 
02412 static void dbzeroreleasestack (void) {
02413     
02414     disposehandle ((**databasedata).releasestack);
02415     
02416     (**databasedata).releasestack = nil;
02417     } /*dbzeroreleasestack*/
02418 
02419 
02420 boolean dbdispose (void) {
02421 
02422     dbzeroreleasestack ();
02423 
02424 #ifdef SMART_DB_OPENING 
02425     dbdisposeshadowavaillist ();
02426 #else
02427     disposehandle ((**databasedata).u.extensions.availlistshadow.data);
02428 #endif
02429     
02430     disposehandle ((Handle) databasedata);
02431     
02432     databasedata = nil;
02433     
02434     return (true);
02435     } /*dbdispose*/
02436 
02437 
02438 boolean dbnew (hdlfilenum fnum) {
02439     
02440     /*
02441     2002-11-11 AR: Added assert to make sure the C compiler chose the
02442     proper byte alignment for the tydatabaserecord struct. If it did not,
02443     we would end up corrupting any database files we saved.
02444     */
02445     
02446     register hdldatabaserecord hdb;
02447     
02448     assert (sizeof (tydatabaserecord) == 88);
02449     
02450     if (!newclearhandle (sizeof (tydatabaserecord), (Handle *) &databasedata))
02451         return (false);
02452         
02453     hdb = databasedata; /*copy into register*/
02454     
02455     (**hdb).fnumdatabase = (long) fnum;
02456     
02457 #ifdef MACVERSION
02458     (**hdb).systemid = dbsystemidMac;
02459 #endif
02460 
02461 #ifdef WIN95VERSION
02462     (**hdb).systemid = dbsystemidWin32;
02463 #endif
02464 
02465     (**hdb).versionnumber = dbversionnumber;
02466 
02467     (**hdb).headerLength = firstphysicaladdress;
02468     (**hdb).longversionMajor = dbversionnumber;
02469     (**hdb).longversionMinor = dbversionnumberminor;
02470     
02471     dbshadowavaillist ();
02472 
02473     setdirty (hdb);
02474     
02475     if (dbflushheader ()) /*initial info written to disk*/
02476         return (true);
02477     
02478     dbdispose (); /*error flushing the data out to disk*/
02479     
02480     return (false);
02481     } /*dbnew*/
02482     
02483 
02484 boolean dbopenfile (hdlfilenum fnum, boolean flreadonly) {
02485     
02486     /*
02487     4.1b9 dmb: allow opening of databases newer than us, as long as 
02488     version number change is not major.
02489     
02490     5.1.5 dmb: use diskrec instead of handle locking; shadow avail list
02491 
02492     6.2a9 AR: To support the builtins.db verbs we need to know whether
02493     we have write permission, so we introduced the flreadonly param.
02494     
02495     2002-11-11 AR: Added assert to make sure the C compiler chose the
02496     proper byte alignment for the tydatabaserecord struct. If it did not,
02497     we would end up corrupting any database files we saved.
02498     */
02499 
02500     tydatabaserecord diskrec;
02501     register hdldatabaserecord hdb;
02502     
02503     assert (sizeof (tydatabaserecord) == 88);
02504     
02505     if (!newclearhandle (longsizeof (tydatabaserecord), (Handle *) &databasedata))
02506         return (false);
02507     
02508     hdb = databasedata; /*copy into register*/
02509     
02510     (**hdb).fnumdatabase = (long) fnum; /*set up so dbread will work*/
02511     
02512     if (!dbread ((dbaddress) 0, sizeof (tydatabaserecord), &diskrec))
02513         goto error;
02514     
02515     #ifdef SWAP_BYTE_ORDER
02516         {
02517         short i;
02518         disktomemlong (diskrec.availlist);
02519         disktomemlong (diskrec.u.extensions.availlistblock);
02520         disktomemshort (diskrec.flags);
02521         for (i = 0; i < ctviews; i++)
02522             {
02523             disktomemlong (diskrec.views[i]);
02524             }
02525 //      disktomemlong (diskrec.fnumdatabase);
02526         disktomemlong (diskrec.headerLength);
02527         disktomemshort (diskrec.longversionMajor);
02528         disktomemshort (diskrec.longversionMinor);
02529         }
02530     #endif
02531     
02532     diskrec.fnumdatabase = (long) fnum; /*this just got overwritten*/
02533     
02534     diskrec.releasestack = nil; /*this is an in-memory structure only*/
02535     
02536     diskrec.u.extensions.flreadonly = flreadonly; /*this is an in-memory structure only*/
02537 
02538     **hdb = diskrec;
02539     
02540     if ((**hdb).versionnumber != dbversionnumber) {
02541 
02542         if (majorversion ((**hdb).versionnumber) != majorversion (dbversionnumber)) {
02543         
02544             dberror (dbwrongversionerror);
02545             
02546             goto error;
02547             }
02548         
02549         #ifdef SMART_DB_OPENING
02550         if ((**hdb).versionnumber < dbfirstversionwithcachedshadowavaillist)
02551             (**hdb).u.extensions.availlistblock = nildbaddress; /*don't count on old version to handle this one*/
02552         #endif
02553         
02554         (**hdb).versionnumber = dbversionnumber; /*we can only write what we know*/
02555         
02556         setdirty (hdb);
02557         }
02558         
02559     if (!dbshadowavaillist ())
02560         goto error;
02561     
02562     return (true);
02563     
02564     error:
02565     
02566     disposehandle ((Handle) hdb);
02567     
02568     databasedata = nil;
02569     
02570     return (false); /*error loading in header*/
02571     } /*dbopenfile*/
02572 
02573 
02574 boolean dbclose (void) {
02575     
02576     dbzeroreleasestack (); /*don't release chunks accumulated in release stack*/
02577     
02578     setdirty (databasedata);
02579     
02580     return (dbflushheader ());
02581     } /*dbclose*/
02582 
02583 
02584 boolean dbstartsaveas (hdlfilenum fnum) {
02585     
02586     register boolean fl;
02587         
02588     fldatabasesaveas = true; /*set global; enables databasehandle swapping*/
02589     
02590     dbswapglobals ();
02591     
02592     fl = dbnew (fnum);
02593     
02594     dbswapglobals ();
02595     
02596     fldatabasesaveas = fl;
02597     
02598     return (fl);
02599     } /*dbstartsaveas*/
02600 
02601 
02602 boolean dbendsaveas (void) {
02603     
02604     register boolean fl;
02605     
02606     if (!fldatabasesaveas)
02607         return (false);
02608         
02609     dbswapglobals ();
02610     
02611     fl = dbclose ();
02612     
02613     dbdispose ();
02614     
02615     dbswapglobals ();
02616     
02617     fldatabasesaveas = false;
02618     
02619     return (fl);
02620     } /*dbendsaveas*/
02621 

Generated on Wed May 31 18:19:45 2006 for frontierkernel 10.1.10a by  doxygen 1.4.6