DIRECTORY BasicTime, Buttons, CedarProcess, DFUtilities, DFUtilitiesExtras, FileNames, FS, HashTable, IO, MessageWindow, Process, PopUpSelection, RefText, RegularExpression, Rope, RopeFile, TiogaMenuOps, UserProfile, ViewerClasses, ViewerOps, ViewerTools; TopButtonsImpl: CEDAR MONITOR IMPORTS BasicTime, Buttons, CedarProcess, DFUtilities, DFUtilitiesExtras, FileNames, FS, HashTable, IO, MessageWindow, Process, PopUpSelection, RefText, RegularExpression, Rope, RopeFile, TiogaMenuOps, UserProfile, ViewerOps, ViewerTools ~ BEGIN ROPE: TYPE = Rope.ROPE; ROPES: TYPE = LIST OF ROPE; IsFileName: PROC [selection: ROPE] RETURNS [BOOL] = { RETURN [Rope.Length[selection]>1 AND Rope.SkipTo[selection, 0, " \n\t\l\f\b;,""^_|=)(*&~@"]=Rope.Length[selection]]; }; RequestSelection: PROC [label: ROPE, choice: ROPES] RETURNS [chosen: ROPE _ NIL] ~ { index: INT _ PopUpSelection.Request[header: label, choice: choice, default: 0, timeOut: 10]; IF index=0 THEN RETURN [""]; IF index=-1 THEN RETURN; WHILE index#1 DO choice _ choice.rest; index _ index -1 ENDLOOP; chosen _ choice.first; }; directories: ROPES _ NIL; catalogs: ROPES _ NIL; refsButtonCreated: BOOL _ FALSE; rootDFs: ROPES _ NIL; NoteProfile: UserProfile.ProfileChangedProc ~ { directories _ UserProfile.ListOfTokens["TopButtons.Directories", LIST [FileNames.HomeDirectory[]]]; catalogs _ UserProfile.ListOfTokens["TopButtons.Catalogs", LIST ["CedarCatalog", "CedarChestCatalog"]]; rootDFs _ UserProfile.ListOfTokens["TopButtons.RootDFs"]; IF NOT refsButtonCreated AND rootDFs#NIL THEN {[] _ Buttons.Create[info: [name: "Refs"], proc: Refs]; refsButtonCreated _ TRUE}; }; Open: Buttons.ButtonProc ~ { selection: ROPE _ ViewerTools.GetSelectionContents[]; viewer: ViewerClasses.Viewer; IF IsFileName[selection] THEN { selection _ FileNames.ConvertToSlashFormat[selection]; IF Rope.Fetch[selection]#'/ THEN { directory: ROPE _ RequestSelection[Rope.Cat["Open ", selection, " among:"], directories]; IF directory=NIL THEN RETURN; -- menu discarded directory _ FileNames.ConvertToSlashFormat[directory]; IF NOT Rope.IsEmpty[directory] AND Rope.Fetch[directory, Rope.Length[directory]-1]='/ THEN selection _ Rope.Cat[directory, selection]; }; viewer _ TiogaMenuOps.Open[selection]; IF viewer=NIL THEN RETURN; } ELSE { directory: ROPE _ RequestSelection["New in directory:", directories]; IF Rope.IsEmpty[directory] THEN {MessageWindow.Append["Directory menu for new discarded", TRUE]; RETURN}; viewer _ TiogaMenuOps.Open[directory]; IF viewer=NIL THEN RETURN; IF Rope.IsEmpty[ViewerTools.GetContents[viewer]] THEN ViewerTools.SetSelection[viewer]; }; IF shift THEN ViewerOps.GrowViewer[viewer]; }; Doc: Buttons.ButtonProc ~ { selection: ROPE _ ViewerTools.GetSelectionContents[]; viewer: ViewerClasses.Viewer; IF IsFileName[selection] THEN { SELECT TRUE FROM Rope.Match["*Doc.tioga", selection] => {}; Rope.Match["*Doc", selection] => {}; Rope.Match["*.*", selection] => selection _ Rope.Replace[base: selection, start: Rope.Index[selection, 0, "."], with: "Doc.tioga"]; ENDCASE => selection _ Rope.Cat[selection, "Doc.tioga"]; viewer _ TiogaMenuOps.Open[selection]; } ELSE { catalog: ROPE _ RequestSelection["Catalogs:", catalogs]; IF Rope.IsEmpty[catalog] THEN {MessageWindow.Append["Catalog menu discarded", TRUE]; RETURN}; viewer _ TiogaMenuOps.Open[catalog]; }; IF viewer=NIL THEN RETURN; IF shift THEN ViewerOps.GrowViewer[viewer]; }; CheckDFData: TYPE = REF CheckDFDataRec; CheckDFDataRec: TYPE = RECORD [dfName: ROPE, state: State]; CheckFileData: TYPE = REF CheckFileDataRec; CheckFileDataRec: TYPE = RECORD [fileName: ROPE, state: State]; State: TYPE = REF StateRec; -- modification of state must always be monitored StateRec: TYPE = RECORD [ dfSeen: HashTable.Table, -- HashTable [DF full Name -> NIL]: list of all DF full names already seen (or being seen) moduleName, functionName: ROPE, output: ViewerClasses.Viewer _ NIL, pool: ProcessPool, matches: INT _ 0 ]; streamOptions: FS.StreamOptions = [tiogaRead: FALSE, commitAndReopenTransOnFlush: TRUE, truncatePagesOnClose: TRUE, finishTransOnClose: TRUE, closeFSOpenFileOnClose: TRUE]; CheckDF: CedarProcess.ForkableProc = { ProcessItem1: DFUtilities.ProcessItemProc = { WITH item SELECT FROM directory: REF DFUtilities.DirectoryItem => {}; file: REF DFUtilities.FileItem => IF Rope.Match[importeePattern, file.name, FALSE] THEN defining _ TRUE; import: REF DFUtilities.ImportsItem => { DFUtilities.SortUsingList[import.list, TRUE]; IF import.form=list AND DFUtilities.SearchUsingList[importeeName, import.list].found THEN importing _ TRUE; IF import.exported THEN { new: CheckDFData = NEW [CheckDFDataRec _ [dfName: import.path1, state: state]]; [] _ ForkInPool[state.pool, CheckDF, new]; }; }; include: REF DFUtilities.IncludeItem => { new: CheckDFData = NEW [CheckDFDataRec _ [dfName: include.path1, state: state]]; [] _ ForkInPool[state.pool, CheckDF, new]; }; ENDCASE => ERROR; }; ProcessItem2: DFUtilities.ProcessItemProc = { WITH item SELECT FROM directory: REF DFUtilities.DirectoryItem => currentDirectory _ directory.path1; file: REF DFUtilities.FileItem => IF Rope.Match["*.mesa!*", file.name, FALSE] THEN { new: CheckFileData = NEW [CheckFileDataRec _ [fileName: FS.ExpandName[file.name, currentDirectory].fullFName, state: state]]; [] _ ForkInPool[state.pool, CheckFile, new]; }; import: REF DFUtilities.ImportsItem => {}; include: REF DFUtilities.IncludeItem => {}; ENDCASE => ERROR; }; processes: LIST OF CedarProcess.Process _ NIL; checkDFData: CheckDFData = NARROW [data]; dfName: ROPE = checkDFData.dfName; state: State = checkDFData.state; currentDirectory: ROPE; importing, defining: BOOL _ FALSE; importeeName: ROPE = Rope.Cat[state.moduleName, ".bcd"]; importeePattern: ROPE = Rope.Cat[state.moduleName, ".bcd*"]; IF HashTable.Fetch[state.dfSeen, dfName].found THEN RETURN; [] _ HashTable.Store[state.dfSeen, dfName, NIL]; DFUtilitiesExtras.ParseFromFile[dfName, ProcessItem1 ! FS.Error => {FailedMessage[state, Rope.Cat["Cannot open: ", dfName]]; GOTO Fails}; DFUtilitiesExtras.FileSyntaxError => {FailedMessage[state, Rope.Cat["Cannot parse: ", dfName]]; GOTO Fails}]; IF importing OR defining THEN DFUtilitiesExtras.ParseFromFile[dfName, ProcessItem2]; EXITS Fails => {}; }; QuickSearch: PROC [fileName: ROPE, rope: ROPE] RETURNS [possible: BOOL _ FALSE] = { stream: IO.STREAM = FS.StreamOpen[fileName: fileName, streamOptions: streamOptions]; firstChar: CHAR = Rope.Fetch[rope]; restText: REF TEXT = Rope.ToRefText[Rope.Substr[rope, 1]]; buffer: REF TEXT = RefText.ObtainScratch[RefText.line]; DO WHILE IO.GetChar[stream ! IO.EndOfStream => EXIT]#firstChar DO ENDLOOP; -- optimization IF RefText.Find[IO.GetLine[stream, buffer ! ANY => EXIT], restText]#-1 THEN {possible _ TRUE; EXIT}; ENDLOOP; IO.Close[stream]; RefText.ReleaseScratch[buffer]; }; CheckFile: CedarProcess.ForkableProc = { checkFileData: CheckFileData = NARROW [data]; fileName: ROPE = checkFileData.fileName; state: State = checkFileData.state; MessageWindow.Append[Rope.Cat["Searching: ", fileName], TRUE]; IF NOT QuickSearch[fileName, state.functionName] OR NOT QuickSearch[fileName, state.moduleName] THEN RETURN; BEGIN output: ROPE _ NIL; rope: ROPE _ RopeFile.Create[fileName]; finder: RegularExpression.Finder = RegularExpression.CreateFromRope[pattern: state.functionName, literal: TRUE, word: TRUE, ignoreCase: FALSE]; localMatches: INT _ 0; start: INT _ 0; found: BOOL _ TRUE; WHILE found DO at, atEnd: INT; [found: found, at: at, atEnd: atEnd] _ RegularExpression.SearchRope[finder, rope, start]; IF ~found THEN LOOP; IF localMatches=0 THEN output _ Rope.Cat[fileName, ":\t"]; output _ Rope.Cat[output, IO.PutR1[IO.int[at]], " "]; localMatches _ localMatches + 1; start _ atEnd + 1; ENDLOOP; IF localMatches=0 THEN RETURN; IncrMatches[state, output]; END; }; FindReferences: PROC [rootDF, moduleName, functionName: ROPE] = { time: BasicTime.GMT _ BasicTime.Now[]; state: State = NEW [StateRec _ [ moduleName: moduleName, functionName: functionName, dfSeen: HashTable.Create[equal: HashTable.RopeEqualModCase, hash: HashTable.HashRopeModCase], output: ViewerTools.MakeNewTextViewer[[ name: Rope.Cat["REFS OF: ", moduleName, ".", functionName] ]], pool: CreateProcessPool[] ]]; ViewerTools.SetContents[state.output, Rope.Cat["References of ", moduleName, ".", functionName, Rope.Cat[" in ", rootDF, ":\n"]]]; [] _ ForkInPool[state.pool, CheckDF, NEW [CheckDFDataRec _ [dfName: rootDF, state: state]], [priority: background, usePriority: TRUE]]; JoinPool[state.pool]; MessageWindow.Append["References search done!", TRUE]; ViewerTools.SetContents[state.output, IO.PutFR["%g%g DF files considered in %g seconds;\t%g file%g matched.\n", IO.rope[ViewerTools.GetContents[state.output]], IO.int[HashTable.GetSize[state.dfSeen]], IO.int[BasicTime.Period[time, BasicTime.Now[]]], IO.int[state.matches], IO.rope[IF state.matches=1 THEN NIL ELSE "s"]]]; ViewerOps.OpenIcon[state.output]; }; ProcessPool: TYPE = REF ProcessPoolRec; ProcessPoolRec: TYPE = RECORD [ resultAction: ResultActionProc _ NIL, processes: SEQUENCE maxProcesses: NAT OF CedarProcess.Process ]; ResultActionProc: TYPE = PROC [REF]; -- applied to results of actions, only if those results are NON-NIL CreateProcessPool: ENTRY PROC [maxProcesses: NAT _ 4, resultAction: ResultActionProc _ NIL] RETURNS [pool: ProcessPool] = { pool _ NEW [ProcessPoolRec[maxProcesses]]; pool.resultAction _ resultAction; }; ForkInPool: PROC [pool: ProcessPool, action: CedarProcess.ForkableProc, data: REF _ NIL, options: CedarProcess.ForkOptions _ CedarProcess.DefaultForkOptions] RETURNS [forked: BOOL] = { IF TryToForkInPool[pool, action, data, options] THEN RETURN; Process.Yield[]; IF TryToForkInPool[pool, action, data, options] THEN RETURN; BEGIN results: REF = action[data]; IF results#NIL AND pool.resultAction#NIL THEN pool.resultAction[results]; RETURN [FALSE]; END; }; TryToForkInPool: ENTRY PROC [pool: ProcessPool, action: CedarProcess.ForkableProc, data: REF, options: CedarProcess.ForkOptions] RETURNS [forked: BOOL] = { LaunderPool[pool]; FOR i: NAT IN [0 .. pool.maxProcesses) DO IF pool[i]=NIL THEN {pool[i] _ CedarProcess.Fork[action, data, options]; RETURN [TRUE]}; ENDLOOP; RETURN [FALSE]; }; JoinPool: PROC [pool: ProcessPool] = { UNTIL IsPoolDone[pool] DO Process.Pause[Process.MsecToTicks[1000]] ENDLOOP; }; IsPoolDone: ENTRY PROC [pool: ProcessPool] RETURNS [done: BOOL _ FALSE] = { LaunderPool[pool]; FOR i: NAT IN [0 .. pool.maxProcesses) DO IF pool[i]#NIL THEN RETURN [FALSE] ENDLOOP; RETURN[TRUE]; }; LaunderPool: PROC [pool: ProcessPool] = { FOR i: NAT IN [0 .. pool.maxProcesses) DO IF pool[i]=NIL THEN LOOP; IF pool[i].status=aborted THEN {pool[i] _ NIL; LOOP}; IF pool[i].status=done THEN { IF pool[i].results#NIL AND pool.resultAction#NIL THEN pool.resultAction[pool[i].results]; pool[i] _ NIL; }; ENDLOOP; }; Message: INTERNAL PROC [viewer: ViewerClasses.Viewer, rope: ROPE] = { ViewerTools.InhibitUserEdits[viewer]; ViewerTools.SetContents[viewer, Rope.Cat[ViewerTools.GetContents[viewer], rope, "\n"]]; ViewerTools.EnableUserEdits[viewer]; }; FailedMessage: ENTRY PROC [state: State, rope: ROPE] = { Message[state.output, rope]; }; IncrMatches: ENTRY PROC [state: State, rope: ROPE] = { Message[state.output, rope]; state.matches _ state.matches + 1; }; Refs: Buttons.ButtonProc ~ { selection: ROPE _ ViewerTools.GetSelectionContents[]; dot: INT = Rope.Find[selection, "."]; rootDF: ROPE; IF dot<=0 OR Rope.Length[selection]<=dot+1 OR Rope.Find[selection, ".", dot+1]#-1 THEN {MessageWindow.Append["Selection is not of the from *.*", TRUE]; RETURN}; rootDF _ RequestSelection[Rope.Cat["Search ", selection, " in:"], rootDFs]; IF NOT Rope.IsEmpty[rootDF] THEN FindReferences[ rootDF: rootDF, moduleName: Rope.Substr[selection, 0, dot], functionName: Rope.Substr[selection, dot+1] ]; }; UserProfile.CallWhenProfileChanges[NoteProfile]; END. ΞTopButtonsImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Bertrand Serlet, January 27, 1987 6:07:25 pm PST This module turns the Open and New buttons located on top on a regular Cedar screen into something useful. Auxiliary functions User Profile Management Open Doc References -- We check for functionName and moduleName to appear somewhere in that source rootDF: root of the search moduleName: short name of the BCD we are looking for functionName: name of the function we are searching for resultAction is only called for processes which are done and when results are not NIL No more process possible in the pool, we do not fork! must only be called from an ENTRY PROC Initialization Κ ‡˜codešœ™Kšœ Οmœ1™Ÿœ Ÿœ˜kšŸœŸœ˜JšœŸœ9˜OJšœ*˜*J˜—J˜—šœ Ÿœ˜+JšœŸœ:˜PJšœ*˜*J˜—JšŸœ Ÿœ˜—J˜—š  œ!˜-šŸœŸœŸ˜Jšœ ŸœA˜OšœŸœŸœ#Ÿœ˜QšŸœ˜JšœŸœ ŸœC˜}Jšœ,˜,J˜——JšœŸœ ˜+Jšœ Ÿœ ˜,JšŸœ Ÿœ˜—J˜—Jšœ ŸœŸœŸœ˜.JšœŸœ˜)JšœŸœ˜"Jšœ!˜!JšœŸœ˜JšœŸœŸœ˜"JšœŸœ&˜8JšœŸœ'˜˜TJšœ Ÿœ˜#Jšœ ŸœŸœ(˜:KšœŸœŸœ'˜7šŸ˜Kš ŸœŸœŸœŸœ ŸœŸœ‘˜WKšŸœŸœŸœŸœŸœ ŸœŸœ˜dKšŸœ˜—JšŸœ˜Jšœ˜Jšœ˜J˜—š  œ˜(JšœŸœ˜-Jšœ Ÿœ˜(Jšœ#˜#Jšœ8Ÿœ˜?JšœN™NJš ŸœŸœ+ŸœŸœ)ŸœŸœ˜lšŸ˜JšœŸœŸœ˜JšœŸœ˜'JšœjŸœŸœŸœ˜JšœŸœ˜JšœŸœ˜JšœŸœŸœ˜šŸœŸ˜Jšœ Ÿœ˜JšœY˜YJšŸœŸœŸœ˜JšŸœŸœ$˜:JšœŸœŸœ˜5Jšœ!˜!Jšœ˜JšŸœ˜—JšŸœŸœŸœ˜Kšœ˜JšŸœ˜—J˜J˜—Jšœ‘™Jšœ ‘(™4Jšœ‘)™7š œŸœ$Ÿœ˜AJšœŸœ˜&šœŸœ˜ Jšœ4˜4Jšœ]˜]šœ'˜'Jšœ:˜:Jšœ˜—Jšœ˜Jšœ˜—Jšœ‚˜‚Jšœ%ŸœXŸœ˜‡Jšœ˜Jšœ0Ÿœ˜6Jšœ&ŸœHŸœ.Ÿœ'Ÿœ/ŸœŸœŸœŸœŸœŸœ ˜ΒJšœ!˜!J˜J˜—Kšœ ŸœŸœ˜'šœŸœŸœ˜Kšœ!Ÿœ˜%Kšœ ŸœŸœŸœ˜=K˜K˜—šœŸœŸœŸœ‘C˜hK˜—š  œŸœŸœŸœ'ŸœŸœ˜{KšœŸœ ˜*Kšœ!˜!K˜K˜—KšœU™Uš   œŸœ>ŸœŸœGŸœ Ÿœ˜ΈKšŸœ.ŸœŸœ˜Ÿœ%Ÿœ Ÿœ˜›Kšœ˜šŸœŸœŸœŸ˜)Kš Ÿœ ŸœŸœ6ŸœŸœ˜XKšŸœ˜—KšŸœŸœ˜K˜K˜—š œŸœ˜&KšŸœŸœ*Ÿœ˜KK˜K˜—š   œŸœŸœŸœŸœŸœ˜KKšœ˜šŸœŸœŸœŸ˜)Kš Ÿœ ŸœŸœŸœŸœ˜"KšŸœ˜—KšŸœŸœ˜ K˜K˜—Kšœ&™&š  œŸœ˜)šŸœŸœŸœŸ˜)KšŸœ ŸœŸœŸœ˜KšŸœŸœ ŸœŸœ˜5šŸœŸœ˜Kš ŸœŸœŸœŸœŸœ$˜YKšœ Ÿœ˜Kšœ˜—KšŸœ˜—K˜Kšœ˜—š œŸœŸœ&Ÿœ˜EKšœ%˜%KšœW˜WKšœ$˜$K˜K˜—š œ œŸœŸœŸœ˜8Kšœ˜K˜K˜—š  œŸœŸœŸœ˜6Kšœ˜Kšœ"˜"K˜K˜—š œ˜Kšœ Ÿœ&˜5KšœŸœ˜%KšœŸœ˜ Kš ŸœŸœŸœ%Ÿœ;ŸœŸœ˜ KšœK˜KšŸœŸœŸœ˜0Kšœ˜Kšœ,˜,Kšœ+˜+Kšœ˜—J˜——™Kšœ0˜0K˜—šŸœ˜K˜——…—.ˆ>έ