1212 lines
40 KiB
C
1212 lines
40 KiB
C
/*
|
|
* Windows Calendar
|
|
* Copyright (c) 1985 by Microsoft Corporation, all rights reserved.
|
|
* Written by Mark L. Chamberlin, consultant to Microsoft.
|
|
*
|
|
****** calfile2.c
|
|
*
|
|
*/
|
|
|
|
#include "cal.h"
|
|
#include "memory.h"
|
|
|
|
extern BOOL f24Time;
|
|
|
|
|
|
CHAR * APIENTRY PFileInPath(
|
|
CHAR *sz);
|
|
|
|
|
|
/**** FCopyToNewFile - copy the active dates from the specified file
|
|
(original or change) to the new file.
|
|
****/
|
|
|
|
BOOL APIENTRY FCopyToNewFile (
|
|
INT idFileSource, /* The id of the source file. */
|
|
DR *pdr, /* Pointer to DR to use as a buffer. */
|
|
DD *pddFirst, /* Pointer to locked tdd. */
|
|
DD *pddMax) /* Pointer beyond locked tdd. */
|
|
{
|
|
register BOOL fSwap;
|
|
register DD *pddCur;
|
|
DL dl;
|
|
|
|
/* If the source file does not exist, there is no work to be done,
|
|
so return right away, indicating success. (The original file
|
|
doesn't exist if we're as yet untitled, and the change file
|
|
doesn't exist if it was impossible to create it (and the user
|
|
was told of the error when the attempt to create the change
|
|
file failed).)
|
|
*/
|
|
|
|
if (idFileSource == IDFILEORIGINAL && !vfOriginalFile
|
|
|| idFileSource == IDFILECHANGE && !vfChangeFile)
|
|
return (TRUE);
|
|
|
|
/* Try to reopen the source file, returning an error if the open fails. */
|
|
if (!FReopenFile (idFileSource, OF_PROMPT | OF_CANCEL | OF_REOPEN | OF_READWRITE))
|
|
goto error1;
|
|
|
|
|
|
/* See if the destination file is available without swapping diskettes.
|
|
If it is, leave the source and destination files both open.
|
|
If it is not available, leave both files closed.
|
|
*/
|
|
|
|
if (fSwap = !FReopenFile (IDFILENEW, OF_REOPEN | OF_READWRITE))
|
|
{
|
|
/* Don't worry about errors closing the files.
|
|
If there's really a problem, it will be detected when we try
|
|
to use the files below.
|
|
*/
|
|
FCloseFile (idFileSource);
|
|
FCloseFile (IDFILENEW);
|
|
}
|
|
|
|
|
|
|
|
/* Make a pass through the tdd looking for dates that are
|
|
in the source file. Copy these dates into the destination file.
|
|
*/
|
|
for (pddCur = pddFirst; pddCur < pddMax; pddCur++)
|
|
{
|
|
/* If this date has not already been transferred, and it's not
|
|
a special DL, and it resides in the source file we're working
|
|
on, copy the data for the date.
|
|
*/
|
|
if (pddCur -> dlSave == DLNOCHANGE
|
|
&& (dl = pddCur -> dl) < DLSPECIALLOW
|
|
&& ((dl & DLFCHANGEFILEMASK) && idFileSource == IDFILECHANGE
|
|
|| !(dl & DLFCHANGEFILEMASK) && idFileSource == IDFILEORIGINAL))
|
|
{
|
|
if (!FReadDrFromFile (fSwap, pdr, dl))
|
|
{
|
|
/* An error occurred,
|
|
so we tell the caller to give up the Save.
|
|
*/
|
|
goto error1;
|
|
}
|
|
|
|
/* Remember the current DL, and set the new one to be the
|
|
place where we are about to write the date to in the
|
|
new file.
|
|
*/
|
|
pddCur -> dlSave = dl;
|
|
pddCur -> dl = (DL)vobkEODNew;
|
|
|
|
/* Try to write the date to the new file. If an error
|
|
occurs, the Save will be aborted. It may be due
|
|
to a disk full condition or some I/O error.
|
|
*/
|
|
if (!FWriteDrToFile (fSwap, IDFILENEW, pdr))
|
|
goto error1;
|
|
}
|
|
}
|
|
|
|
|
|
/* If we weren't swapping, we need to close the files (and it's harmless
|
|
to call FCloseFile for an unopen file).
|
|
We ignore errors closing the source file since no modifications
|
|
were made to it, we are done with it, and an error here has no
|
|
effect on the integrity of the new file (which is what the user
|
|
really cares about now). And anyway, what sort of error could
|
|
occur when closing a file that has only been read from?
|
|
As for closing the new file, if an error occurs, we return it
|
|
as an I/O error, which will cause the Save to be aborted.
|
|
(Can't ignore error closing the new file, since an error here
|
|
means there may be something wrong with it.) By the way, according
|
|
to Chris Peters, close cannot cause a Disk Full error since it
|
|
cannot cause any disk space allocation. Close does update the
|
|
directory entry though. Therefore, the only the only
|
|
kind of error that can occur during a close is an I/O error.
|
|
Chris says most programmers do not bother to check for errors
|
|
when closing files. I still think it's the prudent thing to
|
|
to when closing a file that one has written to.
|
|
*/
|
|
|
|
FCloseFile (idFileSource);
|
|
return (FCloseFile (IDFILENEW));
|
|
|
|
|
|
error1: /* An error occurred - close all files, and return FALSE. */
|
|
FCloseFile (idFileSource);
|
|
FCloseFile (IDFILENEW);
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** FSaveFile - the guts of Save and Save As. ****/
|
|
BOOL APIENTRY FSaveFile (
|
|
CHAR *szFileSpec, /* Name of the file to save to. ANSI STRING. */
|
|
BOOL fOverwrite) /* TRUE means the old copy of the file with
|
|
same name in the same directory as the new
|
|
file should be overwritten by the new one
|
|
if the Save is successful.
|
|
*/
|
|
{
|
|
DD *pddFirst, *pddMax, *pddCur;
|
|
WORD idr;
|
|
INT FileHandle, fp;
|
|
DR *pdr;
|
|
BOOL fOk;
|
|
DL dl;
|
|
|
|
/* Show the hour glass cursor. */
|
|
HourGlassOn ();
|
|
|
|
if ((fp = M_lopen((LPSTR)szFileSpec, 2)) < 0) /* fgd 9/20/89 */
|
|
fp = M_lcreat((LPSTR)szFileSpec, 0); /* _lopen & _lcreat */
|
|
/* use ANSI chrs */
|
|
if (fp > 0)
|
|
M_lclose(fp);
|
|
else
|
|
{
|
|
AlertBox (vrgsz[IDS_NOCREATE], szFileSpec,
|
|
MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
HourGlassOff();
|
|
return FALSE;
|
|
}
|
|
|
|
/* Force edits to be recorded prior to flushing the DRs. Note that
|
|
leaving the focus as is will work OK since the DR corresponding
|
|
to vidrCur remains in memory during the Save. This means that
|
|
if the focus is changed later, the correct date will still be
|
|
around for use by StoreQd or StoreNotes. Since Save does not
|
|
change the mode (day or month) we want the focus to stay where
|
|
it was, so we don't do a CalSetFocus ((HWND)NULL) here.
|
|
*/
|
|
RecordEdits ();
|
|
|
|
|
|
/* Create the new file as a temporary - we will rename it when the
|
|
save is finished. Note that we must force the temp file to
|
|
be on the drive we want the new file on, since, of course, we can't
|
|
rename across drives. Surprizingly though, we can rename across
|
|
directories.
|
|
*/
|
|
if (!FCreateTempFile (IDFILENEW, GetDrive (szFileSpec) | TF_FORCEDRIVE))
|
|
goto error1;
|
|
|
|
|
|
/* Calculate the number of BK required to hold the header and the
|
|
index. Set the end of data of the new file in
|
|
order to reserve the required space. Note that we don't actually
|
|
write the header or the index until after the dates have been
|
|
successfully written to the new file. This is done for two reasons:
|
|
|
|
1) By not writing the magic number until the end, we reduce the
|
|
risk of ever trying to use a file that has the correct magic
|
|
number but which is corrupt. (In other words, if an error
|
|
causes the Save to be aborted, and for some reason the new
|
|
file cannot be deleted, it probably won't look like a valid
|
|
calendar file (although I suppose the disk space allocated
|
|
to the file could contain the correct magic number - but I
|
|
am not going to to bother to write something into the first byte
|
|
of the file just to handle this unbelievably pathological case.
|
|
|
|
2) More importantly - we cannot write the index now because
|
|
it does not yet contain the new DLs. These will be set as
|
|
the dates are moved into the new file.
|
|
|
|
Note that adding CBBK - 1 prior to dividing by CBBK has the
|
|
effect of rounding up to the next bk.
|
|
*/
|
|
vobkEODNew = 1 + (vcddUsed * sizeof (DD) + CBBK - 1) / CBBK;
|
|
|
|
|
|
|
|
/* Mark each DD to indicate that its data has not yet been copied to
|
|
the new file.
|
|
*/
|
|
for (pddMax = (pddCur = TddLock ()) + vcddUsed; pddCur < pddMax; pddCur++)
|
|
pddCur -> dlSave = DLNOCHANGE;
|
|
|
|
TddUnlock ();
|
|
|
|
|
|
/* Now we flush any dates that are in memory to the new file. */
|
|
fOk = FFlushDr ();
|
|
|
|
|
|
/* The tdd may have become smaller during FFlushDr since we may
|
|
have deleted some empty DDs. Lock it, and set up First and Max
|
|
pointers. These pointers are needed for error recovery, so we
|
|
must do this prior to bailing out if FFlushDr failed.
|
|
*/
|
|
pddMax = (pddFirst = TddLock ()) + vcddUsed;
|
|
|
|
|
|
/* Bail out if FFlushDr encountered an error trying to write out
|
|
one of the DRs.
|
|
*/
|
|
if (!fOk)
|
|
goto error2;
|
|
|
|
|
|
/* Find a free DR to use as a buffer, and lock it. */
|
|
idr = IdrFree ();
|
|
pdr = PdrLock (idr);
|
|
|
|
|
|
/* Copy the dates from the change file to the new file, then
|
|
copy the dates from the original file to the new file.
|
|
*/
|
|
fOk = FCopyToNewFile (IDFILECHANGE, pdr, pddFirst, pddMax)
|
|
&& FCopyToNewFile (IDFILEORIGINAL, pdr, pddFirst, pddMax);
|
|
|
|
/* Make sure the DR we used as a buffer looks free, then
|
|
unlock it.
|
|
*/
|
|
pdr -> dt = DTNIL;
|
|
DrUnlock (idr);
|
|
|
|
/* If dates were copied OK, then try to write the header. If either
|
|
operation failed, abort the Save.
|
|
*/
|
|
if (!fOk || !FWriteHeader (pddFirst))
|
|
goto error2;
|
|
|
|
/* Finished with the tdd - unlock it. */
|
|
TddUnlock ();
|
|
|
|
/* Reconnect the DRs with their DDs. */
|
|
Reconnect (TRUE);
|
|
|
|
|
|
/* Delete the file being overwritten. Note that this is the
|
|
file on the same device and directory with the same name
|
|
as what our new file is to be named - it is not necessarily
|
|
the original file. However, we use the reo of the original
|
|
file since it is available at this point. So we call
|
|
OpenFile to get the user to swap diskettes if necessary,
|
|
and then we delete the file.
|
|
If an error occurs during the delete, ignore it.
|
|
The new file is in good shape, the only problem is that
|
|
we won't be able to rename it to it's correct name since
|
|
we couldn't delete the old copy. This will get detected
|
|
when we attempt the rename (below), and the user will be
|
|
told about the problem then. Bear in mind that if we can't
|
|
delete the old file, we probably can't access it at all,
|
|
and since it could be the original file, it would be a bad
|
|
idea to abort the Save now.
|
|
*/
|
|
if ((FileHandle = MOpenFile ((LPSTR)szFileSpec,
|
|
(OFSTRUCT FAR *)&OFStruct [IDFILEORIGINAL],
|
|
OF_PROMPT | OF_CANCEL | OF_READWRITE))
|
|
!= -1)
|
|
{
|
|
M_lclose (FileHandle);
|
|
FDosDelete (OFStruct [IDFILEORIGINAL].szPathName);
|
|
}
|
|
|
|
/* Rename the new file. */
|
|
fOk = FReopenFile (IDFILENEW, OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_READWRITE);
|
|
FCloseFile (IDFILENEW);
|
|
|
|
if (!fOk || FDosRename (OFStruct [IDFILENEW].szPathName, szFileSpec))
|
|
{
|
|
/* Could not rename the file. Tell the user that his data is
|
|
in the temp file with the funny name. Also change szFileSpec
|
|
to point to the temporary file name since this is what we will
|
|
be using now.
|
|
This should rarely, if ever, occur. What could
|
|
have gone wrong? An I/O error perhaps while trying to
|
|
rewrite the directory entry? Anyway, we do our best
|
|
to recover from the situation.
|
|
*/
|
|
|
|
/* delete the file we created which the user wanted to save as.
|
|
* bug fix. we were leaving it in existence with 0 length.
|
|
* don't delete if we were just doing a save.
|
|
* 21-Jun-1987. davidhab
|
|
*/
|
|
|
|
if (!fOverwrite)
|
|
FDosDelete(szFileSpec);
|
|
|
|
szFileSpec = OFStruct [IDFILENEW].szPathName;
|
|
|
|
AlertBox (vszRenameFailed, szFileSpec,
|
|
MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
}
|
|
|
|
/* The new file is now the original file. We need to set up
|
|
OFStruct [IDFILEORIGINAL] accordingly. The simplest way to do this
|
|
is to call OpenFile using szFileSpec - this will set up the reo.
|
|
Call with OF_EXIST since this means the file will not be left open.
|
|
Don't bother to check for errors since all we care about at this
|
|
point is getting the reo set up, and this will presumably happen
|
|
no matter what.
|
|
*/
|
|
MOpenFile ((LPSTR)szFileSpec, (OFSTRUCT FAR *)&OFStruct [IDFILEORIGINAL], OF_EXIST);
|
|
|
|
/* Delete and recreate the change file. If the delete fails, ignore
|
|
it. If the new one can't be created CreateChangeFile will tell
|
|
the user about the problem. (Note that it may be possible to
|
|
create the new change file even if the old one can't be deleted
|
|
since a different temporary file name will be used.)
|
|
The point is, the Save has successfully completed, and not being
|
|
able to delete the old change file or create the new change file
|
|
should not abort the Save at this point.
|
|
The only problem is, the user may think the Save was not successful
|
|
if he sees the error message generated by CreateChangeFile. I am
|
|
assuming that the chances of this failure occuring are extremely
|
|
remote, and in any case the result is not catastrophic.
|
|
*/
|
|
CreateChangeFile ();
|
|
|
|
/* Everything is clean now. */
|
|
vfDirty = FALSE;
|
|
|
|
/* Set the new title. */
|
|
SetTitle (szFileSpec);
|
|
|
|
/* The waiting is over. */
|
|
HourGlassOff ();
|
|
|
|
return (TRUE);
|
|
|
|
|
|
error2: /* Error while trying to flush the DRs, copy the dates,
|
|
or write the header.
|
|
*/
|
|
/* Set the DLs back to their old values. */
|
|
for (pddCur = pddFirst; pddCur < pddMax; pddCur++)
|
|
{
|
|
if ((dl = pddCur -> dlSave) != DLNOCHANGE)
|
|
pddCur -> dl = dl;
|
|
}
|
|
|
|
/* Unlock the tdd. */
|
|
TddUnlock ();
|
|
|
|
/* Try to delete the new file. If an error occurs, ignore it.
|
|
We are already going to tell the user that a problem has occurred,
|
|
and there is nothing we can do about it if the new file (which
|
|
has a funny name) can't be deleted.
|
|
*/
|
|
FDeleteFile (IDFILENEW);
|
|
|
|
/* delete the file we created which the user wanted to save as.
|
|
* bug fix. we were leaving it in existence with 0 length.
|
|
* don't delete if we were doing just a save.
|
|
* 21-Jun-1987. davidhab
|
|
*/
|
|
if (!fOverwrite)
|
|
FDosDelete(szFileSpec);
|
|
|
|
|
|
/* Reconnect the DRs with their DDs. */
|
|
Reconnect (FALSE);
|
|
|
|
error1: /* Error attempting to create the new file. */
|
|
/* Tell the user that the Save failed. */
|
|
AlertBox (vszSaveFailed, (CHAR *)NULL,
|
|
MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
/* The waiting is over. */
|
|
HourGlassOff ();
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** Reconnect - reconnect DRs with their DDs, and make sure the
|
|
selected date is in memory.
|
|
****/
|
|
|
|
VOID APIENTRY Reconnect (BOOL fSaveOk)
|
|
{
|
|
register WORD idr;
|
|
register DR *pdr;
|
|
INT itdd;
|
|
HWND hwndFocus;
|
|
|
|
/* Look at all the DRs. */
|
|
for (idr = 0; idr < CDR; idr++)
|
|
{
|
|
/* Get a pointer to the DR. */
|
|
pdr = PdrLock (idr);
|
|
|
|
/* Skip over DRs that are not in use. */
|
|
if (pdr -> dt != DTNIL)
|
|
{
|
|
/* Reconnect it to its DD. Note that there must be a
|
|
DD for it since when FFlushDr deleted a DD it had
|
|
already marked the DR as free (which this one is not).
|
|
*/
|
|
FSearchTdd (pdr -> dt, &itdd);
|
|
(TddLock () + itdd) -> idr = idr;
|
|
TddUnlock ();
|
|
|
|
/* If the Save succeeded then the DR is now clean. If
|
|
it failed we leave the dirty flag alone (it could be
|
|
be either dirty or clean, depending on it's state
|
|
before the Save was attempted).
|
|
*/
|
|
if (fSaveOk)
|
|
pdr -> fDirty = FALSE;
|
|
}
|
|
|
|
DrUnlock (idr);
|
|
}
|
|
|
|
/* We need to make sure the selected date is in memory. We know
|
|
that this call to FGetDateDr cannot fail since the date is
|
|
already in one of the DRs we just reconnected, or it has no
|
|
data associated with it. In either case, it cannot require
|
|
a disk read so no error can occur. It may no longer be in the
|
|
tdd (FFlushDr may have deleted it), but in that case FGetDateDr
|
|
will build a new DD for it, and we know there will be room for
|
|
the new DD since it was in the tdd before the Save started
|
|
and the DD could only have gotten smaller not larger.
|
|
Also note that FGetDateDr will not have to kick out another
|
|
date since the selected date was in a DR before the Save and
|
|
we have not reassigned any of them, so no disk writes will
|
|
be done either. So no errors can occur.
|
|
*/
|
|
hwndFocus = GetFocus();
|
|
FGetDateDr (DtFromPd3 (&vd3Sel));
|
|
CalSetFocus(hwndFocus);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** GetDrive - extract the drive letter from a file spec.
|
|
if none specified, return the current drive.
|
|
****/
|
|
|
|
INT APIENTRY GetDrive (CHAR *szFileSpec)
|
|
{
|
|
CHAR *pch;
|
|
|
|
/* Skip leading spaces. */
|
|
pch = szFileSpec;
|
|
SkipSpace (&pch);
|
|
|
|
/* If the second character is a colon, the first character is a drive
|
|
letter. Otherwise return the current drive.
|
|
*/
|
|
#ifdef DBCS
|
|
if( IsDBCSLeadByte(*pch) )
|
|
return GetCurDrive();
|
|
else
|
|
return (*(pch + 1) == ':' ? *pch : GetCurDrive ());
|
|
#else
|
|
return (*(pch + 1) == ':' ? *pch : GetCurDrive ());
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**** FFlushDr - iterate the DRs to:
|
|
1) free up empty ones (no data) and get rid of the associated DD
|
|
if it too is empty.
|
|
2) write out non-empty dirty DRs to the new file.
|
|
|
|
Return TRUE if no errors, FALSE if an error
|
|
occurs while trying to flush one of the DRs.
|
|
****/
|
|
BOOL APIENTRY FFlushDr ()
|
|
{
|
|
WORD idr;
|
|
register DR *pdr;
|
|
BOOL fOk;
|
|
INT itdd;
|
|
register DD *pdd;
|
|
|
|
/* Look at all the DRs - stop if an error occurs while trying
|
|
to write one of them to the new file.
|
|
*/
|
|
|
|
for (idr = 0, fOk = TRUE; idr < CDR && fOk; idr++)
|
|
{
|
|
/* Get a pointer to the DR. */
|
|
pdr = PdrLock (idr);
|
|
|
|
/* Skip over DRs that are not in use. */
|
|
if (pdr -> dt != DTNIL)
|
|
{
|
|
/* Break the connection between the DD and the DR.
|
|
Note - the owner of the DR must be in the
|
|
tdd, so don't bother checking the return value
|
|
of FSearchTdd.
|
|
*/
|
|
FSearchTdd (pdr -> dt, &itdd);
|
|
(pdd = TddLock () + itdd) -> idr = IDRNIL;
|
|
|
|
if (pdr -> cbNotes + pdr -> cbTqr == 0)
|
|
{
|
|
/* The DR is empty. Free it up. */
|
|
pdr -> dt = DTNIL;
|
|
|
|
/* Say the date is no longer on disk either. Since
|
|
there is no longer any data associated with the
|
|
date, even if the Save fails we want the DL to
|
|
be DLNIL, so make both dlSave and dl DLNIL.
|
|
*/
|
|
pdd -> dlSave = pdd -> dl = DLNIL;
|
|
|
|
/* The DD may now be empty (if the data is not marked).
|
|
If this is the case, get rid of
|
|
the DD since we don't want to write it out to
|
|
the new file. Even if the Save fails this is OK.
|
|
If it's the selected date it will get reinserted
|
|
into the DD, and if it's not the selected date it
|
|
it not needed and will get reinserted if the user
|
|
ever switches to it.
|
|
Note that it's OK to call DeleteEmptyDd even though
|
|
the tdd is locked since if it does a ReAlloc it
|
|
will only be to make the tdd smaller.
|
|
*/
|
|
DeleteEmptyDd (itdd);
|
|
}
|
|
else
|
|
{
|
|
/* The DR is not empty - see if it's dirty. */
|
|
if (pdr -> fDirty)
|
|
{
|
|
/* Remember old disk location, set new one. */
|
|
pdd -> dlSave = pdd -> dl;
|
|
pdd -> dl = (DL)vobkEODNew;
|
|
|
|
/* Write it to the new file. Note that we
|
|
intentionally leave fDirty TRUE. This is
|
|
necessary for putting things back the way
|
|
they were if the Save fails.
|
|
*/
|
|
fOk = FWriteDrToFile (TRUE, IDFILENEW, pdr);
|
|
}
|
|
}
|
|
|
|
/* Unlock the tdd. */
|
|
TddUnlock ();
|
|
}
|
|
|
|
/* Unlock the DR. */
|
|
DrUnlock (idr);
|
|
}
|
|
|
|
return (fOk);
|
|
|
|
}
|
|
|
|
|
|
/**** FCloseFile - close he specified file if it's open. Return TRUE
|
|
if the file is successfully closed. Assume this happens since
|
|
M_lclose does not give us an error return anyway.
|
|
|
|
Note -
|
|
According to Chris Peters, close cannot cause a Disk Full error since
|
|
it cannot cause any disk space allocation. Close does update the
|
|
directory entry though. Therefore, the only the only
|
|
kind of error that can occur during a close is an I/O error.
|
|
Chris says most programmers do not bother to check for errors
|
|
when closing files. I still think it's the prudent thing to
|
|
to when closing a file that one has written to.
|
|
****/
|
|
|
|
BOOL APIENTRY FCloseFile (INT idFile)
|
|
{
|
|
INT FileHandle;
|
|
|
|
if ((FileHandle = hFile[idFile]) == -1) // Q: no valid handle?
|
|
return (TRUE); // Y: no need to close file
|
|
else
|
|
{
|
|
hFile[idFile] = -1;
|
|
M_lclose(FileHandle); // close file,
|
|
return TRUE; // return success always
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** FWriteHeader - write out the file header and index.
|
|
Return TRUE if successful, FALSE if an error occurs.
|
|
****/
|
|
|
|
BOOL APIENTRY FWriteHeader (DD*pddFirst)
|
|
{
|
|
BYTE bkBuf [CBBK];
|
|
register BYTE *pb;
|
|
register BOOL fOk;
|
|
INT FileHandle;
|
|
|
|
/* Reopen the file, seek to the beginning of the second BK, and write
|
|
the index (tdd).
|
|
*/
|
|
fOk=FReopenFile(IDFILENEW, OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_READWRITE)
|
|
&& M_llseek ((FileHandle = hFile [IDFILENEW]), (LONG)CBBK, 0) != -1
|
|
&& FWriteFile (FileHandle, (BYTE *)pddFirst, (WORD)(vcddUsed * sizeof (DD)));
|
|
|
|
if (fOk)
|
|
{
|
|
//- Save Header: Need a 16 bit word to copy the header info into.
|
|
WORD wTemp;
|
|
|
|
/* Build a header bk containing the magic number, the
|
|
size of the index, and the options.
|
|
Unused bytes are set to 0 and are reserved for future use.
|
|
*/
|
|
FillBuf (bkBuf, CBBK, 0);
|
|
|
|
pb = BltByte ((BYTE *)vrgbMagic, (BYTE *)bkBuf, CBMAGIC);
|
|
|
|
//- Save Header: Copy to temporary 16 bit storage then assign.
|
|
wTemp = (WORD)vcddUsed;
|
|
pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBCDD);
|
|
|
|
wTemp = (WORD)vcMinEarlyRing;
|
|
pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBMINEARLYRING);
|
|
|
|
wTemp = (WORD)vfSound;
|
|
pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBFSOUND);
|
|
|
|
wTemp = (WORD)vmdInterval;
|
|
pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBMDINTERVAL);
|
|
|
|
wTemp = (WORD)vcMinInterval;
|
|
pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBMININTERVAL);
|
|
|
|
wTemp = (WORD)vfHour24;
|
|
pb = BltByte ((BYTE *)&wTemp, (BYTE *)pb, CBFHOUR24);
|
|
|
|
pb = BltByte ((BYTE *)&vtmStart, (BYTE *)pb, CBTMSTART);
|
|
|
|
/* Write the header into the first BK of the file. */
|
|
fOk = M_llseek (FileHandle, (LONG)0, 0) != -1
|
|
&& FWriteFile (FileHandle, bkBuf, CBBK);
|
|
}
|
|
|
|
/* Close the file. Note that calling FCloseFile is Ok even if the
|
|
file was not successfully opened. Also note that the FCloseFile
|
|
call must be separate from the other operations and must be done
|
|
before checking fOk since we want the file to be closed regardless
|
|
of whether an error has occurred.
|
|
*/
|
|
return (FCloseFile (IDFILENEW) && fOk);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** FWriteFile - write to file.
|
|
If a disk full error occurs, put up a message box for the user.
|
|
If some other type of error occurs (I/O error), assume that the user
|
|
has already seen the INT 24 Abort, Retry, Ignore dialog, so no need
|
|
to put up another message. Return FALSE if error occurs, TRUE if
|
|
write is successful.
|
|
****/
|
|
|
|
BOOL APIENTRY FWriteFile (
|
|
INT FileHandle, /* Handle of file to write to. */
|
|
BYTE *pb, /* Pointer to bytes to be writeen. */
|
|
UINT cb) /* COunt of bytes to write. */
|
|
{
|
|
/* Do the write. Return TRUE if it's successful.
|
|
Note that due to some ambiquity about what write returns
|
|
if it runs out of disk space (the C manual talks about -1 for
|
|
an error, but at the same time says the count of bytes written
|
|
could be less than specified but positive if the write runs
|
|
out of disk space), we call it a bad write if the return value
|
|
(count of bytes written) is not cb (the number of bytes we say to
|
|
write). This works for both cases.
|
|
*/
|
|
if ((WORD)_lwrite (FileHandle, (LPSTR)pb, cb) == cb)
|
|
return (TRUE);
|
|
|
|
/* Put up disk full message if that's the error that occurred. */
|
|
/* Assume out of disk space. */
|
|
#ifdef DISABLE
|
|
if (_errno == ENOSPC)
|
|
{
|
|
#endif
|
|
/* Need to make this system modal since the file is still
|
|
open and it's against the rules to relinquish control with
|
|
files open.
|
|
*/
|
|
AlertBox (vszDiskFull, (CHAR *)NULL,
|
|
MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
#ifdef DISABLE
|
|
}
|
|
#endif
|
|
|
|
return (FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** FDeleteFile - delete the specified file. Return TRUE if successful,
|
|
FALSE if an error occurs.
|
|
****/
|
|
BOOL APIENTRY FDeleteFile (INT idFile)
|
|
{
|
|
register BOOL fOk;
|
|
|
|
/* Make sure the file exists, prompting for it if necessary. Then
|
|
try to delete it.
|
|
*/
|
|
fOk=FReopenFile(idFile, OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_READWRITE);
|
|
|
|
FCloseFile (idFile);
|
|
return (fOk && FDosDelete (OFStruct [idFile].szPathName));
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** FReopenFile - */
|
|
BOOL APIENTRY FReopenFile (
|
|
INT idFile,
|
|
WORD wFlags)
|
|
{
|
|
hFile[idFile]= MOpenFile ((LPSTR)vrgsz [IDS_GIVEMEFIRST + idFile],
|
|
(OFSTRUCT FAR *)&OFStruct[idFile], wFlags);
|
|
|
|
return (hFile[idFile]!=-1);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** SetTitle ****/
|
|
VOID APIENTRY SetTitle (CHAR *sz)
|
|
{
|
|
/* We know that the sz we are being passed has been through
|
|
DlgCheckFilename at some point, so it has to contain a valid
|
|
filename and extension and no longer has any trailing blanks.
|
|
Therefore we know that this buffer is large enough.
|
|
*/
|
|
CHAR szWindowText [CCHSZWINDOWTEXTMAX];
|
|
|
|
/* Set the flag indicating if there is an open calendar file. */
|
|
if (vfOriginalFile = sz != vszUntitled)
|
|
{
|
|
/* Convert file name to upper case. Note - "(untitled)" should
|
|
not be converted to upper case.
|
|
*/
|
|
lstrcpy( vszFileSpec, sz);
|
|
AnsiUpper ((LPSTR)vszFileSpec);
|
|
}
|
|
|
|
else
|
|
lstrcpy (vszFileSpec, sz);
|
|
|
|
|
|
/* Strip the path name, build the title string,
|
|
and tell Windows about it.
|
|
*/
|
|
lstrcat (lstrcpy (szWindowText, vszCalendarDash), PFileInPath (vszFileSpec));
|
|
SetWindowText (vhwnd0, (LPSTR)szWindowText);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** FCondClose - conditionally close the specified file. */
|
|
|
|
BOOL APIENTRY FCondClose (
|
|
BOOL fClose, /* FALSE means don't close the file, TRUE means do. */
|
|
INT idFile) /* The id of the file to close. */
|
|
{
|
|
/* If we're not supposed to close the file, just return TRUE.
|
|
If we are, return the result of FCloseFile.
|
|
*/
|
|
return (!fClose || FCloseFile (idFile));
|
|
}
|
|
|
|
|
|
|
|
|
|
/**** CleanSlate ****/
|
|
|
|
VOID APIENTRY CleanSlate (BOOL fShowDate)
|
|
{
|
|
register WORD idr;
|
|
|
|
/* Show the hour glass cursor. */
|
|
HourGlassOn ();
|
|
|
|
/* Take the focus away so that the current edits get recorded now
|
|
and not later when the slate is suppose to be clean. (We don't
|
|
care about the current edits since we are about to throw away
|
|
everything, but if we were to leave the focus on an edit control,
|
|
its contents would get stored when the focus gets set later. This
|
|
would make the file dirty with data that's supposed to
|
|
be gone.)
|
|
*/
|
|
CalSetFocus ((HWND)NULL);
|
|
|
|
|
|
/* Say everything is clean. */
|
|
vfDirty = FALSE;
|
|
|
|
/* Say there is no next alarm, and forget about any unacknowledged
|
|
ones.
|
|
*/
|
|
vftAlarmNext.dt = vftAlarmFirst.dt = DTNIL;
|
|
|
|
/* Mark all DR as available. */
|
|
for (idr = 0; idr < CDR; idr++)
|
|
{
|
|
PdrLock (idr) -> dt = DTNIL;
|
|
DrUnlock (idr);
|
|
}
|
|
|
|
/* Get rid of all entries in the tdd. */
|
|
InitTdd ();
|
|
|
|
/* Set all options to their default values. */
|
|
vcMinEarlyRing = 0;
|
|
vfSound = TRUE;
|
|
vmdInterval = MDINTERVAL60;
|
|
vcMinInterval = 60;
|
|
InitTimeDate(vhInstance, 0);
|
|
vfHour24 = f24Time;
|
|
vtmStart = 7 * 60; /* changed from 8 to 7 11/27/88 */
|
|
|
|
/* Say there is no current file. */
|
|
SetTitle (vszUntitled);
|
|
|
|
/* Delete old change file (if there is one), and create the new
|
|
one. If a Save has just been done, the change file has
|
|
already been recreated, but I guess this is OK. We must
|
|
recreate the change file now because we don't know whether
|
|
the the user said to ignore old edits (in which case the
|
|
Save didn't get done and the old change file is still around).
|
|
This CreateChangeFile is also needed here for the call from
|
|
CalInit, in which case it creates the change file for the
|
|
first time.
|
|
*/
|
|
CreateChangeFile ();
|
|
|
|
/* Go into day mode for today. Set the selected month to an
|
|
impossible one - this will cause SwitchToDate (called by
|
|
DayMode) to call SetUpMonth. Note that the SwitchToDate call
|
|
cannot fail since it will not attempt to read from a file.
|
|
*/
|
|
if (fShowDate)
|
|
{
|
|
vd3Sel.wMonth = MONTHDEC + 1;
|
|
DayMode (&vd3Cur);
|
|
}
|
|
/* The waiting is over. */
|
|
HourGlassOff ();
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
/* old version commented out - L.Raman */
|
|
/**** OpenCal - open a calendar file. ****/
|
|
|
|
VOID APIENTRY OpenCal ()
|
|
{
|
|
CHAR szNewFileSpec [CCHFILESPECMAX];
|
|
INT wResult;
|
|
CHAR szExtension[8];
|
|
|
|
lstrcpy((LPSTR)szExtension, (LPSTR)"\\*");
|
|
lstrcat((LPSTR)szExtension, (LPSTR)vrgsz[IDS_FILEEXTENSION]);
|
|
|
|
wResult = cDlgOpen (vhInstance, vhwnd0, IDD_OPEN, IDCN_EDIT,
|
|
IDCN_LISTBOX, IDCN_PATH, szExtension, CCHFILESPECMAX,
|
|
szNewFileSpec, (OFSTRUCT *)&OFStruct[IDFILEORIGINAL], &hFile[IDFILEORIGINAL]);
|
|
|
|
|
|
switch (wResult)
|
|
{
|
|
case 1: /* hit ok, legal name */
|
|
LoadCal ();
|
|
break;
|
|
|
|
case 2: /*hit ok, illegal name */
|
|
AlertBox (vszBadFileName, szNewFileSpec, MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
break;
|
|
|
|
case -1: /*out of memory */
|
|
AlertBox (vszOutOfMemory, (CHAR *) NULL, MB_SYSTEMMODAL | MB_OK | MB_ICONHAND);
|
|
break;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
VOID APIENTRY OpenCal ()
|
|
{
|
|
CHAR szNewFileSpec [CCHFILESPECMAX] = "";
|
|
extern CHAR szLastDir[];
|
|
|
|
/* set up the variable fields of the OPENFILENAME struct. (the constant
|
|
* fields have been sel in CalInit()
|
|
*/
|
|
vOFN.lpstrFile = szNewFileSpec;
|
|
vOFN.lpstrInitialDir = szLastDir;
|
|
vOFN.lpstrTitle = vszOpenCaption;
|
|
vOFN.Flags = vfOpenFileReadOnly ? OFN_READONLY : 0L;
|
|
|
|
/* All long pointers should be defined immediately before the call.
|
|
* L.Raman - 2/12/91
|
|
*/
|
|
vOFN.lpstrFilter = (LPSTR)vszFilterSpec;
|
|
vOFN.lpstrCustomFilter = (LPSTR)vszCustFilterSpec;
|
|
vOFN.lpstrDefExt = vszFileExtension + 1; /* point to "CAL" */
|
|
|
|
if ( GetOpenFileName ((LPOPENFILENAME)&vOFN))
|
|
{
|
|
/* set the read-only flag depending on the state of Flags field */
|
|
vfOpenFileReadOnly = (vOFN.Flags & OFN_READONLY ? TRUE : FALSE);
|
|
hFile [IDFILEORIGINAL] = MOpenFile ( vOFN.lpstrFile,
|
|
(LPOFSTRUCT)&OFStruct [IDFILEORIGINAL],
|
|
OF_PROMPT+OF_CANCEL);
|
|
LoadCal ();
|
|
}
|
|
|
|
/* GetOpenFilename doesn't return if the supplied filename is bad */
|
|
}
|
|
|
|
|
|
/**** LoadCal ****/
|
|
|
|
VOID APIENTRY LoadCal ()
|
|
{
|
|
INT i;
|
|
INT FileHandle;
|
|
BYTE bkBuf [CBBK];
|
|
BOOL fOk;
|
|
UINT cb;
|
|
INT cdd;
|
|
DD * pdd;
|
|
D3 d3First;
|
|
BOOL fLoadToday = TRUE; /* Intilaise it; Fix for a Bug by SANKAR */
|
|
CHAR szNewFileSpec [CCHFILESPECMAX];
|
|
//- Save Header: Need temporary 16 bit buffer.
|
|
WORD wTemp;
|
|
|
|
/* Show the hour glass cursor. */
|
|
HourGlassOn ();
|
|
|
|
/* Start with a clean slate. Note that if the load fails, we will
|
|
end up in untitled mode, and this is OK since even if there is
|
|
another file open, it has either been saved or the user said not
|
|
to save the changes. In other words, the user has said it is
|
|
Ok to switch to another file, so there is no reason to be concerned
|
|
about what happens to the current file if the new file can't be
|
|
loaded.
|
|
*/
|
|
CleanSlate(FALSE);
|
|
|
|
if ((FileHandle = hFile[IDFILEORIGINAL]) == -1)
|
|
goto loadfin;
|
|
|
|
/* try opening file in READWRITE mode. If it fails, try opening it
|
|
in READ mode. If it works the file must have been marked read only
|
|
Set vfOpenFileReadOnly so that Calendar knows about this and does
|
|
not attempt to save in the end */
|
|
|
|
/* the file must be closed before a BLOCKWRITE call can successfully
|
|
be executed. The call to FCloseFile() was moved from inside the
|
|
following if conditional to in front of it. 1 Sept 1989 clarkc */
|
|
|
|
FCloseFile (IDFILEORIGINAL);
|
|
lstrcpy((LPSTR)szNewFileSpec, (LPSTR)OFStruct[IDFILEORIGINAL].szPathName);
|
|
|
|
// on win32, don't do the OemToAnsi -- ianja
|
|
// OemToAnsi((LPSTR)OFStruct[IDFILEORIGINAL].szPathName, (LPSTR)szNewFileSpec);
|
|
|
|
|
|
if((FileHandle = MOpenFile (szNewFileSpec,
|
|
(OFSTRUCT FAR *)&OFStruct[IDFILEORIGINAL],
|
|
OF_READWRITE|BLOCKWRITE))== -1)
|
|
{
|
|
FileHandle = MOpenFile (szNewFileSpec,
|
|
(OFSTRUCT FAR *)&OFStruct[IDFILEORIGINAL],
|
|
OF_READ);
|
|
vfOpenFileReadOnly = TRUE;
|
|
}
|
|
/*
|
|
* The opened file's handle must be stored sothat the file will get
|
|
* closed when FCloseFile() is called latter in this function;
|
|
* Otherwise the file handle in hFile[] will be -1 and the file will
|
|
* remain open cause "sharing violations" when run under SHARE.EXE.
|
|
* Fix for Bugs #5135, #5305 and #1848 --SANKAR-- 10-13-89
|
|
*/
|
|
hFile[IDFILEORIGINAL] = FileHandle;
|
|
|
|
if (!(fOk = M_llseek (FileHandle, (LONG)0, 0) != -1
|
|
&& _lread (FileHandle, bkBuf, CBBK) == CBBK))
|
|
goto error1;
|
|
|
|
|
|
/* Check the magic number. */
|
|
|
|
for (i = 6; i < CBMAGIC; i++)
|
|
{
|
|
if (bkBuf [i] != vrgbMagic [i])
|
|
{
|
|
/* File is open so this must be system modal. */
|
|
AlertBox (vszNotCalFile, (CHAR *)NULL,
|
|
MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
goto error0;
|
|
}
|
|
}
|
|
|
|
/* Try to make the tdd large enough to hold the DDs from the file.
|
|
Note that if successful, FGrowTdd will set vcddUsed to cdd since
|
|
CleanSlate had set it to 0 by calling InitTdd.
|
|
*/
|
|
BltByte ((BYTE *)(bkBuf + OBCDD), (BYTE *)&wTemp, CBCDD);
|
|
cdd = (INT)wTemp;
|
|
if (!FGrowTdd (0, cdd))
|
|
{
|
|
/* Couldn't grow the tdd. The error message has already been
|
|
displayed by FGrowTdd.
|
|
*/
|
|
goto error0;
|
|
}
|
|
|
|
/* Set up the rest of the items from the header. */
|
|
//- Get Header: Store the values in temporary 16-bit buffer.
|
|
BltByte ((BYTE *)(bkBuf + OBMINEARLYRING),(BYTE *)&wTemp, CBMINEARLYRING);
|
|
vcMinEarlyRing = (INT)wTemp;
|
|
|
|
BltByte ((BYTE *)(bkBuf + OBFSOUND), (BYTE *)&wTemp, CBFSOUND);
|
|
vfSound = (INT)wTemp;
|
|
|
|
BltByte ((BYTE *)(bkBuf + OBMDINTERVAL), (BYTE *)&wTemp, CBMDINTERVAL);
|
|
vmdInterval = (INT)wTemp;
|
|
|
|
BltByte ((BYTE *)(bkBuf + OBMININTERVAL), (BYTE *)&wTemp, CBMININTERVAL);
|
|
vcMinInterval = (INT)wTemp;
|
|
|
|
BltByte ((BYTE *)(bkBuf + OBFHOUR24), (BYTE *)&wTemp, CBFHOUR24);
|
|
vfHour24 = (INT)wTemp;
|
|
|
|
BltByte ((BYTE *)(bkBuf + OBTMSTART), (BYTE *)&vtmStart, CBTMSTART);
|
|
|
|
/* Set format of time display according to vfHour24 */
|
|
InitTimeDate(vhInstance, vfHour24 ? GTS_24HOUR : GTS_12HOUR);
|
|
|
|
/* Read in the tdd, and close the file. Ignore errors on close
|
|
(very unlikely) because if the data has been successfully read
|
|
there is no reason to give up now. If there's a real problem
|
|
we should find out about it when we try to read dates from the
|
|
file later on.
|
|
*/
|
|
cb = cdd * sizeof (DD);
|
|
fOk = _lread (FileHandle, TddLock (), cb) == cb;
|
|
TddUnlock ();
|
|
FCloseFile (IDFILEORIGINAL);
|
|
|
|
if (!fOk)
|
|
goto error2;
|
|
|
|
/* if can't load today, goto first record in file. 27-Oct-1987. davidhab */
|
|
fLoadToday = TRUE;
|
|
if (!FGetDateDr (vftCur.dt))
|
|
{
|
|
fLoadToday = FALSE;
|
|
pdd = TddLock();
|
|
|
|
if (!FGetDateDr(pdd->dt))
|
|
{
|
|
TddUnlock();
|
|
goto error2;
|
|
}
|
|
|
|
GetD3FromDt(pdd->dt, &d3First);
|
|
TddUnlock();
|
|
}
|
|
|
|
/* Arm the first alarm >= the current time.
|
|
Also see if the first alarm must go off immediately.
|
|
*/
|
|
GetNextAlarm (&vftCur, &vftCur, TRUE, (HWND)NULL);
|
|
AlarmCheck ();
|
|
|
|
/* The load went Ok - make this the open calendar file. */
|
|
SetTitle ((CHAR *)OFStruct [IDFILEORIGINAL].szPathName);
|
|
|
|
loadfin:
|
|
/* Go into day mode for today. Set the selected month to an
|
|
impossible one - this will cause SwitchToDate (called by
|
|
DayMode) to call SetUpMonth. Note that the SwitchToDate call
|
|
cannot fail since we have already read in the data for today
|
|
if there is any.
|
|
*/
|
|
vd3Sel.wMonth = MONTHDEC + 1;
|
|
if (fLoadToday)
|
|
DayMode (&vd3Cur);
|
|
else
|
|
DayMode (&d3First);
|
|
|
|
/* The waiting is over. */
|
|
HourGlassOff ();
|
|
return;
|
|
|
|
|
|
|
|
error2: /* Error occurred while attempting to read the tdd or the data
|
|
for today.
|
|
*/
|
|
/* Get rid of the partially loaded garbage. */
|
|
CleanSlate (FALSE);
|
|
|
|
|
|
error1: /* Error occurred attempting to read the header - nothing
|
|
has been changed, so no need to call CleanSlate again.
|
|
*/
|
|
/* Error attempting to read file. File is still open, so this
|
|
alert must be system modal.
|
|
*/
|
|
MessageBeep(0);
|
|
|
|
AlertBox (vszCannotReadFile, (CHAR *)NULL,
|
|
MB_SYSTEMMODAL | MB_OK | MB_ICONEXCLAMATION);
|
|
|
|
error0: /* Not a calendar file, or couldn't make tdd big enough. The
|
|
error message has already been displayed so the only thing
|
|
we must do is close the file. It's OK for other cases
|
|
(where the file has already been closed) to come through
|
|
here since FCloseFile properly handles an already closed file.
|
|
*/
|
|
/* Make sure the file is closed. */
|
|
FCloseFile (IDFILEORIGINAL);
|
|
|
|
goto loadfin;
|
|
}
|
|
|
|
/* ** Given filename which may or maynot include path, return pointer to
|
|
filename (not including path part.) */
|
|
CHAR * APIENTRY PFileInPath(register CHAR *sz)
|
|
{
|
|
register CHAR *pch;
|
|
|
|
/* Strip path/drive specification from name if there is one */
|
|
pch = (CHAR *)AnsiPrev((LPSTR)sz, (LPSTR)(sz + lstrlen((LPSTR)sz)));
|
|
while (pch > sz)
|
|
{
|
|
pch = (CHAR *)AnsiPrev((LPSTR)sz, (LPSTR)pch);
|
|
if (*pch == '\\' || *pch == ':')
|
|
{
|
|
pch = (CHAR *)AnsiNext((LPSTR)pch);
|
|
break;
|
|
}
|
|
}
|
|
return(pch);
|
|
}
|
|
|