commit before ov-res changes
This commit is contained in:
parent
ee445dc022
commit
2fb8ced454
1 changed files with 191 additions and 0 deletions
191
tools/nimgrep.nim
Executable file
191
tools/nimgrep.nim
Executable file
|
|
@ -0,0 +1,191 @@
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Nimrod Grep Utility
|
||||||
|
# (c) Copyright 2010 Andreas Rumpf
|
||||||
|
#
|
||||||
|
# See the file "copying.txt", included in this
|
||||||
|
# distribution, for details about the copyright.
|
||||||
|
#
|
||||||
|
|
||||||
|
import
|
||||||
|
os, strutils, parseopt, pegs, re, terminal
|
||||||
|
|
||||||
|
const
|
||||||
|
Usage = """
|
||||||
|
Usage: nimgrep [options] [pattern] [files/directory]
|
||||||
|
Options:
|
||||||
|
--find, -f find the pattern (default)
|
||||||
|
--replace, -r replace the pattern
|
||||||
|
--peg pattern is a peg (default)
|
||||||
|
--re pattern is a regular expression
|
||||||
|
--recursive process directories recursively
|
||||||
|
--confirm confirm each occurence/replacement; there is a chance
|
||||||
|
to abort any time without touching the file(s)
|
||||||
|
--stdin read pattern from stdin (to avoid the shell's confusing
|
||||||
|
quoting rules)
|
||||||
|
--word, -w the pattern should have word boundaries
|
||||||
|
--ignore_case, -i be case insensitive
|
||||||
|
--ignore_style, -y be style insensitive
|
||||||
|
"""
|
||||||
|
|
||||||
|
type
|
||||||
|
TOption = enum
|
||||||
|
optFind, optReplace, optPeg, optRegex, optRecursive, optConfirm, optStdin,
|
||||||
|
optWord, optIgnoreCase, optIgnoreStyle
|
||||||
|
TOptions = set[TOption]
|
||||||
|
TConfirmEnum = enum
|
||||||
|
ceAbort, ceYes, ceAll, ceNo, ceNone
|
||||||
|
|
||||||
|
var
|
||||||
|
filename = ""
|
||||||
|
pattern = ""
|
||||||
|
replacement = ""
|
||||||
|
options: TOptions
|
||||||
|
|
||||||
|
proc ask(msg: string): string =
|
||||||
|
stdout.write(msg)
|
||||||
|
result = stdin.readline()
|
||||||
|
|
||||||
|
proc Confirm: TConfirmEnum =
|
||||||
|
while true:
|
||||||
|
case normalize(ask("[a]bort; [y]es, a[l]l, [n]o, non[e]: "))
|
||||||
|
of "a", "abort": return ceAbort
|
||||||
|
of "y", "yes": return ceYes
|
||||||
|
of "l", "all": return ceAll
|
||||||
|
of "n", "no": return ceNo
|
||||||
|
of "e", "none": return ceNone
|
||||||
|
else: nil
|
||||||
|
|
||||||
|
proc highlight(a, b, c: string) =
|
||||||
|
stdout.write(a)
|
||||||
|
terminal.WriteStyled(b)
|
||||||
|
stdout.writeln(c)
|
||||||
|
|
||||||
|
proc countLines(s: string, first = 0, last = s.high): int =
|
||||||
|
var i = first
|
||||||
|
while i <= last:
|
||||||
|
if s[i] == '\13':
|
||||||
|
inc result
|
||||||
|
if i < last and s[i+1] == '\10': inc(i)
|
||||||
|
elif s[i] == '\10':
|
||||||
|
inc result
|
||||||
|
inc i
|
||||||
|
|
||||||
|
proc processFile(filename: string) =
|
||||||
|
var buffer = system.readFile(filename)
|
||||||
|
if isNil(buffer): quit("cannot open file: " & filename)
|
||||||
|
var pegp: TPeg
|
||||||
|
var rep: TRegex
|
||||||
|
var result: string
|
||||||
|
|
||||||
|
if optRegex in options:
|
||||||
|
if optIgnoreCase in options:
|
||||||
|
rep = re(pattern, {reExtended, reIgnoreCase})
|
||||||
|
else:
|
||||||
|
rep = re(pattern)
|
||||||
|
else:
|
||||||
|
pegp = peg(pattern)
|
||||||
|
|
||||||
|
if optReplace in options:
|
||||||
|
result = newString(buffer.len)
|
||||||
|
setLen(result, 0)
|
||||||
|
|
||||||
|
var line = 1
|
||||||
|
var i = 0
|
||||||
|
var matches: array[0..re.MaxSubpatterns-1. string]
|
||||||
|
var reallyReplace = true
|
||||||
|
while i < buffer.len:
|
||||||
|
var t: tuple[first, last: int]
|
||||||
|
if optRegex in options:
|
||||||
|
quit "to implement"
|
||||||
|
else:
|
||||||
|
t = findBounds(buffer, pegp, matches, i)
|
||||||
|
|
||||||
|
if t.first <= 0: break
|
||||||
|
inc(line, countLines(buffer, i, t.first-1))
|
||||||
|
|
||||||
|
var wholeMatch = buffer.copy(t.first, t.last)
|
||||||
|
echo "line ", line, ": ", wholeMatch
|
||||||
|
|
||||||
|
if optReplace in options:
|
||||||
|
var r = replace(wholeMatch, pegp, replacement)
|
||||||
|
|
||||||
|
if optConfirm in options:
|
||||||
|
case Confirm()
|
||||||
|
of ceAbort:
|
||||||
|
of ceYes:
|
||||||
|
of ceAll:
|
||||||
|
reallyReplace = true
|
||||||
|
of ceNo:
|
||||||
|
reallyReplace = false
|
||||||
|
of ceNone:
|
||||||
|
reallyReplace = false
|
||||||
|
if reallyReplace:
|
||||||
|
|
||||||
|
|
||||||
|
inc(line, countLines(buffer, t.first, t.last))
|
||||||
|
|
||||||
|
i = t.last+1
|
||||||
|
|
||||||
|
|
||||||
|
proc walker(dir: string) =
|
||||||
|
for kind, path in walkDir(dir):
|
||||||
|
case kind
|
||||||
|
of pcFile: processFile(path)
|
||||||
|
of pcDirectory:
|
||||||
|
if optRecursive in options:
|
||||||
|
walker(path)
|
||||||
|
else: nil
|
||||||
|
|
||||||
|
proc writeHelp() = quit(Usage)
|
||||||
|
proc writeVersion() = quit("1.0")
|
||||||
|
|
||||||
|
proc checkOptions(subset: TOptions, a, b: string) =
|
||||||
|
if subset <= options:
|
||||||
|
quit("cannot specify both '$#' and '$#'" % [a, b])
|
||||||
|
|
||||||
|
for kind, key, val in getopt():
|
||||||
|
case kind
|
||||||
|
of cmdArgument:
|
||||||
|
if options.contains(optStdIn):
|
||||||
|
filename = key
|
||||||
|
elif pattern.len == 0:
|
||||||
|
pattern = key
|
||||||
|
elif options.contains(optReplace) and replacement.len == 0:
|
||||||
|
replacement = key
|
||||||
|
else:
|
||||||
|
filename = key
|
||||||
|
of cmdLongOption, cmdShortOption:
|
||||||
|
case normalize(key)
|
||||||
|
of "find", "f": incl(options, optFind)
|
||||||
|
of "replace", "r": incl(options, optReplace)
|
||||||
|
of "peg": incl(options, optPeg)
|
||||||
|
of "re": incl(options, optRegex)
|
||||||
|
of "recursive": incl(options, optRecursive)
|
||||||
|
of "confirm": incl(options, optConfirm)
|
||||||
|
of "stdin": incl(options, optStdin)
|
||||||
|
of "word", "w": incl(options, optWord)
|
||||||
|
of "ignorecase", "i": incl(options, optIgnoreCase)
|
||||||
|
of "ignorestyle", "y": incl(options, optIgnoreStyle)
|
||||||
|
of "help", "h": writeHelp()
|
||||||
|
of "version", "v": writeVersion()
|
||||||
|
else: writeHelp()
|
||||||
|
of cmdEnd: assert(false) # cannot happen
|
||||||
|
|
||||||
|
checkOptions({optFind, optReplace}, "find", "replace")
|
||||||
|
checkOptions({optPeg, optRegex}, "peg", "re")
|
||||||
|
checkOptions({optIgnoreCase, optIgnoreStyle}, "ignore_case", "ignore_style")
|
||||||
|
checkOptions({optIgnoreCase, optPeg}, "ignore_case", "peg")
|
||||||
|
|
||||||
|
if optStdin in options:
|
||||||
|
pattern = ask("pattern [ENTER to exit]: ")
|
||||||
|
if IsNil(pattern) or pattern.len == 0: quit(0)
|
||||||
|
if optReplace in options:
|
||||||
|
replacement = ask("replacement [supports $1, $# notations]: ")
|
||||||
|
|
||||||
|
if pattern.len == 0:
|
||||||
|
writeHelp()
|
||||||
|
else:
|
||||||
|
if filename.len == 0: filename = os.getCurrentDir()
|
||||||
|
walker(filename)
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue