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