DIRECTORY BasicTime, BcdDefs, BcdOps, Commander, CommandTool, FileNames, FS, FSBackdoor, GenerateDFClosure, IO, List, MessagesOut, Process, RedBlackTree, Rope, TiogaAccess, UserProfile, VM; Dependencies: CEDAR MONITOR IMPORTS BcdOps, Commander, CommandTool, FileNames, FS, FSBackdoor, GenerateDFClosure, IO, List, MessagesOut, Process, RedBlackTree, Rope, TiogaAccess, UserProfile, VM = BEGIN GMT: TYPE = BasicTime.GMT; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; suspectFrom: ROPE _ NIL; suspectTo: ROPE _ NIL; SuspectSeen: SIGNAL = CODE; MyData: TYPE = REF MyDataRep; MyDataRep: TYPE = RECORD [ table: RedBlackTree.Table _ NIL, abortRequested: BOOL _ FALSE, errs: STREAM _ NIL ]; BaseEntry: TYPE = REF BaseEntryRep; BaseEntryList: TYPE = LIST OF BaseEntry; BaseEntryRep: TYPE = RECORD [ name: ROPE, from: ROPE _ NIL, version: BcdDefs.VersionStamp, dependents: BaseEntryList _ NIL, next: BaseEntry _ NIL ]; triesForServerGlitch: NAT _ 8; pauseForServerGlitch: NAT _ 15; forkers: NAT _ 1; DependenciesCommandProc: Commander.CommandProc = { out: STREAM = cmd.out; switches: PACKED ARRAY CHAR['a..'z] OF BOOL _ ALL[FALSE]; ProcessSwitches: PROC [arg: ROPE] = { sense: BOOL _ TRUE; FOR index: INT IN [0..Rope.Length[arg]) DO char: CHAR _ Rope.Fetch[arg, index]; SELECT char FROM '- => LOOP; '~ => {sense _ NOT sense; LOOP}; IN ['a..'z] => switches[char] _ sense; IN ['A..'Z] => switches[char + ('a-'A)] _ sense; ENDCASE; sense _ TRUE; ENDLOOP; }; ProcessArgument: PROC [arg: ROPE] = { table: RedBlackTree.Table _ RedBlackTree.Create[getKey: GetKey, compare: Compare]; myData: MyData _ NEW[MyDataRep _ [table: table, errs: out]]; dfName: ROPE _ Rope.Concat[arg, ".df"]; outName: ROPE _ Rope.Concat[arg, ".depends"]; Process.CheckForAbort[]; [] _ GenerateDFClosure.GenerateClosureToProc[dfName, out, EachFile, myData, [toFork: forkers, followImports: NOT switches['s]] ! ABORTED, UNWIND => myData.abortRequested _ TRUE]; { tc: TiogaAccess.TiogaChar _ [ charSet: 0, char: '\n, looks: ALL[FALSE], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: List.PutAssoc[key: $FromTiogaFile, val: $Yes, aList: NIL] ]; PutRope: PROC [rope: ROPE] = { [] _ Rope.Map[base: rope, action: PutCharB]; }; PutRopeBold: PROC [rope: ROPE] = { tc.looks['b] _ TRUE; [] _ Rope.Map[base: rope, action: PutCharB]; tc.looks['b] _ FALSE; }; PutRopeItalic: PROC [rope: ROPE] = { tc.looks['i] _ TRUE; [] _ Rope.Map[base: rope, action: PutCharB]; tc.looks['i] _ FALSE; }; PutCharB: Rope.ActionType = { tc.char _ c; TiogaAccess.Put[writer, tc]; }; EndNode: PROC [delta: INTEGER _ 0, format: ATOM _ NIL] = { tc.endOfNode _ TRUE; tc.char _ '\n; tc.format _ format; tc.deltaLevel _ delta; TiogaAccess.Put[writer, tc]; tc.endOfNode _ FALSE; }; eachNode: RedBlackTree.EachNode = { WITH data SELECT FROM base: BaseEntry => WHILE base # NIL DO dependents: BaseEntryList _ base.dependents _ SortDependents[base.dependents]; SELECT TRUE FROM dependents = NIL AND NOT switches['u] => {}; base.from = NIL AND NOT switches['v] => {}; ENDCASE => { tc.format _ $block; PutRopeBold[base.name]; PutRope[": "]; PutRopeItalic["("]; IF base.from = NIL THEN PutRopeItalic["??"] ELSE PutRopeItalic[base.from]; PutRopeItalic[")"]; EndNode[1, $block]; tc.format _ $block; WHILE dependents # NIL DO PutRope[dependents.first.name]; PutRope[" "]; dependents _ dependents.rest; ENDLOOP; EndNode[-1, $indent]; }; IF Rope.Match[suspectTo, base.name, FALSE] THEN SIGNAL SuspectSeen; base _ base.next; ENDLOOP; ENDCASE; }; writer: TiogaAccess.Writer _ TiogaAccess.Create[]; TiogaAccess.Put[writer, tc]; tc.comment _ TRUE; tc.endOfNode _ FALSE; tc.propList _ NIL; PutRope[outName]; EndNode[]; EndNode[]; tc.comment _ FALSE; IO.PutF1[out, "Writing dependencies to %g", [rope[outName]] ]; RedBlackTree.EnumerateIncreasing[table, eachNode]; TiogaAccess.WriteFile[writer, outName]; IO.PutRope[out, ".\n"]; }; }; argsProcessed: NAT _ 0; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd, starExpand: FALSE ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}]; ProcessSwitches[UserProfile.Token["Dependencies.DefaultSwitches"]]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE = argv[i]; Process.CheckForAbort[]; IF Rope.Length[arg] = 0 THEN LOOP; IF Rope.Fetch[arg, 0] = '- THEN { ProcessSwitches[arg]; LOOP; }; ProcessArgument[arg]; ENDLOOP; EXITS failed => {result _ $Failure}; }; EachFile: GenerateDFClosure.ActionProc = { myData: MyData _ CheckAbort[data]; table: RedBlackTree.Table _ myData.table; EnterDependency: ENTRY PROC [bcdName: ROPE, version: BcdDefs.VersionStamp, to: ROPE, toVersion: BcdDefs.VersionStamp] = { ENABLE UNWIND => NULL; baseFrom: BaseEntry _ GetBaseRecord[bcdName, version]; baseTo: BaseEntry _ GetBaseRecord[to, toVersion]; baseTo.dependents _ CONS[baseFrom, baseTo.dependents]; baseFrom.from _ FileNames.StripVersionNumber[from]; IF Rope.Match[suspectFrom, bcdName, FALSE] THEN SIGNAL SuspectSeen; IF Rope.Match[suspectTo, to, FALSE] THEN SIGNAL SuspectSeen; }; EnsureFrom: ENTRY PROC [bcdName: ROPE, version: BcdDefs.VersionStamp] = { ENABLE UNWIND => NULL; base: BaseEntry _ GetBaseRecord[bcdName, version]; IF base.from = NIL THEN base.from _ FileNames.StripVersionNumber[from]; }; GetBaseRecord: INTERNAL PROC [file: ROPE, version: BcdDefs.VersionStamp] RETURNS [base: BaseEntry] = { old: BaseEntry _ NIL; WITH RedBlackTree.Lookup[table, file] SELECT FROM be: BaseEntry => { WHILE be # NIL DO IF be.version = version THEN RETURN [be]; old _ be; be _ be.next; ENDLOOP; }; ENDCASE; base _ NEW[BaseEntryRep _ [name: file, version: version]]; IF old = NIL THEN RedBlackTree.Insert[table, base, file] ELSE old.next _ base; }; SELECT kind FROM file => { triesLeft: NAT _ triesForServerGlitch; shortName: ROPE _ FileNames.GetShortName[name]; IF Rope.Match["*.bcd", shortName, FALSE] THEN { shortName _ Rope.Flatten[shortName, 0, Rope.Length[shortName]-4]; DO ENABLE FS.Error => { SELECT error.code FROM $serverInaccessible => { MessagesOut.PutRopes[myData.errs, "Server glitch: ", name]; Process.Pause[Process.SecondsToTicks[pauseForServerGlitch]]; IF (triesLeft _ triesLeft - 1) # 0 THEN LOOP; }; ENDCASE; MessagesOut.PutRopes[ myData.errs, "File not found: ", name, IO.PutFR["\n from: %g\n reason: %g\n", [rope[from]], [rope[error.explanation]]]]; IF error.group # bug THEN EXIT ELSE REJECT; }; file: FS.OpenFile; tempName: ROPE _ name; useTemp: BOOL _ NOT IsInFileCache[name, date.gmt]; IF useTemp THEN tempName _ FS.Copy[ from: name, to: "///Temp/Dependencies.temp$", setKeep: TRUE, keep: 20, wantedCreatedTime: date.gmt, remoteCheck: FALSE ]; file _ FS.Open[name: tempName, wantedCreatedTime: date.gmt, remoteCheck: FALSE]; TRUSTED { pages: INT _ FS.GetInfo[file].pages; interval: VM.Interval _ VM.SimpleAllocate[pages]; bcd: BcdDefs.BcdBase _ VM.AddressForPageNumber[interval.page]; RopeForNameRecord: PROC [bcd: BcdDefs.BcdBase, name: BcdDefs.NameRecord] RETURNS [r: ROPE] = TRUSTED { ssb: BcdDefs.NameString = LOOPHOLE[bcd + bcd.ssOffset]; len: NAT; i: INT _ name; GetFromNameString: SAFE PROC RETURNS [char: CHAR] = TRUSTED { char _ ssb.string[i]; i _ i + 1}; r _ Rope.FromProc[ssb.size[name], GetFromNameString]; len _ r.Length[]; IF len > 0 AND r.Fetch[len-1] = '. THEN r _ r.Substr[len: len-1]; }; DoOneFile: PROC [fth: BcdDefs.FTHandle, fti: BcdDefs.FTIndex] RETURNS [BOOL _ FALSE] = TRUSTED { file: ROPE _ RopeForNameRecord[bcd, fth.name]; EnterDependency[shortName, bcd.version, file, fth.version]; }; FS.Read[file: file, from: 0, nPages: pages, to: bcd]; IF bcd.nConfigs = 0 THEN [] _ BcdOps.ProcessFiles[bcd, DoOneFile]; EnsureFrom[shortName, bcd.version]; VM.Free[interval]; FS.Close[file]; IF useTemp THEN FS.Delete[tempName]; }; EXIT; ENDLOOP; }; }; ENDCASE; }; IsInFileCache: PUBLIC PROC [name: ROPE, gmt: GMT] RETURNS [inCache: BOOL _ FALSE] = { cacheChecker: FSBackdoor.InfoProc = { IF bytes > 0 THEN { IF gmt # BasicTime.nullGMT THEN { IF created # gmt THEN RETURN [TRUE]; }; inCache _ TRUE; RETURN [FALSE]; }; RETURN [TRUE]; }; FSBackdoor.EnumerateCacheForInfo[cacheChecker, NIL, name]; }; GetKey: RedBlackTree.GetKey = { RETURN [data]; }; Compare: RedBlackTree.Compare = { key: ROPE _ NIL; WITH k SELECT FROM base: BaseEntry => key _ base.name; rope: ROPE => key _ rope; ENDCASE => ERROR; WITH data SELECT FROM base: BaseEntry => RETURN [Rope.Compare[key, base.name, FALSE]]; ENDCASE; ERROR; }; CompareEntries: List.CompareProc = { WITH ref1 SELECT FROM base1: BaseEntry => WITH ref2 SELECT FROM base2: BaseEntry => RETURN [Rope.Compare[base1.name, base2.name, FALSE]]; ENDCASE; ENDCASE; ERROR; }; SortDependents: PROC [list: BaseEntryList] RETURNS [BaseEntryList] = TRUSTED { RETURN [LOOPHOLE[List.Sort[LOOPHOLE[list], CompareEntries]]]; }; CheckAbort: PROC [ref: REF] RETURNS [MyData] = { WITH ref SELECT FROM mine: MyData => { SELECT TRUE FROM mine.abortRequested => {}; ENDCASE => RETURN [mine]; }; ENDCASE; ERROR ABORTED; }; doc: ROPE = "{switch | item}*\nGenerates object file dependency list -s: shallow (don't follow imports) -u: show NIL dependents -v: show files not from DF files"; Commander.Register[ key: "Dependencies", proc: DependenciesCommandProc, doc: doc, clientData: NIL, interpreted: TRUE ]; END. ~Dependencies.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) October 31, 1985 1:22:23 pm PST # of times to retry a server glitch # of seconds to pause for a server glitch # of processes to fork for help [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...] Now that we have out data structure, we should print it. First Char: [charSet: 0, char: '\n, looks: LOOKS[], format: NIL, comment: FALSE, endOfNode: TRUE, deltaLevel: 1, propList: LIST[^[key: $FromTiogaFile, val: $Yes]]] Comment Char: [charSet: 0, char: 'F, looks: LOOKS[], format: $code, comment: TRUE, endOfNode: FALSE, deltaLevel: 0, propList: NIL] Normal Char: [charSet: 0, char: 'F, looks: LOOKS[], format: $code, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, propList: NIL] Comment end of node (next node nested): [charSet: 0, char: '\n, looks: LOOKS[], format: $code, comment: TRUE, endOfNode: TRUE, deltaLevel: 1, propList: NIL] [c: CHAR] RETURNS [quit: BOOL _ FALSE] [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE] # of arguments processed When parsing the command line, be prepared for failure. The error is reported to the user Allows the user to specify personal defaults for the switches via the user's profile. Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user. It is a good idea to periodically check for a process abort request. Ignore null arguments (it is not easy to generate them, even). This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed. Perform whatever processing is necessary for a normal argument. [data: REF ANY, kind: GenerateDFClosure.ActionKind, name: ROPE, date: DFUtilities.Date, from: ROPE] The from file depends on the to file. The version stamps are present to remove name ambiguity. Process this file for dependencies Now insert the dependencies for this file [fullGName: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL] RETURNS [continue: BOOL] We will only accept a specific date At this point we will either accept anything, or we have a match on the time. [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key] [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison] [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison] We can use List.Sort because BaseEntryList is a list of REFs, and List.Sort only plays with the rest fields, not the first fields. ʾ˜codešœ™Kšœ Ïmœ1™ -- [data: RedBlackTree.UserData] RETURNS [stop: BOOL _ FALSE]šœ#˜#Kš :™:šžœžœž˜˜šžœžœž˜KšœN˜Nšžœžœž˜Kšœ žœžœžœ˜,Kšœ žœžœžœ˜+šžœ˜ K˜Kšœ˜Kšœ˜Kšœ˜šžœ ž˜Kšžœ˜Kšžœ˜—Kšœ˜Kšœ˜K˜šžœžœž˜Kšœ˜Kšœ ˜ Kšœ˜Kšžœ˜—Kšœ˜K˜——Kšžœ"žœžœžœ ˜CK˜Kšžœ˜——Kšžœ˜—K˜—Kšœ2˜2Kšœ˜Kšœ žœ˜Kšœžœ˜Kšœžœ˜K˜Kšœ˜Kšœ ˜ Kšœ ˜ K˜Kšœ žœ˜K˜Kšžœ<˜>Kšœ2˜2Kšœ'˜'Kšžœ˜K˜K˜—K˜—šœžœ˜Kšœ™—–I[cmd: Commander.Handle, starExpand: BOOL _ FALSE, switchChar: CHAR]šœKž˜PKšœ)žœžœ ˜8KšœZ™ZK˜—šœC˜CKšœU™U—K˜šžœžœžœž˜KšœÒ™ÒKšœžœ ˜˜KšœD™D—šžœžœžœ˜"Kšœ>™>—šžœžœ˜!Kšœ—™—Kšœ˜Kšžœ˜Kšœ˜—šœ˜Kšœ?™?—Kšžœ˜—šž˜Kšœ˜—K˜K˜—–g -- [data: REF ANY, kind: GenerateDFClosure.ActionKind, name: ROPE, date: DFUtilities.Date, from: ROPE]šœ*˜*Kš c™cKšœ"˜"Kšœ)˜)š Ÿœžœžœ žœ%žœ&˜yKšœ_™_Kšžœžœžœ˜Kšœ6˜6Kšœ1˜1Kšœžœ˜6Kšœ3˜3Kšžœ"žœžœžœ ˜CKšžœžœžœžœ ˜K˜š Ÿœžœ2žœžœžœ˜fKšœžœ˜7Kšœžœ˜ Kšœžœ˜š Ÿœžœžœžœžœžœ˜=K˜!—Kšœ5˜5K˜Kšžœ žœžœ˜AK˜—š Ÿ œžœ/žœžœžœžœ˜`Kšœžœ$˜.Kšœ;˜;K˜—Kšžœ3˜5K˜Kšœ)™)K˜Kšžœžœ*˜BK˜Kšœ#˜#K˜Kšžœ˜Kšžœ ˜Kšžœ žœžœ˜$K˜—K˜Kšžœ˜Kšžœ˜—K˜—K˜—Kšžœ˜—K˜K˜—šŸ œžœžœžœžœžœ žœžœ˜Ušœ%˜%Kš œ žœžœ žœžœ™EKšžœ žœ™šžœ žœ˜šžœžœ˜!Kšœ#™#Kšžœžœžœžœ˜$K˜—KšœM™MKšœ žœ˜Kšžœžœ˜Kšœ˜—Kšžœžœ˜K˜—Kšœ/žœ˜:K˜K˜—–< -- [data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]šœ˜Kš 8™8Kšžœ˜K˜K˜—–R -- [k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]šœ!˜!Kš N™NKšœžœžœ˜šžœžœž˜Kšœ#˜#Kšœžœ˜Kšžœžœ˜—šžœžœž˜Kšœžœžœ˜@Kšžœ˜—Kšžœ˜K˜K˜—–> -- [ref1: REF ANY, ref2: REF ANY] RETURNS [Basics.Comparison]šœ$˜$Kš :™:šžœžœž˜šœ˜šžœžœž˜šœ˜Kšžœ'žœ˜5—Kšžœ˜——Kšžœ˜—Kšžœ˜K˜K˜—šŸœžœžœžœ˜NKšœ‚™‚K–:[list: LIST OF REF ANY, compareProc: List.CompareProc]šžœžœ žœ˜=K˜K˜—šŸ œžœžœžœ ˜0šžœžœž˜šœ˜šžœžœž˜Kšœ˜Kšžœžœ˜—K˜—Kšžœ˜—Kšžœžœ˜K˜K˜—Kšœžœ¥˜®K˜–x[key: ROPE, proc: Commander.CommandProc, doc: ROPE _ NIL, clientData: REF ANY _ NIL, interpreted: BOOL _ TRUE]šœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ žœ˜Kšœ ž˜Kšœ˜—K˜Kšžœ˜K˜šœ˜K™——…—#ÞA