References
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];
-- We check for functionName and moduleName to appear somewhere in that source
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;
};
rootDF: root of the search
moduleName: short name of the BCD we are looking for
functionName: name of the function we are searching for
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;
};
resultAction is only called for processes which are done and when results are not NIL
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;
No more process possible in the pool, we do not fork!
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];
};
must only be called from an ENTRY PROC
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];
};
Failed
Message:
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]
];
};