windows-nt/Source/XPSP1/NT/base/mvdm/wow16/write/file.c

1488 lines
36 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/************************************************************/
/* Windows Write, Copyright 1985-1992 Microsoft Corporation */
/************************************************************/
/* file.c -- WRITE file interface functions */
/* It is important that the external interface to this module be entirely
at the level of "fn's", not "osfn's" and/or "rfn's". An intermodule call
may cause our files to be closed, and this is the only module internally
capable of compensating for this */
#define NOGDICAPMASKS
#define NOVIRTUALKEYCODES
#define NOWINMESSAGES
#define NOWINSTYLES
#define NOSYSMETRICS
#define NOMENUS
#define NOICON
#define NOKEYSTATE
#define NOSYSCOMMANDS
#define NORASTEROPS
#define NOSHOWWINDOW
#define NOSYSMETRICS
#define NOATOM
#define NOBITMAP
#define NOBRUSH
#define NOCLIPBOARD
#define NOCOLOR
#define NOCREATESTRUCT
#define NOCTLMGR
#define NODRAWTEXT
#define NOFONT
#define NOGDI
#define NOHDC
#define NOMEMMGR
#define NOMENUS
#define NOMETAFILE
#define NOMSG
#define NOPEN
#define NOPOINT
#define NORECT
#define NOREGION
#define NOSCROLL
#define NOSOUND
#define NOTEXTMETRIC
#define NOWH
#define NOWINOFFSETS
#define NOWNDCLASS
#define NOCOMM
#include <windows.h>
#include "mw.h"
#include "doslib.h"
#include "docdefs.h"
#include "filedefs.h"
#define NOSTRUNDO
#include "str.h"
#include "debug.h"
extern int vfDiskFull;
extern int vfSysFull;
extern int vfnWriting;
extern CHAR (*rgbp)[cbSector];
extern typeTS tsMruRfn;
extern struct BPS *mpibpbps;
extern int ibpMax;
extern struct FCB (**hpfnfcb)[];
extern typeTS tsMruBps;
extern struct ERFN dnrfn[rfnMax];
extern int iibpHashMax;
extern CHAR *rgibpHash;
extern int rfnMac;
extern int ferror;
extern CHAR szWriteDocPrompt[];
extern CHAR szScratchFilePrompt[];
extern CHAR szSaveFilePrompt[];
#ifdef CKSM
#ifdef DEBUG
extern unsigned (**hpibpcksm) [];
extern int ibpCksmMax;
#endif
#endif
#define IibpHash(fn,pn) ((int) ((fn + 1) * (pn + 1)) & 077777) % iibpHashMax
#ifdef DEBUG
#define STATIC
#else
#define STATIC static
#define ErrorWithMsg( idpmt, szModule ) Error( idpmt )
#define DiskErrorWithMsg( idpmt, szModule ) DiskError( idpmt )
#endif
#define osfnNil (-1)
STATIC int near RfnGrab( void );
STATIC int near FpeSeekFnPn( int, typePN );
STATIC typeOSFN near OsfnEnsureValid( int );
STATIC typeOSFN near OsfnReopenFn( int );
STATIC CHAR *(near SzPromptFromFn( int ));
/* The following debug flags are used to initiate, during debug, specific
low-level disk errors */
#ifdef DEBUG
int vfFakeReadErr = 0;
int vfFakeWriteErr = 0;
int vfFakeOpenErr = 0;
#endif
#ifdef CKSM
#ifdef DEBUG
unsigned CksmFromIbp( ibp )
int ibp;
{
int cb = mpibpbps [ibp].cch;
unsigned cksm = 0;
CHAR *pb;
Assert( ibp >= 0 && ibp < ibpCksmMax );
Assert( mpibpbps [ibp].fn != fnNil );
Assert( !mpibpbps [ibp].fDirty );
pb = rgbp [ibp];
while (cb-- > 0)
cksm += *(pb++);
return cksm;
}
#endif
#endif
typeFC FcMacFromUnformattedFn( fn )
int fn;
{ /* Obtain fcMac for passed unformatted fn by seeking to the file's end.
If it fails, return -1
If it succeeds, return the fcMac */
typeFC fcMac;
typeOSFN osfn;
Assert( (fn != fnNil) && (!(**hpfnfcb) [fn].fFormatted) );
if ((osfn = OsfnEnsureValid( fn )) == osfnNil)
return (typeFC) -1;
else
{
if ( FIsErrDwSeek( fcMac=DwSeekDw( osfn, 0L, SF_END )))
{
if (fcMac == fpeBadHndError)
{ /* Windows closed the file for us */
if ( ((osfn = OsfnReopenFn( fn )) != osfnNil) &&
!FIsErrDwSeek( fcMac = DwSeekDw( osfn, 0L, SF_END )) )
/* Successfully re-opened file */
return fcMac;
}
if (FIsCaughtDwSeekErr( fcMac ))
/* Suppress reporting of error -- Windows did it */
ferror = TRUE;
DiskErrorWithMsg(IDPMTSDE, " FcMacFromUnformattedFn");
return (typeFC) -1;
}
}
return fcMac;
}
IbpLru(ibpStarting)
int ibpStarting;
/*
Description: Find least recently used BPS (Buffer page) starting
at slot ibpStarting.
Returns: Number of the least recently used buffer slot.
*/
{
int ibp, ibpLru = 0;
typeTS ts, tsLru;
struct BPS *pbps = &mpibpbps[ibpStarting];
tsLru = -1; /* Since time stamps are unsigned ints, -1 */
/* is largest time stamp. */
for(ibp = ibpStarting; ibp < ibpMax; ibp++, pbps++)
{
ts = pbps->ts - (tsMruBps + 1);
/* The time stamp can conceivably wrap around and thus a */
/* simple < or > comparison between time stamps cannot be */
/* used to determine which is more or less recently used. */
/* The above statement normalizes time stamps so that the */
/* most recently used is FFFF and the others fall between */
/* 0 and FFFE. This method makes the assumption that */
/* time stamps older than 2^16 clicks of the time stamp */
/* counter have long since disappeared. Otherwise, such */
/* ancients would appear to be what they are not. */
if (ts <= tsLru) {tsLru = ts; ibpLru = ibp;}
}
return(ibpLru);
}
int IbpMakeValid(fn, pn)
int fn;
typePN pn;
{ /*
Description: Get page pn of file fn into memory.
Assume not already in memory.
Returns: Bp index (buffer slot #) where the page resides
in memory.
*/
#define cbpClump 4
#define cpnAlign 4
#define ALIGN
int ibpT, cbpRead;
int ibp, iibpHash, ibpPrev;
typePN pnT;
register struct BPS *pbps;
int ibpNew;
int cch;
int fFileFlushed;
#ifdef ALIGN
int dibpAlign;
#endif
#ifdef DEBUG
int cbpReadT;
CheckIbp();
#endif /* DEBUG */
Assert(fn != fnNil);
/* page is not currently in memory */
/* We will try to read in cbpClump Buffer Pages beginning with the
least recently used */
/* Pick best starting slot for read based on a least-recently-used scheme */
if (vfnWriting != fnNil)
/* Currently saving file, favor adjacent slots for fn being written */
ibpNew = IbpWriting( fn );
else
/* If reading from the scratch file, do not use the first
cpbMustKeep slots. This is necessary to prevent a disk full
condition from being catastrophic. */
ibpNew = IbpLru( (fn == fnScratch) ? cbpMustKeep : 0 );
/* Empty the slots by flushing out ALL buffers holding pieces of their fn's */
/* Compute cbpRead */
pbps = &mpibpbps[ ibpNew ];
for ( cbpRead = 0; cbpRead < min( cbpClump, ibpMax - ibpNew );
cbpRead++, pbps++ )
{
#ifdef CKSM
#ifdef DEBUG
int ibpT = pbps - mpibpbps;
if (!pbps->fDirty && pbps->fn != fnNil)
Assert( (**hpibpcksm) [ibpT] == CksmFromIbp( ibpT ) );
#endif
#endif
if (pbps->fDirty && pbps->fn != fnNil)
if (!FFlushFn( pbps->fn ))
break;
}
/* If a flush failed, cbpRead was reduced. If it was reduced to 0, this
is serious. If the file was not fnScratch, we can consider it flushed
even though the flush failed, because upper-level procedures will detect
the error and cancel the operation. If it was fnScratch, we must obtain
a new slot for the page we are trying to read */
if (cbpRead == 0)
{
if (pbps->fn == fnScratch)
ibpNew = IbpFindSlot( fn );
cbpRead++;
}
else
{ /* Restrict cbpRead according to the following:
(1) If we are reading in the text area, we don't want to
free pages for stuff past the end of the text area
that we know CchPageIn won't give us
(2) If we are reading in the properties area, we only want to
read one page, because FetchCp depends on us not trashing
the MRU page */
struct FCB *pfcb = &(**hpfnfcb)[fn];
if (pfcb->fFormatted && fn != fnScratch && pn >= pfcb->pnChar)
cbpRead = 1;
else
{
typePN cbpValid = (pfcb->fcMac - (pn * cfcPage)) / cfcPage;
cbpRead = min( cbpRead, max( 1, cbpValid ) );
}
}
#ifdef ALIGN
/* Align the read request on an even sector boundary, for speed */
dibpAlign = (pn % cpnAlign);
if (cbpRead > dibpAlign)
/* We are reading enough pages to cover the desired one */
pn -= dibpAlign;
else
dibpAlign = 0;
#endif
/* Remove flushed slots from their hash chains */
for ( pbps = &mpibpbps[ ibpT = ibpNew + cbpRead - 1 ];
ibpT >= ibpNew;
ibpT--, pbps-- )
if (pbps->fn != fnNil)
FreeBufferPage( pbps->fn, pbps->pn );
/* Free slots holding any existing copies of pages to be read */
#ifdef DBCS /* was in KKBUGFIX */
/* In #else code ,If pn= 8000H(= -32768), pnT can not be littler than pn */
for ( pnT = pn + cbpRead; pnT > pn; pnT-- ) // Assume cbpRead > 0
FreeBufferPage( fn, pnT-1 );
#else
for ( pnT = pn + cbpRead - 1; (int)pnT >= (int)pn; pnT-- )
FreeBufferPage( fn, pnT );
#endif
/* Read contents of file page(s) into buffer slot(s) */
cch = CchPageIn( fn, pn, rgbp[ibpNew], cbpRead );
#ifdef DEBUG
cbpReadT = cbpRead;
#endif
/* Fill in bps records for as many bytes as were read, but always
at least one record (to support PnAlloc). If we reached the end of the
file, the unfilled bps slots are left free */
pbps = &mpibpbps[ ibpT = ibpNew ];
do
{
pbps->fn = fn;
pbps->pn = pn;
pbps->ts = ++tsMruBps; /* mark page as MRUsed */
pbps->fDirty = false;
pbps->cch = min( cch, cbSector );
pbps->ibpHashNext = ibpNil;
cch = max( cch - cbSector, 0 );
/* put in new hash table entry for fn,pn */
iibpHash = IibpHash(fn, pn);
ibp = rgibpHash[iibpHash];
ibpPrev = ibpNil;
while (ibp != ibpNil)
{
ibpPrev = ibp;
ibp = mpibpbps[ibp].ibpHashNext;
}
if (ibpPrev == ibpNil)
rgibpHash[iibpHash] = ibpT;
else
mpibpbps[ibpPrev].ibpHashNext = ibpT;
pn++; ibpT++; pbps++;
} while ( (--cbpRead > 0) && (cch > 0) );
#ifdef CKSM
#ifdef DEBUG
/* Compute checksums for newly read pages */
{
int ibp;
for ( ibp = ibpNew; ibp < ibpNew + cbpReadT; ibp++ )
if (mpibpbps [ibp].fn != fnNil && !mpibpbps [ibp].fDirty)
(**hpibpcksm) [ibp] = CksmFromIbp( ibp );
}
CheckIbp();
#endif /* DEBUG */
#endif
#ifdef ALIGN
return (ibpNew + dibpAlign);
#else
return (ibpNew);
#endif
} /* end of I b p M a k e V a l i d */
FreeBufferPage( fn, pn )
int fn;
int pn;
{ /* Free buffer page holding page pn of file fn if there is one */
/* Flushes fn if page is dirty */
int iibp = IibpHash( fn, pn );
int ibp = rgibpHash[ iibp ];
int ibpPrev = ibpNil;
Assert( fn != fnNil );
while (ibp != ibpNil)
{
struct BPS *pbps=&mpibpbps[ ibp ];
if ( (pbps->fn == fn) && (pbps->pn == pn ) )
{ /* Found it. Remove this page from the chain & mark it free */
if (pbps->fDirty)
FFlushFn( fn );
#ifdef CKSM
#ifdef DEBUG
else /* Page has not been trashed while in memory */
{
Assert( (**hpibpcksm) [ibp] == CksmFromIbp( ibp ) );
}
#endif
#endif
if (ibpPrev == ibpNil)
/* First entry in hash chain */
rgibpHash [ iibp ] = pbps->ibpHashNext;
else
mpibpbps[ ibpPrev ].ibpHashNext = pbps->ibpHashNext;
pbps->fn = fnNil;
pbps->fDirty = FALSE;
/* Mark the page not recently used */
pbps->ts = tsMruBps - (ibpMax * 4);
/* Mark pages that are on even clump boundaries a bit less recently
used so they are favored in new allocations */
if (ibp % cbpClump == 0)
--(pbps->ts);
pbps->ibpHashNext = ibpNil;
}
ibpPrev = ibp;
ibp = pbps->ibpHashNext;
}
#ifdef DEBUG
CheckIbp();
#endif
}
int CchPageIn(fn, pn, rgbBuf, cpnRead)
int fn;
typePN pn;
CHAR rgbBuf[];
int cpnRead;
{ /*
Description: Read a cpnRead pages of file fn from disk into rgbBuf.
Have already determined that page is not resident
in the buffer.
Returns: Number of valid chars read (zero or positive #).
*/
struct FCB *pfcb = &(**hpfnfcb)[fn];
typeFC fcMac = pfcb->fcMac;
typeFC fcPage = pn * cfcPage;
int dfc;
int fCharFormatInfo; /* if reading Format info part of */
/* file then = TRUE, text part of */
/* file then = FALSE; */
/* No reads > 32767 bytes, so dfc can be int */
Assert( cpnRead <= 32767 / cbSector );
/* Don't try to read beyond pnMac */
if (cpnRead > pfcb->pnMac - pn)
cpnRead = pfcb->pnMac - pn;
dfc = cpnRead * (int)cfcPage;
if (pn >= pfcb->pnMac) /* are we trying to read beyond eof? */
{
return 0; /* Nothing to read */
}
else if (pfcb->fFormatted && fn != fnScratch
&& fn != vfnWriting /* Since pnChar is zero in this case */
&& pn >= pfcb->pnChar)
{ /* reading character format info portion of file */
fCharFormatInfo = TRUE;
}
else /* reading text portion of file */
{ /* get dfc (cch) from fcMac */
typeFC dfcT = fcMac - fcPage;
fCharFormatInfo = FALSE;
if (dfcT < dfc)
dfc = (int) dfcT;
else if (dfc <= fc0) /* Nothing to read, so let's avoid disk access. */
{
return 0;
}
}
return CchReadAtPage( fn, pn, rgbBuf, (int)dfc, fCharFormatInfo );
}
CchReadAtPage( fn, pn, rgb, dfc, fSeriousErr )
int fn;
typePN pn;
CHAR rgb[];
int dfc;
int fSeriousErr;
{ /*
Description: Read dfc bytes of file fn starting at page pn
into rgb
Returns: Number of valid chars read (zero or positive #)
Errors: Returns # of chars read; ferror & vfDiskFull are
set (in DiskError) if an error occurs.
Notes: Caller is responsible for assuring that the page
range read is reasonable wrt the fn
*/
typeOSFN osfn;
int fpeSeek;
int fpeRead;
int fCaught; /* Whether error was reported by DOS */
#ifdef DEBUG
if (vfFakeReadErr)
{
dfc = 0;
goto ReportErr;
}
#endif
if (!FIsErrFpe( fpeSeek = FpeSeekFnPn( fn, pn )))
{
osfn = OsfnEnsureValid( fn );
#ifdef DEBUG
#ifdef DFILE
CommSzSz( "Read from file: ", &(**(**hpfnfcb)[fn].hszFile)[0] );
#endif
#endif
if ((fpeRead = CchReadDoshnd( osfn, (CHAR FAR *)rgb, dfc )) == dfc)
{ /* Read succeeded */
return dfc;
}
else
{
/* Should be guaranteed that file was not closed because of seek */
Assert( fpeRead != fpeBadHndError );
fCaught = FIsCaughtFpe( fpeRead );
}
}
else
fCaught = FIsCaughtFpe( fpeSeek );
/* unable to set position or read */
if ((fn == fnScratch) || (fSeriousErr))
{ /* unrecoverable error: either can't read from scratch */
/* file or unable to read format info (FIB or format pages) part of */
/* of some file. */
dfc = 0;
goto ReportErr;
}
else /* serious disk error on file (recoverable).*/
{
int cchRead = (FIsErrFpe(fpeSeek) || FIsErrFpe(fpeRead)) ? 0 : fpeRead;
CHAR *pch = &rgb[cchRead];
int cch = dfc - cchRead;
/* If the positioning failed or the read failed
for a reason other than disk full, completely
fill the buffer with 'X's. Otherwise, just
fill the disputed portion (diff between #char
requested and #char read) with 'X's */
while (cch-- > 0)
*pch++ = 'X';
ReportErr:
if (fCaught)
/* Suppress reporting of the error -- Windows reported it */
ferror = TRUE;
if (pn != 0)
/* Report error if not reading FIB */
DiskErrorWithMsg(IDPMTSDE, " CchReadAtPage");
return (int)dfc;
/* recovery is accomplished: 1) Upper level procedures do not
see any change in the world, 2) the worst thing that
happens is that the user sees some 'X's on the screen. */
}
}
AlignFn(fn, cch, fEven)
int fn, cch;
{ /* Make sure we have cch contiguous chars in fn */
/* if fEven, make sure fcMac is even */
struct FCB *pfcb = &(**hpfnfcb)[fn];
typeFC fcMac = pfcb->fcMac;
typePN pn;
typeFC fcFirstPage;
pn = fcMac / cfcPage;
fcFirstPage = (pn + 1) * cfcPage;
Assert(cch <= cfcPage);
if (fEven && (fcMac & 1) != 0)
++cch;
if (fcFirstPage - fcMac < cch)
{
struct BPS *pbps = &mpibpbps[IbpEnsureValid(fn, pn++)];
pbps->cch = cfcPage;
pbps->fDirty = true;
fcMac = pfcb->fcMac = fcFirstPage;
}
if (fEven && (fcMac & 1) != 0)
{
struct BPS *pbps = &mpibpbps[IbpEnsureValid(fn, pn)];
pbps->cch++;
pbps->fDirty = true;
pfcb->fcMac++;
}
} /* end of A l i g n F n */
/*** OsfnEnsureValid - ensure that file fn is open
*
*
*/
STATIC typeOSFN near OsfnEnsureValid(fn)
int fn;
{ /*
Description: Ensure that file fn is open (really)
Returns: operating system file number (osfnNil if error)
*/
struct FCB *pfcb = &(**hpfnfcb)[fn];
int rfn = pfcb->rfn;
if (rfn == rfnNil)
{ /* file doesn't have a rfn - ie. it is not opened */
#ifdef DEBUG
#ifdef DFILE
CommSzSz( pfcb->fOpened ? "Re-opening file " :
"Opening file", **pfcb->hszFile );
#endif
if (vfFakeOpenErr || !FAccessFn( fn, dtyNormal ))
#else
if (!FAccessFn( fn, dtyNormal ))
#endif
{ /* unrecoverable error - unable to open file */
DiskErrorWithMsg(IDPMTSDE, " OsfnEnsureValid");
return osfnNil;
}
rfn = pfcb->rfn;
Assert( (rfn >= 0) && (rfn < rfnMac) );
}
return dnrfn[rfn].osfn;
}
/* end of O s F n E n s u r e V a l i d */
STATIC int near FpeSeekFnPn( fn, pn )
int fn;
typePN pn;
{ /* Seek to page pn of file fn
return err code on failure, fpeNoErr on success
Leaves file open (if no error occurs)
Recovers from the case in which windows closed the file for us
*/
typeOSFN osfn;
long dwSeekReq;
long dwSeekAct;
#ifdef DEBUG
#ifdef DFILE
CommSzSz( "Seeking within file ", **(**hpfnfcb)[fn].hszFile );
#endif
#endif
//osfn = OsfnEnsureValid( fn );
if ((osfn = OsfnEnsureValid( fn )) == osfnNil)
return fpeNoAccError;
dwSeekReq = (long) pn * (long) cbSector;
dwSeekAct = DwSeekDw( osfn, dwSeekReq, SF_BEGINNING);
if ( ((int) dwSeekAct) == fpeBadHndError )
{ /* Windows closed the file for us -- must reopen it */
if ((osfn = OsfnReopenFn( fn )) == osfnNil)
return fpeNoAccError;
else
dwSeekAct = DwSeekDw( osfn, dwSeekReq, SF_BEGINNING );
}
return (dwSeekAct >= 0) ? fpeNoErr : (int) dwSeekAct;
}
int FFlushFn(fn)
int fn;
{ /*
Description: Write all dirty pages of fn to disk.
Sets vfSysFull = TRUE if disk full error occurred
while flushing fnScratch. Otherwise, a disk full
error causes vfDiskFull = TRUE.
Serious disk errors cause vfDiskError = TRUE
Only the pages which actually made it to disk are
marked as non-dirty.
Returns: TRUE if successful, FALSE if Disk full error
while writing pages to disk. Any other error
is unrecoverable, ie. go back to main loop.
To avoid extraneous error messages, the following
two entry conditions cause FFlush to immediately
return FALSE:
- If vfSysFull = TRUE and fn = fnScratch
- If vfDiskFull = TRUE and fn = vfnWriting
*/
int ibp;
typeOSFN osfn;
int fpe;
int cchWritten;
int cchAskWrite;
struct BPS *pbps;
Assert( fn != fnNil );
if ((vfSysFull) && (fn == fnScratch)) return (FALSE);
if ((vfDiskFull) && (fn == vfnWriting)) return (FALSE);
for (ibp = 0, pbps = mpibpbps; ibp < ibpMax; )
{
if (pbps->fn == fn && pbps->fDirty)
{
typePN pn = pbps->pn;
int cbp = 0;
CHAR *pch = (CHAR *)rgbp[ibp];
struct BPS *pbpsStart = &mpibpbps[ibp];
/* Coalesce all consecutive pages for a single write */
do
{
/* taken out 11/7/84 - can't mark scratch file
page as non dirty if chance that it will never
get written out (write fails - disk full)
pbps->fDirty = false; mark page as clean */
++ibp, ++cbp, ++pbps;
} while (ibp < ibpMax && pbps->fn == fn && pbps->pn == pn + cbp);
/* Now do the write, checking for out of disk space */
Scribble(3, 'W');
cchAskWrite = (int)cbSector * (cbp - 1) + (pbps - 1)->cch;
cchWritten = cchDiskHardError; /* assure hard error if seek fails */
#ifdef DEBUG
if (vfFakeWriteErr)
goto SeriousError;
else
#endif
if ( FIsErrFpe( FpeSeekFnPn( fn, pn )) ||
((osfn = OsfnEnsureValid( fn )) == osfnNil) ||
#ifdef DEBUG
#ifdef DFILE
(CommSzSz( "Writing to file: ", &(**(**hpfnfcb)[fn].hszFile)[0] ),
#endif
#endif
(( cchWritten = CchWriteDoshnd( osfn, (CHAR FAR *)pch,
cchAskWrite )) != cchAskWrite))
#ifdef DEBUG
#ifdef DFILE
)
#endif
#endif
{
/* Should be guaranteed that windows did not close the file
since we have not called intermodule since the seek */
Assert( cchWritten != fpeBadHndError );
/* Seek or Write error */
if ( !FIsErrCchDisk(cchWritten) )
{ /* serious but recoverable disk error */
/* Ran out of disk space; write failed */
if (fn == fnScratch)
vfSysFull = fTrue;
vfDiskFull = fTrue;
DiskErrorWithMsg(IDPMTDFULL, " FFlushFn");
return(FALSE);
}
else /* cause of error is not disk full */
{ /* unrecov. disk error */
#ifdef DEBUG
SeriousError:
#endif
DiskErrorWithMsg(IDPMTSDE2, " FFlushFn");
return FALSE;
}
}
Diag(CommSzNumNum(" cchWritten, cchAskWrite ",cchWritten,cchAskWrite));
/* ---- write was successful ---- */
Scribble(3, ' ');
while (cbp-- > 0)
{ /* mark pages actually copied to disk as non dirty */
(pbpsStart++)->fDirty = false;
#ifdef CKSM
#ifdef DEBUG
{
int ibpT = pbpsStart - 1 - mpibpbps;
/* Recompute checksums for pages which are now clean */
(**hpibpcksm) [ibpT] = CksmFromIbp( ibpT );
}
#endif
#endif
}
}
else
{
++ibp;
++pbps;
}
}
return (TRUE);
}
#ifdef DEBUG
CheckIbp()
{
/* Walk through the rgibpHash and the mpibpbps structure to make sure all of
the links are right. */
/* 10/11/85 - Added extra Assert ( FALSE ) so we get the messages instead
of a freeze-up on systems not connected to a COM port.
Ignoring the assertion will produce the RIP with the ibp info */
extern int fIbpCheck;
int rgfibp[255];
int ibp;
int ibpHash;
int iibp;
static BOOL bAsserted=FALSE;
if (fIbpCheck && !bAsserted)
{
if (!(ibpMax < 256))
{
Assert(0);
bAsserted=TRUE;
return;
}
bltc(rgfibp, false, ibpMax);
/* Are there any circular links in mpibpbps? */
for (iibp = 0; iibp < iibpHashMax; iibp++)
{
if ((ibpHash = rgibpHash[iibp]) != ibpNil)
{
if (!(ibpHash < ibpMax))
{
Assert(0);
bAsserted=TRUE;
return;
}
if (rgfibp[ibpHash])
{
/* Each entry in rgibpHash should point to an unique chain.
*/
Assert(0);
bAsserted=TRUE;
#if DUGSTUB
FatalExit(0x100 | ibp);
#endif
return;
}
else
{
rgfibp[ibpHash] = true;
while ((ibpHash = mpibpbps[ibpHash].ibpHashNext) != ibpNil)
{
if (!(ibpHash < ibpMax))
{
Assert(0);
bAsserted=TRUE;
return;
}
if (rgfibp[ibpHash])
{
/* The chain should be non-circular and unique. */
Assert( FALSE );
bAsserted=TRUE;
#if DUGSTUB
FatalExit(0x200 | ibpHash);
#endif
return;
}
rgfibp[ibpHash] = true;
}
}
}
}
/* All chains not pointed to by rgibpHash should be nil. */
for (ibp = 0; ibp < ibpMax; ibp++)
{
if (!rgfibp[ibp])
{
if (mpibpbps[ibp].fn != fnNil)
{
Assert( FALSE );
bAsserted=TRUE;
#if DUGSTUB
FatalExit(0x400 | mpibpbps[ibp].fn);
#endif
return;
}
if (mpibpbps[ibp].ibpHashNext != ibpNil)
{
Assert( FALSE );
bAsserted=TRUE;
#if DUGSTUB
FatalExit(0x300 | ibp);
#endif
return;
}
}
}
}
}
#endif /* DEBUG */
/* Formerly fileOC.c -- file open and close routines */
/*** SetRfnMac - set usable # of rfn slots
*
* ENTRY: crfn - desired # of rfn slots
* EXIT: (global) rfnMac - set to crfn, if possible
*
* The ability to adjust the usable # of rfn slots is a new addition in
* Windows Word. The two things that it accomplishes are:
*
* (1) Gives the ability for Word to scale back its need for
* DOS file handles if not enough are available
* (2) Permits Word to attempt to grab more file handles than usual
* when this would help performance (in particular, during
* Transfer Save, when the original, scratch, and write files
* are most commonly open)
*
*/
SetRfnMac( crfn )
int crfn;
{
int rfn;
Assert( (crfn > 0) && (crfn <= rfnMax) );
Assert( (sizeof (struct ERFN) & 1) == 0); /* ERFN must be even for blt */
if (crfn > rfnMac)
{ /* Add rfn slots */
for ( rfn = rfnMac; rfn < crfn; rfn++ )
dnrfn [rfn].fn = fnNil; /* These will get used next (see RfnGrab)*/
rfnMac = crfn;
}
else /* Lose Rfn slots (keep the most recently used one(s)) */
while ( rfnMac > crfn )
{
int rfnLru=RfnGrab();
int fn;
if ( (rfnLru != --rfnMac) && ((fn = dnrfn [rfnMac].fn) != fnNil) )
{
extern int fnMac;
Assert( fn >= 0 && fn < fnMac );
(**hpfnfcb) [fn].rfn = rfnLru;
blt( &dnrfn [rfnMac], &dnrfn [rfnLru],
sizeof(struct ERFN)/sizeof(int) );
}
}
}
/*========================================================*/
STATIC int near RfnGrab()
{ /*
Description: Allocate the least recently used rfn (real file #)
slot for a new file.
Returns: rfn slot number. */
int rfn = 0, rfnLru = 0;
typeTS ts, tsLru;
struct ERFN *perfn = &dnrfn[rfn];
/* Time stamp algorithm akin to method used with Bps. */
/* See IbpLru in file.c for comments. */
tsLru = -1; /* max unsigned number */
for ( rfn = 0; rfn < rfnMac ; rfn++, perfn++ )
{
ts = perfn->ts - (tsMruRfn + 1);
if (perfn->fn == fnNil)
ts = ts - rfnMac;
/* cluge: If slot is unused, give it a lower ts. */
/* This ensures that an occupied slot can never be */
/* swapped out if a empty one exists. */
if (ts <= tsLru)
{
tsLru = ts;
rfnLru = rfn;
}
}
if (dnrfn [rfnLru].fn != fnNil)
CloseRfn( rfnLru );
return rfnLru;
}
CloseFn( fn )
int fn;
{ /* Close file fn if it is currently open */
int rfn;
if (fn == fnNil)
return;
if (((rfn = (**hpfnfcb)[fn].rfn) != rfnNil) && (rfn != rfnFree))
CloseRfn( rfn );
}
OpenEveryHardFn()
{ /* For each fn representing a file on nonremoveable media,
try to open it. It is not guaranteed that any or all such files
will be open on return from this routine -- we are merely attempting
to assert our ownership of these files on a network by keeping them open */
extern int fnMac;
int fn;
struct FCB *pfcb;
#ifdef DFILE
extern int docCur;
CommSzNum("OpenEveryHardFn: docCur ", docCur);
#endif
for ( fn = 0, pfcb = &(**hpfnfcb) [0] ; fn < fnMac; fn++, pfcb++ )
{
/* Bryanl 3/26/87: only call FAccessFn if rfn == rfnNil, to prevent
multiple opens of the same file, which fail if the sharer is loaded */
#ifdef DFILE
{
char rgch[100];
if (pfcb->rfn == rfnNil && ((POFSTRUCT)(pfcb->rgbOpenFileBuf))->fFixedDisk)
{
wsprintf(rgch," fn %d, hszFile %s \n\r",fn,(LPSTR)(**pfcb->hszFile));
CommSz(rgch);
wsprintf(rgch," OFSTR %s \n\r", (LPSTR)(((POFSTRUCT)pfcb->rgbOpenFileBuf)->szPathName));
CommSz(rgch);
}
else
{
wsprintf(rgch," fn %d, not accessing, sz %s\n\r", fn, (LPSTR) (LPSTR)(**pfcb->hszFile));
CommSz(rgch);
wsprintf(rgch," OFSTR %s \n\r", (LPSTR)(((POFSTRUCT)pfcb->rgbOpenFileBuf)->szPathName));
CommSz(rgch);
}
}
#endif
if (pfcb->rfn == rfnNil && ((POFSTRUCT)(pfcb->rgbOpenFileBuf))->fFixedDisk)
{ /* fn refers to a file on nonremoveable media */
FAccessFn( fn, dtyNormal );
}
}
}
STATIC typeOSFN near OsfnReopenFn( fn )
int fn;
{ /* Reopen file fn after it was automatically closed by Windows due
to disk swap. State is: fn has an rfn but rfn's osfn has been
made a "bad handle" */
struct FCB *pfcb = &(**hpfnfcb) [fn];
int rfn = pfcb->rfn;
typeOSFN osfn;
WORD wOpen;
Assert( fn != fnNil );
Assert( rfn != rfnNil );
Assert( pfcb->fOpened);
/* Only files on floppies are automatically closed */
Assert( ! ((POFSTRUCT)(pfcb->rgbOpenFileBuf))->fFixedDisk );
#ifdef DEBUG
#ifdef DFILE
CommSzSz( "Opening after WINDOWS close: ", **pfcb->hszFile );
#endif
#endif
wOpen = OF_REOPEN | OF_PROMPT | OF_CANCEL | OF_SHARE_DENY_WRITE |
((pfcb->mdFile == mdBinary) ? OF_READWRITE : OF_READ );
SetErrorMode(1);
osfn = OpenFile( (LPSTR) SzPromptFromFn( fn ),
(LPOFSTRUCT) pfcb->rgbOpenFileBuf, wOpen );
SetErrorMode(0);
if (osfn == -1)
return osfnNil;
else
{
dnrfn[ rfn ].osfn = osfn;
}
return osfn;
}
FAccessFn( fn, dty)
int fn;
int dty;
{ /* Description: Access file which is not currently opened.
Open file and make an appropriate entry in the
rfn table. Put the rfn into (**hpfnfcb)[fn].rfn.
Returns: TRUE on success, FALSE on failure
*/
extern int vwDosVersion;
extern HANDLE hParentWw;
extern HWND vhWndMsgBoxParent;
int rfn;
register struct FCB *pfcb = &(**hpfnfcb)[fn];
typeOSFN osfn;
int wOpen;
#ifdef DEBUG
int junk;
Assert(FValidFile(**pfcb->hszFile, CchSz(**pfcb->hszFile)-1, &junk));
#ifdef DFILE
{
char rgch[100];
CommSzSz("FAccessFn: ", pfcb->fOpened ? SzPromptFromFn( fn ) : &(**pfcb->hszFile)[0]);
wsprintf(rgch, " * OFSTR before %s \n\r", (LPSTR)(((POFSTRUCT)pfcb->rgbOpenFileBuf)->szPathName));
CommSz(rgch);
}
#endif
#endif /*DEBUG*/
if ((**pfcb->hszFile)[0] == 0) /* if file name field is blank, */
return FALSE; /* unable to open file */
wOpen = /*OF_PROMPT + OF_CANCEL + (6.21.91) v-dougk bug #6910 */
(((pfcb->mdFile == mdBinary) ?
OF_READWRITE : OF_READ) |
OF_SHARE_DENY_WRITE);
if (pfcb->fOpened)
wOpen += OF_REOPEN;
else if (pfcb->fSearchPath)
wOpen += OF_VERIFY;
if ((vwDosVersion & 0x7F) >= 2)
{
WORD da;
if ((vwDosVersion & 0x7F) >= 3)
/* Above DOS 3, set attributes to deny access if the sharer is in */
wOpen += bSHARE_DENYRDWR;
if ( ( (pfcb->mdFile == mdBinary) && (!pfcb->fOpened)) &&
((da = DaGetFileModeSz( &(**pfcb->hszFile) [0] )) != DA_NIL) &&
(da & DA_READONLY) )
{ /* This is here because the Novell net does not allow us to test
for read-only files by opening in read/write mode -- it lets
us open them anyway! */
goto TryReadOnly;
}
}
for ( ;; )
{
/* OpenFile's first parm is a filename when opening for the
first time, a prompt on successive occasions (OF_REOPEN) */
SetErrorMode(1);
osfn = OpenFile( pfcb->fOpened ?
(LPSTR) SzPromptFromFn( fn ) :
(LPSTR) &(**pfcb->hszFile)[0],
(LPOFSTRUCT) &pfcb->rgbOpenFileBuf[0],
wOpen );
SetErrorMode(0);
if (osfn != -1) /* Note != -1: osfn is unsigned */
{ /* Opened file OK */
#ifdef DFILE
{
char rgch[100];
wsprintf(rgch, " * OFSTR now %s \n\r", (LPSTR)(((POFSTRUCT)(**hpfnfcb) [fn].rgbOpenFileBuf)->szPathName));
CommSz(rgch);
}
#endif
if (!pfcb->fOpened)
{ /* First time through: OpenFile may have given us a
different name for the file */
CHAR szT [cchMaxFile];
CHAR (**hsz) [];
#if WINVER >= 0x300
/* Currently: FNormSzFile *TAKES* an OEM sz, and
*RETURNS* an ANSI sz ..pault */
#endif
if (FNormSzFile( szT, ((POFSTRUCT)pfcb->rgbOpenFileBuf)->szPathName,
dty ) &&
WCompSz( szT, &(**pfcb->hszFile) [0] ) != 0 &&
FnFromSz( szT ) == fnNil &&
!FNoHeap( hsz = HszCreate( szT )))
{
/* Yes, indeed, the name OpenFile gave us was different.
Put the normalized version into the fcb entry */
FreeH( (**hpfnfcb) [fn].hszFile ); /* HEAP MOVEMENT */
(**hpfnfcb) [fn].hszFile = hsz;
}
}
break; /* We succeeded; break out of the loop */
}
else
{ /* Open failed -- try read-only; don't prompt this time */
if ( (pfcb->mdFile == mdBinary) && (!pfcb->fOpened) )
{ /* Failed as read/write; try read-only */
/* Check for sharing violation */
if (((vwDosVersion & 0x7F) >= 3) &&
(((POFSTRUCT) pfcb->rgbOpenFileBuf)->nErrCode == nErrNoAcc))
{
if ( DosxError() == dosxSharing )
{
extern int vfInitializing;
int fT = vfInitializing;
vfInitializing = FALSE; /* Report this err, even during inz */
{
char szMsg[cchMaxSz];
MergeStrings (IDPMTCantShare, **pfcb->hszFile, szMsg);
IdPromptBoxSz(vhWndMsgBoxParent ? vhWndMsgBoxParent : hParentWw, szMsg, MB_OK|MB_ICONEXCLAMATION);
}
vfInitializing = fT;
return FALSE;
}
}
TryReadOnly:
pfcb->mdFile = mdBinRO;
wOpen = OF_READ;
if (pfcb->fOpened)
wOpen += OF_REOPEN;
#ifdef ENABLE
else if (pfcb->fSearchPath)
wOpen += OF_VERIFY;
#endif
}
else
{
if ((**hpfnfcb)[fn].fDisableRead)
/* already reported */
{
ferror = TRUE;
return FALSE;
}
else
{ /* Could not find file in place specified */
char szMsg[cchMaxSz];
extern int ferror;
extern int vfInitializing;
int fT = vfInitializing;
BOOL bRetry=TRUE;
extern struct DOD (**hpdocdod)[];
extern int docCur;
/* get user to put file back */
MergeStrings (IDPMTFileNotFound, **pfcb->hszFile, szMsg);
vfInitializing = FALSE; /* Report this err, even during inz */
/*
This is frippin insidious. MessageBox yields and allows us to get
into here again before it has even been issued!
*/
(**hpdocdod)[docCur].fDisplayable = FALSE; // block redraws
/* if we're being called from a message box, then use it
as the parent window, otherwise use main write window */
if (IdPromptBoxSz(vhWndMsgBoxParent ? vhWndMsgBoxParent : hParentWw,
szMsg, MB_OKCANCEL | MB_ICONEXCLAMATION | MB_APPLMODAL)
== IDCANCEL)
{
vfInitializing = fT;
(**hpfnfcb)[fn].fDisableRead = TRUE;
ferror = TRUE; /* need to flag */
bRetry = FALSE;
}
(**hpdocdod)[docCur].fDisplayable = TRUE; // unblock redraws
vfInitializing = fT;
if (!bRetry)
return FALSE;
}
}
}
}
pfcb->fOpened = TRUE;
rfn = RfnGrab();
{
struct ERFN *perfn = &dnrfn [rfn];
perfn->osfn = osfn;
perfn->fn = fn;
perfn->ts = ++tsMruRfn; /* mark Rfn as MRused */
}
(**hpfnfcb) [fn].rfn = rfn;
(**hpfnfcb) [fn].fDisableRead = FALSE;
return TRUE;
}
FCreateFile( szFile, fn ) /* returns szFile in ANSI ..pault */
CHAR *szFile;
int fn;
{ /* Create a new, unique file.
Return the name in szFile.
Returns TRUE on success, FALSE on failure.
Leaves the filename in (**hpfnfcb)[fn].hszFile (if successful),
the rfn in (**hpfnfcb)[fn].rfn.
If szFile begins "X:...", creates the file on the specified drive;
otherwise, creates the file on a drive of Windows' choice */
extern CHAR szExtDoc[];
CHAR (**hsz)[];
CHAR szFileT [cchMaxFile];
if (!GetTempFileName(szFile[0] | ((szFile[1] == ':') ? TF_FORCEDRIVE : 0),
(LPSTR)(szExtDoc+1), 0, (LPSTR)szFileT) )
{ /* can't create file */
DiskErrorWithMsg( IDPMTSDE, " RfnCreate" );
/* recovery is accomplished: only FnCreateSz calls FCreateFile.
FnCreateSz returns nil if FCreateFile returns FALSE. All of
FnCreateSz's callers check for nil */
return FALSE;
}
/* Succeeded in creating file */
FNormSzFile( szFile, szFileT, dtyNormal );
if ( FNoHeap( hsz = HszCreate( (PCH)szFile )))
return FALSE;
(**hpfnfcb) [fn].hszFile = hsz;
if ( !FAccessFn( fn, dtyNormal ) )
return FALSE;
return TRUE;
} /* end of F C r e a t e F i l e */
FEnsureOnLineFn( fn )
int fn;
{ /* Ensure that file fn is on line (i.e. on a disk that is accessible).
Return TRUE if we were able to guarantee this, FALSE if not */
int rfn;
Assert( fn != fnNil );
if ( ((POFSTRUCT)(**hpfnfcb) [fn].rgbOpenFileBuf)->fFixedDisk )
/* If it's on nonremovable media, we know it's on line */
return TRUE;
/* If it's open, must close and re-open, cause windows might have closed it */
if ((rfn = (**hpfnfcb) [fn].rfn) != rfnNil)
CloseRfn( rfn );
return FAccessFn( fn, dtyNormal );
}
typePN PnAlloc(fn)
int fn;
{ /* Allocate the next page of file fn */
typePN pn;
struct BPS *pbps;
struct FCB *pfcb = &(**hpfnfcb)[fn];
AlignFn(fn, (int)cfcPage, false);
pn = pfcb->fcMac / cfcPage;
pbps = &mpibpbps[IbpEnsureValid(fn, pn)];
pbps->cch = cfcPage;
pbps->fDirty = true;
pfcb->fcMac += cfcPage;
pfcb->pnMac = pn + 1;
return pn;
}
STATIC CHAR *(near SzPromptFromFn( fn ))
int fn;
{
extern int vfnSaving;
CHAR *pch;
Assert( fn != fnNil );
if (fn == vfnSaving)
pch = szSaveFilePrompt;
else if (fn == fnScratch)
pch = szScratchFilePrompt;
else
pch = szWriteDocPrompt;
return pch;
}