############################################################################ # # File: igrep.icn # # Subject: Program for string search similar to egrep # # Author: Robert J. Alexander # # Date: May 1, 1994 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # Program to emulate UNIX egrep, but using the enhanced regular # expressions supported by regexp.icn. Options supported are nearly # identical to those supported by egrep (no -b: print disk block # number). There is one additional option, -E, to allow Icon-type # (hence C-type) string escape sequences in the pattern string. # BEWARE: when -E is used, backslashes that are meant to be processed # in the regular expression context must be doubled. The following # patterns are equivalent: # # without -E: '\bFred\b' # with -E: '\\bFred\\b' # # To enable the -D option (intended mainly for debugging), the Icon # Program Library file "ximage" must be linked with this program. # ############################################################################ procedure Usage(n) write(&errout, "igrep -- emulates UNIX egrep\n_ Usage: igrep -Options [expression] filename..._ \n Options:_ \n c print count of matching lines rather than actual lines_ \n h don't display file names_ \n i ignore case of letters_ \n l list only the names of files containing matching lines_ \n n precede lines with line numbers_ \n s work silently -- display nothing_ \n v invert search to display only lines that don't match_ \n e expr useful if expressions starts with -_ \n E expr expresson containing Icon escape sequences_ \n f file take list of alternated expressions from \"file\"" # ,if \xdump then # "\n D dump compiled pattern and quit" else "" ) exit(n) end link options,regexp procedure main(arg) local compiledPattern if *arg = 0 then Usage() Options(arg) compiledPattern := GetPattern(arg) | {write(&errout,"Bad pattern ",image(Pattern)) ; exit(2)} # if \Dump then (\xdump)(compiledPattern) exit(ScanFiles(arg,compiledPattern)) end global CountOnly,NoNames,NamesOnly,NumberLines,Out,Invert,Escapes, Pattern,PatternFile,Dump,Re_LeftmostShortest procedure Options(arg) local opt opt := options(arg,"chilnsve:E:f:DS") CountOnly := opt["c"] NoNames := opt["h"] if \opt["i"] then Re_Filter := map NamesOnly := opt["l"] NumberLines := opt["n"] Out := if \opt["s"] then &null else &output Invert := opt["v"] Pattern := \opt["e" | "E"] Escapes := opt["E"] PatternFile := opt["f"] Dump := opt["D"] Re_LeftmostShortest := (\opt["S"],&null) | 1 return opt end procedure GetPattern(arg) local f,sep if \PatternFile then { f := open(PatternFile) | stop("Can't open pattern file \"",PatternFile,"\"") (/Pattern := "" & sep := "") | (sep := "|") while Pattern ||:= sep || read(f) do sep := "|" close(f) } /Pattern := get(arg) if /Pattern then Usage(2) return RePat(if \Escapes then istring(Pattern) else Pattern) end procedure ScanFiles(arg,pattern) local errors,totalCount,fn,f,header,lineNbr,count,line,fLine,status, lineNbrTag totalCount := 0 if *arg = 0 then arg := ["-"] every fn := !arg do { f := if fn == "-" then &input else open(fn) | {write(&errout,"Can't open \"",fn,"\" -- skipped") ; errors := 2 ; next} header := if \NoNames | *arg = 1 then &null else fn || ":" lineNbr := count := 0 while line := read(f) do { lineNbr +:= 1 fLine := (\Re_Filter)(line) | line status := ReFind(pattern,fLine) | &null status := if \Invert then (\status,&null) | 1 if \status then { count +:= 1 if count = 1 & \NamesOnly then {write(\Out,fn) ; next} lineNbrTag := if \NumberLines then lineNbr || ":" else &null if not \(CountOnly | NamesOnly) then write(\Out,header,lineNbrTag,line) } } close(f) if \CountOnly then write(header,count) totalCount +:= count } ## if \CountOnly & *arg > 1 then write(\Out,"** Total ** ",totalCount) return \errors | if totalCount = 0 then 1 else 0 end # # istring() -- Procedure to convert a string containing special escape # constructs, of the same format as Icon source language character # strings, to their true string representation. Value returned is the # string with special constructs converted to their respective # characters. # procedure istring(s) local r,c r := "" s ? { while r ||:= tab(upto('\\')) do { move(1) r ||:= case c := map(move(1)) of { "b": "\b" # backspace "d": "\d" # delete (rubout) "e": "\e" # escape (altmode) "f": "\f" # formfeed "l": "\l" # linefeed (newline) "n": "\n" # newline (linefeed) "r": "\r" # carriage return "t": "\t" # horizontal tab "v": "\v" # vertical tab "x": istring_radix(16,2)# hexadecimal code "^": char(ord(move(1)) % 32) | break # control code default: { # either octal code or non-escaped character if any('01234567',c) then { # if octal digit move(-1) istring_radix(8,3) } else c # else non-escaped character } | break } } r ||:= tab(0) } return r end procedure istring_radix(r,max) local n,d,i,c d := "0123456789abcdef"[1:r + 1] n := 0 every 1 to max do { c := move(1) | break if not (i := find(map(c),d) - 1) then { move(-1) break } n := n * r + i } return char(n) end