windows-nt/Source/XPSP1/NT/shell/osshell/accesory/calendar/calfile.c
2020-09-26 16:20:57 +08:00

457 lines
14 KiB
C

/*
* 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);
}