// this is the main file for the slmck function #include "precomp.h" #pragma hdrstop #include 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); }