VerifyDFImpl.Mesa
last edit February 4, 1983 11:36 am by Schmidt
last edit April 12, 1983 12:33 pm by Paul Rovner
last edit May 23, 1983 6:51 pm by Russ Atkinson
Usage:
VerifyDF dffile
where there is at least one "+" in the df file
or alternatively
VerifyDF bcdfile
or alternatively
VerifyDF dffile bcdfile
if no extension is present on the first argument, assume it is a DF file; an optional /f after VerifyDF and before the args causes verifydf to print the flattened DF file on Flattened.dffile.DF
note that calling any procedure in VerifyDFInterface will cause a start trap that will register "VerifyDF"; it is ok to call this from the Simple Exec
DIRECTORY
BcdOps: TYPE USING [BcdBase],
Commander: TYPE USING[CommandProc, Register],
CWF: TYPE USING
[SetCode, SetWriteProcedure, SWF1, SWF2, SWF3, WF0, WF1, WF2, WF3, WF4, WFCR],
DFSubr: TYPE USING
[AllocateDFSeq, Criterion, DF, DFSeq, FlattenDF, FreeDFSeq, InsertCRs, LookupDF, NextDF, ReadInDir, SortByCap, SortByFileName, StripLongName, TooManyEntries, WriteOut],
Directory: TYPE USING [Error, Handle, ignore, Lookup, UpdateDates],
File: TYPE USING [Capability, nullCapability, read, Unknown],
FQ: TYPE USING[FileQuery, Result],
IFSFile: TYPE USING [CantOpen, Close, Error, FileHandle, GetTimes, UnableToLogin],
IO: TYPE USING
[Handle, PutChar, PutF, rope, UserAborted, STREAM, UserAbort, ResetUserAbort, PutRope],
IOMisc: TYPE USING[AskUser],
LeafSubr: TYPE USING
[Open, PrintLeafAccessFailure, PrintLeafProblem, RemoteMap, StopLeaf],
LongString: TYPE USING [AppendString, EquivalentString],
ProcBcds: TYPE USING
[InnardsObject, InstallAddressesBcd, PrintDepends, ProcDep, ProcMod, ReadInSegmentsBcd, UnstallBcd],
Resource: TYPE USING[Acquire, Release, AbortProc],
Rope: TYPE USING[Fetch, Find, ROPE, Text, Cat],
RopeInline: TYPE USING[InlineFlatten],
Space: TYPE USING
[Create, Delete, Handle, LongPointer, Map, nullHandle, Unmap, virtualMemory],
UnsafeSTP: TYPE USING [Error],
STPSubr: TYPE USING [StopSTP],
Stream: TYPE USING [Delete, Handle],
Subr: TYPE USING
[AbortMyself, AllocateString, Any, CheckForModify, CopyString, debugflg, EndsIn, errorflg, FileError, FreeString, GetCreateDateWithSpace, GetRemoteFilenameProp, MakeTTYProcs, NewStream, PackedTime, PrintGreeting, strcpy, SubrStop, TTYProcs, Write],
Time: TYPE USING [Current],
TimeStamp: TYPE USING[Null, Stamp],
UECP: TYPE USING[Argv, Parse],
VerifyDFInterface: TYPE USING [];
VerifyDFImpl: PROGRAM
IMPORTS
Commander, CWF, DFSubr, Directory, File, FQ, IFSFile, IO, IOMisc, LeafSubr, LongString, ProcBcds, Resource, Rope, RopeInline, Space, STP: UnsafeSTP, STPSubr, Stream, Subr, Time, UECP
EXPORTS VerifyDFInterface = {
MDS usage!!
stdout: IO.Handle;
end of mds
MAXFILES: CARDINAL = 1200;
NPAGESINHUGEZONE: CARDINAL = 500;
abortProc: Resource.AbortProc = TRUSTED{
PROC [data: REF ANY] RETURNS[abort: BOOL];
in: IO.STREAM = NARROW[data, IO.STREAM];
abort ← in.UserAbort[];
IF abort THEN in.ResetUserAbort[];
};
Main: Commander.CommandProc = TRUSTED {
PROC [cmd: Handle]
ENABLE UNWIND => [] ← Resource.Release[$VerifyDF];
h: Subr.TTYProcs;
in: IO.Handle = cmd.in;
out: IO.Handle = cmd.out;
success: BOOLFALSE;
otherOwner: Rope.ROPENIL;
[success, otherOwner] ← Resource.Acquire[resource: $VerifyDF,
owner: "VerifyDF",
waitForIt: FALSE];
IF NOT success
THEN {
out.PutRope[Rope.Cat["Waiting for ", otherOwner, " to finish..."]];
[success, ] ← Resource.Acquire[resource: $VerifyDF,
owner: "VerifyDF",
waitForIt: TRUE,
abortProc: abortProc,
abortProcData: in
];
IF NOT success THEN {
out.PutRope["ABORTED\n"];
RETURN;
} ELSE out.PutRope["proceeding\n"];
};
h ← Subr.MakeTTYProcs[in, out, NIL, MyConfirm];
VerifyDFUsingProcs[h, cmd.commandLine];
[] ← Resource.Release[$VerifyDF];
};
VerifyDFUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = {
strdffilename: LONG STRING ← Subr.AllocateString[100];
strbcdfilename: LONG STRING ← Subr.AllocateString[100];
starttime: Subr.PackedTime;
printFlattened: BOOLFALSE;
checkForOverwrite: BOOLTRUE;
argv: UECP.Argv ← UECP.Parse[commandLine];
dffilename, bcdfilename, firstname, tok: Rope.ROPE;
parm: CARDINAL;
flat: Rope.Text;
Cleanup: PROC = {
STPSubr.StopSTP[];
LeafSubr.StopLeaf[];
Subr.FreeString[strdffilename];
Subr.FreeString[strbcdfilename];
Subr.SubrStop[];
};
{
ENABLE {
IFSFile.Error, IFSFile.UnableToLogin => {
LeafSubr.PrintLeafProblem[reason];
GOTO leave;
};
IFSFile.CantOpen => {
LeafSubr.PrintLeafAccessFailure[reason];
GOTO leave;
};
STP.Error => {
CWF.WF0["FTP Error. "L];
IF error ~= NIL THEN
CWF.WF2["message: %s, code %u in Stp.Mesa\n"L, error, @code];
GOTO leave;
};
Subr.AbortMyself, IO.UserAborted => {
h.out.ResetUserAbort[];
CWF.WF0["VerifyDF Aborted.\n"L];
GOTO leave;
};
UNWIND => Cleanup[];
};
set up WF stuff
stdout ← h.out;
[] ← CWF.SetWriteProcedure[MyPutChar];
Subr.debugflg ← Subr.errorflg ← FALSE;
Subr.PrintGreeting["VerifyDF"L];
starttime ← Time.Current[];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
parm ← 1;
DO
IF parm >= argv.argc THEN GOTO usage;
tok ← argv[parm];
SELECT tok.Fetch[0] FROM
'-, '/ =>
SELECT tok.Fetch[1] FROM
'a, 'A =>
checkForOverwrite ← FALSE;
'f, 'F => {
printFlattened ← TRUE;
Subr.debugflg ← TRUE;
};
ENDCASE => h.out.PutF["Unknown flag '%s'\n", IO.rope[tok]];
ENDCASE => EXIT;
parm ← parm + 1;
ENDLOOP;
firstname ← argv[parm];
parm ← parm + 1;
IF Rope.Find[s1: firstname, s2: ".bcd", case: FALSE] > 0 THEN {
bcdfilename ← firstname;
dffilename ← NIL;
}
ELSE {
dffilename ← firstname;
IF parm < argv.argc THEN bcdfilename ← argv[parm];
};
flat ← RopeInline.InlineFlatten[bcdfilename];
IF flat ~= NIL THEN Subr.strcpy[strbcdfilename, LOOPHOLE[flat]];
flat ← RopeInline.InlineFlatten[dffilename];
IF flat ~= NIL THEN Subr.strcpy[strdffilename, LOOPHOLE[flat]];
bcdfilename and dffilename may be stored in!
VerifyBcds[bcdfilename: strbcdfilename, dffilename: strdffilename, h: h,
checkForOverwrite: checkForOverwrite, printFlattened: printFlattened,
useHugeZone: TRUE, wantRTVersionID: 0
! DFSubr.TooManyEntries => {
CWF.WF0["Error - Too many entries in all the DF files.\n"L];
CWF.WF0["Try a smaller set of DF files.\n"L];
Subr.errorflg ← TRUE;
CONTINUE;
}
];
IF Subr.errorflg THEN CWF.WF0["There were Error(s) in the DF file(s).\n"L];
starttime ← Time.Current[] - starttime;
CWF.WF1["\nTotal elapsed time for VerifyDF %lr\n"L, @starttime];
EXITS
leave => NULL;
usage =>CWF.WF0["Usage: VerifyDF DFFileName BCDFileName or VerifyDF BCDFileName or VerifyDF DFFileName\n"L];
};
Cleanup[];
};
VerifyBcds: PUBLIC PROC
assumes a HugeZone already exists and is available
if wantRTVersionID = 0 then simply checks agreement
[bcdfilename, dffilename: LONG STRING, h: Subr.TTYProcs, checkForOverwrite, printFlattened, useHugeZone: BOOL, wantRTVersionID: CARDINAL] = {
dfseq, dfseqother: DFSubr.DFSeq ← NIL;
goaround: BOOL;
ldspace: Space.Handle ← Space.nullHandle;
Cleanup: PROC = {
DFSubr.FreeDFSeq[@dfseq];
DFSubr.FreeDFSeq[@dfseqother];
IF ldspace ~= Space.nullHandle THEN Space.Delete[ldspace];
ldspace ← Space.nullHandle;
};
{
ENABLE UNWIND => Cleanup[];
dfseq ← DFSubr.AllocateDFSeq[maxEntries: MAXFILES,
zoneType: IF useHugeZone THEN huge ELSE shared];
dfseqother ← DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: shared];
IF dffilename ~= NIL AND dffilename.length > 0 THEN {
IF NOT Subr.Any[dffilename, '.] THEN
LongString.AppendString[dffilename, ".DF"L];
DFSubr.FlattenDF[dfseq: dfseq, dffilename: dffilename, h: h,
setRecorder: TRUE, checkForOverwrite: checkForOverwrite,
printStatus: Subr.debugflg];
IF dfseq.size = 0 THEN GOTO leave;
};
IF printFlattened THEN {
sfn: LONG STRING ← Subr.AllocateString[100];
root: LONG STRING ← Subr.AllocateString[100];
{ENABLE UNWIND => {Subr.FreeString[sfn]; Subr.FreeString[root]};
Subr.strcpy[root, dffilename];
IF Subr.EndsIn[root, ".df"L] THEN root.length ← root.length - 3;
CWF.SWF1[sfn, "%s.Flat"L, root];
IF Subr.CheckForModify[sfn, h] THEN {
sh: Stream.Handle ← Subr.NewStream[sfn, Subr.Write];
DFSubr.WriteOut[
dfseq: dfseq, topLevelFile: NIL, outputStream: sh, print: FALSE, altoCompatibility: FALSE, wrapUsingLists: FALSE];
Stream.Delete[sh];
CWF.WF1["Flattened DF file written on %s.\n"L, sfn];
};
}; -- of ENABLE UNWIND
Subr.FreeString[sfn]; Subr.FreeString[root];
};
now analyze the BCD; the algorithm used is as follows:
initally all .need flags are FALSE, all .eval flags are false
for each file we discover, the corresponding .need flag is set to TRUE
on the nxt goround, a file is analyzed if .need is TRUE and .eval is FALSE
after analysis, the .eval flag is set to TRUE
goaround ← TRUE;
ldspace ← Space.Create[size: 1, parent: Space.virtualMemory];
Space.Map[ldspace];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
IF bcdfilename ~= NIL AND bcdfilename.length > 0 THEN {
wantRTVersionID ← AnalBcd[bcdfilename, dfseq, dfseqother, h, ldspace, wantRTVersionID];
IF dffilename.length = 0 THEN {
dfdep: DFSubr.DF ← DFSubr.NextDF[dfseqother]; -- add top-level .Bcd to needed list
dfdep.shortname ← Subr.CopyString[bcdfilename, dfseqother.dfzone];
};
}
ELSE {
anal: BOOLFALSE;
df: DFSubr.DF;
FOR i: CARDINAL IN [0 .. dfseq.size) DO
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
df ← @dfseq[i];
IF df.topmark AND Subr.EndsIn[df.shortname, ".bcd"L]THEN {
wantRTVersionID ← AnalBcd[df.shortname, dfseq, dfseqother,
h, ldspace, wantRTVersionID];
anal ← TRUE;
};
ENDLOOP;
IF NOT anal THEN {
CWF.WF1["Error - no '+' entries in %s.\n"L, dffilename];
Subr.errorflg ← TRUE;
RETURN;
};
};
WHILE goaround DO
goaround ← FALSE;
FOR i: CARDINAL IN [0.. dfseq.size) DO
IF NOT dfseq[i].eval AND dfseq[i].need THEN {
IF Subr.EndsIn[dfseq[i].shortname, ".bcd"L]
OR NOT Subr.Any[dfseq[i].shortname, '.] THEN
wantRTVersionID ← AnalBcd[dfseq[i].shortname, dfseq, dfseqother,
h, ldspace, wantRTVersionID];
dfseq[i].eval ← TRUE;
goaround ← TRUE;
IF h.in.UserAbort[] THEN
SIGNAL Subr.AbortMyself;
};
ENDLOOP;
ENDLOOP;
CheckReadOnlyFiles[dfseq, h, ldspace];
also prints unused entries
CheckForCommonBlunders[dfseq];
now print out extra missing entries
IF dfseqother.size > 0 THEN {
shortname: LONG STRING ← Subr.AllocateString[100];
miss: LONG STRING ← Subr.AllocateString[100];
{ENABLE UNWIND => {Subr.FreeString[shortname]; Subr.FreeString[miss]};
CWF.WF0["Missing Entries:\n"L];
[] ← DFSubr.StripLongName[dffilename, NIL, NIL, shortname];
IF shortname.length > 0 THEN
CWF.SWF1[miss, "MissingEntries.%s$"L, shortname]
ELSE Subr.strcpy[miss, "MissingEntries.DF$"L];
IF Subr.CheckForModify[miss, h] THEN {
sh: Stream.Handle;
DFSubr.SortByCap[dfseqother, 0, dfseqother.size-1];
FillInCreateDates[dfseqother, ldspace, h];
DFSubr.SortByFileName[dfseqother, 0, dfseqother.size-1, FALSE];
DFSubr.InsertCRs[dfseqother];
sh ← Subr.NewStream[miss, Subr.Write];
DFSubr.WriteOut[dfseq: dfseqother, topLevelFile: NIL,
outputStream: sh, print: TRUE];
Stream.Delete[sh];
CWF.WF1["List written on %s.\n"L, miss];
};
}; -- of ENABLE UNWIND
Subr.FreeString[shortname]; Subr.FreeString[miss];
}
ELSE CWF.WF0["\nNo files were omitted from the DF files.\n"L];
EXITS
leave => NULL;
};
Cleanup[];
};
AnalBcd: PROC
[startsfn: LONG STRING, dfseq, dfseqother: DFSubr.DFSeq, h: Subr.TTYProcs, ldspace: Space.Handle, wantRTVersionID: CARDINAL]
RETURNS[newRTVersionID: CARDINAL] = {
Analyze Bcd
dftop: DFSubr.DF;
innardsobject: ProcBcds.InnardsObject ← [bcdheaderspace: Space.nullHandle];
sfn: LONG STRING ← Subr.AllocateString[100];
skip: BOOLFALSE;
versionstamp rule:
versionstamp is set by procMod
or first call on procDep, subsequent calls to procDep
do not change it
procMod: ProcBcds.ProcMod = {
dftop, sfn passed from the open
uns ← NIL;
check RTBcd.VersionID
IF rtVersionID > 0 THEN {
IF wantRTVersionID = 0 THEN newRTVersionID ← rtVersionID
ELSE newRTVersionID ← wantRTVersionID;
IF rtVersionID ~= newRTVersionID THEN
CWF.WF1["Warning - there are two different RTBcd version stamps (RTBcd.VersionID) mentioned in this DF file. One of the files involved is %s.\n"L,
sfn];
};
IF dftop ~= NIL THEN {
dftop.eval ← TRUE;
IF dftop.versionstamp ~= TimeStamp.Null AND dftop.versionstamp ~= bcdvers THEN {
skip ← TRUE;
CWF.WF4["Error - the version of %s listed in the DF file is stamped %a, but %s wants it to be of %a.\n"L,
dftop.shortname, @bcdvers,
dftop.recorder, @dftop.versionstamp];
Subr.errorflg ← TRUE;
};
dftop.versionstamp ← bcdvers;
Subr.FreeString[dftop.recorder, dfseq.dfzone];
dftop.recorder ← Subr.CopyString[dftop.shortname, dfseq.dfzone];
};
IF (dftop = NIL OR NOT dftop.readonly) AND sourcefile.length > 0 THEN {   
if not readonly, ask about sourcefile
dfsrc: DFSubr.DF;
dfsrc ← DFSubr.LookupDF[dfseq, sourcefile];
IF dfsrc = NIL AND DFSubr.LookupDF[dfseqother, sourcefile] = NIL
THEN {
CWF.WF3["Error - %s is the source file for %s\n\tbut %s is not in the df file(s).\n"L,
sourcefile, sfn, sourcefile];
dfsrc ← DFSubr.NextDF[dfseqother];
dfsrc.shortname ← Subr.CopyString[sourcefile,
dfseqother.dfzone];
dfsrc.createtime ← sourcevers.time;
Subr.errorflg ← TRUE;
};
IF dfsrc.createtime > 0 AND dfsrc.createtime ~= sourcevers.time THEN {
CWF.WF4["Error - %s of %lt is the source file for %s,\n\tbut the DF file lists version of %lt.\n"L,
dfsrc.shortname, @sourcevers.time, sfn, @dfsrc.createtime];
Subr.errorflg ← TRUE;
};
dfsrc.eval ← TRUE;
};
};
procDep: ProcBcds.ProcDep = {
filename will probably have ".bcd" at the end
look at otherdepends, canignore, and defstype only
sfn, dfseq, dfseqother, skip, and dftop passed in from outside
dfdep: DFSubr.DF;
IF skip OR filename.length = 0 THEN RETURN;
IF relcode ~= otherdepends AND relcode ~= canignore
AND relcode ~= defstype THEN RETURN;
IF dfseq.size = 0 OR (dfdep ← DFSubr.LookupDF[dfseq, filename]) = NIL
THEN {
IF DFSubr.LookupDF[dfseqother, filename] = NIL
THEN {
CWF.WF2[
"Error - %s is not in the DF file(s),\n\tbut %s depends on it.\n"L,
filename, sfn];
Subr.errorflg ← TRUE;
dfdep ← DFSubr.NextDF[dfseqother];
dfdep.shortname ← Subr.CopyString[filename,
dfseqother.dfzone];
dfdep.versionstamp ← bcdvers;
};
}
ELSE {
IF NOT dfdep.need THEN {
forces analysis only if not READONLY
IF dftop ~= NIL THEN dfdep.need ← NOT dftop.readonly
ELSE dfdep.need ← TRUE;
};
check its the right version
IF bcdvers = TimeStamp.Null OR LongString.EquivalentString[filename, sfn] THEN
RETURN;
IF dfdep.versionstamp = TimeStamp.Null THEN {
dfdep.versionstamp ← bcdvers;
enter who recorded this entry
Subr.FreeString[dfdep.recorder, dfseq.dfzone];
dfdep.recorder ← Subr.CopyString[sfn, dfseq.dfzone];
};
IF bcdvers # dfdep.versionstamp AND dfdep.recorder ~= NIL THEN {
IF LongString.EquivalentString[dfdep.shortname, dfdep.recorder]
THEN
CWF.WF4[
"Error - %s is stamped %a\n but %s wants it to be %a.\n"L,
dfdep.shortname, @dfdep.versionstamp, sfn, @bcdvers]
ELSE {
CWF.WF4[
"Error - %s wants %s of %a\n but %s"L,
sfn, dfdep.shortname, @bcdvers, dfdep.recorder];
CWF.WF1[" wants it to be %a.\n"L, @dfdep.versionstamp];
};
Subr.errorflg ← TRUE;
};
};
};
{ENABLE {
UNWIND => {
Subr.FreeString[sfn];
IF innardsobject.fh ~= NIL THEN IFSFile.Close[innardsobject.fh];
innardsobject.fh ← NIL;
};
File.Unknown => {
CWF.WF1["ERROR File.Unknown - problem reading %s.\n"L, sfn];
GOTO err;
};
};
success: BOOL;
newRTVersionID ← wantRTVersionID;
Subr.strcpy[sfn, startsfn];
IF NOT Subr.Any[sfn, '.] THEN LongString.AppendString[sfn, ".Bcd"L];
dftop ← DFSubr.LookupDF[dfseq, sfn];
IF dftop ~= NIL AND dftop.readonly THEN GO TO return; -- don't analyze readonly things
IF dftop ~= NIL
THEN
[innardsobject.cap, innardsobject.fh] ← CachedBcd[dftop, h, ldspace
! Subr.FileError => {
IF error = wrongVersion
THEN
CWF.WF2[" File %s of %lt cannot be found.\n"L, sfn, @dftop.createtime]
ELSE
CWF.WF1[" File %s cannot be found.\n"L, sfn];
GOTO err
};
IFSFile.UnableToLogin =>
IF reason = io THEN {
CWF.WF1["Unable to analyze - no Leaf server on %s.\n"L,
dftop.host];
GOTO err;
};
]
ELSE {
innardsobject.cap ← Directory.Lookup[
fileName: sfn,
permissions: Directory.ignore
! Directory.Error => {
CWF.WF1[" File %s cannot be fourd.\n"L, sfn];
GOTO err
}];
CWF.WF1["Analyzing %s."L, sfn];
};
CWF.WFCR[];
ProcBcds.ReadInSegmentsBcd[@innardsobject];
ProcBcds.InstallAddressesBcd[@innardsobject];
[success] ← ProcBcds.PrintDepends[innards: @innardsobject,
procMod: procMod, procDep: procDep,
print: FALSE, calltwice: FALSE, less: TRUE, bcdfilename: sfn];
IF NOT success THEN {
CWF.WF1["Error - couldn't analyze %s correctly.\n"L, sfn];
Subr.errorflg ← TRUE;
};
IF innardsobject.fh ~= NIL THEN {
IFSFile.Close[innardsobject.fh];
innardsobject.fh ← NIL;
};
ProcBcds.UnstallBcd[@innardsobject];
EXITS
err => Subr.errorflg ← TRUE;
return => {};
}; -- of ENABLE UNWIND
Subr.FreeString[sfn];
};
CachedBcd: PROC
[df: DFSubr.DF, h: Subr.TTYProcs, ldspace: Space.Handle]
RETURNS[cap: File.Capability ← File.nullCapability, fh: IFSFile.FileHandle ← NIL] = {
if fh is NIL, is local, otherwise remote
localcreatetime: LONG CARDINAL;
{
cap ← Directory.Lookup[
fileName: df.shortname, permissions: Directory.ignore
! Directory.Error => GOTO notonlocal];
localcreatetime ← Subr.GetCreateDateWithSpace[cap, ldspace];
IF localcreatetime = df.createtime OR (df.createtime = 0 AND df.host = NIL) THEN {
CWF.WF1["Analyzing %s."L, df.shortname];
cap ← Directory.UpdateDates[cap, File.read];
RETURN[cap, NIL];
};
EXITS notonlocal => NULL;
};
IF LongString.EquivalentString[df.host, "Unknown"L] THEN
ERROR Subr.FileError[notFound];
CWF.WF3["Analyzing [%s]<%s>%s."L, df.host, df.directory, df.shortname];
fh ← LeafOpenWithCreate[df.host, df.directory, df.shortname, df.version,
df.createtime, df.criterion, h]; -- may raise Subr.FileError
};
LeafOpenWithCreate: PROC
[host, directory, shortname: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL, criterion: DFSubr.Criterion, tty: Subr.TTYProcs] RETURNS [fh: IFSFile.FileHandle] = {
looks on remote server for file with createtime, enumerates if necessary
may raise Subr.FileError
sfn: LONG STRING ← Subr.AllocateString[100];
targetFileName: LONG STRING ← Subr.AllocateString[125];
{ENABLE UNWIND => {Subr.FreeString[sfn]; Subr.FreeString[targetFileName]};
IF version > 0 AND criterion = none AND createtime = 0
THEN CWF.SWF3[sfn, "<%s>%s!%u"L, directory, shortname, @version]
ELSE CWF.SWF2[sfn, "<%s>%s"L, directory, shortname]; -- this gets !H
fh ← LeafSubr.Open[host, sfn, tty, oldReadOnly];
IF createtime ~= 0 AND IFSFile.GetTimes[fh].create ~= createtime THEN {
fres: FQ.Result;
remoteVersion: CARDINAL;
IFSFile.Close[fh];
[fres: fres, remoteVersion: remoteVersion] ← FQ.FileQuery[host, directory,
shortname, version, createtime, FALSE, tty, targetFileName];
SELECT fres FROM
foundCorrectVersion => {
CWF.SWF3[sfn, "<%s>%s!%u"L, directory, shortname, @remoteVersion];
fh ← LeafSubr.Open[host, sfn, tty, oldReadOnly];
};
foundWrongVersion =>
ERROR Subr.FileError[wrongVersion];
notFound =>
ERROR Subr.FileError[notFound];
ENDCASE => ERROR;
};
}; -- of ENABLE UNWIND
Subr.FreeString[sfn]; Subr.FreeString[targetFileName];
};
CheckReadOnlyFiles: PROC[dfseq: DFSubr.DFSeq, h: Subr.TTYProcs, ldspace: Space.Handle] = {
look at ReadOnly files to see if their create times agree with their functional stamps
space: Space.Handle ← Space.Create[size: 1, parent: Space.virtualMemory];
fh: IFSFile.FileHandle;
bcd: BcdOps.BcdBase;
df: DFSubr.DF;
cap: File.Capability;
bcd ← Space.LongPointer[space];
CWF.WF0["(Examining ReadOnly files.)\n"L];
FOR i: CARDINAL IN [0 .. dfseq.size) DO
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
df ← @dfseq[i];
IF df.readonly
AND Subr.EndsIn[df.shortname, "bcd"L]
AND df.versionstamp ~= TimeStamp.Null THEN {
ENABLE File.Unknown => {
CWF.WF1["ERROR File.Unknown - problem reading %s.\n"L, df.shortname];
LOOP;
};
df.eval ← TRUE;
[cap, fh] ← CachedBcd[df, h, ldspace
! Subr.FileError => {
IF error = wrongVersion THEN
CWF.WF2[" File %s of %lt cannot be fourd.\n"L,
df.shortname, @df.createtime]
ELSE CWF.WF1[" File %s cannot be fourd.\n"L, df.shortname];
Subr.errorflg ← TRUE;
LOOP;
};
IFSFile.UnableToLogin =>
IF reason = io THEN {
CWF.WF1["Unable to analyze - no Leaf server on %s.\n"L, df.host];
LOOP;
};
];
CWF.WFCR[];
IF fh = NIL THEN
Space.Map[space, [cap, 1]]
ELSE LeafSubr.RemoteMap[space, fh, 0];
IF bcd.version ~= df.versionstamp THEN {
CWF.WF3["Error - %s of %lt has a version stamp of %a,\n"L,
df.shortname, @df.createtime, @bcd.version];
CWF.WF2["\tbut %s wants one with version stamp %a.\n"L,
df.recorder, @df.versionstamp];
Subr.errorflg ← TRUE;
};
Space.Unmap[space];
IF fh ~= NIL THEN IFSFile.Close[fh];
};
ENDLOOP;
Space.Delete[space];
};
FillInCreateDates: PROC[dfseq: DFSubr.DFSeq, ldspace: Space.Handle, h: Subr.TTYProcs] = {
fill in create dates for entries where we know the version stamp but don't know their create dates; involves looking on the local disk
df: DFSubr.DF;
bcd: BcdOps.BcdBase;
space: Space.Handle ← Space.Create[size: 1, parent: Space.virtualMemory];
host: LONG STRING ← Subr.AllocateString[30];
directory: LONG STRING ← Subr.AllocateString[100];
fullname: LONG STRING ← Subr.AllocateString[125];
short: LONG STRING ← Subr.AllocateString[100];
FillDefs: PROC = {
IF df.host = NIL THEN
df.host ← Subr.CopyString["Unknown"L, dfseq.dfzone];
IF df.directory = NIL THEN
df.directory ← Subr.CopyString["Unknown"L, dfseq.dfzone];
};
Cleanup: PROC = {
Subr.FreeString[host]; Subr.FreeString[directory];
Subr.FreeString[fullname]; Subr.FreeString[short];
Space.Delete[space];
};
{ENABLE UNWIND => Cleanup[];
CWF.WF0["(Filling in create dates from files on local disk... "L];
DFSubr.ReadInDir[dfseq];
FOR i: CARDINAL IN [0 .. dfseq.size) DO
{
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
df ← @dfseq[i];
IF NOT df.presentonlocaldisk THEN GOTO next;
IF Subr.EndsIn[df.shortname, "bcd"L] AND df.createtime = 0
AND df.versionstamp ~= TimeStamp.Null THEN {
Space.Map[space, [df.cap, 1]];
bcd ← Space.LongPointer[space];
this is the bcd version stamp
IF df.versionstamp = bcd.version THEN {
df.createtime ← Subr.GetCreateDateWithSpace[df.cap, ldspace];
Subr.GetRemoteFilenameProp[df.cap, fullname];
IF fullname.length > 0 THEN {
[] ← DFSubr.StripLongName[fullname, host,
directory, short];
IF LongString.EquivalentString[short, df.shortname]
AND host.length > 0 AND directory.length > 0 THEN {
IF df.host ~= NIL THEN Subr.FreeString[df.host, dfseq.dfzone];
df.host ← Subr.CopyString[host, dfseq.dfzone];
IF df.directory ~= NIL THEN Subr.FreeString[df.directory, dfseq.dfzone];
df.directory ← Subr.CopyString[directory, dfseq.dfzone];
Space.Unmap[space];
LOOP;  -- avoid FillDefs
}
};
};
Space.Unmap[space];
}
ELSE IF df.createtime ~= 0
AND df.createtime = Subr.GetCreateDateWithSpace[df.cap, ldspace] THEN {
Subr.GetRemoteFilenameProp[df.cap, fullname];
IF fullname.length > 0 THEN {
[] ← DFSubr.StripLongName[fullname, host,
directory, short];
IF LongString.EquivalentString[short, df.shortname]
AND host.length > 0 AND directory.length > 0 THEN {
IF df.host ~= NIL THEN
Subr.FreeString[df.host, dfseq.dfzone];
df.host ← Subr.CopyString[host, dfseq.dfzone];
IF df.directory ~= NIL THEN
Subr.FreeString[df.directory, dfseq.dfzone];
df.directory ← Subr.CopyString[directory, dfseq.dfzone];
LOOP;  -- avoid FillDefs
}
};
};
GOTO next;
EXITS next => FillDefs[];
};
ENDLOOP;
}; -- of ENABLE UNWIND
Cleanup[];
CWF.WF0["done.)\n"L];
};
CheckForCommonBlunders: PROC[dfseq: DFSubr.DFSeq] = {
df: DFSubr.DF;
p: BOOL;
check various features of the DF file
p ← FALSE;
Directory XXX
FOR i: CARDINAL IN [0 .. dfseq.size) DO
df ← @dfseq[i];
IF NOT df.readonly AND df.releaseDirectory = NIL THEN {
IF NOT p THEN {
CWF.WF2["Warning - these files are in directories that are not ReadOnly and have no ReleaseAs or CameFrom clause.\n\t%s in %s"L,
df.shortname, df.recorder];
p ← TRUE;
}
ELSE
CWF.WF2[", %s in %s"L, df.shortname, df.recorder];
};
ENDLOOP;
IF p THEN CWF.WFCR[];
p ← FALSE;
ReadOnly XXX ReleaseAs YYY
doesn't work because Imports look like
ReadOnly XXX ReleaseAs YYY
FOR i: CARDINAL IN [0 .. dfseq.size) DO
df ← @dfseq[i];
IF df.readonly AND df.releaseDirectory ~= NIL AND NOT df.cameFrom THEN {
IF NOT p THEN {
CWF.WF2["Warning - these files are in directories that are ReadOnly and also have ReleaseAs clauses.\n\t%s in %s"L,
df.shortname, df.recorder];
p ← TRUE;
}
ELSE
CWF.WF2[", %s in %s"L, df.shortname, df.recorder];
};
ENDLOOP;
IF p THEN CWF.WFCR[];
p ← FALSE;
ReleaseAs Cedar>Top>X.DF
FOR i: CARDINAL IN [0 .. dfseq.size) DO
df ← @dfseq[i];
IF NOT df.cameFrom
AND df.releaseDirectory ~= NIL
AND (Count[df.releaseDirectory, '>] > 1
OR Subr.EndsIn[df.releaseDirectory, ".df"L]) THEN {
IF NOT p THEN {
CWF.WF2["Warning - the ReleaseAs clauses for these files may be incorrect.\n\t%s in %s"L,
df.shortname, df.recorder];
p ← TRUE;
}
ELSE
CWF.WF2[", %s in %s"L, df.shortname, df.recorder];
};
ENDLOOP;
IF p THEN CWF.WFCR[];
p ← FALSE;
print out warnings about wrong RTBcd version stamps
FOR i: CARDINAL IN [0 .. dfseq.size) DO
df ← @dfseq[i];
IF NOT df.eval
AND (Subr.EndsIn[df.shortname, ".Mesa"L]
OR Subr.EndsIn[df.shortname, ".Bcd"L]
OR Subr.EndsIn[df.shortname, ".Config"L])
AND NotDuplicated[dfseq, i] THEN {
IF NOT p THEN {
CWF.WF2["Warning - these files are not needed by any top-level bcd files.\n\t%s in %s"L,
df.shortname, df.recorder];
p ← TRUE;
}
ELSE
CWF.WF2[", %s in %s"L, df.shortname, df.recorder];
};
ENDLOOP;
IF p THEN CWF.WFCR[];
};
NotDuplicated: PROC [dfseq: DFSubr.DFSeq, i: CARDINAL] RETURNS [BOOL] = {
dfi, dfj: DFSubr.DF;
dfi ← @dfseq[i];
IF dfi.createtime = 0 THEN RETURN[TRUE];
FOR j: CARDINAL IN [0 .. i) DO
dfj ← @dfseq[j];
IF dfj.eval AND dfj.createtime = dfi.createtime
AND LongString.EquivalentString[dfj.shortname, dfi.shortname] THEN
RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
Count: PROC[s: LONG STRING, c: CHAR] RETURNS[nOcurrences: CARDINAL] = {
nOcurrences ← 0;
FOR i: CARDINAL IN [0 .. s.length) DO
IF s[i] = c THEN nOcurrences ← nOcurrences + 1;
ENDLOOP;
};
CWFAProc: PROC[lp: LONG POINTER, fmt: LONG STRING, wp: PROC[CHARACTER]] = {
pts: LONG POINTER TO TimeStamp.Stamp ← lp;
hex: PACKED ARRAY [0 .. 12) OF [0 .. 16) ← LOOPHOLE[pts^];
FOR i: CARDINAL IN [0 .. 12) DO
IF hex[i] IN [0 .. 9]
THEN wp['0 + hex[i]]
ELSE wp['A + (hex[i] - 10)];
ENDLOOP;
};
MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR]
RETURNS[CHAR] = {
value: ATOM;
value ← IOMisc.AskUser[msg: msg, in: in, out: out,
keyList: LIST[$Yes, $No, $All, $Quit]]; -- order is important
SELECT value FROM
$All => RETURN['a];
$No => RETURN['n];
$Quit => RETURN['q];
$Yes => RETURN['y];
ENDCASE => ERROR;
};
MyPutChar: PROC[ch: CHAR] = {
stdout.PutChar[ch];
};
start code
Commander.Register[
key: "VerifyDF",
proc: Main,
doc: "verify consistency and completeness of a .df file"];
CWF.SetCode['a, CWFAProc];
}.