mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-06-23 00:39:13 +02:00
Switch to github.com/gookit/color for terminal colors
This commit is contained in:
9
vendor/github.com/xo/terminfo/.gitignore
generated
vendored
Normal file
9
vendor/github.com/xo/terminfo/.gitignore
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
/.cache/
|
||||
|
||||
/cmd/infocmp/infocmp
|
||||
/cmd/infocmp/.out/
|
||||
|
||||
/infocmp
|
||||
/.out/
|
||||
|
||||
*.txt
|
21
vendor/github.com/xo/terminfo/LICENSE
generated
vendored
Normal file
21
vendor/github.com/xo/terminfo/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Anmol Sethi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
139
vendor/github.com/xo/terminfo/README.md
generated
vendored
Normal file
139
vendor/github.com/xo/terminfo/README.md
generated
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
# About terminfo [![GoDoc][1]][2]
|
||||
|
||||
Package `terminfo` provides a pure-Go implementation of reading information
|
||||
from the terminfo database.
|
||||
|
||||
`terminfo` is meant as a replacement for `ncurses` in simple Go programs.
|
||||
|
||||
## Installing
|
||||
|
||||
Install in the usual Go way:
|
||||
|
||||
```sh
|
||||
$ go get -u github.com/xo/terminfo
|
||||
```
|
||||
|
||||
## Using
|
||||
|
||||
Please see the [GoDoc API listing][2] for more information on using `terminfo`.
|
||||
|
||||
```go
|
||||
// _examples/simple/main.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//r := rand.New(nil)
|
||||
|
||||
// load terminfo
|
||||
ti, err := terminfo.LoadFromEnv()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// cleanup
|
||||
defer func() {
|
||||
err := recover()
|
||||
termreset(ti)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
terminit(ti)
|
||||
termtitle(ti, "simple example!")
|
||||
termputs(ti, 3, 3, "Ctrl-C to exit")
|
||||
maxColors := termcolors(ti)
|
||||
if maxColors > 256 {
|
||||
maxColors = 256
|
||||
}
|
||||
for i := 0; i < maxColors; i++ {
|
||||
termputs(ti, 5+i/16, 5+i%16, ti.Colorf(i, 0, "█"))
|
||||
}
|
||||
|
||||
// wait for signal
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigs
|
||||
}
|
||||
|
||||
// terminit initializes the special CA mode on the terminal, and makes the
|
||||
// cursor invisible.
|
||||
func terminit(ti *terminfo.Terminfo) {
|
||||
buf := new(bytes.Buffer)
|
||||
// set the cursor invisible
|
||||
ti.Fprintf(buf, terminfo.CursorInvisible)
|
||||
// enter special mode
|
||||
ti.Fprintf(buf, terminfo.EnterCaMode)
|
||||
// clear the screen
|
||||
ti.Fprintf(buf, terminfo.ClearScreen)
|
||||
os.Stdout.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// termreset is the inverse of terminit.
|
||||
func termreset(ti *terminfo.Terminfo) {
|
||||
buf := new(bytes.Buffer)
|
||||
ti.Fprintf(buf, terminfo.ExitCaMode)
|
||||
ti.Fprintf(buf, terminfo.CursorNormal)
|
||||
os.Stdout.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// termputs puts a string at row, col, interpolating v.
|
||||
func termputs(ti *terminfo.Terminfo, row, col int, s string, v ...interface{}) {
|
||||
buf := new(bytes.Buffer)
|
||||
ti.Fprintf(buf, terminfo.CursorAddress, row, col)
|
||||
fmt.Fprintf(buf, s, v...)
|
||||
os.Stdout.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// sl is the status line terminfo.
|
||||
var sl *terminfo.Terminfo
|
||||
|
||||
// termtitle sets the window title.
|
||||
func termtitle(ti *terminfo.Terminfo, s string) {
|
||||
var once sync.Once
|
||||
once.Do(func() {
|
||||
if ti.Has(terminfo.HasStatusLine) {
|
||||
return
|
||||
}
|
||||
// load the sl xterm if terminal is an xterm or has COLORTERM
|
||||
if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") || os.Getenv("COLORTERM") == "truecolor" {
|
||||
sl, _ = terminfo.Load("xterm+sl")
|
||||
}
|
||||
})
|
||||
if sl != nil {
|
||||
ti = sl
|
||||
}
|
||||
if !ti.Has(terminfo.HasStatusLine) {
|
||||
return
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
ti.Fprintf(buf, terminfo.ToStatusLine)
|
||||
fmt.Fprint(buf, s)
|
||||
ti.Fprintf(buf, terminfo.FromStatusLine)
|
||||
os.Stdout.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// termcolors returns the maximum colors available for the terminal.
|
||||
func termcolors(ti *terminfo.Terminfo) int {
|
||||
if colors := ti.Num(terminfo.MaxColors); colors > 0 {
|
||||
return colors
|
||||
}
|
||||
return int(terminfo.ColorLevelBasic)
|
||||
}
|
||||
```
|
||||
|
||||
[1]: https://godoc.org/github.com/xo/terminfo?status.svg
|
||||
[2]: https://godoc.org/github.com/xo/terminfo
|
33
vendor/github.com/xo/terminfo/caps.go
generated
vendored
Normal file
33
vendor/github.com/xo/terminfo/caps.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package terminfo
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// BoolCapName returns the bool capability name.
|
||||
func BoolCapName(i int) string {
|
||||
return boolCapNames[2*i]
|
||||
}
|
||||
|
||||
// BoolCapNameShort returns the short bool capability name.
|
||||
func BoolCapNameShort(i int) string {
|
||||
return boolCapNames[2*i+1]
|
||||
}
|
||||
|
||||
// NumCapName returns the num capability name.
|
||||
func NumCapName(i int) string {
|
||||
return numCapNames[2*i]
|
||||
}
|
||||
|
||||
// NumCapNameShort returns the short num capability name.
|
||||
func NumCapNameShort(i int) string {
|
||||
return numCapNames[2*i+1]
|
||||
}
|
||||
|
||||
// StringCapName returns the string capability name.
|
||||
func StringCapName(i int) string {
|
||||
return stringCapNames[2*i]
|
||||
}
|
||||
|
||||
// StringCapNameShort returns the short string capability name.
|
||||
func StringCapNameShort(i int) string {
|
||||
return stringCapNames[2*i+1]
|
||||
}
|
2023
vendor/github.com/xo/terminfo/capvals.go
generated
vendored
Normal file
2023
vendor/github.com/xo/terminfo/capvals.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
91
vendor/github.com/xo/terminfo/color.go
generated
vendored
Normal file
91
vendor/github.com/xo/terminfo/color.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
package terminfo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ColorLevel is the color level supported by a terminal.
|
||||
type ColorLevel uint
|
||||
|
||||
// ColorLevel values.
|
||||
const (
|
||||
ColorLevelNone ColorLevel = iota
|
||||
ColorLevelBasic
|
||||
ColorLevelHundreds
|
||||
ColorLevelMillions
|
||||
)
|
||||
|
||||
// String satisfies the Stringer interface.
|
||||
func (c ColorLevel) String() string {
|
||||
switch c {
|
||||
case ColorLevelBasic:
|
||||
return "basic"
|
||||
case ColorLevelHundreds:
|
||||
return "hundreds"
|
||||
case ColorLevelMillions:
|
||||
return "millions"
|
||||
}
|
||||
return "none"
|
||||
}
|
||||
|
||||
// ChromaFormatterName returns the github.com/alecthomas/chroma compatible
|
||||
// formatter name for the color level.
|
||||
func (c ColorLevel) ChromaFormatterName() string {
|
||||
switch c {
|
||||
case ColorLevelBasic:
|
||||
return "terminal"
|
||||
case ColorLevelHundreds:
|
||||
return "terminal256"
|
||||
case ColorLevelMillions:
|
||||
return "terminal16m"
|
||||
}
|
||||
return "noop"
|
||||
}
|
||||
|
||||
// ColorLevelFromEnv returns the color level COLORTERM, FORCE_COLOR,
|
||||
// TERM_PROGRAM, or determined from the TERM environment variable.
|
||||
func ColorLevelFromEnv() (ColorLevel, error) {
|
||||
// check for overriding environment variables
|
||||
colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR")
|
||||
switch {
|
||||
case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit") || termProg == "Hyper":
|
||||
return ColorLevelMillions, nil
|
||||
case colorTerm != "" || forceColor != "":
|
||||
return ColorLevelBasic, nil
|
||||
case termProg == "Apple_Terminal":
|
||||
return ColorLevelHundreds, nil
|
||||
case termProg == "iTerm.app":
|
||||
ver := os.Getenv("TERM_PROGRAM_VERSION")
|
||||
if ver == "" {
|
||||
return ColorLevelHundreds, nil
|
||||
}
|
||||
i, err := strconv.Atoi(strings.Split(ver, ".")[0])
|
||||
if err != nil {
|
||||
return ColorLevelNone, ErrInvalidTermProgramVersion
|
||||
}
|
||||
if i == 3 {
|
||||
return ColorLevelMillions, nil
|
||||
}
|
||||
return ColorLevelHundreds, nil
|
||||
}
|
||||
|
||||
// otherwise determine from TERM's max_colors capability
|
||||
if term := os.Getenv("TERM"); term != "" {
|
||||
ti, err := Load(term)
|
||||
if err != nil {
|
||||
return ColorLevelNone, err
|
||||
}
|
||||
|
||||
v, ok := ti.Nums[MaxColors]
|
||||
switch {
|
||||
case !ok || v <= 16:
|
||||
return ColorLevelNone, nil
|
||||
case ok && v >= 256:
|
||||
return ColorLevelHundreds, nil
|
||||
}
|
||||
}
|
||||
|
||||
return ColorLevelBasic, nil
|
||||
}
|
3
vendor/github.com/xo/terminfo/go.mod
generated
vendored
Normal file
3
vendor/github.com/xo/terminfo/go.mod
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
module github.com/xo/terminfo
|
||||
|
||||
go 1.15
|
0
vendor/github.com/xo/terminfo/go.sum
generated
vendored
Normal file
0
vendor/github.com/xo/terminfo/go.sum
generated
vendored
Normal file
72
vendor/github.com/xo/terminfo/load.go
generated
vendored
Normal file
72
vendor/github.com/xo/terminfo/load.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
package terminfo
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// termCache is the terminfo cache.
|
||||
var termCache = struct {
|
||||
db map[string]*Terminfo
|
||||
sync.RWMutex
|
||||
}{
|
||||
db: make(map[string]*Terminfo),
|
||||
}
|
||||
|
||||
// Load follows the behavior described in terminfo(5) to find correct the
|
||||
// terminfo file using the name, reads the file and then returns a Terminfo
|
||||
// struct that describes the file.
|
||||
func Load(name string) (*Terminfo, error) {
|
||||
if name == "" {
|
||||
return nil, ErrEmptyTermName
|
||||
}
|
||||
|
||||
termCache.RLock()
|
||||
ti, ok := termCache.db[name]
|
||||
termCache.RUnlock()
|
||||
|
||||
if ok {
|
||||
return ti, nil
|
||||
}
|
||||
|
||||
var checkDirs []string
|
||||
|
||||
// check $TERMINFO
|
||||
if dir := os.Getenv("TERMINFO"); dir != "" {
|
||||
checkDirs = append(checkDirs, dir)
|
||||
}
|
||||
|
||||
// check $HOME/.terminfo
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo"))
|
||||
|
||||
// check $TERMINFO_DIRS
|
||||
if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" {
|
||||
checkDirs = append(checkDirs, strings.Split(dirs, ":")...)
|
||||
}
|
||||
|
||||
// check fallback directories
|
||||
checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo")
|
||||
for _, dir := range checkDirs {
|
||||
ti, err = Open(dir, name)
|
||||
if err != nil && err != ErrFileNotFound && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
} else if err == nil {
|
||||
return ti, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrDatabaseDirectoryNotFound
|
||||
}
|
||||
|
||||
// LoadFromEnv loads the terminal info based on the name contained in
|
||||
// environment variable TERM.
|
||||
func LoadFromEnv() (*Terminfo, error) {
|
||||
return Load(os.Getenv("TERM"))
|
||||
}
|
490
vendor/github.com/xo/terminfo/param.go
generated
vendored
Normal file
490
vendor/github.com/xo/terminfo/param.go
generated
vendored
Normal file
@ -0,0 +1,490 @@
|
||||
package terminfo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// parametizer represents the a scan state for a parameterized string.
|
||||
type parametizer struct {
|
||||
// z is the string to parameterize
|
||||
z []byte
|
||||
|
||||
// pos is the current position in s.
|
||||
pos int
|
||||
|
||||
// nest is the current nest level.
|
||||
nest int
|
||||
|
||||
// s is the variable stack.
|
||||
s stack
|
||||
|
||||
// skipElse keeps the state of skipping else.
|
||||
skipElse bool
|
||||
|
||||
// buf is the result buffer.
|
||||
buf *bytes.Buffer
|
||||
|
||||
// params are the parameters to interpolate.
|
||||
params [9]interface{}
|
||||
|
||||
// vars are dynamic variables.
|
||||
vars [26]interface{}
|
||||
}
|
||||
|
||||
// staticVars are the static, global variables.
|
||||
var staticVars = struct {
|
||||
vars [26]interface{}
|
||||
sync.Mutex
|
||||
}{}
|
||||
|
||||
var parametizerPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
p := new(parametizer)
|
||||
p.buf = bytes.NewBuffer(make([]byte, 0, 45))
|
||||
return p
|
||||
},
|
||||
}
|
||||
|
||||
// newParametizer returns a new initialized parametizer from the pool.
|
||||
func newParametizer(z []byte) *parametizer {
|
||||
p := parametizerPool.Get().(*parametizer)
|
||||
p.z = z
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// reset resets the parametizer.
|
||||
func (p *parametizer) reset() {
|
||||
p.pos, p.nest = 0, 0
|
||||
|
||||
p.s.reset()
|
||||
p.buf.Reset()
|
||||
|
||||
p.params, p.vars = [9]interface{}{}, [26]interface{}{}
|
||||
|
||||
parametizerPool.Put(p)
|
||||
}
|
||||
|
||||
// stateFn represents the state of the scanner as a function that returns the
|
||||
// next state.
|
||||
type stateFn func() stateFn
|
||||
|
||||
// exec executes the parameterizer, interpolating the supplied parameters.
|
||||
func (p *parametizer) exec() string {
|
||||
for state := p.scanTextFn; state != nil; {
|
||||
state = state()
|
||||
}
|
||||
return p.buf.String()
|
||||
}
|
||||
|
||||
// peek returns the next byte.
|
||||
func (p *parametizer) peek() (byte, error) {
|
||||
if p.pos >= len(p.z) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return p.z[p.pos], nil
|
||||
}
|
||||
|
||||
// writeFrom writes the characters from ppos to pos to the buffer.
|
||||
func (p *parametizer) writeFrom(ppos int) {
|
||||
if p.pos > ppos {
|
||||
// append remaining characters.
|
||||
p.buf.Write(p.z[ppos:p.pos])
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parametizer) scanTextFn() stateFn {
|
||||
ppos := p.pos
|
||||
for {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
p.writeFrom(ppos)
|
||||
return nil
|
||||
}
|
||||
|
||||
if ch == '%' {
|
||||
p.writeFrom(ppos)
|
||||
p.pos++
|
||||
return p.scanCodeFn
|
||||
}
|
||||
|
||||
p.pos++
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parametizer) scanCodeFn() stateFn {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case '%':
|
||||
p.buf.WriteByte('%')
|
||||
|
||||
case ':':
|
||||
// this character is used to avoid interpreting "%-" and "%+" as operators.
|
||||
// the next character is where the format really begins.
|
||||
p.pos++
|
||||
_, err = p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return p.scanFormatFn
|
||||
|
||||
case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.':
|
||||
return p.scanFormatFn
|
||||
|
||||
case 'o':
|
||||
p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8))
|
||||
|
||||
case 'd':
|
||||
p.buf.WriteString(strconv.Itoa(p.s.popInt()))
|
||||
|
||||
case 'x':
|
||||
p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16))
|
||||
|
||||
case 'X':
|
||||
p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16)))
|
||||
|
||||
case 's':
|
||||
p.buf.WriteString(p.s.popString())
|
||||
|
||||
case 'c':
|
||||
p.buf.WriteByte(p.s.popByte())
|
||||
|
||||
case 'p':
|
||||
p.pos++
|
||||
return p.pushParamFn
|
||||
|
||||
case 'P':
|
||||
p.pos++
|
||||
return p.setDsVarFn
|
||||
|
||||
case 'g':
|
||||
p.pos++
|
||||
return p.getDsVarFn
|
||||
|
||||
case '\'':
|
||||
p.pos++
|
||||
ch, err = p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.s.push(ch)
|
||||
|
||||
// skip the '\''
|
||||
p.pos++
|
||||
|
||||
case '{':
|
||||
p.pos++
|
||||
return p.pushIntfn
|
||||
|
||||
case 'l':
|
||||
p.s.push(len(p.s.popString()))
|
||||
|
||||
case '+':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai + bi)
|
||||
|
||||
case '-':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai - bi)
|
||||
|
||||
case '*':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai * bi)
|
||||
|
||||
case '/':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
if bi != 0 {
|
||||
p.s.push(ai / bi)
|
||||
} else {
|
||||
p.s.push(0)
|
||||
}
|
||||
|
||||
case 'm':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
if bi != 0 {
|
||||
p.s.push(ai % bi)
|
||||
} else {
|
||||
p.s.push(0)
|
||||
}
|
||||
|
||||
case '&':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai & bi)
|
||||
|
||||
case '|':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai | bi)
|
||||
|
||||
case '^':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai ^ bi)
|
||||
|
||||
case '=':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai == bi)
|
||||
|
||||
case '>':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai > bi)
|
||||
|
||||
case '<':
|
||||
bi, ai := p.s.popInt(), p.s.popInt()
|
||||
p.s.push(ai < bi)
|
||||
|
||||
case 'A':
|
||||
bi, ai := p.s.popBool(), p.s.popBool()
|
||||
p.s.push(ai && bi)
|
||||
|
||||
case 'O':
|
||||
bi, ai := p.s.popBool(), p.s.popBool()
|
||||
p.s.push(ai || bi)
|
||||
|
||||
case '!':
|
||||
p.s.push(!p.s.popBool())
|
||||
|
||||
case '~':
|
||||
p.s.push(^p.s.popInt())
|
||||
|
||||
case 'i':
|
||||
for i := range p.params[:2] {
|
||||
if n, ok := p.params[i].(int); ok {
|
||||
p.params[i] = n + 1
|
||||
}
|
||||
}
|
||||
|
||||
case '?', ';':
|
||||
|
||||
case 't':
|
||||
return p.scanThenFn
|
||||
|
||||
case 'e':
|
||||
p.skipElse = true
|
||||
return p.skipTextFn
|
||||
}
|
||||
|
||||
p.pos++
|
||||
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) scanFormatFn() stateFn {
|
||||
// the character was already read, so no need to check the error.
|
||||
ch, _ := p.peek()
|
||||
|
||||
// 6 should be the maximum length of a format string, for example "%:-9.9d".
|
||||
f := []byte{'%', ch, 0, 0, 0, 0}
|
||||
|
||||
var err error
|
||||
|
||||
for {
|
||||
p.pos++
|
||||
ch, err = p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
f = append(f, ch)
|
||||
switch ch {
|
||||
case 'o', 'd', 'x', 'X':
|
||||
fmt.Fprintf(p.buf, string(f), p.s.popInt())
|
||||
break
|
||||
|
||||
case 's':
|
||||
fmt.Fprintf(p.buf, string(f), p.s.popString())
|
||||
break
|
||||
|
||||
case 'c':
|
||||
fmt.Fprintf(p.buf, string(f), p.s.popByte())
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
p.pos++
|
||||
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) pushParamFn() stateFn {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) {
|
||||
p.s.push(p.params[ai])
|
||||
} else {
|
||||
p.s.push(0)
|
||||
}
|
||||
|
||||
// skip the '}'
|
||||
p.pos++
|
||||
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) setDsVarFn() stateFn {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if ch >= 'A' && ch <= 'Z' {
|
||||
staticVars.Lock()
|
||||
staticVars.vars[int(ch-'A')] = p.s.pop()
|
||||
staticVars.Unlock()
|
||||
} else if ch >= 'a' && ch <= 'z' {
|
||||
p.vars[int(ch-'a')] = p.s.pop()
|
||||
}
|
||||
|
||||
p.pos++
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) getDsVarFn() stateFn {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var a byte
|
||||
if ch >= 'A' && ch <= 'Z' {
|
||||
a = 'A'
|
||||
} else if ch >= 'a' && ch <= 'z' {
|
||||
a = 'a'
|
||||
}
|
||||
|
||||
staticVars.Lock()
|
||||
p.s.push(staticVars.vars[int(ch-a)])
|
||||
staticVars.Unlock()
|
||||
|
||||
p.pos++
|
||||
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) pushIntfn() stateFn {
|
||||
var ai int
|
||||
for {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.pos++
|
||||
if ch < '0' || ch > '9' {
|
||||
p.s.push(ai)
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
ai = (ai * 10) + int(ch-'0')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *parametizer) scanThenFn() stateFn {
|
||||
p.pos++
|
||||
|
||||
if p.s.popBool() {
|
||||
return p.scanTextFn
|
||||
}
|
||||
|
||||
p.skipElse = false
|
||||
|
||||
return p.skipTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) skipTextFn() stateFn {
|
||||
for {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.pos++
|
||||
if ch == '%' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if p.skipElse {
|
||||
return p.skipElseFn
|
||||
}
|
||||
|
||||
return p.skipThenFn
|
||||
}
|
||||
|
||||
func (p *parametizer) skipThenFn() stateFn {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.pos++
|
||||
switch ch {
|
||||
case ';':
|
||||
if p.nest == 0 {
|
||||
return p.scanTextFn
|
||||
}
|
||||
p.nest--
|
||||
|
||||
case '?':
|
||||
p.nest++
|
||||
|
||||
case 'e':
|
||||
if p.nest == 0 {
|
||||
return p.scanTextFn
|
||||
}
|
||||
}
|
||||
|
||||
return p.skipTextFn
|
||||
}
|
||||
|
||||
func (p *parametizer) skipElseFn() stateFn {
|
||||
ch, err := p.peek()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.pos++
|
||||
switch ch {
|
||||
case ';':
|
||||
if p.nest == 0 {
|
||||
return p.scanTextFn
|
||||
}
|
||||
p.nest--
|
||||
|
||||
case '?':
|
||||
p.nest++
|
||||
}
|
||||
|
||||
return p.skipTextFn
|
||||
}
|
||||
|
||||
// Printf evaluates a parameterized terminfo value z, interpolating params.
|
||||
func Printf(z []byte, params ...interface{}) string {
|
||||
p := newParametizer(z)
|
||||
defer p.reset()
|
||||
|
||||
// make sure we always have 9 parameters -- makes it easier
|
||||
// later to skip checks and its faster
|
||||
for i := 0; i < len(p.params) && i < len(params); i++ {
|
||||
p.params[i] = params[i]
|
||||
}
|
||||
|
||||
return p.exec()
|
||||
}
|
||||
|
||||
// Fprintf evaluates a parameterized terminfo value z, interpolating params and
|
||||
// writing to w.
|
||||
func Fprintf(w io.Writer, z []byte, params ...interface{}) {
|
||||
w.Write([]byte(Printf(z, params...)))
|
||||
}
|
48
vendor/github.com/xo/terminfo/stack.go
generated
vendored
Normal file
48
vendor/github.com/xo/terminfo/stack.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
package terminfo
|
||||
|
||||
type stack []interface{}
|
||||
|
||||
func (s *stack) push(v interface{}) {
|
||||
*s = append(*s, v)
|
||||
}
|
||||
|
||||
func (s *stack) pop() interface{} {
|
||||
if len(*s) == 0 {
|
||||
return nil
|
||||
}
|
||||
v := (*s)[len(*s)-1]
|
||||
*s = (*s)[:len(*s)-1]
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *stack) popInt() int {
|
||||
if i, ok := s.pop().(int); ok {
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *stack) popBool() bool {
|
||||
if b, ok := s.pop().(bool); ok {
|
||||
return b
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *stack) popByte() byte {
|
||||
if b, ok := s.pop().(byte); ok {
|
||||
return b
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *stack) popString() string {
|
||||
if a, ok := s.pop().(string); ok {
|
||||
return a
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (s *stack) reset() {
|
||||
*s = (*s)[:0]
|
||||
}
|
538
vendor/github.com/xo/terminfo/terminfo.go
generated
vendored
Normal file
538
vendor/github.com/xo/terminfo/terminfo.go
generated
vendored
Normal file
@ -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
|
||||
}*/
|
266
vendor/github.com/xo/terminfo/util.go
generated
vendored
Normal file
266
vendor/github.com/xo/terminfo/util.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
package terminfo
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
const (
|
||||
// maxFileLength is the max file length.
|
||||
maxFileLength = 4096
|
||||
|
||||
// magic is the file magic for terminfo files.
|
||||
magic = 0432
|
||||
|
||||
// magicExtended is the file magic for terminfo files with the extended number format.
|
||||
magicExtended = 01036
|
||||
)
|
||||
|
||||
// header fields.
|
||||
const (
|
||||
fieldMagic = iota
|
||||
fieldNameSize
|
||||
fieldBoolCount
|
||||
fieldNumCount
|
||||
fieldStringCount
|
||||
fieldTableSize
|
||||
)
|
||||
|
||||
// header extended fields.
|
||||
const (
|
||||
fieldExtBoolCount = iota
|
||||
fieldExtNumCount
|
||||
fieldExtStringCount
|
||||
fieldExtOffsetCount
|
||||
fieldExtTableSize
|
||||
)
|
||||
|
||||
// hasInvalidCaps determines if the capabilities in h are invalid.
|
||||
func hasInvalidCaps(h []int) bool {
|
||||
return h[fieldBoolCount] > CapCountBool ||
|
||||
h[fieldNumCount] > CapCountNum ||
|
||||
h[fieldStringCount] > CapCountString
|
||||
}
|
||||
|
||||
// capLength returns the total length of the capabilities in bytes.
|
||||
func capLength(h []int) int {
|
||||
return h[fieldNameSize] +
|
||||
h[fieldBoolCount] +
|
||||
(h[fieldNameSize]+h[fieldBoolCount])%2 + // account for word align
|
||||
h[fieldNumCount]*2 +
|
||||
h[fieldStringCount]*2 +
|
||||
h[fieldTableSize]
|
||||
}
|
||||
|
||||
// hasInvalidExtOffset determines if the extended offset field is valid.
|
||||
func hasInvalidExtOffset(h []int) bool {
|
||||
return h[fieldExtBoolCount]+
|
||||
h[fieldExtNumCount]+
|
||||
h[fieldExtStringCount]*2 != h[fieldExtOffsetCount]
|
||||
}
|
||||
|
||||
// extCapLength returns the total length of extended capabilities in bytes.
|
||||
func extCapLength(h []int, numWidth int) int {
|
||||
return h[fieldExtBoolCount] +
|
||||
h[fieldExtBoolCount]%2 + // account for word align
|
||||
h[fieldExtNumCount]*(numWidth/8) +
|
||||
h[fieldExtOffsetCount]*2 +
|
||||
h[fieldExtTableSize]
|
||||
}
|
||||
|
||||
// findNull finds the position of null in buf.
|
||||
func findNull(buf []byte, i int) int {
|
||||
for ; i < len(buf); i++ {
|
||||
if buf[i] == 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// readStrings decodes n strings from string data table buf using the indexes in idx.
|
||||
func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) {
|
||||
var last int
|
||||
m := make(map[int][]byte)
|
||||
for i := 0; i < n; i++ {
|
||||
start := idx[i]
|
||||
if start < 0 {
|
||||
continue
|
||||
}
|
||||
if end := findNull(buf, start); end != -1 {
|
||||
m[i], last = buf[start:end], end+1
|
||||
} else {
|
||||
return nil, 0, ErrInvalidStringTable
|
||||
}
|
||||
}
|
||||
return m, last, nil
|
||||
}
|
||||
|
||||
// decoder holds state info while decoding a terminfo file.
|
||||
type decoder struct {
|
||||
buf []byte
|
||||
pos int
|
||||
len int
|
||||
}
|
||||
|
||||
// readBytes reads the next n bytes of buf, incrementing pos by n.
|
||||
func (d *decoder) readBytes(n int) ([]byte, error) {
|
||||
if d.len < d.pos+n {
|
||||
return nil, ErrUnexpectedFileEnd
|
||||
}
|
||||
n, d.pos = d.pos, d.pos+n
|
||||
return d.buf[n:d.pos], nil
|
||||
}
|
||||
|
||||
// readInts reads n number of ints with width w.
|
||||
func (d *decoder) readInts(n, w int) ([]int, error) {
|
||||
w /= 8
|
||||
l := n * w
|
||||
|
||||
buf, err := d.readBytes(l)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// align
|
||||
d.pos += d.pos % 2
|
||||
|
||||
z := make([]int, n)
|
||||
for i, j := 0, 0; i < l; i, j = i+w, j+1 {
|
||||
switch w {
|
||||
case 1:
|
||||
z[i] = int(buf[i])
|
||||
case 2:
|
||||
z[j] = int(int16(buf[i+1])<<8 | int16(buf[i]))
|
||||
case 4:
|
||||
z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i])
|
||||
}
|
||||
}
|
||||
|
||||
return z, nil
|
||||
}
|
||||
|
||||
// readBools reads the next n bools.
|
||||
func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) {
|
||||
buf, err := d.readInts(n, 8)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// process
|
||||
bools, boolsM := make(map[int]bool), make(map[int]bool)
|
||||
for i, b := range buf {
|
||||
bools[i] = b == 1
|
||||
if int8(b) == -2 {
|
||||
boolsM[i] = true
|
||||
}
|
||||
}
|
||||
|
||||
return bools, boolsM, nil
|
||||
}
|
||||
|
||||
// readNums reads the next n nums.
|
||||
func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) {
|
||||
buf, err := d.readInts(n, w)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// process
|
||||
nums, numsM := make(map[int]int), make(map[int]bool)
|
||||
for i := 0; i < n; i++ {
|
||||
nums[i] = buf[i]
|
||||
if buf[i] == -2 {
|
||||
numsM[i] = true
|
||||
}
|
||||
}
|
||||
|
||||
return nums, numsM, nil
|
||||
}
|
||||
|
||||
// readStringTable reads the string data for n strings and the accompanying data
|
||||
// table of length sz.
|
||||
func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) {
|
||||
buf, err := d.readInts(n, 16)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// read string data table
|
||||
data, err := d.readBytes(sz)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// align
|
||||
d.pos += d.pos % 2
|
||||
|
||||
// process
|
||||
s := make([][]byte, n)
|
||||
var m []int
|
||||
for i := 0; i < n; i++ {
|
||||
start := buf[i]
|
||||
if start == -2 {
|
||||
m = append(m, i)
|
||||
} else if start >= 0 {
|
||||
if end := findNull(data, start); end != -1 {
|
||||
s[i] = data[start:end]
|
||||
} else {
|
||||
return nil, nil, ErrInvalidStringTable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s, m, nil
|
||||
}
|
||||
|
||||
// readStrings reads the next n strings and processes the string data table of
|
||||
// length sz.
|
||||
func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) {
|
||||
s, m, err := d.readStringTable(n, sz)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
strs := make(map[int][]byte)
|
||||
for k, v := range s {
|
||||
if k == AcsChars {
|
||||
v = canonicalizeAscChars(v)
|
||||
}
|
||||
strs[k] = v
|
||||
}
|
||||
|
||||
strsM := make(map[int]bool, len(m))
|
||||
for _, k := range m {
|
||||
strsM[k] = true
|
||||
}
|
||||
|
||||
return strs, strsM, nil
|
||||
}
|
||||
|
||||
// canonicalizeAscChars reorders chars to be unique, in order.
|
||||
//
|
||||
// see repair_ascc in ncurses-6.0/progs/dump_entry.c
|
||||
func canonicalizeAscChars(z []byte) []byte {
|
||||
var c chars
|
||||
enc := make(map[byte]byte, len(z)/2)
|
||||
for i := 0; i < len(z); i += 2 {
|
||||
if _, ok := enc[z[i]]; !ok {
|
||||
a, b := z[i], z[i+1]
|
||||
//log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b)
|
||||
c, enc[a] = append(c, b), b
|
||||
}
|
||||
}
|
||||
sort.Sort(c)
|
||||
|
||||
r := make([]byte, 2*len(c))
|
||||
for i := 0; i < len(c); i++ {
|
||||
r[i*2], r[i*2+1] = c[i], enc[c[i]]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type chars []byte
|
||||
|
||||
func (c chars) Len() int { return len(c) }
|
||||
func (c chars) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||
func (c chars) Less(i, j int) bool { return c[i] < c[j] }
|
Reference in New Issue
Block a user