...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package dynamic
23
24 import (
25 "bytes"
26 "errors"
27 "os/exec"
28 "regexp"
29 "strconv"
30 "strings"
31
32 "github.com/gdamore/tcell/v2/terminfo"
33 )
34
35 type termcap struct {
36 name string
37 desc string
38 aliases []string
39 bools map[string]bool
40 nums map[string]int
41 strs map[string]string
42 }
43
44 func (tc *termcap) getnum(s string) int {
45 return (tc.nums[s])
46 }
47
48 func (tc *termcap) getflag(s string) bool {
49 return (tc.bools[s])
50 }
51
52 func (tc *termcap) getstr(s string) string {
53 return (tc.strs[s])
54 }
55
56 const (
57 none = iota
58 control
59 escaped
60 )
61
62 var errNotAddressable = errors.New("terminal not cursor addressable")
63
64 func unescape(s string) string {
65
66
67
68
69 buf := &bytes.Buffer{}
70 esc := none
71
72 for i := 0; i < len(s); i++ {
73 c := s[i]
74 switch esc {
75 case none:
76 switch c {
77 case '\\':
78 esc = escaped
79 case '^':
80 esc = control
81 default:
82 buf.WriteByte(c)
83 }
84 case control:
85 buf.WriteByte(c ^ 1<<6)
86 esc = none
87 case escaped:
88 switch c {
89 case 'E', 'e':
90 buf.WriteByte(0x1b)
91 case '0', '1', '2', '3', '4', '5', '6', '7':
92 if i+2 < len(s) && s[i+1] >= '0' && s[i+1] <= '7' && s[i+2] >= '0' && s[i+2] <= '7' {
93 buf.WriteByte(((c - '0') * 64) + ((s[i+1] - '0') * 8) + (s[i+2] - '0'))
94 i = i + 2
95 } else if c == '0' {
96 buf.WriteByte(0)
97 }
98 case 'n':
99 buf.WriteByte('\n')
100 case 'r':
101 buf.WriteByte('\r')
102 case 't':
103 buf.WriteByte('\t')
104 case 'b':
105 buf.WriteByte('\b')
106 case 'f':
107 buf.WriteByte('\f')
108 case 's':
109 buf.WriteByte(' ')
110 default:
111 buf.WriteByte(c)
112 }
113 esc = none
114 }
115 }
116 return (buf.String())
117 }
118
119 func (tc *termcap) setupterm(name string) error {
120 cmd := exec.Command("infocmp", "-1", name)
121 output := &bytes.Buffer{}
122 cmd.Stdout = output
123
124 tc.strs = make(map[string]string)
125 tc.bools = make(map[string]bool)
126 tc.nums = make(map[string]int)
127
128 if err := cmd.Run(); err != nil {
129 return err
130 }
131
132
133
134
135
136
137 lines := strings.Split(output.String(), "\n")
138 for len(lines) > 0 && strings.HasPrefix(lines[0], "#") {
139 lines = lines[1:]
140 }
141
142
143 if lines[len(lines)-1] == "" {
144 lines = lines[:len(lines)-1]
145 }
146 header := lines[0]
147 if strings.HasSuffix(header, ",") {
148 header = header[:len(header)-1]
149 }
150 names := strings.Split(header, "|")
151 tc.name = names[0]
152 names = names[1:]
153 if len(names) > 0 {
154 tc.desc = names[len(names)-1]
155 names = names[:len(names)-1]
156 }
157 tc.aliases = names
158 for _, val := range lines[1:] {
159 if (!strings.HasPrefix(val, "\t")) ||
160 (!strings.HasSuffix(val, ",")) {
161 return (errors.New("malformed infocmp: " + val))
162 }
163
164 val = val[1:]
165 val = val[:len(val)-1]
166
167 if k := strings.SplitN(val, "=", 2); len(k) == 2 {
168 tc.strs[k[0]] = unescape(k[1])
169 } else if k := strings.SplitN(val, "#", 2); len(k) == 2 {
170 u, err := strconv.ParseUint(k[1], 0, 0)
171 if err != nil {
172 return (err)
173 }
174 tc.nums[k[0]] = int(u)
175 } else {
176 tc.bools[val] = true
177 }
178 }
179 return nil
180 }
181
182
183
184
185 func LoadTerminfo(name string) (*terminfo.Terminfo, string, error) {
186 var tc termcap
187 if err := tc.setupterm(name); err != nil {
188 if err != nil {
189 return nil, "", err
190 }
191 }
192 t := &terminfo.Terminfo{}
193
194 t.Name = tc.name
195 if t.Name != name {
196 return t, "", nil
197 }
198 t.Aliases = tc.aliases
199 t.Colors = tc.getnum("colors")
200 t.Columns = tc.getnum("cols")
201 t.Lines = tc.getnum("lines")
202 t.Bell = tc.getstr("bel")
203 t.Clear = tc.getstr("clear")
204 t.EnterCA = tc.getstr("smcup")
205 t.ExitCA = tc.getstr("rmcup")
206 t.ShowCursor = tc.getstr("cnorm")
207 t.HideCursor = tc.getstr("civis")
208 t.AttrOff = tc.getstr("sgr0")
209 t.Underline = tc.getstr("smul")
210 t.Bold = tc.getstr("bold")
211 t.Blink = tc.getstr("blink")
212 t.Dim = tc.getstr("dim")
213 t.Italic = tc.getstr("sitm")
214 t.Reverse = tc.getstr("rev")
215 t.EnterKeypad = tc.getstr("smkx")
216 t.ExitKeypad = tc.getstr("rmkx")
217 t.SetFg = tc.getstr("setaf")
218 t.SetBg = tc.getstr("setab")
219 t.SetCursor = tc.getstr("cup")
220 t.CursorBack1 = tc.getstr("cub1")
221 t.CursorUp1 = tc.getstr("cuu1")
222 t.KeyF1 = tc.getstr("kf1")
223 t.KeyF2 = tc.getstr("kf2")
224 t.KeyF3 = tc.getstr("kf3")
225 t.KeyF4 = tc.getstr("kf4")
226 t.KeyF5 = tc.getstr("kf5")
227 t.KeyF6 = tc.getstr("kf6")
228 t.KeyF7 = tc.getstr("kf7")
229 t.KeyF8 = tc.getstr("kf8")
230 t.KeyF9 = tc.getstr("kf9")
231 t.KeyF10 = tc.getstr("kf10")
232 t.KeyF11 = tc.getstr("kf11")
233 t.KeyF12 = tc.getstr("kf12")
234 t.KeyF13 = tc.getstr("kf13")
235 t.KeyF14 = tc.getstr("kf14")
236 t.KeyF15 = tc.getstr("kf15")
237 t.KeyF16 = tc.getstr("kf16")
238 t.KeyF17 = tc.getstr("kf17")
239 t.KeyF18 = tc.getstr("kf18")
240 t.KeyF19 = tc.getstr("kf19")
241 t.KeyF20 = tc.getstr("kf20")
242 t.KeyF21 = tc.getstr("kf21")
243 t.KeyF22 = tc.getstr("kf22")
244 t.KeyF23 = tc.getstr("kf23")
245 t.KeyF24 = tc.getstr("kf24")
246 t.KeyF25 = tc.getstr("kf25")
247 t.KeyF26 = tc.getstr("kf26")
248 t.KeyF27 = tc.getstr("kf27")
249 t.KeyF28 = tc.getstr("kf28")
250 t.KeyF29 = tc.getstr("kf29")
251 t.KeyF30 = tc.getstr("kf30")
252 t.KeyF31 = tc.getstr("kf31")
253 t.KeyF32 = tc.getstr("kf32")
254 t.KeyF33 = tc.getstr("kf33")
255 t.KeyF34 = tc.getstr("kf34")
256 t.KeyF35 = tc.getstr("kf35")
257 t.KeyF36 = tc.getstr("kf36")
258 t.KeyF37 = tc.getstr("kf37")
259 t.KeyF38 = tc.getstr("kf38")
260 t.KeyF39 = tc.getstr("kf39")
261 t.KeyF40 = tc.getstr("kf40")
262 t.KeyF41 = tc.getstr("kf41")
263 t.KeyF42 = tc.getstr("kf42")
264 t.KeyF43 = tc.getstr("kf43")
265 t.KeyF44 = tc.getstr("kf44")
266 t.KeyF45 = tc.getstr("kf45")
267 t.KeyF46 = tc.getstr("kf46")
268 t.KeyF47 = tc.getstr("kf47")
269 t.KeyF48 = tc.getstr("kf48")
270 t.KeyF49 = tc.getstr("kf49")
271 t.KeyF50 = tc.getstr("kf50")
272 t.KeyF51 = tc.getstr("kf51")
273 t.KeyF52 = tc.getstr("kf52")
274 t.KeyF53 = tc.getstr("kf53")
275 t.KeyF54 = tc.getstr("kf54")
276 t.KeyF55 = tc.getstr("kf55")
277 t.KeyF56 = tc.getstr("kf56")
278 t.KeyF57 = tc.getstr("kf57")
279 t.KeyF58 = tc.getstr("kf58")
280 t.KeyF59 = tc.getstr("kf59")
281 t.KeyF60 = tc.getstr("kf60")
282 t.KeyF61 = tc.getstr("kf61")
283 t.KeyF62 = tc.getstr("kf62")
284 t.KeyF63 = tc.getstr("kf63")
285 t.KeyF64 = tc.getstr("kf64")
286 t.KeyInsert = tc.getstr("kich1")
287 t.KeyDelete = tc.getstr("kdch1")
288 t.KeyBackspace = tc.getstr("kbs")
289 t.KeyHome = tc.getstr("khome")
290 t.KeyEnd = tc.getstr("kend")
291 t.KeyUp = tc.getstr("kcuu1")
292 t.KeyDown = tc.getstr("kcud1")
293 t.KeyRight = tc.getstr("kcuf1")
294 t.KeyLeft = tc.getstr("kcub1")
295 t.KeyPgDn = tc.getstr("knp")
296 t.KeyPgUp = tc.getstr("kpp")
297 t.KeyBacktab = tc.getstr("kcbt")
298 t.KeyExit = tc.getstr("kext")
299 t.KeyCancel = tc.getstr("kcan")
300 t.KeyPrint = tc.getstr("kprt")
301 t.KeyHelp = tc.getstr("khlp")
302 t.KeyClear = tc.getstr("kclr")
303 t.AltChars = tc.getstr("acsc")
304 t.EnterAcs = tc.getstr("smacs")
305 t.ExitAcs = tc.getstr("rmacs")
306 t.EnableAcs = tc.getstr("enacs")
307 t.Mouse = tc.getstr("kmous")
308 t.KeyShfRight = tc.getstr("kRIT")
309 t.KeyShfLeft = tc.getstr("kLFT")
310 t.KeyShfHome = tc.getstr("kHOM")
311 t.KeyShfEnd = tc.getstr("kEND")
312
313
314
315
316 if t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" {
317 t.KeyShfUp = "\x1b[1;2A"
318 t.KeyShfDown = "\x1b[1;2B"
319 t.KeyMetaUp = "\x1b[1;9A"
320 t.KeyMetaDown = "\x1b[1;9B"
321 t.KeyMetaRight = "\x1b[1;9C"
322 t.KeyMetaLeft = "\x1b[1;9D"
323 t.KeyAltUp = "\x1b[1;3A"
324 t.KeyAltDown = "\x1b[1;3B"
325 t.KeyAltRight = "\x1b[1;3C"
326 t.KeyAltLeft = "\x1b[1;3D"
327 t.KeyCtrlUp = "\x1b[1;5A"
328 t.KeyCtrlDown = "\x1b[1;5B"
329 t.KeyCtrlRight = "\x1b[1;5C"
330 t.KeyCtrlLeft = "\x1b[1;5D"
331 t.KeyAltShfUp = "\x1b[1;4A"
332 t.KeyAltShfDown = "\x1b[1;4B"
333 t.KeyAltShfRight = "\x1b[1;4C"
334 t.KeyAltShfLeft = "\x1b[1;4D"
335
336 t.KeyMetaShfUp = "\x1b[1;10A"
337 t.KeyMetaShfDown = "\x1b[1;10B"
338 t.KeyMetaShfRight = "\x1b[1;10C"
339 t.KeyMetaShfLeft = "\x1b[1;10D"
340
341 t.KeyCtrlShfUp = "\x1b[1;6A"
342 t.KeyCtrlShfDown = "\x1b[1;6B"
343 t.KeyCtrlShfRight = "\x1b[1;6C"
344 t.KeyCtrlShfLeft = "\x1b[1;6D"
345
346 t.KeyShfPgUp = "\x1b[5;2~"
347 t.KeyShfPgDn = "\x1b[6;2~"
348 }
349
350 if t.KeyShfHome == "\x1b[1;2H" && t.KeyShfEnd == "\x1b[1;2F" {
351 t.KeyCtrlHome = "\x1b[1;5H"
352 t.KeyCtrlEnd = "\x1b[1;5F"
353 t.KeyAltHome = "\x1b[1;9H"
354 t.KeyAltEnd = "\x1b[1;9F"
355 t.KeyCtrlShfHome = "\x1b[1;6H"
356 t.KeyCtrlShfEnd = "\x1b[1;6F"
357 t.KeyAltShfHome = "\x1b[1;4H"
358 t.KeyAltShfEnd = "\x1b[1;4F"
359 t.KeyMetaShfHome = "\x1b[1;10H"
360 t.KeyMetaShfEnd = "\x1b[1;10F"
361 }
362
363
364
365
366 if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" {
367 t.KeyShfUp = "\x1b[a"
368 t.KeyShfDown = "\x1b[b"
369 t.KeyCtrlUp = "\x1b[Oa"
370 t.KeyCtrlDown = "\x1b[Ob"
371 t.KeyCtrlRight = "\x1b[Oc"
372 t.KeyCtrlLeft = "\x1b[Od"
373 }
374 if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" {
375 t.KeyCtrlHome = "\x1b[7^"
376 t.KeyCtrlEnd = "\x1b[8^"
377 }
378
379
380
381
382
383
384
385 if tc.getflag("Tc") {
386
387 t.TrueColor = true
388 } else if tc.getflag("RGB") {
389
390
391
392 t.TrueColor = true
393 t.SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"
394 t.SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"
395 }
396
397
398 if t.Colors < 8 || t.SetFg == "" {
399 t.Colors = 0
400 }
401 if t.SetCursor == "" {
402 return nil, "", errNotAddressable
403 }
404
405
406
407 t.PadChar = tc.getstr("pad")
408 if t.PadChar == "" {
409 if !tc.getflag("npc") {
410 t.PadChar = "\u0000"
411 }
412 }
413
414
415
416 if strings.HasPrefix(t.SetFg, "\x1b[") &&
417 strings.HasPrefix(t.SetBg, "\x1b[") &&
418 strings.HasSuffix(t.SetFg, "m") &&
419 strings.HasSuffix(t.SetBg, "m") {
420 fg := t.SetFg[:len(t.SetFg)-1]
421 r := regexp.MustCompile("%p1")
422 bg := r.ReplaceAllString(t.SetBg[2:], "%p2")
423 t.SetFgBg = fg + ";" + bg
424 }
425
426 return t, tc.desc, nil
427 }
428
View as plain text