// Copyright 2021 The TCell Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use file except in compliance with the License.// You may obtain a copy of the license at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.// The dynamic package is used to generate a terminal description dynamically,// using infocmp. This is really a method of last resort, as the performance// will be slow, and it requires a working infocmp. But, the hope is that it// will assist folks who have to deal with a terminal description that isn't// already built in. This requires infocmp to be in the user's path, and to// support reasonably the -1 option.package dynamicimport ()type termcap struct { name string desc string aliases []string bools map[string]bool nums map[string]int strs map[string]string}func ( *termcap) ( string) int {return (.nums[])}func ( *termcap) ( string) bool {return (.bools[])}func ( *termcap) ( string) string {return (.strs[])}const ( none = iota control escaped)var errNotAddressable = errors.New("terminal not cursor addressable")func unescape( string) string {// Various escapes are in \x format. Control codes are // encoded as ^M (carat followed by ASCII equivalent). // escapes are: \e, \E - escape // \0 NULL, \n \l \r \t \b \f \s for equivalent C escape. := &bytes.Buffer{} := nonefor := 0; < len(); ++ { := []switch {casenone:switch {case'\\': = escapedcase'^': = controldefault: .WriteByte() }casecontrol: .WriteByte( ^ 1<<6) = nonecaseescaped:switch {case'E', 'e': .WriteByte(0x1b)case'0', '1', '2', '3', '4', '5', '6', '7':if +2 < len() && [+1] >= '0' && [+1] <= '7' && [+2] >= '0' && [+2] <= '7' { .WriteByte((( - '0') * 64) + (([+1] - '0') * 8) + ([+2] - '0')) = + 2 } elseif == '0' { .WriteByte(0) }case'n': .WriteByte('\n')case'r': .WriteByte('\r')case't': .WriteByte('\t')case'b': .WriteByte('\b')case'f': .WriteByte('\f')case's': .WriteByte(' ')default: .WriteByte() } = none } }return (.String())}func ( *termcap) ( string) error { := exec.Command("infocmp", "-1", ) := &bytes.Buffer{} .Stdout = .strs = make(map[string]string) .bools = make(map[string]bool) .nums = make(map[string]int)if := .Run(); != nil {returnfmt.Errorf("couldn't open terminfo ($TERM) file for %s: %w", , ) }// Now parse the output. // We get comment lines (starting with "#"), followed by // a header line that looks like "<name>|<alias>|...|<desc>" // then capabilities, one per line, starting with a tab and ending // with a comma and newline. := strings.Split(.String(), "\n")forlen() > 0 && strings.HasPrefix([0], "#") { = [1:] }// Ditch trailing empty last lineif [len()-1] == "" { = [:len()-1] } := [0] = strings.TrimSuffix(, ",") := strings.Split(, "|") .name = [0] = [1:]iflen() > 0 { .desc = [len()-1] = [:len()-1] } .aliases = for , := range [1:] {if (!strings.HasPrefix(, "\t")) || (!strings.HasSuffix(, ",")) {return (errors.New("malformed infocmp: " + )) } = [1:] = [:len()-1]if := strings.SplitN(, "=", 2); len() == 2 { .strs[[0]] = unescape([1]) } elseif := strings.SplitN(, "#", 2); len() == 2 { , := strconv.ParseUint([1], 0, 0)if != nil {return () } .nums[[0]] = int() } else { .bools[] = true } }returnnil}// LoadTerminfo creates a Terminfo by for named terminal by attempting to parse// the output from infocmp. This returns the terminfo entry, a description of// the terminal, and either nil or an error.func ( string) (*terminfo.Terminfo, string, error) {vartermcapif := .setupterm(); != nil {returnnil, "", } := &terminfo.Terminfo{} .Name = .name .Aliases = .aliases .Colors = .getnum("colors") .Columns = .getnum("cols") .Lines = .getnum("lines") .Clear = .getstr("clear") .EnterCA = .getstr("smcup") .ExitCA = .getstr("rmcup") .ShowCursor = .getstr("cnorm") .HideCursor = .getstr("civis") .AttrOff = .getstr("sgr0") .Underline = .getstr("smul") .Bold = .getstr("bold") .Blink = .getstr("blink") .Dim = .getstr("dim") .Italic = .getstr("sitm") .Reverse = .getstr("rev") .EnterKeypad = .getstr("smkx") .ExitKeypad = .getstr("rmkx") .SetFg = .getstr("setaf") .SetBg = .getstr("setab") .SetCursor = .getstr("cup") .AltChars = .getstr("acsc") .EnterAcs = .getstr("smacs") .ExitAcs = .getstr("rmacs") .EnableAcs = .getstr("enacs") .Mouse = .getstr("kmous")// Technically the RGB flag that is provided for xterm-direct is not // quite right. The problem is that the -direct flag that was introduced // with ncurses 6.1 requires a parsing for the parameters that we lack. // For this case we'll just assume it's XTerm compatible. Someday this // may be incorrect, but right now it is correct, and nobody uses it // anyway.if .getflag("Tc") {// This presumes XTerm 24-bit true color. .TrueColor = true } elseif .getflag("RGB") {// This is for xterm-direct, which uses a different scheme entirely. // (ncurses went a very different direction from everyone else, and // so it's unlikely anything is using this definition.) .TrueColor = true .SetBg = "\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m" .SetFg = "\x1b[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m" }// We only support colors in ANSI 8 or 256 color mode.if .Colors < 8 || .SetFg == "" { .Colors = 0 }if .SetCursor == "" {returnnil, "", errNotAddressable }// For padding, we lookup the pad char. If that isn't present, // and npc is *not* set, then we assume a null byte. .PadChar = .getstr("pad")if .PadChar == "" {if !.getflag("npc") { .PadChar = "\u0000" } }// For terminals that use "standard" SGR sequences, lets combine the // foreground and background together.ifstrings.HasPrefix(.SetFg, "\x1b[") &&strings.HasPrefix(.SetBg, "\x1b[") &&strings.HasSuffix(.SetFg, "m") &&strings.HasSuffix(.SetBg, "m") { := .SetFg[:len(.SetFg)-1] := regexp.MustCompile("%p1") := .ReplaceAllString(.SetBg[2:], "%p2") .SetFgBg = + ";" + }return , .desc, nil}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.