-
Notifications
You must be signed in to change notification settings - Fork 2
/
main.go
90 lines (85 loc) · 1.98 KB
/
main.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
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
)
func main() {
var (
dry bool
quiet bool
sizeHint int
)
flag.BoolVar(&dry, "d", false, "dry mode (no file modification)")
flag.BoolVar(&quiet, "q", false, "quiet mode")
flag.IntVar(&sizeHint, "n", 1024, "size hint")
flag.Parse()
w := io.Discard
if !quiet {
w = os.Stdout
}
var r io.Reader = &emptyReader{}
if fn := flag.Arg(0); len(fn) != 0 {
f, err := os.OpenFile(fn, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "failure to open file %q\n", fn)
os.Exit(1)
}
defer func() {
if err := f.Close(); err != nil {
fmt.Fprintf(os.Stderr, "failure to close file: %v\n", err)
os.Exit(1)
}
}()
if !dry {
w = io.MultiWriter(f, w)
}
r = f
}
if err := Bnew(w, r, os.Stdin, sizeHint); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
// Bnew writes the unique lines present in r2 but absent from r1 to w, using n
// as a hint for the number of unique lines present in either r1 or r2. If an
// error occurs, Bnew returns it. Bnew buffers reads and writes internally.
func Bnew(w io.Writer, r1, r2 io.Reader, n int) error {
if n < 0 {
return fmt.Errorf("negative size hint: %d", n)
}
set := make(map[string]struct{}, n)
var empty struct{}
sc := bufio.NewScanner(r1)
for sc.Scan() {
set[sc.Text()] = empty
}
if err := sc.Err(); err != nil {
return fmt.Errorf("failure to scan: %v", err)
}
bw := bufio.NewWriter(w)
sc = bufio.NewScanner(r2)
for sc.Scan() {
line := sc.Text()
if _, ok := set[line]; ok {
continue
}
set[line] = empty
if _, err := fmt.Fprintln(bw, line); err != nil {
return fmt.Errorf("failure to write line: %v", err)
}
}
if err := sc.Err(); err != nil {
return fmt.Errorf("failure to scan: %v", err)
}
if err := bw.Flush(); err != nil {
return fmt.Errorf("failure to flush: %v", err)
}
return nil
}
type emptyReader struct{}
func (*emptyReader) Read(buf []byte) (int, error) {
return 0, io.EOF
}