DIRECTORY BasicTime, Commander, DFCachingUtilities USING [CreateEnumerationCache, EnumerationCache, FileSpec, FSErrorOnDF, Miss, NestItem], DFCachingUtilitiesExtras USING [FullEnumerateDFContents], DFUtilities, DFUtilitiesExtras, FileNames USING [StripVersionNumber], FS USING [ComponentPositions, Error, ErrorGroup, ExpandName, FileInfo], FSExtras USING [GetWDir], IO, MakeDo, MakeDoPorting USING [verifyAttachments], MakeDoPrivate USING [NodeClassRep], Process USING [CheckForAbort], RefTab USING [Create, Erase, Fetch, Pairs, Ref, Store], Rope, RopeHash, SymTab USING [Create, Erase, Fetch, Insert, Pairs, Ref, Store]; MakeDoCmdUtilsImpl: CEDAR PROGRAM IMPORTS BasicTime, DFCachingUtilities, DFCachingUtilitiesExtras, DFUtilitiesExtras, FileNames, FS, FSExtras, IO, MakeDo, MakeDoPorting, Process, RefTab, Rope, RopeHash, SymTab EXPORTS MakeDo, MakeDoPrivate SHARES MakeDoPrivate = BEGIN ROPE: TYPE = Rope.ROPE; RefTable: TYPE = MakeDo.RefTable; AnaCacheKey: TYPE ~ REF AnaCacheKeyPrivate; AnaCacheKeyPrivate: TYPE ~ RECORD [wDir, fullDF: ROPE, created: BasicTime.GMT]; AnaCacheEntry: TYPE ~ REF AnaCacheEntryPrivate; AnaCacheEntryPrivate: TYPE ~ RECORD [ deps: ACDepList, verifyGoals, imports, others: MakeDo.RefTable ]; ACDepList: TYPE ~ LIST OF RECORD [dfName: ROPE, created: BasicTime.GMT]; anaCache: PUBLIC RefTab.Ref _ RefTab.Create[hash: ACKHash, equal: ACKEqual]; ACKHash: PROC [key: REF ANY] RETURNS [hash: CARDINAL] --RefTab.HashProc-- ~ { ack: AnaCacheKey ~ NARROW[key]; hash _ LOOPHOLE[ack.created, CARD] MOD 65536; hash _ RopeHash.FromRope[rope: ack.wDir, case: TRUE, seed: hash]; hash _ RopeHash.FromRope[rope: ack.fullDF, case: TRUE, seed: hash]; RETURN}; ACKEqual: PROC [key1, key2: REF ANY] RETURNS [BOOL] --RefTab.EqualProc-- ~ { ack1: AnaCacheKey ~ NARROW[key1]; ack2: AnaCacheKey ~ NARROW[key2]; IF ack1.created # ack2.created THEN RETURN [FALSE]; IF NOT ack1.wDir.Equal[ack2.wDir] THEN RETURN [FALSE]; IF NOT ack1.fullDF.Equal[ack2.fullDF] THEN RETURN [FALSE]; RETURN [TRUE]}; EmptyCmdUtils: PUBLIC PROC ~ { anaCache.Erase[]; RETURN}; AnalyzeDFFile: PUBLIC PROC [dfName: ROPE, goals, supportFiles: MakeDo.RefTable, modifiable: MakeDo.ModifiabilitySpec, doToVerifyGoals, doToOtherOwns, doToImports: MakeDo.DoToFile] = { ack: AnaCacheKey _ NEW [AnaCacheKeyPrivate _ [FSExtras.GetWDir[], NIL, BasicTime.nullGMT]]; ace: AnaCacheEntry; need: BOOL; Pass: PROC [table: MakeDo.RefTable, do: MakeDo.DoToFile] ~ { PassFile: PROC [key, val: REF ANY] RETURNS [stop: BOOL _ FALSE] ~ { n: MakeDo.Node ~ MakeDo.NarrowToNode[key]; [] _ RefTab.Store[ SELECT do FROM makeGoal => goals, makeModifiable => modifiable, makeSupport => supportFiles, ENDCASE => ERROR, n, $T]; IF do=makeGoal THEN [] _ modifiable.Store[n, $T]; RETURN [FALSE]}; IF do#ignore AND table.Pairs[PassFile] THEN ERROR; RETURN}; [fullFName: ack.fullDF, created: ack.created] _ FS.FileInfo[dfName]; ace _ NARROW[anaCache.Fetch[ack].val]; need _ ace=NIL; IF debugLog#NIL THEN debugLog.PutF["Ana[%g, %g, %g] => %g\n", [rope[ack.wDir]], [rope[ack.fullDF]], [time[ack.created]], [boolean[ace#NIL]] ]; IF NOT need THEN { FOR acds: ACDepList _ ace.deps, acds.rest WHILE acds#NIL AND NOT need DO created: BasicTime.GMT _ BasicTime.nullGMT; [created: created] _ FS.FileInfo[acds.first.dfName !FS.Error => CONTINUE]; IF created # acds.first.created THEN { IF debugLog#NIL THEN debugLog.PutF["Ana[%g].%g = %g # %g\n", [rope[dfName]], [rope[acds.first.dfName]], [rope[MakeDo.FmtTime[acds.first.created]]], [rope[MakeDo.FmtTime[created]]] ]; need _ TRUE}; ENDLOOP}; IF need THEN [] _ anaCache.Store[ack, ace _ MakeACE[ack.fullDF, ack.created]]; Pass[ace.verifyGoals, doToVerifyGoals]; Pass[ace.imports, doToImports]; Pass[ace.others, doToOtherOwns]; RETURN}; MakeACE: PROC [dfName: ROPE, dfCreated: BasicTime.GMT] RETURNS [ace: AnaCacheEntry] = { start: BasicTime.GMT ~ BasicTime.Now[]; Do: PROC [verless: ROPE, table: RefTab.Ref] ~ { n: MakeDo.Node ~ MakeDo.GetNode[verless, MakeDo.fileClass, TRUE]; [] _ table.Store[n, $T]; RETURN}; Recurse: PROC [dfName: ROPE, wanted: DFUtilities.Date, privates, imported: BOOL] ~ { readonly: BOOL _ FALSE; created: BasicTime.GMT _ wanted.gmt; Consume: PROC [item: REF ANY] RETURNS [stop: BOOL _ FALSE] ~ { Process.CheckForAbort[]; WITH item SELECT FROM di: REF DFUtilities.DirectoryItem => readonly _ di.readOnly; fi: REF DFUtilities.FileItem => { table: RefTab.Ref ~ IF readonly OR imported THEN ace.imports ELSE IF fi.verifyRoot THEN ace.verifyGoals ELSE ace.others; Do[FileNames.StripVersionNumber[fi.name], table]; }; ii: REF DFUtilities.ImportsItem => SELECT ii.form FROM exports => IF recurseOnExports[ii.exported] THEN Recurse[VerlessShort[ii.path1], ii.date, FALSE, TRUE]; all => Recurse[VerlessShort[ii.path1], ii.date, TRUE, TRUE]; list => {FOR i: NAT IN [0 .. ii.list.nEntries) DO Do[ii.list[i].name, ace.imports]; ENDLOOP}; ENDCASE => ERROR; ii: REF DFUtilities.IncludeItem => Recurse[VerlessShort[ii.path1], ii.date, TRUE, imported]; ci: REF DFUtilities.CommentItem => NULL; wi: REF DFUtilities.WhiteSpaceItem => NULL; ENDCASE => ERROR; RETURN}; IF wanted.format#explicit THEN { [created: created] _ FS.FileInfo[dfName !FS.Error => CONTINUE]; ace.deps _ CONS[[dfName, created], ace.deps]}; DFUtilitiesExtras.ParseFromFile[dfName, Consume, [filterB: IF privates THEN all ELSE public, filterC: all], created ! DFUtilitiesExtras.FileSyntaxError => { MakeDo.Warning[IO.PutFR["DF syntax error (%g) near %g; parsing of %g abandoned", [rope[reason]], [integer[position]], [rope[dfName]] ]]; CONTINUE}; FS.Error => { MakeDo.Warning[IO.PutFR["FS.Error[%g, %g] while analyzing %g%g - skipping the rest of that DF", [atom[error.code]], [rope[error.explanation]], [rope[IF privates THEN IF imported THEN "" ELSE "private part of " ELSE IF imported THEN "imported part of " ELSE "none(!) of "]], [rope[dfName]] ]]; CONTINUE} ]; RETURN}; stop: BasicTime.GMT; ace _ NEW [AnaCacheEntryPrivate _ [ deps: NIL, verifyGoals: RefTab.Create[], imports: RefTab.Create[], others: RefTab.Create[] ]]; Recurse[dfName, [explicit, dfCreated], TRUE, FALSE]; stop _ BasicTime.Now[]; IF debugLog#NIL THEN debugLog.PutF["MakeACE[%g] took %gs\n", [rope[dfName]], [integer[BasicTime.Period[start, stop]]] ]; RETURN}; recurseOnExports: ARRAY BOOL OF BOOL _ [FALSE: TRUE, TRUE: FALSE]; VerlessShort: PROC [given: ROPE] RETURNS [ROPE] ~ { full: ROPE; cp: FS.ComponentPositions; [full, cp, ] _ FS.ExpandName[given]; RETURN full.Substr[start: cp.base.start, len: cp.ext.start+cp.ext.length-cp.base.start]}; NodeClass: PUBLIC TYPE = REF NodeClassRep; NodeClassRep: PUBLIC TYPE = MakeDoPrivate.NodeClassRep; Verify: PUBLIC PROC [pkgList: LIST OF ROPE] ~ { doneDFs: SymTab.Ref ~ SymTab.Create[case: FALSE]; dfdRels: SymTab.Ref ~ SymTab.Create[case: FALSE]; --imported with UsingForm=list eiRels: SymTab.Ref ~ SymTab.Create[case: FALSE]; --imported with UsingForm=exported or all extraRels: SymTab.Ref ~ SymTab.Create[case: FALSE]; goals: MakeDo.RefTable ~ MakeDo.MakeRefTable[]; modifiable: MakeDo.RefTable ~ MakeDo.MakeRefTable[]; modRels: SymTab.Ref ~ SymTab.Create[case: FALSE]; neededLeaves: MakeDo.RefTable ~ MakeDo.MakeRefTable[]; hids: RefTab.Ref ~ RefTab.Create[]; optionalLeaves: MakeDo.RefTable ~ MakeDo.MakeRefTable[]; determinerLeaves: MakeDo.RefTable ~ MakeDo.MakeRefTable[]; brokenGoals: MakeDo.RefTable ~ MakeDo.MakeRefTable[]; myFileClass: NodeClass ~ MakeDo.fileClass; CanonizeFileName: PROC [ROPE] RETURNS [ROPE] ~ myFileClass.CanonizeName; wDir: ROPE ~ CanonizeFileName[FSExtras.GetWDir[]]; wDirLen: INT ~ wDir.Length; { curDir: ROPE _ NIL; readonly: BOOL _ FALSE; ownedDF: LIST OF BOOL _ NIL; ei: LIST OF BOOL _ NIL; pushOwn: BOOL; pushEI: BOOL; dfStack: LIST OF DFCachingUtilities.FileSpec _ NIL; localEIRels: SymTab.Ref ~ SymTab.Create[case:FALSE]; localDfdRels: SymTab.Ref ~ SymTab.Create[case:FALSE]; localModRels: SymTab.Ref ~ SymTab.Create[case:FALSE]; pkg: ROPE; SeeDFContent: PROC [item: REF ANY] RETURNS [stop, clip, dontCache: BOOL _ FALSE] ~ { WITH item SELECT FROM x: REF DFUtilities.DirectoryItem => {readonly _ x.readOnly; curDir _ x.path1}; x: REF DFUtilities.FileItem => { ownedFile: BOOL ~ ownedDF.first AND NOT readonly; cand: ROPE ~ CanonizeFileName[x.name]; rel: ROPE ~ RelativeName[cand, wDirLen]; IF ownedFile THEN { n: MakeDo.Node ~ MakeDo.FindNode[cand, MakeDo.fileClass]; MakeDo.EnsureRefInTable[n, modifiable]; [] _ localModRels.Insert[rel, $T]; IF x.verifyRoot THEN MakeDo.EnsureRefInTable[n, goals]; } ELSE { full: ROPE ~ FS.ExpandName[rel, curDir].fullFName; actualFull, actualAttach: ROPE; actualCreate: BasicTime.GMT; created: BasicTime.GMT _ IF x.date.format=explicit THEN x.date.gmt ELSE BasicTime.nullGMT; relsTabl: SymTab.Ref ~ IF ei.first THEN localEIRels ELSE localDfdRels; <> IF dfStack=NIL THEN ERROR; IF NOT relsTabl.Insert[rel, dfStack] THEN MakeDo.Warning[IO.PutFR["Multiple attachments for %g (%g and %g).", [rope[rel]], [rope[FmtDfs[NARROW[relsTabl.Fetch[rel].val]]]], [rope[FmtDfs[dfStack]]] ]]; IF created=BasicTime.nullGMT THEN created _ FS.FileInfo[name: full !FS.Error => {MakeDo.Warning[IO.PutFR["FS.Error[%g, %g, %g].", [rope[groupNames[error.group]]], [atom[error.code]], [rope[error.explanation]] ]]; GOTO DontCheck}].created; [fullFName: actualFull, attachedTo: actualAttach, created: actualCreate] _ FS.FileInfo[name: rel !FS.Error => {MakeDo.Warning[IO.PutFR["FS.Error[%g, %g, %g].", [rope[groupNames[error.group]]], [atom[error.code]], [rope[error.explanation]] ]]; GOTO DontCheck}]; IF MakeDoPorting.verifyAttachments THEN { IF NOT (full.Equal[actualAttach.Substr[len: full.Length], FALSE] AND actualCreate=created) THEN MakeDo.Warning[IO.PutFR["Bad attachment for %g (attached to %g of %g instead of %g of %g).", [rope[rel]], [rope[actualAttach]], [time[actualCreate]], [rope[full]], [time[created]] ]]; } ELSE { IF NOT (actualCreate = created) THEN MakeDo.Warning[IO.PutFR["Bad create date for %g (%g instead of %g, as %g).", [rope[rel]], [time[actualCreate]], [time[created]], [rope[FmtDfs[dfStack]]] ]]; } EXITS DontCheck => pkg _ pkg}; stop _ stop}; x: REF DFUtilities.ImportsItem => { pushOwn _ FALSE; pushEI _ ei.first OR ownedDF.first AND x.form#list; IF x.list#NIL THEN FOR i: NAT IN [0 .. x.list.nEntries) DO rel: ROPE ~ RelativeName[full: CanonizeFileName[x.list[i].name], baseLen: wDirLen]; IF x.list[i].verifyRoot THEN [] _ extraRels.Store[rel, $T]; ENDLOOP; }; x: REF DFUtilities.IncludeItem => { pushOwn _ ownedDF.first; pushEI _ ei.first}; x: REF DFCachingUtilities.NestItem => SELECT x.bracket FROM begin => {short: ROPE ~ VerlessShort[x.df.name]; ownedDF _ CONS[pushOwn, ownedDF]; ei _ CONS[pushEI, ei]; dfStack _ CONS[x.df, dfStack]; IF doneDFs.Fetch[short].found THEN RETURN [clip: TRUE]; IF pushOwn THEN IF NOT doneDFs.Insert[key: short, val: $T] THEN ERROR; }; end => {ownedDF _ ownedDF.rest; ei _ ei.rest; dfStack _ dfStack.rest}; ENDCASE => ERROR; x: REF DFUtilities.CommentItem => NULL; x: REF DFUtilities.WhiteSpaceItem => NULL; ENDCASE => ERROR; }; AddToSymTable: PROC [global, local: SymTab.Ref] ~ { InnerAdd: PROC [key: ROPE, val: REF ANY] RETURNS [BOOL] ~ { [] _ global.Store[key: key, val: val]; RETURN [FALSE]; }; [] _ local.Pairs[InnerAdd]; }; dfName: ROPE; cp: FS.ComponentPositions; dfCache: DFCachingUtilities.EnumerationCache ~ DFCachingUtilities.CreateEnumerationCache[]; FOR pkgRest: LIST OF ROPE _ pkgList, pkgRest.rest WHILE pkgRest # NIL DO pkg _ pkgRest.first; pushOwn _ TRUE; pushEI _ FALSE; [dfName, cp, ] _ FS.ExpandName[pkg]; IF cp.ext.length=0 AND cp.ext.start=cp.base.start+cp.base.length THEN dfName _ dfName.Concat[".df"]; [] _ DFCachingUtilitiesExtras.FullEnumerateDFContents[df: [dfName], side: local, Consumer: SeeDFContent, cache: dfCache ! DFCachingUtilities.FSErrorOnDF => { MakeDo.Warning[IO.PutFR["FS.Error[%g, %g, %g].", [rope[groupNames[error.group]]], [atom[error.code]], [rope[error.explanation]] ]]; RESUME}; DFCachingUtilities.Miss => { MakeDo.Warning[IO.PutFR["%g not found in %g", [rope[dataFileName]], [rope[dfFileName]] ]]; RESUME} ]; IF ownedDF#NIL THEN ERROR; IF ei#NIL THEN ERROR; { AddToSymTable[eiRels, localEIRels]; AddToSymTable[dfdRels, localDfdRels]; AddToSymTable[modRels, localModRels]; localEIRels.Erase[]; localDfdRels.Erase[]; localModRels.Erase[]; }; ENDLOOP; }; {actions: MakeDo.ActionList _ MakeDo.GetActions[goals, modifiable, toBeDone, neededLeaves, optionalLeaves, determinerLeaves, brokenGoals]; wronglyUnlisted: RefTab.Ref ~ RefTab.Create[]; wronglyListed: RefTab.Ref ~ RefTab.Create[]; CheckLeafIsListed: PROC [key, val: REF ANY] RETURNS [stop: BOOL] ~ { n: MakeDo.Node ~ MakeDo.NarrowToNode[key]; CheckListness[n, NIL, TRUE]; RETURN [FALSE]}; CheckLeafMaybeListed: PROC [key, val: REF ANY] RETURNS [stop: BOOL] ~ { n: MakeDo.Node ~ MakeDo.NarrowToNode[key]; CheckListness[n, NIL, FALSE]; RETURN [FALSE]}; CheckHidListed: PROC [key, val: REF ANY] RETURNS [BOOL] ~ { n: MakeDo.Node ~ MakeDo.NarrowToNode[key]; refs: MakeDo.ActionList ~ NARROW[val]; CheckListness[n, refs, TRUE]; RETURN [FALSE]}; CheckListness: PROC [n: MakeDo.Node, refs: MakeDo.ActionList, required: BOOL] ~ { name: ROPE; class: MakeDo.NodeClass; [name, class] _ MakeDo.PublicPartsOfNode[n]; IF class # MakeDo.fileClass THEN RETURN; IF NOT wDir.Equal[name.Substr[len: wDirLen], FALSE] THEN { MakeDo.Warning[IO.PutFR["Dependency on %g can't be handled by DFs.", [rope[name]] ]]; RETURN}; required _ required OR hids.Fetch[n].found OR neededLeaves.Fetch[n].found; {rel: ROPE ~ RelativeName[full: name, baseLen: wDirLen]; shouldBeListed: BOOL ~ required OR n.Exists[]; isListed: BOOL ~ modifiable.Fetch[n].found OR dfdRels.Fetch[rel].found OR eiRels.Fetch[rel].found; IF shouldBeListed=isListed THEN RETURN; {wrongTable: RefTab.Ref ~ IF isListed THEN wronglyListed ELSE wronglyUnlisted; needs: RefTab.Ref _ NARROW[wrongTable.Fetch[n].val]; NoteRef: PROC [a: MakeDo.Action, ad: MakeDo.ActionDep] ~ { [] _ needs.Store[a, $T]; RETURN}; IF needs=NIL THEN [] _ wrongTable.Store[n, needs _ RefTab.Create[]]; IF refs#NIL THEN FOR refs _ refs, refs.rest WHILE refs#NIL DO NoteRef[refs.first, data]; ENDLOOP ELSE { n.EnumerateConsumers[data, NoteRef]; n.EnumerateConsumers[cmd, NoteRef]}; [] _ needs.Store[$Fmt, IF isListed THEN IO.PutFR["File %g (affects %%g) doesn't exist but is in DF(s).", [rope[rel]]] ELSE IO.PutFR["File %g (needed by %%g) missing from DF(s).", [rope[rel]]] ]; RETURN}}}; ReportWrong: PROC [key, val: REF ANY] RETURNS [BOOL] ~ { n: MakeDo.Node ~ MakeDo.NarrowToNode[key]; needs: RefTab.Ref ~ NARROW[val]; fmt, nr: ROPE _ NIL; PerAction: PROC [key, val: REF ANY] RETURNS [BOOL] ~ { IF key=$Fmt THEN fmt _ NARROW[val] ELSE { a: MakeDo.Action ~ MakeDo.NarrowToAction[key]; IF nr#NIL THEN nr _ nr.Concat["; "]; nr _ nr.Concat[MakeDo.PublicPartsOfAction[a].cmd]}; RETURN [FALSE]}; IF needs.Pairs[PerAction] THEN ERROR; MakeDo.Warning[IO.PutFR[fmt, [rope[nr]] ]]; RETURN [FALSE]}; CheckDfdRel: PROC [key: ROPE, val: REF ANY] RETURNS [BOOL] ~ { IF extraRels.Fetch[key].found THEN RETURN [FALSE]; {n: MakeDo.Node ~ MakeDo.FindNode[key, MakeDo.fileClass]; IF NOT (neededLeaves.Fetch[n].found OR hids.Fetch[n].found OR optionalLeaves.Fetch[n].found OR determinerLeaves.Fetch[n].found) THEN MakeDo.Warning[IO.PutFR["Imported file %g not needed.", [rope[key]] ]]; RETURN [FALSE]}}; ReportBadGoal: PROC [key, val: REF ANY] RETURNS [BOOL] ~ { n: MakeDo.Node ~ MakeDo.NarrowToNode[key]; name: ROPE ~ MakeDo.PublicPartsOfNode[n].name; MakeDo.Warning[IO.PutFR["Goal %g not happy.", [rope[name]] ]]; RETURN [FALSE]}; seen: RefTab.Ref ~ RefTab.Create[]; toDo: LIST OF REF ANY _ NIL; StartFromGoal: PROC [key, val: REF ANY] RETURNS [stop: BOOL] ~ {IF seen.Store[key, $T] THEN toDo _ CONS[key, toDo]; RETURN [FALSE]}; IF brokenGoals.Pairs[ReportBadGoal] THEN ERROR; IF goals.Pairs[StartFromGoal] THEN ERROR; WHILE toDo#NIL DO this: REF ANY ~ toDo.first; toDo _ toDo.rest; IF MakeDo.IsNode[this] THEN { n: MakeDo.Node ~ MakeDo.NarrowToNode[this]; a: MakeDo.Action ~ MakeDo.GetProducer[n]; IF a#NIL AND seen.Store[a, $T] THEN toDo _ CONS[a, toDo]} ELSE {a: MakeDo.Action ~ MakeDo.NarrowToAction[this]; ac: MakeDo.ActionClass ~ MakeDo.PublicPartsOfAction[a].class; CheckHidden: PROC [n: MakeDo.Node] ~ { IF (NOT modifiable.Fetch[n].found) AND n.PublicPartsOfNode[].class=MakeDo.fileClass THEN { refs: MakeDo.ActionList _ NARROW[hids.Fetch[n].val]; refs _ CONS[a, refs]; [] _ hids.Store[n, refs]}}; PerSource: PROC [n: MakeDo.Node, which: MakeDo.ActionDep, optional: BOOL] ~ { IF modifiable.Fetch[n].found AND seen.Store[n, $T] THEN toDo _ CONS[n, toDo]}; MakeDo.EnumerateSources[a, cmd, PerSource]; MakeDo.EnumerateSources[a, data, PerSource]; IF ac.EnumHiddenDeps#NIL THEN ac.EnumHiddenDeps[a, CheckHidden]; toDo _ toDo}; ENDLOOP; IF dfdRels.Pairs[CheckDfdRel] THEN ERROR; IF hids.Pairs[CheckHidListed] THEN ERROR; IF neededLeaves.Pairs[CheckLeafIsListed] THEN ERROR; IF optionalLeaves.Pairs[CheckLeafMaybeListed] THEN ERROR; IF determinerLeaves.Pairs[CheckLeafMaybeListed] THEN ERROR; IF wronglyUnlisted.Pairs[ReportWrong] THEN ERROR; IF wronglyListed.Pairs[ReportWrong] THEN ERROR; IF actions#NIL THEN { msg: ROPE _ NIL; FOR actions _ actions, actions.rest WHILE actions#NIL DO msg _ msg.Cat["\n\t", MakeDo.PublicPartsOfAction[actions.first].cmd]; ENDLOOP; MakeDo.Warning[Rope.Cat["Need to:", msg, "."]]; }; RETURN}}; FmtDfs: PROC [dfStack: LIST OF DFCachingUtilities.FileSpec] RETURNS [ans: ROPE _ NIL] ~ { FOR dfStack _ dfStack, dfStack.rest WHILE dfStack#NIL DO step: ROPE ~ dfStack.first.name; IF ans#NIL THEN ans _ ans.Cat[" in ", step] ELSE ans _ Rope.Concat["in ", step]; ENDLOOP; RETURN}; RelativeName: PROC [full: ROPE, baseLen: INT] RETURNS [rel: ROPE] ~ { IF full.Length>baseLen AND full.Fetch[baseLen]='< THEN baseLen _ baseLen.SUCC; rel _ full.Substr[start: baseLen]; RETURN}; groupNames: ARRAY FS.ErrorGroup OF ROPE ~ [ ok: "ok", bug: "bug", environment: "environment", lock: "lock", client: "client", user: "user" ]; MakeDoCmdUtilsImplDebug: Commander.CommandProc ~ {debugLog _ cmd.out}; MakeDoCmdUtilsImplDontDebug: Commander.CommandProc ~ {debugLog _ NIL}; debugLog: IO.STREAM _ NIL; END. H MakeDoCmdUtilsImpl.Mesa Mike Spreitzer, February 9, 1987 2:28:06 pm PST Last tweaked by Mike Spreitzer on November 6, 1990 5:04 pm PST Eduardo Pelegri-Llopart May 22, 1989 2:34:55 pm PDT JKF January 11, 1989 10:33:06 am PST Willie-s, December 4, 1991 4:35 pm PST Same as modifiable, but just relative names wDir: ROPE ~ FS.ExpandName[FSExtras.GetWDir[]].fullFName; Inner block here to avoid confusing messages. For the loop Commander.Register["MakeDoCmdUtilsImplDebug", MakeDoCmdUtilsImplDebug]; Commander.Register["MakeDoCmdUtilsImplDontDebug", MakeDoCmdUtilsImplDontDebug]; Κ*•NewlineDelimiter – "cedar" style™code™Kšœ,Οk™/Kšœ>™>Kšœ3™3K™$K™&—K˜š ˜ Kšœ ˜ K˜ KšœœS˜kKšœœ˜9Kšœ ˜ Kšœ˜Kšœ œ˜%Kšœœ?˜GKšœ œ ˜Kšœ˜Kšœ˜Kšœœ˜(Kšœœ˜$Kšœœ˜Kšœœ+˜7Kšœ˜K˜ Kšœœ3˜?—K˜šΡbnxœœ˜!KšœXœ œ@˜―Kšœ˜Kšœ˜—K˜Kš˜K˜Kšœœœ˜K˜!K˜Kšœ œœ˜+šœ˜Kšœœœœ˜6—K˜Kšœœœ˜/šœœœ˜%Kšœ˜K˜-K˜—K˜Kš œ œœœœ œœ˜HK˜Kšœ œ<˜LK˜šΟnœœœœœœΟcœ˜MKšœœ˜Kšœœœœ˜-Kšœ/œ˜AKšœ1œ˜CKšœ˜—K˜šŸœœœœœœ œ˜LKšœœ˜!Kšœœ˜!Kšœœœœ˜3Kš œœœœœ˜6Kš œœ œœœ˜:Kšœœ˜—K˜šŸ œœœ˜K˜Kšœ˜—K˜šŸ œœœ œ˜·Kšœœ,œ˜[Kšœ˜Kšœœ˜ šŸœœ2˜<šŸœœ œœœœœ˜CKšœ*˜*˜KšœœOœœ˜nK˜K˜—Kšœ œ˜1Kšœœ˜—Kšœ œœœ˜2Kšœ˜—Kšœ0œ˜DKšœœ˜&Kšœ œ˜Kšœ œœrœ˜Žšœœœ˜š œ'œœœœ˜HKšœœ˜+Kšœœœ œ˜Jšœœ˜&Kšœ œœ’˜ΆKšœœ˜ —Kšœ˜ ——KšœœB˜NKšœ'˜'Kšœ˜Kšœ ˜ Kšœ˜—K˜š Ÿœœ œœœ˜WKšœœ˜'šŸœœ œ˜/Kšœ;œ˜AK˜Kšœ˜—šŸœœ œ0œ˜TKšœ œœ˜Kšœœ˜$šŸœœœœœœœ˜>K˜šœœ˜Kšœœ5˜<šœœ˜!Kšœœ œ œ œœœœ ˜xKšœ1˜1K˜—šœœœ ˜6Kš œ œœ*œœ˜gKšœ0œœ˜<šœ œœœ˜1Kšœ!˜!Kšœ˜ —Kšœœ˜—KšœœEœ ˜\Kšœœœ˜(Kšœœœ˜+Kšœœ˜—Kšœ˜—šœœ˜ Kšœœœ œ˜?Kšœ œ˜.—šœ;œ œœ!˜ušœ&˜&Kšœœw˜ˆKšœ˜ —šœ ˜ Kšœœ„œ œœ œœœœ œœ$˜€Kšœ˜ —Kšœ˜—Kšœ˜—Kšœœ˜šœœ˜#Kšœœ˜ K˜K˜K˜—Kšœ'œœ˜4K˜Kšœ œœd˜xKšœ˜Kšœœœœœœœœœ˜B—K˜š Ÿ œœ œœœ˜3Kšœœ˜ Kšœœ˜Kšœœ˜$KšœS˜Y—K˜Kšœ œœœ˜*Kšœœœ˜7K˜š Ÿœœœ œœœ˜/Kšœ*œ˜1Kšœ*œ ˜PKšœ)œ )˜ZKšœ,œ˜3Kšœ/˜/Kšœ4˜4šœ*œ˜1K™+—Kšœ6˜6Kšœ#˜#Kšœ8˜8Kšœ:˜:Kšœ5˜5Kšœ*˜*Kš Ÿœœœœœ˜HKšœœ(˜2KšΟyΠky‘’‘*™9Kšœ œ˜Kšœ˜K™Kšœœœ˜Kšœ œœ˜Kš œ œœœœ˜Kš œœœœœ˜Kšœ œ˜Kšœœ˜ Kšœ œœœ˜3Kšœ-œ˜4Kšœ.œ˜5Kšœ.œ˜5Kšœœ˜ šŸ œœœœœœœ˜Tšœœ˜KšœœH˜Nšœœ˜ Kšœ œœœ ˜1Kšœœ˜&Kšœœ˜(šœ œ˜Kšœ9˜9Kšœ'˜'K˜"Kšœœ#˜7K˜—šœ˜Kšœœœ#˜2Kšœœ˜Kšœœ˜Kš œœœœ œ˜ZKšœœ œ œ˜Fšœ(˜(Kšœœœ˜(Kšœ™—Kšœ˜Kšœ œœœ˜Kš œœœœMœ9˜ΗKš œœ œœœsœ˜ξKš œKœœœsœ ˜„šœ ˜"šœ˜Kš œœ4œœœœ¦˜—K˜—šœ˜Kšœœœœ‹˜ΑK˜——Kšœ˜—K˜ —šœœ˜#Kšœ œ˜Kšœœœ ˜3š œœœœœœ˜:KšœœJ˜SKšœœ˜;Kšœ˜—K˜—šœœ˜#Kšœ˜Kšœ˜—šœœ œ ˜;šœœ˜0Kšœ œ˜!Kšœœœ˜5Kšœœœœ˜7Kš œ œœœ%œœ˜FKšœ˜—KšœF˜FKšœœ˜—Kšœœœ˜'Kšœœœ˜*Kšœœ˜—K˜—šŸ œœ ˜3šŸœœœœœœœ˜;K˜&Kšœœ˜K˜—K˜K˜—Kšœœœ˜(Kšœ[˜[š œ œœœœ œ˜HKšœ˜Kšœ œ˜Kšœ œ˜Kšœœ˜$Kšœœ+œ˜dšœQŸœ ˜yšœ#˜#Kšœœr˜ƒKšœ˜—šœ˜KšœœI˜ZKšœ˜ ——Kšœ œœœ˜Kšœœœœ˜˜K™ K˜#K˜%K˜%Kšœ˜K˜K˜K˜—Kšœ˜—Kšœ˜KšœŠ˜ŠK˜.K˜,š Ÿœœ œœœœ˜DKšœ*˜*Kšœœœ˜Kšœœ˜—š Ÿœœ œœœœ˜GKšœ*˜*Kšœœœ˜Kšœœ˜—š Ÿœœ œœœœ˜;Kšœ*˜*Kšœœ˜&Kšœœ˜Kšœœ˜—šŸ œœ5œ˜QKšœœ˜ Kšœ˜Kšœ,˜,Kšœœœ˜(šœœ'œœ˜:KšœœD˜UKšœ˜—Kšœœœ˜JKšœœ.˜8Kšœœ œ ˜.Kšœ œœœ˜bKšœœœ˜'Kšœœ œœ˜NKšœœ˜4šŸœœ-˜:K˜Kšœ˜—Kšœœœ3˜Dš œœœœœœ˜=Kšœ˜"—šœ˜Kšœ$˜$Kšœ$˜$—šœœ ˜"KšœœK˜RKšœœE˜L—Kšœ˜ —š Ÿ œœ œœœœ˜8Kšœ*˜*Kšœœ˜ Kšœ œœ˜š Ÿ œœ œœœœ˜6šœ œœœ˜)Kšœ.˜.Kšœœœ˜$K˜3—Kšœœ˜—Kšœœœ˜%Kšœœ˜+Kšœœ˜—šŸ œœœœœœœ˜>Kšœœœœ˜2Kšœ9˜9Kšœœœœœ"œœ6˜ΜKšœœ˜—š Ÿ œœ œœœœ˜:Kšœ*˜*Kšœœ$˜.Kšœœ-˜>Kšœœ˜—K˜#Kš œœœœœœ˜š Ÿ œœ œœœœ˜