terminfo.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. // Copyright 2022 The TCell Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use file except in compliance with the License.
  5. // You may obtain a copy of the license at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package terminfo
  15. import (
  16. "bytes"
  17. "errors"
  18. "fmt"
  19. "io"
  20. "os"
  21. "strconv"
  22. "strings"
  23. "sync"
  24. "time"
  25. )
  26. var (
  27. // ErrTermNotFound indicates that a suitable terminal entry could
  28. // not be found. This can result from either not having TERM set,
  29. // or from the TERM failing to support certain minimal functionality,
  30. // in particular absolute cursor addressability (the cup capability)
  31. // is required. For example, legacy "adm3" lacks this capability,
  32. // whereas the slightly newer "adm3a" supports it. This failure
  33. // occurs most often with "dumb".
  34. ErrTermNotFound = errors.New("terminal entry not found")
  35. )
  36. // Terminfo represents a terminfo entry. Note that we use friendly names
  37. // in Go, but when we write out JSON, we use the same names as terminfo.
  38. // The name, aliases and smous, rmous fields do not come from terminfo directly.
  39. type Terminfo struct {
  40. Name string
  41. Aliases []string
  42. Columns int // cols
  43. Lines int // lines
  44. Colors int // colors
  45. Bell string // bell
  46. Clear string // clear
  47. EnterCA string // smcup
  48. ExitCA string // rmcup
  49. ShowCursor string // cnorm
  50. HideCursor string // civis
  51. AttrOff string // sgr0
  52. Underline string // smul
  53. Bold string // bold
  54. Blink string // blink
  55. Reverse string // rev
  56. Dim string // dim
  57. Italic string // sitm
  58. EnterKeypad string // smkx
  59. ExitKeypad string // rmkx
  60. SetFg string // setaf
  61. SetBg string // setab
  62. ResetFgBg string // op
  63. SetCursor string // cup
  64. CursorBack1 string // cub1
  65. CursorUp1 string // cuu1
  66. PadChar string // pad
  67. KeyBackspace string // kbs
  68. KeyF1 string // kf1
  69. KeyF2 string // kf2
  70. KeyF3 string // kf3
  71. KeyF4 string // kf4
  72. KeyF5 string // kf5
  73. KeyF6 string // kf6
  74. KeyF7 string // kf7
  75. KeyF8 string // kf8
  76. KeyF9 string // kf9
  77. KeyF10 string // kf10
  78. KeyF11 string // kf11
  79. KeyF12 string // kf12
  80. KeyF13 string // kf13
  81. KeyF14 string // kf14
  82. KeyF15 string // kf15
  83. KeyF16 string // kf16
  84. KeyF17 string // kf17
  85. KeyF18 string // kf18
  86. KeyF19 string // kf19
  87. KeyF20 string // kf20
  88. KeyF21 string // kf21
  89. KeyF22 string // kf22
  90. KeyF23 string // kf23
  91. KeyF24 string // kf24
  92. KeyF25 string // kf25
  93. KeyF26 string // kf26
  94. KeyF27 string // kf27
  95. KeyF28 string // kf28
  96. KeyF29 string // kf29
  97. KeyF30 string // kf30
  98. KeyF31 string // kf31
  99. KeyF32 string // kf32
  100. KeyF33 string // kf33
  101. KeyF34 string // kf34
  102. KeyF35 string // kf35
  103. KeyF36 string // kf36
  104. KeyF37 string // kf37
  105. KeyF38 string // kf38
  106. KeyF39 string // kf39
  107. KeyF40 string // kf40
  108. KeyF41 string // kf41
  109. KeyF42 string // kf42
  110. KeyF43 string // kf43
  111. KeyF44 string // kf44
  112. KeyF45 string // kf45
  113. KeyF46 string // kf46
  114. KeyF47 string // kf47
  115. KeyF48 string // kf48
  116. KeyF49 string // kf49
  117. KeyF50 string // kf50
  118. KeyF51 string // kf51
  119. KeyF52 string // kf52
  120. KeyF53 string // kf53
  121. KeyF54 string // kf54
  122. KeyF55 string // kf55
  123. KeyF56 string // kf56
  124. KeyF57 string // kf57
  125. KeyF58 string // kf58
  126. KeyF59 string // kf59
  127. KeyF60 string // kf60
  128. KeyF61 string // kf61
  129. KeyF62 string // kf62
  130. KeyF63 string // kf63
  131. KeyF64 string // kf64
  132. KeyInsert string // kich1
  133. KeyDelete string // kdch1
  134. KeyHome string // khome
  135. KeyEnd string // kend
  136. KeyHelp string // khlp
  137. KeyPgUp string // kpp
  138. KeyPgDn string // knp
  139. KeyUp string // kcuu1
  140. KeyDown string // kcud1
  141. KeyLeft string // kcub1
  142. KeyRight string // kcuf1
  143. KeyBacktab string // kcbt
  144. KeyExit string // kext
  145. KeyClear string // kclr
  146. KeyPrint string // kprt
  147. KeyCancel string // kcan
  148. Mouse string // kmous
  149. AltChars string // acsc
  150. EnterAcs string // smacs
  151. ExitAcs string // rmacs
  152. EnableAcs string // enacs
  153. KeyShfRight string // kRIT
  154. KeyShfLeft string // kLFT
  155. KeyShfHome string // kHOM
  156. KeyShfEnd string // kEND
  157. KeyShfInsert string // kIC
  158. KeyShfDelete string // kDC
  159. // These are non-standard extensions to terminfo. This includes
  160. // true color support, and some additional keys. Its kind of bizarre
  161. // that shifted variants of left and right exist, but not up and down.
  162. // Terminal support for these are going to vary amongst XTerm
  163. // emulations, so don't depend too much on them in your application.
  164. StrikeThrough string // smxx
  165. SetFgBg string // setfgbg
  166. SetFgBgRGB string // setfgbgrgb
  167. SetFgRGB string // setfrgb
  168. SetBgRGB string // setbrgb
  169. KeyShfUp string // shift-up
  170. KeyShfDown string // shift-down
  171. KeyShfPgUp string // shift-kpp
  172. KeyShfPgDn string // shift-knp
  173. KeyCtrlUp string // ctrl-up
  174. KeyCtrlDown string // ctrl-left
  175. KeyCtrlRight string // ctrl-right
  176. KeyCtrlLeft string // ctrl-left
  177. KeyMetaUp string // meta-up
  178. KeyMetaDown string // meta-left
  179. KeyMetaRight string // meta-right
  180. KeyMetaLeft string // meta-left
  181. KeyAltUp string // alt-up
  182. KeyAltDown string // alt-left
  183. KeyAltRight string // alt-right
  184. KeyAltLeft string // alt-left
  185. KeyCtrlHome string
  186. KeyCtrlEnd string
  187. KeyMetaHome string
  188. KeyMetaEnd string
  189. KeyAltHome string
  190. KeyAltEnd string
  191. KeyAltShfUp string
  192. KeyAltShfDown string
  193. KeyAltShfLeft string
  194. KeyAltShfRight string
  195. KeyMetaShfUp string
  196. KeyMetaShfDown string
  197. KeyMetaShfLeft string
  198. KeyMetaShfRight string
  199. KeyCtrlShfUp string
  200. KeyCtrlShfDown string
  201. KeyCtrlShfLeft string
  202. KeyCtrlShfRight string
  203. KeyCtrlShfHome string
  204. KeyCtrlShfEnd string
  205. KeyAltShfHome string
  206. KeyAltShfEnd string
  207. KeyMetaShfHome string
  208. KeyMetaShfEnd string
  209. EnablePaste string // bracketed paste mode
  210. DisablePaste string
  211. PasteStart string
  212. PasteEnd string
  213. Modifiers int
  214. InsertChar string // string to insert a character (ich1)
  215. AutoMargin bool // true if writing to last cell in line advances
  216. TrueColor bool // true if the terminal supports direct color
  217. CursorDefault string
  218. CursorBlinkingBlock string
  219. CursorSteadyBlock string
  220. CursorBlinkingUnderline string
  221. CursorSteadyUnderline string
  222. CursorBlinkingBar string
  223. CursorSteadyBar string
  224. EnterUrl string
  225. ExitUrl string
  226. SetWindowSize string
  227. }
  228. const (
  229. ModifiersNone = 0
  230. ModifiersXTerm = 1
  231. )
  232. type stack []interface{}
  233. func (st stack) Push(v interface{}) stack {
  234. if b, ok := v.(bool); ok {
  235. if b {
  236. return append(st, 1)
  237. } else {
  238. return append(st, 0)
  239. }
  240. }
  241. return append(st, v)
  242. }
  243. func (st stack) PopString() (string, stack) {
  244. if len(st) > 0 {
  245. e := st[len(st)-1]
  246. var s string
  247. switch v := e.(type) {
  248. case int:
  249. s = strconv.Itoa(v)
  250. case string:
  251. s = v
  252. }
  253. return s, st[:len(st)-1]
  254. }
  255. return "", st
  256. }
  257. func (st stack) PopInt() (int, stack) {
  258. if len(st) > 0 {
  259. e := st[len(st)-1]
  260. var i int
  261. switch v := e.(type) {
  262. case int:
  263. i = v
  264. case string:
  265. i, _ = strconv.Atoi(v)
  266. }
  267. return i, st[:len(st)-1]
  268. }
  269. return 0, st
  270. }
  271. // static vars
  272. var svars [26]string
  273. type paramsBuffer struct {
  274. out bytes.Buffer
  275. buf bytes.Buffer
  276. }
  277. // Start initializes the params buffer with the initial string data.
  278. // It also locks the paramsBuffer. The caller must call End() when
  279. // finished.
  280. func (pb *paramsBuffer) Start(s string) {
  281. pb.out.Reset()
  282. pb.buf.Reset()
  283. pb.buf.WriteString(s)
  284. }
  285. // End returns the final output from TParam, but it also releases the lock.
  286. func (pb *paramsBuffer) End() string {
  287. s := pb.out.String()
  288. return s
  289. }
  290. // NextCh returns the next input character to the expander.
  291. func (pb *paramsBuffer) NextCh() (byte, error) {
  292. return pb.buf.ReadByte()
  293. }
  294. // PutCh "emits" (rather schedules for output) a single byte character.
  295. func (pb *paramsBuffer) PutCh(ch byte) {
  296. pb.out.WriteByte(ch)
  297. }
  298. // PutString schedules a string for output.
  299. func (pb *paramsBuffer) PutString(s string) {
  300. pb.out.WriteString(s)
  301. }
  302. // TParm takes a terminfo parameterized string, such as setaf or cup, and
  303. // evaluates the string, and returns the result with the parameter
  304. // applied.
  305. func (t *Terminfo) TParm(s string, p ...interface{}) string {
  306. var stk stack
  307. var a string
  308. var ai, bi int
  309. var dvars [26]string
  310. var params [9]interface{}
  311. var pb = &paramsBuffer{}
  312. pb.Start(s)
  313. // make sure we always have 9 parameters -- makes it easier
  314. // later to skip checks
  315. for i := 0; i < len(params) && i < len(p); i++ {
  316. params[i] = p[i]
  317. }
  318. const (
  319. emit = iota
  320. toEnd
  321. toElse
  322. )
  323. skip := emit
  324. for {
  325. ch, err := pb.NextCh()
  326. if err != nil {
  327. break
  328. }
  329. if ch != '%' {
  330. if skip == emit {
  331. pb.PutCh(ch)
  332. }
  333. continue
  334. }
  335. ch, err = pb.NextCh()
  336. if err != nil {
  337. // XXX Error
  338. break
  339. }
  340. if skip == toEnd {
  341. if ch == ';' {
  342. skip = emit
  343. }
  344. continue
  345. } else if skip == toElse {
  346. if ch == 'e' || ch == ';' {
  347. skip = emit
  348. }
  349. continue
  350. }
  351. switch ch {
  352. case '%': // quoted %
  353. pb.PutCh(ch)
  354. case 'i': // increment both parameters (ANSI cup support)
  355. if i, ok := params[0].(int); ok {
  356. params[0] = i + 1
  357. }
  358. if i, ok := params[1].(int); ok {
  359. params[1] = i + 1
  360. }
  361. case 's':
  362. // NB: 's', 'c', and 'd' below are special cased for
  363. // efficiency. They could be handled by the richer
  364. // format support below, less efficiently.
  365. a, stk = stk.PopString()
  366. pb.PutString(a)
  367. case 'c':
  368. // Integer as special character.
  369. ai, stk = stk.PopInt()
  370. pb.PutCh(byte(ai))
  371. case 'd':
  372. ai, stk = stk.PopInt()
  373. pb.PutString(strconv.Itoa(ai))
  374. case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'x', 'X', 'o', ':':
  375. // This is pretty suboptimal, but this is rarely used.
  376. // None of the mainstream terminals use any of this,
  377. // and it would surprise me if this code is ever
  378. // executed outside test cases.
  379. f := "%"
  380. if ch == ':' {
  381. ch, _ = pb.NextCh()
  382. }
  383. f += string(ch)
  384. for ch == '+' || ch == '-' || ch == '#' || ch == ' ' {
  385. ch, _ = pb.NextCh()
  386. f += string(ch)
  387. }
  388. for (ch >= '0' && ch <= '9') || ch == '.' {
  389. ch, _ = pb.NextCh()
  390. f += string(ch)
  391. }
  392. switch ch {
  393. case 'd', 'x', 'X', 'o':
  394. ai, stk = stk.PopInt()
  395. pb.PutString(fmt.Sprintf(f, ai))
  396. case 's':
  397. a, stk = stk.PopString()
  398. pb.PutString(fmt.Sprintf(f, a))
  399. case 'c':
  400. ai, stk = stk.PopInt()
  401. pb.PutString(fmt.Sprintf(f, ai))
  402. }
  403. case 'p': // push parameter
  404. ch, _ = pb.NextCh()
  405. ai = int(ch - '1')
  406. if ai >= 0 && ai < len(params) {
  407. stk = stk.Push(params[ai])
  408. } else {
  409. stk = stk.Push(0)
  410. }
  411. case 'P': // pop & store variable
  412. ch, _ = pb.NextCh()
  413. if ch >= 'A' && ch <= 'Z' {
  414. svars[int(ch-'A')], stk = stk.PopString()
  415. } else if ch >= 'a' && ch <= 'z' {
  416. dvars[int(ch-'a')], stk = stk.PopString()
  417. }
  418. case 'g': // recall & push variable
  419. ch, _ = pb.NextCh()
  420. if ch >= 'A' && ch <= 'Z' {
  421. stk = stk.Push(svars[int(ch-'A')])
  422. } else if ch >= 'a' && ch <= 'z' {
  423. stk = stk.Push(dvars[int(ch-'a')])
  424. }
  425. case '\'': // push(char) - the integer value of it
  426. ch, _ = pb.NextCh()
  427. _, _ = pb.NextCh() // must be ' but we don't check
  428. stk = stk.Push(int(ch))
  429. case '{': // push(int)
  430. ai = 0
  431. ch, _ = pb.NextCh()
  432. for ch >= '0' && ch <= '9' {
  433. ai *= 10
  434. ai += int(ch - '0')
  435. ch, _ = pb.NextCh()
  436. }
  437. // ch must be '}' but no verification
  438. stk = stk.Push(ai)
  439. case 'l': // push(strlen(pop))
  440. a, stk = stk.PopString()
  441. stk = stk.Push(len(a))
  442. case '+':
  443. bi, stk = stk.PopInt()
  444. ai, stk = stk.PopInt()
  445. stk = stk.Push(ai + bi)
  446. case '-':
  447. bi, stk = stk.PopInt()
  448. ai, stk = stk.PopInt()
  449. stk = stk.Push(ai - bi)
  450. case '*':
  451. bi, stk = stk.PopInt()
  452. ai, stk = stk.PopInt()
  453. stk = stk.Push(ai * bi)
  454. case '/':
  455. bi, stk = stk.PopInt()
  456. ai, stk = stk.PopInt()
  457. if bi != 0 {
  458. stk = stk.Push(ai / bi)
  459. } else {
  460. stk = stk.Push(0)
  461. }
  462. case 'm': // push(pop mod pop)
  463. bi, stk = stk.PopInt()
  464. ai, stk = stk.PopInt()
  465. if bi != 0 {
  466. stk = stk.Push(ai % bi)
  467. } else {
  468. stk = stk.Push(0)
  469. }
  470. case '&': // AND
  471. bi, stk = stk.PopInt()
  472. ai, stk = stk.PopInt()
  473. stk = stk.Push(ai & bi)
  474. case '|': // OR
  475. bi, stk = stk.PopInt()
  476. ai, stk = stk.PopInt()
  477. stk = stk.Push(ai | bi)
  478. case '^': // XOR
  479. bi, stk = stk.PopInt()
  480. ai, stk = stk.PopInt()
  481. stk = stk.Push(ai ^ bi)
  482. case '~': // bit complement
  483. ai, stk = stk.PopInt()
  484. stk = stk.Push(ai ^ -1)
  485. case '!': // logical NOT
  486. ai, stk = stk.PopInt()
  487. stk = stk.Push(ai == 0)
  488. case '=': // numeric compare
  489. bi, stk = stk.PopInt()
  490. ai, stk = stk.PopInt()
  491. stk = stk.Push(ai == bi)
  492. case '>': // greater than, numeric
  493. bi, stk = stk.PopInt()
  494. ai, stk = stk.PopInt()
  495. stk = stk.Push(ai > bi)
  496. case '<': // less than, numeric
  497. bi, stk = stk.PopInt()
  498. ai, stk = stk.PopInt()
  499. stk = stk.Push(ai < bi)
  500. case '?': // start conditional
  501. case ';':
  502. skip = emit
  503. case 't':
  504. ai, stk = stk.PopInt()
  505. if ai == 0 {
  506. skip = toElse
  507. }
  508. case 'e':
  509. skip = toEnd
  510. default:
  511. pb.PutString("%" + string(ch))
  512. }
  513. }
  514. return pb.End()
  515. }
  516. // TPuts emits the string to the writer, but expands inline padding
  517. // indications (of the form $<[delay]> where [delay] is msec) to
  518. // a suitable time (unless the terminfo string indicates this isn't needed
  519. // by specifying npc - no padding). All Terminfo based strings should be
  520. // emitted using this function.
  521. func (t *Terminfo) TPuts(w io.Writer, s string) {
  522. for {
  523. beg := strings.Index(s, "$<")
  524. if beg < 0 {
  525. // Most strings don't need padding, which is good news!
  526. _, _ = io.WriteString(w, s)
  527. return
  528. }
  529. _, _ = io.WriteString(w, s[:beg])
  530. s = s[beg+2:]
  531. end := strings.Index(s, ">")
  532. if end < 0 {
  533. // unterminated.. just emit bytes unadulterated
  534. _, _ = io.WriteString(w, "$<"+s)
  535. return
  536. }
  537. val := s[:end]
  538. s = s[end+1:]
  539. padus := 0
  540. unit := time.Millisecond
  541. dot := false
  542. loop:
  543. for i := range val {
  544. switch val[i] {
  545. case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
  546. padus *= 10
  547. padus += int(val[i] - '0')
  548. if dot {
  549. unit /= 10
  550. }
  551. case '.':
  552. if !dot {
  553. dot = true
  554. } else {
  555. break loop
  556. }
  557. default:
  558. break loop
  559. }
  560. }
  561. // Curses historically uses padding to achieve "fine grained"
  562. // delays. We have much better clocks these days, and so we
  563. // do not rely on padding but simply sleep a bit.
  564. if len(t.PadChar) > 0 {
  565. time.Sleep(unit * time.Duration(padus))
  566. }
  567. }
  568. }
  569. // TGoto returns a string suitable for addressing the cursor at the given
  570. // row and column. The origin 0, 0 is in the upper left corner of the screen.
  571. func (t *Terminfo) TGoto(col, row int) string {
  572. return t.TParm(t.SetCursor, row, col)
  573. }
  574. // TColor returns a string corresponding to the given foreground and background
  575. // colors. Either fg or bg can be set to -1 to elide.
  576. func (t *Terminfo) TColor(fi, bi int) string {
  577. rv := ""
  578. // As a special case, we map bright colors to lower versions if the
  579. // color table only holds 8. For the remaining 240 colors, the user
  580. // is out of luck. Someday we could create a mapping table, but its
  581. // not worth it.
  582. if t.Colors == 8 {
  583. if fi > 7 && fi < 16 {
  584. fi -= 8
  585. }
  586. if bi > 7 && bi < 16 {
  587. bi -= 8
  588. }
  589. }
  590. if t.Colors > fi && fi >= 0 {
  591. rv += t.TParm(t.SetFg, fi)
  592. }
  593. if t.Colors > bi && bi >= 0 {
  594. rv += t.TParm(t.SetBg, bi)
  595. }
  596. return rv
  597. }
  598. var (
  599. dblock sync.Mutex
  600. terminfos = make(map[string]*Terminfo)
  601. )
  602. // AddTerminfo can be called to register a new Terminfo entry.
  603. func AddTerminfo(t *Terminfo) {
  604. dblock.Lock()
  605. terminfos[t.Name] = t
  606. for _, x := range t.Aliases {
  607. terminfos[x] = t
  608. }
  609. dblock.Unlock()
  610. }
  611. // LookupTerminfo attempts to find a definition for the named $TERM.
  612. func LookupTerminfo(name string) (*Terminfo, error) {
  613. if name == "" {
  614. // else on windows: index out of bounds
  615. // on the name[0] reference below
  616. return nil, ErrTermNotFound
  617. }
  618. addtruecolor := false
  619. add256color := false
  620. switch os.Getenv("COLORTERM") {
  621. case "truecolor", "24bit", "24-bit":
  622. addtruecolor = true
  623. }
  624. dblock.Lock()
  625. t := terminfos[name]
  626. dblock.Unlock()
  627. // If the name ends in -truecolor, then fabricate an entry
  628. // from the corresponding -256color, -color, or bare terminal.
  629. if t != nil && t.TrueColor {
  630. addtruecolor = true
  631. } else if t == nil && strings.HasSuffix(name, "-truecolor") {
  632. suffixes := []string{
  633. "-256color",
  634. "-88color",
  635. "-color",
  636. "",
  637. }
  638. base := name[:len(name)-len("-truecolor")]
  639. for _, s := range suffixes {
  640. if t, _ = LookupTerminfo(base + s); t != nil {
  641. addtruecolor = true
  642. break
  643. }
  644. }
  645. }
  646. // If the name ends in -256color, maybe fabricate using the xterm 256 color sequences
  647. if t == nil && strings.HasSuffix(name, "-256color") {
  648. suffixes := []string{
  649. "-88color",
  650. "-color",
  651. }
  652. base := name[:len(name)-len("-256color")]
  653. for _, s := range suffixes {
  654. if t, _ = LookupTerminfo(base + s); t != nil {
  655. add256color = true
  656. break
  657. }
  658. }
  659. }
  660. if t == nil {
  661. return nil, ErrTermNotFound
  662. }
  663. switch os.Getenv("TCELL_TRUECOLOR") {
  664. case "":
  665. case "disable":
  666. addtruecolor = false
  667. default:
  668. addtruecolor = true
  669. }
  670. // If the user has requested 24-bit color with $COLORTERM, then
  671. // amend the value (unless already present). This means we don't
  672. // need to have a value present.
  673. if addtruecolor &&
  674. t.SetFgBgRGB == "" &&
  675. t.SetFgRGB == "" &&
  676. t.SetBgRGB == "" {
  677. // Supply vanilla ISO 8613-6:1994 24-bit color sequences.
  678. t.SetFgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%dm"
  679. t.SetBgRGB = "\x1b[48;2;%p1%d;%p2%d;%p3%dm"
  680. t.SetFgBgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%d;" +
  681. "48;2;%p4%d;%p5%d;%p6%dm"
  682. }
  683. if add256color {
  684. t.Colors = 256
  685. t.SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m"
  686. t.SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m"
  687. t.SetFgBg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;;%?%p2%{8}%<%t4%p2%d%e%p2%{16}%<%t10%p2%{8}%-%d%e48;5;%p2%d%;m"
  688. t.ResetFgBg = "\x1b[39;49m"
  689. }
  690. return t, nil
  691. }