1488 lines
36 KiB
C
1488 lines
36 KiB
C
/************************************************************/
|
||
/* 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;
|
||
}
|
||
|
||
|