2020-09-30 17:12:29 +02:00

402 lines
11 KiB
C

// this is the main file for the slmck function
#include "precomp.h"
#pragma hdrstop
#include <version.h>
EnableAssert
#if (rup < 10)
#define ruppad "000"
#elif (rup< 100)
#define ruppad "00"
#elif (rup < 1000)
#define ruppad "0"
#else
#define ruppad
#endif
#define VERSION_STR2(a,b,c) " " #a "." #b "." ruppad #c
#define VERSION_STR(a,b,c) VERSION_STR2(a,b,c)
const char szVersion[] =
"Microsoft (R) Source Library Manager Diagnostic (SLMCK)\nVersion" VERSION_STR(rmj, rmm, rup) "\nCopyright (C) Microsoft Corp 1985-1994. All rights reserved.\n\n";
char * szOp = "slmck";
AD adGlobal; // this is the ad that will be used throughout
F fVerbose;
F fNeedInter = fFalse;
private F FIsVer1(SD *);
FT rgftSlmck[] = {
{ '&', atFlag, flagErrToOut },
{ 'a', atFlag, flagAll },
{ 'f', atFlag, flagForce },
{ 'g', atFlag, flagCkGlobal },
{ 'h', atHelp, 0 },
{ '?', atHelp, 0 },
{ 'i', atFlag, flagCkRc },
{ 'l', atFlag, flagCkLog },
{ 'n', atFlag, flagCkUpgrade },
{ 'o', atFlag, flagCkOverride },
{ 'r', atFlag, flagRecursive },
{ 'u', atFlag, flagCkUser },
{ 'v', atFlag, flagVerbose },
{ 'w', atWindows, flagWindowsQuery },
{ 'z', atFlag, flagCkIgnDrive },
{ 's', atSlmRoot, 0 },
{ 'p', atFlag, 0 },
{ 'c', atComment, 0 },
{ 0, 0, 0 }
};
ECMD ecmdSlmck = {
cmdSlmck,
"slmck",
"%s [-?&fhvwargilnouz] [-s SLM-location] [-p proj[/subdir]] [-c comment]\n",
"-v (verbose) SLM tells you what is happening as the command proceeds.\r\n"
"-w (Windows) prompts using a dialog box instead of the console.\r\n"
"-f (force) answers all SLM queries \"Yes\" (no user input; no safeguards)\r\n"
"-a (all) applies the command to all directories of the project\r\n"
"-r (recursive) applies the command to a given directory and to every\r\n"
" subdirectory beneath it (no patterns with slmck!).\r\n"
"-g (global) check the global or master state of the project [NOTE:\r\n"
" this flag is intended only for use by project administrators!].\r\n"
"-i (ini) check the SLM initialization file (slm.ini).\r\n"
"-l (log) check the SLM log file (but don't necessarily fix it).\r\n"
"-n (new) upgrade the project to a new SLM format\r\n"
"-o (override) override the status file lock\r\n"
"-u (user) check the user's directory (default)\r\n"
"-s, -p If you are running slmck from a directory not enlisted in the project,\r\n"
" use -s to specify the network location where the project is located (in\r\n"
" the format: -s \\\\server\\share), and -p to specify the project's name.\r\n"
" Otherwise, you don't need to include these flags.\r\n"
"-& redirects stderr to stdout, so that all SLM messages can be redirected\r\n"
" together (to a file, or to a printer, etc.)\r\n",
rgftSlmck,
atOptProjOptSubDir,
fTrue,
fglNone,
0,
0,
0,
"- checks the integrity of an SLM project"
};
F (*rgpfnFIsVerN[])(SD *) = { // predicates for checking version
FIsVer1,
FIsVer234or5, // Version 2
FIsVer234or5, // Version 3
FIsVer234or5, // Version 4
FIsVer234or5 // Version 5
};
F (*rgpfnFLockVerN[])(AD *, SD *, char *) = { // predicates for checking SLM lock
0,
FVer2Lock,
FVer2Lock,
FVer2Lock,
FVer2Lock
};
F (*rgpfnFBlockVerN[])(AD *, SD *) = { // blocking functions
0,
FVer2Block,
FVer2Block,
FVer2Block,
FVer2Block
};
F (*rgpfnFSemanticsVerN[])(AD *, SD *) = { // Semantics checker
0,
FVer2Semantics,
FVer2Semantics,
FVer2Semantics,
FVer2Semantics
};
void (*rgpfnUpgradeVerN[])(AD *, SD *) = { // version conversion
0,
Ver3Upgrade,
Ver4Upgrade,
Ver5Upgrade
};
int SlmExceptionFilter(DWORD, PEXCEPTION_POINTERS);
void __cdecl
main(
int iszMac,
char *rgsz[])
{
PTH pthSRootT[cchPthMax];
// Make outputs raw instead of cooked, to avoid CRCRLF line separation
_setmode(_fileno(stdout), O_BINARY);
_setmode(_fileno(stderr), O_BINARY);
InitErr();
InitPerms();
InitAd(&adGlobal);
InitPath();
GetRoot(&adGlobal);
PthCopy(pthSRootT, adGlobal.pthSRoot); // For comparison below
GetUser(&adGlobal);
FLoadRc(&adGlobal);
adGlobal.pecmd = &ecmdSlmck;
ParseArgs(&adGlobal, rgsz, iszMac);
fVerbose = (adGlobal.flags & flagVerbose) != 0;
if (fVerbose)
PrErr(szVersion);
InitQuery(adGlobal.flags&(flagForce|flagWindowsQuery));
if (FEmptyNm(adGlobal.nmProj)) {
Error("must specify a project name\n");
Usage(&adGlobal);
}
ValidateProject(&adGlobal);
if ((adGlobal.flags&(flagCkGlobal|flagCkUser|flagCkRc|flagCkLog)) == 0) {
// neither -g, -i, -l, or -u; make like -u
Warn("assuming -u (checking user's files)\n");
adGlobal.flags |= flagCkUser;
}
// pthSRoot might have been changed
if (fVerbose && getenv("SLM") != 0 && PthCmp(pthSRootT, adGlobal.pthSRoot) != 0) {
Error("SLM root defined differently in SLM variable and %s file:\n", pthSlmrc + 1);
PrErr("\t%!s vs. %!s; %!s used\n\n", pthSRootT, adGlobal.pthSRoot, adGlobal.pthSRoot);
}
if (adGlobal.flags & flagAll)
ChngDir(&adGlobal, "/");
if (adGlobal.flags & flagCkIgnDrive &&
(adGlobal.flags & (flagCkRc|flagCkUser) == 0))
Warn("-z ignored without -i or -u\n");
CheckForBreak();
__try {
if (FCkSRoot(&adGlobal) && FCkDir(&adGlobal)) {
if (adGlobal.flags & flagAll) {
CreatePeekThread(&adGlobal);
}
if (adGlobal.flags & (flagAll|flagRecursive)) {
CkSubDir(&adGlobal);
}
else
FlushStatus(&adGlobal);
}
} __except(SlmExceptionFilter(GetExceptionCode(),
GetExceptionInformation())) {
PrErr("SLMCK ERROR - UnHandled Exception %08x\nSLM aborting.\n",
GetExceptionCode());
ExitSlm();
}
if (fNeedInter)
Error("Some things may not have been fixed because they\n"
"\trequire interaction. Run slmck again without -f\n");
if (fVerbose)
PrErr("Slmck complete\n");
ExitSlm();
//NOTREACHED
}
// Perform Slmck operation on a single directory. Return fTrue if operation
// succeeded and status file was left loaded.
F
FCkDir(
AD *pad)
{
if ((pad->flags&flagCkGlobal) != 0 && !FCkGlobal(pad))
return fFalse;
if (pad->flags&(flagCkLog|flagCkRc|flagCkUser)) {
if (!FLoadStatus(pad, lckAll, flsNone))
return fFalse;
} else {
// not -i, -l or -u; may need status for recursive operation
if ((pad->flags&(flagAll|flagRecursive)) != 0)
return FLoadStatus(pad, lckNil, flsJustFi);
else
return fFalse;
}
if (pad->flags&flagCkLog)
CkLog(pad);
if (pad->flags&flagCkRc)
CkRcAndEd(pad);
else if (pad->flags&flagCkUser)
CheckUser(pad);
return fTrue;
}
// Recursively check all subdirectories of a directory.
void
CkSubDir(
AD *pad)
{
NE *pneList, *pne;
F fOk;
int FAddADir();
pneList = PneLstFiles(pad, FAddADir);
FlushStatus(pad);
fOk = fTrue;
ForEachNe(pne, pneList) {
CheckForBreak();
ChngDir(pad, SzOfNe(pne));
if (FCkDir(pad))
CkSubDir(pad);
ChngDir(pad, "..");
}
FreeNe(pneList);
}
// perform all correcting of global status for a single directory
F
FCkGlobal(
AD *pad)
{
SD sd;
int verCur; // Version of SLM in which file was made
NM nmLocker[cchUserMax];
PrErr("Checking project files for %&P/C\n", pad);
if (!FCkMaster(pad) || !FLoadSd(pad, &sd)) {
PrErr("\n");
return fFalse;
}
// status file is now in a buffer. Want to determine version #
// and whether fLocked in the shortest possible time since FLoadSd
// opens the status file in exclusive mode.
if ((verCur = VerGet(&sd)) == 0) {
FlushSd(pad, &sd, fTrue);
Error("Can't determine status file version number!\n");
return fFalse;
}
if (1 == verCur) {
FlushSd(pad, &sd, fTrue);
Error("Version 1 status files are no longer supported. Ask your project\n"
"administrator to use version 2.00 slmck to upgrade to version 3, 4 or 5!\n");
return (fFalse);
}
if (verCur < VERSION_COMPAT_MAC && !(pad->flags&flagCkUpgrade)) {
Error("%&P/C should be upgraded to the SLM 1.9 format.\n"
"Ask your project administrator to run slmck -gna (New version upgrade)\n",
pad);
FlushSd(pad, &sd, fTrue);
return fFalse;
}
if ((*rgpfnFLockVerN[verCur-1])(pad, &sd, nmLocker)) {
// status file is locked, flush before anything else
if (!(pad->flags & flagCkOverride))
FlushSd(pad, &sd, fTrue);
AssertF(cchUserMax == 14);
Error("Status file for %&P/C is locked by %.14s\n", pad, nmLocker);
if (!(pad->flags & flagCkOverride)) {
Error("use -o switch to override lock\n\n");
return fFalse;
}
else
PrErr("Overriding lock\n");
}
if (!FInitScript(pad, lckAll)) {
// put init script here, after test for lock; Abort/Run in FlushSd
FlushSd(pad, &sd, fTrue);
PrErr("\n");
return fFalse;
}
// Block the file
if (!(*rgpfnFBlockVerN[verCur-1])(pad, &sd)) {
FlushSd(pad, &sd, fTrue);
PrErr("\n");
return fFalse;
}
// check semantics and upgrade to next version
for (;;) {
if (!(*rgpfnFSemanticsVerN[verCur-1])(pad, &sd)) {
FlushSd(pad, &sd, fTrue);
PrErr("\n");
return fFalse;
}
if (verCur >= VERSION || !(pad->flags&flagCkUpgrade))
break;
(*rgpfnUpgradeVerN[verCur - 1])(pad, &sd);
++verCur;
}
FlushSd(pad, &sd, fFalse);
if (fVerbose)
PrErr("Check for %&P/C complete\n\n", pad);
return fTrue;
}
// This routine must decide the version number. Since there are only two
// versions at present, we check if the the version as specified
// in the SH is correct. In general we may want to scan the status file
// for differences between versions.
// NB: this function returns 0 if it is unable to determine the version.
int
VerGet(
SD *psd)
{
int verProb; // guess of version number
verProb = ((SH far *)psd->hpbStatus)->version;
if (verProb <= VERSION && verProb > 0 && (*rgpfnFIsVerN[verProb - 1])(psd))
return verProb;
else
return 0; // have to somehow determine version
}
// tries to determine if stuff in status buffer is a version 1 status file.
private F
FIsVer1(
SD *psd)
{
return (((SH *)psd->hpbStatus)->version == 1);
}