402 lines
11 KiB
C
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);
|
|
}
|