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