Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1543 lines
35 KiB

#include "precomp.h"
#pragma hdrstop
#include "messages.h"
EnableAssert
const PTH pthEtc[] = "/etc";
const PTH pthSrc[] = "/src";
const PTH pthDiff[] = "/diff";
const char szEtcPZ[] = "%&/S/etc/P/C/Z";
const char szSrcPZ[] = "%&/S/src/P/C/Z";
const char szDifPZ[] = "%&/S/diff/P/C/Z";
const char szDifPF[] = "%&/S/diff/P/C/F";
const char szEtcPT[] = "%&/S/etc/P/C/T";
const char szEtcPA[] = "%&/S/etc/P/C/A";
const char szEtcPL[] = "%&/S/etc/P/C/L";
const char szSrcPF[] = "%&/S/src/P/C/F";
const char szBasPB[] = "%&/S/etc/P/C/B";
const char szUQ[] = "%&/U/Q";
const char szUQF[] = "%&/U/Q/F";
const char szUQFR[] = "%&/U/Q/F/R";
const char szUQZCache[] = "%&/U/Q/slm.dif/Z";
const char szUQFCached[] = "%&/U/Q/slm.dif/F";
const char szYEtcPC[] = "%&/Y/etc/P/C";
const char szYEtcPCT[] = "%&/Y/etc/P/C/T";
const char szYSrcPC[] = "%&/Y/src/P/C";
const char szYSrcPCF[] = "%&/Y/src/P/C/F";
const char szCD[] = "%c%d";
/* replace backward slashes by forward ones */
void ConvToSlash(
char *sz)
{
for ( ;*sz != '\0'; sz++)
if (*sz == '\\')
*sz = '/';
}
/* replace forward slashes by backward ones */
void ConvFromSlash(
char *sz)
{
for (;*sz != '\0'; sz++)
if (*sz == '/')
*sz = '\\';
}
/* returns a duplicate sz */
char *SzDup(
char *sz)
{
return strcpy(PbAllocCb((unsigned)strlen(sz)+1,fFalse), sz);
}
/* converts lsz to lower case */
void LowerLsz(
char far *lsz)
{
while (*lsz)
{
if (isupper(*lsz))
*lsz = (char)tolower((int)*lsz);
lsz++;
}
}
/* sets *pw to the integer at pch and return pointer after integer. */
char *PchGetW(
char *pch,
int *pw)
{
F fNeg;
if ((fNeg = *pch == '-') == fTrue)
pch++;
*pw=0;
while (isdigit(*pch))
*pw = *pw * 10 + (*pch++ - '0');
if (fNeg)
*pw = -*pw;
return pch;
}
void InitAppendNe(
NE ***pppneLast,
NE **ppneHead)
{
*pppneLast = ppneHead;
}
void AppendNe(
NE ***pppneLast,
NE *pne)
{
**pppneLast = pne;
*pppneLast = &(**pppneLast)->pneNext;
**pppneLast = 0;
}
void InsertNe(
NE **ppneList,
NE *pne)
{
pne->pneNext = *ppneList;
*ppneList = pne;
}
void
RemoveNe(
NE **ppneList,
NE *pne
)
{
NE *pneT;
pneT = *ppneList;
if (pne == pneT) {
// First node in the list
if (pneT->pneNext == pne) {
// Only node in the list
*ppneList = NULL;
} else {
*ppneList = pne->pneNext;
}
} else {
while (pneT && pneT->pneNext != pne) {
pneT= pneT->pneNext;
}
// Remove it from the list
pneT->pneNext = pne->pneNext;
}
free((char *)pne);
}
/* Return count of names in NE list */
int Cne(
NE *pneList)
{
int cne = 0;
NE *pne;
ForEachNe(pne, pneList)
++cne;
return cne;
}
/* for list of files in current dir by reading directory; returns 0 if there
were none.
*/
NE *PneLstInDir(
AD *pad)
{
FA fa;
NE *pneList = 0;
NE **ppneLast; /* place to hook next element */
char pth[cchPthMax];
char sz[cchFileMax+1];
DE de;
InitAppendNe(&ppneLast, &pneList);
OpenDir(&de, PthForUDir(pad, pth), faFiles | faDir);
while (FGetDirSz(&de, sz, &fa))
AppendNe(&ppneLast, PneNewNm((NM far *)sz, strlen(sz), fa));
CloseDir(&de);
return pneList; /* may be 0 */
}
/* forms list of all files which match function; pfnFAdd is called:
(*pfnFAdd)(pfi)
*/
NE *PneLstFiles(
AD *pad,
F (*pfnFAdd)(FI far *))
{
register FI far *pfi;
FI far *pfiMac;
NE *pneList = 0;
NE **ppneLast; /* place to hook next element */
InitAppendNe(&ppneLast, &pneList);
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
{
if ((*pfnFAdd)(pfi))
AppendNe(&ppneLast, PneNewNm(pfi->nmFile, cchFileMax, (FA)((pfi->fk == fkDir) ? faDir : faNormal)));
}
return pneList; /* may be 0 */
}
/* These three functions are used with PneLstFiles() above */
F FAddMDir(
FI far *pfi)
{
return !pfi->fDeleted && pfi->fMarked && pfi->fk == fkDir;
}
F FAddADir(
FI far *pfi)
{
return !pfi->fDeleted && pfi->fk == fkDir;
}
F FAddAFi(
FI far *pfi)
{
return !pfi->fDeleted;
}
NE *PneLstBroken(
AD *pad)
{
FI far *pfi;
FI far *pfiMac;
FS far *pfs;
NE *pneList = 0;
NE **ppneLast; /* place to hook next element */
InitAppendNe(&ppneLast, &pneList);
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
{
pfs = PfsForPfi(pad, pad->iedCur, pfi);
if (FBroken(pad, pfi, pfs, fTrue))
AppendNe(&ppneLast, PneNewNm(pfi->nmFile, cchFileMax, (FA)((pfi->fk == fkDir) ? faDir : faNormal)));
}
return pneList; /* may be 0 */
}
/* Return a copy of the given pne, but with zeroed pneNext field. */
NE *PneCopy(
NE *pne)
{
NE *pneNew = PneNewNm(SzOfNe(pne), strlen(SzOfNe(pne)), pne->faNe);
pneNew->u = pne->u;
pneNew->pneNext = 0;
return pneNew;
}
/* return a new ne holding the name */
NE *PneNewNm(
NM *nm,
int cchMac,
FA fa)
{
register NE *pne;
AssertF(cchMac >= 0);
/* allocate a zero-filled block; + 1 for '\0' */
pne = (NE *)PbAllocCb((unsigned)(sizeof(NE) + cchMac + 1), fTrue);
pne->faNe = fa;
SzCopyNm(SzOfNe(pne), nm, cchMac);
return pne;
}
/* free list passed */
void FreeNe(
NE *pne)
{
register NE *pneT;
while (pne != 0)
{
pneT = pne->pneNext;
free((char *)pne);
pne = pneT;
}
}
/* Reverse a list. */
NE *PneReverse(
NE *pne)
{
NE *pnePrev, *pneNext;
pnePrev = 0;
while (pne)
{
pneNext = pne->pneNext;
pne->pneNext = pnePrev;
pnePrev = pne;
pne = pneNext;
}
return pnePrev;
}
/* unmark all fi */
void UnMarkAll(
AD *pad)
{
register FI far *pfi;
register FI far *pfiMac;
AssertLoaded(pad);
/* unmark all first */
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
pfi->fMarked = fFalse;
}
/* marks the files given in lst; loop through rgfi */
void MarkList(
AD *pad,
NE *pneList,
int fDelOk) /* true -> deleted files are ok */
{
FI far *pfi;
F fExists;
int cErr = 0;
NE *pne;
AssertLoaded(pad);
UnMarkAll(pad);
ForEachNe(pne, pneList)
{
CheckForBreak();
/* mark if ok or recently deleted */
if (FLookupSz(pad, SzOfNe(pne), &pfi, &fExists) || fDelOk && fExists)
pfi->fMarked = fTrue;
else
{
Error("%s is not a file of %&P/C\n", SzOfNe(pne), pad);
cErr++;
}
}
if (cErr != 0 && (!FCanQuery((char *)0) || !FQueryUser("continue ? ")))
ExitSlm();
}
/* Mark the marked files in pne list. All names have already been shown to
* exist (or are fDeleted) by glob.
*/
void MarkFiForMarkedNeList(
AD *pad,
NE *pneList)
{
FI far *pfi;
F fExists;
NE *pne;
AssertLoaded(pad);
UnMarkAll(pad);
ForEachNe(pne, pneList)
{
if (!FMarkedNe(pne))
continue;
if (FLookupSz(pad, SzOfNe(pne), &pfi, &fExists))
pfi->fMarked = fTrue;
else
Warn("%s is not a file of %&P/C\n", SzOfNe(pne), pad);
}
}
/* binary searchs the names looking for sz;
NOTE: all names, even those deleted, are sorted.
Returns fTrue if found and not deleted (*pfExists is true in this case);
*ppfi is points to the matching name if *pfExists is fTrue; otherwise,
*ppfi points to the name before which the new name should be placed.
*/
F FLookupSz(
AD *pad,
char *sz,
FI far **ppfi,
F *pfExists)
{
register IFI ifi, ifiLim;
IFI ifiMin;
int w;
SH far *psh;
FI far *rgfi;
AssertLoaded(pad);
psh = pad->psh;
rgfi = pad->rgfi;
ifiLim = psh->ifiMac;
ifiMin = 0;
/* invariant at top of loop: name is >= ifiMin and < ifiLim */
while(ifiLim > ifiMin)
{
ifi = (IFI)((ifiMin + ifiLim) / 2);
if ((w = SzCmpiNm(sz, rgfi[ifi].nmFile, cchFileMax)) == 0)
{
*ppfi = &rgfi[ifi];
*pfExists = fTrue;
return !rgfi[ifi].fDeleted;
}
else if (w < 0)
/* name is less than ifi */
ifiLim = ifi;
else
/* name is greater than ifi; set min to one after */
ifiMin = (IFI)(ifi + 1);
}
/* name goes before ifiLim (which may be equal to ifiMac) */
*ppfi = &rgfi[ifiLim];
*pfExists = fFalse;
return fFalse;
}
/* Unmark files which aren't in pneFiles. Complain about files in pneFiles
* which weren't marked or which don't exist.
*/
void ReMarkList(
AD *pad,
NE *pneFiles,
char *szWarnNotMarked) /* Warning if file wasn't marked. */
{
FI far *pfi;
FI far *pfiMac = pad->rgfi + pad->psh->ifiMac;
NE *pne;
AssertLoaded(pad);
/* Complain about the files which weren't marked or don't exist. */
ForEachNe(pne, pneFiles)
{
F fExists;
if (FLookupSz(pad, SzOfNe(pne), &pfi, &fExists) || fExists)
{
if (!pfi->fMarked && fVerbose)
{
AssertF(szWarnNotMarked != 0);
Warn(szWarnNotMarked, pad, pfi);
}
}
else
Error("%s is not a file of %&P/C\n", SzOfNe(pne), pad);
}
/* Unmark those marked files which aren't in the list. */
for (pfi = pad->rgfi; pfi < pfiMac; pfi++)
{
char sz[cchFileMax + 1];
if (pfi->fMarked)
{
SzCopyNm(sz, pfi->nmFile, cchFileMax);
if (!PneLookup(pneFiles, sz))
pfi->fMarked = fFalse;
}
}
}
/* Return a pointer to the first ne matching sz. */
NE *PneLookup(
NE *pneList,
char *sz)
{
NE *pne;
ForEachNe(pne, pneList)
if (SzCmp(SzOfNe(pne), sz) == 0)
return pne;
return 0;
}
/* marks all of those files checked out to a particular directory */
void MarkOut(
AD *pad,
IED ied)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
AssertF(ied != iedNil);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
pfi->fMarked = (BIT)FCheckedOut(pad, ied, pfi);
}
/* marks all of those files out or out of sync for a particular directory */
void MarkOSync(
AD *pad,
IED ied,
F fChkBroken,
F fMarkGhosted)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
AssertF(ied != iedNil);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
{
register FS far *pfs = PfsForPfi(pad, ied, pfi);
switch(pfs->fm)
{
default: FatalError(szBadFileFormat, pad, pfi);
case fmGhost:
pfi->fMarked = fMarkGhosted;
break;
case fmNonExistent:
pfi->fMarked = fFalse;
break;
case fmIn:
pfi->fMarked = (BIT)(fChkBroken &&
ied == pad->iedCur &&
FBroken(pad, pfi, pfs, fFalse));
break;
case fmAdd:
case fmDelIn:
case fmDelOut:
case fmCopyIn:
case fmOut:
case fmVerify:
case fmConflict:
case fmMerge:
pfi->fMarked = fTrue;
break;
}
}
}
/* marks all of those files checked out to any directory */
void MarkAOut(
AD *pad)
{
register FI far *pfi;
register IED ied;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
{
pfi->fMarked = fFalse;
for (ied = 0; ied < pad->psh->iedMac; ied++)
{
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
FCheckedOut(pad, ied, pfi))
{
pfi->fMarked = fTrue;
break;
}
}
}
}
/* marks all of the files and directories */
void MarkAll(
AD *pad)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
pfi->fMarked = fTrue;
}
/* marks all of the directories */
void MarkAllDir(
AD *pad)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
if (pfi->fk == fkDir)
pfi->fMarked = fTrue;
}
/* marks all of the directories */
void MarkAllDirOnly(
AD *pad)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
if (pfi->fk == fkDir)
pfi->fMarked = fTrue;
else
pfi->fMarked = fFalse;
}
/* marks all of the non-deleted files and directories */
void MarkNonDel(
AD *pad)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
pfi->fMarked = (BIT)(!pfi->fDeleted);
}
void MarkDelDir(pad)
/* marks all of the deleted directories */
AD *pad;
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
{
if (pfi->fk == fkDir && PfsForPfi(pad, pad->iedCur, pfi)->fm == fmDelIn)
{
pfi->fMarked = fTrue;
}
else
{
pfi->fMarked = fFalse;
}
}
}
/* marks all broken linked files in the given ed */
void MarkBroken(
AD *pad)
{
register FI far *pfi;
FI far *pfiMac;
AssertLoaded(pad);
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
{
register FS far *pfs = PfsForPfi(pad, pad->iedCur, pfi);
pfi->fMarked = (BIT)((pfs->fm == fmIn ||
pfs->fm == fmCopyIn) &&
FBroken(pad, pfi, pfs, fFalse));
}
}
/* returns true if all FI are deleted */
F FAllFiDel(
AD *pad)
{
register FI far *pfi;
FI far *pfiMac;
for (pfi=pad->rgfi, pfiMac=pfi+pad->psh->ifiMac; pfi < pfiMac; pfi++)
if (!pfi->fDeleted)
return fFalse;
return fTrue;
}
/* returns true if all FS for the file pfi are fmNonExistent. Must have
loaded all the ed.
*/
F FAllFsDel(
AD *pad,
FI far *pfi)
{
IED ied, iedMac;
AssertLoaded(pad);
iedMac = pad->psh->iedMac;
for (ied = 0; ied < iedMac; ied++)
if (PfsForPfi(pad, ied, pfi)->fm != fmNonExistent)
return fFalse;
AssertF(iedMac == 0 || pfi->fDeleted);
return pfi->fDeleted;
}
/* assert that there is a current directory; print a message and return false
if none. If fFalse is returned, we flush the status file.
*/
F FHaveCurDir(
AD *pad)
{
AssertLoaded(pad);
if (pad->iedCur != iedNil)
{
ED *rged;
if (pad->fQuickIO) {
rged = pad->rged1;
}
else {
rged = &pad->rged[pad->iedCur];
}
if (NmCmp(pad->nmInvoker, rged->nmOwner, cchUserMax) != 0)
Warn("invoker is not owner of directory\n");
return fTrue;
}
else
{
Error(szNotEnlisted, pad, pad, pad, pad);
FlushStatus(pad);
return fFalse;
}
}
/* We simulate a directory stack with a linked list of (pthSSubDir,pthUSubDir)
* NE pairs.
*/
NE *pneDirStack = 0;
void PushDir(
AD *pad,
char *sz)
{
NE *pneSSubDir = PneNewNm(pad->pthSSubDir, CchOfPth(pad->pthSSubDir),
faNormal);
NE *pneUSubDir = PneNewNm(pad->pthUSubDir, CchOfPth(pad->pthUSubDir),
faNormal);
/* Insert pneSSubDir and pneUSubDir at the front of pneDirStack. */
pneSSubDir->pneNext = pneUSubDir;
pneUSubDir->pneNext = pneDirStack;
pneDirStack = pneSSubDir;
ChngDir(pad, sz);
}
void PopDir(
AD *pad)
{
NE *pneSSubDir;
NE *pneUSubDir;
/* Retrieve pneSSubDir and pneUSubDir. */
pneSSubDir = pneDirStack;
AssertF(pneSSubDir != 0);
pneUSubDir = pneSSubDir->pneNext;
AssertF(pneUSubDir != 0);
/* Restore current subdirectories. */
PthCopySz(pad->pthSSubDir, SzOfNe(pneSSubDir));
PthCopySz(pad->pthUSubDir, SzOfNe(pneUSubDir));
/* Unlink them from the pneDirStack and free them. */
pneDirStack = pneUSubDir->pneNext;
pneUSubDir->pneNext = 0;
FreeNe(pneSSubDir);
}
/* Change the notion of current directory in both the system and user domains.
* Sz can be an absolute or relative path, including . and .., but not
* wildcards.
*/
void ChngDir(
AD *pad,
char *sz)
{
if (*sz == '/')
{
int cchUSub = CchOfPth(pad->pthUSubDir);
if (cchUSub > 1)
{
int cchDiff;
/* Remove pthUSubDir from both pthUSubDir and
* pthSSubDir. If pthUSubDir is a proper suffix of
* pthSSubDir, then we will leave pthSSubDir with more
* than just "/". Example:
* pthSSubDir = "/a/b/c/d"
* pthUSubDir = "/c/d"
* ChngDir(pad, "/");
* pthSSubDir = "/a/b"
* pthUSubDir = "/"
*/
cchDiff = CchOfPth(pad->pthSSubDir) - cchUSub;
AssertF(cchDiff >= 0 &&
PthCmpCb(pad->pthSSubDir + cchDiff, pad->pthUSubDir, cchUSub) == 0 &&
pad->pthSSubDir[cchDiff] == '/');
if (cchDiff > 0)
pad->pthSSubDir[cchDiff] = 0;
else
PthCopySz(pad->pthSSubDir, "/");
PthCopySz(pad->pthUSubDir, "/");
}
else
AssertF(PthCmp(pad->pthUSubDir, "/") == 0);
++sz;
}
/* Process each component of the pathname. */
for (;;)
{
char *pchSl = index(sz, '/');
/* Null terminate this component. */
if (pchSl)
*pchSl = 0;
if (strcmp(sz, ".") == 0)
/* Ignore . */
;
else if (strcmp(sz, "..") == 0)
{
/* Remove last component of both pthSSubDir and
* pthUSubDir. Note that since pthUSubDir is a
* suffix of pthSSubDir, we only need to check that
* pthUSubDir will remain in the tree.
*/
char *pchS;
char *pchU;
if (PthCmp(pad->pthUSubDir, "/") == 0)
FatalError(".. would leave the project tree\n");
/* Find last component of path */
pchS = rindex(pad->pthSSubDir, '/');
pchU = rindex(pad->pthUSubDir, '/');
/* System and user's last components should be
* identical.
*/
AssertF(pchS && pchU && *pchU && PthCmp(pchS, pchU)==0);
/* If just "/component", advance pchX past '/' */
pchS += (pchS == pad->pthSSubDir);
pchU += (pchU == pad->pthUSubDir);
AssertF(*pchU && *pchS);
ClearLpbCb((char far *)pchS,
cchPthMax - (pchS - pad->pthSSubDir));
ClearLpbCb((char far *)pchU,
cchPthMax - (pchU - pad->pthUSubDir));
}
else if (*sz)
{
/* Append component to pthSSubDir and pthUSubDir.
* Since pthUSubDir is a suffix of pthSSubDir we need
* only check pthSSubDir for overflow.
*/
if (CchOfPth(pad->pthSSubDir) + strlen(sz) + 2 > sizeof pad->pthSSubDir)
FatalError("subdirectory path too long at \"%s\"\n", sz);
if (CchOfPth(pad->pthSSubDir) > 1)
PthCatSz(pad->pthSSubDir, "/");
PthCatSz(pad->pthSSubDir, sz);
if (CchOfPth(pad->pthUSubDir) > 1)
PthCatSz(pad->pthUSubDir, "/");
PthCatSz(pad->pthUSubDir, sz);
}
/* Restore slash at end of component, and advance to next
* component. */
if (pchSl)
{
*pchSl = '/';
sz = pchSl + 1;
}
else
break;
}
}
/* calls malloc and aborts if we ran out of memory; zeros the block before
returning a pointer to it. REVIEW.
*/
char *PbAllocCb(
unsigned cb,
int fClear)
{
char *pb;
if ((pb = malloc(cb)) == 0)
FatalError("out of memory\n");
if (fClear)
ClearPbCb(pb, cb);
return pb;
}
/* Calls malloc (if pb == 0) or remalloc and aborts if we ran out of memory.
* To free the memory, call free() (ug).
*/
char *PbReallocPbCb(
char * pb,
unsigned cbNewSize)
{
if (!(pb = (!pb ? malloc(cbNewSize) : realloc(pb, cbNewSize))))
FatalError("out of memory\n");
return pb;
}
#define cdayWeek 7
static char *rgszDay[cdayWeek] = { "Sun",
"Mon",
"Tue",
"Wed",
"Thu",
"Fri",
"Sat" };
/* return short or long version of time;
* 23 for long, 14 for short.
*
* short - 11-06-85@19:57
* long - 11-06-85@19:57:16 (Tue)
*/
char *SzTime(
TIME time)
{
static char szUnknown[] = "(unknown)";
static char szTTime[26];
struct tm *tmT;
if (timeNil == time)
return (szUnknown);
if ((tmT = localtime(&time)) == NULL)
return (szUnknown);
if (fVerbose)
{
SzPrint(szTTime,"%02d-%02d-%02d@%02d:%02d:%02d (%s)",tmT->tm_mon+1,tmT->tm_mday,
tmT->tm_year,tmT->tm_hour,tmT->tm_min,tmT->tm_sec,
rgszDay[tmT->tm_wday]);
}
else
{
SzPrint(szTTime,"%02d-%02d-%02d@%02d:%02d",tmT->tm_mon+1,tmT->tm_mday,
tmT->tm_year,tmT->tm_hour,tmT->tm_min);
}
return szTTime;
}
/* return short or long version of time;
* 23 for long, 14 for short.
* But year is first so it is sortable.
*
* short - 85-11-06@19:57
* long - 85-11-06@19:57:16 (Tue)
*/
char *SzTimeSortable(
TIME time)
{
static char szUnknown[] = "(unknown)";
static char szTTime[26];
struct tm *tmT;
if (timeNil == time)
return (szUnknown);
if ((tmT = localtime(&time)) == NULL)
return (szUnknown);
if (fVerbose)
{
SzPrint(szTTime,"%02d-%02d-%02d@%02d:%02d:%02d (%s)",tmT->tm_year,tmT->tm_mon+1,tmT->tm_mday,
tmT->tm_hour,tmT->tm_min,tmT->tm_sec,
rgszDay[tmT->tm_wday]);
}
else
{
SzPrint(szTTime,"%02d-%02d-%02d@%02d:%02d",tmT->tm_year,tmT->tm_mon+1,tmT->tm_mday,
tmT->tm_hour,tmT->tm_min);
}
return szTTime;
}
/* This predicate tests the first 50 characters in a file to see if the
* file is binary or not. It returns fTrue if any nonASCII characters
* or unusual control codes are found.
*/
F FBinaryPth(
PTH pth[cchPthMax])
{
#define cchBinMax 50
#define BYTE_ORDER_MARK 0xFEFF
char rgb[cchBinMax];
register char *pbMac;
register char *pb;
MF *pmf;
struct _stat st;
if (!FStatPth(pth, &st) || (st.st_mode&S_IFREG) == 0)
/* not present, or not regular */
return fFalse;
if ((pmf = PmfOpen(pth, omReadOnly, fxNil)) == 0)
return fFalse;
pbMac = CbReadMf(pmf, (char far *)rgb, cchBinMax) + rgb;
CloseMf(pmf);
if ((*TestForUnicode) (rgb, pbMac - rgb, NULL))
return fUnicode;
for (pb = rgb; pb < pbMac; pb++)
{
switch(*pb)
{
default:
/* special case ^Z at end of file...*/
if (!(*pb == '\032' && pb == pbMac - 1) &&
(*pb < ' ' || *pb > '~'))
{
return fTrue;
}
/* else fall through */
case '\n':
case '\r':
case '\b':
case '\f':
case '\t':
/* printable chars plus allowable control chars */
break;
}
}
// It looks like text so far. Add a final test for the COFF
// library signature.
if (!strncmp(rgb, IMAGE_ARCHIVE_START, IMAGE_ARCHIVE_START_SIZE))
return(fTrue);
{
PIMAGE_DOS_HEADER pHdr = (PIMAGE_DOS_HEADER)rgb;
if ((pHdr->e_magic == IMAGE_NT_SIGNATURE) ||
(pHdr->e_magic == IMAGE_DOS_SIGNATURE) ||
(pHdr->e_magic == IMAGE_OS2_SIGNATURE) ||
(pHdr->e_magic == IMAGE_VXD_SIGNATURE)
)
return(fTrue);
}
return fText;
}
/* Fills the given buffer with the names of those who have the file checked
* out. If the names don't all fit, tries to put an ellipsis in.
*/
F FOutUsers(
char *sz,
int cchSz,
AD *pad,
FI far *pfi)
{
IED ied, iedMac;
int cchUsed;
register char *pch;
F fFound = fFalse;
for (ied = 0, iedMac = pad->psh->iedMac; ied < iedMac; ied++)
{
if ((!FIsFreeEdValid(pad->psh) || !pad->rged[ied].fFreeEd) &&
FCheckedOut(pad,ied,pfi))
{
cchUsed = strlen(sz);
pch = sz + cchUsed;
if (cchSz - cchUsed < cchUserMax + 2)
{
if (cchSz - cchUsed > 3)
SzPrint(pch, "...");
fFound = fTrue;
break;
}
SzPrint(pch, fFound ? ", %&O" : "%&O", pad, ied);
fFound = fTrue;
}
}
return fFound;
}
/* return fTrue if any names in the list have associated TDs. */
F FAnyFileTimes(
NE *pneList)
{
register NE *pne;
for (pne = pneList; pne; pne = pne->pneNext)
if (pne->u.tdNe.tdt != tdtNone)
return fTrue;
return fFalse;
}
private F FMatchPart(
char *,
char *,
int);
/* DOS style pattern match. The DOS strategy is to match the file parts and
* the extensions separately. * and ? are wildcards, * fills the rest of the
* compiled pattern with ?s. ? even matches nulls, thus "????" matches "foo".
* Case insensitive too.
*/
F FMatch(
char *sz,
char *szPat)
{
char *szExt = "";
char *szPatExt = "";
char *pchDot = index(sz, '.');
char *pchPatDot = index(szPat, '.');
F fMatch;
if (pchDot)
{
*pchDot = 0;
szExt = pchDot + 1;
}
if (pchPatDot)
{
*pchPatDot = 0;
szPatExt = pchPatDot + 1;
}
fMatch = FMatchPart(sz, szPat, cchDosName) &&
FMatchPart(szExt, szPatExt, cchDosExt);
if (pchDot)
*pchDot = '.';
if (pchPatDot)
*pchPatDot = '.';
return fMatch;
}
#define ChLower(ch) (isupper(ch) ? tolower(ch) : (ch))
private F FMatchPart(
char *sz,
char *szPat,
int ichMac)
{
char rgch[cchDosName+1];
char rgchPat[cchDosName+1];
int ich;
/* Copy sz into rgch, compile szPat into rgchPat. Yes, we could
* compile the pattern just once for any given run of matchings, but
* then we'd have to have a different interface for DOS vs UNIX code.
*/
LszCopyCb((char far *)rgch, sz, ichMac); /* zeroes remainder */
ClearLpbCb((char far *)rgchPat, ichMac);
for (ich = 0; ich < ichMac && szPat[ich]; ich++)
{
if (szPat[ich] == '*')
{
for ( ; ich < ichMac; ich++)
rgchPat[ich] = '?';
break;
}
rgchPat[ich] = szPat[ich];
}
rgchPat[ich] = '\0';
for (ich = 0; ich < ichMac; ich++)
{
if ((rgchPat[ich] == '\0') && (rgch[ich] == '\0')) return fTrue;
if (ChLower(rgch[ich]) == ChLower(rgchPat[ich])) continue ;
if (rgchPat[ich] == '?') continue;
return fFalse;
}
return fTrue;
}
F FWildSz(
char *sz)
{
return index(sz, '*') || index(sz, '?');
}
PV PvGlobal(
AD *pad)
{
return pad->psh->pv;
}
PV PvLocal(
AD *pad,
IED ied)
{
ED *rged;
AssertLoaded(pad);
AssertF(ied != iedNil);
if (pad->fQuickIO) {
rged = pad->rged1;
}
else {
rged = &pad->rged[ied];
}
return (rged->fNewVer) ? PvIncr(PvGlobal(pad)) : PvGlobal(pad);
}
PV PvIncr(
PV pv)
{
/* Increment project version number. 1.1.1 -> 1.1.2; 1.2.0 -> 1.3.1. */
if (pv.rup == 0)
pv.rmm++;
pv.rup++;
/* Clear the project version name. */
ClearPbCb(pv.szName, cchPvNameMax + 1);
return pv;
}
char *SzForPv(
char *sz,
PV pv,
F fNameToo)
{
SzPrint(sz, pv.rup ? "%d.%02d.%02d" : "%d.%02d", pv.rmj, pv.rmm, pv.rup);
if (fNameToo && pv.szName[0])
{
strcat(sz, " ");
strcat(sz, pv.szName);
}
return sz;
}
/* Return -1, 0, 1 if pv1 is <, =, or > pv2. */
int CmpPv(
PV pv1,
PV pv2)
{
if (pv1.rmj < pv2.rmj)
return -1;
if (pv1.rmj > pv2.rmj)
return 1;
if (pv1.rmm < pv2.rmm)
return -1;
if (pv1.rmm > pv2.rmm)
return 1;
if (pv1.rup == pv2.rup)
return 0;
/* special case for no revision number (i.e. 2.28 > 2.28.01) */
if (pv1.rup == 0)
return 1;
if (pv2.rup == 0 || pv1.rup < pv2.rup)
return -1;
AssertF(pv1.rup > pv2.rup);
return 1;
}
F FIsF(
int w)
{
return w == (int)fTrue || w == (int)fFalse;
}
/* return T if:
file is a version.h and doesn't exist or
file is not a version.h and:
on Xenix: the file is not linked to the src dir
on DOS: the file is r/w
*/
F FBroken(
AD *pad,
FI far *pfi,
FS far *pfs,
int fDelOk)
{
struct _stat stUFile;
PTH pthUFile[cchPthMax];
AssertLoaded(pad);
AssertF(pad->iedCur != iedNil && pfi != 0 && pfs != 0);
switch(pfs->fm)
{
default: FatalError(szBadFileFormat, pad, pfi);
case fmGhost:
case fmOut:
case fmMerge:
case fmVerify:
case fmConflict:
AssertF(!pfi->fDeleted);
case fmNonExistent: /* not necc delete because of defect */
case fmDelOut: /* not necc delete because of defect */
return fFalse;
case fmDelIn: /* not necc delete because of defect */
case fmIn:
case fmCopyIn:
if (!FStatPth(PthForUFile(pad, pfi, pthUFile), &stUFile))
/* gone but we are not going to destroy anything */
return !fDelOk;
break;
case fmAdd:
/* broken if non-directory exists */
return FStatPth(PthForUFile(pad, pfi, pthUFile), &stUFile) &&
((stUFile.st_mode&S_IFDIR) == 0 || pfi->fk != fkDir);
}
/* file exists for fm...In; test for write permission */
if (pfi->fk == fkDir && (stUFile.st_mode&S_IFDIR) == 0)
{
Error("directory %s is now a file?\n", pthUFile);
return fTrue;
}
if (pfi->fk != fkDir && (stUFile.st_mode&S_IFDIR) != 0)
{
Error("regular file %s is now a directory?\n", pthUFile);
return fTrue;
}
if ((stUFile.st_mode&S_IFDIR) != 0)
/* directories are never broken */
return fFalse;
if (pfi->fk == fkVersion)
/* neither are version.h files */
return fFalse;
return (stUFile.st_mode&S_IWRITE) != 0;/* return true if writable */
}
/*
* Returns fTrue if the given name contains only valid filename characters.
*/
F FIsValidFileNm(
NM *nm)
{
register char *pch;
while (*nm != '\0' && (isalnum(*nm) || ((pch = index("`~!@#$%^&()-_'.}{", *nm)) != 0)))
{
nm++;
}
return *nm == '\0';
}
/*
* Make sure the given name is a valid file or dir, not a protected
* SLM file name and not a DOS device. "." and ".." are also disallowed.
*
* This checking is a superset of FIsValidFileNm().
*
*/
BOOL
ValidateFileName(
char *szFile,
BOOL fAbortOnSystemFile
)
{
char *pch;
unsigned cchName;
unsigned isz;
F fDev;
static char *rgszSLMNames[] = { "slm.ini",
"local.scr",
"status.slm",
"iedcache.slm",
"cookie" };
static char *rgszDOSNames[] = { "AUX",
"CLOCK$",
"CON",
"NUL",
"PRN" };
#define cSLMNames (sizeof(rgszSLMNames)/sizeof(char *))
#define cDOSNames (sizeof(rgszDOSNames)/sizeof(char *))
/* Check for szName == "<dev><n>". Any device name with a
* colon will have been filtered by FIsValidFileNm, so no
* checking is done here.
*/
#define FIsDevN(szDev, szName) (strlen(szName) == 4 && \
_strnicmp(szDev, szName, 3) == 0 && \
isdigit(szName[3]))
pch = index(szFile, '.');
/* Make sure file name conforms to cchDosName.cchDosExt.
* These checks disallow "." and "..".
*/
if (pch != NULL) {
if (index(pch + 1, '.') != NULL)
FatalError("\"%s\": names cannot contain more"
" than one '.'\n", szFile);
/* Check length of extension */
if (strlen(pch + 1) > cchDosExt)
FatalError("\"%s\": extension must have %u or fewer"
" characters\n", szFile, cchDosExt);
cchName = pch - szFile;
}
else
cchName = strlen(szFile);
/* Check length of base name */
if (cchName < 1 || cchName > cchDosName)
FatalError("\"%s\": base name must have between 1 and %u"
" characters\n", szFile, cchDosName);
if (!FIsValidFileNm(szFile))
FatalError("\"%s\" is not a valid file or directory name.\n",
szFile);
/* Check for protected SLM names */
for (isz = 0; isz < cSLMNames; isz++) {
if (_stricmp(szFile, rgszSLMNames[isz]) == 0)
if (fAbortOnSystemFile)
FatalError("the name \"%s\" would conflict with"
" SLM system files.\n", szFile);
else
return(FALSE);
}
/* Check for DOS devices, with or w/o extension (relies on
* pch already being set).
*/
if (pch != NULL)
*pch = '\0';
for (isz = 0, fDev = fFalse; !fDev && isz < cDOSNames; isz++)
fDev = _stricmp(szFile, rgszDOSNames[isz]) == 0;
fDev |= FIsDevN("LPT", szFile) || FIsDevN("COM", szFile);
/* Restore the extension if there was one */
if (pch != NULL)
*pch = '.';
if (fDev)
if (fAbortOnSystemFile)
FatalError("the name \"%s\" would conflict with"
" a DOS device.\n", szFile);
else
return(FALSE);
return(TRUE);
#undef cSLMNames
#undef cDOSNames
#undef FIsDevN
}