/* * Windows Calendar * Copyright (c) 1985 by Microsoft Corporation, all rights reserved. * Written by Mark L. Chamberlin, consultant to Microsoft. * ****** calfile.c * */ #include "cal.h" /**** CreateChangeFile *****/ VOID APIENTRY CreateChangeFile () { /* If there is already a change file, delete it, ignoring any errors since we will be creating a new one, and that's the important one. */ DeleteChangeFile (); /* Set the end-of-data of the change file to block 0. */ vobkEODChange = 0; /* By passing the drive letter as 0 we tell GetTempFileName to decide where to put the temp file. */ if (!(vfChangeFile = FCreateTempFile (IDFILECHANGE, 0))) { /* Post error saying edits will not be recorded. */ OutputDebugString ("Message Box Is Broken\n"); //AlertBox (vszNoCreateChangeFile, (CHAR *)NULL, //MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION); } } /**** DeleteChangeFile - delete the change file if there is one. ****/ VOID APIENTRY DeleteChangeFile () { if (vfChangeFile) { /* Ignore errors since callers don't care about them. */ vfChangeFile = FALSE; FDeleteFile (IDFILECHANGE); } } /**** FCreateTempFile ****/ BOOL APIENTRY FCreateTempFile ( INT idFile, INT iDrive) /* 0 means let GetTempFileName decide where to put the temp file. Otherwise, this is the drive letter, and it should also have the TF_FORCEDRIVE bit set to make sure that the temp file is created on the specified drive. */ { CHAR szFileSpec [CCHFILESPECMAX]; INT FileHandle; /* Create a temp file with a unique name. * 0 for the third parameter means GetTempFileName should * produce a unique file name and create the file. * GetTempFileName returns the random number it used, which Steve Wood * guarantees is 0 iff the call fails (he does not allow the random * number to be 0). If the file is created OK by GetTempFileName, * open it to set up the reopen buffer. */ if (MGetTempFileName ((BYTE)iDrive, "CAL", 0, szFileSpec) == 0) { return (FALSE); } if ((FileHandle = MOpenFile (szFileSpec, &OFStruct [idFile], OF_READWRITE )) != -1) { /* File is OK. Close it, and return TRUE. */ M_lclose (FileHandle); return (TRUE); } /* GetTempFileName failed or OpenFile failed. */ return (FALSE); } /**** FFreeUpDr - Free up the specified DR. ****/ BOOL APIENTRY FFreeUpDr ( DR *pdr, /* Pointer to the DR to be written out. */ DL *pdl) /* OUTPUT - DL indicating where the occupant was put. */ { DL dlNew; if (!pdr -> fDirty) { /* It's not dirty so don't change the date's location. */ *pdl = DLNOCHANGE; return (TRUE); } /* The DR is dirty. However, it may be empty, in which case we tell the caller it is not on disk. */ if (pdr -> cbNotes + pdr -> cbTqr == 0) { *pdl = DLNIL; return (TRUE); } /* It's not empty. */ if (!vfChangeFile) { /* Tell the user that the edits are not being recorded. We already warned the turkey when we couldn't create the change file, but he didn't listen. Now just throw away his edits. */ AlertBox (vszNoChangeFile, (CHAR *)NULL, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION); *pdl = DLNOCHANGE; return (TRUE); } /* Set up the new DL using vobkEODChange before FWriteDrToFile changes it. */ dlNew = DLFCHANGEFILEMASK | vobkEODChange; if (FWriteDrToFile (TRUE, IDFILECHANGE, pdr)) { /* The write was successful - tell the caller about the new DL. */ *pdl = dlNew; return (TRUE); } else { /* An error occured while attempting to write the date. */ AlertBox (vszErrorWritingChanges, (CHAR *)NULL, MB_APPLMODAL | MB_OK | MB_ICONHAND); return (FALSE); } } /**** FWriteDrToFile *****/ BOOL APIENTRY FWriteDrToFile ( BOOL fOpenClose, /* If TRUE, the file must be opened and then closed. If FALSE, the file is already open and should be left open. */ INT idFile, /* Which file to write to. */ DR *pdr) /* Which DR to write from. */ { INT *pobkEOD; INT cbkTransfer; INT cbTransfer; INT FileHandle; BOOL fOk; /* Set up a pointer to the appropriate EOD. */ pobkEOD = &vobkEODNew; if (idFile == IDFILECHANGE) pobkEOD = &vobkEODChange; /* Try to reopen the file. */ if (!fOpenClose || FReopenFile (idFile, OF_PROMPT | OF_CANCEL | OF_REOPEN | OF_READWRITE)) { /* Make a local copy of the file handle to save code below. */ FileHandle = hFile [idFile]; /* Calculate the minimum number of BKs we must write out. Do this by taking the count of bytes in use in the DR and adding the count of bytes in a BK minus 1 in order to round up to the next BK. Then divide by the count of bytes in a BK to get the number of BKs to be written. */ cbkTransfer = (pdr -> cbNotes + pdr -> cbTqr + CBDRHEAD + CBBK - 1) / CBBK; /* Clear the reserved word. */ pdr -> wReserved = 0; /* Seek to the current end of data, write the current DR, and close the file. */ cbTransfer = CBBK * cbkTransfer; fOk = M_llseek (FileHandle, (LONG)(CBBK*(*pobkEOD)), 0) != -1 && FWriteFile (FileHandle, (BYTE *)pdr, cbTransfer); if (FCondClose (fOpenClose, idFile) && fOk) { /* The DR has been successfully written to the file. Update the EOD of the file. */ *pobkEOD += cbkTransfer; return (TRUE); } } return (FALSE); } /**** FReadDrFromFile ****/ BOOL APIENTRY FReadDrFromFile ( BOOL fOpenClose, /* If TRUE, the file must be opened and then closed. If FALSE, the file is already open and should be left open. */ DR *pdr, /* Where to read it into. */ DL dl) /* File location of date. */ { INT idFile; INT FileHandle; WORD cbData; OBK obk; BOOL fOk; /* Assume we will be reading from the original file. */ idFile = IDFILEORIGINAL; /* Separate the block offset, and switch to the change file if the change file flag is set in the DL. */ obk = dl & DLOBKMASK; if (dl & DLFCHANGEFILEMASK) idFile = IDFILECHANGE; /* Try to reopen the file. */ if (fOpenClose && !FReopenFile (idFile, OF_PROMPT | OF_CANCEL | OF_REOPEN | OF_READ)) return (FALSE); /* Reopen was successful - seek to the beginning of the DR, and read its header in order to know how much data there is. */ FileHandle = hFile [idFile]; if (M_llseek (FileHandle, (LONG)(CBBK * obk), 0) == -1 || M_lread (FileHandle, (LPSTR)pdr, CBDRHEAD) != CBDRHEAD) { FCondClose (fOpenClose, idFile); return (FALSE); } /* Header was successfully read. Now read in the rest. */ cbData = pdr -> cbNotes + pdr -> cbTqr; fOk = (WORD)M_lread (FileHandle, (LPSTR)pdr + CBDRHEAD, cbData) == cbData; /* Close the file. */ return (FCondClose (fOpenClose, idFile) && fOk); } /**** FGetDateDr ****/ BOOL APIENTRY FGetDateDr (DT dtTarget) { DR *pdr; INT itdd; DD *pdd; DL dlTarget; WORD idrTarget; WORD idrNew; WORD idrKickOut; DL dlKickOut; DT dtKickOut; HWND hwndFocus; /* If this routine succeeds in getting the requested date, the focus is left NULL. If it fails, the focus is set back to its previous window. Remember who has the focus now, then set it NULL to record the current edits into the current date. This MUST be done before switching dates so the data goes into the correct date. It's important to leave the focus NULL in the success case so that the data doesn't get recorded again (into the wrong date) when the caller changes the focus later. */ hwndFocus = GetFocus (); CalSetFocus ((HWND)NULL); /* See if this date is already in the tdd. */ if (!FSearchTdd (dtTarget, &itdd)) { /* Not found, try to insert it. */ if (!FGrowTdd (itdd, 1)) { /* Cannot grow tdd to include the new date. FGrowTdd has already put up the error message. */ CalSetFocus (hwndFocus); return (FALSE); } /* Put the date into the new entry, say it is not marked, it has no alarms, it's not on disk, and it's not in memory. */ pdd = TddLock () + itdd; pdd -> dt = dtTarget; pdd -> fMarked = FALSE; pdd -> cAlarms = 0; pdd -> dl = DLNIL; pdd -> idr = IDRNIL; TddUnlock (); } /* At this point itdd is the index of the target date within the tdd. See if the target date is already in memory. */ dlTarget = (pdd = TddLock () + itdd) -> dl; idrTarget = pdd -> idr; TddUnlock (); if ((WORD)idrTarget != IDRNIL) { /* The target date is already in memory. Make the DR it is stored in the current DR, and return TRUE. */ vidrCur = idrTarget; return (TRUE); } /* Find a free DR to put the target date in. */ idrNew = IdrFree (); /* In order to comply with the rule that there is always one free DR, we have to kick out a date if the 2 DRs other than idrNew are both in use. Here's how we decide which, if any, date must be kicked out of memory: - Look at each DR: - If it's idrNew, or it's in use for today, skip it. So we end up either kicking nothing out (if we find a second free DR), or kicking out a date that's not today. Since the same date can't be in two DRs at the same time, we know that we will either find another free DR or one that contains a date other than today, so this loop will terminate. Note - 9/2/85 - MLC - I originally kept today in memory at all times because in month mode I always displayed the notes for today, regardless of which day was selected. Some time ago I changed month mode so it shows the notes for the selected date. I decided to still keep today around since I assume the user will be refering to it more than any other date, so it seemed better than just keeping the two most recently accessed dates around. */ idrKickOut = CDR; do { idrKickOut--; dtKickOut = PdrLock (idrKickOut) -> dt; DrUnlock (idrKickOut); } while (idrKickOut == idrNew || dtKickOut == vftCur.dt); if (dtKickOut != DTNIL) { /* We must kick out a date to free up a DR. */ pdr = PdrLock (idrKickOut); if (!FFreeUpDr (pdr, &dlKickOut)) { /* If we just created the DD for the target date, it is still empty, so we will get rid of it if so. */ DrUnlock (idrKickOut); DeleteEmptyDd (itdd); CalSetFocus (hwndFocus); return (FALSE); } pdr -> fDirty = FALSE; DrUnlock (idrKickOut); } pdr = PdrLock (idrNew); if (dlTarget == DLNIL) { /* No previous data for this date so create an empty DR for it. */ pdr -> cbNotes = pdr -> cbTqr = 0; } else { if (!FReadDrFromFile (TRUE, pdr, dlTarget)) { /* Mark the DR as still not in use. */ pdr -> dt = DTNIL; DrUnlock (idrNew); /* If we just created the DD for the target date, it is still empty, so we will get rid of it if so. */ DeleteEmptyDd (itdd); CalSetFocus (hwndFocus); return (FALSE); } } /* Could be here if no previous data or where date was successfully read from file. Therefore, set the dt, since in the first case it has not been set. */ pdr -> dt = dtTarget; pdr -> fDirty = FALSE; DrUnlock (idrNew); (TddLock () + itdd) -> idr = idrNew; TddUnlock (); if (dtKickOut != DTNIL) { /* We kicked out a date. Mark that DR free. */ PdrLock (idrKickOut) -> dt = DTNIL; DrUnlock (idrKickOut); /* Search for the DD of the date we kicked out. It's OK to ignore the return value of FSearchTdd since the date we kicked out must be in the tdd. */ FSearchTdd (dtKickOut, &itdd); /* Say the kicked out date is no longer in memory, and change the DL if the date's location has changed. */ (pdd = TddLock () + itdd) -> idr = IDRNIL; if (dlKickOut != DLNOCHANGE) pdd -> dl = dlKickOut; TddUnlock (); /* Get rid of the DD of the kicked out date if it's "empty". */ DeleteEmptyDd (itdd); } vidrCur = idrNew; return (TRUE); }