diff options
Diffstat (limited to 'vendor/github.com/xo/terminfo/terminfo.go')
-rw-r--r-- | vendor/github.com/xo/terminfo/terminfo.go | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/vendor/github.com/xo/terminfo/terminfo.go b/vendor/github.com/xo/terminfo/terminfo.go new file mode 100644 index 000000000..8ebbf9599 --- /dev/null +++ b/vendor/github.com/xo/terminfo/terminfo.go @@ -0,0 +1,538 @@ +// Package terminfo implements reading terminfo files in pure go. +package terminfo + +import ( + "io" + "io/ioutil" + "path" + "strconv" + "strings" +) + +// Error is a terminfo error. +type Error string + +// Error satisfies the error interface. +func (err Error) Error() string { + return string(err) +} + +const ( + // ErrInvalidFileSize is the invalid file size error. + ErrInvalidFileSize Error = "invalid file size" + + // ErrUnexpectedFileEnd is the unexpected file end error. + ErrUnexpectedFileEnd Error = "unexpected file end" + + // ErrInvalidStringTable is the invalid string table error. + ErrInvalidStringTable Error = "invalid string table" + + // ErrInvalidMagic is the invalid magic error. + ErrInvalidMagic Error = "invalid magic" + + // ErrInvalidHeader is the invalid header error. + ErrInvalidHeader Error = "invalid header" + + // ErrInvalidNames is the invalid names error. + ErrInvalidNames Error = "invalid names" + + // ErrInvalidExtendedHeader is the invalid extended header error. + ErrInvalidExtendedHeader Error = "invalid extended header" + + // ErrEmptyTermName is the empty term name error. + ErrEmptyTermName Error = "empty term name" + + // ErrDatabaseDirectoryNotFound is the database directory not found error. + ErrDatabaseDirectoryNotFound Error = "database directory not found" + + // ErrFileNotFound is the file not found error. + ErrFileNotFound Error = "file not found" + + // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error. + ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION" +) + +// Terminfo describes a terminal's capabilities. +type Terminfo struct { + // File is the original source file. + File string + + // Names are the provided cap names. + Names []string + + // Bools are the bool capabilities. + Bools map[int]bool + + // BoolsM are the missing bool capabilities. + BoolsM map[int]bool + + // Nums are the num capabilities. + Nums map[int]int + + // NumsM are the missing num capabilities. + NumsM map[int]bool + + // Strings are the string capabilities. + Strings map[int][]byte + + // StringsM are the missing string capabilities. + StringsM map[int]bool + + // ExtBools are the extended bool capabilities. + ExtBools map[int]bool + + // ExtBoolsNames is the map of extended bool capabilities to their index. + ExtBoolNames map[int][]byte + + // ExtNums are the extended num capabilities. + ExtNums map[int]int + + // ExtNumsNames is the map of extended num capabilities to their index. + ExtNumNames map[int][]byte + + // ExtStrings are the extended string capabilities. + ExtStrings map[int][]byte + + // ExtStringsNames is the map of extended string capabilities to their index. + ExtStringNames map[int][]byte +} + +// Decode decodes the terminfo data contained in buf. +func Decode(buf []byte) (*Terminfo, error) { + var err error + + // check max file length + if len(buf) >= maxFileLength { + return nil, ErrInvalidFileSize + } + + d := &decoder{ + buf: buf, + len: len(buf), + } + + // read header + h, err := d.readInts(6, 16) + if err != nil { + return nil, err + } + + var numWidth int + + // check magic + if h[fieldMagic] == magic { + numWidth = 16 + } else if h[fieldMagic] == magicExtended { + numWidth = 32 + } else { + return nil, ErrInvalidMagic + } + + // check header + if hasInvalidCaps(h) { + return nil, ErrInvalidHeader + } + + // check remaining length + if d.len-d.pos < capLength(h) { + return nil, ErrUnexpectedFileEnd + } + + // read names + names, err := d.readBytes(h[fieldNameSize]) + if err != nil { + return nil, err + } + + // check name is terminated properly + i := findNull(names, 0) + if i == -1 { + return nil, ErrInvalidNames + } + names = names[:i] + + // read bool caps + bools, boolsM, err := d.readBools(h[fieldBoolCount]) + if err != nil { + return nil, err + } + + // read num caps + nums, numsM, err := d.readNums(h[fieldNumCount], numWidth) + if err != nil { + return nil, err + } + + // read string caps + strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize]) + if err != nil { + return nil, err + } + + ti := &Terminfo{ + Names: strings.Split(string(names), "|"), + Bools: bools, + BoolsM: boolsM, + Nums: nums, + NumsM: numsM, + Strings: strs, + StringsM: strsM, + } + + // at the end of file, so no extended caps + if d.pos >= d.len { + return ti, nil + } + + // decode extended header + eh, err := d.readInts(5, 16) + if err != nil { + return nil, err + } + + // check extended offset field + if hasInvalidExtOffset(eh) { + return nil, ErrInvalidExtendedHeader + } + + // check extended cap lengths + if d.len-d.pos != extCapLength(eh, numWidth) { + return nil, ErrInvalidExtendedHeader + } + + // read extended bool caps + ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount]) + if err != nil { + return nil, err + } + + // read extended num caps + ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth) + if err != nil { + return nil, err + } + + // read extended string data table indexes + extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16) + if err != nil { + return nil, err + } + + // read string data table + extData, err := d.readBytes(eh[fieldExtTableSize]) + if err != nil { + return nil, err + } + + // precautionary check that exactly at end of file + if d.pos != d.len { + return nil, ErrUnexpectedFileEnd + } + + var last int + // read extended string caps + ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) + if err != nil { + return nil, err + } + extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:] + + // read extended bool names + ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount]) + if err != nil { + return nil, err + } + extIndexes = extIndexes[eh[fieldExtBoolCount]:] + + // read extended num names + ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount]) + if err != nil { + return nil, err + } + extIndexes = extIndexes[eh[fieldExtNumCount]:] + + // read extended string names + ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) + if err != nil { + return nil, err + } + //extIndexes = extIndexes[eh[fieldExtStringCount]:] + + return ti, nil +} + +// Open reads the terminfo file name from the specified directory dir. +func Open(dir, name string) (*Terminfo, error) { + var err error + var buf []byte + var filename string + for _, f := range []string{ + path.Join(dir, name[0:1], name), + path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name), + } { + buf, err = ioutil.ReadFile(f) + if err == nil { + filename = f + break + } + } + if buf == nil { + return nil, ErrFileNotFound + } + + // decode + ti, err := Decode(buf) + if err != nil { + return nil, err + } + + // save original file name + ti.File = filename + + // add to cache + termCache.Lock() + for _, n := range ti.Names { + termCache.db[n] = ti + } + termCache.Unlock() + + return ti, nil +} + +// boolCaps returns all bool and extended capabilities using f to format the +// index key. +func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool { + m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools)) + if !extended { + for k, v := range ti.Bools { + m[f(k)] = v + } + } else { + for k, v := range ti.ExtBools { + m[string(ti.ExtBoolNames[k])] = v + } + } + return m +} + +// BoolCaps returns all bool capabilities. +func (ti *Terminfo) BoolCaps() map[string]bool { + return ti.boolCaps(BoolCapName, false) +} + +// BoolCapsShort returns all bool capabilities, using the short name as the +// index. +func (ti *Terminfo) BoolCapsShort() map[string]bool { + return ti.boolCaps(BoolCapNameShort, false) +} + +// ExtBoolCaps returns all extended bool capabilities. +func (ti *Terminfo) ExtBoolCaps() map[string]bool { + return ti.boolCaps(BoolCapName, true) +} + +// ExtBoolCapsShort returns all extended bool capabilities, using the short +// name as the index. +func (ti *Terminfo) ExtBoolCapsShort() map[string]bool { + return ti.boolCaps(BoolCapNameShort, true) +} + +// numCaps returns all num and extended capabilities using f to format the +// index key. +func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int { + m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums)) + if !extended { + for k, v := range ti.Nums { + m[f(k)] = v + } + } else { + for k, v := range ti.ExtNums { + m[string(ti.ExtNumNames[k])] = v + } + } + return m +} + +// NumCaps returns all num capabilities. +func (ti *Terminfo) NumCaps() map[string]int { + return ti.numCaps(NumCapName, false) +} + +// NumCapsShort returns all num capabilities, using the short name as the +// index. +func (ti *Terminfo) NumCapsShort() map[string]int { + return ti.numCaps(NumCapNameShort, false) +} + +// ExtNumCaps returns all extended num capabilities. +func (ti *Terminfo) ExtNumCaps() map[string]int { + return ti.numCaps(NumCapName, true) +} + +// ExtNumCapsShort returns all extended num capabilities, using the short +// name as the index. +func (ti *Terminfo) ExtNumCapsShort() map[string]int { + return ti.numCaps(NumCapNameShort, true) +} + +// stringCaps returns all string and extended capabilities using f to format the +// index key. +func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte { + m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings)) + if !extended { + for k, v := range ti.Strings { + m[f(k)] = v + } + } else { + for k, v := range ti.ExtStrings { + m[string(ti.ExtStringNames[k])] = v + } + } + return m +} + +// StringCaps returns all string capabilities. +func (ti *Terminfo) StringCaps() map[string][]byte { + return ti.stringCaps(StringCapName, false) +} + +// StringCapsShort returns all string capabilities, using the short name as the +// index. +func (ti *Terminfo) StringCapsShort() map[string][]byte { + return ti.stringCaps(StringCapNameShort, false) +} + +// ExtStringCaps returns all extended string capabilities. +func (ti *Terminfo) ExtStringCaps() map[string][]byte { + return ti.stringCaps(StringCapName, true) +} + +// ExtStringCapsShort returns all extended string capabilities, using the short +// name as the index. +func (ti *Terminfo) ExtStringCapsShort() map[string][]byte { + return ti.stringCaps(StringCapNameShort, true) +} + +// Has determines if the bool cap i is present. +func (ti *Terminfo) Has(i int) bool { + return ti.Bools[i] +} + +// Num returns the num cap i, or -1 if not present. +func (ti *Terminfo) Num(i int) int { + n, ok := ti.Nums[i] + if !ok { + return -1 + } + return n +} + +// Printf formats the string cap i, interpolating parameters v. +func (ti *Terminfo) Printf(i int, v ...interface{}) string { + return Printf(ti.Strings[i], v...) +} + +// Fprintf prints the string cap i to writer w, interpolating parameters v. +func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) { + Fprintf(w, ti.Strings[i], v...) +} + +// Color takes a foreground and background color and returns string that sets +// them for this terminal. +func (ti *Terminfo) Colorf(fg, bg int, str string) string { + maxColors := int(ti.Nums[MaxColors]) + + // map bright colors to lower versions if the color table only holds 8. + if maxColors == 8 { + if fg > 7 && fg < 16 { + fg -= 8 + } + if bg > 7 && bg < 16 { + bg -= 8 + } + } + + var s string + if maxColors > fg && fg >= 0 { + s += ti.Printf(SetAForeground, fg) + } + if maxColors > bg && bg >= 0 { + s += ti.Printf(SetABackground, bg) + } + return s + str + ti.Printf(ExitAttributeMode) +} + +// Goto returns a string suitable for addressing the cursor at the given +// row and column. The origin 0, 0 is in the upper left corner of the screen. +func (ti *Terminfo) Goto(row, col int) string { + return Printf(ti.Strings[CursorAddress], row, col) +} + +// Puts emits the string to the writer, but expands inline padding indications +// (of the form $<[delay]> where [delay] is msec) to a suitable number of +// padding characters (usually null bytes) based upon the supplied baud. At +// high baud rates, more padding characters will be inserted. +/*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) { + var err error + for { + start := strings.Index(s, "$<") + if start == -1 { + // most strings don't need padding, which is good news! + return io.WriteString(w, s) + } + + end := strings.Index(s, ">") + if end == -1 { + // unterminated... just emit bytes unadulterated. + return io.WriteString(w, "$<"+s) + } + + var c int + c, err = io.WriteString(w, s[:start]) + if err != nil { + return n + c, err + } + n += c + + s = s[start+2:] + val := s[:end] + s = s[end+1:] + var ms int + var dot, mandatory, asterisk bool + unit := 1000 + for _, ch := range val { + switch { + case ch >= '0' && ch <= '9': + ms = (ms * 10) + int(ch-'0') + if dot { + unit *= 10 + } + case ch == '.' && !dot: + dot = true + case ch == '*' && !asterisk: + ms *= lines + asterisk = true + case ch == '/': + mandatory = true + default: + break + } + } + + z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar] + b := make([]byte, len(pad)*z) + for bp := copy(b, pad); bp < len(b); bp *= 2 { + copy(b[bp:], b[:bp]) + } + + if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory { + c, err = w.Write(b) + if err != nil { + return n + c, err + } + n += c + } + } + + return n, nil +}*/ |