all repos — fae @ 509fa6dc73769e092281b65d702e27982750d18a

A minuscule find and edit utility.

far.nim

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
import 
  slre,
  parseopt2,
  os

type
  StringBounds = array[0..1, int]
  FarOptions = object
    regex: string
    recursive: bool
    filter: string
    substitute: string
    directory: string

proc matchBounds(str, regex: string, offset=0): StringBounds =
  var c  = cast[ptr array[0..9,Capture]](alloc0(sizeof(array[0..9, Capture])))
  let match = slre_match(("(" & regex & ")").cstring, str.cstring, str.len.cint, c, 10, 0)
  if match >= 0:
    result = [(match-c[0].len+offset), match-1+offset]
  else:
    result = [-1, match]

proc matchBoundsRec(str, regex: string, offset = 0, matches: var seq[StringBounds]) =
  let match = matchBounds(str, regex, offset)
  if match[0] >= 0:
    matches.add(match)
    var off = offset+match[1]
    matchBoundsRec(str.substr(off), regex, off, matches)

proc match(str, regex: string): bool = 
  var c  = cast[ptr array[0..9,Capture]](alloc0(sizeof(array[0..9, Capture])))
  return slre_match(regex.cstring, str.cstring, str.len.cint, c, 10, 0) >= 0

proc countLines(s: string, first, last: int): 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

### MAIN

var options = FarOptions(regex: nil, recursive: false, filter: nil, substitute: nil, directory: ".")

for kind, key, val in getOpt():
  case kind:
    of cmdArgument:
      options.regex = key
    of cmdLongOption, cmdShortOption:
      case key:
        of "recursive", "r":
          options.recursive = true
        of "filter", "f":
          options.filter = val
        of "substitute", "s":
          options.substitute = val 
        of "directory", "d":
          options.directory = val
        else:
          discard
    else:
      discard

if options.regex == nil:
  quit("No regex specified.", 1)

var scan: iterator(dir: string): string {.closure.}

if options.recursive:
  scan = iterator (dir: string): string {.closure.}=
    for path in walkDirRec(dir):
      if options.filter == nil or path.match(options.filter):
        yield path
else:
  scan = iterator (dir: string): string {.closure.} =
    for kind, path in walkDir(dir):
      if kind == pcFile and (options.filter == nil or path.match(options.filter)):
        yield path

# test

# test

var contents = ""
var contentsLen = 0
var matches = newSeq[StringBounds](0)
var count = 0

for f in scan(options.directory):
  count.inc
  contents = f.readfile()
  contentsLen = contents.len
  matchBoundsRec(contents, options.regex, 0, matches)
  if matches.len > 0:
    echo "[", count, "] --> " & f
    for match in matches:
      echo contents.countLines(0, match[1])+1, ": ", contents.substr(match[0], match[1])
  matches = newSeq[StringBounds](0)

echo "=== End: ", count, " files processed."