home *** CD-ROM | disk | FTP | other *** search
- /*
- *
- * Structured file I/O. Provides structure I/O for both DATABASE and
- * INDEX classes. In addition, we will provide the means to add/delete
- * records as necessary
- *
- * (C) 1990 Vision Software
- *
- * $Id: access.c 1.2003 91/05/08 14:02:17 pcalvin beta $
- *
- * Comments:
- *
- * This class provides lowlevel (ANSI-C) file I/O for the DATABASE and
- * index classes. By centralizing this class, we may provide extended
- * service (such as caching) without breaking the other code. In addition
- * We provide services to maintain/add/delete records..
- *
- * Bugs:
- *
- * None documented
- *
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <io.h>
-
- #include <stdhdr.h>
-
- #include <access.h>
- #include <adl.h>
-
- /*
- * Opens stream based upon szFileName
- */
- ACCESS::ACCESS(PINF pinf,CCH cchRecord,SZ szFileName,SZ szExtension,BOOL fCreateIfNeeded)
- {
- Assert(pinf != pinfNil);
- Assert(szFileName != szNil);
- Assert(cchRecord != cchNil);
-
- SZ sz = SzFullPathFromSzSz(szFileName,szExtension);
-
- pinfBase = pinf;
- cch = cchRecord;
- rgchStorage = new char[cch];
- stmFile = fopen(sz,"rb+");
-
- /*
- * Will only create the file if the user wants it..
- */
- if (stmFile == stmNil)
- {
- if (fCreateIfNeeded)
- {
- Verify(FCreateFile(sz));
- }
- else
- {
- IOError("Unable to Open %s",sz);
- }
- }
- else
- {
- /*
- * Determine the number of records already in the file.
- */
- recMax = RecFromStmCch(stmFile,cch);
- recCurrent = recError;
-
- /*
- * Read in the header. To avoid disk-seeks, we store the header
- * in a variable.
- */
- rewind(stmFile);
- Verify(fread(&fhd,sizeof(fhd),1,stmFile) == 1);
- }
-
-
- /*
- * Check the file header. If sizes are different, ask about
- * an update procedure.
- */
- if (fhd.cchSizeofRecord != cch)
- {
- if (FAskSz("Record size has changed, update file?","Press \"Y\" to fixup datafile, \"N\" to ignore"))
- {
- fclose(stmFile);
-
- Verify(FUpdateDatabase(pinfBase,sz,cch,fhd.cchSizeofRecord));
- }
- }
- }
-
- /*
- * Destruction is simple, standard fclose()
- */
- ACCESS::~ACCESS()
- {
- delete rgchStorage;
- fclose(stmFile);
- }
-
- /*
- * Marks the current record
- */
- BOOL ACCESS::FMark()
- {
- recMarked = RecQuery();
- return (fTrue);
- }
-
- /*
- * Goes to the previously marked record
- */
- BOOL ACCESS::FGotoMark()
- {
- return (FGotoRec(recMarked));
- }
-
- /*
- * Answers with the base of the record
- */
- PINF ACCESS::PinfQuery(VOID)
- {
- return (pinfBase);
- }
-
- /*
- * Saves the current record. Does not adjust the record pointer
- */
- BOOL ACCESS::FSave(VOID)
- {
- if (fseek(stmFile,sizeof(FHEADER)+recCurrent*cch,SEEK_SET) == 0)
- {
- Verify(fwrite(pinfBase,cch,1,stmFile) == 1);
- return (fTrue);
- }
- else
- {
- return (fFalse);
- }
- }
-
- /*
- * Answers if the current record has been deleted..
- */
- BOOL ACCESS::FDelete(VOID)
- {
- REC recDelete = RecQuery();
-
- /*
- * If none have been deleted, make this the first..
- */
- if (fhd.recFirstDeleted == recError)
- fhd.recFirstDeleted = recDelete;
-
- /*
- * Clear all info from the record..
- */
- memset(pinfBase,0,cch);
- pinfBase->recNextDeleted = recError;
-
- Verify(FSave());
-
- /*
- * Keep link to last in deleted chain..
- */
- if (FGotoRec(fhd.recLastDeleted))
- {
- pinfBase->recNextDeleted = recDelete;
-
- Verify(FSave());
- }
-
- /*
- * Update pointer to new end and save.
- */
- fhd.recLastDeleted = recDelete;
- rewind(stmFile);
- Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
-
- return (fTrue);
- }
-
- /*
- * Goes to the first record in the list. Due to the ability to delete
- * records, this need not be the first physical record..
- *
- * NOTE: Does not MODIFY to record in pinfBase if the record is not found
- */
- BOOL ACCESS::FFirst(VOID)
- {
- return (FGotoRec(fhd.recFirstActive));
- }
-
- /*
- * Makes the desired record the first logical record. This happens (internally)
- * if the first record is deleted. It could also be called (externally) if
- * an INDEX root is deleted/created
- */
- BOOL ACCESS::FMakeFirstRec(REC rec)
- {
- fhd.recFirstActive = rec;
- rewind(stmFile);
- Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
-
- return (fTrue);
- }
-
- /*
- * Answer if the specific physical record can be found
- */
- BOOL ACCESS::FGotoRec(REC recDest)
- {
- if (recDest >= recMax || recDest == recError)
- {
- return (fFalse);
- }
- else if ((fseek(stmFile,sizeof(FHEADER)+recDest*cch,SEEK_SET) == 0) && (fread(pinfBase,cch,1,stmFile) == 1))
- {
- recCurrent = recDest;
- return (fTrue);
- }
- else
- {
- return (fFalse);
- }
- }
-
- /*
- * Answers if a new record has been created..
- */
- REC ACCESS::RecCreate()
- {
- /*
- * Try to create a new record from the scraps left over
- * by previous deletes.
- * Before search, save contents of created record.
- */
- if (fhd.recFirstDeleted != recError)
- {
- memcpy(rgchStorage,pinfBase,cch);
-
- Verify(FGotoRec(fhd.recFirstDeleted));
- fhd.recFirstDeleted = pinfBase->recNextDeleted;
- rewind(stmFile);
- Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
-
- memcpy(pinfBase,rgchStorage,cch);
- }
- else
- {
- recCurrent = recMax;
- recMax += 1;
- Verify(FSave());
- }
-
- /*
- * Assert that garbage does not get left here..
- */
- pinfBase->recNextDeleted = recError;
-
- /*
- * If there are no other active records, I guess this is the first..
- */
- if (fhd.recFirstActive == recError)
- {
- Verify(FMakeFirstRec(recCurrent));
- }
-
- return (recCurrent);
- }
-
- /*
- * Answers with the current physical record number
- */
- REC ACCESS::RecQuery(VOID)
- {
- return (recCurrent);
- }
-
- /*
- * Answers with the absolute number of records in the file.
- * NOTE: Includes deleted records
- */
- REC ACCESS::RecMaxQuery(VOID)
- {
- return (recMax);
- }
-
- /*
- * Answers with the number of records in a stream.
- *
- * NOTE: This is a DOS KLUDGE. Porting to UN*X or other civilized??
- * operating system will require modification here
- */
- REC ACCESS::RecFromStmCch(FILE *stmInput,CCH cchInput)
- {
- LONG cbytes = filelength(fileno(stmInput));
-
- return ((INT)(cbytes / cchInput));
- }
-
- /*
- * Using the static member szDataPath, we answer with a string that
- * contains the entire pathname (including extension) for the
- * desired file.
- *
- * NOTE: This is a TEMPORARY string. Successive calls to this function
- * provide the same address, with a different string.
- */
- SZ ACCESS::SzFullPathFromSzSz(SZ sz,SZ szExtension)
- {
- PointerAssert(sz);
- PointerAssert(szExtension);
-
- STATIC SZTEMP szAnswer;
-
- if (szDataPath == szNil)
- {
- strcpy(szAnswer,sz);
- strcat(szAnswer,szExtension);
- }
- else
- {
- strcpy(szAnswer,szDataPath);
- strcat(szAnswer,sz);
- strcat(szAnswer,szExtension);
- }
-
- return (szAnswer);
- }
-
-
- /*
- * Creates the file and initializes the header
- */
- BOOL ACCESS::FCreateFile(SZ sz)
- {
- stmFile = fopen(sz,"wb+");
-
- if (stmFile == stmNil)
- {
- IOError("Unable to Create %s",sz);
- NotReached();
- }
- else
- {
- fhd.cchSizeofRecord = cch;
- fhd.recFirstDeleted = recError;
- fhd.recLastDeleted = recError;
- fhd.recFirstActive = recError;
-
- Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
-
- /*
- * Assert that no records are available..
- */
- recMax = recNil;
- recCurrent = recError;
- }
-
- return (fTrue);
- }
-
- /*
- * Updates the database because the number of bytes within
- * each record has been perverted. If the value
- * has gone up, the user may place a "default" value. If
- * the database has gone down, the record is truncated.
- */
- BOOL ACCESS::FUpdateDatabase(PINF pinf,SZ sz,CCH cchNew,CCH cchOld)
- {
- SZ szWork = tmpnam(NULL);
- SZTEMP szTmp;
- SZTEMP szOriginal;
-
- /*
- * Be sure the TMP file gets placed in the correct directory
- */
- Verify(strcpy(szOriginal,sz) != szNil);
- Verify(strcpy(szTmp,SzFullPathFromSzSz(szWork,"")) != szNil);
-
- /*
- * Assert the file may be renamed..
- */
- if (rename(szOriginal,szTmp) == 0)
- {
- ACCESS acc(pinf,cchOld,szWork,"",fFalse);
- REC rec = recNil;
-
- /*
- * Creates the NEW database file that is going to be fixedup
- */
- Verify(FCreateFile(szOriginal));
-
- /*
- * Now, traverse the old database and save the contents
- * in the new one..
- */
- while (acc.FGotoRec(rec))
- {
- Verify(RecCreate() == rec);
- Verify(FSave());
-
- rec++;
- }
- }
-
- /*
- * And finally, delete the old one..
- */
- return (!remove(szTmp));
- }
-