<> <> <> <> <> <> <> <> <<>> DIRECTORY Ascii USING [Upper], Commander USING [CommandProc, Register], CommandTool USING [ParseToList], FS USING [defaultStreamOptions, EnumerateForNames, Error, NameProc, StreamOpen, StreamOptions], Grep, IO USING [Close, EndOfStream, GetLine, PutBlock, PutChar, PutF, PutF1, PutRope, PutText, STREAM], RefText USING [TrustTextAsRope], Rope USING [Concat, Equal, Fetch, Length, ROPE], RegularExpression USING [CreateFromRope, Finder, MalformedPattern, SearchRope], RuntimeError USING [BoundsFault]; GrepImpl: CEDAR PROGRAM IMPORTS Ascii, Commander, CommandTool, FS, IO, RefText, Rope, RegularExpression, RuntimeError EXPORTS Grep = { ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; binaryFileExtensions: LIST OF ROPE _ LIST[".bcd", ".press", ".ip", ".interpress", ".symbols", ".tipc", ".boot", ".versionmap", ".bittable"]; GrepCmd: Commander.CommandProc = { <> stdout: STREAM _ cmd.out; stdin: STREAM _ cmd.in; stderr: STREAM _ cmd.err; cmdLine: LIST OF ROPE; fileCount: INT _ 0; totalNumberOfHits, totalFileHits, totalFilesExamined: INT _ 0; pattern: RegularExpression.Finder; interrupt: REF BOOL _ NEW[BOOL _ FALSE]; openOptions: FS.StreamOptions _ FS.defaultStreamOptions; stopOnFirstError: BOOL; ignoreTiogaFormatting: BOOL _ FALSE; binaryFilesToo: BOOL; literal, word, ignoreCase: BOOL; <> switches: Grep.SwitchSettings; regularOutput: BOOL; <> UsageMessage: PROC [] = { IO.PutRope[stderr, "Usage: GREP [switches] \n"]; }; GrepFile: FS.NameProc = { file: STREAM; hits: INT; continue _ TRUE; IF ~binaryFilesToo THEN FOR l: LIST OF ROPE _ binaryFileExtensions, l.rest UNTIL l = NIL DO IF Rope.Equal[fullFName, l.first, FALSE] THEN RETURN; ENDLOOP; file _ FS.StreamOpen[fullFName, read, openOptions ! FS.Error => IF error.group # bug THEN { IO.PutF1[stderr, "** %g\n", [rope[error.explanation]]]; GOTO openFailed; }]; IF switches[verbose] THEN IO.PutF1[stderr, "Searching \"%g\"...", [rope[fullFName]]]; hits _ GrepStream[pattern: pattern, inStream: file, fileName: fullFName, outStream: stdout, switches: switches, interrupt: interrupt]; totalNumberOfHits _ totalNumberOfHits + hits; totalFileHits _ totalFileHits + (IF hits > 0 THEN 1 ELSE 0); totalFilesExamined _ totalFilesExamined+1; IO.Close[file]; continue _ ~interrupt^; EXITS openFailed => { interrupt^ _ interrupt^ OR stopOnFirstError; continue _ ~interrupt^; }; }; cmdLine _ CommandTool.ParseToList[cmd].list; [literal, cmdLine] _ GetSwitch["-pattern", cmdLine]; literal _ ~literal; [ignoreCase, cmdLine] _ GetSwitch["-caseSensitive", cmdLine]; ignoreCase _ ~ignoreCase; [word, cmdLine] _ GetSwitch["-word", cmdLine]; [stopOnFirstError, cmdLine] _ GetSwitch["-stopOnFirstError", cmdLine]; [binaryFilesToo, cmdLine] _ GetSwitch["-binaryFilesToo", cmdLine]; [ignoreTiogaFormatting, cmdLine] _ GetSwitch["-ignoreTiogaFormatting", cmdLine]; [switches, cmdLine] _ GetSwitches[cmdLine]; IF SwitchesLeft[cmdLine, stderr] THEN GOTO prematureExit; IF cmdLine = NIL THEN { UsageMessage[]; GOTO prematureExit; }; regularOutput _ RegularOutput[switches]; pattern _ RegularExpression.CreateFromRope[pattern: cmdLine.first, literal: literal, word: word, ignoreCase: ignoreCase ! RegularExpression.MalformedPattern => { IO.PutF1[stderr, "Syntax error in pattern \"%g\"\n", [rope[cmdLine.first]]]; GOTO prematureExit}]; cmdLine _ cmdLine.rest; -- Get rid of the pattern. openOptions _ FS.defaultStreamOptions; openOptions[tiogaRead] _ ignoreTiogaFormatting; IF cmdLine = NIL THEN totalNumberOfHits _ GrepStream[pattern: pattern, inStream: stdin, fileName: "Standard input", outStream: stdout, interrupt: interrupt, switches: switches] ELSE FOR l: LIST OF ROPE _ cmdLine, l.rest UNTIL l = NIL DO currentFiles: INT _ totalFilesExamined; pattern: ROPE _ DefaultToHighestGeneration[l.first]; FS.EnumerateForNames[pattern, GrepFile ! FS.Error => IF error.group # bug THEN { IO.PutF1[stderr, "** %g\n", [rope[error.explanation]] ]; LOOP}; ]; IF interrupt^ THEN GOTO prematureExit; IF currentFiles = totalFilesExamined THEN IO.PutF1[stderr, "** No files examined for '%g'\n", [rope[pattern]] ]; ENDLOOP; IF regularOutput OR switches[verbose] THEN IO.PutF[stderr, "Files examined: %g, files matched: %g, number of matches: %g\n", [integer[totalFilesExamined]], [integer[totalFileHits]], [integer[totalNumberOfHits]]]; EXITS prematureExit => NULL; }; GetSwitches: PUBLIC PROC[cmdLine: LIST OF Rope.ROPE, prefixLen: INT _ 2] RETURNS[ switches: Grep.SwitchSettings, remainder: LIST OF Rope.ROPE ] = { [switches[oncePerLine], remainder] _ GetSwitch["-oncePerLine", cmdLine, prefixLen]; [switches[fileNamesOnly], remainder] _ GetSwitch["-fileNamesOnly", remainder, prefixLen]; [switches[textOnly], remainder] _ GetSwitch["-textOnly", remainder, prefixLen]; [switches[positionsOnly], remainder] _ GetSwitch["-positionsOnly", remainder, prefixLen]; [switches[verbose], remainder] _ GetSwitch["-verbose", remainder, prefixLen] }; GetSwitch: PROC [switch: ROPE, cmdLine: LIST OF ROPE, prefixLen: INT _ 2] RETURNS [present: BOOL _ FALSE, remainder: LIST OF ROPE] = { IF cmdLine = NIL THEN RETURN; IF Prefix[switch, cmdLine.first, prefixLen] THEN { present _ TRUE; remainder _ cmdLine.rest; } ELSE { remainder _ cmdLine; FOR l: LIST OF ROPE _ cmdLine, l.rest UNTIL l.rest = NIL DO IF Prefix[switch, l.rest.first, prefixLen] THEN { l.rest _ l.rest.rest; present _ TRUE; RETURN }; ENDLOOP; }; }; SwitchesLeft: PROC [cmdLine: LIST OF ROPE, stderr: STREAM, switchChar: CHAR _ '-] RETURNS [switchesLeft: BOOL _ FALSE] = { FOR l: LIST OF ROPE _ cmdLine, l.rest UNTIL l = NIL DO IF Rope.Length[l.first] >= 1 THEN IF Rope.Fetch[l.first] = switchChar THEN { IO.PutF1[stderr, "Invalid switch: \"%g\"\n", [rope[l.first]]]; switchesLeft _ TRUE; }; ENDLOOP; }; Prefix: PROC [r1, r2: ROPE, length: INT, ignoreCase: BOOL _ TRUE] RETURNS [BOOL] = { r2Len: INT _ r2.Length[]; r1Len: INT _ r1.Length[]; IF r1Len < length THEN ERROR; IF r2Len > r1Len OR r2Len < length THEN RETURN[FALSE]; IF ~ignoreCase THEN FOR i: INT IN [0..r2Len) DO IF r1.Fetch[i] # r2.Fetch[i] THEN RETURN[FALSE]; ENDLOOP ELSE FOR i: INT IN [0..r2Len) DO IF Ascii.Upper[r1.Fetch[i]] # Ascii.Upper[r2.Fetch[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; DefaultToHighestGeneration: PROC [filePattern: ROPE] RETURNS [ROPE] = { len: INT _ Rope.Length[filePattern]; bang: INT _ len; star: INT _ len; dot: INT _ len; pos: INT _ len; WHILE pos > 0 DO c: CHAR _ Rope.Fetch[filePattern, pos _ pos - 1]; SELECT c FROM '! => bang _ pos; '. => {dot _ pos; EXIT}; '* => IF star = len THEN star _ pos; '>, '] => EXIT; ENDCASE; ENDLOOP; SELECT TRUE FROM dot = len AND star = len AND bang = len => filePattern _ Rope.Concat[filePattern, ".mesa!h"]; bang = len => filePattern _ Rope.Concat[filePattern, "!h"]; ENDCASE; RETURN[filePattern]; }; RegularOutput: PUBLIC PROC[switches: Grep.SwitchSettings] RETURNS[ yes: BOOL ] = { yes _ ~(switches[fileNamesOnly] OR switches[textOnly] OR switches[positionsOnly] OR switches[verbose]) }; GrepStream: PUBLIC PROC [pattern: RegularExpression.Finder, inStream, outStream: IO.STREAM, switches: Grep.SwitchSettings, fileName: Rope.ROPE, interrupt: REF BOOL] RETURNS [numberOfHits: INT] = { found: BOOL; lineTooLongError: BOOL _ FALSE; at, atEnd, before, after: INT; regularOutput: BOOL = RegularOutput[switches]; position: INT _ 0; start: INT; line: REF TEXT _ NEW[TEXT[200]]; numberOfHits _ 0; DO <> line _ IO.GetLine[inStream, line ! IO.EndOfStream => EXIT; RuntimeError.BoundsFault => { IF ~lineTooLongError THEN IO.PutF[outStream, "Line too long in %g (%g). Line skipped.\n", [rope[fileName]], [integer[position]]]; lineTooLongError _ TRUE; LOOP; }; ]; start _ 0; DO <> [found, at, atEnd, before, after] _ RegularExpression.SearchRope[pattern, RefText.TrustTextAsRope[line], start, line.length, interrupt]; IF ~found THEN EXIT; IF switches[verbose] AND numberOfHits = 0 THEN IO.PutRope[outStream, "\n"]; numberOfHits _ numberOfHits + 1; SELECT TRUE FROM switches[fileNamesOnly] => { IO.PutF1[outStream, "%g\n", [rope[fileName]]]; GOTO prematureExit; }; switches[textOnly] => IO.PutF1[outStream, "%g\n", [text[line]]]; switches[positionsOnly] => IO.PutF[outStream, "%g (%g)\n", [rope[fileName]], [integer[position+at]]]; regularOutput => { IF numberOfHits = 1 THEN IO.PutF1[outStream, "%g\n", [rope[fileName]]]; IO.PutF1[outStream, " (%g): ", [integer[position+at]]]; IF line.length > 50 THEN { start: INT _ MAX[0, at-20]; end: INT _ MIN[line.length, start+50]; len: INT _ end-start; dots: BOOL _ end < line.length; IF start > 0 THEN IO.PutRope[outStream, "..."]; IO.PutBlock[self: outStream, block: line, startIndex: start, count: len]; IF dots THEN IO.PutRope[outStream, "..."]; } ELSE IO.PutText[outStream, line]; IO.PutChar[outStream, '\n]; }; switches[verbose] => IO.PutF[outStream, "%g (%g): %g\n", [rope[fileName]], [integer[position+at]], [text[line]]]; ENDCASE => ERROR; start _ atEnd; IF switches[oncePerLine] THEN EXIT; ENDLOOP; position _ position + line.length + 1; REPEAT prematureExit => NULL; ENDLOOP; IF switches[verbose] THEN IF numberOfHits = 0 THEN IO.PutRope[outStream, "no matches found.\n"] ELSE IO.PutF1[outStream, "Matched %g times.\n", [integer[numberOfHits]]] }; Commander.Register[ key: "Grep", proc: GrepCmd, doc: "Searches files for lines that match a pattern (uninterpreted)", interpreted: FALSE]; Commander.Register[ key: "GrepI", proc: GrepCmd, doc: "Searches files for lines that match a pattern (interpreted)", interpreted: TRUE]; }. <> <> <> <<>> <<>> <<>>