terminfo.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. // Package terminfo implements reading terminfo files in pure go.
  2. package terminfo
  3. //go:generate go run gen.go
  4. import (
  5. "io"
  6. "io/ioutil"
  7. "path"
  8. "strconv"
  9. "strings"
  10. )
  11. // Error is a terminfo error.
  12. type Error string
  13. // Error satisfies the error interface.
  14. func (err Error) Error() string {
  15. return string(err)
  16. }
  17. const (
  18. // ErrInvalidFileSize is the invalid file size error.
  19. ErrInvalidFileSize Error = "invalid file size"
  20. // ErrUnexpectedFileEnd is the unexpected file end error.
  21. ErrUnexpectedFileEnd Error = "unexpected file end"
  22. // ErrInvalidStringTable is the invalid string table error.
  23. ErrInvalidStringTable Error = "invalid string table"
  24. // ErrInvalidMagic is the invalid magic error.
  25. ErrInvalidMagic Error = "invalid magic"
  26. // ErrInvalidHeader is the invalid header error.
  27. ErrInvalidHeader Error = "invalid header"
  28. // ErrInvalidNames is the invalid names error.
  29. ErrInvalidNames Error = "invalid names"
  30. // ErrInvalidExtendedHeader is the invalid extended header error.
  31. ErrInvalidExtendedHeader Error = "invalid extended header"
  32. // ErrEmptyTermName is the empty term name error.
  33. ErrEmptyTermName Error = "empty term name"
  34. // ErrDatabaseDirectoryNotFound is the database directory not found error.
  35. ErrDatabaseDirectoryNotFound Error = "database directory not found"
  36. // ErrFileNotFound is the file not found error.
  37. ErrFileNotFound Error = "file not found"
  38. // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error.
  39. ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION"
  40. )
  41. // Terminfo describes a terminal's capabilities.
  42. type Terminfo struct {
  43. // File is the original source file.
  44. File string
  45. // Names are the provided cap names.
  46. Names []string
  47. // Bools are the bool capabilities.
  48. Bools map[int]bool
  49. // BoolsM are the missing bool capabilities.
  50. BoolsM map[int]bool
  51. // Nums are the num capabilities.
  52. Nums map[int]int
  53. // NumsM are the missing num capabilities.
  54. NumsM map[int]bool
  55. // Strings are the string capabilities.
  56. Strings map[int][]byte
  57. // StringsM are the missing string capabilities.
  58. StringsM map[int]bool
  59. // ExtBools are the extended bool capabilities.
  60. ExtBools map[int]bool
  61. // ExtBoolsNames is the map of extended bool capabilities to their index.
  62. ExtBoolNames map[int][]byte
  63. // ExtNums are the extended num capabilities.
  64. ExtNums map[int]int
  65. // ExtNumsNames is the map of extended num capabilities to their index.
  66. ExtNumNames map[int][]byte
  67. // ExtStrings are the extended string capabilities.
  68. ExtStrings map[int][]byte
  69. // ExtStringsNames is the map of extended string capabilities to their index.
  70. ExtStringNames map[int][]byte
  71. }
  72. // Decode decodes the terminfo data contained in buf.
  73. func Decode(buf []byte) (*Terminfo, error) {
  74. var err error
  75. // check max file length
  76. if len(buf) >= maxFileLength {
  77. return nil, ErrInvalidFileSize
  78. }
  79. d := &decoder{
  80. buf: buf,
  81. n: len(buf),
  82. }
  83. // read header
  84. h, err := d.readInts(6, 16)
  85. if err != nil {
  86. return nil, err
  87. }
  88. var numWidth int
  89. // check magic
  90. switch {
  91. case h[fieldMagic] == magic:
  92. numWidth = 16
  93. case h[fieldMagic] == magicExtended:
  94. numWidth = 32
  95. default:
  96. return nil, ErrInvalidMagic
  97. }
  98. // check header
  99. if hasInvalidCaps(h) {
  100. return nil, ErrInvalidHeader
  101. }
  102. // check remaining length
  103. if d.n-d.pos < capLength(h) {
  104. return nil, ErrUnexpectedFileEnd
  105. }
  106. // read names
  107. names, err := d.readBytes(h[fieldNameSize])
  108. if err != nil {
  109. return nil, err
  110. }
  111. // check name is terminated properly
  112. i := findNull(names, 0)
  113. if i == -1 {
  114. return nil, ErrInvalidNames
  115. }
  116. names = names[:i]
  117. // read bool caps
  118. bools, boolsM, err := d.readBools(h[fieldBoolCount])
  119. if err != nil {
  120. return nil, err
  121. }
  122. // read num caps
  123. nums, numsM, err := d.readNums(h[fieldNumCount], numWidth)
  124. if err != nil {
  125. return nil, err
  126. }
  127. // read string caps
  128. strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize])
  129. if err != nil {
  130. return nil, err
  131. }
  132. ti := &Terminfo{
  133. Names: strings.Split(string(names), "|"),
  134. Bools: bools,
  135. BoolsM: boolsM,
  136. Nums: nums,
  137. NumsM: numsM,
  138. Strings: strs,
  139. StringsM: strsM,
  140. }
  141. // at the end of file, so no extended caps
  142. if d.pos >= d.n {
  143. return ti, nil
  144. }
  145. // decode extended header
  146. eh, err := d.readInts(5, 16)
  147. if err != nil {
  148. return nil, err
  149. }
  150. // check extended offset field
  151. if hasInvalidExtOffset(eh) {
  152. return nil, ErrInvalidExtendedHeader
  153. }
  154. // check extended cap lengths
  155. if d.n-d.pos != extCapLength(eh, numWidth) {
  156. return nil, ErrInvalidExtendedHeader
  157. }
  158. // read extended bool caps
  159. ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount])
  160. if err != nil {
  161. return nil, err
  162. }
  163. // read extended num caps
  164. ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth)
  165. if err != nil {
  166. return nil, err
  167. }
  168. // read extended string data table indexes
  169. extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16)
  170. if err != nil {
  171. return nil, err
  172. }
  173. // read string data table
  174. extData, err := d.readBytes(eh[fieldExtTableSize])
  175. if err != nil {
  176. return nil, err
  177. }
  178. // precautionary check that exactly at end of file
  179. if d.pos != d.n {
  180. return nil, ErrUnexpectedFileEnd
  181. }
  182. var last int
  183. // read extended string caps
  184. ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount])
  185. if err != nil {
  186. return nil, err
  187. }
  188. extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:]
  189. // read extended bool names
  190. ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount])
  191. if err != nil {
  192. return nil, err
  193. }
  194. extIndexes = extIndexes[eh[fieldExtBoolCount]:]
  195. // read extended num names
  196. ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount])
  197. if err != nil {
  198. return nil, err
  199. }
  200. extIndexes = extIndexes[eh[fieldExtNumCount]:]
  201. // read extended string names
  202. ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount])
  203. if err != nil {
  204. return nil, err
  205. }
  206. // extIndexes = extIndexes[eh[fieldExtStringCount]:]
  207. return ti, nil
  208. }
  209. // Open reads the terminfo file name from the specified directory dir.
  210. func Open(dir, name string) (*Terminfo, error) {
  211. var err error
  212. var buf []byte
  213. var filename string
  214. for _, f := range []string{
  215. path.Join(dir, name[0:1], name),
  216. path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name),
  217. } {
  218. buf, err = ioutil.ReadFile(f)
  219. if err == nil {
  220. filename = f
  221. break
  222. }
  223. }
  224. if buf == nil {
  225. return nil, ErrFileNotFound
  226. }
  227. // decode
  228. ti, err := Decode(buf)
  229. if err != nil {
  230. return nil, err
  231. }
  232. // save original file name
  233. ti.File = filename
  234. // add to cache
  235. termCache.Lock()
  236. for _, n := range ti.Names {
  237. termCache.db[n] = ti
  238. }
  239. termCache.Unlock()
  240. return ti, nil
  241. }
  242. // boolCaps returns all bool and extended capabilities using f to format the
  243. // index key.
  244. func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool {
  245. m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools))
  246. if !extended {
  247. for k, v := range ti.Bools {
  248. m[f(k)] = v
  249. }
  250. } else {
  251. for k, v := range ti.ExtBools {
  252. m[string(ti.ExtBoolNames[k])] = v
  253. }
  254. }
  255. return m
  256. }
  257. // BoolCaps returns all bool capabilities.
  258. func (ti *Terminfo) BoolCaps() map[string]bool {
  259. return ti.boolCaps(BoolCapName, false)
  260. }
  261. // BoolCapsShort returns all bool capabilities, using the short name as the
  262. // index.
  263. func (ti *Terminfo) BoolCapsShort() map[string]bool {
  264. return ti.boolCaps(BoolCapNameShort, false)
  265. }
  266. // ExtBoolCaps returns all extended bool capabilities.
  267. func (ti *Terminfo) ExtBoolCaps() map[string]bool {
  268. return ti.boolCaps(BoolCapName, true)
  269. }
  270. // ExtBoolCapsShort returns all extended bool capabilities, using the short
  271. // name as the index.
  272. func (ti *Terminfo) ExtBoolCapsShort() map[string]bool {
  273. return ti.boolCaps(BoolCapNameShort, true)
  274. }
  275. // numCaps returns all num and extended capabilities using f to format the
  276. // index key.
  277. func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int {
  278. m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums))
  279. if !extended {
  280. for k, v := range ti.Nums {
  281. m[f(k)] = v
  282. }
  283. } else {
  284. for k, v := range ti.ExtNums {
  285. m[string(ti.ExtNumNames[k])] = v
  286. }
  287. }
  288. return m
  289. }
  290. // NumCaps returns all num capabilities.
  291. func (ti *Terminfo) NumCaps() map[string]int {
  292. return ti.numCaps(NumCapName, false)
  293. }
  294. // NumCapsShort returns all num capabilities, using the short name as the
  295. // index.
  296. func (ti *Terminfo) NumCapsShort() map[string]int {
  297. return ti.numCaps(NumCapNameShort, false)
  298. }
  299. // ExtNumCaps returns all extended num capabilities.
  300. func (ti *Terminfo) ExtNumCaps() map[string]int {
  301. return ti.numCaps(NumCapName, true)
  302. }
  303. // ExtNumCapsShort returns all extended num capabilities, using the short
  304. // name as the index.
  305. func (ti *Terminfo) ExtNumCapsShort() map[string]int {
  306. return ti.numCaps(NumCapNameShort, true)
  307. }
  308. // stringCaps returns all string and extended capabilities using f to format the
  309. // index key.
  310. func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte {
  311. m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings))
  312. if !extended {
  313. for k, v := range ti.Strings {
  314. m[f(k)] = v
  315. }
  316. } else {
  317. for k, v := range ti.ExtStrings {
  318. m[string(ti.ExtStringNames[k])] = v
  319. }
  320. }
  321. return m
  322. }
  323. // StringCaps returns all string capabilities.
  324. func (ti *Terminfo) StringCaps() map[string][]byte {
  325. return ti.stringCaps(StringCapName, false)
  326. }
  327. // StringCapsShort returns all string capabilities, using the short name as the
  328. // index.
  329. func (ti *Terminfo) StringCapsShort() map[string][]byte {
  330. return ti.stringCaps(StringCapNameShort, false)
  331. }
  332. // ExtStringCaps returns all extended string capabilities.
  333. func (ti *Terminfo) ExtStringCaps() map[string][]byte {
  334. return ti.stringCaps(StringCapName, true)
  335. }
  336. // ExtStringCapsShort returns all extended string capabilities, using the short
  337. // name as the index.
  338. func (ti *Terminfo) ExtStringCapsShort() map[string][]byte {
  339. return ti.stringCaps(StringCapNameShort, true)
  340. }
  341. // Has determines if the bool cap i is present.
  342. func (ti *Terminfo) Has(i int) bool {
  343. return ti.Bools[i]
  344. }
  345. // Num returns the num cap i, or -1 if not present.
  346. func (ti *Terminfo) Num(i int) int {
  347. n, ok := ti.Nums[i]
  348. if !ok {
  349. return -1
  350. }
  351. return n
  352. }
  353. // Printf formats the string cap i, interpolating parameters v.
  354. func (ti *Terminfo) Printf(i int, v ...interface{}) string {
  355. return Printf(ti.Strings[i], v...)
  356. }
  357. // Fprintf prints the string cap i to writer w, interpolating parameters v.
  358. func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) {
  359. Fprintf(w, ti.Strings[i], v...)
  360. }
  361. // Color takes a foreground and background color and returns string that sets
  362. // them for this terminal.
  363. func (ti *Terminfo) Colorf(fg, bg int, str string) string {
  364. maxColors := int(ti.Nums[MaxColors])
  365. // map bright colors to lower versions if the color table only holds 8.
  366. if maxColors == 8 {
  367. if fg > 7 && fg < 16 {
  368. fg -= 8
  369. }
  370. if bg > 7 && bg < 16 {
  371. bg -= 8
  372. }
  373. }
  374. var s string
  375. if maxColors > fg && fg >= 0 {
  376. s += ti.Printf(SetAForeground, fg)
  377. }
  378. if maxColors > bg && bg >= 0 {
  379. s += ti.Printf(SetABackground, bg)
  380. }
  381. return s + str + ti.Printf(ExitAttributeMode)
  382. }
  383. // Goto returns a string suitable for addressing the cursor at the given
  384. // row and column. The origin 0, 0 is in the upper left corner of the screen.
  385. func (ti *Terminfo) Goto(row, col int) string {
  386. return Printf(ti.Strings[CursorAddress], row, col)
  387. }
  388. // Puts emits the string to the writer, but expands inline padding indications
  389. // (of the form $<[delay]> where [delay] is msec) to a suitable number of
  390. // padding characters (usually null bytes) based upon the supplied baud. At
  391. // high baud rates, more padding characters will be inserted.
  392. /*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) {
  393. var err error
  394. for {
  395. start := strings.Index(s, "$<")
  396. if start == -1 {
  397. // most strings don't need padding, which is good news!
  398. return io.WriteString(w, s)
  399. }
  400. end := strings.Index(s, ">")
  401. if end == -1 {
  402. // unterminated... just emit bytes unadulterated.
  403. return io.WriteString(w, "$<"+s)
  404. }
  405. var c int
  406. c, err = io.WriteString(w, s[:start])
  407. if err != nil {
  408. return n + c, err
  409. }
  410. n += c
  411. s = s[start+2:]
  412. val := s[:end]
  413. s = s[end+1:]
  414. var ms int
  415. var dot, mandatory, asterisk bool
  416. unit := 1000
  417. for _, ch := range val {
  418. switch {
  419. case ch >= '0' && ch <= '9':
  420. ms = (ms * 10) + int(ch-'0')
  421. if dot {
  422. unit *= 10
  423. }
  424. case ch == '.' && !dot:
  425. dot = true
  426. case ch == '*' && !asterisk:
  427. ms *= lines
  428. asterisk = true
  429. case ch == '/':
  430. mandatory = true
  431. default:
  432. break
  433. }
  434. }
  435. z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar]
  436. b := make([]byte, len(pad)*z)
  437. for bp := copy(b, pad); bp < len(b); bp *= 2 {
  438. copy(b[bp:], b[:bp])
  439. }
  440. if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory {
  441. c, err = w.Write(b)
  442. if err != nil {
  443. return n + c, err
  444. }
  445. n += c
  446. }
  447. }
  448. return n, nil
  449. }*/