-
Notifications
You must be signed in to change notification settings - Fork 0
/
tgrep.go
133 lines (117 loc) · 3.08 KB
/
tgrep.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// tgrep.go
// tgrep read stdin and find N lines within best timestamp distance
//
// Author : thc2cat@gmail.com
// 2018, ?? License
// 2021 v0.23 Bug corrections
//
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"regexp"
"time"
)
var (
stime string
lines int
version bool
datafile string
stampRegexp = regexp.MustCompile(`^(?P<date>(... .. ..:..:..))`)
stampRef = "Jan 1 00:00:00"
versionS = "tgrep v0.23"
)
type keep struct {
text string
distance int64
}
func main() {
flag.StringVar(&stime, "t", stampRef, "approximate timestamp to look after")
flag.IntVar(&lines, "n", 1, "lines count for output")
flag.BoolVar(&version, "v", false, fmt.Sprintf("prints current version (%s)", versionS))
flag.StringVar(&datafile, "f", "-", "read data from file instead of stdin")
flag.Parse()
if version {
fmt.Println(versionS)
os.Exit(0)
}
if lines < 1 {
log.Fatal("ERROR: line count must be >0 ")
}
t, err := time.Parse(time.Stamp, stime)
if err != nil {
log.Fatal("ERROR: time.Parse timestamp ", err)
}
stampRefT, _ := time.Parse(time.Stamp, stampRef)
bestdistance := WithTwosComplement(int64(t.Sub(stampRefT)))
// buffer for keeping nearest lines
keeps := make([]keep, lines)
keeps[0].distance = bestdistance
keeps[lines-1].distance = bestdistance
var s *bufio.Scanner
if datafile != "-" { // read from datafile parameter
f, errf := os.Open(datafile)
if errf != nil {
log.Fatal(errf)
}
defer f.Close()
s = bufio.NewScanner(f)
} else {
// Scanner read from Stdin otherwise
s = bufio.NewScanner(os.Stdin)
}
for s.Scan() {
texte := s.Text()
results := reSubMatchMap(stampRegexp, texte)
if results == nil {
continue
}
d, err := time.Parse(time.Stamp, results["date"])
if err != nil {
log.Printf("Error time.Parse -->%s<--\n", results["date"])
continue
}
distance := WithTwosComplement(int64(t.Sub(d)))
if (distance == 0 || distance <= keeps[lines-1].distance) || (distance < keeps[0].distance) {
for line := 1; line < lines; line++ { // Keep best lines
if keeps[line].text != "" { // avoid copy if unitialised
keeps[line-1].distance = keeps[line].distance
keeps[line-1].text = keeps[line].text
}
}
keeps[lines-1].distance = distance
keeps[lines-1].text = texte
}
if (distance > keeps[lines-1].distance) && (distance > keeps[0].distance) {
break // too far in the file
}
}
for line := 0; line < lines; line++ {
if (keeps[line].text != "") || (keeps[line].distance != 0) { // keeps may contain empty lines
fmt.Println(keeps[line].text)
}
}
os.Exit(0)
}
func reSubMatchMap(r *regexp.Regexp, str string) map[string]string {
match := r.FindStringSubmatch(str)
if len(match) == 0 {
return nil
}
subMatchMap := make(map[string]string)
for i, name := range r.SubexpNames() {
if i != 0 {
subMatchMap[name] = match[i]
}
}
return subMatchMap
}
// WithTwosComplement return Abs without math.
func WithTwosComplement(n int64) int64 {
// http://cavaliercoder.com/blog/optimized-abs-for-int64-in-go.html
y := n >> 63 // y ← x ⟫ 63
return (n ^ y) - y // (x ⨁ y) - y
}