diff --git a/pkg/terminfo/LICENSE.md b/pkg/terminfo/LICENSE.md deleted file mode 100644 index 6b0b1270..00000000 --- a/pkg/terminfo/LICENSE.md +++ /dev/null @@ -1,203 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this 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. - diff --git a/pkg/terminfo/README.md b/pkg/terminfo/README.md deleted file mode 100644 index aaaef1ee..00000000 --- a/pkg/terminfo/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Terminfo parser - -This terminfo parser was written by the authors of [tcell](https://github.com/gdamore/tcell). We are using it here -to compile the terminal database if the terminal entry is not found in set of precompiled terminals. - -The source for `mkinfo.go` is adapted from tcell's `mkinfo` tool to be more of a library. diff --git a/pkg/terminfo/mkinfo.go b/pkg/terminfo/mkinfo.go deleted file mode 100644 index 9d222655..00000000 --- a/pkg/terminfo/mkinfo.go +++ /dev/null @@ -1,518 +0,0 @@ -// Copyright 2017 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. - -// This command is used to generate suitable configuration files in either -// go syntax or in JSON. It defaults to JSON output on stdout. If no -// term values are specified on the command line, then $TERM is used. -// -// Usage is like this: -// -// mkinfo [-init] [-go file.go] [-json file.json] [-quiet] [-nofatal] [...] -// -// -gzip specifies output should be compressed (json only) -// -go specifies Go output into the named file. Use - for stdout. -// -json specifies JSON output in the named file. Use - for stdout -// -nofatal indicates that errors loading definitions should not be fatal -// - -package terminfo - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io" - "os" - "os/exec" - "regexp" - "strconv" - "strings" -) - -type termcap struct { - name string - desc string - aliases []string - bools map[string]bool - nums map[string]int - strs map[string]string -} - -func (tc *termcap) getnum(s string) int { - return (tc.nums[s]) -} - -func (tc *termcap) getflag(s string) bool { - return (tc.bools[s]) -} - -func (tc *termcap) getstr(s string) string { - return (tc.strs[s]) -} - -const ( - NONE = iota - CTRL - ESC -) - -func unescape(s 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. - buf := &bytes.Buffer{} - esc := NONE - - for i := 0; i < len(s); i++ { - c := s[i] - switch esc { - case NONE: - switch c { - case '\\': - esc = ESC - case '^': - esc = CTRL - default: - buf.WriteByte(c) - } - case CTRL: - buf.WriteByte(c - 0x40) - esc = NONE - case ESC: - switch c { - case 'E', 'e': - buf.WriteByte(0x1b) - case '0', '1', '2', '3', '4', '5', '6', '7': - if i+2 < len(s) && s[i+1] >= '0' && s[i+1] <= '7' && s[i+2] >= '0' && s[i+2] <= '7' { - buf.WriteByte(((c - '0') * 64) + ((s[i+1] - '0') * 8) + (s[i+2] - '0')) - i = i + 2 - } else if c == '0' { - buf.WriteByte(0) - } - case 'n': - buf.WriteByte('\n') - case 'r': - buf.WriteByte('\r') - case 't': - buf.WriteByte('\t') - case 'b': - buf.WriteByte('\b') - case 'f': - buf.WriteByte('\f') - case 's': - buf.WriteByte(' ') - case 'l': - panic("WTF: weird format: " + s) - default: - buf.WriteByte(c) - } - esc = NONE - } - } - return (buf.String()) -} - -func (tc *termcap) setupterm(name string) error { - cmd := exec.Command("infocmp", "-1", name) - output := &bytes.Buffer{} - cmd.Stdout = output - - tc.strs = make(map[string]string) - tc.bools = make(map[string]bool) - tc.nums = make(map[string]int) - - err := cmd.Run() - if err != nil { - return err - } - - // Now parse the output. - // We get comment lines (starting with "#"), followed by - // a header line that looks like "||...|" - // then capabilities, one per line, starting with a tab and ending - // with a comma and newline. - lines := strings.Split(output.String(), "\n") - for len(lines) > 0 && strings.HasPrefix(lines[0], "#") { - lines = lines[1:] - } - - // Ditch trailing empty last line - if lines[len(lines)-1] == "" { - lines = lines[:len(lines)-1] - } - header := lines[0] - if strings.HasSuffix(header, ",") { - header = header[:len(header)-1] - } - names := strings.Split(header, "|") - tc.name = names[0] - names = names[1:] - if len(names) > 0 { - tc.desc = names[len(names)-1] - names = names[:len(names)-1] - } - tc.aliases = names - for _, val := range lines[1:] { - if (!strings.HasPrefix(val, "\t")) || - (!strings.HasSuffix(val, ",")) { - return (errors.New("malformed infocmp: " + val)) - } - - val = val[1:] - val = val[:len(val)-1] - - if k := strings.SplitN(val, "=", 2); len(k) == 2 { - tc.strs[k[0]] = unescape(k[1]) - } else if k := strings.SplitN(val, "#", 2); len(k) == 2 { - if strings.HasPrefix(k[1], "0x") { - if u, err := strconv.ParseUint(k[1][2:], 16, 0); err != nil { - return (err) - } else { - tc.nums[k[0]] = int(u) - } - } else if u, err := strconv.ParseUint(k[1], 10, 0); err != nil { - return (err) - } else { - tc.nums[k[0]] = int(u) - } - } else { - tc.bools[val] = true - } - } - return nil -} - -// This program is used to collect data from the system's terminfo library, -// and write it into Go source code. That is, we maintain our terminfo -// capabilities encoded in the program. It should never need to be run by -// an end user, but developers can use this to add codes for additional -// terminal types. -// -// If a terminal name ending with -truecolor is given, and we cannot find -// one, we will try to fabricate one from either the -256color (if present) -// or the unadorned base name, adding the XTerm specific 24-bit color -// escapes. We believe that all 24-bit capable terminals use the same -// escape sequences, and terminfo has yet to evolve to support this. -func getinfo(name string) (*Terminfo, string, error) { - var tc termcap - addTrueColor := false - if err := tc.setupterm(name); err != nil { - if strings.HasSuffix(name, "-truecolor") { - base := name[:len(name)-len("-truecolor")] - // Probably -256color is closest to what we want - if err = tc.setupterm(base + "-256color"); err != nil { - err = tc.setupterm(base) - } - if err == nil { - addTrueColor = true - } - tc.name = name - } - if err != nil { - return nil, "", err - } - } - t := &Terminfo{} - // If this is an alias record, then just emit the alias - t.Name = tc.name - if t.Name != name { - return t, "", nil - } - t.Aliases = tc.aliases - t.Colors = tc.getnum("colors") - t.Columns = tc.getnum("cols") - t.Lines = tc.getnum("lines") - t.Bell = tc.getstr("bel") - t.Clear = tc.getstr("clear") - t.EnterCA = tc.getstr("smcup") - t.ExitCA = tc.getstr("rmcup") - t.ShowCursor = tc.getstr("cnorm") - t.HideCursor = tc.getstr("civis") - t.AttrOff = tc.getstr("sgr0") - t.Underline = tc.getstr("smul") - t.Bold = tc.getstr("bold") - t.Blink = tc.getstr("blink") - t.Dim = tc.getstr("dim") - t.Reverse = tc.getstr("rev") - t.EnterKeypad = tc.getstr("smkx") - t.ExitKeypad = tc.getstr("rmkx") - t.SetFg = tc.getstr("setaf") - t.SetBg = tc.getstr("setab") - t.SetCursor = tc.getstr("cup") - t.CursorBack1 = tc.getstr("cub1") - t.CursorUp1 = tc.getstr("cuu1") - t.KeyF1 = tc.getstr("kf1") - t.KeyF2 = tc.getstr("kf2") - t.KeyF3 = tc.getstr("kf3") - t.KeyF4 = tc.getstr("kf4") - t.KeyF5 = tc.getstr("kf5") - t.KeyF6 = tc.getstr("kf6") - t.KeyF7 = tc.getstr("kf7") - t.KeyF8 = tc.getstr("kf8") - t.KeyF9 = tc.getstr("kf9") - t.KeyF10 = tc.getstr("kf10") - t.KeyF11 = tc.getstr("kf11") - t.KeyF12 = tc.getstr("kf12") - t.KeyF13 = tc.getstr("kf13") - t.KeyF14 = tc.getstr("kf14") - t.KeyF15 = tc.getstr("kf15") - t.KeyF16 = tc.getstr("kf16") - t.KeyF17 = tc.getstr("kf17") - t.KeyF18 = tc.getstr("kf18") - t.KeyF19 = tc.getstr("kf19") - t.KeyF20 = tc.getstr("kf20") - t.KeyF21 = tc.getstr("kf21") - t.KeyF22 = tc.getstr("kf22") - t.KeyF23 = tc.getstr("kf23") - t.KeyF24 = tc.getstr("kf24") - t.KeyF25 = tc.getstr("kf25") - t.KeyF26 = tc.getstr("kf26") - t.KeyF27 = tc.getstr("kf27") - t.KeyF28 = tc.getstr("kf28") - t.KeyF29 = tc.getstr("kf29") - t.KeyF30 = tc.getstr("kf30") - t.KeyF31 = tc.getstr("kf31") - t.KeyF32 = tc.getstr("kf32") - t.KeyF33 = tc.getstr("kf33") - t.KeyF34 = tc.getstr("kf34") - t.KeyF35 = tc.getstr("kf35") - t.KeyF36 = tc.getstr("kf36") - t.KeyF37 = tc.getstr("kf37") - t.KeyF38 = tc.getstr("kf38") - t.KeyF39 = tc.getstr("kf39") - t.KeyF40 = tc.getstr("kf40") - t.KeyF41 = tc.getstr("kf41") - t.KeyF42 = tc.getstr("kf42") - t.KeyF43 = tc.getstr("kf43") - t.KeyF44 = tc.getstr("kf44") - t.KeyF45 = tc.getstr("kf45") - t.KeyF46 = tc.getstr("kf46") - t.KeyF47 = tc.getstr("kf47") - t.KeyF48 = tc.getstr("kf48") - t.KeyF49 = tc.getstr("kf49") - t.KeyF50 = tc.getstr("kf50") - t.KeyF51 = tc.getstr("kf51") - t.KeyF52 = tc.getstr("kf52") - t.KeyF53 = tc.getstr("kf53") - t.KeyF54 = tc.getstr("kf54") - t.KeyF55 = tc.getstr("kf55") - t.KeyF56 = tc.getstr("kf56") - t.KeyF57 = tc.getstr("kf57") - t.KeyF58 = tc.getstr("kf58") - t.KeyF59 = tc.getstr("kf59") - t.KeyF60 = tc.getstr("kf60") - t.KeyF61 = tc.getstr("kf61") - t.KeyF62 = tc.getstr("kf62") - t.KeyF63 = tc.getstr("kf63") - t.KeyF64 = tc.getstr("kf64") - t.KeyInsert = tc.getstr("kich1") - t.KeyDelete = tc.getstr("kdch1") - t.KeyBackspace = tc.getstr("kbs") - t.KeyHome = tc.getstr("khome") - t.KeyEnd = tc.getstr("kend") - t.KeyUp = tc.getstr("kcuu1") - t.KeyDown = tc.getstr("kcud1") - t.KeyRight = tc.getstr("kcuf1") - t.KeyLeft = tc.getstr("kcub1") - t.KeyPgDn = tc.getstr("knp") - t.KeyPgUp = tc.getstr("kpp") - t.KeyBacktab = tc.getstr("kcbt") - t.KeyExit = tc.getstr("kext") - t.KeyCancel = tc.getstr("kcan") - t.KeyPrint = tc.getstr("kprt") - t.KeyHelp = tc.getstr("khlp") - t.KeyClear = tc.getstr("kclr") - t.AltChars = tc.getstr("acsc") - t.EnterAcs = tc.getstr("smacs") - t.ExitAcs = tc.getstr("rmacs") - t.EnableAcs = tc.getstr("enacs") - t.Mouse = tc.getstr("kmous") - t.KeyShfRight = tc.getstr("kRIT") - t.KeyShfLeft = tc.getstr("kLFT") - t.KeyShfHome = tc.getstr("kHOM") - t.KeyShfEnd = tc.getstr("kEND") - - // Terminfo lacks descriptions for a bunch of modified keys, - // but modern XTerm and emulators often have them. Let's add them, - // if the shifted right and left arrows are defined. - if t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" { - t.KeyShfUp = "\x1b[1;2A" - t.KeyShfDown = "\x1b[1;2B" - t.KeyMetaUp = "\x1b[1;9A" - t.KeyMetaDown = "\x1b[1;9B" - t.KeyMetaRight = "\x1b[1;9C" - t.KeyMetaLeft = "\x1b[1;9D" - t.KeyAltUp = "\x1b[1;3A" - t.KeyAltDown = "\x1b[1;3B" - t.KeyAltRight = "\x1b[1;3C" - t.KeyAltLeft = "\x1b[1;3D" - t.KeyCtrlUp = "\x1b[1;5A" - t.KeyCtrlDown = "\x1b[1;5B" - t.KeyCtrlRight = "\x1b[1;5C" - t.KeyCtrlLeft = "\x1b[1;5D" - t.KeyAltShfUp = "\x1b[1;4A" - t.KeyAltShfDown = "\x1b[1;4B" - t.KeyAltShfRight = "\x1b[1;4C" - t.KeyAltShfLeft = "\x1b[1;4D" - - t.KeyMetaShfUp = "\x1b[1;10A" - t.KeyMetaShfDown = "\x1b[1;10B" - t.KeyMetaShfRight = "\x1b[1;10C" - t.KeyMetaShfLeft = "\x1b[1;10D" - - t.KeyCtrlShfUp = "\x1b[1;6A" - t.KeyCtrlShfDown = "\x1b[1;6B" - t.KeyCtrlShfRight = "\x1b[1;6C" - t.KeyCtrlShfLeft = "\x1b[1;6D" - } - // And also for Home and End - if t.KeyShfHome == "\x1b[1;2H" && t.KeyShfEnd == "\x1b[1;2F" { - t.KeyCtrlHome = "\x1b[1;5H" - t.KeyCtrlEnd = "\x1b[1;5F" - t.KeyAltHome = "\x1b[1;9H" - t.KeyAltEnd = "\x1b[1;9F" - t.KeyCtrlShfHome = "\x1b[1;6H" - t.KeyCtrlShfEnd = "\x1b[1;6F" - t.KeyAltShfHome = "\x1b[1;4H" - t.KeyAltShfEnd = "\x1b[1;4F" - t.KeyMetaShfHome = "\x1b[1;10H" - t.KeyMetaShfEnd = "\x1b[1;10F" - } - - // And the same thing for rxvt and workalikes (Eterm, aterm, etc.) - // It seems that urxvt at least send ESC as ALT prefix for these, - // although some places seem to indicate a separate ALT key sesquence. - if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { - t.KeyShfUp = "\x1b[a" - t.KeyShfDown = "\x1b[b" - t.KeyCtrlUp = "\x1b[Oa" - t.KeyCtrlDown = "\x1b[Ob" - t.KeyCtrlRight = "\x1b[Oc" - t.KeyCtrlLeft = "\x1b[Od" - } - if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" { - t.KeyCtrlHome = "\x1b[7^" - t.KeyCtrlEnd = "\x1b[8^" - } - - // If the kmous entry is present, then we need to record the - // the codes to enter and exit mouse mode. Sadly, this is not - // part of the terminfo databases anywhere that I've found, but - // is an extension. The escape codes are documented in the XTerm - // manual, and all terminals that have kmous are expected to - // use these same codes, unless explicitly configured otherwise - // vi XM. Note that in any event, we only known how to parse either - // x11 or SGR mouse events -- if your terminal doesn't support one - // of these two forms, you maybe out of luck. - t.MouseMode = tc.getstr("XM") - if t.Mouse != "" && t.MouseMode == "" { - // we anticipate that all xterm mouse tracking compatible - // terminals understand mouse tracking (1000), but we hope - // that those that don't understand any-event tracking (1003) - // will at least ignore it. Likewise we hope that terminals - // that don't understand SGR reporting (1006) just ignore it. - t.MouseMode = "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;" + - "\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c" - } - - // We only support colors in ANSI 8 or 256 color mode. - if t.Colors < 8 || t.SetFg == "" { - t.Colors = 0 - } - if t.SetCursor == "" { - return nil, "", errors.New("terminal not cursor addressable") - } - - // For padding, we lookup the pad char. If that isn't present, - // and npc is *not* set, then we assume a null byte. - t.PadChar = tc.getstr("pad") - if t.PadChar == "" { - if !tc.getflag("npc") { - t.PadChar = "\u0000" - } - } - - // For some terminals we fabricate a -truecolor entry, that may - // not exist in terminfo. - if addTrueColor { - t.SetFgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%dm" - t.SetBgRGB = "\x1b[48;2;%p1%d;%p2%d;%p3%dm" - t.SetFgBgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%d;" + - "48;2;%p4%d;%p5%d;%p6%dm" - } - - // For terminals that use "standard" SGR sequences, lets combine the - // foreground and background together. - if strings.HasPrefix(t.SetFg, "\x1b[") && - strings.HasPrefix(t.SetBg, "\x1b[") && - strings.HasSuffix(t.SetFg, "m") && - strings.HasSuffix(t.SetBg, "m") { - fg := t.SetFg[:len(t.SetFg)-1] - r := regexp.MustCompile("%p1") - bg := r.ReplaceAllString(t.SetBg[2:], "%p2") - t.SetFgBg = fg + ";" + bg - } - - return t, tc.desc, nil -} - -func WriteDB(filename string) error { - var e error - js := []byte{} - args := []string{os.Getenv("TERM")} - - tdata := make(map[string]*Terminfo) - descs := make(map[string]string) - - for _, term := range args { - if t, desc, e := getinfo(term); e != nil { - return e - } else { - tdata[term] = t - descs[term] = desc - } - } - - if len(tdata) == 0 { - // No data. - return errors.New("No data") - } - o := os.Stdout - if o, e = os.Create(filename); e != nil { - return e - } - var w io.WriteCloser - w = o - for _, term := range args { - if t := tdata[term]; t != nil { - js, e = json.Marshal(t) - fmt.Fprintln(w, string(js)) - } - // arguably if there is more than one term, this - // should be a javascript array, but that's not how - // we load it. We marshal objects one at a time from - // the file. - } - if e != nil { - return e - } - w.Close() - if w != o { - o.Close() - } - - return nil -} diff --git a/pkg/terminfo/terminfo.go b/pkg/terminfo/terminfo.go deleted file mode 100644 index 4bf3beed..00000000 --- a/pkg/terminfo/terminfo.go +++ /dev/null @@ -1,838 +0,0 @@ -// Copyright 2017 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. - -package terminfo - -import ( - "bytes" - "compress/gzip" - "encoding/json" - "errors" - "fmt" - "io" - "os" - "path" - "strconv" - "strings" - "sync" -) - -var ( - // ErrTermNotFound indicates that a suitable terminal entry could - // not be found. This can result from either not having TERM set, - // or from the TERM failing to support certain minimal functionality, - // in particular absolute cursor addressability (the cup capability) - // is required. For example, legacy "adm3" lacks this capability, - // whereas the slightly newer "adm3a" supports it. This failure - // occurs most often with "dumb". - ErrTermNotFound = errors.New("terminal entry not found") -) - -// Terminfo represents a terminfo entry. Note that we use friendly names -// in Go, but when we write out JSON, we use the same names as terminfo. -// The name, aliases and smous, rmous fields do not come from terminfo directly. -type Terminfo struct { - Name string `json:"name"` - Aliases []string `json:"aliases,omitempty"` - Columns int `json:"cols,omitempty"` // cols - Lines int `json:"lines,omitempty"` // lines - Colors int `json:"colors,omitempty"` // colors - Bell string `json:"bell,omitempty"` // bell - Clear string `json:"clear,omitempty"` // clear - EnterCA string `json:"smcup,omitempty"` // smcup - ExitCA string `json:"rmcup,omitempty"` // rmcup - ShowCursor string `json:"cnorm,omitempty"` // cnorm - HideCursor string `json:"civis,omitempty"` // civis - AttrOff string `json:"sgr0,omitempty"` // sgr0 - Underline string `json:"smul,omitempty"` // smul - Bold string `json:"bold,omitempty"` // bold - Blink string `json:"blink,omitempty"` // blink - Reverse string `json:"rev,omitempty"` // rev - Dim string `json:"dim,omitempty"` // dim - EnterKeypad string `json:"smkx,omitempty"` // smkx - ExitKeypad string `json:"rmkx,omitempty"` // rmkx - SetFg string `json:"setaf,omitempty"` // setaf - SetBg string `json:"setbg,omitempty"` // setab - SetCursor string `json:"cup,omitempty"` // cup - CursorBack1 string `json:"cub1,omitempty"` // cub1 - CursorUp1 string `json:"cuu1,omitempty"` // cuu1 - PadChar string `json:"pad,omitempty"` // pad - KeyBackspace string `json:"kbs,omitempty"` // kbs - KeyF1 string `json:"kf1,omitempty"` // kf1 - KeyF2 string `json:"kf2,omitempty"` // kf2 - KeyF3 string `json:"kf3,omitempty"` // kf3 - KeyF4 string `json:"kf4,omitempty"` // kf4 - KeyF5 string `json:"kf5,omitempty"` // kf5 - KeyF6 string `json:"kf6,omitempty"` // kf6 - KeyF7 string `json:"kf7,omitempty"` // kf7 - KeyF8 string `json:"kf8,omitempty"` // kf8 - KeyF9 string `json:"kf9,omitempty"` // kf9 - KeyF10 string `json:"kf10,omitempty"` // kf10 - KeyF11 string `json:"kf11,omitempty"` // kf11 - KeyF12 string `json:"kf12,omitempty"` // kf12 - KeyF13 string `json:"kf13,omitempty"` // kf13 - KeyF14 string `json:"kf14,omitempty"` // kf14 - KeyF15 string `json:"kf15,omitempty"` // kf15 - KeyF16 string `json:"kf16,omitempty"` // kf16 - KeyF17 string `json:"kf17,omitempty"` // kf17 - KeyF18 string `json:"kf18,omitempty"` // kf18 - KeyF19 string `json:"kf19,omitempty"` // kf19 - KeyF20 string `json:"kf20,omitempty"` // kf20 - KeyF21 string `json:"kf21,omitempty"` // kf21 - KeyF22 string `json:"kf22,omitempty"` // kf22 - KeyF23 string `json:"kf23,omitempty"` // kf23 - KeyF24 string `json:"kf24,omitempty"` // kf24 - KeyF25 string `json:"kf25,omitempty"` // kf25 - KeyF26 string `json:"kf26,omitempty"` // kf26 - KeyF27 string `json:"kf27,omitempty"` // kf27 - KeyF28 string `json:"kf28,omitempty"` // kf28 - KeyF29 string `json:"kf29,omitempty"` // kf29 - KeyF30 string `json:"kf30,omitempty"` // kf30 - KeyF31 string `json:"kf31,omitempty"` // kf31 - KeyF32 string `json:"kf32,omitempty"` // kf32 - KeyF33 string `json:"kf33,omitempty"` // kf33 - KeyF34 string `json:"kf34,omitempty"` // kf34 - KeyF35 string `json:"kf35,omitempty"` // kf35 - KeyF36 string `json:"kf36,omitempty"` // kf36 - KeyF37 string `json:"kf37,omitempty"` // kf37 - KeyF38 string `json:"kf38,omitempty"` // kf38 - KeyF39 string `json:"kf39,omitempty"` // kf39 - KeyF40 string `json:"kf40,omitempty"` // kf40 - KeyF41 string `json:"kf41,omitempty"` // kf41 - KeyF42 string `json:"kf42,omitempty"` // kf42 - KeyF43 string `json:"kf43,omitempty"` // kf43 - KeyF44 string `json:"kf44,omitempty"` // kf44 - KeyF45 string `json:"kf45,omitempty"` // kf45 - KeyF46 string `json:"kf46,omitempty"` // kf46 - KeyF47 string `json:"kf47,omitempty"` // kf47 - KeyF48 string `json:"kf48,omitempty"` // kf48 - KeyF49 string `json:"kf49,omitempty"` // kf49 - KeyF50 string `json:"kf50,omitempty"` // kf50 - KeyF51 string `json:"kf51,omitempty"` // kf51 - KeyF52 string `json:"kf52,omitempty"` // kf52 - KeyF53 string `json:"kf53,omitempty"` // kf53 - KeyF54 string `json:"kf54,omitempty"` // kf54 - KeyF55 string `json:"kf55,omitempty"` // kf55 - KeyF56 string `json:"kf56,omitempty"` // kf56 - KeyF57 string `json:"kf57,omitempty"` // kf57 - KeyF58 string `json:"kf58,omitempty"` // kf58 - KeyF59 string `json:"kf59,omitempty"` // kf59 - KeyF60 string `json:"kf60,omitempty"` // kf60 - KeyF61 string `json:"kf61,omitempty"` // kf61 - KeyF62 string `json:"kf62,omitempty"` // kf62 - KeyF63 string `json:"kf63,omitempty"` // kf63 - KeyF64 string `json:"kf64,omitempty"` // kf64 - KeyInsert string `json:"kich,omitempty"` // kich1 - KeyDelete string `json:"kdch,omitempty"` // kdch1 - KeyHome string `json:"khome,omitempty"` // khome - KeyEnd string `json:"kend,omitempty"` // kend - KeyHelp string `json:"khlp,omitempty"` // khlp - KeyPgUp string `json:"kpp,omitempty"` // kpp - KeyPgDn string `json:"knp,omitempty"` // knp - KeyUp string `json:"kcuu1,omitempty"` // kcuu1 - KeyDown string `json:"kcud1,omitempty"` // kcud1 - KeyLeft string `json:"kcub1,omitempty"` // kcub1 - KeyRight string `json:"kcuf1,omitempty"` // kcuf1 - KeyBacktab string `json:"kcbt,omitempty"` // kcbt - KeyExit string `json:"kext,omitempty"` // kext - KeyClear string `json:"kclr,omitempty"` // kclr - KeyPrint string `json:"kprt,omitempty"` // kprt - KeyCancel string `json:"kcan,omitempty"` // kcan - Mouse string `json:"kmous,omitempty"` // kmous - MouseMode string `json:"XM,omitempty"` // XM - AltChars string `json:"acsc,omitempty"` // acsc - EnterAcs string `json:"smacs,omitempty"` // smacs - ExitAcs string `json:"rmacs,omitempty"` // rmacs - EnableAcs string `json:"enacs,omitempty"` // enacs - KeyShfRight string `json:"kRIT,omitempty"` // kRIT - KeyShfLeft string `json:"kLFT,omitempty"` // kLFT - KeyShfHome string `json:"kHOM,omitempty"` // kHOM - KeyShfEnd string `json:"kEND,omitempty"` // kEND - - // These are non-standard extensions to terminfo. This includes - // true color support, and some additional keys. Its kind of bizarre - // that shifted variants of left and right exist, but not up and down. - // Terminal support for these are going to vary amongst XTerm - // emulations, so don't depend too much on them in your application. - - SetFgBg string `json:"_setfgbg,omitempty"` // setfgbg - SetFgBgRGB string `json:"_setfgbgrgb,omitempty"` // setfgbgrgb - SetFgRGB string `json:"_setfrgb,omitempty"` // setfrgb - SetBgRGB string `json:"_setbrgb,omitempty"` // setbrgb - KeyShfUp string `json:"_kscu1,omitempty"` // shift-up - KeyShfDown string `json:"_kscud1,omitempty"` // shift-down - KeyCtrlUp string `json:"_kccu1,omitempty"` // ctrl-up - KeyCtrlDown string `json:"_kccud1,omitempty"` // ctrl-left - KeyCtrlRight string `json:"_kccuf1,omitempty"` // ctrl-right - KeyCtrlLeft string `json:"_kccub1,omitempty"` // ctrl-left - KeyMetaUp string `json:"_kmcu1,omitempty"` // meta-up - KeyMetaDown string `json:"_kmcud1,omitempty"` // meta-left - KeyMetaRight string `json:"_kmcuf1,omitempty"` // meta-right - KeyMetaLeft string `json:"_kmcub1,omitempty"` // meta-left - KeyAltUp string `json:"_kacu1,omitempty"` // alt-up - KeyAltDown string `json:"_kacud1,omitempty"` // alt-left - KeyAltRight string `json:"_kacuf1,omitempty"` // alt-right - KeyAltLeft string `json:"_kacub1,omitempty"` // alt-left - KeyCtrlHome string `json:"_kchome,omitempty"` - KeyCtrlEnd string `json:"_kcend,omitempty"` - KeyMetaHome string `json:"_kmhome,omitempty"` - KeyMetaEnd string `json:"_kmend,omitempty"` - KeyAltHome string `json:"_kahome,omitempty"` - KeyAltEnd string `json:"_kaend,omitempty"` - KeyAltShfUp string `json:"_kascu1,omitempty"` - KeyAltShfDown string `json:"_kascud1,omitempty"` - KeyAltShfLeft string `json:"_kascub1,omitempty"` - KeyAltShfRight string `json:"_kascuf1,omitempty"` - KeyMetaShfUp string `json:"_kmscu1,omitempty"` - KeyMetaShfDown string `json:"_kmscud1,omitempty"` - KeyMetaShfLeft string `json:"_kmscub1,omitempty"` - KeyMetaShfRight string `json:"_kmscuf1,omitempty"` - KeyCtrlShfUp string `json:"_kcscu1,omitempty"` - KeyCtrlShfDown string `json:"_kcscud1,omitempty"` - KeyCtrlShfLeft string `json:"_kcscub1,omitempty"` - KeyCtrlShfRight string `json:"_kcscuf1,omitempty"` - KeyCtrlShfHome string `json:"_kcHOME,omitempty"` - KeyCtrlShfEnd string `json:"_kcEND,omitempty"` - KeyAltShfHome string `json:"_kaHOME,omitempty"` - KeyAltShfEnd string `json:"_kaEND,omitempty"` - KeyMetaShfHome string `json:"_kmHOME,omitempty"` - KeyMetaShfEnd string `json:"_kmEND,omitempty"` -} - -type stackElem struct { - s string - i int - isStr bool - isInt bool -} - -type stack []stackElem - -func (st stack) Push(v string) stack { - e := stackElem{ - s: v, - isStr: true, - } - return append(st, e) -} - -func (st stack) Pop() (string, stack) { - v := "" - if len(st) > 0 { - e := st[len(st)-1] - st = st[:len(st)-1] - if e.isStr { - v = e.s - } else { - v = strconv.Itoa(e.i) - } - } - return v, st -} - -func (st stack) PopInt() (int, stack) { - if len(st) > 0 { - e := st[len(st)-1] - st = st[:len(st)-1] - if e.isInt { - return e.i, st - } else if e.isStr { - i, _ := strconv.Atoi(e.s) - return i, st - } - } - return 0, st -} - -func (st stack) PopBool() (bool, stack) { - if len(st) > 0 { - e := st[len(st)-1] - st = st[:len(st)-1] - if e.isStr { - if e.s == "1" { - return true, st - } - return false, st - } else if e.i == 1 { - return true, st - } else { - return false, st - } - } - return false, st -} - -func (st stack) PushInt(i int) stack { - e := stackElem{ - i: i, - isInt: true, - } - return append(st, e) -} - -func (st stack) PushBool(i bool) stack { - if i { - return st.PushInt(1) - } - return st.PushInt(0) -} - -func nextch(s string, index int) (byte, int) { - if index < len(s) { - return s[index], index + 1 - } - return 0, index -} - -// static vars -var svars [26]string - -// paramsBuffer handles some persistent state for TParam. Technically we -// could probably dispense with this, but caching buffer arrays gives us -// a nice little performance boost. Furthermore, we know that TParam is -// rarely (never?) called re-entrantly, so we can just reuse the same -// buffers, making it thread-safe by stashing a lock. -type paramsBuffer struct { - out bytes.Buffer - buf bytes.Buffer - lk sync.Mutex -} - -// Start initializes the params buffer with the initial string data. -// It also locks the paramsBuffer. The caller must call End() when -// finished. -func (pb *paramsBuffer) Start(s string) { - pb.lk.Lock() - pb.out.Reset() - pb.buf.Reset() - pb.buf.WriteString(s) -} - -// End returns the final output from TParam, but it also releases the lock. -func (pb *paramsBuffer) End() string { - s := pb.out.String() - pb.lk.Unlock() - return s -} - -// NextCh returns the next input character to the expander. -func (pb *paramsBuffer) NextCh() (byte, error) { - return pb.buf.ReadByte() -} - -// PutCh "emits" (rather schedules for output) a single byte character. -func (pb *paramsBuffer) PutCh(ch byte) { - pb.out.WriteByte(ch) -} - -// PutString schedules a string for output. -func (pb *paramsBuffer) PutString(s string) { - pb.out.WriteString(s) -} - -var pb = ¶msBuffer{} - -// TParm takes a terminfo parameterized string, such as setaf or cup, and -// evaluates the string, and returns the result with the parameter -// applied. -func (t *Terminfo) TParm(s string, p ...int) string { - var stk stack - var a, b string - var ai, bi int - var ab bool - var dvars [26]string - var params [9]int - - pb.Start(s) - - // make sure we always have 9 parameters -- makes it easier - // later to skip checks - for i := 0; i < len(params) && i < len(p); i++ { - params[i] = p[i] - } - - nest := 0 - - for { - - ch, err := pb.NextCh() - if err != nil { - break - } - - if ch != '%' { - pb.PutCh(ch) - continue - } - - ch, err = pb.NextCh() - if err != nil { - // XXX Error - break - } - - switch ch { - case '%': // quoted % - pb.PutCh(ch) - - case 'i': // increment both parameters (ANSI cup support) - params[0]++ - params[1]++ - - case 'c', 's': - // NB: these, and 'd' below are special cased for - // efficiency. They could be handled by the richer - // format support below, less efficiently. - a, stk = stk.Pop() - pb.PutString(a) - - case 'd': - ai, stk = stk.PopInt() - pb.PutString(strconv.Itoa(ai)) - - case '0', '1', '2', '3', '4', 'x', 'X', 'o', ':': - // This is pretty suboptimal, but this is rarely used. - // None of the mainstream terminals use any of this, - // and it would surprise me if this code is ever - // executed outside of test cases. - f := "%" - if ch == ':' { - ch, _ = pb.NextCh() - } - f += string(ch) - for ch == '+' || ch == '-' || ch == '#' || ch == ' ' { - ch, _ = pb.NextCh() - f += string(ch) - } - for (ch >= '0' && ch <= '9') || ch == '.' { - ch, _ = pb.NextCh() - f += string(ch) - } - switch ch { - case 'd', 'x', 'X', 'o': - ai, stk = stk.PopInt() - pb.PutString(fmt.Sprintf(f, ai)) - case 'c', 's': - a, stk = stk.Pop() - pb.PutString(fmt.Sprintf(f, a)) - } - - case 'p': // push parameter - ch, _ = pb.NextCh() - ai = int(ch - '1') - if ai >= 0 && ai < len(params) { - stk = stk.PushInt(params[ai]) - } else { - stk = stk.PushInt(0) - } - - case 'P': // pop & store variable - ch, _ = pb.NextCh() - if ch >= 'A' && ch <= 'Z' { - svars[int(ch-'A')], stk = stk.Pop() - } else if ch >= 'a' && ch <= 'z' { - dvars[int(ch-'a')], stk = stk.Pop() - } - - case 'g': // recall & push variable - ch, _ = pb.NextCh() - if ch >= 'A' && ch <= 'Z' { - stk = stk.Push(svars[int(ch-'A')]) - } else if ch >= 'a' && ch <= 'z' { - stk = stk.Push(dvars[int(ch-'a')]) - } - - case '\'': // push(char) - ch, _ = pb.NextCh() - pb.NextCh() // must be ' but we don't check - stk = stk.Push(string(ch)) - - case '{': // push(int) - ai = 0 - ch, _ = pb.NextCh() - for ch >= '0' && ch <= '9' { - ai *= 10 - ai += int(ch - '0') - ch, _ = pb.NextCh() - } - // ch must be '}' but no verification - stk = stk.PushInt(ai) - - case 'l': // push(strlen(pop)) - a, stk = stk.Pop() - stk = stk.PushInt(len(a)) - - case '+': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai + bi) - - case '-': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai - bi) - - case '*': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai * bi) - - case '/': - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - if bi != 0 { - stk = stk.PushInt(ai / bi) - } else { - stk = stk.PushInt(0) - } - - case 'm': // push(pop mod pop) - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - if bi != 0 { - stk = stk.PushInt(ai % bi) - } else { - stk = stk.PushInt(0) - } - - case '&': // AND - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai & bi) - - case '|': // OR - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai | bi) - - case '^': // XOR - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushInt(ai ^ bi) - - case '~': // bit complement - ai, stk = stk.PopInt() - stk = stk.PushInt(ai ^ -1) - - case '!': // logical NOT - ai, stk = stk.PopInt() - stk = stk.PushBool(ai != 0) - - case '=': // numeric compare or string compare - b, stk = stk.Pop() - a, stk = stk.Pop() - stk = stk.PushBool(a == b) - - case '>': // greater than, numeric - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushBool(ai > bi) - - case '<': // less than, numeric - bi, stk = stk.PopInt() - ai, stk = stk.PopInt() - stk = stk.PushBool(ai < bi) - - case '?': // start conditional - - case 't': - ab, stk = stk.PopBool() - if ab { - // just keep going - break - } - nest = 0 - ifloop: - // this loop consumes everything until we hit our else, - // or the end of the conditional - for { - ch, err = pb.NextCh() - if err != nil { - break - } - if ch != '%' { - continue - } - ch, _ = pb.NextCh() - switch ch { - case ';': - if nest == 0 { - break ifloop - } - nest-- - case '?': - nest++ - case 'e': - if nest == 0 { - break ifloop - } - } - } - - case 'e': - // if we got here, it means we didn't use the else - // in the 't' case above, and we should skip until - // the end of the conditional - nest = 0 - elloop: - for { - ch, err = pb.NextCh() - if err != nil { - break - } - if ch != '%' { - continue - } - ch, _ = pb.NextCh() - switch ch { - case ';': - if nest == 0 { - break elloop - } - nest-- - case '?': - nest++ - } - } - - case ';': // endif - - } - } - - return pb.End() -} - -// TPuts 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. All Terminfo based strings should be emitted using -// this function. -func (t *Terminfo) TPuts(w io.Writer, s string, baud int) { - for { - beg := strings.Index(s, "$<") - if beg < 0 { - // Most strings don't need padding, which is good news! - io.WriteString(w, s) - return - } - io.WriteString(w, s[:beg]) - s = s[beg+2:] - end := strings.Index(s, ">") - if end < 0 { - // unterminated.. just emit bytes unadulterated - io.WriteString(w, "$<"+s) - return - } - val := s[:end] - s = s[end+1:] - padus := 0 - unit := 1000 - dot := false - loop: - for i := range val { - switch val[i] { - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - padus *= 10 - padus += int(val[i] - '0') - if dot { - unit *= 10 - } - case '.': - if !dot { - dot = true - } else { - break loop - } - default: - break loop - } - } - cnt := int(((baud / 8) * padus) / unit) - for cnt > 0 { - io.WriteString(w, t.PadChar) - cnt-- - } - } -} - -// TGoto 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 (t *Terminfo) TGoto(col, row int) string { - return t.TParm(t.SetCursor, row, col) -} - -// TColor returns a string corresponding to the given foreground and background -// colors. Either fg or bg can be set to -1 to elide. -func (t *Terminfo) TColor(fi, bi int) string { - rv := "" - // As a special case, we map bright colors to lower versions if the - // color table only holds 8. For the remaining 240 colors, the user - // is out of luck. Someday we could create a mapping table, but its - // not worth it. - if t.Colors == 8 { - if fi > 7 && fi < 16 { - fi -= 8 - } - if bi > 7 && bi < 16 { - bi -= 8 - } - } - if t.Colors > fi && fi >= 0 { - rv += t.TParm(t.SetFg, fi) - } - if t.Colors > bi && bi >= 0 { - rv += t.TParm(t.SetBg, bi) - } - return rv -} - -var ( - dblock sync.Mutex - terminfos = make(map[string]*Terminfo) - aliases = make(map[string]string) -) - -// AddTerminfo can be called to register a new Terminfo entry. -func AddTerminfo(t *Terminfo) { - dblock.Lock() - terminfos[t.Name] = t - for _, x := range t.Aliases { - terminfos[x] = t - } - dblock.Unlock() -} - -func loadFromFile(fname string, term string) (*Terminfo, error) { - var e error - var f io.ReadCloser - if f, e = os.Open(fname); e != nil { - return nil, e - } - defer f.Close() - if strings.HasSuffix(fname, ".gz") { - if f, e = gzip.NewReader(f); e != nil { - return nil, e - } - } - d := json.NewDecoder(f) - for { - t := &Terminfo{} - if e := d.Decode(t); e != nil { - if e == io.EOF { - return nil, ErrTermNotFound - } - return nil, e - } - if t.SetCursor == "" { - // This must be an alias record, return it. - return t, nil - } - if t.Name == term { - return t, nil - } - for _, a := range t.Aliases { - if a == term { - return t, nil - } - } - } -} - -// LookupTerminfo attempts to find a definition for the named $TERM. -// It first looks in the builtin database, which should cover just about -// everyone. If it can't find one there, then it will attempt to read -// one from the JSON file located in either $TCELLDB, $HOME/.tcelldb -// or in this package's source directory as database.json). -func LookupTerminfo(name string) (*Terminfo, error) { - if name == "" { - // else on windows: index out of bounds - // on the name[0] reference below - return nil, ErrTermNotFound - } - - dblock.Lock() - t := terminfos[name] - dblock.Unlock() - - if t == nil { - - var files []string - letter := fmt.Sprintf("%02x", name[0]) - gzfile := path.Join(letter, name+".gz") - jsfile := path.Join(letter, name) - - // Build up the search path. Old versions of tcell used a - // single database file, whereas the new ones locate them - // in JSON (optionally compressed) files. - // - // The search path looks like: - // - // $TCELLDB/x/xterm.gz - // $TCELLDB/x/xterm - // $TCELLDB - // $HOME/.tcelldb/x/xterm.gz - // $HOME/.tcelldb/x/xterm - // $HOME/.tcelldb - // $GOPATH/terminfo/database/x/xterm.gz - // $GOPATH/terminfo/database/x/xterm - // - if pth := os.Getenv("TCELLDB"); pth != "" { - files = append(files, path.Join(pth, gzfile)) - files = append(files, path.Join(pth, jsfile)) - files = append(files, pth) - } - if pth := os.Getenv("HOME"); pth != "" { - pth = path.Join(pth, ".tcelldb") - files = append(files, path.Join(pth, gzfile)) - files = append(files, path.Join(pth, jsfile)) - files = append(files, pth) - } - - for _, pth := range strings.Split(os.Getenv("GOPATH"), string(os.PathListSeparator)) { - pth = path.Join(pth, "src", "github.com", "gdamore", "tcell", "terminfo", "database") - files = append(files, path.Join(pth, gzfile)) - files = append(files, path.Join(pth, jsfile)) - } - - for _, fname := range files { - t, _ = loadFromFile(fname, name) - if t != nil { - break - } - } - if t != nil { - if t.Name != name { - // Check for a database loop (no infinite - // recursion). - dblock.Lock() - if aliases[name] != "" { - dblock.Unlock() - return nil, ErrTermNotFound - } - aliases[name] = t.Name - dblock.Unlock() - return LookupTerminfo(t.Name) - } - dblock.Lock() - terminfos[name] = t - dblock.Unlock() - } - } - if t == nil { - return nil, ErrTermNotFound - } - return t, nil -}