-
Notifications
You must be signed in to change notification settings - Fork 1
/
cat.go
140 lines (129 loc) · 3.35 KB
/
cat.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
134
135
136
137
138
139
140
package main
import (
"bufio"
"flag"
"fmt"
"io"
"os"
"strings"
)
var (
numberNonBlankOutputLines = flag.Bool("b", false, "Number the non-blank output lines, starting at 1")
displayDollarAtEnd = flag.Bool("e", false, "Display non-printing characters (see the -v option), and display a dollar sign (`$') at the end of each line.")
displayNonPrintingChars = flag.Bool("v", false, "Display non-printing characters so they are visible.")
numberOutputLines = flag.Bool("n", false, "Number the output lines, starting at 1.")
singleSpacedOutput = flag.Bool("s", false, "Squeeze multiple adjacent empty lines, causing the output to be single spaced.")
displayTabChar = flag.Bool("t", false, "Display non-printing characters (see the -v option), and display tab characters as `^I'.")
disableOutputBuffer = flag.Bool("u", false, "Disable output buffering.")
)
func main() {
flag.Parse()
Cat(os.Args, os.Stdin)
}
func Cat(args []string, r io.ReadCloser) {
if len(args) == 1 {
io.Copy(os.Stdout, r)
return
}
for i, fn := range args[1:] {
fh, err := Choose(fn)
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
if fh == nil && i < len(args[1:])-1 {
continue
}
if fh == nil {
fh = r
}
defer fh.Close()
counter := 1
lineCounter := 0
flag := false
scanner := bufio.NewScanner(fh)
for scanner.Scan() {
output := scanner.Text()
if *numberOutputLines && !*numberNonBlankOutputLines {
output = fmt.Sprintf(" %d %s", counter, output)
counter++
}
if *numberNonBlankOutputLines {
if output != "" {
output = fmt.Sprintf(" %d %s", counter, output)
counter++
}
}
if *displayDollarAtEnd || *displayNonPrintingChars || *displayTabChar {
var nonPrintingStr string
for _, value := range []byte(output) {
nonPrintingStr += GetNonPrintingStr(value)
}
output = nonPrintingStr
}
if *singleSpacedOutput {
if output != "" {
if flag && lineCounter >= 1 {
flag = false
lineCounter = 0
}
} else {
var str string
if *displayDollarAtEnd {
str = fmt.Sprintf("%s$", str)
}
if !flag {
io.Copy(os.Stdout, strings.NewReader(str+"\n"))
}
lineCounter++
flag = true
continue
}
}
if *displayDollarAtEnd {
output = fmt.Sprintf("%s$", output)
}
output = fmt.Sprintf("%s\n", output)
io.Copy(os.Stdout, strings.NewReader(output))
}
}
}
func Choose(name string) (io.ReadCloser, error) {
splitArg := strings.Split(name, "-")
if len(splitArg) == 2 && flag.Lookup(splitArg[1]) != nil {
return nil, nil
}
if name == "-" {
return os.Stdin, nil
} else {
return os.Open(name)
}
}
/*
Non-printing character:
Control characters print as `^X' for control-X;
the delete character (octal 0177) prints as `^?'.
Non-ASCII characters (with the high bit set) are printed as `M-' (for meta) followed by the character for the low 7 bits.
160 = 128+32
255 = 128+127
*/
func GetNonPrintingStr(b byte) string {
switch {
case b >= 32 && b < 127:
return string(b)
case b == 127:
return "^?"
case b > 127 && b < 160:
return "M-^" + string(b-128+64)
case b >= 160 && b < 255:
return "M-" + string(b-128)
case b >= 255:
return "M-^?"
case b == '\t' && *displayTabChar:
return "^I"
case b == '\t':
return "\t"
default:
return "^" + string(b+64)
}
}