dec.go

  1package terminfo
  2
  3import (
  4	"sort"
  5)
  6
  7const (
  8	// maxFileLength is the max file length.
  9	maxFileLength = 4096
 10	// magic is the file magic for terminfo files.
 11	magic = 0o432
 12	// magicExtended is the file magic for terminfo files with the extended
 13	// number format.
 14	magicExtended = 0o1036
 15)
 16
 17// header fields.
 18const (
 19	fieldMagic = iota
 20	fieldNameSize
 21	fieldBoolCount
 22	fieldNumCount
 23	fieldStringCount
 24	fieldTableSize
 25)
 26
 27// header extended fields.
 28const (
 29	fieldExtBoolCount = iota
 30	fieldExtNumCount
 31	fieldExtStringCount
 32	fieldExtOffsetCount
 33	fieldExtTableSize
 34)
 35
 36// hasInvalidCaps determines if the capabilities in h are invalid.
 37func hasInvalidCaps(h []int) bool {
 38	return h[fieldBoolCount] > CapCountBool ||
 39		h[fieldNumCount] > CapCountNum ||
 40		h[fieldStringCount] > CapCountString
 41}
 42
 43// capLength returns the total length of the capabilities in bytes.
 44func capLength(h []int) int {
 45	return h[fieldNameSize] +
 46		h[fieldBoolCount] +
 47		(h[fieldNameSize]+h[fieldBoolCount])%2 + // account for word align
 48		h[fieldNumCount]*2 +
 49		h[fieldStringCount]*2 +
 50		h[fieldTableSize]
 51}
 52
 53// hasInvalidExtOffset determines if the extended offset field is valid.
 54func hasInvalidExtOffset(h []int) bool {
 55	return h[fieldExtBoolCount]+
 56		h[fieldExtNumCount]+
 57		h[fieldExtStringCount]*2 != h[fieldExtOffsetCount]
 58}
 59
 60// extCapLength returns the total length of extended capabilities in bytes.
 61func extCapLength(h []int, numWidth int) int {
 62	return h[fieldExtBoolCount] +
 63		h[fieldExtBoolCount]%2 + // account for word align
 64		h[fieldExtNumCount]*(numWidth/8) +
 65		h[fieldExtOffsetCount]*2 +
 66		h[fieldExtTableSize]
 67}
 68
 69// findNull finds the position of null in buf.
 70func findNull(buf []byte, i int) int {
 71	for ; i < len(buf); i++ {
 72		if buf[i] == 0 {
 73			return i
 74		}
 75	}
 76	return -1
 77}
 78
 79// readStrings decodes n strings from string data table buf using the indexes in idx.
 80func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) {
 81	var last int
 82	m := make(map[int][]byte)
 83	for i := 0; i < n; i++ {
 84		start := idx[i]
 85		if start < 0 {
 86			continue
 87		}
 88		if end := findNull(buf, start); end != -1 {
 89			m[i], last = buf[start:end], end+1
 90		} else {
 91			return nil, 0, ErrInvalidStringTable
 92		}
 93	}
 94	return m, last, nil
 95}
 96
 97// decoder holds state info while decoding a terminfo file.
 98type decoder struct {
 99	buf []byte
100	pos int
101	n   int
102}
103
104// readBytes reads the next n bytes of buf, incrementing pos by n.
105func (d *decoder) readBytes(n int) ([]byte, error) {
106	if d.n < d.pos+n {
107		return nil, ErrUnexpectedFileEnd
108	}
109	n, d.pos = d.pos, d.pos+n
110	return d.buf[n:d.pos], nil
111}
112
113// readInts reads n number of ints with width w.
114func (d *decoder) readInts(n, w int) ([]int, error) {
115	w /= 8
116	l := n * w
117	buf, err := d.readBytes(l)
118	if err != nil {
119		return nil, err
120	}
121	// align
122	d.pos += d.pos % 2
123	z := make([]int, n)
124	for i, j := 0, 0; i < l; i, j = i+w, j+1 {
125		switch w {
126		case 1:
127			z[i] = int(buf[i])
128		case 2:
129			z[j] = int(int16(buf[i+1])<<8 | int16(buf[i]))
130		case 4:
131			z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i])
132		}
133	}
134	return z, nil
135}
136
137// readBools reads the next n bools.
138func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) {
139	buf, err := d.readInts(n, 8)
140	if err != nil {
141		return nil, nil, err
142	}
143	// process
144	bools, boolsM := make(map[int]bool), make(map[int]bool)
145	for i, b := range buf {
146		bools[i] = b == 1
147		if int8(b) == -2 {
148			boolsM[i] = true
149		}
150	}
151	return bools, boolsM, nil
152}
153
154// readNums reads the next n nums.
155func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) {
156	buf, err := d.readInts(n, w)
157	if err != nil {
158		return nil, nil, err
159	}
160	// process
161	nums, numsM := make(map[int]int), make(map[int]bool)
162	for i := 0; i < n; i++ {
163		nums[i] = buf[i]
164		if buf[i] == -2 {
165			numsM[i] = true
166		}
167	}
168	return nums, numsM, nil
169}
170
171// readStringTable reads the string data for n strings and the accompanying data
172// table of length sz.
173func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) {
174	buf, err := d.readInts(n, 16)
175	if err != nil {
176		return nil, nil, err
177	}
178	// read string data table
179	data, err := d.readBytes(sz)
180	if err != nil {
181		return nil, nil, err
182	}
183	// align
184	d.pos += d.pos % 2
185	// process
186	s := make([][]byte, n)
187	var m []int
188	for i := 0; i < n; i++ {
189		start := buf[i]
190		if start == -2 {
191			m = append(m, i)
192		} else if start >= 0 {
193			if end := findNull(data, start); end != -1 {
194				s[i] = data[start:end]
195			} else {
196				return nil, nil, ErrInvalidStringTable
197			}
198		}
199	}
200	return s, m, nil
201}
202
203// readStrings reads the next n strings and processes the string data table of
204// length sz.
205func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) {
206	s, m, err := d.readStringTable(n, sz)
207	if err != nil {
208		return nil, nil, err
209	}
210	strs := make(map[int][]byte)
211	for k, v := range s {
212		if k == AcsChars {
213			v = canonicalizeAscChars(v)
214		}
215		strs[k] = v
216	}
217	strsM := make(map[int]bool, len(m))
218	for _, k := range m {
219		strsM[k] = true
220	}
221	return strs, strsM, nil
222}
223
224// canonicalizeAscChars reorders chars to be unique, in order.
225//
226// see repair_ascc in ncurses-6.3/progs/dump_entry.c
227func canonicalizeAscChars(z []byte) []byte {
228	var c []byte
229	enc := make(map[byte]byte, len(z)/2)
230	for i := 0; i < len(z); i += 2 {
231		if _, ok := enc[z[i]]; !ok {
232			a, b := z[i], z[i+1]
233			// log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b)
234			c, enc[a] = append(c, b), b
235		}
236	}
237	sort.Slice(c, func(i, j int) bool {
238		return c[i] < c[j]
239	})
240	r := make([]byte, 2*len(c))
241	for i := 0; i < len(c); i++ {
242		r[i*2], r[i*2+1] = c[i], enc[c[i]]
243	}
244	return r
245}