-
Notifications
You must be signed in to change notification settings - Fork 2
/
grep.go
112 lines (102 loc) · 2.24 KB
/
grep.go
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
106
107
108
109
110
111
112
package filter
import (
"bytes"
"fmt"
"io"
"regexp"
"github.com/koron/nvgd/resource"
)
// Grep represents grep like filter.
type Grep struct {
Base
currLnum int
re *regexp.Regexp
match bool
lf LineFilter
lnum bool
cnum int
contextBefore [][]byte
contextAfter int
}
// NewGrep creates an instance of grep filter.
func NewGrep(r io.ReadCloser, re *regexp.Regexp, match bool, lf LineFilter, lnum bool, cnum int) *Grep {
g := &Grep{
re: re,
match: match,
lf: TrimEOL.Chain(lf),
lnum: lnum,
cnum: cnum,
}
if cnum > 0 {
g.contextBefore = make([][]byte, 0, cnum)
}
g.Base.Init(r, g.readNext)
return g
}
func (g *Grep) readNext(buf *bytes.Buffer) error {
for {
raw, err := g.ReadLine()
if err != nil && len(raw) == 0 {
return err
}
g.currLnum++
b := g.lf.Apply(raw)
if g.re.Match(b) != g.match {
if g.contextAfter > 0 {
g.contextAfter--
return g.output(buf, g.currLnum, raw)
}
// add to the before context.
if g.cnum > 0 {
if len(g.contextBefore) >= g.cnum {
copy(g.contextBefore[:g.cnum-1], g.contextBefore[1:g.cnum])
g.contextBefore = g.contextBefore[:g.cnum-1]
}
g.contextBefore = append(g.contextBefore, raw)
}
continue
}
if g.cnum > 0 {
// output the before conext.
for i, d := range g.contextBefore {
err := g.output(buf, g.currLnum-len(g.contextBefore)+i, d)
if err != nil {
return err
}
}
g.contextBefore = g.contextBefore[0:0]
g.contextAfter = g.cnum
}
return g.output(buf, g.currLnum, raw)
}
}
func (g *Grep) output(buf *bytes.Buffer, lnum int, data []byte) error {
if g.lnum {
_, err := fmt.Fprintf(buf, "%d: %s", lnum, data)
return err
}
_, err := buf.Write(data)
return err
}
func newGrep(r *resource.Resource, p Params) (*resource.Resource, error) {
re, err := regexp.Compile(p.String("re", ""))
if err != nil {
return nil, err
}
match := p.Bool("match", true)
lnum := p.Bool("number", false)
cnum := p.Int("context", 0)
var lf LineFilter
// field filter
var (
field = p.Int("field", 0)
delim = []byte(p.String("delim", "\t"))
)
if field > 0 {
lf = lf.Chain(NewCutLF(delim, field-1))
}
return r.Wrap(NewGrep(r, re, match, lf, lnum, cnum)), nil
}
func init() {
MustRegister("grep", newGrep)
}