DIRECTORY Ascii USING [Upper], Commander USING [CommandProc, Register], CommanderOps USING [ParseToList], FS USING [defaultStreamOptions, EnumerateForNames, Error, NameProc, StreamOpen, StreamOptions], Grep, IO USING [Close, EndOfStream, GetLine, PutBlock, PutChar, PutF, PutF1, PutRope, PutText, STREAM], Process USING [ CheckForAbort ], 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, CommanderOps, FS, IO, Process, 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; enumerateCache: BOOL _ FALSE; binaryFilesToo: BOOL; isPattern, word, caseSensitive: 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[fileName: fullFName, accessOptions: read, streamOptions: openOptions, remoteCheck: NOT enumerateCache ! 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 _ CommanderOps.ParseToList[cmd].list; [isPattern, cmdLine] _ GetSwitch["-pattern", cmdLine]; [caseSensitive, cmdLine] _ GetSwitch["-caseSensitive", cmdLine]; [word, cmdLine] _ GetSwitch["-word", cmdLine]; [stopOnFirstError, cmdLine] _ GetSwitch["-stopOnFirstError", cmdLine]; [binaryFilesToo, cmdLine] _ GetSwitch["-binaryFilesToo", cmdLine]; [ignoreTiogaFormatting, cmdLine] _ GetSwitch["-ignoreTiogaFormatting", cmdLine]; [enumerateCache, cmdLine] _ GetSwitch["-enumerateCache", cmdLine]; IF enumerateCache THEN RETURN [$Failure, "-enumerateCache not supported in PCedar"]; [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: NOT isPattern, word: word, ignoreCase: NOT caseSensitive ! 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]; { ENABLE FS.Error => IF error.group # bug THEN { IO.PutF1[stderr, "** %g\n", [rope[error.explanation]] ]; LOOP; }; FS.EnumerateForNames[pattern, GrepFile]; }; 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]) AND NOT switches[quiet] 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[locations], remainder] _ GetSwitch["-locations", remainder, prefixLen]; [switches[verbose], remainder] _ GetSwitch["-verbose", remainder, prefixLen]; [switches[except], remainder] _ GetSwitch["-xcept", remainder, prefixLen]; [switches[quiet], remainder] _ GetSwitch["-quiet", 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[locations] OR switches[verbose] OR switches[except]) }; 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 Process.CheckForAbort[]; [found, at, atEnd, before, after] _ RegularExpression.SearchRope[pattern, RefText.TrustTextAsRope[line], start, line.length, interrupt]; IF found = switches[except] 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[locations] => 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]]]; switches[except] => IO.PutF1[outStream, "%g\n", [text[line]]]; ENDCASE => ERROR; IF switches[oncePerLine] OR switches[except] THEN EXIT; start _ atEnd; 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]]] }; doc: Rope.ROPE ~ "Searches files for lines that match a pattern. (Grep|GrepI) switch* pattern fileNames Grep is uninterpreted, GrepI is interpreted. -b binary files too, -c case sensitive, -e enumerate cache, -f file names only, -i ignore tioga formatting, -l locations only, -o once per line, -p pattern (see RegularExpressionDoc), -q quiet -s stop on first error, -t text only, -v verbose, -w word, -x lines except those matching pattern."; Commander.Register[key: "Grep", proc: GrepCmd, doc: doc, interpreted: FALSE]; Commander.Register[key: "GrepI", proc: GrepCmd, doc: doc, interpreted: TRUE]; }. GrepImpl.mesa Copyright Ó 1985, 1987, 1992 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) June 1, 1985 10:03:13 am PDT Donahue, July 16, 1985 8:29:50 am PDT John Larson, September 22, 1985 9:57:21 pm PDT Peter Kessler May 12, 1987 5:31:32 pm PDT Tim Diebert: December 31, 1985 8:59:11 am PST Bob Hagmann February 6, 1986 2:16:28 pm PST Doug Wyatt, March 11, 1987 3:53:52 pm PST Swinehar, December 14, 1990 1:12 pm PST Ken Fishkin, June 19, 1991 4:37 pm PDT Willie-s, April 2, 1992 9:22 am PST Callable from the command interpreter. Expects a list of file names as arguments, and counts the number of words in the indicated files. Interesting pattern options Display options. Match the pattern against the next line Keep matching the pattern until it fails. Bob Hagmann February 6, 1986 2:16:28 pm PST Added check in GrepStream to avoid bounds errors for very long lines changes to: GrepStream, DIRECTORY, GrepImpl Peter Kessler May 12, 1987 5:31:22 pm PDT Added -x switch to hit on all lines except those matching pattern. changes to: RegularOutput -x not included, GrepStream use -x to print mismatching lines (once per line), doc added switches documentation to Commander.Register.doc, Commander used common documentation string. , GetSwitches Ê ˜codešœ ™ KšœH™HK™0K™%K™.K™)K™-K™+K™)K™'K™&K™#K™—šÏk ˜ Kšœœ ˜Kšœ œ˜(Kšœ œ˜!KšœœW˜_K˜KšœœQœ˜aKšœœ˜ Kšœœ˜ Kšœœ œ˜0Kšœœ8˜OKšœ œ˜!—K˜šÏbœœ˜Kšœ!œœ9˜gKšœ ˜K˜Kšœœœ˜Kšœœœœ˜Kš œœœœœc˜Œ—K˜•StartOfExpansion -- [cmd: Commander.Handle] -- šžœÐckœ˜"KšÏc‰™‰Kšœœ ˜Kšœœ ˜Kšœœ ˜Kšœ œœœ˜Kšœ œ˜Kšœ6œ˜>Kšœ"˜"Kš œ œœœœœ˜(Kšœ œœ˜8Kšœœ˜Kšœœœ˜$Kšœœœ˜Kšœœ˜šœ œ˜%Kšœ™—Kšœ˜šœœ˜Kšœ™—šÏn œœ˜KšœC˜EK˜—šžœœ ˜Kšœœ˜ Kšœœ˜ Kšœ œ˜šœ˜š œœœœ œœ˜CKšœ œœœ˜5Kšœ˜——šœœ_œ˜zšœœ œœ˜)Kšœ5˜7Kšœ ˜K˜——Kšœœœ9˜UKšœ†˜†Kšœ-˜-Kšœ!œ œœ˜Kšœœ˜K˜——Kšœ˜—K˜K˜—š¡œœ œ œœœœœ˜TKšœœ˜Kšœœ˜Kšœœœ˜Kš œœœœœ˜6šœ ˜šœ˜šœœœ ˜Kšœœœœ˜0Kš˜——š˜šœœœ ˜Kšœ5œœœ˜JKšœ˜———Kšœœ˜ K˜K˜—š ¡œœœœœ˜GKšœœ˜$Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜šœ ˜Kšœœ*˜1šœ˜ Kšœ˜Kšœœ˜Kšœœ œ ˜$Kšœ œ˜Kšœ˜—Kšœ˜—šœœ˜šœ œ œ˜*Kšœ2˜2—šœ ˜ Kšœ-˜-—Kšœ˜—Kšœ˜K˜K˜—š ¡ œœœ œœ˜RKš œ œœœœ˜vKšœ˜—K˜šÐbn œœœ:œœ0œ œœœœ˜ÄKšœœ˜ Kšœœœ˜Kšœœ˜Kšœœ˜.Kšœ œ˜Kšœœ˜ Kš œœœœœ˜ Kšœ˜š˜Kšœ'™'šœœ˜"Kšœ˜šœ˜Kšœœœf˜‚Kšœœ˜Kšœ˜K˜——Kšœ˜K˜ š˜Kšœ)™)K˜šœ$˜$Kšœd˜d—Kšœœœ˜&šœœ˜.Kšœ˜—Kšœ ˜ šœœ˜šœ˜Kšœ,˜.Kšœ˜K˜—šœ˜Kšœ(˜*—šœ˜KšœH˜J—˜šœ˜Kšœ,˜.—Kšœ6˜8šœ˜šœ˜Kšœœœ ˜Kšœœœ˜&Kšœœ ˜Kšœœ˜Kšœ œœ˜/KšœG˜IKšœœœ˜*K˜—š˜Kšœ˜——Kšœ˜K˜—KšœœZ˜qšœ˜Kšœ(˜*—Kšœœ˜—Kšœœœœ˜7K˜Kšœ˜—K˜&š˜Kšœœ˜—Kšœ˜—šœ˜Kšœœœ*˜EKšœœD˜K——K˜šœ œ»˜ÉK˜—š œœœ œ œ˜MK˜—Kš œ œœ œ œ˜MK˜K˜K˜K˜™+KšœD™DKšœ Ïr™+—K™™)K™Bšœ ™ Kš£ œ£™Kš£ œ1£™?Kš£œ8£™=Kš£ œ#™,—Kš£ ™ —K™K™—…—(j:ˆ