mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
584 lines
15 KiB
584 lines
15 KiB
#include "precomp.h"
|
|
#pragma hdrstop
|
|
EnableAssert
|
|
|
|
/* This file contains non version-specific lower level functions used by the
|
|
* version specific routines as well as general utility type stuff.
|
|
*/
|
|
|
|
/* Checks to see if a vector of cchMac characters is a Nm */
|
|
F
|
|
FIsNm(
|
|
char far lrgch[], // String to test
|
|
int cchMac,
|
|
int *pfTrailZ) // Set according to nature of data after name
|
|
{
|
|
register char far *lpchT = lrgch;
|
|
register char far *lpchMac = lrgch + cchMac;
|
|
|
|
AssertF(cchMac >= 0);
|
|
|
|
/* must have only alphanumerics or punctuation */
|
|
while (lpchT < lpchMac && (isalnum(*lpchT) || ispunct(*lpchT)))
|
|
lpchT++;
|
|
|
|
if (lpchT == lrgch || lpchT < lpchMac && *lpchT != 0)
|
|
/* no alnum/punct or does not end in 0 */
|
|
{
|
|
*pfTrailZ = fFalse;
|
|
return fFalse;
|
|
}
|
|
|
|
/* loop over zeros */
|
|
while (lpchT < lpchMac && *lpchT == 0)
|
|
lpchT++;
|
|
|
|
*pfTrailZ = (lpchT == lpchMac);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* This function takes a pth and checks its syntax to see whether or not it
|
|
is a valid pth.
|
|
*/
|
|
F FIsPth(
|
|
PTH far *pth,
|
|
int cchMac,
|
|
int * pfTrailZ)
|
|
{
|
|
|
|
register char far *lpchPth = pth;
|
|
register char far *lpchSlash; /* pointer to slash */
|
|
|
|
Unreferenced(cchMac);
|
|
|
|
*pfTrailZ = fFalse;
|
|
|
|
if (*lpchPth++ != '/' || *lpchPth++ != '/') /* need two slashes */
|
|
return fFalse;
|
|
|
|
/* see if looking at drive name and volume */
|
|
if (isalpha(*lpchPth) && *(lpchPth + 1) == ':')
|
|
{
|
|
/* volume must be a non-nil legal user name */
|
|
NM nm[cchVolMax];
|
|
|
|
lpchPth += 2;
|
|
lpchSlash = LszIndex(lpchPth, '/');
|
|
|
|
ClearPbCb((char *)nm, cchVolMax);
|
|
NmCopy(nm, lpchPth, (unsigned)((unsigned long)(lpchSlash ? lpchSlash : LszIndex(lpchPth,'\0')) - (unsigned long)lpchPth));
|
|
if (!FIsNm((char far *)nm, cchVolMax, pfTrailZ) || !*pfTrailZ)
|
|
return fFalse;
|
|
}
|
|
else
|
|
{
|
|
/* //mach/shortname type path; do machine name */
|
|
NM nm[cchMachMax];
|
|
|
|
lpchSlash = LszIndex(lpchPth, '/');
|
|
|
|
ClearPbCb((char *)nm, cchMachMax);
|
|
NmCopy(nm, lpchPth, (unsigned)((unsigned long)(lpchSlash ? lpchSlash : LszIndex(lpchPth, '\0')) - (unsigned long)lpchPth));
|
|
if (!FIsNm((char far *)nm, cchMachMax, pfTrailZ) || !*pfTrailZ)
|
|
return fFalse;
|
|
}
|
|
|
|
/* at this point lpchSlash = position of next '/', 0 if none
|
|
* if it is 0 we must check for zeroes at end.
|
|
*/
|
|
while (lpchSlash != 0)
|
|
{
|
|
/* move to next position after '/' */
|
|
lpchPth = lpchSlash + 1;
|
|
|
|
lpchSlash = LszIndex(lpchPth, '/');
|
|
if (!FIsFileNm(lpchPth, (lpchSlash ? lpchSlash : LszIndex(lpchPth, '\0')) - lpchPth, pfTrailZ) ||
|
|
!*pfTrailZ)
|
|
return fFalse;
|
|
}
|
|
|
|
/* must end in all zeroes */
|
|
for (lpchPth = LszIndex(lpchPth, '\0'); lpchPth < pth + cchPthMax && *lpchPth == 0; lpchPth++)
|
|
;
|
|
|
|
*pfTrailZ = (lpchPth == pth + cchPthMax);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* This function determines whether the string of length cchMac pointed to by
|
|
* lrgch is a valid file name. These are not necessarily zero terminated.
|
|
* XENIX allows any 14 charactrer name with no bad characters,
|
|
* DOS allows 8.3 of legal characters.
|
|
*/
|
|
F
|
|
FIsFileNm(
|
|
char far lrgch[],
|
|
int cchMac,
|
|
int *pfTrailZ)
|
|
{
|
|
#define szBad "\"/\\[]:|<>; "
|
|
|
|
#define cchFnameMax 8 /* max number of charcters in file name */
|
|
#define cchExtMax 3 /* max number of characters in extension */
|
|
|
|
register char far *lpch1;
|
|
register char far *lpch2;
|
|
char far *lpchMac;
|
|
char far *lpchLim;
|
|
|
|
*pfTrailZ = fFalse;
|
|
|
|
AssertF(cchMac >= 0);
|
|
if (cchMac > cchFileMax)
|
|
return fFalse;
|
|
|
|
lpchMac = lrgch + cchMac;
|
|
lpchLim = lrgch + cchFnameMax;
|
|
for (lpch1 = lrgch; lpch1 < lpchLim && lpch1 < lpchMac && *lpch1 != 0 && *lpch1 != '.'; lpch1++)
|
|
{
|
|
if (!isascii(*lpch1) || index(szBad, *lpch1) != 0 || iscntrl(*lpch1))
|
|
return fFalse;
|
|
}
|
|
if (lpch1 == lrgch)
|
|
/* zero length name, no chars or extension only */
|
|
return fFalse;
|
|
|
|
if (lpch1 == lpchMac)
|
|
{
|
|
/* done; no extension if dos */
|
|
*pfTrailZ = fTrue;
|
|
return fTrue;
|
|
}
|
|
|
|
if (*lpch1 != 0)
|
|
{
|
|
if (*lpch1 != '.')
|
|
return fFalse; /* no '.' at start of ext */
|
|
lpch1++;
|
|
}
|
|
|
|
lpchLim = lpch1 + cchExtMax;
|
|
for (lpch2 = lpch1; lpch2 < lpchLim && lpch2 < lpchMac && *lpch2 != 0; lpch2++)
|
|
{
|
|
if (!isascii(*lpch2) || index(szBad, *lpch2) != 0 ||
|
|
iscntrl(*lpch2) || *lpch2 == '.')
|
|
return fFalse;
|
|
}
|
|
|
|
if (lpch2 < lpchMac && *lpch2 != 0)
|
|
/* does not end in 0 */
|
|
return fFalse;
|
|
|
|
/* make sure whatever is left is zero */
|
|
while (lpch2 < lpchMac && *lpch2 == 0)
|
|
lpch2++;
|
|
|
|
*pfTrailZ = (lpch2 == lpchMac);
|
|
return fTrue;
|
|
}
|
|
|
|
|
|
/* This function takes a string and removes unprintable characters,
|
|
* changing them in place to dots.
|
|
*/
|
|
void
|
|
MakePrintLsz(
|
|
register char far *lsz)
|
|
{
|
|
while (*lsz)
|
|
{
|
|
if (!isascii(*lsz) || !isprint(*lsz))
|
|
*lsz = '.';
|
|
lsz++;
|
|
}
|
|
}
|
|
|
|
|
|
/*VARARGS2*/
|
|
F
|
|
FQueryPsd(
|
|
SD *psd,
|
|
char *sz, ...)
|
|
{
|
|
va_list ap;
|
|
F f;
|
|
|
|
va_start(ap, sz);
|
|
f = VaFQueryPsd(psd, sz, ap);
|
|
va_end(ap);
|
|
|
|
return f;
|
|
}
|
|
|
|
/* This function handles all queries concerning changes to the status file.
|
|
* If permission to alter contents of status file is given, we set the
|
|
* fAnyChanges flag.
|
|
* NB: This function must be called when an answer of yes to a query leads
|
|
* to a change in the status buffer. It should only be called when the status
|
|
* file was loaded via FLoadSd, never when from FLoadStatus.
|
|
*/
|
|
F
|
|
VaFQueryPsd(
|
|
SD *psd,
|
|
char *sz,
|
|
va_list ap)
|
|
{
|
|
if (VaFQueryApp(sz, "Fix", ap))
|
|
return (psd->fAnyChanges = fTrue);
|
|
else
|
|
return fFalse;
|
|
}
|
|
|
|
|
|
/* This function examines the etc directory to find what the biNext should be.
|
|
*/
|
|
BI
|
|
GetBiNext(
|
|
PTH *pthDir)
|
|
{
|
|
int bi;
|
|
BI biNext = biMin;
|
|
DE de;
|
|
char szFile[cchFileMax + 1];
|
|
FA fa;
|
|
|
|
OpenDir(&de, pthDir, faFiles);
|
|
while (FGetDirSz(&de, szFile, &fa))
|
|
{
|
|
if (szFile[0] == 'B' && isdigit(szFile[1]) &&
|
|
*PchGetW(szFile + 1, &bi) == 0)
|
|
{
|
|
AssertF( bi >= 0 );
|
|
if ((unsigned)bi + 1 > biNext)
|
|
biNext = bi + 1;
|
|
}
|
|
}
|
|
CloseDir(&de);
|
|
return biNext;
|
|
}
|
|
|
|
|
|
/* This function creates a sorted linked list of NE of the contents of the
|
|
* directory pth.
|
|
*/
|
|
NE *
|
|
PneSortDir(
|
|
PTH pth[cchPthMax])
|
|
{
|
|
FA fa;
|
|
DE de;
|
|
char sz[cchFileMax + 1]; /* assume that cchFileMax > cchDirMax */
|
|
NE neDummy; /* holds start of list */
|
|
register NE *pneComp; /* NE being compared */
|
|
register NE *pnePrevious; /* back pointer */
|
|
|
|
neDummy.pneNext = 0;
|
|
OpenDir(&de, pth, faFiles | faDir);
|
|
|
|
/* Insertion sort. */
|
|
while (FGetDirSz(&de, sz, &fa))
|
|
{
|
|
for (pnePrevious = &neDummy, pneComp = neDummy.pneNext;
|
|
pneComp && NeCmpiSz(pneComp, sz) < 0;
|
|
pneComp = (pnePrevious = pneComp)->pneNext)
|
|
;
|
|
pnePrevious->pneNext = PneNewNm((NM far *)sz, strlen(sz), fa);
|
|
pnePrevious->pneNext->pneNext = pneComp;
|
|
}
|
|
CloseDir(&de);
|
|
return neDummy.pneNext;
|
|
}
|
|
|
|
|
|
/* Function checks if cb bytes at lpb are all zero */
|
|
F
|
|
FAllZero(
|
|
register char far *lpb,
|
|
register int cb)
|
|
{
|
|
while (cb-- > 0 && *lpb++ == 0)
|
|
;
|
|
return (cb == -1);
|
|
}
|
|
|
|
|
|
/* This function examines two files to determine if they are identical;
|
|
|
|
Note: this function has a 2 K frame
|
|
*/
|
|
F
|
|
FSameFile(
|
|
PTH *pth1,
|
|
PTH *pth2)
|
|
{
|
|
#define cbBufMax 1024
|
|
unsigned cbCmpMax = 0;
|
|
unsigned cb1;
|
|
unsigned cb2;
|
|
register char far *lpb1;
|
|
register char far *lpb2;
|
|
MF *pmf1, *pmf2;
|
|
char rgb1[cbBufMax];
|
|
char rgb2[cbBufMax];
|
|
|
|
if ((pmf1 = PmfOpen(pth1, omReadOnly, fxNil)) == 0)
|
|
{
|
|
return fFalse;
|
|
}
|
|
else if ((pmf2 = PmfOpen(pth2, omReadOnly, fxNil)) == 0)
|
|
{
|
|
CloseMf(pmf1);
|
|
return fFalse;
|
|
}
|
|
|
|
if ((lpb1 = LpbAllocCb((unsigned)(63*1024),fFalse)) != 0)
|
|
{
|
|
if ((lpb2 = LpbAllocCb((unsigned)(63*1024),fFalse)) != 0)
|
|
cbCmpMax = 63*1024;
|
|
else
|
|
FreeLpb(lpb1);
|
|
}
|
|
|
|
if (cbCmpMax == 0 && (lpb1 = LpbAllocCb((unsigned)(32*1024),fFalse)) != 0)
|
|
{
|
|
if ((lpb2 = LpbAllocCb((unsigned)(32*1024),fFalse)) != 0)
|
|
cbCmpMax = 32*1024;
|
|
else
|
|
FreeLpb(lpb1);
|
|
}
|
|
|
|
if (cbCmpMax == 0 && (lpb1 = LpbAllocCb(16*1024,fFalse)) != 0)
|
|
{
|
|
if ((lpb2 = LpbAllocCb(16*1024,fFalse)) != 0)
|
|
cbCmpMax = 16*1024;
|
|
else
|
|
FreeLpb(lpb1);
|
|
}
|
|
|
|
if (cbCmpMax == 0)
|
|
{
|
|
cbCmpMax = cbBufMax;
|
|
lpb1 = (char far *)rgb1;
|
|
lpb2 = (char far *)rgb2;
|
|
}
|
|
|
|
while ((cb1 = CbReadMf(pmf1, lpb1, cbCmpMax)) ==
|
|
(cb2 = CbReadMf(pmf2, lpb2, cbCmpMax)) && cb1 > 0)
|
|
{
|
|
if (!FCmpLpbCb(lpb1, lpb2, cb1))
|
|
{
|
|
CloseMf(pmf1);
|
|
CloseMf(pmf2);
|
|
|
|
if (cbCmpMax != cbBufMax)
|
|
{
|
|
FreeLpb(lpb1);
|
|
FreeLpb(lpb2);
|
|
}
|
|
|
|
return fFalse;
|
|
}
|
|
}
|
|
|
|
if (cbCmpMax != cbBufMax)
|
|
{
|
|
FreeLpb(lpb1);
|
|
FreeLpb(lpb2);
|
|
}
|
|
|
|
CloseMf(pmf1);
|
|
CloseMf(pmf2);
|
|
return (cb1 == cb2);
|
|
}
|
|
|
|
|
|
F FReadOnly(pst)
|
|
struct _stat *pst;
|
|
{
|
|
return ((pst->st_mode & (S_IREAD|S_IWRITE)) == S_IREAD);
|
|
}
|
|
|
|
|
|
F FCkWritePth(pth, pst)
|
|
/* This function is simiilar to FMkPth, but more passive. It checks if we can
|
|
* write to a directory specified by pth. Returns fFalse if error.
|
|
*/
|
|
PTH pth[cchPthMax];
|
|
register struct _stat *pst;
|
|
{
|
|
char sz[cchPthMax];
|
|
|
|
SzPhysPath(sz, pth); /* convert for error messages */
|
|
|
|
if ((pst->st_mode&S_IFREG) != 0)
|
|
{
|
|
Error("regular file %s should be replaced with directory\n",sz);
|
|
return fFalse;
|
|
}
|
|
else
|
|
/* must be dir; on DOS we can always write to a directory */
|
|
return fTrue;
|
|
}
|
|
|
|
/****************************************************************/
|
|
/* generic index ordering functions */
|
|
|
|
INO *PinoNew(short iindMac, PFN_CMP pfnCmp)
|
|
/*
|
|
* Build a new index ordering
|
|
*/
|
|
{
|
|
INO *pino;
|
|
|
|
pino = (INO *)PbAllocCb(sizeof(INO), fFalse);
|
|
pino->iindMac = iindMac;
|
|
pino->iindLim = 0;
|
|
pino->rgind = (IND *)PbAllocCb(iindMac*sizeof(IND), fFalse);
|
|
pino->pfnCmp = pfnCmp;
|
|
|
|
return pino;
|
|
}
|
|
|
|
void FreeIno(pino)
|
|
/*
|
|
* Free the storage associated with the ordering.
|
|
*/
|
|
INO *pino;
|
|
{
|
|
free((char *)pino->rgind);
|
|
free((char *)pino);
|
|
}
|
|
|
|
IND *PindLookup(SD *psd, IND ind, INO *pino)
|
|
/*
|
|
* Look for the Index entry in the ordering which compares equal to the given
|
|
* index. If there is no such entry, return the first entry which compares
|
|
* greater than the given element.
|
|
*/
|
|
{
|
|
IND *pind;
|
|
short iind;
|
|
|
|
for (pind = pino->rgind, iind = 0;
|
|
iind < pino->iindLim && (*pino->pfnCmp)(psd, ind, *pind) >= 0;
|
|
pind++, iind++)
|
|
;
|
|
|
|
return pind;
|
|
}
|
|
|
|
void InsertInd(SD *psd, IND ind, INO *pino)
|
|
/*
|
|
* Insert the given index into the ordering, using the given comparison
|
|
* function.
|
|
*/
|
|
{
|
|
IND *pind;
|
|
|
|
AssertF(pino->iindLim < pino->iindMac); /* assert entry available */
|
|
|
|
pind = PindLookup(psd, ind, pino);
|
|
|
|
/* blt the entire index, including nil terminator to make room */
|
|
CopyOverlapLrgb((char far *)pind,
|
|
(char far *)pind+sizeof(ind),
|
|
(pino->iindLim - (pind - pino->rgind))*sizeof(ind));
|
|
pino->iindLim++;
|
|
*pind = ind;
|
|
}
|
|
|
|
void ApplyIno(INO *pino, char far *rgelt, unsigned short cbElt)
|
|
/*
|
|
* Apply the given ordering to the given range of elements. Each element
|
|
* is the same length, nd they must be contiguous.
|
|
*/
|
|
{
|
|
F *pf, *pfMac;
|
|
F *rgfUsed; /* rgfUsed[x] <==> rgind[x] already applied */
|
|
char *peltSrc = PbAllocCb(cbElt, fFalse);
|
|
char *peltSave = PbAllocCb(cbElt, fFalse);
|
|
short iind;
|
|
|
|
/* set up marking array */
|
|
rgfUsed = (F *)PbAllocCb(pino->iindLim*sizeof(F), fFalse);
|
|
for (pf = rgfUsed, pfMac = pf + pino->iindLim;
|
|
pf < pfMac;
|
|
pf++)
|
|
{
|
|
*pf = fFalse;
|
|
}
|
|
|
|
for (iind = -1;;)
|
|
{
|
|
/*
|
|
* Apply the ordering until no indices remain. We
|
|
* cycle through the array, keeping the most recently
|
|
* overwritten element in peltSrc. If it has already
|
|
* been remapped, we start again with the first unused
|
|
* element.
|
|
*/
|
|
if (iind == -1 || rgfUsed[iind])
|
|
{
|
|
/* find first unused */
|
|
for (iind = 0; iind < pino->iindLim; iind++)
|
|
{
|
|
if (!rgfUsed[iind])
|
|
break;
|
|
}
|
|
|
|
if (iind == pino->iindLim)
|
|
{
|
|
/* no more unused elements, break */
|
|
break;
|
|
}
|
|
|
|
/* copy rgelt[rgind[iind]] into peltSrc */
|
|
CopyLrgb(rgelt+(pino->rgind[iind]*cbElt),
|
|
peltSrc,
|
|
cbElt);
|
|
}
|
|
|
|
/* copy rgelt[iind] into peltSave */
|
|
CopyLrgb(rgelt+(iind*cbElt),
|
|
peltSave,
|
|
cbElt);
|
|
|
|
/* copy peltSrc into rgelt[iind] */
|
|
CopyLrgb(peltSrc,
|
|
rgelt+(iind*cbElt),
|
|
cbElt);
|
|
|
|
/* mark the operation as done */
|
|
rgfUsed[iind] = fTrue;
|
|
|
|
BLOCK
|
|
{
|
|
short iindT;
|
|
|
|
/* find position of *peltSave in new ordering */
|
|
for (iindT = 0; pino->rgind[iindT] != (IND)iind;
|
|
iindT++)
|
|
;
|
|
|
|
iind = iindT;
|
|
}
|
|
|
|
BLOCK
|
|
{
|
|
char *peltT;
|
|
|
|
/* Swap save and src to avoid copying. */
|
|
peltT = peltSrc;
|
|
peltSrc = peltSave;
|
|
peltSave = peltT;
|
|
}
|
|
}
|
|
|
|
free((char *)rgfUsed);
|
|
free(peltSrc);
|
|
free(peltSave);
|
|
}
|
|
|
|
/****************************************************************/
|