1991 lines
48 KiB
C
1991 lines
48 KiB
C
#include "precomp.h"
|
|
#pragma hdrstop
|
|
EnableAssert
|
|
|
|
/* sys.c - provides access to system calls and a consistent error handling
|
|
mechanism. On DOS, it is assumed that the int 24 handler calls
|
|
GetExtendedError to set some globals and returns with al = 3 (fail the call).
|
|
*/
|
|
|
|
int enCur;
|
|
int eaCur;
|
|
|
|
int enPrev = -1; /* used to save state so we don't */
|
|
int eaPrev = -1; /* retry the same error too many times */
|
|
|
|
void geterr(void);
|
|
int WRetryErr(int, char *, MF *, char *);
|
|
|
|
|
|
/* make and open temp file in same directory in which pthReal is located.
|
|
|
|
Mm is set to mmDelTemp. Must free via CloseMf. Pointer to pthReal retained!
|
|
*/
|
|
MF *
|
|
PmfMkTemp(
|
|
PTH pthReal[],
|
|
int mode,
|
|
FX fx)
|
|
{
|
|
register MF *pmf;
|
|
register char *pch;
|
|
char szReal[cchPthMax];
|
|
int wRetErr;
|
|
|
|
pmf = PmfAlloc(pthReal, (char *)0, fx); /* we install unique name below */
|
|
|
|
SzPhysPath(szReal, pthReal);
|
|
|
|
pch = rindex(szReal, '/'); /* find last / of physical path */
|
|
AssertF(pch != 0);
|
|
*(pch+1) = '\0'; /* name will be put here */
|
|
|
|
while ((pmf->fdWrite = ucreat(szReal, mode)) < 0 &&
|
|
(wRetErr = WRetryErr(0, "creating temp for", pmf, 0)) != 0);
|
|
|
|
if (pmf->fdWrite < 0)
|
|
{
|
|
*pch = '\0';
|
|
FatalError("could not create unique file in %s\n", szReal);
|
|
}
|
|
|
|
NmCopySz(pmf->nmTemp, pch+1, cchFileMax);
|
|
|
|
pmf->mm = mmDelTemp;
|
|
pmf->pos = 0L;
|
|
|
|
return pmf;
|
|
}
|
|
|
|
|
|
/* create temp file from mf with mode passed; abort on errors;
|
|
fdWrite is set; if there is a temp name, mm is set to mmDelTemp.
|
|
*/
|
|
void
|
|
CreateMf(
|
|
MF *pmf,
|
|
int mode)
|
|
{
|
|
char szTemp[cchPthMax];
|
|
int wRetErr;
|
|
|
|
AssertF(FIsClosedMf(pmf));
|
|
|
|
AssertF(pmf->mm == mmNil);
|
|
if (!FEmptyNm(pmf->nmTemp))
|
|
/* some temp; set to delete */
|
|
pmf->mm = mmDelTemp;
|
|
|
|
PthForTMf(pmf, (PTH *)szTemp);
|
|
|
|
UnlinkNow((PTH *)szTemp, fFalse);/* remove any residue; ignore error */
|
|
|
|
SzPhysPath(szTemp, (PTH *)szTemp);
|
|
|
|
while ((pmf->fdWrite = _creat(szTemp, mode)) < 0 &&
|
|
(wRetErr = WRetryErr(0, "creating", (MF *)0, szTemp)) != 0);
|
|
|
|
if (pmf->fdWrite < 0)
|
|
FatalError("cannot create temp file %s (%s)\n", szTemp, SzForEn(enCur));
|
|
|
|
pmf->pos = 0L;
|
|
}
|
|
|
|
/* opens the file and returns a pointer to a new mf; we abort if the file
|
|
cannot be opened and the abort bit of the om is set. We return an MF with
|
|
the following info set:
|
|
|
|
omAppend: szReal-set, pthTemp-set, fdRead = fdNil,fdWrite-set, mm = mmAppToReal.
|
|
omReadOnly: szReal-set, pthTemp=0, fdRead-set, fdWrite = fdNil, mm = mmNil.
|
|
omReadWrite:szReal-set, pthTemp=0, fdRead-set, fdWrite-set, mm = mmNil,
|
|
|
|
Must be freed via FreeMf or CloseMf. Retains pointer to pth!
|
|
|
|
if omReadWrite, FLockMf and UnlockMf are available.
|
|
*/
|
|
MF *
|
|
PmfOpen(
|
|
PTH *pth,
|
|
OM om,
|
|
FX fx)
|
|
{
|
|
int fAbort;
|
|
register MF *pmf;
|
|
char sz[cchPthMax];
|
|
int wRetErr;
|
|
F fNetPth;
|
|
|
|
fAbort = (om&ooAbort) != 0;
|
|
om &= omMask;
|
|
|
|
switch(om)
|
|
{
|
|
default: AssertF(fFalse);
|
|
|
|
case omAppend:
|
|
/* create temp file for writing */
|
|
AssertF(fAbort);
|
|
pmf = PmfMkTemp(pth, permSysFiles, fx); /* assume mode for log file */
|
|
pmf->mm = mmAppToReal;
|
|
break;
|
|
|
|
case omReadOnly:
|
|
case omReadWrite:
|
|
/* open existing file; no temp name */
|
|
pmf = PmfAlloc(pth, (char *)0, fx);
|
|
SzPhysPath(sz, pth);
|
|
|
|
while ((pmf->fdRead = _open(sz, om)) < 0 && (wRetErr = WRetryErr(0, "opening", 0, sz)) != 0)
|
|
;
|
|
|
|
if (pmf->fdRead < 0)
|
|
{
|
|
FreeMf(pmf);
|
|
|
|
if (fAbort)
|
|
FatalError("cannot open %s (%s)\n", sz, SzForEn(enCur));
|
|
|
|
return (MF *)0;
|
|
}
|
|
|
|
if (om == omReadWrite)
|
|
pmf->fdWrite = pmf->fdRead;
|
|
|
|
pmf->pos = 0L;
|
|
break;
|
|
}
|
|
|
|
return pmf;
|
|
}
|
|
|
|
|
|
/* Same as PmfOpen, except opens the file with FILE_FLAG_NO_BUFFERING.
|
|
*/
|
|
MF *
|
|
PmfOpenNoBuffering(
|
|
PTH *pth,
|
|
OM om,
|
|
FX fx)
|
|
{
|
|
int fAbort;
|
|
register MF *pmf;
|
|
char sz[cchPthMax];
|
|
int wRetErr;
|
|
F fNetPth;
|
|
HANDLE hf;
|
|
DWORD fileaccess;
|
|
int fd;
|
|
|
|
fAbort = (om&ooAbort) != 0;
|
|
om &= omMask;
|
|
|
|
switch(om)
|
|
{
|
|
default: AssertF(fFalse);
|
|
|
|
case omAppend:
|
|
/* create temp file for writing */
|
|
AssertF(fAbort);
|
|
pmf = PmfMkTemp(pth, permSysFiles, fx); /* assume mode for log file */
|
|
pmf->mm = mmAppToReal;
|
|
break;
|
|
|
|
case omReadOnly:
|
|
case omReadWrite:
|
|
/* open existing file; no temp name */
|
|
pmf = PmfAlloc(pth, (char *)0, fx);
|
|
fNetPth = FNetPth(pth) ? pth[3] != ':' : fFalse;
|
|
SzPhysPath(sz, pth);
|
|
|
|
while (TRUE) {
|
|
if (!fNetPth)
|
|
fd = _open(sz, om);
|
|
else {
|
|
fd = -1; // Assume failure
|
|
|
|
/*
|
|
* decode the access flags
|
|
*/
|
|
switch( om & (_O_RDONLY | _O_WRONLY | _O_RDWR) ) {
|
|
|
|
case _O_WRONLY: /* write access */
|
|
fileaccess = GENERIC_WRITE;
|
|
break;
|
|
|
|
case _O_RDWR: /* read and write access */
|
|
fileaccess = GENERIC_READ | GENERIC_WRITE;
|
|
break;
|
|
|
|
case _O_RDONLY: /* read access */
|
|
default: /* error, bad oflag */
|
|
fileaccess = GENERIC_READ;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* try to open/create the file
|
|
*/
|
|
hf = CreateFile(sz,
|
|
fileaccess,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_NO_BUFFERING | FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
if (hf == INVALID_HANDLE_VALUE)
|
|
_doserrno = GetLastError();
|
|
else {
|
|
fd = _open_osfhandle((long)hf, 0);
|
|
if (fd >= 0)
|
|
pmf->fNoBuffering = fTrue;
|
|
}
|
|
}
|
|
|
|
if (fd < 0) {
|
|
if ((wRetErr = WRetryErr(0, "opening", 0, sz)) == 0)
|
|
break;
|
|
} else {
|
|
pmf->fdRead = fd;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pmf->fdRead < 0)
|
|
{
|
|
FreeMf(pmf);
|
|
|
|
if (fAbort)
|
|
FatalError("cannot open %s (%s)\n", sz, SzForEn(enCur));
|
|
|
|
return (MF *)0;
|
|
}
|
|
|
|
if (om == omReadWrite)
|
|
pmf->fdWrite = pmf->fdRead;
|
|
|
|
pmf->pos = 0L;
|
|
break;
|
|
}
|
|
|
|
return pmf;
|
|
}
|
|
|
|
|
|
/* Called from higher level routines such as CloseLog, this routine ensures
|
|
* that it is safe to append the temp file to the real file at RunScript time.
|
|
*
|
|
* It does this by padding the real file with as many spaces as there are
|
|
* characters in the temp file, forcing a write error if the append action
|
|
* would have run out of disk space.
|
|
*
|
|
* We read the tail end of the real file and count how many spaces are already
|
|
* there (and don't append those). This feature is necessary to recover space
|
|
* wasted if a previous CheckAppendMf wrote spaces onto the end of the log
|
|
* and was then aborted.
|
|
*/
|
|
void
|
|
CheckAppendMf(
|
|
MF *pmfAppend,
|
|
F fPrVerbose)
|
|
{
|
|
struct _stat st;
|
|
MF *pmf;
|
|
long cbPad;
|
|
int cb;
|
|
PTH pthTemp[cchPthMax];
|
|
char rgb[512];
|
|
|
|
AssertF(FIsClosedMf(pmfAppend));
|
|
AssertF(pmfAppend->pthReal);
|
|
AssertF(pmfAppend->nmTemp[0]);
|
|
|
|
PthForTMf(pmfAppend, pthTemp);
|
|
|
|
if (fVerbose && fPrVerbose)
|
|
/* print message as if we were appending now */
|
|
PrErr("Append %!s to %!s\n", pthTemp, pmfAppend->pthReal);
|
|
|
|
/* Determine number of bytes to pad real file with. */
|
|
StatPth(pthTemp, &st);
|
|
cbPad = st.st_size;
|
|
|
|
if (cbPad == 0)
|
|
return;
|
|
|
|
/* Open real file */
|
|
pmf = PmfOpen(pmfAppend->pthReal, omAReadWrite, fxNil);
|
|
|
|
/* Count trailing spaces */
|
|
cbPad -= LcbSpacesMf(pmf);
|
|
|
|
/* (If we determine the file already has enough padding, we may not
|
|
* have to do anything.)
|
|
*/
|
|
if (cbPad > 0)
|
|
{
|
|
int ib;
|
|
|
|
for (ib = 0; ib < sizeof rgb; ib++)
|
|
rgb[ib] = ' ';
|
|
|
|
/* Pad the file, a block at a time. */
|
|
SeekMf(pmf, (POS)0, 2);
|
|
while (cbPad > 0)
|
|
{
|
|
cb = WMinLL(sizeof rgb, cbPad);
|
|
WriteMf(pmf, (char far *)rgb, cb);
|
|
cbPad -= cb;
|
|
}
|
|
}
|
|
|
|
CloseMf(pmf);
|
|
}
|
|
|
|
|
|
/* Return the number of spaces at the end of the file.
|
|
*
|
|
* Read the file backwards, a block at a time, until we find a non-space or run
|
|
* out of file.
|
|
*/
|
|
long
|
|
LcbSpacesMf(
|
|
MF *pmf)
|
|
{
|
|
POS pos;
|
|
int ib;
|
|
int cb;
|
|
long lcbSpaces = 0;
|
|
char rgb[512];
|
|
|
|
AssertF(FIsOpenMf(pmf));
|
|
|
|
for (pos = SeekMf(pmf, 0L, 2); pos > 0; )
|
|
{
|
|
cb = WMinLL(sizeof rgb, pos);
|
|
pos -= cb;
|
|
SeekMf(pmf, pos, 0);
|
|
ReadMf(pmf, (char far *)rgb, cb);
|
|
|
|
for (ib = cb - 1; ib >= 0 && rgb[ib] == ' '; ib--)
|
|
lcbSpaces++;
|
|
if (ib >= 0)
|
|
break;
|
|
}
|
|
|
|
return lcbSpaces;
|
|
}
|
|
|
|
|
|
/* Creates a temporary file in the user's TMP directory.
|
|
Return 0 if no TMP variable. pthLocal is set with the full name of the
|
|
temporary. Mm is set to mmDelTemp.
|
|
|
|
om is effectively omReadWrite with a lock.
|
|
|
|
MF is set as follows:
|
|
|
|
szReal-set, pthTemp-set, fdRead = fdNil, fdWrite-set, mm = mmDelTemp
|
|
|
|
fx == fxLocal
|
|
*/
|
|
MF *
|
|
PmfMkLocalTemp(
|
|
int mode,
|
|
PTH pthLocal[])
|
|
{
|
|
register MF *pmf;
|
|
char *szLocalDir;
|
|
char *pch;
|
|
int cchLocalDir;
|
|
PTH pthT[cchPthMax];
|
|
int cchT;
|
|
int wRetErr;
|
|
|
|
if ((szLocalDir = getenv("TMP")) == 0)
|
|
return (MF *) 0;
|
|
|
|
PthCopySz(pthT, szLocalDir);
|
|
|
|
/* Append \ if TMP var doesn't already end with one */
|
|
if (pthT[(cchT = strlen(pthT))-1] != '\\')
|
|
{
|
|
pthT[cchT] = '\\';
|
|
pthT[cchT+1] = '\0';
|
|
}
|
|
|
|
if (!FPthLogicalSz(pthLocal, pthT))
|
|
{
|
|
Error("TMP path variable %s is not well formed; should be a valid path\n", pthT);
|
|
return (MF *) 0;
|
|
}
|
|
|
|
pmf = PmfAlloc(pthLocal, (char *)0, fxLocal); /* we install unique name below */
|
|
|
|
SzPhysPath((char *)pthLocal, pthLocal);
|
|
|
|
cchLocalDir = CchOfPth(pthLocal);
|
|
pch = (char *) &pthLocal[cchLocalDir-1];
|
|
|
|
if (*pch != '/') /* append / if last character isn't one */
|
|
*++pch = '/';
|
|
*(pch+1) = '\0'; /* name will be put here */
|
|
|
|
while ((pmf->fdWrite = ucreat((char *)pthLocal, mode)) < 0 && (wRetErr = WRetryErr(0, "creating temp file in", pmf, 0)) != 0)
|
|
;
|
|
|
|
if (pmf->fdWrite < 0)
|
|
{
|
|
*pch = '\0';
|
|
FatalError("could not create unique file in %s\n", pthT);
|
|
}
|
|
|
|
NmCopySz(pmf->nmTemp, pch+1, cchFileMax);
|
|
|
|
/* we make the real name the same as the temp so FIsValidMf will work */
|
|
FPthLogicalSz(pthLocal, (char *)pthLocal);
|
|
pmf->pthReal = pthLocal;
|
|
|
|
pmf->pos = 0L;
|
|
pmf->mm = mmDelTemp;
|
|
|
|
return pmf;
|
|
}
|
|
|
|
|
|
/* creates the file with mode passed; we create a temp file and write to that.
|
|
For a successful exit, that file is renamed to the original (after deleting
|
|
it). The owner and group will be the same as the owner of this installation
|
|
of SLM; returns the pmf of the created file.
|
|
|
|
om is effectively omReadWrite with a lock.
|
|
|
|
MF is set as follows:
|
|
|
|
szReal-set, pthTemp-set, fdRead-set, fdWrite-set, mm = mmRenTemp.
|
|
|
|
Never return 0.
|
|
*/
|
|
MF *
|
|
PmfCreate(
|
|
PTH pth[],
|
|
int mode,
|
|
int fPrVerbose,
|
|
FX fx)
|
|
{
|
|
register MF *pmf;
|
|
|
|
if (fVerbose && fPrVerbose)
|
|
{
|
|
/* print as if we created the original file */
|
|
PrErr("Create %!s%s\n", pth, SzForMode(mode));
|
|
}
|
|
|
|
pmf = PmfMkTemp(pth, mode, fx);
|
|
|
|
/* Allow reading so that data written to a status or
|
|
* temporary file can be read back in and checked to
|
|
* ensure that it was written correctly.
|
|
*
|
|
* Note: pmf->fdWrite is guaranteed to be valid by PmfMkTemp.
|
|
*/
|
|
pmf->fdRead = pmf->fdWrite;
|
|
|
|
/* Check the user write permission in the mode and set the mm based
|
|
* on it. We do this because the temp file must be writeable for us
|
|
* to open it unbuffered.
|
|
*/
|
|
pmf->mm = ((mode & 0222) != 0) ? mmRenTemp : mmRenTempRO;
|
|
|
|
return pmf;
|
|
}
|
|
|
|
|
|
/* Reopen an existing (temp) file. The MF concept needs a little REVIEWing. */
|
|
MF *
|
|
PmfReopen(
|
|
PTH * pth,
|
|
char * szTemp,
|
|
OM om,
|
|
FX fx)
|
|
{
|
|
register MF *pmf;
|
|
char sz[cchPthMax];
|
|
int wRetErr;
|
|
|
|
AssertF(om == omAReadWrite);
|
|
om &= omMask;
|
|
|
|
pmf = PmfAlloc(pth, szTemp, fx);
|
|
|
|
SzPhysTMf(sz, pmf);
|
|
while ((pmf->fdWrite = pmf->fdRead = _open(sz, om)) < 0 &&
|
|
(wRetErr = WRetryErr(0, "opening", 0, sz)) != 0)
|
|
;
|
|
|
|
if (pmf->fdRead < 0)
|
|
{
|
|
FatalError("can't reopen %s (%s)\n", sz, SzForEn(enCur));
|
|
return (MF *)0;
|
|
}
|
|
|
|
pmf->pos = 0L;
|
|
|
|
return pmf;
|
|
}
|
|
|
|
#define WLock(fd) lockfile((fd), fFalse)
|
|
#define WUnLock(fd) lockfile((fd), fTrue)
|
|
|
|
/* lock the whole file; must be read/write; return fTrue if successful. */
|
|
F
|
|
FLockMf(
|
|
MF *pmf)
|
|
{
|
|
int w;
|
|
int wRetErr;
|
|
|
|
AssertF(FIsOpenMf(pmf));
|
|
AssertF(!pmf->fFileLock);
|
|
|
|
pmf->fFileLock = fTrue; /* set now in case we abort */
|
|
|
|
while ((w = WLock(pmf->fdRead)) != 0 &&
|
|
(wRetErr = WRetryErr(0, "locking", pmf, 0)) != 0)
|
|
;
|
|
|
|
return pmf->fFileLock = (w == 0); /* set for real */
|
|
}
|
|
|
|
|
|
/* unlock file so others can access it. */
|
|
void
|
|
UnlockMf(
|
|
MF *pmf)
|
|
{
|
|
int wRetErr;
|
|
|
|
AssertF(FIsOpenMf(pmf));
|
|
AssertF(pmf->fFileLock);
|
|
|
|
while (WUnLock(pmf->fdWrite) != 0 &&
|
|
(wRetErr = WRetryErr(0, "releasing", pmf, 0)) != 0)
|
|
;
|
|
|
|
pmf->fFileLock = fFalse;
|
|
}
|
|
|
|
|
|
/* close the file; Xenix automatically releases all locks; called when we
|
|
abort; must call FreeMf later.
|
|
*/
|
|
void
|
|
CloseOnly(
|
|
MF *pmf)
|
|
{
|
|
int w;
|
|
register int fd;
|
|
int wRetErr;
|
|
|
|
AssertF(FIsValidMf(pmf));
|
|
|
|
/* test < 0 because we may abort before we can change some erroneous
|
|
fd to fdNil.
|
|
*/
|
|
if ((fd = pmf->fdRead) < 0)
|
|
fd = pmf->fdWrite;
|
|
|
|
if (fd < 0)
|
|
/* both closed already (or previous errors) */
|
|
{
|
|
/* clear info for FreeMf() */
|
|
pmf->fFileLock = fFalse;
|
|
pmf->fdRead = pmf->fdWrite = fdNil;
|
|
return;
|
|
}
|
|
|
|
if (pmf->fFileLock)
|
|
lockfile(fd, fTrue);
|
|
|
|
pmf->fFileLock = fFalse;
|
|
pmf->fdRead = pmf->fdWrite = fdNil;
|
|
pmf->pos = -1L;
|
|
|
|
while ((w = _close(fd)) != 0 && (wRetErr = WRetryErr(0, "closing", pmf, 0)) != 0)
|
|
;
|
|
|
|
if (w != 0)
|
|
Error("error closing %!@T (%s)\n", pmf, SzForEn(enCur));
|
|
}
|
|
|
|
|
|
/* close the file and free mf; can be called when we abort */
|
|
void
|
|
CloseMf(
|
|
MF *pmf)
|
|
{
|
|
CloseOnly(pmf);
|
|
|
|
FreeMf(pmf);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Name: ReadMf
|
|
* Purpose: Read exactly cb bytes from the file
|
|
* Assumes:
|
|
* Returns: nothing; aborts if we can't get the right amount of data
|
|
*/
|
|
void
|
|
ReadMf(
|
|
MF *pmf,
|
|
char *pb,
|
|
unsigned cb)
|
|
{
|
|
unsigned cbT;
|
|
|
|
AssertF( FIsOpenMf(pmf) );
|
|
|
|
while (cb)
|
|
{
|
|
cbT = CbReadMf(pmf, pb, cb);
|
|
AssertF( cbT != (unsigned)-1 );
|
|
if (0 == cbT)
|
|
FatalError("error reading %!@T (unexpected end of file)\n", pmf);
|
|
cb -= cbT;
|
|
pb += cbT;
|
|
}
|
|
}
|
|
|
|
extern DWORD dwPageSize;
|
|
|
|
/* read up to cb bytes and return amount read */
|
|
unsigned
|
|
CbReadMf(
|
|
MF * pmf,
|
|
char far *lpb,
|
|
unsigned cb)
|
|
{
|
|
register unsigned cbT;
|
|
int wRetErr;
|
|
|
|
AssertF(FIsOpenMf(pmf));
|
|
AssertF(pmf->fdRead >= 0);
|
|
AssertF(cb != (unsigned)-1);
|
|
AssertF(!pmf->fNoBuffering || ((DWORD)lpb & (dwPageSize-1)) == 0);
|
|
|
|
while ((cbT = _read(pmf->fdRead, lpb, cb)) == (unsigned)-1 &&
|
|
(wRetErr = WRetryErr(0, "reading", pmf, 0)) != 0)
|
|
/* we DON'T assume that the file position has not changed!
|
|
* The system call may have been interrupted; if so, reset the
|
|
* file pointer.
|
|
*/
|
|
{
|
|
if (wRetErr == enInterruptSysCall)
|
|
AssertF(_lseek(pmf->fdRead, pmf->pos, 0) == pmf->pos);
|
|
}
|
|
|
|
if (cbT == (unsigned)-1)
|
|
FatalError("error reading %!@T (%s)\n", pmf, SzForEn(enCur));
|
|
|
|
pmf->pos += cbT;
|
|
|
|
return cbT;
|
|
}
|
|
|
|
#define chCtrlZ 0x1a
|
|
|
|
/* write to the file, return fTrue if all the whole buffer was written. */
|
|
F
|
|
FWriteMf(
|
|
MF * pmf,
|
|
char far *lpb,
|
|
unsigned cb)
|
|
{
|
|
register unsigned cbT;
|
|
|
|
CheckForBreak();
|
|
|
|
AssertF(FIsOpenMf(pmf));
|
|
AssertF(pmf->fdWrite >= 0);
|
|
AssertF(cb != (unsigned)-1);
|
|
AssertF(!pmf->fNoBuffering || ((DWORD)lpb & (dwPageSize-1)) == 0);
|
|
|
|
if (pmf == &mfStdout && lpb[cb-1] == chCtrlZ)
|
|
cb--;
|
|
|
|
while (cb)
|
|
{
|
|
cbT = WriteLpbCb(pmf->fdWrite, lpb, cb);
|
|
|
|
// write shouldn't ever return 0, but just in case...
|
|
if (-1 == cbT || 0 == cbT)
|
|
{
|
|
if (WRetryError(eoWrite, "writing", pmf, 0) != 0)
|
|
continue;
|
|
else
|
|
return fFalse;
|
|
}
|
|
ClearPreviousError();
|
|
cb -= cbT;
|
|
lpb += cbT;
|
|
pmf->pos += cbT;
|
|
pmf->fWritten = fTrue;
|
|
}
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
/* write to the file, check all the data is successfully written */
|
|
void
|
|
WriteMf(
|
|
MF * pmf,
|
|
char far *lpb,
|
|
unsigned cb)
|
|
{
|
|
if (!FWriteMf(pmf, lpb, cb))
|
|
FatalError("incomplete write to %!@T (%s)\n",
|
|
pmf, SzForEn(enCur));
|
|
}
|
|
|
|
/* seek within the file according to the Xenix lseek standard. It is an
|
|
error if lseek returns -1 or, for mode 0, we do not get to where we
|
|
though we were going.
|
|
*/
|
|
POS
|
|
SeekMf(
|
|
MF *pmf,
|
|
POS posSet,
|
|
int mode) /* 0 - BOF, 1 - current pos, 2 - EOF */
|
|
{
|
|
register int fd;
|
|
POS posRet;
|
|
int wRetErr;
|
|
|
|
AssertF(FIsOpenMf(pmf));
|
|
|
|
if (pmf == &mfStdout)
|
|
return posSet;
|
|
|
|
if ((fd = pmf->fdRead) < 0)
|
|
fd = pmf->fdWrite;
|
|
|
|
AssertF(fd >= 0);
|
|
|
|
/* should we allow seeking on a writeonly file ????? */
|
|
|
|
if (mode == 0 && pmf->pos == posSet)
|
|
/* simple optimization for seeking to the current location;
|
|
retain mode 0 so we can test for success below.
|
|
*/
|
|
return posSet;
|
|
|
|
while ((posRet = _lseek(fd, (long)posSet, (int)mode)) == -1L && (wRetErr = WRetryErr(0, "moving within", pmf, 0)) != 0)
|
|
;
|
|
|
|
if (mode == 0 && posRet != posSet)
|
|
{
|
|
/* we did not get to the correct place; simulate error */
|
|
posRet = -1L;
|
|
enCur = 5; /* access denied */
|
|
}
|
|
|
|
if (posRet == -1L)
|
|
FatalError("error moving within %!@T (%s)\n", pmf, SzForEn(enCur));
|
|
|
|
pmf->pos = posRet;
|
|
|
|
return posRet;
|
|
}
|
|
|
|
|
|
/* Return the current offset in the file. */
|
|
POS
|
|
PosCurMf(
|
|
MF *pmf)
|
|
{
|
|
return SeekMf(pmf, (POS)0, 1);
|
|
}
|
|
|
|
|
|
/* set up a delayed unlink for pth */
|
|
void
|
|
UnlinkPth(
|
|
PTH *pth,
|
|
FX fx)
|
|
{
|
|
register MF *pmf;
|
|
|
|
if (fVerbose)
|
|
PrErr("Delete %!s\n", pth);
|
|
|
|
pmf = PmfAlloc(pth, (char *)0, fx);
|
|
pmf->mm = mmDelReal;
|
|
FreeMf(pmf);
|
|
}
|
|
|
|
/* set up a delayed rename from pthOld to pthNew */
|
|
void
|
|
RenamePth(
|
|
NM nmOld[],
|
|
PTH pthNew[],
|
|
FX fx)
|
|
{
|
|
register MF *pmf;
|
|
|
|
pmf = PmfAlloc(pthNew, nmOld, fx);
|
|
pmf->mm = mmRenReal;
|
|
|
|
if (fVerbose)
|
|
PrErr("Rename %@T %@R\n", pmf, pmf);
|
|
|
|
FreeMf(pmf);
|
|
}
|
|
|
|
/* do unlink now! */
|
|
void
|
|
UnlinkNow(
|
|
PTH pth[],
|
|
int fPrVerbose)
|
|
{
|
|
char sz[cchPthMax];
|
|
int wRetErr;
|
|
|
|
SzPhysPath(sz, pth);
|
|
|
|
if (fVerbose && fPrVerbose)
|
|
PrErr("Delete %s\n", sz);
|
|
|
|
while (SLM_Unlink(sz) != 0 && (wRetErr = WRetryErr(0, "removing", 0, sz)) != 0)
|
|
;
|
|
|
|
/* ignore unlink errors */
|
|
}
|
|
|
|
|
|
/* for this mmRenTemp or mmRenReal file, we do the rename now instead of waiting until
|
|
the program exits. File must be closed.
|
|
*/
|
|
void
|
|
RenameMf(
|
|
MF * pmf,
|
|
int fPrVerbose)
|
|
{
|
|
register int w;
|
|
char szTo[cchPthMax];
|
|
char szFrom[cchPthMax];
|
|
int wRetErr;
|
|
|
|
AssertF(FIsClosedMf(pmf));
|
|
AssertF((pmf->mm == mmRenTemp || pmf->mm == mmRenReal || pmf->mm == mmRenTempRO));
|
|
|
|
UnlinkNow(pmf->pthReal, fFalse);
|
|
|
|
SzPhysPath(szTo, pmf->pthReal);
|
|
SzPhysTMf(szFrom, pmf);
|
|
|
|
if (fVerbose && fPrVerbose)
|
|
PrErr("Rename %s to %s\n", szFrom, szTo);
|
|
|
|
while ((w = SLM_Rename(szFrom, szTo)) != 0 && (wRetErr = WRetryErr(0, "renaming", 0, szFrom)) != 0)
|
|
{
|
|
/* check to see if the system call DosMove was interrupted, and
|
|
* if it was, make sure the state of the files are valid before
|
|
* retrying!.
|
|
*/
|
|
if (wRetErr == enInterruptSysCall)
|
|
{
|
|
/* if szTo now exists, and szFrom is gone, then assume
|
|
* successful!
|
|
*/
|
|
if (!FExistSz(szFrom) && FExistSz(szTo))
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (w != 0)
|
|
FatalError("cannot rename %s to %s\n", szFrom, szTo);
|
|
|
|
if (pmf->mm == mmRenTempRO)
|
|
setro(szTo, fTrue);
|
|
|
|
pmf->mm = mmNil; /* no action required for normal exit */
|
|
}
|
|
|
|
F
|
|
FStatPth(
|
|
PTH pth[],
|
|
struct _stat *pst)
|
|
{
|
|
int w;
|
|
int wRetErr;
|
|
char sz[cchPthMax];
|
|
|
|
SzPhysPath(sz, pth);
|
|
|
|
while ((w = _stat(sz, pst)) != 0 &&
|
|
(wRetErr = WRetryErr(0, "accessing", 0, sz)) != 0)
|
|
;
|
|
|
|
return w == 0;
|
|
}
|
|
|
|
|
|
void
|
|
StatPth(
|
|
PTH pth[],
|
|
struct _stat *pst)
|
|
{
|
|
if (!FStatPth(pth, pst))
|
|
FatalError("cannot access %!s\n", pth);
|
|
}
|
|
|
|
|
|
|
|
F
|
|
FExistSz(char *sz)
|
|
{
|
|
struct _stat st;
|
|
|
|
if (_stat(sz, &st) != 0)
|
|
return (fFalse);
|
|
return (fTrue);
|
|
}
|
|
|
|
/* returns fTrue if sz exists according to fDir. */
|
|
F
|
|
FPthExists(
|
|
PTH *pth,
|
|
int fDir)
|
|
{
|
|
register int w;
|
|
int wRetErr;
|
|
char sz[cchPthMax];
|
|
|
|
DE de;
|
|
|
|
SzPhysPath(sz, pth);
|
|
|
|
while ((w = findfirst(&de, sz, faAll&~faVolume)) != 0 && (wRetErr = WRetryErr(0, "accessing", 0, sz)) != 0)
|
|
CloseDir(&de);
|
|
|
|
CloseDir(&de);
|
|
return (w == 0 && (fDir != 0) == ((FaFromPde(&de)&faDir) != 0));
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Name: CbFile
|
|
* Purpose: determine size of file
|
|
* Assumes:
|
|
* Returns: size of file, or 0 if any error occured
|
|
* Warning: there was a LanMan 2.1 bug that would occasionally reboot the
|
|
* machine if you opened a file that was already open. So maybe
|
|
* you won't want to use this if you have the file open (use lseek).
|
|
*/
|
|
unsigned long
|
|
CbFile(
|
|
char *szFile)
|
|
{
|
|
char szPhys[cchPthMax];
|
|
int hf;
|
|
unsigned long cb;
|
|
|
|
hf = _open(SzPhysPath(szPhys, szFile), omReadOnly);
|
|
if (-1 == hf)
|
|
return (0);
|
|
|
|
cb = _lseek(hf, 0, SEEK_END);
|
|
_close(hf);
|
|
|
|
return (-1L == cb ? 0L : cb);
|
|
}
|
|
|
|
|
|
int chngtime(char *, char *);
|
|
|
|
/* change the time of the temp file in pmf to be the same as the time of
|
|
pmfTime->pthTemp; for Xenix, we MUST own pmfChng->pthTemp and be able to
|
|
stat pmfTime->pthTemp.
|
|
|
|
Both files must be closed because on DOS the directory entry is written
|
|
when the file is closed, so it is possible that this change we are making
|
|
will be overwritten by a later call to close.
|
|
*/
|
|
void
|
|
UtimeMf(
|
|
MF *pmfChng,
|
|
MF *pmfTime)
|
|
{
|
|
int w;
|
|
int wRetErr;
|
|
char szChng[cchPthMax];
|
|
|
|
char szTime[cchPthMax];
|
|
|
|
AssertF(FIsClosedMf(pmfChng));
|
|
AssertF(FIsClosedMf(pmfTime));
|
|
|
|
SzPhysTMf(szChng, pmfChng);
|
|
SzPhysTMf(szTime, pmfTime);
|
|
|
|
while ((w = chngtime(szChng, szTime)) != 0 && (wRetErr = WRetryErr(0, "updating time of", 0, szChng)) != 0)
|
|
;
|
|
|
|
if (w != 0)
|
|
FatalError("cannot change the time of %s (%s)\n", szChng, SzForEn(enCur));
|
|
}
|
|
|
|
|
|
/* set up for a delayed change to read/only */
|
|
void
|
|
SetROPth(
|
|
PTH *pth,
|
|
F fReadOnly, /* if fFalse, we set to read/write */
|
|
FX fx)
|
|
{
|
|
register MF *pmf = PmfAlloc(pth, (char *)0, fx);
|
|
|
|
if (fVerbose)
|
|
PrErr("Change %!s to be %s\n", pth, fReadOnly ? "readonly" : "writeable");
|
|
|
|
pmf->mm = fReadOnly ? mmSetRO : mmSetRW;
|
|
FreeMf(pmf);
|
|
}
|
|
|
|
|
|
private F FEnsurSz(char *sz, char *pchMac);
|
|
|
|
/* Ensure pth exists by creating each component of it. Return fTrue if the
|
|
* path exists and is a directory.
|
|
*/
|
|
F
|
|
FEnsurePth(
|
|
PTH *pth)
|
|
{
|
|
char *pchMac; /* current end of component */
|
|
char sz[cchPthMax];
|
|
|
|
/* Ensure each component of the path is a directory. It is easiest
|
|
* to first convert the path to a physical path, then step through
|
|
* each subdirectory of the physical path. For instance, for
|
|
* pth=//server/short/a/b/c, we get sz=x:/a/b/c and check x:/a,
|
|
* x:/a/b, and x:/a/b/c.
|
|
*/
|
|
SzPhysPath(sz, pth);
|
|
|
|
/* Check first component (x:/, special case) */
|
|
pchMac = index(sz, '/');
|
|
AssertF(pchMac != 0);
|
|
if (!FEnsurSz(sz, pchMac + 1))
|
|
return fFalse;
|
|
|
|
/* Repeat for each successive component, until the entire physical
|
|
* path has been checked. The last component is another special case.
|
|
*/
|
|
do
|
|
{
|
|
pchMac = index(pchMac + 1, '/');
|
|
if (pchMac == 0)
|
|
pchMac = index(sz, '\0');
|
|
AssertF(pchMac != 0);
|
|
if (!FEnsurSz(sz, pchMac))
|
|
return fFalse;
|
|
}
|
|
while (*pchMac == '/');
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* Ensure that this physical directory exists, create it if it doesn't, return
|
|
* fTrue if directory now exists.
|
|
*/
|
|
private F
|
|
FEnsurSz(
|
|
char *sz,
|
|
char *pchMac)
|
|
{
|
|
PTH pth[cchPthMax];
|
|
struct _stat st;
|
|
char chSav;
|
|
|
|
AssertF(sz != 0);
|
|
AssertF(pchMac != 0);
|
|
AssertF(pchMac > sz);
|
|
|
|
chSav = *pchMac;
|
|
*pchMac = '\0'; /* temporarily null-terminate the sz */
|
|
|
|
/* Convert back to a path, required for FStatPth and FMkPth. */
|
|
if (!FPthLogicalSz(pth, sz))
|
|
AssertF(fFalse);
|
|
|
|
if (!FStatPth(pth, &st))
|
|
{
|
|
/* Pth doesn't exist. Create it. */
|
|
if (!FMkPth(pth, (void *)0, fTrue))
|
|
return fFalse;
|
|
}
|
|
else if ((st.st_mode&S_IFDIR) != S_IFDIR)
|
|
{
|
|
Error("%s is not a directory\n", pth);
|
|
return fFalse;
|
|
}
|
|
|
|
*pchMac = chSav; /* restore the overwritten character */
|
|
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* make directory or ensure that we can write to an existing one; this routine
|
|
never returns on an error when fErrOk is false.
|
|
|
|
On Xenix, if ppwd is not 0, change ownership of directory to ppwd->pw_uid.
|
|
*/
|
|
F
|
|
FMkPth(
|
|
PTH *pth,
|
|
int *ppwd,
|
|
int fErrOk)
|
|
{
|
|
char sz[cchPthMax];
|
|
struct _stat st;
|
|
int w;
|
|
int wRetErr;
|
|
|
|
AssertF(ppwd == 0);
|
|
|
|
SzPhysPath(sz, pth); /* convert now for use in error messages */
|
|
|
|
if (FStatPth(pth, &st))
|
|
{
|
|
/* name alread exists; if file, ask to delete; if dir, try
|
|
to change if needed.
|
|
*/
|
|
if ((st.st_mode&S_IFREG) != 0)
|
|
{
|
|
if (!FQueryApp("regular file %s should be replaced with directory", "replace now", sz))
|
|
goto MkPthErr;
|
|
|
|
UnlinkNow(pth, fTrue);
|
|
|
|
/* continue to make directory */
|
|
}
|
|
else
|
|
/* must be dir */
|
|
return fTrue;
|
|
}
|
|
|
|
if (fVerbose)
|
|
PrErr("Mkdir %s\n", sz);
|
|
|
|
while ((w = _mkdir(sz)) != 0 && (wRetErr = WRetryErr(0, "making directory", 0, sz)) != 0)
|
|
;
|
|
|
|
if (w != 0)
|
|
{
|
|
/* mkdir prints the error on Xenix. */
|
|
Error("cannot make directory %s\n", sz);
|
|
goto MkPthErr;
|
|
}
|
|
|
|
if (w == 0)
|
|
return fTrue;
|
|
|
|
MkPthErr:/* error; message already printed */
|
|
|
|
if (!fErrOk)
|
|
ExitSlm();
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/* removes all of the files in the directory and then removes the dir.
|
|
There should be no sub directories by now. The user should have write
|
|
permssion to the directory and to the parent of the directory.
|
|
|
|
NOTE: we assume that we can append a filename onto pthDir temporarily.
|
|
This is only possible in the DOS version because OpenDir does not retain
|
|
pthDir.
|
|
*/
|
|
void
|
|
RmPth(
|
|
PTH *pthDir)
|
|
{
|
|
FA fa;
|
|
DE de;
|
|
char pthSub[cchPthMax]; /* dual use! */
|
|
char *sz = index(pthDir, '\0'); /* point into pthDir */
|
|
int wRetErr, w;
|
|
|
|
/* delete all files in the directory */
|
|
|
|
OpenDir(&de, pthDir, faAll&~faVolume); /* open directory given */
|
|
|
|
*sz++ = '/'; /* add path separator */
|
|
while(FGetDirSz(&de, sz, &fa))
|
|
{
|
|
if (fa == faDir)
|
|
{
|
|
PthCopy(pthSub, pthDir);/* includes sub dir name */
|
|
RmPth(pthSub); /* recursive call! */
|
|
}
|
|
else
|
|
UnlinkNow(pthDir, fTrue);
|
|
}
|
|
*--sz = '\0'; /* restore pthDir */
|
|
|
|
CloseDir(&de);
|
|
|
|
/* now delete the directory */
|
|
|
|
SzPhysPath((char *)pthSub, pthDir); /* use pthSub as sz */
|
|
|
|
if (fVerbose)
|
|
PrErr("Rmdir %s\n", pthSub);
|
|
|
|
while ((w = _rmdir((char *)pthSub)) != 0 && (wRetErr = WRetryErr(0, "removing directory", 0, pthSub)) != 0)
|
|
;
|
|
|
|
if (w == -1 && enCur != ERROR_PATH_NOT_FOUND)
|
|
FatalError("cannot delete %s (%s)\n", pthSub, SzForEn(enCur));
|
|
/* ignore ERROR_PATH_NOT_FOUND */
|
|
}
|
|
|
|
|
|
/* force stderr to be the same as stdout */
|
|
void
|
|
ChngErrToOut(
|
|
void)
|
|
{
|
|
_close(2);
|
|
_dup(1);
|
|
}
|
|
|
|
|
|
/*VARARGS3*/
|
|
/* run binary executable szCmd with args as passed; one arg MUST be (char *)0;
|
|
Returns status of program executed. The program is started with interrupts
|
|
set to SIG_IGN. We ignore interrupts during the wait loop. Return -1 if
|
|
error executing the program.
|
|
|
|
For Xenix, the uid and gid are set to the real ones.
|
|
|
|
Can pass &mfStdout or some other mf, which must be open for writing.
|
|
|
|
PmfOut is not be closed or freed.
|
|
*/
|
|
int
|
|
RunSz(
|
|
char *szCmd, /* for DOS, no path and no .exe extension */
|
|
MF *pmfOut,
|
|
char *sz1,
|
|
char *sz2,
|
|
char *sz3,
|
|
char *sz4,
|
|
char *sz5,
|
|
char *sz6,
|
|
char *sz7,
|
|
char *sz8,
|
|
char *sz9
|
|
)
|
|
{
|
|
int fdOutSav; /* where stdout fd is saved. */
|
|
int status;
|
|
|
|
AssertF(pmfOut != 0);
|
|
|
|
if (pmfOut == &mfStdout)
|
|
fdOutSav = 1;
|
|
else
|
|
{
|
|
AssertF(FIsValidMf(pmfOut));
|
|
|
|
/* We are going to commandeer fd 1 (standard output stream)
|
|
* so that writes to it are actually sent to our file.
|
|
* First we save the old fd 1 away by duping it onto fdOutSav.
|
|
*/
|
|
if ((fdOutSav = _dup(1)) < 0)
|
|
FatalError("cannot dup stdout for redirection\n");
|
|
|
|
/* Close fd 1 so that the next open/create will get it. */
|
|
_close(1);
|
|
|
|
/* Dup existing stream onto fd 1. */
|
|
AssertF(pmfOut->fdWrite > 0);
|
|
VerifyF(_dup(pmfOut->fdWrite) == 1);
|
|
}
|
|
|
|
if (fVerbose)
|
|
{
|
|
PrErr("Run %s ", szCmd);
|
|
if (sz1 != 0) PrErr("%s ", sz1); else goto PrStdOut;
|
|
if (sz2 != 0) PrErr("%s ", sz2); else goto PrStdOut;
|
|
if (sz3 != 0) PrErr("%s ", sz3); else goto PrStdOut;
|
|
if (sz4 != 0) PrErr("%s ", sz4); else goto PrStdOut;
|
|
if (sz5 != 0) PrErr("%s ", sz5); else goto PrStdOut;
|
|
if (sz6 != 0) PrErr("%s ", sz6); else goto PrStdOut;
|
|
if (sz7 != 0) PrErr("%s ", sz7); else goto PrStdOut;
|
|
if (sz8 != 0) PrErr("%s ", sz8); else goto PrStdOut;
|
|
if (sz9 != 0) PrErr("%s ", sz9); else goto PrStdOut;
|
|
PrStdOut:
|
|
if (pmfOut != &mfStdout)
|
|
PrErr("> %!@T", pmfOut);
|
|
PrErr("\n");
|
|
}
|
|
status = _spawnlp(P_WAIT,szCmd,szCmd, sz1, sz2, sz3, sz4, sz5, sz6, sz7, sz8, sz9);
|
|
if (status != -1)
|
|
status <<= 8;
|
|
|
|
if (fdOutSav != 1)
|
|
{
|
|
/* Restore fd 1 to previous stdout stream. */
|
|
_close(1);
|
|
VerifyF(_dup(fdOutSav) == 1);
|
|
_close(fdOutSav);
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
/* return pointer to static memory containing a visualization of mode */
|
|
char *
|
|
SzForMode(
|
|
int mode)
|
|
{
|
|
if ((mode&0222) != 0)
|
|
{
|
|
/* read/write */
|
|
if ((mode&0x8000) != 0)
|
|
/* read/write and hidden */
|
|
return " (hidden)";
|
|
else
|
|
return "";
|
|
}
|
|
else
|
|
{
|
|
/* read only */
|
|
if ((mode&0x8000) == 0)
|
|
/* read only, not hidden */
|
|
return " (read only)";
|
|
else
|
|
/* read only and hidden */
|
|
return " (read only, hidden)";
|
|
}
|
|
}
|
|
|
|
#define cbLocCopy 1024 /* 1024 bytes if we cannot allocate more */
|
|
#define cbOutCopy 160 /* fewer still writing to stdout */
|
|
|
|
/* Copies contents of pmfTo to pmfFrom. Assumes mf's are correct and
|
|
files are rewound. The mode argument is used only to generate the
|
|
correct message.
|
|
*/
|
|
F
|
|
FCopyPmfPmf(
|
|
MF *pmfTo,
|
|
MF *pmfFrom,
|
|
int mode,
|
|
F fDoCkSum) /* set ulCkSum to checksum of copied pmf? */
|
|
{
|
|
unsigned cb;
|
|
register char far *lpbCopy;
|
|
unsigned cbCopy;
|
|
char rgbCopy[cbLocCopy];
|
|
F fOk = fTrue;
|
|
|
|
AssertF(FIsOpenMf(pmfTo));
|
|
AssertF(FIsOpenMf(pmfFrom));
|
|
|
|
if (fVerbose)
|
|
{
|
|
if (pmfTo != &mfStdout)
|
|
{
|
|
PrErr("Copy %!s ", pmfFrom->pthReal);
|
|
PrErr("to %!s%s\n", pmfTo->pthReal, SzForMode(mode));
|
|
}
|
|
else
|
|
PrErr("Type %!s\n", pmfFrom->pthReal);
|
|
}
|
|
|
|
/* try allocating 63K, then 32K, then 16K; as last
|
|
* resort, use local buffer. These numbers are tuned for DOS. Ha!
|
|
*/
|
|
if (pmfTo == &mfStdout)
|
|
{
|
|
if ((lpbCopy = LpbAllocCb((cbCopy = cbOutCopy),fFalse)) == 0)
|
|
{
|
|
/* use local buffer */
|
|
lpbCopy = (char far *)rgbCopy;
|
|
}
|
|
}
|
|
else
|
|
if ((lpbCopy = LpbAllocCb((cbCopy = (unsigned)(63*1024)),fFalse)) == 0)
|
|
if ((lpbCopy = LpbAllocCb((cbCopy = (unsigned)(32*1024)),fFalse)) == 0)
|
|
if ((lpbCopy = LpbAllocCb((cbCopy = 16*1024),fFalse)) == 0)
|
|
{
|
|
/* use local buffer */
|
|
lpbCopy = (char far *)rgbCopy;
|
|
cbCopy = cbLocCopy;
|
|
}
|
|
|
|
ulCkSum = 0;
|
|
while ((cb = CbReadMf(pmfFrom, lpbCopy, cbCopy)) != 0 &&
|
|
(fOk = FWriteMf(pmfTo, lpbCopy, cb)))
|
|
if (fDoCkSum)
|
|
ComputeCkSum(lpbCopy, cb, &ulCkSum);
|
|
|
|
if (fDoCkSum)
|
|
fCkSum = fTrue;
|
|
|
|
/* free buffer if it was allocated */
|
|
if (lpbCopy != rgbCopy)
|
|
FreeLpb(lpbCopy);
|
|
|
|
return fOk;
|
|
}
|
|
|
|
/* Create the file *now*, so that it will show up using FStatPth.
|
|
* Pth must not already exist.
|
|
*
|
|
* mmCreate: do nothing if successful, remove file upon abort.
|
|
*/
|
|
void
|
|
CreateNow(
|
|
PTH *pth,
|
|
int mode,
|
|
FX fx)
|
|
{
|
|
MF *pmf;
|
|
|
|
pmf = PmfCreate(pth, mode, fTrue, fx);
|
|
CloseOnly(pmf);
|
|
RenameMf(pmf, fFalse);
|
|
pmf->mm = mmCreate;
|
|
FreeMf(pmf);
|
|
}
|
|
|
|
|
|
/* Copy the file *now*, so that it will show up using FStatPth.
|
|
* PthTo must not already exist.
|
|
*
|
|
* mmCreate: do nothing if successful, remove file upon abort.
|
|
*/
|
|
void
|
|
CopyNow(
|
|
PTH pthTo[],
|
|
PTH pthFrom[],
|
|
int mode,
|
|
FX fxTo)
|
|
{
|
|
MF *pmfTo, *pmfFrom;
|
|
|
|
pmfTo = PmfCreate(pthTo, mode, fFalse, fxTo);
|
|
pmfFrom = PmfOpen(pthFrom, omAReadOnly, fxNil);
|
|
if (!FCopyPmfPmf(pmfTo, pmfFrom, mode, fFalse /* fDoCkSum */))
|
|
FatalError("copy %s to %s failed (incomplete write)\n", pthFrom, pthTo);
|
|
CloseMf(pmfFrom);
|
|
|
|
CloseOnly(pmfTo);
|
|
RenameMf(pmfTo, fFalse);
|
|
pmfTo->mm = mmCreate;
|
|
FreeMf(pmfTo);
|
|
}
|
|
|
|
/*VARARGS2*/
|
|
/* copies the file pthFrom to pthTo, creating pthTo with the mode passed. Uses
|
|
standard out if pthTo is 0. Mode and fCopyTime are only referenced if pthTo
|
|
is non-zero, so the calls to this procedure are in two forms:
|
|
|
|
FCopyFile(0, pth, NULL, NULL. NULL);
|
|
FCopyFile(pth1, pth2, mode, fCopyTime, fx);
|
|
*/
|
|
F
|
|
FCopyFile(
|
|
PTH pthTo[],
|
|
PTH pthFrom[],
|
|
int mode,
|
|
F fCopyTime,
|
|
FX fxTo)
|
|
{
|
|
MF *pmfTo, *pmfFrom;
|
|
|
|
pmfTo = pthTo ? PmfCreate(pthTo, mode, fFalse, fxTo) : &mfStdout;
|
|
pmfFrom = PmfOpen(pthFrom, omAReadOnly, fxNil);
|
|
|
|
if (FCopyPmfPmf(pmfTo, pmfFrom, mode, fFalse /* fDoCkSum */))
|
|
{
|
|
/* close before UtimeMf, free after */
|
|
CloseOnly(pmfFrom);
|
|
if (pmfTo != &mfStdout)
|
|
{
|
|
CloseOnly(pmfTo);
|
|
if (fCopyTime)
|
|
UtimeMf(pmfTo, pmfFrom);
|
|
FreeMf(pmfTo);
|
|
}
|
|
FreeMf(pmfFrom);
|
|
return fTrue;
|
|
}
|
|
|
|
/* Copy failed. If we were writing to a file, change the mode
|
|
* so that we don't rename the temp file over the real file in the
|
|
* event that the script is run instead of aborted. This is the case
|
|
* for an ssync which runs out of space; the script is run anyway.
|
|
*/
|
|
if (pmfTo != &mfStdout)
|
|
{
|
|
AssertF(pmfTo->mm == mmRenTemp || pmfTo->mm == mmRenTempRO);
|
|
pmfTo->mm = mmDelTemp;
|
|
CloseMf(pmfTo);
|
|
}
|
|
|
|
CloseMf(pmfFrom);
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/* see the FCopyFile comment. */
|
|
void
|
|
CopyFile(
|
|
PTH pthTo[],
|
|
PTH pthFrom[],
|
|
int mode,
|
|
int fCopyTime,
|
|
FX fxTo)
|
|
{
|
|
if (!FCopyFile(pthTo, pthFrom, mode, fCopyTime, fxTo))
|
|
FatalError("copy %s to %s failed (incomplete write)\n", pthFrom, pthTo);
|
|
}
|
|
|
|
#define ctixSec 18
|
|
|
|
F
|
|
SleepTicks(
|
|
int cTicks)
|
|
{
|
|
Sleep(cTicks * (1000/ctixSec));
|
|
return CheckForBreak();
|
|
}
|
|
|
|
|
|
/* Sleep for 'cSecs' seconds. */
|
|
void
|
|
SleepCsecs(
|
|
int cSecs)
|
|
{
|
|
PrErr("(Sleeping");
|
|
|
|
/*
|
|
* For DOS, we actually only sleep for a second, then print a '.' so
|
|
* that we can recieve user interrupts and process them without the
|
|
* user having to wait till the end of the full sleep period.
|
|
*/
|
|
while (cSecs-- > 0)
|
|
{
|
|
/* A timer tick occurs 18.2 times per second. */
|
|
if (SleepTicks(ctixSec))
|
|
break;
|
|
PrErr(".");
|
|
}
|
|
|
|
PrErr(")\n");
|
|
}
|
|
|
|
#define ctryMax 200
|
|
static int ctry = 0;
|
|
|
|
#define HIDE_CERR 50
|
|
|
|
/* an error just occured; return fTrue only if ea..Retry and the user
|
|
answered y.
|
|
|
|
Only one of pmf or sz2 may be non-zero.
|
|
*/
|
|
int
|
|
WRetryErr(
|
|
int pem,
|
|
char *sz1,
|
|
MF *pmf,
|
|
char *sz2)
|
|
{
|
|
int fRetry;
|
|
char szTemp[cchPthMax];
|
|
|
|
static int cerr = 0;
|
|
|
|
/* Under OS/2, starting with 1.63, sharing violation errors show up
|
|
in certain circumstances, along with lock violation errors. Since
|
|
the lock violation did occur prior to 1.63, it is being assumed
|
|
by the programmer that the sharing violation is also not a problem.
|
|
The very poor grade hack here prevents the first 50 (HIDE_CERR) of
|
|
these from appearing, as the test that shows these rarely gets to
|
|
50 retrys.
|
|
|
|
My fault: ErichS
|
|
*/
|
|
|
|
if ((errno == 0) &&
|
|
((_doserrno == ERROR_SHARING_VIOLATION) ||
|
|
(_doserrno == ERROR_LOCK_VIOLATION)) &&
|
|
(cerr++ < HIDE_CERR))
|
|
{ //and we don't appear to be looped
|
|
SleepTicks(1); //sleep for a clock tick
|
|
return fTrue; //then mask the error
|
|
}
|
|
else
|
|
cerr = 0; //nonproblem, restart mask count
|
|
|
|
AssertF(pem == 0);
|
|
|
|
geterr(); /* get enCur and eaCur */
|
|
|
|
AssertF(pmf != 0 || sz2 != 0);
|
|
if (pmf != 0)
|
|
{
|
|
AssertF(sz2 == 0);
|
|
SzPhysTMf(szTemp, pmf);
|
|
sz2 = szTemp;
|
|
}
|
|
|
|
switch(eaCur)
|
|
{
|
|
default:
|
|
Error("unknown action %d for error %s\n", eaCur, SzForEn(enCur));
|
|
/* fall through */
|
|
|
|
case eaNil:
|
|
case eaIgnore:
|
|
fRetry = fFalse;
|
|
break;
|
|
|
|
case eaFrcRetry:
|
|
fRetry = fTrue;
|
|
break;
|
|
|
|
case eaCleanUp:
|
|
case eaUserErr:
|
|
/*
|
|
* For some reason, DOS returns eaUserErr when we get
|
|
* an access error due to hardware locking. This
|
|
* special case code catches only that case.
|
|
*
|
|
* REVIEW: 1. why is this happening?
|
|
* 2. is this a problem on OS/2?
|
|
*/
|
|
if (enCur != EIO && eaCur == eaUserErr)
|
|
{
|
|
fRetry = fFalse;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
|
|
case eaRetry:
|
|
case eaDRetry:
|
|
case eaURetry:
|
|
/* if the user has the force flag on, keep track of the retried
|
|
* errors, and abort if the count of retries gets too high. This
|
|
* avoids infinite retrying in cases where DOS returns the wrong
|
|
* error action.
|
|
*/
|
|
if (FForce())
|
|
{
|
|
if (enCur == enPrev && eaCur == eaPrev)
|
|
{
|
|
if (ctry++ == ctryMax)
|
|
{
|
|
Error("retry count over maximum (%s)\n", SzForEn(enCur));
|
|
fRetry = fFalse;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
ctry = 0;
|
|
}
|
|
fRetry = FQueryApp("error %s %s (%s)", "retry", sz1,sz2,SzForEn(enCur));
|
|
break;
|
|
|
|
case eaAbort:
|
|
/* try to let the user know */
|
|
Error("PANIC: an unrecoverable operating system error has occurred.\n");
|
|
exit(1);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
if (!fRetry)
|
|
ctry = 0;
|
|
|
|
enPrev = enCur;
|
|
eaPrev = eaCur;
|
|
|
|
/* return special code if we were interrupted during a system
|
|
* call. This way we can restore the state before retrying.
|
|
*/
|
|
if (_doserrno == enInterruptSysCall && fRetry == fTrue)
|
|
return enInterruptSysCall;
|
|
else
|
|
return fRetry;
|
|
}
|
|
|
|
// Error Map (maps en to ea). An array of these things is terminated by
|
|
// en==enNil. Unexpected errors map to eaCleanUp.
|
|
typedef struct
|
|
{
|
|
int en;
|
|
int ea;
|
|
} EM;
|
|
|
|
static EM rgemWrite[] =
|
|
{
|
|
ENOSPC, eaRetry, // no space on device, but give it another shot
|
|
EBADF, eaUserErr, // bad file descriptor
|
|
enNil, eaNil
|
|
};
|
|
|
|
static EM *rgpem[] =
|
|
{
|
|
rgemWrite,
|
|
};
|
|
|
|
// an error just occured; return fTrue only if ea..Retry and the user
|
|
// answered y.
|
|
//
|
|
// Only one of pmf or sz2 may be non-zero.
|
|
int
|
|
WRetryError(
|
|
int eo,
|
|
char *sz1,
|
|
MF *pmf,
|
|
char *sz2)
|
|
{
|
|
int fRetry;
|
|
char szTemp[cchPthMax];
|
|
EM *pem;
|
|
|
|
// Hack to silently retry bogus network write errors
|
|
// We'll only do this once, that seems to be catching all the
|
|
// errors we're seeing.
|
|
if (ENOSPC == errno && eaFrcRetry != eaPrev && eoWrite == eo)
|
|
{
|
|
enCur = enPrev = ENOSPC;
|
|
eaCur = eaPrev = eaFrcRetry;
|
|
return fTrue;
|
|
}
|
|
|
|
geterr();
|
|
|
|
if (enCur == 0 && errno != 0)
|
|
{
|
|
enCur = errno;
|
|
pem = rgpem[eo];
|
|
for (;; pem++)
|
|
{
|
|
if (pem->en == enNil)
|
|
{
|
|
Error("unexpected error %s\n", SzForEn(enCur));
|
|
eaCur = eaNil;
|
|
break;
|
|
}
|
|
else if (pem->en == errno)
|
|
{
|
|
eaCur = pem->ea;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
AssertF(pmf != (MF *)NULL || sz2 != (char *)NULL);
|
|
if (pmf != (MF *)NULL)
|
|
{
|
|
AssertF(sz2 == (char *)NULL);
|
|
SzPhysTMf(szTemp, pmf);
|
|
sz2 = szTemp;
|
|
}
|
|
|
|
switch (eaCur)
|
|
{
|
|
default:
|
|
Error("unknown action %d for error %s\n", eaCur, SzForEn(enCur));
|
|
/* fall through */
|
|
|
|
case eaNil:
|
|
case eaIgnore:
|
|
fRetry = fFalse;
|
|
break;
|
|
|
|
case eaFrcRetry:
|
|
fRetry = fTrue;
|
|
break;
|
|
|
|
case eaUserErr:
|
|
// For some reason, DOS returns eaUserErr when we get
|
|
// an access error due to hardware locking. This
|
|
// special case code catches only that case.
|
|
//
|
|
// REVIEW: 1. why is this happening?
|
|
// 2. is this a problem on OS/2?
|
|
if (enCur != EIO && eaCur == eaUserErr)
|
|
{
|
|
fRetry = fFalse;
|
|
break;
|
|
}
|
|
goto MaybeRetry;
|
|
|
|
case eaDRetry:
|
|
SleepTicks(1);
|
|
goto MaybeRetry;
|
|
|
|
case eaCleanUp:
|
|
case eaRetry:
|
|
case eaURetry:
|
|
MaybeRetry:
|
|
// if the user has the force flag on, keep track of the retried
|
|
// errors, and abort if the count of retries gets too high. This
|
|
// avoids infinite retrying in cases where DOS returns the wrong
|
|
// error action.
|
|
if (FForce())
|
|
{
|
|
if (enCur == enPrev && eaCur == eaPrev)
|
|
{
|
|
if (ctry++ == ctryMax)
|
|
{
|
|
Error("retry count over maximum (%s)\n", SzForEn(enCur));
|
|
fRetry = fFalse;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
ctry = 0;
|
|
}
|
|
fRetry = FQueryApp("error %s %s (%s)", "retry",
|
|
sz1, sz2, SzForEn(enCur));
|
|
break;
|
|
|
|
case eaAbort:
|
|
/* try to let the user know */
|
|
Error("PANIC: an unrecoverable operating system error has occurred.\n");
|
|
exit(1);
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
if (!fRetry)
|
|
ctry = 0;
|
|
|
|
enPrev = enCur;
|
|
eaPrev = eaCur;
|
|
|
|
return fRetry;
|
|
}
|
|
|
|
char szResvd[] = "reserved";
|
|
|
|
char *mpensz[] = {
|
|
szResvd,
|
|
/* 1*/ "invalid function code",
|
|
/* 2*/ "file not found",
|
|
/* 3*/ "path not found",
|
|
/* 4*/ "too many open files",
|
|
/* 5*/ "access denied",
|
|
/* 6*/ "invalid handle",
|
|
/* 7*/ "memory control blocks destroyed",
|
|
/* 8*/ "insufficient memory",
|
|
/* 9*/ "invalid memory block address",
|
|
/*10*/ "invalid environment",
|
|
/*11*/ "invalid format",
|
|
/*12*/ "invalid access code",
|
|
/*13*/ "invalid data",
|
|
/*14*/ szResvd,
|
|
/*15*/ "invalid drive",
|
|
/*16*/ "attempt to remove the current directory",
|
|
/*17*/ "not same device",
|
|
/*18*/ "no more files",
|
|
/*19*/ "disk is write-protected",
|
|
/*20*/ "bad disk unit",
|
|
/*21*/ "drive not ready",
|
|
/*22*/ "invalid disk command",
|
|
/*23*/ "CRC error",
|
|
/*24*/ "invalid length",
|
|
/*25*/ "seek error",
|
|
/*26*/ "not an MS-DOS disk",
|
|
/*27*/ "sector not found",
|
|
/*28*/ "out of paper",
|
|
/*29*/ "write fault",
|
|
/*30*/ "read fault",
|
|
/*31*/ "general failure",
|
|
/*32*/ "sharing violation",
|
|
/*33*/ "lock violation",
|
|
/*34*/ "wrong disk",
|
|
/*35*/ "fcb unavailable",
|
|
/*36*/ "sharing buffer exceeded",
|
|
/*37*/ szResvd,
|
|
/*38*/ szResvd,
|
|
/*39*/ szResvd,
|
|
/*40*/ szResvd,
|
|
/*41*/ szResvd,
|
|
/*42*/ szResvd,
|
|
/*43*/ szResvd,
|
|
/*44*/ szResvd,
|
|
/*45*/ szResvd,
|
|
/*46*/ szResvd,
|
|
/*47*/ szResvd,
|
|
/*48*/ szResvd,
|
|
/*49*/ szResvd,
|
|
/*50*/ "network request not support",
|
|
/*51*/ "remote computer not listening",
|
|
/*52*/ "duplicate name on network",
|
|
/*53*/ "network name not found",
|
|
/*54*/ "network busy",
|
|
/*55*/ "network device no longer exists",
|
|
/*56*/ "net BIOS command limit exceeded",
|
|
/*57*/ "network adapter hardware error",
|
|
/*58*/ "incorrect response from network",
|
|
/*59*/ "unexpected network error",
|
|
/*60*/ "incompatable remote adapt",
|
|
/*61*/ "print queue full",
|
|
/*62*/ "queue not full",
|
|
/*63*/ "not enough space for print file",
|
|
/*64*/ "network name was deleted",
|
|
/*65*/ "access denied",
|
|
/*66*/ "network device type incorrect",
|
|
/*67*/ "network name not found",
|
|
/*68*/ "network name limit exceeded",
|
|
/*69*/ "net BIOS session limit exceeded",
|
|
/*70*/ "temporarily paused",
|
|
/*71*/ "network request not accepted",
|
|
/*72*/ "print or disk redirection is paused",
|
|
/*73*/ szResvd,
|
|
/*74*/ szResvd,
|
|
/*75*/ szResvd,
|
|
/*76*/ szResvd,
|
|
/*77*/ szResvd,
|
|
/*78*/ szResvd,
|
|
/*79*/ szResvd,
|
|
/*80*/ "file exists",
|
|
/*81*/ szResvd,
|
|
/*82*/ "cannot make",
|
|
/*83*/ "interrupt 24 failure",
|
|
/*84*/ "out of structures",
|
|
/*85*/ "already assigned",
|
|
/*86*/ "invalid password",
|
|
/*87*/ "invalid parameter",
|
|
/*88*/ "net write fault"
|
|
};
|
|
#define enMax (sizeof mpensz / sizeof mpensz[0])
|
|
|
|
char *
|
|
SzForEn(
|
|
int en)
|
|
{
|
|
static char szError[80];
|
|
|
|
if (en == errno && en != (int)_doserrno && en > 0 && en < sys_nerr)
|
|
SzPrint(szError, "%d(%s)", en, sys_errlist[en]);
|
|
else if (en > 0 && en < enMax)
|
|
SzPrint(szError, "%d(%s)", en, mpensz[en]);
|
|
else
|
|
SzPrint(szError, "%d", en);
|
|
return szError;
|
|
}
|