-
Notifications
You must be signed in to change notification settings - Fork 30
/
ansi_convert.go
83 lines (78 loc) · 1.39 KB
/
ansi_convert.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
package ansi
import (
"unicode/utf8"
)
// States
const (
Default = iota
Escaping
ParsingControl
InControl
SkipOne
)
type AnsiParser struct {
Rune func(r rune)
Escape func(e EscapeSequence)
}
func (a *AnsiParser) ConvertFromUTF8(input []byte) error {
s := Default
buf := make([]rune, 0, 16)
var esc EscapeSequence
for i, n := 0, len(input); i < n; {
r, sz := utf8.DecodeRune(input[i:])
if r == utf8.RuneError {
i++
continue
}
switch s {
case Default:
switch r {
case 033:
s = Escaping
buf = buf[0:0]
esc.Reset()
default:
a.Rune(r)
}
case Escaping:
switch r {
case '*':
// special case in ptt, not implemented here
s = SkipOne
case '[':
// multi-byte control sequence
s = ParsingControl
case 'm':
// XXX: some asciarts tend to use this as reset, but is not in the spec.
a.Escape(esc)
s = Default
default:
if r >= '@' && r <= '_' {
// 2-char control code, not supported
s = SkipOne
} else {
// error! but be nice
a.Rune(r)
s = Default
}
}
case ParsingControl:
switch {
case r >= ' ' && r <= '/':
esc.Trailings = append(esc.Trailings, r)
case r >= '@' && r <= '~':
esc.Mode = r
esc.ParseNumbers(buf)
a.Escape(esc)
s = Default
default:
buf = append(buf, r)
}
case SkipOne:
// just skip
s = Default
}
i += sz
}
return nil
}