-- FFind.mesa last edited by:
-- JGS 1-Jul-82 12:42:59
-- Sweet 17-Sep-82 13:58:28
DIRECTORY
Ascii,
ByteBlt,
Environment,
Exec,
FileTransfer,
Format,
Heap,
MSegment,
Profile,
Stream;
FFind: PROGRAM IMPORTS ByteBlt, Exec, FileTransfer, Format, Heap, MSegment, Profile, Stream =
BEGIN
maxPatternLength: CARDINAL = 100;
pagesPerBuffer: CARDINAL = 100;
charsPerBuffer: CARDINAL = pagesPerBuffer*Environment.charsPerWord;
conn: FileTransfer.Connection ← NIL;
chars: LONG POINTER TO PACKED ARRAY OF CHARACTER;
out: Format.StringProc;
exec: Exec.Handle;
pattern: LONG STRING ← NIL;
Delta1: TYPE = ARRAY CHARACTER OF INTEGER;
Delta2: TYPE = RECORD [SEQUENCE COMPUTED INTEGER OF INTEGER];
delta1: LONG POINTER TO Delta1 ← NIL;
delta2: LONG POINTER TO Delta2 ← NIL;
nMatches, nFiles: LONG CARDINAL ← 0;
ignoreCase: BOOLEAN;
ExecCalling: Exec.ExecProc =
BEGIN
name, switches: LONG STRING ← NIL;
bufferSegment: MSegment.Handle ← NIL;
Finalize: PROC = {
IF delta1 # NIL THEN Heap.systemZone.FREE[@delta1];
IF delta2 # NIL THEN Heap.systemZone.FREE[@delta2];
IF bufferSegment # NIL THEN {
MSegment.Delete[bufferSegment]; bufferSegment ← NIL; chars ← NIL};
IF conn # NIL THEN {
FileTransfer.Close[conn]; FileTransfer.Destroy[conn]; conn ← NIL};
IF name # NIL THEN name ← Exec.FreeTokenString[name]};
BEGIN
ENABLE {
ABORTED => GO TO aborted;
FileTransfer.Error --[code]-- =>
SELECT code FROM
login => {LoginUser[clientData: NIL]; RETRY};
retry => GOTO timedOut;
unknown => GOTO fileTransferProblem;
ENDCASE;
UNWIND => Finalize[]};
exec ← h;
out ← Exec.OutputProc[h];
bufferSegment ← MSegment.Create[pages: pagesPerBuffer, release: []];
chars ← MSegment.Address[bufferSegment];
nMatches ← nFiles ← 0;
conn ← FileTransfer.Create[];
FileTransfer.SetProcs[
conn: conn, clientData: NIL, messages: PutMessages, login: LoginUser];
FileTransfer.SetPrimaryCredentials[
conn: conn, user: Profile.userName, password: Profile.userPassword];
[pattern, switches] ← Exec.GetToken[h];
-- do any switch processing here
ignoreCase ← FALSE;
IF switches # NIL THEN {
effect: BOOLEAN ← TRUE;
FOR i: CARDINAL IN [0..switches.length) DO
SELECT switches[i] FROM
'~, '- => effect ← FALSE;
'c,'C => {ignoreCase ← effect; effect ← TRUE};
ENDCASE => effect ← TRUE;
ENDLOOP;
switches ← Exec.FreeTokenString[switches]};
IF pattern = NIL THEN GOTO noPattern;
IF ignoreCase THEN
FOR i: CARDINAL IN [0..pattern.length) DO
pattern[i] ← ToLower[pattern[i]];
ENDLOOP;
MakeFailureFunctions[pattern];
DO
IF Exec.CheckForAbort[h] THEN {outcome ← abort; EXIT};
[name, switches] ← Exec.GetToken[h];
switches ← Exec.FreeTokenString[switches];
IF name = NIL THEN EXIT;
SearchFile[name];
name ← Exec.FreeTokenString[name];
IF Exec.CheckForAbort[h] THEN {outcome ← abort; EXIT};
ENDLOOP;
Format.LongDecimal[out, nFiles];
Format.Line[out, " files searched"L];
SELECT nMatches FROM
0 => Format.Text[out, "No"L];
ENDCASE => Format.LongDecimal[out, nMatches];
Format.Text[out, " match"L];
IF nMatches # 1 THEN {Format.Char[out, 'e]; Format.Char[out, 's]};
Format.Text[out, " found"];
outcome ← normal;
Finalize[];
EXITS
noPattern => {
outcome ← error;
Format.CR[out]; Format.Line[out, "...no pattern specified"L]};
aborted => {
outcome ← abort;
Format.CR[out]; Format.Line[out, "...aborted"L]};
timedOut => {
outcome ← error;
Format.CR[out]; Format.Line[out, "...connection timed out!"L]};
fileTransferProblem => {
outcome ← error;
Format.CR[out];
Format.Line[out, "...unknown FileTransfer problem!"L]};
END; -- of ENABLE
END;
PutMessages: FileTransfer.MessageProc = {
IF level = fatal THEN {
Format.Text[out, "Fatal error: "L];
IF s1 # NIL THEN Format.Text[out, s1];
IF s2 # NIL THEN Format.Text[out, s2];
IF s3 # NIL THEN Format.Text[out, s3];
IF s4 # NIL THEN Format.Text[out, s4]}};
LoginUser: FileTransfer.ClientProc --[clientData: LONG POINTER]-- = {
user: STRING = [40];
password: STRING = [40];
Exec.GetNameandPassword[exec, user, password];
FileTransfer.SetPrimaryCredentials[
conn: conn, user: user, password: password]};
SearchFile: PROC [name: LONG STRING] = {
OPEN FileTransfer;
ENABLE Error => IF code = skip THEN CONTINUE;
vfn: VFN ← AllocVFN[name];
stream: Stream.Handle;
stream ← ReadStream[conn, vfn ! UNWIND => FreeVFN[vfn]];
WHILE stream # NIL DO
ENABLE UNWIND => FreeVFN[vfn];
stream.options.signalEndOfStream ← TRUE;
nFiles ← nFiles + 1;
SearchStream[stream];
stream ← ReadNextStream[stream ! Error => IF code = skip THEN CONTINUE]
ENDLOOP};
SearchStream: PROC [stream: Stream.Handle] = {
source: FileTransfer.FileInfo;
j, k: INTEGER;
m: INTEGER = pattern.length;
nChars: INTEGER;
eof: BOOLEAN ← FALSE;
bufferOffset: LONG CARDINAL ← 0;
firstChar: CHARACTER = pattern[0];
LoadBlock: PROCEDURE = INLINE
BEGIN
bufferOffset ← bufferOffset + charsPerBuffer - maxPatternLength;
k ← maxPatternLength + k - nChars;
[] ← ByteBlt.ByteBlt[
from: [LOOPHOLE[chars], charsPerBuffer - maxPatternLength, charsPerBuffer],
to: [LOOPHOLE[chars], 0, charsPerBuffer]];
nChars ← Stream.GetBlock[
stream, [LOOPHOLE[chars], maxPatternLength, charsPerBuffer] !
Stream.EndOfStream => {eof ← TRUE; CONTINUE}].bytesTransferred + maxPatternLength;
IF ~eof THEN nChars ← charsPerBuffer;
END;
ShowMatch: PROC [index: INTEGER] =
BEGIN
begin, end: INTEGER ← index;
THROUGH [0..100) WHILE end < nChars DO
IF chars[end] = Ascii.CR THEN EXIT;
end ← end + 1;
ENDLOOP;
THROUGH [0..100) WHILE begin > 0 DO
IF chars[begin-1] = Ascii.CR THEN EXIT;
begin ← begin - 1;
ENDLOOP;
Format.LongDecimal[out, bufferOffset+index];
Format.Text[out, ": "L];
Format.Block[out, [LOOPHOLE[chars], begin, end]];
Format.CR[out];
nMatches ← nMatches + 1;
END;
IF Exec.CheckForAbort[exec] THEN ERROR ABORTED;
source ← FileTransfer.GetStreamInfo[stream];
Format.Text[out, "*** "L];
Format.Line[out, source.body];
nChars ← Stream.GetBlock[stream, [LOOPHOLE[chars], 0, charsPerBuffer] !
Stream.EndOfStream => {eof ← TRUE; CONTINUE}].bytesTransferred;
IF ~eof THEN nChars ← charsPerBuffer;
k ← m;
DO
DO
IF k <= nChars THEN EXIT;
IF eof THEN RETURN;
LoadBlock[];
ENDLOOP;
j ← m;
WHILE j > 0 AND chars[k-1] = pattern[j-1] DO
j ← j-1; k ← k-1;
ENDLOOP;
IF j = 0 THEN {ShowMatch[k]; k ← k+m+1}
ELSE k ← k + MAX[delta1[chars[k-1]], delta2[j-1]];
ENDLOOP;
};
MakeFailureFunctions: PROCEDURE [pat: LONG STRING] =
BEGIN
-- See Knuth, Morris, Pratt, "Fast Pattern ...", SIAM J. Comp, June 1977
i, j, k, t: INTEGER;
m: INTEGER = pat.length;
f: LONG POINTER TO Delta2 ←
Heap.systemZone.NEW[Delta2[m+1]]; -- auxilliary array
delta1 ← Heap.systemZone.NEW[Delta1 ← ALL[m]];
FOR i IN [1..m] DO delta1[pat[i-1]] ← m-i; ENDLOOP;
delta2 ← Heap.systemZone.NEW[Delta2[m]];
FOR k IN [1..m] DO
delta2[k-1] ← 2*m - k;
ENDLOOP;
j ← m; t ← j + 1;
WHILE j > 0 DO
f[j] ← t;
WHILE t <= m AND pat[j-1] # pat[t-1] DO
delta2[t-1] ← MIN [delta2[t-1], m-j];
t ← f[t];
ENDLOOP;
t ← t-1; j ← j-1;
ENDLOOP;
FOR k IN [1..t] DO
delta2[k-1] ← MIN[delta2[k-1], m+t-k];
ENDLOOP;
Heap.systemZone.FREE[@f];
END;
ToLower: PROCEDURE [ch: CHARACTER] RETURNS [CHARACTER] = INLINE {
-- test order significant
IF ch <= 'Z AND ch >= 'A THEN ch ← ch + ('a - 'A); RETURN[ch]};
Exec.AddCommand["FFind.~"L, ExecCalling];
END.