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

