-- 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.