<> <> <> <> DIRECTORY Ascii USING [CR, LF, SP, TAB], Basics, BasicTime, Commander USING [CommandProc, Handle, Register], CommandTool USING [DoCommand], FS, FileNames, Interpreter, IO, SymTab, RefText, TiogaAccess, List, Rope; UsingDependenciesImpl: CEDAR PROGRAM IMPORTS BasicTime, IO, FS, FileNames, Commander, CommandTool, Interpreter, RefText, Rope, List, TiogaAccess, SymTab ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Scanner: TYPE ~ REF ScannerRep; ScannerRep: TYPE ~ RECORD [ stream: IO.STREAM, tokenKind: Kind, inputBuffer: REF TEXT, locationOfBufferStart: INT, tokenStart: NAT, tokenLength: NAT ]; inputBufferSize: NAT _ 2*1024; CreateScanner: PROC RETURNS [scanner: Scanner] ~ { scanner _ NEW[ScannerRep]; scanner.inputBuffer _ NEW[TEXT[inputBufferSize]]; }; SetScanner: PROC [scanner: Scanner, stream: IO.STREAM] ~ { scanner.stream _ stream; scanner.tokenKind _ whitespace; scanner.locationOfBufferStart _ IO.GetIndex[stream]; scanner.tokenStart _ 0; scanner.tokenLength _ 0; scanner.inputBuffer.length _ 0; }; Kind: TYPE ~ {identifier, special, charconst, string, whitespace, newline, comment, eof}; KindOf: PROC [c: CHAR] RETURNS [Kind] ~ { SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '$ => RETURN [identifier]; Ascii.SP, Ascii.TAB, Ascii.LF => RETURN [whitespace]; Ascii.CR => RETURN [newline]; ENDCASE => RETURN [special]; }; breakLoc: INT _ LAST[INT]; breakCount: INT _ 0; GetCharConstant: PROC [scanner: Scanner] ~ { IF scanner.tokenStart>=scanner.inputBuffer.length-10 THEN RefillBuffer[scanner]; scanner.tokenKind _ charconst; scanner.tokenLength _ 2; IF scanner.inputBuffer[scanner.tokenStart+1] = '\\ THEN { IF scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] THEN { WHILE scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] DO scanner.tokenLength _ scanner.tokenLength + 1; ENDLOOP; } ELSE scanner.tokenLength _ 3; }; }; GetStringConstant: PROC [scanner: Scanner] ~ { GetC: PROC ~ { IF scanner.tokenStart+scanner.tokenLength>=scanner.inputBuffer.length-10 THEN RefillBuffer[scanner]; scanner.tokenLength _ scanner.tokenLength + 1; IF scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] = '\\ THEN { scanner.tokenLength _ scanner.tokenLength + 1; IF scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] THEN { WHILE scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] IN ['0..'7] DO scanner.tokenLength _ scanner.tokenLength + 1; ENDLOOP; } ELSE scanner.tokenLength _ scanner.tokenLength + 1; }; }; scanner.tokenLength _ 0; GetC[]; UNTIL scanner.inputBuffer[scanner.tokenStart+scanner.tokenLength] = '" DO GetC[]; ENDLOOP; scanner.tokenLength _ scanner.tokenLength + 1; scanner.tokenKind _ string; }; GetComment: PROC [scanner: Scanner] ~ { c: CHAR _ ' ; -- next char after the current end-of-token cc: CHAR _ ' ; -- char after c PeekCC: PROC ~ { IF scanner.tokenStart+scanner.tokenLength+2>=scanner.inputBuffer.length THEN RefillBuffer[scanner]; c _ cc _ '\n; IF scanner.tokenStart+scanner.tokenLength> deftail, result: ROPE _ NIL; noResult: BOOL _ TRUE; [result, , noResult] _ Interpreter.EvaluateToRope[def]; IF noResult OR result=NIL THEN RETURN["??"]; deftail _ FileNames.Tail[def, '.]; result _ IF Rope.Equal[deftail, FileNames.Tail[result, '.], TRUE] THEN Rope.Substr[base: result, start: 0, len: Rope.Length[result]-Rope.Length[deftail]-1] ELSE "!!"; RETURN[IF Rope.Length[result]=0 THEN "!!" ELSE result]; }; DReverse: PROC [old: LIST OF ROPE] RETURNS [new: LIST OF ROPE _ NIL] ~ { <> WHILE old # NIL DO t: LIST OF ROPE ~ old; old _ t.rest; t.rest _ new; new _ t; ENDLOOP; }; Break: PROC [char: CHAR] RETURNS [IO.CharClass] = { IF char = '_ OR char = '; THEN RETURN [break]; IF char = ' OR char = ' OR char = ', OR char = '\n THEN RETURN [sepr]; RETURN [other]; }; GetCmdToken: PROC [stream: IO.STREAM] RETURNS [rope: ROPE _ NIL] ~ { rope _ IO.GetTokenRope[stream, Break ! IO.EndOfStream => CONTINUE].token; }; FindNewer: PROC [fileName: ROPE, newerThan: BasicTime.GMT] RETURNS [fullName: ROPE _ NIL] ~ { full: ROPE _ NIL; infoProc: FS.InfoProc ~ {IF BasicTime.Period[from: newerThan, to: created] > 0 THEN {fullName _ fullFName; continue _ FALSE}}; FS.EnumerateForInfo[fileName, infoProc]; }; UsingDependenciesCommand: Commander.CommandProc ~ { Reformat: PROC [filename: ROPE] ~ { 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; }; PutCharB: Rope.ActionType = { <<[c: CHAR] RETURNS [quit: BOOL _ FALSE]>> tc.char _ c; TiogaAccess.Put[writer, tc]; }; PutRope: PROC [rope: ROPE] = { [] _ Rope.Map[base: rope, action: PutCharB]; }; PutRopeItalic: PROC [rope: ROPE] = { tc.looks['i] _ TRUE; [] _ Rope.Map[base: rope, action: PutCharB]; tc.looks['i] _ FALSE; }; PutRopeBold: PROC [rope: ROPE] = { tc.looks['b] _ TRUE; [] _ Rope.Map[base: rope, action: PutCharB]; tc.looks['b] _ FALSE; }; tc: TiogaAccess.TiogaChar _ [ charSet: 0, char: '\n, looks: ALL[FALSE], format: $code, comment: FALSE, endOfNode: FALSE, deltaLevel: 0, <> propList: NIL ]; nextS: IO.STREAM; in: IO.STREAM; nextRope: ROPE; ropeList, newList: LIST OF ROPE; writer: TiogaAccess.Writer _ TiogaAccess.Create[]; in _ FS.StreamOpen[fileName: filename, accessOptions: $read ! FS.Error => {result _ $Failure; msg _ error.explanation; CONTINUE}]; IF in = NIL THEN RETURN; <> UNTIL IO.EndOf[in] DO newList _ ropeList _ NIL; nextS _ IO.RIS[IO.GetLineRope[stream: in]]; -- get next rope from input nextRope _ IO.GetTokenRope[nextS, IO.TokenProc].token; -- first token, boldface PutRopeBold[Rope.Concat[nextRope, ":"]]; UNTIL IO.EndOf[nextS] DO ropeList _ CONS[IO.GetTokenRope[nextS, IO.TokenProc ! IO.EndOfStream => CONTINUE;].token, ropeList]; ENDLOOP; IF ropeList#NIL THEN { newList _ DReverse[ropeList]; -- destructive reverse nextRope _ IF newList.rest=NIL THEN newList.first ELSE newList.rest.first; -- initialize in case never do following loop FOR r: LIST OF ROPE _ newList, r.rest UNTIL r.rest=NIL DO -- do all but last rope PutRope[Rope.Concat[" ", r.first]]; nextRope _ r.rest.first; ENDLOOP; IF doImpls THEN PutRopeItalic[Rope.Concat[" ", nextRope]] -- last rope is Implementor, italics ELSE PutRope[Rope.Concat[" ", nextRope]]; -- last rope is not Implementor EndNode[delta: 0, format: $code]; }; ENDLOOP; TiogaAccess.WriteFile[writer, filename]; }; switchChar: CHAR = '-; gets: ROPE _ NIL; outputStream: IO.STREAM _ NIL; symTab: SymTab.Ref _ SymTab.Create[997, TRUE]; scanner: Scanner _ CreateScanner[]; c: IO.STREAM _ IO.RIS[cmd.commandLine]; outputName: ROPE _ GetCmdToken[c]; IF outputName=NIL OR Rope.Equal[outputName, ""] THEN RETURN[$Failure, docRope]; doImpls _ reformat _ FALSE; WHILE Rope.Fetch[base: outputName, index: 0]=switchChar DO FOR index: INT IN [1..Rope.Length[outputName]) DO SELECT Rope.Fetch[base: outputName, index: index] FROM 'i => doImpls _ TRUE; 'f => reformat _ TRUE; ENDCASE; ENDLOOP; outputName _ GetCmdToken[c]; ENDLOOP; gets _ GetCmdToken[c]; IF NOT gets.Equal["_"] THEN RETURN[$Failure, docRope]; outputStream _ FS.StreamOpen[fileName: outputName, accessOptions: $create, keep: 2 ! FS.Error => {result _ $Failure; msg _ error.explanation; CONTINUE}]; IF outputStream = NIL THEN RETURN; FOR stem: ROPE _ GetCmdToken[c], GetCmdToken[c] UNTIL stem.Length = 0 DO pattern: ROPE _ IF Rope.Find[stem, ".bcd", 0, FALSE] < 0 THEN Rope.Concat[stem, ".bcd"] ELSE stem; ForEachFile: PROC [fullFName: ROPE] RETURNS [continue: BOOLEAN _ TRUE] ~ { bcdDate: BasicTime.GMT ~ FS.FileInfo[name: fullFName, remoteCheck: FALSE].created; base: ROPE; cp: FS.ComponentPositions; useName: ROPE _ Rope.Concat[base, ".usingList"]; [fullFName, cp] _ FS.ExpandName[fullFName]; base _ fullFName.Substr[cp.base.start, cp.base.length]; useName _ FindNewer[Rope.Concat[base, ".usingList"], bcdDate]; IF useName=NIL THEN { result _ CommandTool.DoCommand[Rope.Concat["UsingList ", fullFName], cmd]; useName _ Rope.Concat[base, ".usingList"]; } ELSE { cmd.out.PutF["Using old %g\n", IO.rope[useName]]; result _ NIL; }; IF result = $Failure THEN NULL ELSE { stream: IO.STREAM _ NIL; stream _ FS.StreamOpen[useName ! FS.Error => {cmd.out.PutRope[error.explanation]; cmd.out.PutChar['\n]; CONTINUE}]; IF stream # NIL THEN { SetScanner[scanner, stream]; RecordDependencies[scanner, base, cmd, symTab]; stream.Close; }; }; }; IF Rope.Find[pattern, "*"] >= 0 THEN FS.EnumerateForNames[pattern, ForEachFile ! FS.Error => {msg _ error.explanation; result _ $Failure; CONTINUE}] ELSE [] _ ForEachFile[pattern ! FS.Error => {msg _ error.explanation; result _ $Failure; CONTINUE}]; IF result = $Failure THEN RETURN; ENDLOOP; WriteResults[outputStream, symTab, cmd]; outputStream.Close; <> IF reformat THEN Reformat[outputName]; cmd.out.PutF["%g written.\n", IO.rope[outputName]]; }; reformat: BOOL _ FALSE; doImpls: BOOL _ FALSE; docRope: ROPE ~ "Create listing of procedure-level dependencies from bcds ([switches] _ )\n"; Commander.Register["UsingDependencies", UsingDependenciesCommand, docRope]; END.