├── .gitignore ├── LICENSE ├── README.md ├── _examples └── main.go ├── caps.go ├── capvals.go ├── cmd └── infocmp │ ├── comp.sh │ ├── main.go │ └── test.sh ├── color.go ├── dec.go ├── gen.go ├── go.mod ├── go.sum ├── load.go ├── param.go ├── stack.go ├── terminfo.go └── terminfo_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.cache/ 2 | 3 | /cmd/infocmp/infocmp 4 | /cmd/infocmp/.out/ 5 | 6 | /infocmp 7 | /.out/ 8 | 9 | *.txt 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Anmol Sethi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About terminfo [![GoDoc][1]][2] 2 | 3 | Package `terminfo` provides a pure-Go implementation of reading information 4 | from the terminfo database. 5 | 6 | `terminfo` is meant as a replacement for `ncurses` in simple Go programs. 7 | 8 | ## Installing 9 | 10 | Install in the usual Go way: 11 | 12 | ```sh 13 | $ go get -u github.com/xo/terminfo 14 | ``` 15 | 16 | ## Using 17 | 18 | Please see the [GoDoc API listing][2] for more information on using `terminfo`. 19 | 20 | ```go 21 | // _examples/simple/main.go 22 | package main 23 | 24 | import ( 25 | "bytes" 26 | "fmt" 27 | "log" 28 | "os" 29 | "os/signal" 30 | "strings" 31 | "sync" 32 | "syscall" 33 | 34 | "github.com/xo/terminfo" 35 | ) 36 | 37 | func main() { 38 | //r := rand.New(nil) 39 | 40 | // load terminfo 41 | ti, err := terminfo.LoadFromEnv() 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | // cleanup 47 | defer func() { 48 | err := recover() 49 | termreset(ti) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | }() 54 | 55 | terminit(ti) 56 | termtitle(ti, "simple example!") 57 | termputs(ti, 3, 3, "Ctrl-C to exit") 58 | maxColors := termcolors(ti) 59 | if maxColors > 256 { 60 | maxColors = 256 61 | } 62 | for i := 0; i < maxColors; i++ { 63 | termputs(ti, 5+i/16, 5+i%16, ti.Colorf(i, 0, "█")) 64 | } 65 | 66 | // wait for signal 67 | sigs := make(chan os.Signal, 1) 68 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 69 | <-sigs 70 | } 71 | 72 | // terminit initializes the special CA mode on the terminal, and makes the 73 | // cursor invisible. 74 | func terminit(ti *terminfo.Terminfo) { 75 | buf := new(bytes.Buffer) 76 | // set the cursor invisible 77 | ti.Fprintf(buf, terminfo.CursorInvisible) 78 | // enter special mode 79 | ti.Fprintf(buf, terminfo.EnterCaMode) 80 | // clear the screen 81 | ti.Fprintf(buf, terminfo.ClearScreen) 82 | os.Stdout.Write(buf.Bytes()) 83 | } 84 | 85 | // termreset is the inverse of terminit. 86 | func termreset(ti *terminfo.Terminfo) { 87 | buf := new(bytes.Buffer) 88 | ti.Fprintf(buf, terminfo.ExitCaMode) 89 | ti.Fprintf(buf, terminfo.CursorNormal) 90 | os.Stdout.Write(buf.Bytes()) 91 | } 92 | 93 | // termputs puts a string at row, col, interpolating v. 94 | func termputs(ti *terminfo.Terminfo, row, col int, s string, v ...interface{}) { 95 | buf := new(bytes.Buffer) 96 | ti.Fprintf(buf, terminfo.CursorAddress, row, col) 97 | fmt.Fprintf(buf, s, v...) 98 | os.Stdout.Write(buf.Bytes()) 99 | } 100 | 101 | // sl is the status line terminfo. 102 | var sl *terminfo.Terminfo 103 | 104 | // termtitle sets the window title. 105 | func termtitle(ti *terminfo.Terminfo, s string) { 106 | var once sync.Once 107 | once.Do(func() { 108 | if ti.Has(terminfo.HasStatusLine) { 109 | return 110 | } 111 | // load the sl xterm if terminal is an xterm or has COLORTERM 112 | if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") || os.Getenv("COLORTERM") == "truecolor" { 113 | sl, _ = terminfo.Load("xterm+sl") 114 | } 115 | }) 116 | if sl != nil { 117 | ti = sl 118 | } 119 | if !ti.Has(terminfo.HasStatusLine) { 120 | return 121 | } 122 | buf := new(bytes.Buffer) 123 | ti.Fprintf(buf, terminfo.ToStatusLine) 124 | fmt.Fprint(buf, s) 125 | ti.Fprintf(buf, terminfo.FromStatusLine) 126 | os.Stdout.Write(buf.Bytes()) 127 | } 128 | 129 | // termcolors returns the maximum colors available for the terminal. 130 | func termcolors(ti *terminfo.Terminfo) int { 131 | if colors := ti.Num(terminfo.MaxColors); colors > 0 { 132 | return colors 133 | } 134 | return int(terminfo.ColorLevelBasic) 135 | } 136 | ``` 137 | 138 | [1]: https://godoc.org/github.com/xo/terminfo?status.svg 139 | [2]: https://godoc.org/github.com/xo/terminfo 140 | -------------------------------------------------------------------------------- /_examples/main.go: -------------------------------------------------------------------------------- 1 | // _examples/simple/main.go 2 | package main 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "log" 8 | "os" 9 | "os/signal" 10 | "strings" 11 | "sync" 12 | "syscall" 13 | 14 | "github.com/xo/terminfo" 15 | ) 16 | 17 | func main() { 18 | //r := rand.New(nil) 19 | 20 | // load terminfo 21 | ti, err := terminfo.LoadFromEnv() 22 | if err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | // cleanup 27 | defer func() { 28 | err := recover() 29 | termreset(ti) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | }() 34 | 35 | terminit(ti) 36 | termtitle(ti, "simple example!") 37 | termputs(ti, 3, 3, "Ctrl-C to exit") 38 | maxColors := termcolors(ti) 39 | if maxColors > 256 { 40 | maxColors = 256 41 | } 42 | for i := 0; i < maxColors; i++ { 43 | termputs(ti, 5+i/16, 5+i%16, ti.Colorf(i, 0, "█")) 44 | } 45 | 46 | // wait for signal 47 | sigs := make(chan os.Signal, 1) 48 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 49 | <-sigs 50 | } 51 | 52 | // terminit initializes the special CA mode on the terminal, and makes the 53 | // cursor invisible. 54 | func terminit(ti *terminfo.Terminfo) { 55 | buf := new(bytes.Buffer) 56 | // set the cursor invisible 57 | ti.Fprintf(buf, terminfo.CursorInvisible) 58 | // enter special mode 59 | ti.Fprintf(buf, terminfo.EnterCaMode) 60 | // clear the screen 61 | ti.Fprintf(buf, terminfo.ClearScreen) 62 | os.Stdout.Write(buf.Bytes()) 63 | } 64 | 65 | // termreset is the inverse of terminit. 66 | func termreset(ti *terminfo.Terminfo) { 67 | buf := new(bytes.Buffer) 68 | ti.Fprintf(buf, terminfo.ExitCaMode) 69 | ti.Fprintf(buf, terminfo.CursorNormal) 70 | os.Stdout.Write(buf.Bytes()) 71 | } 72 | 73 | // termputs puts a string at row, col, interpolating v. 74 | func termputs(ti *terminfo.Terminfo, row, col int, s string, v ...interface{}) { 75 | buf := new(bytes.Buffer) 76 | ti.Fprintf(buf, terminfo.CursorAddress, row, col) 77 | fmt.Fprintf(buf, s, v...) 78 | os.Stdout.Write(buf.Bytes()) 79 | } 80 | 81 | // sl is the status line terminfo. 82 | var sl *terminfo.Terminfo 83 | 84 | // termtitle sets the window title. 85 | func termtitle(ti *terminfo.Terminfo, s string) { 86 | var once sync.Once 87 | once.Do(func() { 88 | if ti.Has(terminfo.HasStatusLine) { 89 | return 90 | } 91 | // load the sl xterm if terminal is an xterm or has COLORTERM 92 | if strings.Contains(strings.ToLower(os.Getenv("TERM")), "xterm") || os.Getenv("COLORTERM") == "truecolor" { 93 | sl, _ = terminfo.Load("xterm+sl") 94 | } 95 | }) 96 | if sl != nil { 97 | ti = sl 98 | } 99 | if !ti.Has(terminfo.HasStatusLine) { 100 | return 101 | } 102 | buf := new(bytes.Buffer) 103 | ti.Fprintf(buf, terminfo.ToStatusLine) 104 | fmt.Fprint(buf, s) 105 | ti.Fprintf(buf, terminfo.FromStatusLine) 106 | os.Stdout.Write(buf.Bytes()) 107 | } 108 | 109 | // termcolors returns the maximum colors available for the terminal. 110 | func termcolors(ti *terminfo.Terminfo) int { 111 | if colors := ti.Num(terminfo.MaxColors); colors > 0 { 112 | return colors 113 | } 114 | return int(terminfo.ColorLevelBasic) 115 | } 116 | -------------------------------------------------------------------------------- /caps.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | // BoolCapName returns the bool capability name. 4 | func BoolCapName(i int) string { 5 | return boolCapNames[2*i] 6 | } 7 | 8 | // BoolCapNameShort returns the short bool capability name. 9 | func BoolCapNameShort(i int) string { 10 | return boolCapNames[2*i+1] 11 | } 12 | 13 | // NumCapName returns the num capability name. 14 | func NumCapName(i int) string { 15 | return numCapNames[2*i] 16 | } 17 | 18 | // NumCapNameShort returns the short num capability name. 19 | func NumCapNameShort(i int) string { 20 | return numCapNames[2*i+1] 21 | } 22 | 23 | // StringCapName returns the string capability name. 24 | func StringCapName(i int) string { 25 | return stringCapNames[2*i] 26 | } 27 | 28 | // StringCapNameShort returns the short string capability name. 29 | func StringCapNameShort(i int) string { 30 | return stringCapNames[2*i+1] 31 | } 32 | -------------------------------------------------------------------------------- /capvals.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | // Code generated by gen.go. DO NOT EDIT. 4 | // Bool capabilities. 5 | const ( 6 | // The AutoLeftMargin [auto_left_margin, bw] bool capability indicates cub1 wraps from column 0 to last column. 7 | AutoLeftMargin = iota 8 | // The AutoRightMargin [auto_right_margin, am] bool capability indicates terminal has automatic margins. 9 | AutoRightMargin 10 | // The NoEscCtlc [no_esc_ctlc, xsb] bool capability indicates beehive (f1=escape, f2=ctrl C). 11 | NoEscCtlc 12 | // The CeolStandoutGlitch [ceol_standout_glitch, xhp] bool capability indicates standout not erased by overwriting (hp). 13 | CeolStandoutGlitch 14 | // The EatNewlineGlitch [eat_newline_glitch, xenl] bool capability indicates newline ignored after 80 cols (concept). 15 | EatNewlineGlitch 16 | // The EraseOverstrike [erase_overstrike, eo] bool capability indicates can erase overstrikes with a blank. 17 | EraseOverstrike 18 | // The GenericType [generic_type, gn] bool capability indicates generic line type. 19 | GenericType 20 | // The HardCopy [hard_copy, hc] bool capability indicates hardcopy terminal. 21 | HardCopy 22 | // The HasMetaKey [has_meta_key, km] bool capability indicates Has a meta key (i.e., sets 8th-bit). 23 | HasMetaKey 24 | // The HasStatusLine [has_status_line, hs] bool capability indicates has extra status line. 25 | HasStatusLine 26 | // The InsertNullGlitch [insert_null_glitch, in] bool capability indicates insert mode distinguishes nulls. 27 | InsertNullGlitch 28 | // The MemoryAbove [memory_above, da] bool capability indicates display may be retained above the screen. 29 | MemoryAbove 30 | // The MemoryBelow [memory_below, db] bool capability indicates display may be retained below the screen. 31 | MemoryBelow 32 | // The MoveInsertMode [move_insert_mode, mir] bool capability indicates safe to move while in insert mode. 33 | MoveInsertMode 34 | // The MoveStandoutMode [move_standout_mode, msgr] bool capability indicates safe to move while in standout mode. 35 | MoveStandoutMode 36 | // The OverStrike [over_strike, os] bool capability indicates terminal can overstrike. 37 | OverStrike 38 | // The StatusLineEscOk [status_line_esc_ok, eslok] bool capability indicates escape can be used on the status line. 39 | StatusLineEscOk 40 | // The DestTabsMagicSmso [dest_tabs_magic_smso, xt] bool capability indicates tabs destructive, magic so char (t1061). 41 | DestTabsMagicSmso 42 | // The TildeGlitch [tilde_glitch, hz] bool capability indicates cannot print ~'s (Hazeltine). 43 | TildeGlitch 44 | // The TransparentUnderline [transparent_underline, ul] bool capability indicates underline character overstrikes. 45 | TransparentUnderline 46 | // The XonXoff [xon_xoff, xon] bool capability indicates terminal uses xon/xoff handshaking. 47 | XonXoff 48 | // The NeedsXonXoff [needs_xon_xoff, nxon] bool capability indicates padding will not work, xon/xoff required. 49 | NeedsXonXoff 50 | // The PrtrSilent [prtr_silent, mc5i] bool capability indicates printer will not echo on screen. 51 | PrtrSilent 52 | // The HardCursor [hard_cursor, chts] bool capability indicates cursor is hard to see. 53 | HardCursor 54 | // The NonRevRmcup [non_rev_rmcup, nrrmc] bool capability indicates smcup does not reverse rmcup. 55 | NonRevRmcup 56 | // The NoPadChar [no_pad_char, npc] bool capability indicates pad character does not exist. 57 | NoPadChar 58 | // The NonDestScrollRegion [non_dest_scroll_region, ndscr] bool capability indicates scrolling region is non-destructive. 59 | NonDestScrollRegion 60 | // The CanChange [can_change, ccc] bool capability indicates terminal can re-define existing colors. 61 | CanChange 62 | // The BackColorErase [back_color_erase, bce] bool capability indicates screen erased with background color. 63 | BackColorErase 64 | // The HueLightnessSaturation [hue_lightness_saturation, hls] bool capability indicates terminal uses only HLS color notation (Tektronix). 65 | HueLightnessSaturation 66 | // The ColAddrGlitch [col_addr_glitch, xhpa] bool capability indicates only positive motion for hpa/mhpa caps. 67 | ColAddrGlitch 68 | // The CrCancelsMicroMode [cr_cancels_micro_mode, crxm] bool capability indicates using cr turns off micro mode. 69 | CrCancelsMicroMode 70 | // The HasPrintWheel [has_print_wheel, daisy] bool capability indicates printer needs operator to change character set. 71 | HasPrintWheel 72 | // The RowAddrGlitch [row_addr_glitch, xvpa] bool capability indicates only positive motion for vpa/mvpa caps. 73 | RowAddrGlitch 74 | // The SemiAutoRightMargin [semi_auto_right_margin, sam] bool capability indicates printing in last column causes cr. 75 | SemiAutoRightMargin 76 | // The CpiChangesRes [cpi_changes_res, cpix] bool capability indicates changing character pitch changes resolution. 77 | CpiChangesRes 78 | // The LpiChangesRes [lpi_changes_res, lpix] bool capability indicates changing line pitch changes resolution. 79 | LpiChangesRes 80 | // The BackspacesWithBs [backspaces_with_bs, OTbs] bool capability indicates uses ^H to move left. 81 | BackspacesWithBs 82 | // The CrtNoScrolling [crt_no_scrolling, OTns] bool capability indicates crt cannot scroll. 83 | CrtNoScrolling 84 | // The NoCorrectlyWorkingCr [no_correctly_working_cr, OTnc] bool capability indicates no way to go to start of line. 85 | NoCorrectlyWorkingCr 86 | // The GnuHasMetaKey [gnu_has_meta_key, OTMT] bool capability indicates has meta key. 87 | GnuHasMetaKey 88 | // The LinefeedIsNewline [linefeed_is_newline, OTNL] bool capability indicates move down with \n. 89 | LinefeedIsNewline 90 | // The HasHardwareTabs [has_hardware_tabs, OTpt] bool capability indicates has 8-char tabs invoked with ^I. 91 | HasHardwareTabs 92 | // The ReturnDoesClrEol [return_does_clr_eol, OTxr] bool capability indicates return clears the line. 93 | ReturnDoesClrEol 94 | ) 95 | 96 | // Num capabilities. 97 | const ( 98 | // The Columns [columns, cols] num capability is number of columns in a line. 99 | Columns = iota 100 | // The InitTabs [init_tabs, it] num capability is tabs initially every # spaces. 101 | InitTabs 102 | // The Lines [lines, lines] num capability is number of lines on screen or page. 103 | Lines 104 | // The LinesOfMemory [lines_of_memory, lm] num capability is lines of memory if > line. 0 means varies. 105 | LinesOfMemory 106 | // The MagicCookieGlitch [magic_cookie_glitch, xmc] num capability is number of blank characters left by smso or rmso. 107 | MagicCookieGlitch 108 | // The PaddingBaudRate [padding_baud_rate, pb] num capability is lowest baud rate where padding needed. 109 | PaddingBaudRate 110 | // The VirtualTerminal [virtual_terminal, vt] num capability is virtual terminal number (CB/unix). 111 | VirtualTerminal 112 | // The WidthStatusLine [width_status_line, wsl] num capability is number of columns in status line. 113 | WidthStatusLine 114 | // The NumLabels [num_labels, nlab] num capability is number of labels on screen. 115 | NumLabels 116 | // The LabelHeight [label_height, lh] num capability is rows in each label. 117 | LabelHeight 118 | // The LabelWidth [label_width, lw] num capability is columns in each label. 119 | LabelWidth 120 | // The MaxAttributes [max_attributes, ma] num capability is maximum combined attributes terminal can handle. 121 | MaxAttributes 122 | // The MaximumWindows [maximum_windows, wnum] num capability is maximum number of definable windows. 123 | MaximumWindows 124 | // The MaxColors [max_colors, colors] num capability is maximum number of colors on screen. 125 | MaxColors 126 | // The MaxPairs [max_pairs, pairs] num capability is maximum number of color-pairs on the screen. 127 | MaxPairs 128 | // The NoColorVideo [no_color_video, ncv] num capability is video attributes that cannot be used with colors. 129 | NoColorVideo 130 | // The BufferCapacity [buffer_capacity, bufsz] num capability is numbers of bytes buffered before printing. 131 | BufferCapacity 132 | // The DotVertSpacing [dot_vert_spacing, spinv] num capability is spacing of pins vertically in pins per inch. 133 | DotVertSpacing 134 | // The DotHorzSpacing [dot_horz_spacing, spinh] num capability is spacing of dots horizontally in dots per inch. 135 | DotHorzSpacing 136 | // The MaxMicroAddress [max_micro_address, maddr] num capability is maximum value in micro_..._address. 137 | MaxMicroAddress 138 | // The MaxMicroJump [max_micro_jump, mjump] num capability is maximum value in parm_..._micro. 139 | MaxMicroJump 140 | // The MicroColSize [micro_col_size, mcs] num capability is character step size when in micro mode. 141 | MicroColSize 142 | // The MicroLineSize [micro_line_size, mls] num capability is line step size when in micro mode. 143 | MicroLineSize 144 | // The NumberOfPins [number_of_pins, npins] num capability is numbers of pins in print-head. 145 | NumberOfPins 146 | // The OutputResChar [output_res_char, orc] num capability is horizontal resolution in units per line. 147 | OutputResChar 148 | // The OutputResLine [output_res_line, orl] num capability is vertical resolution in units per line. 149 | OutputResLine 150 | // The OutputResHorzInch [output_res_horz_inch, orhi] num capability is horizontal resolution in units per inch. 151 | OutputResHorzInch 152 | // The OutputResVertInch [output_res_vert_inch, orvi] num capability is vertical resolution in units per inch. 153 | OutputResVertInch 154 | // The PrintRate [print_rate, cps] num capability is print rate in characters per second. 155 | PrintRate 156 | // The WideCharSize [wide_char_size, widcs] num capability is character step size when in double wide mode. 157 | WideCharSize 158 | // The Buttons [buttons, btns] num capability is number of buttons on mouse. 159 | Buttons 160 | // The BitImageEntwining [bit_image_entwining, bitwin] num capability is number of passes for each bit-image row. 161 | BitImageEntwining 162 | // The BitImageType [bit_image_type, bitype] num capability is type of bit-image device. 163 | BitImageType 164 | // The MagicCookieGlitchUl [magic_cookie_glitch_ul, OTug] num capability is number of blanks left by ul. 165 | MagicCookieGlitchUl 166 | // The CarriageReturnDelay [carriage_return_delay, OTdC] num capability is pad needed for CR. 167 | CarriageReturnDelay 168 | // The NewLineDelay [new_line_delay, OTdN] num capability is pad needed for LF. 169 | NewLineDelay 170 | // The BackspaceDelay [backspace_delay, OTdB] num capability is padding required for ^H. 171 | BackspaceDelay 172 | // The HorizontalTabDelay [horizontal_tab_delay, OTdT] num capability is padding required for ^I. 173 | HorizontalTabDelay 174 | // The NumberOfFunctionKeys [number_of_function_keys, OTkn] num capability is count of function keys. 175 | NumberOfFunctionKeys 176 | ) 177 | 178 | // String capabilities. 179 | const ( 180 | // The BackTab [back_tab, cbt] string capability is the back tab (P). 181 | BackTab = iota 182 | // The Bell [bell, bel] string capability is the audible signal (bell) (P). 183 | Bell 184 | // The CarriageReturn [carriage_return, cr] string capability is the carriage return (P*) (P*). 185 | CarriageReturn 186 | // The ChangeScrollRegion [change_scroll_region, csr] string capability is the change region to line #1 to line #2 (P). 187 | ChangeScrollRegion 188 | // The ClearAllTabs [clear_all_tabs, tbc] string capability is the clear all tab stops (P). 189 | ClearAllTabs 190 | // The ClearScreen [clear_screen, clear] string capability is the clear screen and home cursor (P*). 191 | ClearScreen 192 | // The ClrEol [clr_eol, el] string capability is the clear to end of line (P). 193 | ClrEol 194 | // The ClrEos [clr_eos, ed] string capability is the clear to end of screen (P*). 195 | ClrEos 196 | // The ColumnAddress [column_address, hpa] string capability is the horizontal position #1, absolute (P). 197 | ColumnAddress 198 | // The CommandCharacter [command_character, cmdch] string capability is the terminal settable cmd character in prototype !?. 199 | CommandCharacter 200 | // The CursorAddress [cursor_address, cup] string capability is the move to row #1 columns #2. 201 | CursorAddress 202 | // The CursorDown [cursor_down, cud1] string capability is the down one line. 203 | CursorDown 204 | // The CursorHome [cursor_home, home] string capability is the home cursor (if no cup). 205 | CursorHome 206 | // The CursorInvisible [cursor_invisible, civis] string capability is the make cursor invisible. 207 | CursorInvisible 208 | // The CursorLeft [cursor_left, cub1] string capability is the move left one space. 209 | CursorLeft 210 | // The CursorMemAddress [cursor_mem_address, mrcup] string capability is the memory relative cursor addressing, move to row #1 columns #2. 211 | CursorMemAddress 212 | // The CursorNormal [cursor_normal, cnorm] string capability is the make cursor appear normal (undo civis/cvvis). 213 | CursorNormal 214 | // The CursorRight [cursor_right, cuf1] string capability is the non-destructive space (move right one space). 215 | CursorRight 216 | // The CursorToLl [cursor_to_ll, ll] string capability is the last line, first column (if no cup). 217 | CursorToLl 218 | // The CursorUp [cursor_up, cuu1] string capability is the up one line. 219 | CursorUp 220 | // The CursorVisible [cursor_visible, cvvis] string capability is the make cursor very visible. 221 | CursorVisible 222 | // The DeleteCharacter [delete_character, dch1] string capability is the delete character (P*). 223 | DeleteCharacter 224 | // The DeleteLine [delete_line, dl1] string capability is the delete line (P*). 225 | DeleteLine 226 | // The DisStatusLine [dis_status_line, dsl] string capability is the disable status line. 227 | DisStatusLine 228 | // The DownHalfLine [down_half_line, hd] string capability is the half a line down. 229 | DownHalfLine 230 | // The EnterAltCharsetMode [enter_alt_charset_mode, smacs] string capability is the start alternate character set (P). 231 | EnterAltCharsetMode 232 | // The EnterBlinkMode [enter_blink_mode, blink] string capability is the turn on blinking. 233 | EnterBlinkMode 234 | // The EnterBoldMode [enter_bold_mode, bold] string capability is the turn on bold (extra bright) mode. 235 | EnterBoldMode 236 | // The EnterCaMode [enter_ca_mode, smcup] string capability is the string to start programs using cup. 237 | EnterCaMode 238 | // The EnterDeleteMode [enter_delete_mode, smdc] string capability is the enter delete mode. 239 | EnterDeleteMode 240 | // The EnterDimMode [enter_dim_mode, dim] string capability is the turn on half-bright mode. 241 | EnterDimMode 242 | // The EnterInsertMode [enter_insert_mode, smir] string capability is the enter insert mode. 243 | EnterInsertMode 244 | // The EnterSecureMode [enter_secure_mode, invis] string capability is the turn on blank mode (characters invisible). 245 | EnterSecureMode 246 | // The EnterProtectedMode [enter_protected_mode, prot] string capability is the turn on protected mode. 247 | EnterProtectedMode 248 | // The EnterReverseMode [enter_reverse_mode, rev] string capability is the turn on reverse video mode. 249 | EnterReverseMode 250 | // The EnterStandoutMode [enter_standout_mode, smso] string capability is the begin standout mode. 251 | EnterStandoutMode 252 | // The EnterUnderlineMode [enter_underline_mode, smul] string capability is the begin underline mode. 253 | EnterUnderlineMode 254 | // The EraseChars [erase_chars, ech] string capability is the erase #1 characters (P). 255 | EraseChars 256 | // The ExitAltCharsetMode [exit_alt_charset_mode, rmacs] string capability is the end alternate character set (P). 257 | ExitAltCharsetMode 258 | // The ExitAttributeMode [exit_attribute_mode, sgr0] string capability is the turn off all attributes. 259 | ExitAttributeMode 260 | // The ExitCaMode [exit_ca_mode, rmcup] string capability is the strings to end programs using cup. 261 | ExitCaMode 262 | // The ExitDeleteMode [exit_delete_mode, rmdc] string capability is the end delete mode. 263 | ExitDeleteMode 264 | // The ExitInsertMode [exit_insert_mode, rmir] string capability is the exit insert mode. 265 | ExitInsertMode 266 | // The ExitStandoutMode [exit_standout_mode, rmso] string capability is the exit standout mode. 267 | ExitStandoutMode 268 | // The ExitUnderlineMode [exit_underline_mode, rmul] string capability is the exit underline mode. 269 | ExitUnderlineMode 270 | // The FlashScreen [flash_screen, flash] string capability is the visible bell (may not move cursor). 271 | FlashScreen 272 | // The FormFeed [form_feed, ff] string capability is the hardcopy terminal page eject (P*). 273 | FormFeed 274 | // The FromStatusLine [from_status_line, fsl] string capability is the return from status line. 275 | FromStatusLine 276 | // The Init1string [init_1string, is1] string capability is the initialization string. 277 | Init1string 278 | // The Init2string [init_2string, is2] string capability is the initialization string. 279 | Init2string 280 | // The Init3string [init_3string, is3] string capability is the initialization string. 281 | Init3string 282 | // The InitFile [init_file, if] string capability is the name of initialization file. 283 | InitFile 284 | // The InsertCharacter [insert_character, ich1] string capability is the insert character (P). 285 | InsertCharacter 286 | // The InsertLine [insert_line, il1] string capability is the insert line (P*). 287 | InsertLine 288 | // The InsertPadding [insert_padding, ip] string capability is the insert padding after inserted character. 289 | InsertPadding 290 | // The KeyBackspace [key_backspace, kbs] string capability is the backspace key. 291 | KeyBackspace 292 | // The KeyCatab [key_catab, ktbc] string capability is the clear-all-tabs key. 293 | KeyCatab 294 | // The KeyClear [key_clear, kclr] string capability is the clear-screen or erase key. 295 | KeyClear 296 | // The KeyCtab [key_ctab, kctab] string capability is the clear-tab key. 297 | KeyCtab 298 | // The KeyDc [key_dc, kdch1] string capability is the delete-character key. 299 | KeyDc 300 | // The KeyDl [key_dl, kdl1] string capability is the delete-line key. 301 | KeyDl 302 | // The KeyDown [key_down, kcud1] string capability is the down-arrow key. 303 | KeyDown 304 | // The KeyEic [key_eic, krmir] string capability is the sent by rmir or smir in insert mode. 305 | KeyEic 306 | // The KeyEol [key_eol, kel] string capability is the clear-to-end-of-line key. 307 | KeyEol 308 | // The KeyEos [key_eos, ked] string capability is the clear-to-end-of-screen key. 309 | KeyEos 310 | // The KeyF0 [key_f0, kf0] string capability is the F0 function key. 311 | KeyF0 312 | // The KeyF1 [key_f1, kf1] string capability is the F1 function key. 313 | KeyF1 314 | // The KeyF10 [key_f10, kf10] string capability is the F10 function key. 315 | KeyF10 316 | // The KeyF2 [key_f2, kf2] string capability is the F2 function key. 317 | KeyF2 318 | // The KeyF3 [key_f3, kf3] string capability is the F3 function key. 319 | KeyF3 320 | // The KeyF4 [key_f4, kf4] string capability is the F4 function key. 321 | KeyF4 322 | // The KeyF5 [key_f5, kf5] string capability is the F5 function key. 323 | KeyF5 324 | // The KeyF6 [key_f6, kf6] string capability is the F6 function key. 325 | KeyF6 326 | // The KeyF7 [key_f7, kf7] string capability is the F7 function key. 327 | KeyF7 328 | // The KeyF8 [key_f8, kf8] string capability is the F8 function key. 329 | KeyF8 330 | // The KeyF9 [key_f9, kf9] string capability is the F9 function key. 331 | KeyF9 332 | // The KeyHome [key_home, khome] string capability is the home key. 333 | KeyHome 334 | // The KeyIc [key_ic, kich1] string capability is the insert-character key. 335 | KeyIc 336 | // The KeyIl [key_il, kil1] string capability is the insert-line key. 337 | KeyIl 338 | // The KeyLeft [key_left, kcub1] string capability is the left-arrow key. 339 | KeyLeft 340 | // The KeyLl [key_ll, kll] string capability is the lower-left key (home down). 341 | KeyLl 342 | // The KeyNpage [key_npage, knp] string capability is the next-page key. 343 | KeyNpage 344 | // The KeyPpage [key_ppage, kpp] string capability is the previous-page key. 345 | KeyPpage 346 | // The KeyRight [key_right, kcuf1] string capability is the right-arrow key. 347 | KeyRight 348 | // The KeySf [key_sf, kind] string capability is the scroll-forward key. 349 | KeySf 350 | // The KeySr [key_sr, kri] string capability is the scroll-backward key. 351 | KeySr 352 | // The KeyStab [key_stab, khts] string capability is the set-tab key. 353 | KeyStab 354 | // The KeyUp [key_up, kcuu1] string capability is the up-arrow key. 355 | KeyUp 356 | // The KeypadLocal [keypad_local, rmkx] string capability is the leave 'keyboard_transmit' mode. 357 | KeypadLocal 358 | // The KeypadXmit [keypad_xmit, smkx] string capability is the enter 'keyboard_transmit' mode. 359 | KeypadXmit 360 | // The LabF0 [lab_f0, lf0] string capability is the label on function key f0 if not f0. 361 | LabF0 362 | // The LabF1 [lab_f1, lf1] string capability is the label on function key f1 if not f1. 363 | LabF1 364 | // The LabF10 [lab_f10, lf10] string capability is the label on function key f10 if not f10. 365 | LabF10 366 | // The LabF2 [lab_f2, lf2] string capability is the label on function key f2 if not f2. 367 | LabF2 368 | // The LabF3 [lab_f3, lf3] string capability is the label on function key f3 if not f3. 369 | LabF3 370 | // The LabF4 [lab_f4, lf4] string capability is the label on function key f4 if not f4. 371 | LabF4 372 | // The LabF5 [lab_f5, lf5] string capability is the label on function key f5 if not f5. 373 | LabF5 374 | // The LabF6 [lab_f6, lf6] string capability is the label on function key f6 if not f6. 375 | LabF6 376 | // The LabF7 [lab_f7, lf7] string capability is the label on function key f7 if not f7. 377 | LabF7 378 | // The LabF8 [lab_f8, lf8] string capability is the label on function key f8 if not f8. 379 | LabF8 380 | // The LabF9 [lab_f9, lf9] string capability is the label on function key f9 if not f9. 381 | LabF9 382 | // The MetaOff [meta_off, rmm] string capability is the turn off meta mode. 383 | MetaOff 384 | // The MetaOn [meta_on, smm] string capability is the turn on meta mode (8th-bit on). 385 | MetaOn 386 | // The Newline [newline, nel] string capability is the newline (behave like cr followed by lf). 387 | Newline 388 | // The PadChar [pad_char, pad] string capability is the padding char (instead of null). 389 | PadChar 390 | // The ParmDch [parm_dch, dch] string capability is the delete #1 characters (P*). 391 | ParmDch 392 | // The ParmDeleteLine [parm_delete_line, dl] string capability is the delete #1 lines (P*). 393 | ParmDeleteLine 394 | // The ParmDownCursor [parm_down_cursor, cud] string capability is the down #1 lines (P*). 395 | ParmDownCursor 396 | // The ParmIch [parm_ich, ich] string capability is the insert #1 characters (P*). 397 | ParmIch 398 | // The ParmIndex [parm_index, indn] string capability is the scroll forward #1 lines (P). 399 | ParmIndex 400 | // The ParmInsertLine [parm_insert_line, il] string capability is the insert #1 lines (P*). 401 | ParmInsertLine 402 | // The ParmLeftCursor [parm_left_cursor, cub] string capability is the move #1 characters to the left (P). 403 | ParmLeftCursor 404 | // The ParmRightCursor [parm_right_cursor, cuf] string capability is the move #1 characters to the right (P*). 405 | ParmRightCursor 406 | // The ParmRindex [parm_rindex, rin] string capability is the scroll back #1 lines (P). 407 | ParmRindex 408 | // The ParmUpCursor [parm_up_cursor, cuu] string capability is the up #1 lines (P*). 409 | ParmUpCursor 410 | // The PkeyKey [pkey_key, pfkey] string capability is the program function key #1 to type string #2. 411 | PkeyKey 412 | // The PkeyLocal [pkey_local, pfloc] string capability is the program function key #1 to execute string #2. 413 | PkeyLocal 414 | // The PkeyXmit [pkey_xmit, pfx] string capability is the program function key #1 to transmit string #2. 415 | PkeyXmit 416 | // The PrintScreen [print_screen, mc0] string capability is the print contents of screen. 417 | PrintScreen 418 | // The PrtrOff [prtr_off, mc4] string capability is the turn off printer. 419 | PrtrOff 420 | // The PrtrOn [prtr_on, mc5] string capability is the turn on printer. 421 | PrtrOn 422 | // The RepeatChar [repeat_char, rep] string capability is the repeat char #1 #2 times (P*). 423 | RepeatChar 424 | // The Reset1string [reset_1string, rs1] string capability is the reset string. 425 | Reset1string 426 | // The Reset2string [reset_2string, rs2] string capability is the reset string. 427 | Reset2string 428 | // The Reset3string [reset_3string, rs3] string capability is the reset string. 429 | Reset3string 430 | // The ResetFile [reset_file, rf] string capability is the name of reset file. 431 | ResetFile 432 | // The RestoreCursor [restore_cursor, rc] string capability is the restore cursor to position of last save_cursor. 433 | RestoreCursor 434 | // The RowAddress [row_address, vpa] string capability is the vertical position #1 absolute (P). 435 | RowAddress 436 | // The SaveCursor [save_cursor, sc] string capability is the save current cursor position (P). 437 | SaveCursor 438 | // The ScrollForward [scroll_forward, ind] string capability is the scroll text up (P). 439 | ScrollForward 440 | // The ScrollReverse [scroll_reverse, ri] string capability is the scroll text down (P). 441 | ScrollReverse 442 | // The SetAttributes [set_attributes, sgr] string capability is the define video attributes #1-#9 (PG9). 443 | SetAttributes 444 | // The SetTab [set_tab, hts] string capability is the set a tab in every row, current columns. 445 | SetTab 446 | // The SetWindow [set_window, wind] string capability is the current window is lines #1-#2 cols #3-#4. 447 | SetWindow 448 | // The Tab [tab, ht] string capability is the tab to next 8-space hardware tab stop. 449 | Tab 450 | // The ToStatusLine [to_status_line, tsl] string capability is the move to status line, column #1. 451 | ToStatusLine 452 | // The UnderlineChar [underline_char, uc] string capability is the underline char and move past it. 453 | UnderlineChar 454 | // The UpHalfLine [up_half_line, hu] string capability is the half a line up. 455 | UpHalfLine 456 | // The InitProg [init_prog, iprog] string capability is the path name of program for initialization. 457 | InitProg 458 | // The KeyA1 [key_a1, ka1] string capability is the upper left of keypad. 459 | KeyA1 460 | // The KeyA3 [key_a3, ka3] string capability is the upper right of keypad. 461 | KeyA3 462 | // The KeyB2 [key_b2, kb2] string capability is the center of keypad. 463 | KeyB2 464 | // The KeyC1 [key_c1, kc1] string capability is the lower left of keypad. 465 | KeyC1 466 | // The KeyC3 [key_c3, kc3] string capability is the lower right of keypad. 467 | KeyC3 468 | // The PrtrNon [prtr_non, mc5p] string capability is the turn on printer for #1 bytes. 469 | PrtrNon 470 | // The CharPadding [char_padding, rmp] string capability is the like ip but when in insert mode. 471 | CharPadding 472 | // The AcsChars [acs_chars, acsc] string capability is the graphics charset pairs, based on vt100. 473 | AcsChars 474 | // The PlabNorm [plab_norm, pln] string capability is the program label #1 to show string #2. 475 | PlabNorm 476 | // The KeyBtab [key_btab, kcbt] string capability is the back-tab key. 477 | KeyBtab 478 | // The EnterXonMode [enter_xon_mode, smxon] string capability is the turn on xon/xoff handshaking. 479 | EnterXonMode 480 | // The ExitXonMode [exit_xon_mode, rmxon] string capability is the turn off xon/xoff handshaking. 481 | ExitXonMode 482 | // The EnterAmMode [enter_am_mode, smam] string capability is the turn on automatic margins. 483 | EnterAmMode 484 | // The ExitAmMode [exit_am_mode, rmam] string capability is the turn off automatic margins. 485 | ExitAmMode 486 | // The XonCharacter [xon_character, xonc] string capability is the XON character. 487 | XonCharacter 488 | // The XoffCharacter [xoff_character, xoffc] string capability is the XOFF character. 489 | XoffCharacter 490 | // The EnaAcs [ena_acs, enacs] string capability is the enable alternate char set. 491 | EnaAcs 492 | // The LabelOn [label_on, smln] string capability is the turn on soft labels. 493 | LabelOn 494 | // The LabelOff [label_off, rmln] string capability is the turn off soft labels. 495 | LabelOff 496 | // The KeyBeg [key_beg, kbeg] string capability is the begin key. 497 | KeyBeg 498 | // The KeyCancel [key_cancel, kcan] string capability is the cancel key. 499 | KeyCancel 500 | // The KeyClose [key_close, kclo] string capability is the close key. 501 | KeyClose 502 | // The KeyCommand [key_command, kcmd] string capability is the command key. 503 | KeyCommand 504 | // The KeyCopy [key_copy, kcpy] string capability is the copy key. 505 | KeyCopy 506 | // The KeyCreate [key_create, kcrt] string capability is the create key. 507 | KeyCreate 508 | // The KeyEnd [key_end, kend] string capability is the end key. 509 | KeyEnd 510 | // The KeyEnter [key_enter, kent] string capability is the enter/send key. 511 | KeyEnter 512 | // The KeyExit [key_exit, kext] string capability is the exit key. 513 | KeyExit 514 | // The KeyFind [key_find, kfnd] string capability is the find key. 515 | KeyFind 516 | // The KeyHelp [key_help, khlp] string capability is the help key. 517 | KeyHelp 518 | // The KeyMark [key_mark, kmrk] string capability is the mark key. 519 | KeyMark 520 | // The KeyMessage [key_message, kmsg] string capability is the message key. 521 | KeyMessage 522 | // The KeyMove [key_move, kmov] string capability is the move key. 523 | KeyMove 524 | // The KeyNext [key_next, knxt] string capability is the next key. 525 | KeyNext 526 | // The KeyOpen [key_open, kopn] string capability is the open key. 527 | KeyOpen 528 | // The KeyOptions [key_options, kopt] string capability is the options key. 529 | KeyOptions 530 | // The KeyPrevious [key_previous, kprv] string capability is the previous key. 531 | KeyPrevious 532 | // The KeyPrint [key_print, kprt] string capability is the print key. 533 | KeyPrint 534 | // The KeyRedo [key_redo, krdo] string capability is the redo key. 535 | KeyRedo 536 | // The KeyReference [key_reference, kref] string capability is the reference key. 537 | KeyReference 538 | // The KeyRefresh [key_refresh, krfr] string capability is the refresh key. 539 | KeyRefresh 540 | // The KeyReplace [key_replace, krpl] string capability is the replace key. 541 | KeyReplace 542 | // The KeyRestart [key_restart, krst] string capability is the restart key. 543 | KeyRestart 544 | // The KeyResume [key_resume, kres] string capability is the resume key. 545 | KeyResume 546 | // The KeySave [key_save, ksav] string capability is the save key. 547 | KeySave 548 | // The KeySuspend [key_suspend, kspd] string capability is the suspend key. 549 | KeySuspend 550 | // The KeyUndo [key_undo, kund] string capability is the undo key. 551 | KeyUndo 552 | // The KeySbeg [key_sbeg, kBEG] string capability is the shifted begin key. 553 | KeySbeg 554 | // The KeyScancel [key_scancel, kCAN] string capability is the shifted cancel key. 555 | KeyScancel 556 | // The KeyScommand [key_scommand, kCMD] string capability is the shifted command key. 557 | KeyScommand 558 | // The KeyScopy [key_scopy, kCPY] string capability is the shifted copy key. 559 | KeyScopy 560 | // The KeyScreate [key_screate, kCRT] string capability is the shifted create key. 561 | KeyScreate 562 | // The KeySdc [key_sdc, kDC] string capability is the shifted delete-character key. 563 | KeySdc 564 | // The KeySdl [key_sdl, kDL] string capability is the shifted delete-line key. 565 | KeySdl 566 | // The KeySelect [key_select, kslt] string capability is the select key. 567 | KeySelect 568 | // The KeySend [key_send, kEND] string capability is the shifted end key. 569 | KeySend 570 | // The KeySeol [key_seol, kEOL] string capability is the shifted clear-to-end-of-line key. 571 | KeySeol 572 | // The KeySexit [key_sexit, kEXT] string capability is the shifted exit key. 573 | KeySexit 574 | // The KeySfind [key_sfind, kFND] string capability is the shifted find key. 575 | KeySfind 576 | // The KeyShelp [key_shelp, kHLP] string capability is the shifted help key. 577 | KeyShelp 578 | // The KeyShome [key_shome, kHOM] string capability is the shifted home key. 579 | KeyShome 580 | // The KeySic [key_sic, kIC] string capability is the shifted insert-character key. 581 | KeySic 582 | // The KeySleft [key_sleft, kLFT] string capability is the shifted left-arrow key. 583 | KeySleft 584 | // The KeySmessage [key_smessage, kMSG] string capability is the shifted message key. 585 | KeySmessage 586 | // The KeySmove [key_smove, kMOV] string capability is the shifted move key. 587 | KeySmove 588 | // The KeySnext [key_snext, kNXT] string capability is the shifted next key. 589 | KeySnext 590 | // The KeySoptions [key_soptions, kOPT] string capability is the shifted options key. 591 | KeySoptions 592 | // The KeySprevious [key_sprevious, kPRV] string capability is the shifted previous key. 593 | KeySprevious 594 | // The KeySprint [key_sprint, kPRT] string capability is the shifted print key. 595 | KeySprint 596 | // The KeySredo [key_sredo, kRDO] string capability is the shifted redo key. 597 | KeySredo 598 | // The KeySreplace [key_sreplace, kRPL] string capability is the shifted replace key. 599 | KeySreplace 600 | // The KeySright [key_sright, kRIT] string capability is the shifted right-arrow key. 601 | KeySright 602 | // The KeySrsume [key_srsume, kRES] string capability is the shifted resume key. 603 | KeySrsume 604 | // The KeySsave [key_ssave, kSAV] string capability is the shifted save key. 605 | KeySsave 606 | // The KeySsuspend [key_ssuspend, kSPD] string capability is the shifted suspend key. 607 | KeySsuspend 608 | // The KeySundo [key_sundo, kUND] string capability is the shifted undo key. 609 | KeySundo 610 | // The ReqForInput [req_for_input, rfi] string capability is the send next input char (for ptys). 611 | ReqForInput 612 | // The KeyF11 [key_f11, kf11] string capability is the F11 function key. 613 | KeyF11 614 | // The KeyF12 [key_f12, kf12] string capability is the F12 function key. 615 | KeyF12 616 | // The KeyF13 [key_f13, kf13] string capability is the F13 function key. 617 | KeyF13 618 | // The KeyF14 [key_f14, kf14] string capability is the F14 function key. 619 | KeyF14 620 | // The KeyF15 [key_f15, kf15] string capability is the F15 function key. 621 | KeyF15 622 | // The KeyF16 [key_f16, kf16] string capability is the F16 function key. 623 | KeyF16 624 | // The KeyF17 [key_f17, kf17] string capability is the F17 function key. 625 | KeyF17 626 | // The KeyF18 [key_f18, kf18] string capability is the F18 function key. 627 | KeyF18 628 | // The KeyF19 [key_f19, kf19] string capability is the F19 function key. 629 | KeyF19 630 | // The KeyF20 [key_f20, kf20] string capability is the F20 function key. 631 | KeyF20 632 | // The KeyF21 [key_f21, kf21] string capability is the F21 function key. 633 | KeyF21 634 | // The KeyF22 [key_f22, kf22] string capability is the F22 function key. 635 | KeyF22 636 | // The KeyF23 [key_f23, kf23] string capability is the F23 function key. 637 | KeyF23 638 | // The KeyF24 [key_f24, kf24] string capability is the F24 function key. 639 | KeyF24 640 | // The KeyF25 [key_f25, kf25] string capability is the F25 function key. 641 | KeyF25 642 | // The KeyF26 [key_f26, kf26] string capability is the F26 function key. 643 | KeyF26 644 | // The KeyF27 [key_f27, kf27] string capability is the F27 function key. 645 | KeyF27 646 | // The KeyF28 [key_f28, kf28] string capability is the F28 function key. 647 | KeyF28 648 | // The KeyF29 [key_f29, kf29] string capability is the F29 function key. 649 | KeyF29 650 | // The KeyF30 [key_f30, kf30] string capability is the F30 function key. 651 | KeyF30 652 | // The KeyF31 [key_f31, kf31] string capability is the F31 function key. 653 | KeyF31 654 | // The KeyF32 [key_f32, kf32] string capability is the F32 function key. 655 | KeyF32 656 | // The KeyF33 [key_f33, kf33] string capability is the F33 function key. 657 | KeyF33 658 | // The KeyF34 [key_f34, kf34] string capability is the F34 function key. 659 | KeyF34 660 | // The KeyF35 [key_f35, kf35] string capability is the F35 function key. 661 | KeyF35 662 | // The KeyF36 [key_f36, kf36] string capability is the F36 function key. 663 | KeyF36 664 | // The KeyF37 [key_f37, kf37] string capability is the F37 function key. 665 | KeyF37 666 | // The KeyF38 [key_f38, kf38] string capability is the F38 function key. 667 | KeyF38 668 | // The KeyF39 [key_f39, kf39] string capability is the F39 function key. 669 | KeyF39 670 | // The KeyF40 [key_f40, kf40] string capability is the F40 function key. 671 | KeyF40 672 | // The KeyF41 [key_f41, kf41] string capability is the F41 function key. 673 | KeyF41 674 | // The KeyF42 [key_f42, kf42] string capability is the F42 function key. 675 | KeyF42 676 | // The KeyF43 [key_f43, kf43] string capability is the F43 function key. 677 | KeyF43 678 | // The KeyF44 [key_f44, kf44] string capability is the F44 function key. 679 | KeyF44 680 | // The KeyF45 [key_f45, kf45] string capability is the F45 function key. 681 | KeyF45 682 | // The KeyF46 [key_f46, kf46] string capability is the F46 function key. 683 | KeyF46 684 | // The KeyF47 [key_f47, kf47] string capability is the F47 function key. 685 | KeyF47 686 | // The KeyF48 [key_f48, kf48] string capability is the F48 function key. 687 | KeyF48 688 | // The KeyF49 [key_f49, kf49] string capability is the F49 function key. 689 | KeyF49 690 | // The KeyF50 [key_f50, kf50] string capability is the F50 function key. 691 | KeyF50 692 | // The KeyF51 [key_f51, kf51] string capability is the F51 function key. 693 | KeyF51 694 | // The KeyF52 [key_f52, kf52] string capability is the F52 function key. 695 | KeyF52 696 | // The KeyF53 [key_f53, kf53] string capability is the F53 function key. 697 | KeyF53 698 | // The KeyF54 [key_f54, kf54] string capability is the F54 function key. 699 | KeyF54 700 | // The KeyF55 [key_f55, kf55] string capability is the F55 function key. 701 | KeyF55 702 | // The KeyF56 [key_f56, kf56] string capability is the F56 function key. 703 | KeyF56 704 | // The KeyF57 [key_f57, kf57] string capability is the F57 function key. 705 | KeyF57 706 | // The KeyF58 [key_f58, kf58] string capability is the F58 function key. 707 | KeyF58 708 | // The KeyF59 [key_f59, kf59] string capability is the F59 function key. 709 | KeyF59 710 | // The KeyF60 [key_f60, kf60] string capability is the F60 function key. 711 | KeyF60 712 | // The KeyF61 [key_f61, kf61] string capability is the F61 function key. 713 | KeyF61 714 | // The KeyF62 [key_f62, kf62] string capability is the F62 function key. 715 | KeyF62 716 | // The KeyF63 [key_f63, kf63] string capability is the F63 function key. 717 | KeyF63 718 | // The ClrBol [clr_bol, el1] string capability is the Clear to beginning of line. 719 | ClrBol 720 | // The ClearMargins [clear_margins, mgc] string capability is the clear right and left soft margins. 721 | ClearMargins 722 | // The SetLeftMargin [set_left_margin, smgl] string capability is the set left soft margin at current column. (ML is not in BSD termcap). 723 | SetLeftMargin 724 | // The SetRightMargin [set_right_margin, smgr] string capability is the set right soft margin at current column. 725 | SetRightMargin 726 | // The LabelFormat [label_format, fln] string capability is the label format. 727 | LabelFormat 728 | // The SetClock [set_clock, sclk] string capability is the set clock, #1 hrs #2 mins #3 secs. 729 | SetClock 730 | // The DisplayClock [display_clock, dclk] string capability is the display clock. 731 | DisplayClock 732 | // The RemoveClock [remove_clock, rmclk] string capability is the remove clock. 733 | RemoveClock 734 | // The CreateWindow [create_window, cwin] string capability is the define a window #1 from #2,#3 to #4,#5. 735 | CreateWindow 736 | // The GotoWindow [goto_window, wingo] string capability is the go to window #1. 737 | GotoWindow 738 | // The Hangup [hangup, hup] string capability is the hang-up phone. 739 | Hangup 740 | // The DialPhone [dial_phone, dial] string capability is the dial number #1. 741 | DialPhone 742 | // The QuickDial [quick_dial, qdial] string capability is the dial number #1 without checking. 743 | QuickDial 744 | // The Tone [tone, tone] string capability is the select touch tone dialing. 745 | Tone 746 | // The Pulse [pulse, pulse] string capability is the select pulse dialing. 747 | Pulse 748 | // The FlashHook [flash_hook, hook] string capability is the flash switch hook. 749 | FlashHook 750 | // The FixedPause [fixed_pause, pause] string capability is the pause for 2-3 seconds. 751 | FixedPause 752 | // The WaitTone [wait_tone, wait] string capability is the wait for dial-tone. 753 | WaitTone 754 | // The User0 [user0, u0] string capability is the User string #0. 755 | User0 756 | // The User1 [user1, u1] string capability is the User string #1. 757 | User1 758 | // The User2 [user2, u2] string capability is the User string #2. 759 | User2 760 | // The User3 [user3, u3] string capability is the User string #3. 761 | User3 762 | // The User4 [user4, u4] string capability is the User string #4. 763 | User4 764 | // The User5 [user5, u5] string capability is the User string #5. 765 | User5 766 | // The User6 [user6, u6] string capability is the User string #6. 767 | User6 768 | // The User7 [user7, u7] string capability is the User string #7. 769 | User7 770 | // The User8 [user8, u8] string capability is the User string #8. 771 | User8 772 | // The User9 [user9, u9] string capability is the User string #9. 773 | User9 774 | // The OrigPair [orig_pair, op] string capability is the Set default pair to its original value. 775 | OrigPair 776 | // The OrigColors [orig_colors, oc] string capability is the Set all color pairs to the original ones. 777 | OrigColors 778 | // The InitializeColor [initialize_color, initc] string capability is the initialize color #1 to (#2,#3,#4). 779 | InitializeColor 780 | // The InitializePair [initialize_pair, initp] string capability is the Initialize color pair #1 to fg=(#2,#3,#4), bg=(#5,#6,#7). 781 | InitializePair 782 | // The SetColorPair [set_color_pair, scp] string capability is the Set current color pair to #1. 783 | SetColorPair 784 | // The SetForeground [set_foreground, setf] string capability is the Set foreground color #1. 785 | SetForeground 786 | // The SetBackground [set_background, setb] string capability is the Set background color #1. 787 | SetBackground 788 | // The ChangeCharPitch [change_char_pitch, cpi] string capability is the Change number of characters per inch to #1. 789 | ChangeCharPitch 790 | // The ChangeLinePitch [change_line_pitch, lpi] string capability is the Change number of lines per inch to #1. 791 | ChangeLinePitch 792 | // The ChangeResHorz [change_res_horz, chr] string capability is the Change horizontal resolution to #1. 793 | ChangeResHorz 794 | // The ChangeResVert [change_res_vert, cvr] string capability is the Change vertical resolution to #1. 795 | ChangeResVert 796 | // The DefineChar [define_char, defc] string capability is the Define a character #1, #2 dots wide, descender #3. 797 | DefineChar 798 | // The EnterDoublewideMode [enter_doublewide_mode, swidm] string capability is the Enter double-wide mode. 799 | EnterDoublewideMode 800 | // The EnterDraftQuality [enter_draft_quality, sdrfq] string capability is the Enter draft-quality mode. 801 | EnterDraftQuality 802 | // The EnterItalicsMode [enter_italics_mode, sitm] string capability is the Enter italic mode. 803 | EnterItalicsMode 804 | // The EnterLeftwardMode [enter_leftward_mode, slm] string capability is the Start leftward carriage motion. 805 | EnterLeftwardMode 806 | // The EnterMicroMode [enter_micro_mode, smicm] string capability is the Start micro-motion mode. 807 | EnterMicroMode 808 | // The EnterNearLetterQuality [enter_near_letter_quality, snlq] string capability is the Enter NLQ mode. 809 | EnterNearLetterQuality 810 | // The EnterNormalQuality [enter_normal_quality, snrmq] string capability is the Enter normal-quality mode. 811 | EnterNormalQuality 812 | // The EnterShadowMode [enter_shadow_mode, sshm] string capability is the Enter shadow-print mode. 813 | EnterShadowMode 814 | // The EnterSubscriptMode [enter_subscript_mode, ssubm] string capability is the Enter subscript mode. 815 | EnterSubscriptMode 816 | // The EnterSuperscriptMode [enter_superscript_mode, ssupm] string capability is the Enter superscript mode. 817 | EnterSuperscriptMode 818 | // The EnterUpwardMode [enter_upward_mode, sum] string capability is the Start upward carriage motion. 819 | EnterUpwardMode 820 | // The ExitDoublewideMode [exit_doublewide_mode, rwidm] string capability is the End double-wide mode. 821 | ExitDoublewideMode 822 | // The ExitItalicsMode [exit_italics_mode, ritm] string capability is the End italic mode. 823 | ExitItalicsMode 824 | // The ExitLeftwardMode [exit_leftward_mode, rlm] string capability is the End left-motion mode. 825 | ExitLeftwardMode 826 | // The ExitMicroMode [exit_micro_mode, rmicm] string capability is the End micro-motion mode. 827 | ExitMicroMode 828 | // The ExitShadowMode [exit_shadow_mode, rshm] string capability is the End shadow-print mode. 829 | ExitShadowMode 830 | // The ExitSubscriptMode [exit_subscript_mode, rsubm] string capability is the End subscript mode. 831 | ExitSubscriptMode 832 | // The ExitSuperscriptMode [exit_superscript_mode, rsupm] string capability is the End superscript mode. 833 | ExitSuperscriptMode 834 | // The ExitUpwardMode [exit_upward_mode, rum] string capability is the End reverse character motion. 835 | ExitUpwardMode 836 | // The MicroColumnAddress [micro_column_address, mhpa] string capability is the Like column_address in micro mode. 837 | MicroColumnAddress 838 | // The MicroDown [micro_down, mcud1] string capability is the Like cursor_down in micro mode. 839 | MicroDown 840 | // The MicroLeft [micro_left, mcub1] string capability is the Like cursor_left in micro mode. 841 | MicroLeft 842 | // The MicroRight [micro_right, mcuf1] string capability is the Like cursor_right in micro mode. 843 | MicroRight 844 | // The MicroRowAddress [micro_row_address, mvpa] string capability is the Like row_address #1 in micro mode. 845 | MicroRowAddress 846 | // The MicroUp [micro_up, mcuu1] string capability is the Like cursor_up in micro mode. 847 | MicroUp 848 | // The OrderOfPins [order_of_pins, porder] string capability is the Match software bits to print-head pins. 849 | OrderOfPins 850 | // The ParmDownMicro [parm_down_micro, mcud] string capability is the Like parm_down_cursor in micro mode. 851 | ParmDownMicro 852 | // The ParmLeftMicro [parm_left_micro, mcub] string capability is the Like parm_left_cursor in micro mode. 853 | ParmLeftMicro 854 | // The ParmRightMicro [parm_right_micro, mcuf] string capability is the Like parm_right_cursor in micro mode. 855 | ParmRightMicro 856 | // The ParmUpMicro [parm_up_micro, mcuu] string capability is the Like parm_up_cursor in micro mode. 857 | ParmUpMicro 858 | // The SelectCharSet [select_char_set, scs] string capability is the Select character set, #1. 859 | SelectCharSet 860 | // The SetBottomMargin [set_bottom_margin, smgb] string capability is the Set bottom margin at current line. 861 | SetBottomMargin 862 | // The SetBottomMarginParm [set_bottom_margin_parm, smgbp] string capability is the Set bottom margin at line #1 or (if smgtp is not given) #2 lines from bottom. 863 | SetBottomMarginParm 864 | // The SetLeftMarginParm [set_left_margin_parm, smglp] string capability is the Set left (right) margin at column #1. 865 | SetLeftMarginParm 866 | // The SetRightMarginParm [set_right_margin_parm, smgrp] string capability is the Set right margin at column #1. 867 | SetRightMarginParm 868 | // The SetTopMargin [set_top_margin, smgt] string capability is the Set top margin at current line. 869 | SetTopMargin 870 | // The SetTopMarginParm [set_top_margin_parm, smgtp] string capability is the Set top (bottom) margin at row #1. 871 | SetTopMarginParm 872 | // The StartBitImage [start_bit_image, sbim] string capability is the Start printing bit image graphics. 873 | StartBitImage 874 | // The StartCharSetDef [start_char_set_def, scsd] string capability is the Start character set definition #1, with #2 characters in the set. 875 | StartCharSetDef 876 | // The StopBitImage [stop_bit_image, rbim] string capability is the Stop printing bit image graphics. 877 | StopBitImage 878 | // The StopCharSetDef [stop_char_set_def, rcsd] string capability is the End definition of character set #1. 879 | StopCharSetDef 880 | // The SubscriptCharacters [subscript_characters, subcs] string capability is the List of subscriptable characters. 881 | SubscriptCharacters 882 | // The SuperscriptCharacters [superscript_characters, supcs] string capability is the List of superscriptable characters. 883 | SuperscriptCharacters 884 | // The TheseCauseCr [these_cause_cr, docr] string capability is the Printing any of these characters causes CR. 885 | TheseCauseCr 886 | // The ZeroMotion [zero_motion, zerom] string capability is the No motion for subsequent character. 887 | ZeroMotion 888 | // The CharSetNames [char_set_names, csnm] string capability is the Produce #1'th item from list of character set names. 889 | CharSetNames 890 | // The KeyMouse [key_mouse, kmous] string capability is the Mouse event has occurred. 891 | KeyMouse 892 | // The MouseInfo [mouse_info, minfo] string capability is the Mouse status information. 893 | MouseInfo 894 | // The ReqMousePos [req_mouse_pos, reqmp] string capability is the Request mouse position. 895 | ReqMousePos 896 | // The GetMouse [get_mouse, getm] string capability is the Curses should get button events, parameter #1 not documented. 897 | GetMouse 898 | // The SetAForeground [set_a_foreground, setaf] string capability is the Set foreground color to #1, using ANSI escape. 899 | SetAForeground 900 | // The SetABackground [set_a_background, setab] string capability is the Set background color to #1, using ANSI escape. 901 | SetABackground 902 | // The PkeyPlab [pkey_plab, pfxl] string capability is the Program function key #1 to type string #2 and show string #3. 903 | PkeyPlab 904 | // The DeviceType [device_type, devt] string capability is the Indicate language/codeset support. 905 | DeviceType 906 | // The CodeSetInit [code_set_init, csin] string capability is the Init sequence for multiple codesets. 907 | CodeSetInit 908 | // The Set0DesSeq [set0_des_seq, s0ds] string capability is the Shift to codeset 0 (EUC set 0, ASCII). 909 | Set0DesSeq 910 | // The Set1DesSeq [set1_des_seq, s1ds] string capability is the Shift to codeset 1. 911 | Set1DesSeq 912 | // The Set2DesSeq [set2_des_seq, s2ds] string capability is the Shift to codeset 2. 913 | Set2DesSeq 914 | // The Set3DesSeq [set3_des_seq, s3ds] string capability is the Shift to codeset 3. 915 | Set3DesSeq 916 | // The SetLrMargin [set_lr_margin, smglr] string capability is the Set both left and right margins to #1, #2. (ML is not in BSD termcap). 917 | SetLrMargin 918 | // The SetTbMargin [set_tb_margin, smgtb] string capability is the Sets both top and bottom margins to #1, #2. 919 | SetTbMargin 920 | // The BitImageRepeat [bit_image_repeat, birep] string capability is the Repeat bit image cell #1 #2 times. 921 | BitImageRepeat 922 | // The BitImageNewline [bit_image_newline, binel] string capability is the Move to next row of the bit image. 923 | BitImageNewline 924 | // The BitImageCarriageReturn [bit_image_carriage_return, bicr] string capability is the Move to beginning of same row. 925 | BitImageCarriageReturn 926 | // The ColorNames [color_names, colornm] string capability is the Give name for color #1. 927 | ColorNames 928 | // The DefineBitImageRegion [define_bit_image_region, defbi] string capability is the Define rectangular bit image region. 929 | DefineBitImageRegion 930 | // The EndBitImageRegion [end_bit_image_region, endbi] string capability is the End a bit-image region. 931 | EndBitImageRegion 932 | // The SetColorBand [set_color_band, setcolor] string capability is the Change to ribbon color #1. 933 | SetColorBand 934 | // The SetPageLength [set_page_length, slines] string capability is the Set page length to #1 lines. 935 | SetPageLength 936 | // The DisplayPcChar [display_pc_char, dispc] string capability is the Display PC character #1. 937 | DisplayPcChar 938 | // The EnterPcCharsetMode [enter_pc_charset_mode, smpch] string capability is the Enter PC character display mode. 939 | EnterPcCharsetMode 940 | // The ExitPcCharsetMode [exit_pc_charset_mode, rmpch] string capability is the Exit PC character display mode. 941 | ExitPcCharsetMode 942 | // The EnterScancodeMode [enter_scancode_mode, smsc] string capability is the Enter PC scancode mode. 943 | EnterScancodeMode 944 | // The ExitScancodeMode [exit_scancode_mode, rmsc] string capability is the Exit PC scancode mode. 945 | ExitScancodeMode 946 | // The PcTermOptions [pc_term_options, pctrm] string capability is the PC terminal options. 947 | PcTermOptions 948 | // The ScancodeEscape [scancode_escape, scesc] string capability is the Escape for scancode emulation. 949 | ScancodeEscape 950 | // The AltScancodeEsc [alt_scancode_esc, scesa] string capability is the Alternate escape for scancode emulation. 951 | AltScancodeEsc 952 | // The EnterHorizontalHlMode [enter_horizontal_hl_mode, ehhlm] string capability is the Enter horizontal highlight mode. 953 | EnterHorizontalHlMode 954 | // The EnterLeftHlMode [enter_left_hl_mode, elhlm] string capability is the Enter left highlight mode. 955 | EnterLeftHlMode 956 | // The EnterLowHlMode [enter_low_hl_mode, elohlm] string capability is the Enter low highlight mode. 957 | EnterLowHlMode 958 | // The EnterRightHlMode [enter_right_hl_mode, erhlm] string capability is the Enter right highlight mode. 959 | EnterRightHlMode 960 | // The EnterTopHlMode [enter_top_hl_mode, ethlm] string capability is the Enter top highlight mode. 961 | EnterTopHlMode 962 | // The EnterVerticalHlMode [enter_vertical_hl_mode, evhlm] string capability is the Enter vertical highlight mode. 963 | EnterVerticalHlMode 964 | // The SetAAttributes [set_a_attributes, sgr1] string capability is the Define second set of video attributes #1-#6. 965 | SetAAttributes 966 | // The SetPglenInch [set_pglen_inch, slength] string capability is the Set page length to #1 hundredth of an inch (some implementations use sL for termcap). 967 | SetPglenInch 968 | // The TermcapInit2 [termcap_init2, OTi2] string capability is the secondary initialization string. 969 | TermcapInit2 970 | // The TermcapReset [termcap_reset, OTrs] string capability is the terminal reset string. 971 | TermcapReset 972 | // The LinefeedIfNotLf [linefeed_if_not_lf, OTnl] string capability is the use to move down. 973 | LinefeedIfNotLf 974 | // The BackspaceIfNotBs [backspace_if_not_bs, OTbc] string capability is the move left, if not ^H. 975 | BackspaceIfNotBs 976 | // The OtherNonFunctionKeys [other_non_function_keys, OTko] string capability is the list of self-mapped keycaps. 977 | OtherNonFunctionKeys 978 | // The ArrowKeyMap [arrow_key_map, OTma] string capability is the map motion-keys for vi version 2. 979 | ArrowKeyMap 980 | // The AcsUlcorner [acs_ulcorner, OTG2] string capability is the single upper left. 981 | AcsUlcorner 982 | // The AcsLlcorner [acs_llcorner, OTG3] string capability is the single lower left. 983 | AcsLlcorner 984 | // The AcsUrcorner [acs_urcorner, OTG1] string capability is the single upper right. 985 | AcsUrcorner 986 | // The AcsLrcorner [acs_lrcorner, OTG4] string capability is the single lower right. 987 | AcsLrcorner 988 | // The AcsLtee [acs_ltee, OTGR] string capability is the tee pointing right. 989 | AcsLtee 990 | // The AcsRtee [acs_rtee, OTGL] string capability is the tee pointing left. 991 | AcsRtee 992 | // The AcsBtee [acs_btee, OTGU] string capability is the tee pointing up. 993 | AcsBtee 994 | // The AcsTtee [acs_ttee, OTGD] string capability is the tee pointing down. 995 | AcsTtee 996 | // The AcsHline [acs_hline, OTGH] string capability is the single horizontal line. 997 | AcsHline 998 | // The AcsVline [acs_vline, OTGV] string capability is the single vertical line. 999 | AcsVline 1000 | // The AcsPlus [acs_plus, OTGC] string capability is the single intersection. 1001 | AcsPlus 1002 | // The MemoryLock [memory_lock, meml] string capability is the lock memory above cursor. 1003 | MemoryLock 1004 | // The MemoryUnlock [memory_unlock, memu] string capability is the unlock memory. 1005 | MemoryUnlock 1006 | // The BoxChars1 [box_chars_1, box1] string capability is the box characters primary set. 1007 | BoxChars1 1008 | ) 1009 | const ( 1010 | // CapCountBool is the count of bool capabilities. 1011 | CapCountBool = ReturnDoesClrEol + 1 1012 | // CapCountNum is the count of num capabilities. 1013 | CapCountNum = NumberOfFunctionKeys + 1 1014 | // CapCountString is the count of string capabilities. 1015 | CapCountString = BoxChars1 + 1 1016 | ) 1017 | 1018 | // boolCapNames are the bool term cap names. 1019 | var boolCapNames = [...]string{ 1020 | "auto_left_margin", "bw", 1021 | "auto_right_margin", "am", 1022 | "no_esc_ctlc", "xsb", 1023 | "ceol_standout_glitch", "xhp", 1024 | "eat_newline_glitch", "xenl", 1025 | "erase_overstrike", "eo", 1026 | "generic_type", "gn", 1027 | "hard_copy", "hc", 1028 | "has_meta_key", "km", 1029 | "has_status_line", "hs", 1030 | "insert_null_glitch", "in", 1031 | "memory_above", "da", 1032 | "memory_below", "db", 1033 | "move_insert_mode", "mir", 1034 | "move_standout_mode", "msgr", 1035 | "over_strike", "os", 1036 | "status_line_esc_ok", "eslok", 1037 | "dest_tabs_magic_smso", "xt", 1038 | "tilde_glitch", "hz", 1039 | "transparent_underline", "ul", 1040 | "xon_xoff", "xon", 1041 | "needs_xon_xoff", "nxon", 1042 | "prtr_silent", "mc5i", 1043 | "hard_cursor", "chts", 1044 | "non_rev_rmcup", "nrrmc", 1045 | "no_pad_char", "npc", 1046 | "non_dest_scroll_region", "ndscr", 1047 | "can_change", "ccc", 1048 | "back_color_erase", "bce", 1049 | "hue_lightness_saturation", "hls", 1050 | "col_addr_glitch", "xhpa", 1051 | "cr_cancels_micro_mode", "crxm", 1052 | "has_print_wheel", "daisy", 1053 | "row_addr_glitch", "xvpa", 1054 | "semi_auto_right_margin", "sam", 1055 | "cpi_changes_res", "cpix", 1056 | "lpi_changes_res", "lpix", 1057 | "backspaces_with_bs", "OTbs", 1058 | "crt_no_scrolling", "OTns", 1059 | "no_correctly_working_cr", "OTnc", 1060 | "gnu_has_meta_key", "OTMT", 1061 | "linefeed_is_newline", "OTNL", 1062 | "has_hardware_tabs", "OTpt", 1063 | "return_does_clr_eol", "OTxr", 1064 | } 1065 | 1066 | // numCapNames are the num term cap names. 1067 | var numCapNames = [...]string{ 1068 | "columns", "cols", 1069 | "init_tabs", "it", 1070 | "lines", "lines", 1071 | "lines_of_memory", "lm", 1072 | "magic_cookie_glitch", "xmc", 1073 | "padding_baud_rate", "pb", 1074 | "virtual_terminal", "vt", 1075 | "width_status_line", "wsl", 1076 | "num_labels", "nlab", 1077 | "label_height", "lh", 1078 | "label_width", "lw", 1079 | "max_attributes", "ma", 1080 | "maximum_windows", "wnum", 1081 | "max_colors", "colors", 1082 | "max_pairs", "pairs", 1083 | "no_color_video", "ncv", 1084 | "buffer_capacity", "bufsz", 1085 | "dot_vert_spacing", "spinv", 1086 | "dot_horz_spacing", "spinh", 1087 | "max_micro_address", "maddr", 1088 | "max_micro_jump", "mjump", 1089 | "micro_col_size", "mcs", 1090 | "micro_line_size", "mls", 1091 | "number_of_pins", "npins", 1092 | "output_res_char", "orc", 1093 | "output_res_line", "orl", 1094 | "output_res_horz_inch", "orhi", 1095 | "output_res_vert_inch", "orvi", 1096 | "print_rate", "cps", 1097 | "wide_char_size", "widcs", 1098 | "buttons", "btns", 1099 | "bit_image_entwining", "bitwin", 1100 | "bit_image_type", "bitype", 1101 | "magic_cookie_glitch_ul", "OTug", 1102 | "carriage_return_delay", "OTdC", 1103 | "new_line_delay", "OTdN", 1104 | "backspace_delay", "OTdB", 1105 | "horizontal_tab_delay", "OTdT", 1106 | "number_of_function_keys", "OTkn", 1107 | } 1108 | 1109 | // stringCapNames are the string term cap names. 1110 | var stringCapNames = [...]string{ 1111 | "back_tab", "cbt", 1112 | "bell", "bel", 1113 | "carriage_return", "cr", 1114 | "change_scroll_region", "csr", 1115 | "clear_all_tabs", "tbc", 1116 | "clear_screen", "clear", 1117 | "clr_eol", "el", 1118 | "clr_eos", "ed", 1119 | "column_address", "hpa", 1120 | "command_character", "cmdch", 1121 | "cursor_address", "cup", 1122 | "cursor_down", "cud1", 1123 | "cursor_home", "home", 1124 | "cursor_invisible", "civis", 1125 | "cursor_left", "cub1", 1126 | "cursor_mem_address", "mrcup", 1127 | "cursor_normal", "cnorm", 1128 | "cursor_right", "cuf1", 1129 | "cursor_to_ll", "ll", 1130 | "cursor_up", "cuu1", 1131 | "cursor_visible", "cvvis", 1132 | "delete_character", "dch1", 1133 | "delete_line", "dl1", 1134 | "dis_status_line", "dsl", 1135 | "down_half_line", "hd", 1136 | "enter_alt_charset_mode", "smacs", 1137 | "enter_blink_mode", "blink", 1138 | "enter_bold_mode", "bold", 1139 | "enter_ca_mode", "smcup", 1140 | "enter_delete_mode", "smdc", 1141 | "enter_dim_mode", "dim", 1142 | "enter_insert_mode", "smir", 1143 | "enter_secure_mode", "invis", 1144 | "enter_protected_mode", "prot", 1145 | "enter_reverse_mode", "rev", 1146 | "enter_standout_mode", "smso", 1147 | "enter_underline_mode", "smul", 1148 | "erase_chars", "ech", 1149 | "exit_alt_charset_mode", "rmacs", 1150 | "exit_attribute_mode", "sgr0", 1151 | "exit_ca_mode", "rmcup", 1152 | "exit_delete_mode", "rmdc", 1153 | "exit_insert_mode", "rmir", 1154 | "exit_standout_mode", "rmso", 1155 | "exit_underline_mode", "rmul", 1156 | "flash_screen", "flash", 1157 | "form_feed", "ff", 1158 | "from_status_line", "fsl", 1159 | "init_1string", "is1", 1160 | "init_2string", "is2", 1161 | "init_3string", "is3", 1162 | "init_file", "if", 1163 | "insert_character", "ich1", 1164 | "insert_line", "il1", 1165 | "insert_padding", "ip", 1166 | "key_backspace", "kbs", 1167 | "key_catab", "ktbc", 1168 | "key_clear", "kclr", 1169 | "key_ctab", "kctab", 1170 | "key_dc", "kdch1", 1171 | "key_dl", "kdl1", 1172 | "key_down", "kcud1", 1173 | "key_eic", "krmir", 1174 | "key_eol", "kel", 1175 | "key_eos", "ked", 1176 | "key_f0", "kf0", 1177 | "key_f1", "kf1", 1178 | "key_f10", "kf10", 1179 | "key_f2", "kf2", 1180 | "key_f3", "kf3", 1181 | "key_f4", "kf4", 1182 | "key_f5", "kf5", 1183 | "key_f6", "kf6", 1184 | "key_f7", "kf7", 1185 | "key_f8", "kf8", 1186 | "key_f9", "kf9", 1187 | "key_home", "khome", 1188 | "key_ic", "kich1", 1189 | "key_il", "kil1", 1190 | "key_left", "kcub1", 1191 | "key_ll", "kll", 1192 | "key_npage", "knp", 1193 | "key_ppage", "kpp", 1194 | "key_right", "kcuf1", 1195 | "key_sf", "kind", 1196 | "key_sr", "kri", 1197 | "key_stab", "khts", 1198 | "key_up", "kcuu1", 1199 | "keypad_local", "rmkx", 1200 | "keypad_xmit", "smkx", 1201 | "lab_f0", "lf0", 1202 | "lab_f1", "lf1", 1203 | "lab_f10", "lf10", 1204 | "lab_f2", "lf2", 1205 | "lab_f3", "lf3", 1206 | "lab_f4", "lf4", 1207 | "lab_f5", "lf5", 1208 | "lab_f6", "lf6", 1209 | "lab_f7", "lf7", 1210 | "lab_f8", "lf8", 1211 | "lab_f9", "lf9", 1212 | "meta_off", "rmm", 1213 | "meta_on", "smm", 1214 | "newline", "nel", 1215 | "pad_char", "pad", 1216 | "parm_dch", "dch", 1217 | "parm_delete_line", "dl", 1218 | "parm_down_cursor", "cud", 1219 | "parm_ich", "ich", 1220 | "parm_index", "indn", 1221 | "parm_insert_line", "il", 1222 | "parm_left_cursor", "cub", 1223 | "parm_right_cursor", "cuf", 1224 | "parm_rindex", "rin", 1225 | "parm_up_cursor", "cuu", 1226 | "pkey_key", "pfkey", 1227 | "pkey_local", "pfloc", 1228 | "pkey_xmit", "pfx", 1229 | "print_screen", "mc0", 1230 | "prtr_off", "mc4", 1231 | "prtr_on", "mc5", 1232 | "repeat_char", "rep", 1233 | "reset_1string", "rs1", 1234 | "reset_2string", "rs2", 1235 | "reset_3string", "rs3", 1236 | "reset_file", "rf", 1237 | "restore_cursor", "rc", 1238 | "row_address", "vpa", 1239 | "save_cursor", "sc", 1240 | "scroll_forward", "ind", 1241 | "scroll_reverse", "ri", 1242 | "set_attributes", "sgr", 1243 | "set_tab", "hts", 1244 | "set_window", "wind", 1245 | "tab", "ht", 1246 | "to_status_line", "tsl", 1247 | "underline_char", "uc", 1248 | "up_half_line", "hu", 1249 | "init_prog", "iprog", 1250 | "key_a1", "ka1", 1251 | "key_a3", "ka3", 1252 | "key_b2", "kb2", 1253 | "key_c1", "kc1", 1254 | "key_c3", "kc3", 1255 | "prtr_non", "mc5p", 1256 | "char_padding", "rmp", 1257 | "acs_chars", "acsc", 1258 | "plab_norm", "pln", 1259 | "key_btab", "kcbt", 1260 | "enter_xon_mode", "smxon", 1261 | "exit_xon_mode", "rmxon", 1262 | "enter_am_mode", "smam", 1263 | "exit_am_mode", "rmam", 1264 | "xon_character", "xonc", 1265 | "xoff_character", "xoffc", 1266 | "ena_acs", "enacs", 1267 | "label_on", "smln", 1268 | "label_off", "rmln", 1269 | "key_beg", "kbeg", 1270 | "key_cancel", "kcan", 1271 | "key_close", "kclo", 1272 | "key_command", "kcmd", 1273 | "key_copy", "kcpy", 1274 | "key_create", "kcrt", 1275 | "key_end", "kend", 1276 | "key_enter", "kent", 1277 | "key_exit", "kext", 1278 | "key_find", "kfnd", 1279 | "key_help", "khlp", 1280 | "key_mark", "kmrk", 1281 | "key_message", "kmsg", 1282 | "key_move", "kmov", 1283 | "key_next", "knxt", 1284 | "key_open", "kopn", 1285 | "key_options", "kopt", 1286 | "key_previous", "kprv", 1287 | "key_print", "kprt", 1288 | "key_redo", "krdo", 1289 | "key_reference", "kref", 1290 | "key_refresh", "krfr", 1291 | "key_replace", "krpl", 1292 | "key_restart", "krst", 1293 | "key_resume", "kres", 1294 | "key_save", "ksav", 1295 | "key_suspend", "kspd", 1296 | "key_undo", "kund", 1297 | "key_sbeg", "kBEG", 1298 | "key_scancel", "kCAN", 1299 | "key_scommand", "kCMD", 1300 | "key_scopy", "kCPY", 1301 | "key_screate", "kCRT", 1302 | "key_sdc", "kDC", 1303 | "key_sdl", "kDL", 1304 | "key_select", "kslt", 1305 | "key_send", "kEND", 1306 | "key_seol", "kEOL", 1307 | "key_sexit", "kEXT", 1308 | "key_sfind", "kFND", 1309 | "key_shelp", "kHLP", 1310 | "key_shome", "kHOM", 1311 | "key_sic", "kIC", 1312 | "key_sleft", "kLFT", 1313 | "key_smessage", "kMSG", 1314 | "key_smove", "kMOV", 1315 | "key_snext", "kNXT", 1316 | "key_soptions", "kOPT", 1317 | "key_sprevious", "kPRV", 1318 | "key_sprint", "kPRT", 1319 | "key_sredo", "kRDO", 1320 | "key_sreplace", "kRPL", 1321 | "key_sright", "kRIT", 1322 | "key_srsume", "kRES", 1323 | "key_ssave", "kSAV", 1324 | "key_ssuspend", "kSPD", 1325 | "key_sundo", "kUND", 1326 | "req_for_input", "rfi", 1327 | "key_f11", "kf11", 1328 | "key_f12", "kf12", 1329 | "key_f13", "kf13", 1330 | "key_f14", "kf14", 1331 | "key_f15", "kf15", 1332 | "key_f16", "kf16", 1333 | "key_f17", "kf17", 1334 | "key_f18", "kf18", 1335 | "key_f19", "kf19", 1336 | "key_f20", "kf20", 1337 | "key_f21", "kf21", 1338 | "key_f22", "kf22", 1339 | "key_f23", "kf23", 1340 | "key_f24", "kf24", 1341 | "key_f25", "kf25", 1342 | "key_f26", "kf26", 1343 | "key_f27", "kf27", 1344 | "key_f28", "kf28", 1345 | "key_f29", "kf29", 1346 | "key_f30", "kf30", 1347 | "key_f31", "kf31", 1348 | "key_f32", "kf32", 1349 | "key_f33", "kf33", 1350 | "key_f34", "kf34", 1351 | "key_f35", "kf35", 1352 | "key_f36", "kf36", 1353 | "key_f37", "kf37", 1354 | "key_f38", "kf38", 1355 | "key_f39", "kf39", 1356 | "key_f40", "kf40", 1357 | "key_f41", "kf41", 1358 | "key_f42", "kf42", 1359 | "key_f43", "kf43", 1360 | "key_f44", "kf44", 1361 | "key_f45", "kf45", 1362 | "key_f46", "kf46", 1363 | "key_f47", "kf47", 1364 | "key_f48", "kf48", 1365 | "key_f49", "kf49", 1366 | "key_f50", "kf50", 1367 | "key_f51", "kf51", 1368 | "key_f52", "kf52", 1369 | "key_f53", "kf53", 1370 | "key_f54", "kf54", 1371 | "key_f55", "kf55", 1372 | "key_f56", "kf56", 1373 | "key_f57", "kf57", 1374 | "key_f58", "kf58", 1375 | "key_f59", "kf59", 1376 | "key_f60", "kf60", 1377 | "key_f61", "kf61", 1378 | "key_f62", "kf62", 1379 | "key_f63", "kf63", 1380 | "clr_bol", "el1", 1381 | "clear_margins", "mgc", 1382 | "set_left_margin", "smgl", 1383 | "set_right_margin", "smgr", 1384 | "label_format", "fln", 1385 | "set_clock", "sclk", 1386 | "display_clock", "dclk", 1387 | "remove_clock", "rmclk", 1388 | "create_window", "cwin", 1389 | "goto_window", "wingo", 1390 | "hangup", "hup", 1391 | "dial_phone", "dial", 1392 | "quick_dial", "qdial", 1393 | "tone", "tone", 1394 | "pulse", "pulse", 1395 | "flash_hook", "hook", 1396 | "fixed_pause", "pause", 1397 | "wait_tone", "wait", 1398 | "user0", "u0", 1399 | "user1", "u1", 1400 | "user2", "u2", 1401 | "user3", "u3", 1402 | "user4", "u4", 1403 | "user5", "u5", 1404 | "user6", "u6", 1405 | "user7", "u7", 1406 | "user8", "u8", 1407 | "user9", "u9", 1408 | "orig_pair", "op", 1409 | "orig_colors", "oc", 1410 | "initialize_color", "initc", 1411 | "initialize_pair", "initp", 1412 | "set_color_pair", "scp", 1413 | "set_foreground", "setf", 1414 | "set_background", "setb", 1415 | "change_char_pitch", "cpi", 1416 | "change_line_pitch", "lpi", 1417 | "change_res_horz", "chr", 1418 | "change_res_vert", "cvr", 1419 | "define_char", "defc", 1420 | "enter_doublewide_mode", "swidm", 1421 | "enter_draft_quality", "sdrfq", 1422 | "enter_italics_mode", "sitm", 1423 | "enter_leftward_mode", "slm", 1424 | "enter_micro_mode", "smicm", 1425 | "enter_near_letter_quality", "snlq", 1426 | "enter_normal_quality", "snrmq", 1427 | "enter_shadow_mode", "sshm", 1428 | "enter_subscript_mode", "ssubm", 1429 | "enter_superscript_mode", "ssupm", 1430 | "enter_upward_mode", "sum", 1431 | "exit_doublewide_mode", "rwidm", 1432 | "exit_italics_mode", "ritm", 1433 | "exit_leftward_mode", "rlm", 1434 | "exit_micro_mode", "rmicm", 1435 | "exit_shadow_mode", "rshm", 1436 | "exit_subscript_mode", "rsubm", 1437 | "exit_superscript_mode", "rsupm", 1438 | "exit_upward_mode", "rum", 1439 | "micro_column_address", "mhpa", 1440 | "micro_down", "mcud1", 1441 | "micro_left", "mcub1", 1442 | "micro_right", "mcuf1", 1443 | "micro_row_address", "mvpa", 1444 | "micro_up", "mcuu1", 1445 | "order_of_pins", "porder", 1446 | "parm_down_micro", "mcud", 1447 | "parm_left_micro", "mcub", 1448 | "parm_right_micro", "mcuf", 1449 | "parm_up_micro", "mcuu", 1450 | "select_char_set", "scs", 1451 | "set_bottom_margin", "smgb", 1452 | "set_bottom_margin_parm", "smgbp", 1453 | "set_left_margin_parm", "smglp", 1454 | "set_right_margin_parm", "smgrp", 1455 | "set_top_margin", "smgt", 1456 | "set_top_margin_parm", "smgtp", 1457 | "start_bit_image", "sbim", 1458 | "start_char_set_def", "scsd", 1459 | "stop_bit_image", "rbim", 1460 | "stop_char_set_def", "rcsd", 1461 | "subscript_characters", "subcs", 1462 | "superscript_characters", "supcs", 1463 | "these_cause_cr", "docr", 1464 | "zero_motion", "zerom", 1465 | "char_set_names", "csnm", 1466 | "key_mouse", "kmous", 1467 | "mouse_info", "minfo", 1468 | "req_mouse_pos", "reqmp", 1469 | "get_mouse", "getm", 1470 | "set_a_foreground", "setaf", 1471 | "set_a_background", "setab", 1472 | "pkey_plab", "pfxl", 1473 | "device_type", "devt", 1474 | "code_set_init", "csin", 1475 | "set0_des_seq", "s0ds", 1476 | "set1_des_seq", "s1ds", 1477 | "set2_des_seq", "s2ds", 1478 | "set3_des_seq", "s3ds", 1479 | "set_lr_margin", "smglr", 1480 | "set_tb_margin", "smgtb", 1481 | "bit_image_repeat", "birep", 1482 | "bit_image_newline", "binel", 1483 | "bit_image_carriage_return", "bicr", 1484 | "color_names", "colornm", 1485 | "define_bit_image_region", "defbi", 1486 | "end_bit_image_region", "endbi", 1487 | "set_color_band", "setcolor", 1488 | "set_page_length", "slines", 1489 | "display_pc_char", "dispc", 1490 | "enter_pc_charset_mode", "smpch", 1491 | "exit_pc_charset_mode", "rmpch", 1492 | "enter_scancode_mode", "smsc", 1493 | "exit_scancode_mode", "rmsc", 1494 | "pc_term_options", "pctrm", 1495 | "scancode_escape", "scesc", 1496 | "alt_scancode_esc", "scesa", 1497 | "enter_horizontal_hl_mode", "ehhlm", 1498 | "enter_left_hl_mode", "elhlm", 1499 | "enter_low_hl_mode", "elohlm", 1500 | "enter_right_hl_mode", "erhlm", 1501 | "enter_top_hl_mode", "ethlm", 1502 | "enter_vertical_hl_mode", "evhlm", 1503 | "set_a_attributes", "sgr1", 1504 | "set_pglen_inch", "slength", 1505 | "termcap_init2", "OTi2", 1506 | "termcap_reset", "OTrs", 1507 | "linefeed_if_not_lf", "OTnl", 1508 | "backspace_if_not_bs", "OTbc", 1509 | "other_non_function_keys", "OTko", 1510 | "arrow_key_map", "OTma", 1511 | "acs_ulcorner", "OTG2", 1512 | "acs_llcorner", "OTG3", 1513 | "acs_urcorner", "OTG1", 1514 | "acs_lrcorner", "OTG4", 1515 | "acs_ltee", "OTGR", 1516 | "acs_rtee", "OTGL", 1517 | "acs_btee", "OTGU", 1518 | "acs_ttee", "OTGD", 1519 | "acs_hline", "OTGH", 1520 | "acs_vline", "OTGV", 1521 | "acs_plus", "OTGC", 1522 | "memory_lock", "meml", 1523 | "memory_unlock", "memu", 1524 | "box_chars_1", "box1", 1525 | } 1526 | -------------------------------------------------------------------------------- /cmd/infocmp/comp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | NAME=$1 6 | 7 | TERM=$NAME infocmp -1 -L -x > a.txt 8 | 9 | go build && TERM=$NAME ./infocmp > b.txt 10 | 11 | $HOME/src/misc/icdiff/icdiff a.txt b.txt 12 | -------------------------------------------------------------------------------- /cmd/infocmp/main.go: -------------------------------------------------------------------------------- 1 | // Application infocmp should have the same output as the standard Unix infocmp 2 | // -1 -L output. 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "fmt" 8 | "log" 9 | "os" 10 | "sort" 11 | "strings" 12 | "unicode" 13 | 14 | "github.com/xo/terminfo" 15 | ) 16 | 17 | var ( 18 | flagTerm = flag.String("term", os.Getenv("TERM"), "term name") 19 | flagExtended = flag.Bool("x", false, "extended options") 20 | ) 21 | 22 | func main() { 23 | flag.Parse() 24 | 25 | ti, err := terminfo.Load(*flagTerm) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | 30 | fmt.Printf("#\tReconstructed via %s from file: %s\n", strings.TrimPrefix(os.Args[0], "./"), ti.File) 31 | fmt.Printf("%s,\n", strings.TrimSpace(strings.Join(ti.Names, "|"))) 32 | 33 | process(ti.BoolCaps, ti.ExtBoolCaps, ti.BoolsM, terminfo.BoolCapName, nil) 34 | process( 35 | ti.NumCaps, ti.ExtNumCaps, ti.NumsM, terminfo.NumCapName, 36 | func(v interface{}) string { return fmt.Sprintf("#%d", v) }, 37 | ) 38 | process( 39 | ti.StringCaps, ti.ExtStringCaps, ti.StringsM, terminfo.StringCapName, 40 | func(v interface{}) string { return "=" + escape(v.([]byte)) }, 41 | ) 42 | } 43 | 44 | func process(x, y interface{}, m map[int]bool, name func(int) string, mask func(interface{}) string) { 45 | printIt(x, m, name, mask) 46 | if *flagExtended { 47 | printIt(y, nil, name, mask) 48 | } 49 | } 50 | 51 | // process walks the values in z, adding missing elements in m. a mask func can 52 | // be provided to format the values in z. 53 | func printIt(z interface{}, m map[int]bool, name func(int) string, mask func(interface{}) string) { 54 | var names []string 55 | x := make(map[string]string) 56 | switch v := z.(type) { 57 | case func() map[string]bool: 58 | for n, a := range v() { 59 | if !a { 60 | continue 61 | } 62 | var f string 63 | if mask != nil { 64 | f = mask(a) 65 | } 66 | x[n], names = f, append(names, n) 67 | } 68 | 69 | case func() map[string]int: 70 | for n, a := range v() { 71 | if a < 0 { 72 | continue 73 | } 74 | var f string 75 | if mask != nil { 76 | f = mask(a) 77 | } 78 | x[n], names = f, append(names, n) 79 | } 80 | 81 | case func() map[string][]byte: 82 | for n, a := range v() { 83 | if a == nil { 84 | continue 85 | } 86 | var f string 87 | if mask != nil { 88 | f = mask(a) 89 | } 90 | if n == "acs_chars" && strings.TrimSpace(strings.TrimPrefix(f, "=")) == "" { 91 | continue 92 | } 93 | x[n], names = f, append(names, n) 94 | } 95 | } 96 | 97 | // add missing 98 | for i := range m { 99 | n := name(i) 100 | x[n], names = "@", append(names, n) 101 | } 102 | 103 | // sort and print 104 | sort.Strings(names) 105 | for _, n := range names { 106 | fmt.Printf("\t%s%s,\n", n, x[n]) 107 | } 108 | } 109 | 110 | // peek peeks a byte. 111 | func peek(b []byte, pos, length int) byte { 112 | if pos < length { 113 | return b[pos] 114 | } 115 | return 0 116 | } 117 | 118 | func isprint(b byte) bool { 119 | return unicode.IsPrint(rune(b)) 120 | } 121 | 122 | func realprint(b byte) bool { 123 | return b < 127 && isprint(b) 124 | } 125 | 126 | func iscntrl(b byte) bool { 127 | return unicode.IsControl(rune(b)) 128 | } 129 | 130 | func realctl(b byte) bool { 131 | return b < 127 && iscntrl(b) 132 | } 133 | 134 | func isdigit(b byte) bool { 135 | return unicode.IsDigit(rune(b)) 136 | } 137 | 138 | // logic taken from _nc_tic_expand from ncurses-6.0/ncurses/tinfo/comp_expand.c 139 | func escape(buf []byte) string { 140 | length := len(buf) 141 | if length == 0 { 142 | return "" 143 | } 144 | 145 | var s []byte 146 | islong := length > 3 147 | for i := 0; i < length; i++ { 148 | ch := buf[i] 149 | switch { 150 | case ch == '%' && realprint(peek(buf, i+1, length)): 151 | s = append(s, buf[i], buf[i+1]) 152 | i++ 153 | 154 | case ch == 128: 155 | s = append(s, '\\', '0') 156 | 157 | case ch == '\033': 158 | s = append(s, '\\', 'E') 159 | 160 | case realprint(ch) && ch != ',' && ch != ':' && ch != '!' && ch != '^': 161 | s = append(s, ch) 162 | 163 | case ch == '\r' && (islong || (i == length-1 && length > 2)): 164 | s = append(s, '\\', 'r') 165 | 166 | case ch == '\n' && islong: 167 | s = append(s, '\\', 'n') 168 | 169 | case realctl(ch) && ch != '\\' && (!islong || isdigit(peek(buf, i+1, length))): 170 | s = append(s, '^', ch+'@') 171 | 172 | default: 173 | s = append(s, []byte(fmt.Sprintf("\\%03o", ch))...) 174 | } 175 | } 176 | 177 | return string(s) 178 | } 179 | -------------------------------------------------------------------------------- /cmd/infocmp/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | go build 6 | 7 | SI=$(which infocmp) 8 | BI=./infocmp 9 | 10 | OUT=.out 11 | 12 | mkdir -p $OUT 13 | 14 | TERMS=$(find /usr/share/terminfo -type f) 15 | for i in $TERMS; do 16 | NAME=$(basename $i) 17 | 18 | TERM=$NAME $SI -1 -L -x > $OUT/$NAME-orig.txt 19 | TERM=$NAME $BI -x > $OUT/$NAME-test.txt 20 | 21 | if [[ "$1" == "--no-acs-chars" ]]; then 22 | perl -pi -e 's/^\tacs_chars=.*\n//' $OUT/$NAME-orig.txt $OUT/$NAME-test.txt 23 | fi 24 | done 25 | 26 | md5sum $OUT/*-orig.txt > $OUT/orig.md5 27 | 28 | cp $OUT/orig.md5 $OUT/test.md5 29 | 30 | perl -pi -e 's/-orig\.txt$/-test.txt/g' $OUT/test.md5 31 | 32 | SED=sed 33 | if [[ "$(uname)" == "Darwin" ]]; then 34 | SED=gsed 35 | fi 36 | 37 | for i in $(md5sum -c $OUT/test.md5 |grep FAILED|$SED -s 's/: FAILED//'); do 38 | $HOME/src/misc/icdiff/icdiff $($SED -e 's/-test\.txt$/-orig.txt/' <<< "$i") $i 39 | done 40 | -------------------------------------------------------------------------------- /color.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | // ColorLevel is the color level supported by a terminal. 10 | type ColorLevel uint 11 | 12 | // ColorLevel values. 13 | const ( 14 | ColorLevelNone ColorLevel = iota 15 | ColorLevelBasic 16 | ColorLevelHundreds 17 | ColorLevelMillions 18 | ) 19 | 20 | // String satisfies the Stringer interface. 21 | func (c ColorLevel) String() string { 22 | switch c { 23 | case ColorLevelBasic: 24 | return "basic" 25 | case ColorLevelHundreds: 26 | return "hundreds" 27 | case ColorLevelMillions: 28 | return "millions" 29 | } 30 | return "none" 31 | } 32 | 33 | // ChromaFormatterName returns the github.com/alecthomas/chroma compatible 34 | // formatter name for the color level. 35 | func (c ColorLevel) ChromaFormatterName() string { 36 | switch c { 37 | case ColorLevelBasic: 38 | return "terminal" 39 | case ColorLevelHundreds: 40 | return "terminal256" 41 | case ColorLevelMillions: 42 | return "terminal16m" 43 | } 44 | return "noop" 45 | } 46 | 47 | // ColorLevelFromEnv returns the color level COLORTERM, FORCE_COLOR, 48 | // TERM_PROGRAM, or determined from the TERM environment variable. 49 | func ColorLevelFromEnv() (ColorLevel, error) { 50 | // check for overriding environment variables 51 | colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR") 52 | switch { 53 | case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit") || termProg == "Hyper": 54 | return ColorLevelMillions, nil 55 | case colorTerm != "" || forceColor != "": 56 | return ColorLevelBasic, nil 57 | case termProg == "Apple_Terminal": 58 | return ColorLevelHundreds, nil 59 | case termProg == "iTerm.app": 60 | ver := os.Getenv("TERM_PROGRAM_VERSION") 61 | if ver == "" { 62 | return ColorLevelHundreds, nil 63 | } 64 | i, err := strconv.Atoi(strings.Split(ver, ".")[0]) 65 | if err != nil { 66 | return ColorLevelNone, ErrInvalidTermProgramVersion 67 | } 68 | if i == 3 { 69 | return ColorLevelMillions, nil 70 | } 71 | return ColorLevelHundreds, nil 72 | } 73 | // otherwise determine from TERM's max_colors capability 74 | if term := os.Getenv("TERM"); term != "" { 75 | ti, err := Load(term) 76 | if err != nil { 77 | return ColorLevelNone, err 78 | } 79 | v, ok := ti.Nums[MaxColors] 80 | switch { 81 | case !ok || v <= 16: 82 | return ColorLevelNone, nil 83 | case ok && v >= 256: 84 | return ColorLevelHundreds, nil 85 | } 86 | } 87 | return ColorLevelBasic, nil 88 | } 89 | -------------------------------------------------------------------------------- /dec.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | const ( 8 | // maxFileLength is the max file length. 9 | maxFileLength = 4096 10 | // magic is the file magic for terminfo files. 11 | magic = 0o432 12 | // magicExtended is the file magic for terminfo files with the extended 13 | // number format. 14 | magicExtended = 0o1036 15 | ) 16 | 17 | // header fields. 18 | const ( 19 | fieldMagic = iota 20 | fieldNameSize 21 | fieldBoolCount 22 | fieldNumCount 23 | fieldStringCount 24 | fieldTableSize 25 | ) 26 | 27 | // header extended fields. 28 | const ( 29 | fieldExtBoolCount = iota 30 | fieldExtNumCount 31 | fieldExtStringCount 32 | fieldExtOffsetCount 33 | fieldExtTableSize 34 | ) 35 | 36 | // hasInvalidCaps determines if the capabilities in h are invalid. 37 | func hasInvalidCaps(h []int) bool { 38 | return h[fieldBoolCount] > CapCountBool || 39 | h[fieldNumCount] > CapCountNum || 40 | h[fieldStringCount] > CapCountString 41 | } 42 | 43 | // capLength returns the total length of the capabilities in bytes. 44 | func capLength(h []int) int { 45 | return h[fieldNameSize] + 46 | h[fieldBoolCount] + 47 | (h[fieldNameSize]+h[fieldBoolCount])%2 + // account for word align 48 | h[fieldNumCount]*2 + 49 | h[fieldStringCount]*2 + 50 | h[fieldTableSize] 51 | } 52 | 53 | // hasInvalidExtOffset determines if the extended offset field is valid. 54 | func hasInvalidExtOffset(h []int) bool { 55 | return h[fieldExtBoolCount]+ 56 | h[fieldExtNumCount]+ 57 | h[fieldExtStringCount]*2 != h[fieldExtOffsetCount] 58 | } 59 | 60 | // extCapLength returns the total length of extended capabilities in bytes. 61 | func extCapLength(h []int, numWidth int) int { 62 | return h[fieldExtBoolCount] + 63 | h[fieldExtBoolCount]%2 + // account for word align 64 | h[fieldExtNumCount]*(numWidth/8) + 65 | h[fieldExtOffsetCount]*2 + 66 | h[fieldExtTableSize] 67 | } 68 | 69 | // findNull finds the position of null in buf. 70 | func findNull(buf []byte, i int) int { 71 | for ; i < len(buf); i++ { 72 | if buf[i] == 0 { 73 | return i 74 | } 75 | } 76 | return -1 77 | } 78 | 79 | // readStrings decodes n strings from string data table buf using the indexes in idx. 80 | func readStrings(idx []int, buf []byte, n int) (map[int][]byte, int, error) { 81 | var last int 82 | m := make(map[int][]byte) 83 | for i := 0; i < n; i++ { 84 | start := idx[i] 85 | if start < 0 { 86 | continue 87 | } 88 | if end := findNull(buf, start); end != -1 { 89 | m[i], last = buf[start:end], end+1 90 | } else { 91 | return nil, 0, ErrInvalidStringTable 92 | } 93 | } 94 | return m, last, nil 95 | } 96 | 97 | // decoder holds state info while decoding a terminfo file. 98 | type decoder struct { 99 | buf []byte 100 | pos int 101 | n int 102 | } 103 | 104 | // readBytes reads the next n bytes of buf, incrementing pos by n. 105 | func (d *decoder) readBytes(n int) ([]byte, error) { 106 | if d.n < d.pos+n { 107 | return nil, ErrUnexpectedFileEnd 108 | } 109 | n, d.pos = d.pos, d.pos+n 110 | return d.buf[n:d.pos], nil 111 | } 112 | 113 | // readInts reads n number of ints with width w. 114 | func (d *decoder) readInts(n, w int) ([]int, error) { 115 | w /= 8 116 | l := n * w 117 | buf, err := d.readBytes(l) 118 | if err != nil { 119 | return nil, err 120 | } 121 | // align 122 | d.pos += d.pos % 2 123 | z := make([]int, n) 124 | for i, j := 0, 0; i < l; i, j = i+w, j+1 { 125 | switch w { 126 | case 1: 127 | z[i] = int(buf[i]) 128 | case 2: 129 | z[j] = int(int16(buf[i+1])<<8 | int16(buf[i])) 130 | case 4: 131 | z[j] = int(buf[i+3])<<24 | int(buf[i+2])<<16 | int(buf[i+1])<<8 | int(buf[i]) 132 | } 133 | } 134 | return z, nil 135 | } 136 | 137 | // readBools reads the next n bools. 138 | func (d *decoder) readBools(n int) (map[int]bool, map[int]bool, error) { 139 | buf, err := d.readInts(n, 8) 140 | if err != nil { 141 | return nil, nil, err 142 | } 143 | // process 144 | bools, boolsM := make(map[int]bool), make(map[int]bool) 145 | for i, b := range buf { 146 | bools[i] = b == 1 147 | if int8(b) == -2 { 148 | boolsM[i] = true 149 | } 150 | } 151 | return bools, boolsM, nil 152 | } 153 | 154 | // readNums reads the next n nums. 155 | func (d *decoder) readNums(n, w int) (map[int]int, map[int]bool, error) { 156 | buf, err := d.readInts(n, w) 157 | if err != nil { 158 | return nil, nil, err 159 | } 160 | // process 161 | nums, numsM := make(map[int]int), make(map[int]bool) 162 | for i := 0; i < n; i++ { 163 | nums[i] = buf[i] 164 | if buf[i] == -2 { 165 | numsM[i] = true 166 | } 167 | } 168 | return nums, numsM, nil 169 | } 170 | 171 | // readStringTable reads the string data for n strings and the accompanying data 172 | // table of length sz. 173 | func (d *decoder) readStringTable(n, sz int) ([][]byte, []int, error) { 174 | buf, err := d.readInts(n, 16) 175 | if err != nil { 176 | return nil, nil, err 177 | } 178 | // read string data table 179 | data, err := d.readBytes(sz) 180 | if err != nil { 181 | return nil, nil, err 182 | } 183 | // align 184 | d.pos += d.pos % 2 185 | // process 186 | s := make([][]byte, n) 187 | var m []int 188 | for i := 0; i < n; i++ { 189 | start := buf[i] 190 | if start == -2 { 191 | m = append(m, i) 192 | } else if start >= 0 { 193 | if end := findNull(data, start); end != -1 { 194 | s[i] = data[start:end] 195 | } else { 196 | return nil, nil, ErrInvalidStringTable 197 | } 198 | } 199 | } 200 | return s, m, nil 201 | } 202 | 203 | // readStrings reads the next n strings and processes the string data table of 204 | // length sz. 205 | func (d *decoder) readStrings(n, sz int) (map[int][]byte, map[int]bool, error) { 206 | s, m, err := d.readStringTable(n, sz) 207 | if err != nil { 208 | return nil, nil, err 209 | } 210 | strs := make(map[int][]byte) 211 | for k, v := range s { 212 | if k == AcsChars { 213 | v = canonicalizeAscChars(v) 214 | } 215 | strs[k] = v 216 | } 217 | strsM := make(map[int]bool, len(m)) 218 | for _, k := range m { 219 | strsM[k] = true 220 | } 221 | return strs, strsM, nil 222 | } 223 | 224 | // canonicalizeAscChars reorders chars to be unique, in order. 225 | // 226 | // see repair_ascc in ncurses-6.3/progs/dump_entry.c 227 | func canonicalizeAscChars(z []byte) []byte { 228 | var c []byte 229 | enc := make(map[byte]byte, len(z)/2) 230 | for i := 0; i < len(z); i += 2 { 231 | if _, ok := enc[z[i]]; !ok { 232 | a, b := z[i], z[i+1] 233 | // log.Printf(">>> a: %d %c, b: %d %c", a, a, b, b) 234 | c, enc[a] = append(c, b), b 235 | } 236 | } 237 | sort.Slice(c, func(i, j int) bool { 238 | return c[i] < c[j] 239 | }) 240 | r := make([]byte, 2*len(c)) 241 | for i := 0; i < len(c); i++ { 242 | r[i*2], r[i*2+1] = c[i], enc[c[i]] 243 | } 244 | return r 245 | } 246 | -------------------------------------------------------------------------------- /gen.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | 3 | package main 4 | 5 | import ( 6 | "archive/tar" 7 | "bufio" 8 | "bytes" 9 | "compress/gzip" 10 | "errors" 11 | "flag" 12 | "fmt" 13 | "go/format" 14 | "io" 15 | "io/ioutil" 16 | "log" 17 | "net/http" 18 | "os" 19 | "path/filepath" 20 | "regexp" 21 | "sort" 22 | "strconv" 23 | "strings" 24 | "unicode" 25 | 26 | "github.com/kenshaw/snaker" 27 | ) 28 | 29 | func main() { 30 | out := flag.String("out", "capvals.go", "out file") 31 | cache := flag.String("cache", ".cache", "cache directory") 32 | ver := flag.String("ver", "", "version") 33 | flag.Parse() 34 | if err := run(*out, *cache, *ver); err != nil { 35 | fmt.Fprintf(os.Stderr, "error: %v\n", err) 36 | os.Exit(1) 37 | } 38 | } 39 | 40 | func run(dest, cache, ver string) error { 41 | // get version 42 | ver, err := getVer(ver) 43 | if err != nil { 44 | return err 45 | } 46 | // get archive 47 | buf, err := get(cache, ver) 48 | if err != nil { 49 | return err 50 | } 51 | // load caps file 52 | caps, err := load(buf, ver) 53 | if err != nil { 54 | return err 55 | } 56 | // process caps 57 | buf, err = processCaps(caps) 58 | if err != nil { 59 | return err 60 | } 61 | // write 62 | buf, err = format.Source(buf) 63 | if err != nil { 64 | return err 65 | } 66 | return ioutil.WriteFile(dest, buf, 0o644) 67 | } 68 | 69 | func getVer(ver string) (string, error) { 70 | if ver != "" { 71 | return ver, nil 72 | } 73 | res, err := http.Get("https://ftp.gnu.org/pub/gnu/ncurses/") 74 | if err != nil { 75 | return "", err 76 | } 77 | defer res.Body.Close() 78 | buf, err := io.ReadAll(res.Body) 79 | if err != nil { 80 | return "", err 81 | } 82 | m := verRE.FindAllStringSubmatch(string(buf), -1) 83 | sort.Slice(m, func(i, j int) bool { 84 | va, _ := strconv.Atoi(m[i][1]) 85 | vb, _ := strconv.Atoi(m[j][1]) 86 | if va == vb { 87 | va, _ = strconv.Atoi(m[i][2]) 88 | vb, _ = strconv.Atoi(m[j][2]) 89 | return va > vb 90 | } 91 | return va > vb 92 | }) 93 | return m[0][1] + "." + m[0][2], nil 94 | } 95 | 96 | var verRE = regexp.MustCompile(`href="ncurses-([0-9]+)\.([0-9]+)\.tar\.gz"`) 97 | 98 | // get retrieves a file either from the the http path, or from disk. 99 | func get(cache, ver string) ([]byte, error) { 100 | file := fmt.Sprintf("https://ftp.gnu.org/pub/gnu/ncurses/ncurses-%s.tar.gz", ver) 101 | if err := os.MkdirAll(cache, 0o755); err != nil { 102 | return nil, err 103 | } 104 | // check if the file exists 105 | cacheFile := filepath.Join(cache, filepath.Base(file)) 106 | fi, err := os.Stat(cacheFile) 107 | if err == nil && !fi.IsDir() { 108 | log.Printf("loading %s", cacheFile) 109 | return ioutil.ReadFile(cacheFile) 110 | } 111 | // retrieve 112 | log.Printf("retrieving %s", file) 113 | res, err := http.Get(file) 114 | if err != nil { 115 | return nil, err 116 | } 117 | defer res.Body.Close() 118 | // read 119 | buf, err := ioutil.ReadAll(res.Body) 120 | if err != nil { 121 | return nil, err 122 | } 123 | // cache 124 | log.Printf("saving %s", cacheFile) 125 | if err := ioutil.WriteFile(cacheFile, buf, 0o644); err != nil { 126 | return nil, err 127 | } 128 | return buf, nil 129 | } 130 | 131 | // load extracts a file from a tar.gz. 132 | func load(buf []byte, ver string) ([]byte, error) { 133 | file := fmt.Sprintf("ncurses-%s/include/Caps", ver) 134 | // create gzip reader 135 | gr, err := gzip.NewReader(bytes.NewReader(buf)) 136 | if err != nil { 137 | return nil, err 138 | } 139 | defer gr.Close() 140 | // walk files in tar 141 | tr := tar.NewReader(gr) 142 | for { 143 | h, err := tr.Next() 144 | if err == io.EOF { 145 | break 146 | } else if err != nil { 147 | return nil, err 148 | } 149 | // found file, read contents 150 | if h.Name == file { 151 | b := bytes.NewBuffer(make([]byte, h.Size)) 152 | var n int64 153 | n, err = io.Copy(b, tr) 154 | if err != nil { 155 | return nil, err 156 | } 157 | // check that all bytes were copied 158 | if n != h.Size { 159 | return nil, errors.New("could not read entire file") 160 | } 161 | return b.Bytes(), nil 162 | } 163 | } 164 | return nil, fmt.Errorf("could not load file %s", file) 165 | } 166 | 167 | // processCaps processes the data in the Caps file. 168 | func processCaps(capsBuf []byte) ([]byte, error) { 169 | // create scanner 170 | s := bufio.NewScanner(bytes.NewReader(capsBuf)) 171 | s.Buffer(make([]byte, 1024*1024), 1024*1024) 172 | // storage 173 | bools, nums, strs := new(bytes.Buffer), new(bytes.Buffer), new(bytes.Buffer) 174 | var boolCount, numCount, stringCount int 175 | var lastBool, lastNum, lastString string 176 | var boolNames, numNames, stringNames []string 177 | // process caps 178 | var n int 179 | for s.Scan() { 180 | // read line 181 | line := strings.TrimSpace(commentRE.ReplaceAllString(strings.Trim(s.Text(), "\x00"), "")) 182 | if len(line) == 0 || strings.HasPrefix(line, "capalias") || strings.HasPrefix(line, "infoalias") { 183 | continue 184 | } 185 | // split line's columns 186 | row := make([]string, 8) 187 | for i := 0; i < 7; i++ { 188 | start := strings.IndexFunc(line, unicode.IsSpace) 189 | end := strings.IndexFunc(line[start:], notSpace) 190 | row[i] = strings.TrimSpace(line[:start+end]) 191 | line = line[start+end:] 192 | } 193 | row[7] = strings.TrimSpace(line) 194 | // manipulation 195 | var buf *bytes.Buffer 196 | var names *[]string 197 | var typ, isFirst, prefix, suffix string 198 | // format variable name 199 | name := snaker.SnakeToCamel(row[0]) 200 | switch row[2] { 201 | case "bool": 202 | if boolCount == 0 { 203 | isFirst = " = iota" 204 | } 205 | buf, names, lastBool, prefix, suffix = bools, &boolNames, name, "indicates", "" 206 | typ = "bool" 207 | boolCount++ 208 | case "num": 209 | if numCount == 0 { 210 | isFirst = " = iota" 211 | } 212 | buf, names, lastNum, prefix, suffix = nums, &numNames, name, "is", "" 213 | typ = "num" 214 | numCount++ 215 | case "str": 216 | if stringCount == 0 { 217 | isFirst = " = iota" 218 | } 219 | buf, names, lastString, prefix, suffix = strs, &stringNames, name, "is the", "" 220 | typ = "string" 221 | stringCount++ 222 | default: 223 | return nil, fmt.Errorf("line %d is invalid, has type: %s", n, row[2]) 224 | } 225 | if isFirst == "" { 226 | buf.WriteString("\n") 227 | } 228 | fmt.Fprintf(buf, "// The %s [%s, %s] %s capability %s\n%s%s", name, row[0], row[1], typ, formatComment(row[7], prefix, suffix), name, isFirst) 229 | *names = append(*names, row[0], row[1]) 230 | n++ 231 | } 232 | if err := s.Err(); err != nil { 233 | return nil, err 234 | } 235 | f := new(bytes.Buffer) 236 | f.WriteString(hdr) 237 | // add consts 238 | typs := []string{"Bool", "Num", "String"} 239 | for i, b := range []*bytes.Buffer{bools, nums, strs} { 240 | f.WriteString(fmt.Sprintf("// %s capabilities.\nconst (\n", typs[i])) 241 | b.WriteTo(f) 242 | f.WriteString(")\n") 243 | } 244 | // add counts 245 | f.WriteString("const (\n") 246 | f.WriteString(fmt.Sprintf("// CapCountBool is the count of bool capabilities.\nCapCountBool = %s+1\n", lastBool)) 247 | f.WriteString(fmt.Sprintf("// CapCountNum is the count of num capabilities.\nCapCountNum = %s+1\n", lastNum)) 248 | f.WriteString(fmt.Sprintf("// CapCountString is the count of string capabilities.\nCapCountString = %s+1\n", lastString)) 249 | f.WriteString(")\n") 250 | // add names 251 | z := []string{"bool", "num", "string"} 252 | for n, s := range [][]string{boolNames, numNames, stringNames} { 253 | y := z[n] 254 | f.WriteString(fmt.Sprintf("// %sCapNames are the %s term cap names.\n", y, y)) 255 | f.WriteString(fmt.Sprintf("var %sCapNames = [...]string{\n", y)) 256 | for i := 0; i < len(s); i += 2 { 257 | f.WriteString(fmt.Sprintf(`"%s", "%s",`+"\n", s[i], s[i+1])) 258 | } 259 | f.WriteString("}\n") 260 | } 261 | return f.Bytes(), nil 262 | } 263 | 264 | // formatComment formats comments with prefix and suffix. 265 | func formatComment(s, prefix, suffix string) string { 266 | s = strings.TrimPrefix(s, prefix) 267 | s = strings.TrimSuffix(s, ".") 268 | s = strings.TrimSuffix(s, suffix) 269 | return strings.TrimSpace(prefix+" "+s+" "+suffix) + "." 270 | } 271 | 272 | func notSpace(r rune) bool { 273 | return !unicode.IsSpace(r) 274 | } 275 | 276 | var commentRE = regexp.MustCompile(`^#.*`) 277 | 278 | const hdr = `package terminfo 279 | // Code generated by gen.go. DO NOT EDIT. 280 | ` 281 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/xo/terminfo 2 | 3 | go 1.19 4 | 5 | require golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= 2 | golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= 3 | -------------------------------------------------------------------------------- /load.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | import ( 4 | "os" 5 | "os/user" 6 | "path" 7 | "strings" 8 | "sync" 9 | ) 10 | 11 | // termCache is the terminfo cache. 12 | var termCache = struct { 13 | db map[string]*Terminfo 14 | sync.RWMutex 15 | }{ 16 | db: make(map[string]*Terminfo), 17 | } 18 | 19 | // Load follows the behavior described in terminfo(5) to find correct the 20 | // terminfo file using the name, reads the file and then returns a Terminfo 21 | // struct that describes the file. 22 | func Load(name string) (*Terminfo, error) { 23 | if name == "" { 24 | return nil, ErrEmptyTermName 25 | } 26 | termCache.RLock() 27 | ti, ok := termCache.db[name] 28 | termCache.RUnlock() 29 | if ok { 30 | return ti, nil 31 | } 32 | var checkDirs []string 33 | // check $TERMINFO 34 | if dir := os.Getenv("TERMINFO"); dir != "" { 35 | checkDirs = append(checkDirs, dir) 36 | } 37 | // check $HOME/.terminfo 38 | u, err := user.Current() 39 | if err != nil { 40 | return nil, err 41 | } 42 | checkDirs = append(checkDirs, path.Join(u.HomeDir, ".terminfo")) 43 | // check $TERMINFO_DIRS 44 | if dirs := os.Getenv("TERMINFO_DIRS"); dirs != "" { 45 | checkDirs = append(checkDirs, strings.Split(dirs, ":")...) 46 | } 47 | // check fallback directories 48 | checkDirs = append(checkDirs, "/etc/terminfo", "/lib/terminfo", "/usr/share/terminfo") 49 | for _, dir := range checkDirs { 50 | ti, err = Open(dir, name) 51 | if err != nil && err != ErrFileNotFound && !os.IsNotExist(err) { 52 | return nil, err 53 | } else if err == nil { 54 | return ti, nil 55 | } 56 | } 57 | return nil, ErrDatabaseDirectoryNotFound 58 | } 59 | 60 | // LoadFromEnv loads the terminal info based on the name contained in 61 | // environment variable TERM. 62 | func LoadFromEnv() (*Terminfo, error) { 63 | return Load(os.Getenv("TERM")) 64 | } 65 | -------------------------------------------------------------------------------- /param.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "strconv" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | // parametizer represents the a scan state for a parameterized string. 13 | type parametizer struct { 14 | // z is the string to parameterize 15 | z []byte 16 | // pos is the current position in s. 17 | pos int 18 | // nest is the current nest level. 19 | nest int 20 | // s is the variable stack. 21 | s stack 22 | // skipElse keeps the state of skipping else. 23 | skipElse bool 24 | // buf is the result buffer. 25 | buf *bytes.Buffer 26 | // params are the parameters to interpolate. 27 | params [9]interface{} 28 | // vars are dynamic variables. 29 | vars [26]interface{} 30 | } 31 | 32 | // staticVars are the static, global variables. 33 | var staticVars = struct { 34 | vars [26]interface{} 35 | sync.Mutex 36 | }{} 37 | 38 | var parametizerPool = sync.Pool{ 39 | New: func() interface{} { 40 | p := new(parametizer) 41 | p.buf = bytes.NewBuffer(make([]byte, 0, 45)) 42 | return p 43 | }, 44 | } 45 | 46 | // newParametizer returns a new initialized parametizer from the pool. 47 | func newParametizer(z []byte) *parametizer { 48 | p := parametizerPool.Get().(*parametizer) 49 | p.z = z 50 | return p 51 | } 52 | 53 | // reset resets the parametizer. 54 | func (p *parametizer) reset() { 55 | p.pos, p.nest = 0, 0 56 | p.s.reset() 57 | p.buf.Reset() 58 | p.params, p.vars = [9]interface{}{}, [26]interface{}{} 59 | parametizerPool.Put(p) 60 | } 61 | 62 | // stateFn represents the state of the scanner as a function that returns the 63 | // next state. 64 | type stateFn func() stateFn 65 | 66 | // exec executes the parameterizer, interpolating the supplied parameters. 67 | func (p *parametizer) exec() string { 68 | for state := p.scanTextFn; state != nil; { 69 | state = state() 70 | } 71 | return p.buf.String() 72 | } 73 | 74 | // peek returns the next byte. 75 | func (p *parametizer) peek() (byte, error) { 76 | if p.pos >= len(p.z) { 77 | return 0, io.EOF 78 | } 79 | return p.z[p.pos], nil 80 | } 81 | 82 | // writeFrom writes the characters from ppos to pos to the buffer. 83 | func (p *parametizer) writeFrom(ppos int) { 84 | if p.pos > ppos { 85 | // append remaining characters. 86 | p.buf.Write(p.z[ppos:p.pos]) 87 | } 88 | } 89 | 90 | func (p *parametizer) scanTextFn() stateFn { 91 | ppos := p.pos 92 | for { 93 | ch, err := p.peek() 94 | if err != nil { 95 | p.writeFrom(ppos) 96 | return nil 97 | } 98 | if ch == '%' { 99 | p.writeFrom(ppos) 100 | p.pos++ 101 | return p.scanCodeFn 102 | } 103 | p.pos++ 104 | } 105 | } 106 | 107 | func (p *parametizer) scanCodeFn() stateFn { 108 | ch, err := p.peek() 109 | if err != nil { 110 | return nil 111 | } 112 | switch ch { 113 | case '%': 114 | p.buf.WriteByte('%') 115 | case ':': 116 | // this character is used to avoid interpreting "%-" and "%+" as operators. 117 | // the next character is where the format really begins. 118 | p.pos++ 119 | _, err = p.peek() 120 | if err != nil { 121 | return nil 122 | } 123 | return p.scanFormatFn 124 | case '#', ' ', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': 125 | return p.scanFormatFn 126 | case 'o': 127 | p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 8)) 128 | case 'd': 129 | p.buf.WriteString(strconv.Itoa(p.s.popInt())) 130 | case 'x': 131 | p.buf.WriteString(strconv.FormatInt(int64(p.s.popInt()), 16)) 132 | case 'X': 133 | p.buf.WriteString(strings.ToUpper(strconv.FormatInt(int64(p.s.popInt()), 16))) 134 | case 's': 135 | p.buf.WriteString(p.s.popString()) 136 | case 'c': 137 | p.buf.WriteByte(p.s.popByte()) 138 | case 'p': 139 | p.pos++ 140 | return p.pushParamFn 141 | case 'P': 142 | p.pos++ 143 | return p.setDsVarFn 144 | case 'g': 145 | p.pos++ 146 | return p.getDsVarFn 147 | case '\'': 148 | p.pos++ 149 | ch, err = p.peek() 150 | if err != nil { 151 | return nil 152 | } 153 | p.s.push(ch) 154 | // skip the '\'' 155 | p.pos++ 156 | case '{': 157 | p.pos++ 158 | return p.pushIntfn 159 | case 'l': 160 | p.s.push(len(p.s.popString())) 161 | case '+': 162 | bi, ai := p.s.popInt(), p.s.popInt() 163 | p.s.push(ai + bi) 164 | case '-': 165 | bi, ai := p.s.popInt(), p.s.popInt() 166 | p.s.push(ai - bi) 167 | case '*': 168 | bi, ai := p.s.popInt(), p.s.popInt() 169 | p.s.push(ai * bi) 170 | case '/': 171 | bi, ai := p.s.popInt(), p.s.popInt() 172 | if bi != 0 { 173 | p.s.push(ai / bi) 174 | } else { 175 | p.s.push(0) 176 | } 177 | case 'm': 178 | bi, ai := p.s.popInt(), p.s.popInt() 179 | if bi != 0 { 180 | p.s.push(ai % bi) 181 | } else { 182 | p.s.push(0) 183 | } 184 | case '&': 185 | bi, ai := p.s.popInt(), p.s.popInt() 186 | p.s.push(ai & bi) 187 | case '|': 188 | bi, ai := p.s.popInt(), p.s.popInt() 189 | p.s.push(ai | bi) 190 | case '^': 191 | bi, ai := p.s.popInt(), p.s.popInt() 192 | p.s.push(ai ^ bi) 193 | case '=': 194 | bi, ai := p.s.popInt(), p.s.popInt() 195 | p.s.push(ai == bi) 196 | case '>': 197 | bi, ai := p.s.popInt(), p.s.popInt() 198 | p.s.push(ai > bi) 199 | case '<': 200 | bi, ai := p.s.popInt(), p.s.popInt() 201 | p.s.push(ai < bi) 202 | case 'A': 203 | bi, ai := p.s.popBool(), p.s.popBool() 204 | p.s.push(ai && bi) 205 | case 'O': 206 | bi, ai := p.s.popBool(), p.s.popBool() 207 | p.s.push(ai || bi) 208 | case '!': 209 | p.s.push(!p.s.popBool()) 210 | case '~': 211 | p.s.push(^p.s.popInt()) 212 | case 'i': 213 | for i := range p.params[:2] { 214 | if n, ok := p.params[i].(int); ok { 215 | p.params[i] = n + 1 216 | } 217 | } 218 | case '?', ';': 219 | case 't': 220 | return p.scanThenFn 221 | case 'e': 222 | p.skipElse = true 223 | return p.skipTextFn 224 | } 225 | p.pos++ 226 | return p.scanTextFn 227 | } 228 | 229 | func (p *parametizer) scanFormatFn() stateFn { 230 | // the character was already read, so no need to check the error. 231 | ch, _ := p.peek() 232 | // 6 should be the maximum length of a format string, for example "%:-9.9d". 233 | f := []byte{'%', ch, 0, 0, 0, 0} 234 | var err error 235 | for { 236 | p.pos++ 237 | ch, err = p.peek() 238 | if err != nil { 239 | return nil 240 | } 241 | f = append(f, ch) 242 | switch ch { 243 | case 'o', 'd', 'x', 'X': 244 | fmt.Fprintf(p.buf, string(f), p.s.popInt()) 245 | break 246 | case 's': 247 | fmt.Fprintf(p.buf, string(f), p.s.popString()) 248 | break 249 | case 'c': 250 | fmt.Fprintf(p.buf, string(f), p.s.popByte()) 251 | break 252 | } 253 | } 254 | p.pos++ 255 | return p.scanTextFn 256 | } 257 | 258 | func (p *parametizer) pushParamFn() stateFn { 259 | ch, err := p.peek() 260 | if err != nil { 261 | return nil 262 | } 263 | if ai := int(ch - '1'); ai >= 0 && ai < len(p.params) { 264 | p.s.push(p.params[ai]) 265 | } else { 266 | p.s.push(0) 267 | } 268 | // skip the '}' 269 | p.pos++ 270 | return p.scanTextFn 271 | } 272 | 273 | func (p *parametizer) setDsVarFn() stateFn { 274 | ch, err := p.peek() 275 | if err != nil { 276 | return nil 277 | } 278 | if ch >= 'A' && ch <= 'Z' { 279 | staticVars.Lock() 280 | staticVars.vars[int(ch-'A')] = p.s.pop() 281 | staticVars.Unlock() 282 | } else if ch >= 'a' && ch <= 'z' { 283 | p.vars[int(ch-'a')] = p.s.pop() 284 | } 285 | p.pos++ 286 | return p.scanTextFn 287 | } 288 | 289 | func (p *parametizer) getDsVarFn() stateFn { 290 | ch, err := p.peek() 291 | if err != nil { 292 | return nil 293 | } 294 | var a byte 295 | if ch >= 'A' && ch <= 'Z' { 296 | a = 'A' 297 | } else if ch >= 'a' && ch <= 'z' { 298 | a = 'a' 299 | } 300 | staticVars.Lock() 301 | p.s.push(staticVars.vars[int(ch-a)]) 302 | staticVars.Unlock() 303 | p.pos++ 304 | return p.scanTextFn 305 | } 306 | 307 | func (p *parametizer) pushIntfn() stateFn { 308 | var ai int 309 | for { 310 | ch, err := p.peek() 311 | if err != nil { 312 | return nil 313 | } 314 | p.pos++ 315 | if ch < '0' || ch > '9' { 316 | p.s.push(ai) 317 | return p.scanTextFn 318 | } 319 | ai = (ai * 10) + int(ch-'0') 320 | } 321 | } 322 | 323 | func (p *parametizer) scanThenFn() stateFn { 324 | p.pos++ 325 | if p.s.popBool() { 326 | return p.scanTextFn 327 | } 328 | p.skipElse = false 329 | return p.skipTextFn 330 | } 331 | 332 | func (p *parametizer) skipTextFn() stateFn { 333 | for { 334 | ch, err := p.peek() 335 | if err != nil { 336 | return nil 337 | } 338 | p.pos++ 339 | if ch == '%' { 340 | break 341 | } 342 | } 343 | if p.skipElse { 344 | return p.skipElseFn 345 | } 346 | return p.skipThenFn 347 | } 348 | 349 | func (p *parametizer) skipThenFn() stateFn { 350 | ch, err := p.peek() 351 | if err != nil { 352 | return nil 353 | } 354 | p.pos++ 355 | switch ch { 356 | case ';': 357 | if p.nest == 0 { 358 | return p.scanTextFn 359 | } 360 | p.nest-- 361 | case '?': 362 | p.nest++ 363 | case 'e': 364 | if p.nest == 0 { 365 | return p.scanTextFn 366 | } 367 | } 368 | return p.skipTextFn 369 | } 370 | 371 | func (p *parametizer) skipElseFn() stateFn { 372 | ch, err := p.peek() 373 | if err != nil { 374 | return nil 375 | } 376 | p.pos++ 377 | switch ch { 378 | case ';': 379 | if p.nest == 0 { 380 | return p.scanTextFn 381 | } 382 | p.nest-- 383 | case '?': 384 | p.nest++ 385 | } 386 | return p.skipTextFn 387 | } 388 | 389 | // Printf evaluates a parameterized terminfo value z, interpolating params. 390 | func Printf(z []byte, params ...interface{}) string { 391 | p := newParametizer(z) 392 | defer p.reset() 393 | // make sure we always have 9 parameters -- makes it easier 394 | // later to skip checks and its faster 395 | for i := 0; i < len(p.params) && i < len(params); i++ { 396 | p.params[i] = params[i] 397 | } 398 | return p.exec() 399 | } 400 | 401 | // Fprintf evaluates a parameterized terminfo value z, interpolating params and 402 | // writing to w. 403 | func Fprintf(w io.Writer, z []byte, params ...interface{}) { 404 | w.Write([]byte(Printf(z, params...))) 405 | } 406 | -------------------------------------------------------------------------------- /stack.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | type stack []interface{} 4 | 5 | func (s *stack) push(v interface{}) { 6 | *s = append(*s, v) 7 | } 8 | 9 | func (s *stack) pop() interface{} { 10 | if len(*s) == 0 { 11 | return nil 12 | } 13 | v := (*s)[len(*s)-1] 14 | *s = (*s)[:len(*s)-1] 15 | return v 16 | } 17 | 18 | func (s *stack) popInt() int { 19 | if i, ok := s.pop().(int); ok { 20 | return i 21 | } 22 | return 0 23 | } 24 | 25 | func (s *stack) popBool() bool { 26 | if b, ok := s.pop().(bool); ok { 27 | return b 28 | } 29 | return false 30 | } 31 | 32 | func (s *stack) popByte() byte { 33 | if b, ok := s.pop().(byte); ok { 34 | return b 35 | } 36 | return 0 37 | } 38 | 39 | func (s *stack) popString() string { 40 | if a, ok := s.pop().(string); ok { 41 | return a 42 | } 43 | return "" 44 | } 45 | 46 | func (s *stack) reset() { 47 | *s = (*s)[:0] 48 | } 49 | -------------------------------------------------------------------------------- /terminfo.go: -------------------------------------------------------------------------------- 1 | // Package terminfo implements reading terminfo files in pure go. 2 | package terminfo 3 | 4 | //go:generate go run gen.go 5 | 6 | import ( 7 | "io" 8 | "io/ioutil" 9 | "path" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | // Error is a terminfo error. 15 | type Error string 16 | 17 | // Error satisfies the error interface. 18 | func (err Error) Error() string { 19 | return string(err) 20 | } 21 | 22 | const ( 23 | // ErrInvalidFileSize is the invalid file size error. 24 | ErrInvalidFileSize Error = "invalid file size" 25 | // ErrUnexpectedFileEnd is the unexpected file end error. 26 | ErrUnexpectedFileEnd Error = "unexpected file end" 27 | // ErrInvalidStringTable is the invalid string table error. 28 | ErrInvalidStringTable Error = "invalid string table" 29 | // ErrInvalidMagic is the invalid magic error. 30 | ErrInvalidMagic Error = "invalid magic" 31 | // ErrInvalidHeader is the invalid header error. 32 | ErrInvalidHeader Error = "invalid header" 33 | // ErrInvalidNames is the invalid names error. 34 | ErrInvalidNames Error = "invalid names" 35 | // ErrInvalidExtendedHeader is the invalid extended header error. 36 | ErrInvalidExtendedHeader Error = "invalid extended header" 37 | // ErrEmptyTermName is the empty term name error. 38 | ErrEmptyTermName Error = "empty term name" 39 | // ErrDatabaseDirectoryNotFound is the database directory not found error. 40 | ErrDatabaseDirectoryNotFound Error = "database directory not found" 41 | // ErrFileNotFound is the file not found error. 42 | ErrFileNotFound Error = "file not found" 43 | // ErrInvalidTermProgramVersion is the invalid TERM_PROGRAM_VERSION error. 44 | ErrInvalidTermProgramVersion Error = "invalid TERM_PROGRAM_VERSION" 45 | ) 46 | 47 | // Terminfo describes a terminal's capabilities. 48 | type Terminfo struct { 49 | // File is the original source file. 50 | File string 51 | // Names are the provided cap names. 52 | Names []string 53 | // Bools are the bool capabilities. 54 | Bools map[int]bool 55 | // BoolsM are the missing bool capabilities. 56 | BoolsM map[int]bool 57 | // Nums are the num capabilities. 58 | Nums map[int]int 59 | // NumsM are the missing num capabilities. 60 | NumsM map[int]bool 61 | // Strings are the string capabilities. 62 | Strings map[int][]byte 63 | // StringsM are the missing string capabilities. 64 | StringsM map[int]bool 65 | // ExtBools are the extended bool capabilities. 66 | ExtBools map[int]bool 67 | // ExtBoolsNames is the map of extended bool capabilities to their index. 68 | ExtBoolNames map[int][]byte 69 | // ExtNums are the extended num capabilities. 70 | ExtNums map[int]int 71 | // ExtNumsNames is the map of extended num capabilities to their index. 72 | ExtNumNames map[int][]byte 73 | // ExtStrings are the extended string capabilities. 74 | ExtStrings map[int][]byte 75 | // ExtStringsNames is the map of extended string capabilities to their index. 76 | ExtStringNames map[int][]byte 77 | } 78 | 79 | // Decode decodes the terminfo data contained in buf. 80 | func Decode(buf []byte) (*Terminfo, error) { 81 | var err error 82 | // check max file length 83 | if len(buf) >= maxFileLength { 84 | return nil, ErrInvalidFileSize 85 | } 86 | d := &decoder{ 87 | buf: buf, 88 | n: len(buf), 89 | } 90 | // read header 91 | h, err := d.readInts(6, 16) 92 | if err != nil { 93 | return nil, err 94 | } 95 | var numWidth int 96 | // check magic 97 | switch { 98 | case h[fieldMagic] == magic: 99 | numWidth = 16 100 | case h[fieldMagic] == magicExtended: 101 | numWidth = 32 102 | default: 103 | return nil, ErrInvalidMagic 104 | } 105 | // check header 106 | if hasInvalidCaps(h) { 107 | return nil, ErrInvalidHeader 108 | } 109 | // check remaining length 110 | if d.n-d.pos < capLength(h) { 111 | return nil, ErrUnexpectedFileEnd 112 | } 113 | // read names 114 | names, err := d.readBytes(h[fieldNameSize]) 115 | if err != nil { 116 | return nil, err 117 | } 118 | // check name is terminated properly 119 | i := findNull(names, 0) 120 | if i == -1 { 121 | return nil, ErrInvalidNames 122 | } 123 | names = names[:i] 124 | // read bool caps 125 | bools, boolsM, err := d.readBools(h[fieldBoolCount]) 126 | if err != nil { 127 | return nil, err 128 | } 129 | // read num caps 130 | nums, numsM, err := d.readNums(h[fieldNumCount], numWidth) 131 | if err != nil { 132 | return nil, err 133 | } 134 | // read string caps 135 | strs, strsM, err := d.readStrings(h[fieldStringCount], h[fieldTableSize]) 136 | if err != nil { 137 | return nil, err 138 | } 139 | ti := &Terminfo{ 140 | Names: strings.Split(string(names), "|"), 141 | Bools: bools, 142 | BoolsM: boolsM, 143 | Nums: nums, 144 | NumsM: numsM, 145 | Strings: strs, 146 | StringsM: strsM, 147 | } 148 | // at the end of file, so no extended caps 149 | if d.pos >= d.n { 150 | return ti, nil 151 | } 152 | // decode extended header 153 | eh, err := d.readInts(5, 16) 154 | if err != nil { 155 | return nil, err 156 | } 157 | // check extended offset field 158 | if hasInvalidExtOffset(eh) { 159 | return nil, ErrInvalidExtendedHeader 160 | } 161 | // check extended cap lengths 162 | if d.n-d.pos != extCapLength(eh, numWidth) { 163 | return nil, ErrInvalidExtendedHeader 164 | } 165 | // read extended bool caps 166 | ti.ExtBools, _, err = d.readBools(eh[fieldExtBoolCount]) 167 | if err != nil { 168 | return nil, err 169 | } 170 | // read extended num caps 171 | ti.ExtNums, _, err = d.readNums(eh[fieldExtNumCount], numWidth) 172 | if err != nil { 173 | return nil, err 174 | } 175 | // read extended string data table indexes 176 | extIndexes, err := d.readInts(eh[fieldExtOffsetCount], 16) 177 | if err != nil { 178 | return nil, err 179 | } 180 | // read string data table 181 | extData, err := d.readBytes(eh[fieldExtTableSize]) 182 | if err != nil { 183 | return nil, err 184 | } 185 | // precautionary check that exactly at end of file 186 | if d.pos != d.n { 187 | return nil, ErrUnexpectedFileEnd 188 | } 189 | var last int 190 | // read extended string caps 191 | ti.ExtStrings, last, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) 192 | if err != nil { 193 | return nil, err 194 | } 195 | extIndexes, extData = extIndexes[eh[fieldExtStringCount]:], extData[last:] 196 | // read extended bool names 197 | ti.ExtBoolNames, _, err = readStrings(extIndexes, extData, eh[fieldExtBoolCount]) 198 | if err != nil { 199 | return nil, err 200 | } 201 | extIndexes = extIndexes[eh[fieldExtBoolCount]:] 202 | // read extended num names 203 | ti.ExtNumNames, _, err = readStrings(extIndexes, extData, eh[fieldExtNumCount]) 204 | if err != nil { 205 | return nil, err 206 | } 207 | extIndexes = extIndexes[eh[fieldExtNumCount]:] 208 | // read extended string names 209 | ti.ExtStringNames, _, err = readStrings(extIndexes, extData, eh[fieldExtStringCount]) 210 | if err != nil { 211 | return nil, err 212 | } 213 | // extIndexes = extIndexes[eh[fieldExtStringCount]:] 214 | return ti, nil 215 | } 216 | 217 | // Open reads the terminfo file name from the specified directory dir. 218 | func Open(dir, name string) (*Terminfo, error) { 219 | var err error 220 | var buf []byte 221 | var filename string 222 | for _, f := range []string{ 223 | path.Join(dir, name[0:1], name), 224 | path.Join(dir, strconv.FormatUint(uint64(name[0]), 16), name), 225 | } { 226 | buf, err = ioutil.ReadFile(f) 227 | if err == nil { 228 | filename = f 229 | break 230 | } 231 | } 232 | if buf == nil { 233 | return nil, ErrFileNotFound 234 | } 235 | // decode 236 | ti, err := Decode(buf) 237 | if err != nil { 238 | return nil, err 239 | } 240 | // save original file name 241 | ti.File = filename 242 | // add to cache 243 | termCache.Lock() 244 | for _, n := range ti.Names { 245 | termCache.db[n] = ti 246 | } 247 | termCache.Unlock() 248 | return ti, nil 249 | } 250 | 251 | // boolCaps returns all bool and extended capabilities using f to format the 252 | // index key. 253 | func (ti *Terminfo) boolCaps(f func(int) string, extended bool) map[string]bool { 254 | m := make(map[string]bool, len(ti.Bools)+len(ti.ExtBools)) 255 | if !extended { 256 | for k, v := range ti.Bools { 257 | m[f(k)] = v 258 | } 259 | } else { 260 | for k, v := range ti.ExtBools { 261 | m[string(ti.ExtBoolNames[k])] = v 262 | } 263 | } 264 | return m 265 | } 266 | 267 | // BoolCaps returns all bool capabilities. 268 | func (ti *Terminfo) BoolCaps() map[string]bool { 269 | return ti.boolCaps(BoolCapName, false) 270 | } 271 | 272 | // BoolCapsShort returns all bool capabilities, using the short name as the 273 | // index. 274 | func (ti *Terminfo) BoolCapsShort() map[string]bool { 275 | return ti.boolCaps(BoolCapNameShort, false) 276 | } 277 | 278 | // ExtBoolCaps returns all extended bool capabilities. 279 | func (ti *Terminfo) ExtBoolCaps() map[string]bool { 280 | return ti.boolCaps(BoolCapName, true) 281 | } 282 | 283 | // ExtBoolCapsShort returns all extended bool capabilities, using the short 284 | // name as the index. 285 | func (ti *Terminfo) ExtBoolCapsShort() map[string]bool { 286 | return ti.boolCaps(BoolCapNameShort, true) 287 | } 288 | 289 | // numCaps returns all num and extended capabilities using f to format the 290 | // index key. 291 | func (ti *Terminfo) numCaps(f func(int) string, extended bool) map[string]int { 292 | m := make(map[string]int, len(ti.Nums)+len(ti.ExtNums)) 293 | if !extended { 294 | for k, v := range ti.Nums { 295 | m[f(k)] = v 296 | } 297 | } else { 298 | for k, v := range ti.ExtNums { 299 | m[string(ti.ExtNumNames[k])] = v 300 | } 301 | } 302 | return m 303 | } 304 | 305 | // NumCaps returns all num capabilities. 306 | func (ti *Terminfo) NumCaps() map[string]int { 307 | return ti.numCaps(NumCapName, false) 308 | } 309 | 310 | // NumCapsShort returns all num capabilities, using the short name as the 311 | // index. 312 | func (ti *Terminfo) NumCapsShort() map[string]int { 313 | return ti.numCaps(NumCapNameShort, false) 314 | } 315 | 316 | // ExtNumCaps returns all extended num capabilities. 317 | func (ti *Terminfo) ExtNumCaps() map[string]int { 318 | return ti.numCaps(NumCapName, true) 319 | } 320 | 321 | // ExtNumCapsShort returns all extended num capabilities, using the short 322 | // name as the index. 323 | func (ti *Terminfo) ExtNumCapsShort() map[string]int { 324 | return ti.numCaps(NumCapNameShort, true) 325 | } 326 | 327 | // stringCaps returns all string and extended capabilities using f to format the 328 | // index key. 329 | func (ti *Terminfo) stringCaps(f func(int) string, extended bool) map[string][]byte { 330 | m := make(map[string][]byte, len(ti.Strings)+len(ti.ExtStrings)) 331 | if !extended { 332 | for k, v := range ti.Strings { 333 | m[f(k)] = v 334 | } 335 | } else { 336 | for k, v := range ti.ExtStrings { 337 | m[string(ti.ExtStringNames[k])] = v 338 | } 339 | } 340 | return m 341 | } 342 | 343 | // StringCaps returns all string capabilities. 344 | func (ti *Terminfo) StringCaps() map[string][]byte { 345 | return ti.stringCaps(StringCapName, false) 346 | } 347 | 348 | // StringCapsShort returns all string capabilities, using the short name as the 349 | // index. 350 | func (ti *Terminfo) StringCapsShort() map[string][]byte { 351 | return ti.stringCaps(StringCapNameShort, false) 352 | } 353 | 354 | // ExtStringCaps returns all extended string capabilities. 355 | func (ti *Terminfo) ExtStringCaps() map[string][]byte { 356 | return ti.stringCaps(StringCapName, true) 357 | } 358 | 359 | // ExtStringCapsShort returns all extended string capabilities, using the short 360 | // name as the index. 361 | func (ti *Terminfo) ExtStringCapsShort() map[string][]byte { 362 | return ti.stringCaps(StringCapNameShort, true) 363 | } 364 | 365 | // Has determines if the bool cap i is present. 366 | func (ti *Terminfo) Has(i int) bool { 367 | return ti.Bools[i] 368 | } 369 | 370 | // Num returns the num cap i, or -1 if not present. 371 | func (ti *Terminfo) Num(i int) int { 372 | n, ok := ti.Nums[i] 373 | if !ok { 374 | return -1 375 | } 376 | return n 377 | } 378 | 379 | // Printf formats the string cap i, interpolating parameters v. 380 | func (ti *Terminfo) Printf(i int, v ...interface{}) string { 381 | return Printf(ti.Strings[i], v...) 382 | } 383 | 384 | // Fprintf prints the string cap i to writer w, interpolating parameters v. 385 | func (ti *Terminfo) Fprintf(w io.Writer, i int, v ...interface{}) { 386 | Fprintf(w, ti.Strings[i], v...) 387 | } 388 | 389 | // Color takes a foreground and background color and returns string that sets 390 | // them for this terminal. 391 | func (ti *Terminfo) Colorf(fg, bg int, str string) string { 392 | maxColors := int(ti.Nums[MaxColors]) 393 | // map bright colors to lower versions if the color table only holds 8. 394 | if maxColors == 8 { 395 | if fg > 7 && fg < 16 { 396 | fg -= 8 397 | } 398 | if bg > 7 && bg < 16 { 399 | bg -= 8 400 | } 401 | } 402 | var s string 403 | if maxColors > fg && fg >= 0 { 404 | s += ti.Printf(SetAForeground, fg) 405 | } 406 | if maxColors > bg && bg >= 0 { 407 | s += ti.Printf(SetABackground, bg) 408 | } 409 | return s + str + ti.Printf(ExitAttributeMode) 410 | } 411 | 412 | // Goto returns a string suitable for addressing the cursor at the given 413 | // row and column. The origin 0, 0 is in the upper left corner of the screen. 414 | func (ti *Terminfo) Goto(row, col int) string { 415 | return Printf(ti.Strings[CursorAddress], row, col) 416 | } 417 | 418 | // Puts emits the string to the writer, but expands inline padding indications 419 | // (of the form $<[delay]> where [delay] is msec) to a suitable number of 420 | // padding characters (usually null bytes) based upon the supplied baud. At 421 | // high baud rates, more padding characters will be inserted. 422 | /*func (ti *Terminfo) Puts(w io.Writer, s string, lines, baud int) (int, error) { 423 | var err error 424 | for { 425 | start := strings.Index(s, "$<") 426 | if start == -1 { 427 | // most strings don't need padding, which is good news! 428 | return io.WriteString(w, s) 429 | } 430 | end := strings.Index(s, ">") 431 | if end == -1 { 432 | // unterminated... just emit bytes unadulterated. 433 | return io.WriteString(w, "$<"+s) 434 | } 435 | var c int 436 | c, err = io.WriteString(w, s[:start]) 437 | if err != nil { 438 | return n + c, err 439 | } 440 | n += c 441 | s = s[start+2:] 442 | val := s[:end] 443 | s = s[end+1:] 444 | var ms int 445 | var dot, mandatory, asterisk bool 446 | unit := 1000 447 | for _, ch := range val { 448 | switch { 449 | case ch >= '0' && ch <= '9': 450 | ms = (ms * 10) + int(ch-'0') 451 | if dot { 452 | unit *= 10 453 | } 454 | case ch == '.' && !dot: 455 | dot = true 456 | case ch == '*' && !asterisk: 457 | ms *= lines 458 | asterisk = true 459 | case ch == '/': 460 | mandatory = true 461 | default: 462 | break 463 | } 464 | } 465 | z, pad := ((baud/8)/unit)*ms, ti.Strings[PadChar] 466 | b := make([]byte, len(pad)*z) 467 | for bp := copy(b, pad); bp < len(b); bp *= 2 { 468 | copy(b[bp:], b[:bp]) 469 | } 470 | if (!ti.Bools[XonXoff] && baud > int(ti.Nums[PaddingBaudRate])) || mandatory { 471 | c, err = w.Write(b) 472 | if err != nil { 473 | return n + c, err 474 | } 475 | n += c 476 | } 477 | } 478 | return n, nil 479 | }*/ 480 | -------------------------------------------------------------------------------- /terminfo_test.go: -------------------------------------------------------------------------------- 1 | package terminfo 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "reflect" 10 | "regexp" 11 | "strconv" 12 | "strings" 13 | "sync" 14 | "testing" 15 | 16 | "golang.org/x/exp/maps" 17 | "golang.org/x/exp/slices" 18 | ) 19 | 20 | func TestLoad(t *testing.T) { 21 | keys := maps.Keys(terms(t)) 22 | slices.Sort(keys) 23 | for _, ts := range keys { 24 | term := ts 25 | t.Run(term, func(t *testing.T) { 26 | if err := os.Setenv("TERM", term); err != nil { 27 | t.Fatalf("could not set TERM environment variable, got: %v", err) 28 | } 29 | // open 30 | info, err := LoadFromEnv() 31 | if err != nil { 32 | t.Fatalf("term %s expected no error, got: %v", term, err) 33 | } 34 | // check we have at least one name 35 | if len(info.Names) < 1 { 36 | t.Errorf("term %s expected names to have at least one value", term) 37 | } 38 | }) 39 | } 40 | } 41 | 42 | func TestOpen(t *testing.T) { 43 | for ts, n := range terms(t) { 44 | term, filename := ts, n 45 | t.Run(strings.TrimPrefix(filename, "/"), func(t *testing.T) { 46 | // open 47 | ti, err := Open(filepath.Dir(filepath.Dir(filename)), term) 48 | if err != nil { 49 | t.Fatalf("term %s expected no error, got: %v", term, err) 50 | } 51 | if ti.File != filename { 52 | t.Errorf("term %s should have file %s, got: %s", term, filename, ti.File) 53 | } 54 | // check we have at least one name 55 | if len(ti.Names) < 1 { 56 | t.Errorf("term %s expected names to have at least one value", term) 57 | } 58 | }) 59 | } 60 | } 61 | 62 | func TestValues(t *testing.T) { 63 | for ts, n := range terms(t) { 64 | term, filename := ts, n 65 | t.Run(filepath.Base(filename), func(t *testing.T) { 66 | t.Parallel() 67 | ic, err := getInfocmpData(t, term) 68 | if err != nil { 69 | t.Fatalf("expected no error, got: %s", err) 70 | } 71 | // load 72 | ti, err := Load(term) 73 | if err != nil { 74 | t.Fatalf("expected no error, got: %v", err) 75 | } 76 | // check names 77 | if !reflect.DeepEqual(ic.names, ti.Names) { 78 | t.Errorf("names do not match") 79 | } 80 | // check bool caps 81 | for i, v := range ic.boolCaps { 82 | if v == nil { 83 | if _, ok := ti.BoolsM[i]; !ok { 84 | t.Errorf("expected bool cap %d (%s) to be missing", i, BoolCapName(i)) 85 | } 86 | } else if v.(bool) != ti.Bools[i] { 87 | t.Errorf("bool cap %d (%s) should be %t", i, BoolCapName(i), v) 88 | } 89 | } 90 | // check extended bool caps 91 | if len(ic.extBoolCaps) != len(ti.ExtBools) { 92 | t.Errorf("should have same number of extended bools (%d, %d)", len(ic.extBoolCaps), len(ti.ExtBools)) 93 | } 94 | for i, v := range ic.extBoolCaps { 95 | z, ok := ti.ExtBools[i] 96 | if !ok { 97 | t.Errorf("should have extended bool %d", i) 98 | } 99 | if v.(bool) != z { 100 | t.Errorf("extended bool cap %d (%s) should be %t", i, ic.extBoolNames[i], v) 101 | } 102 | n, ok := ti.ExtBoolNames[i] 103 | if !ok { 104 | t.Errorf("missing extended bool %d name", i) 105 | } 106 | if string(n) != ic.extBoolNames[i] { 107 | t.Errorf("extended bool %d name should be '%s', got: '%s'", i, ic.extBoolNames[i], string(n)) 108 | } 109 | } 110 | // check num caps 111 | for i, v := range ic.numCaps { 112 | if v == nil { 113 | if _, ok := ti.NumsM[i]; !ok { 114 | // t.Errorf("term %s expected num cap %d (%s) to be missing", i, NumCapName(i)) 115 | } 116 | } else if v.(int) != ti.Nums[i] { 117 | t.Errorf("num cap %d (%s) should be %d", i, NumCapName(i), v) 118 | } 119 | } 120 | // check extended num caps 121 | if len(ic.extNumCaps) != len(ti.ExtNums) { 122 | t.Errorf("should have same number of extended nums (%d, %d)", len(ic.extNumCaps), len(ti.ExtNums)) 123 | } 124 | for i, v := range ic.extNumCaps { 125 | z, ok := ti.ExtNums[i] 126 | if !ok { 127 | t.Errorf("should have extended num %d", i) 128 | } 129 | if v.(int) != z { 130 | t.Errorf("extended num cap %d (%s) should be %t", i, ic.extNumNames[i], v) 131 | } 132 | n, ok := ti.ExtNumNames[i] 133 | if !ok { 134 | t.Errorf("missing extended num %d name", i) 135 | } 136 | if string(n) != ic.extNumNames[i] { 137 | t.Errorf("extended num %d name should be '%s', got: '%s'", i, ic.extNumNames[i], string(n)) 138 | } 139 | } 140 | // check string caps 141 | for i, v := range ic.stringCaps { 142 | if v == nil { 143 | if _, ok := ti.StringsM[i]; !ok { 144 | // t.Errorf("term %s expected string cap %d (%s) to be missing", i, StringCapName(i)) 145 | } 146 | } else if v.(string) != string(ti.Strings[i]) { 147 | t.Errorf("string cap %d (%s) is invalid:", i, StringCapName(i)) 148 | t.Errorf("got: %#v", string(ti.Strings[i])) 149 | t.Errorf("want: %#v", v) 150 | } 151 | } 152 | // check extended string caps 153 | if len(ic.extStringCaps) != len(ti.ExtStrings) { 154 | t.Errorf("should have same number of extended strings (%d, %d)", len(ic.extStringCaps), len(ti.ExtStrings)) 155 | } 156 | for i, v := range ic.extStringCaps { 157 | z, ok := ti.ExtStrings[i] 158 | if !ok { 159 | t.Errorf("should have extended string %d", i) 160 | } 161 | if v.(string) != string(z) { 162 | t.Errorf("extended string cap %d (%s) should be %t", i, ic.extStringNames[i], v) 163 | } 164 | n, ok := ti.ExtStringNames[i] 165 | if !ok { 166 | t.Errorf("missing extended string %d name", i) 167 | } 168 | if string(n) != ic.extStringNames[i] { 169 | t.Errorf("extended string %d name should be '%s', got: '%s'", i, ic.extStringNames[i], string(n)) 170 | } 171 | } 172 | }) 173 | } 174 | } 175 | 176 | var shortCapNameMap map[string]int 177 | 178 | type infocmp struct { 179 | names []string 180 | boolCaps map[int]interface{} 181 | numCaps map[int]interface{} 182 | stringCaps map[int]interface{} 183 | extBoolCaps map[int]interface{} 184 | extNumCaps map[int]interface{} 185 | extStringCaps map[int]interface{} 186 | extBoolNames map[int]string 187 | extNumNames map[int]string 188 | extStringNames map[int]string 189 | } 190 | 191 | func init() { 192 | shortCapNameMap = make(map[string]int) 193 | for _, z := range [][]string{boolCapNames[:], numCapNames[:], stringCapNames[:]} { 194 | for i := 0; i < len(z); i += 2 { 195 | shortCapNameMap[z[i+1]] = i / 2 196 | } 197 | } 198 | } 199 | 200 | var staticCharRE = regexp.MustCompile(`(?m)^static\s+char\s+(.*)\s*\[\]\s*=\s*(".*");$`) 201 | 202 | func getInfocmpData(t *testing.T, term string) (*infocmp, error) { 203 | t.Logf("loading infocmp data for term %s", term) 204 | c := exec.Command("/usr/bin/infocmp", "-E", "-x") 205 | c.Env = []string{"TERM=" + term} 206 | buf, err := c.CombinedOutput() 207 | if err != nil { 208 | t.Logf("shell error (TERM=%s):\n%s\n", term, string(buf)) 209 | return nil, err 210 | } 211 | // read static strings 212 | m := staticCharRE.FindAllStringSubmatch(string(buf), -1) 213 | if !strings.HasSuffix(strings.TrimSpace(m[0][1]), "_alias_data") { 214 | return nil, errors.New("missing _alias_data") 215 | } 216 | // some names have " in them, and infocmp -E doesn't correctly escape them 217 | names, err := strconv.Unquote(`"` + strings.Replace(m[0][2][1:len(m[0][2])-1], `"`, `\"`, -1) + `"`) 218 | if err != nil { 219 | return nil, fmt.Errorf("could not unquote _alias_data: %v", err) 220 | } 221 | ic := &infocmp{ 222 | names: strings.Split(names, "|"), 223 | boolCaps: make(map[int]interface{}), 224 | numCaps: make(map[int]interface{}), 225 | stringCaps: make(map[int]interface{}), 226 | extBoolCaps: make(map[int]interface{}), 227 | extNumCaps: make(map[int]interface{}), 228 | extStringCaps: make(map[int]interface{}), 229 | extBoolNames: make(map[int]string), 230 | extNumNames: make(map[int]string), 231 | extStringNames: make(map[int]string), 232 | } 233 | // load string cap data 234 | caps := make(map[string]string, len(m)) 235 | for i, s := range m[1:] { 236 | k := strings.TrimSpace(s[1]) 237 | idx := strings.LastIndex(k, "_s_") 238 | if idx == -1 { 239 | return nil, fmt.Errorf("string cap %d (%s) does not contain _s_", i, k) 240 | } 241 | v, err := strconv.Unquote(s[2]) 242 | if err != nil { 243 | return nil, fmt.Errorf("could not unquote %d: %v", i, err) 244 | } 245 | caps[k] = v 246 | } 247 | // extract the values 248 | for _, err := range []error{ 249 | processSect(buf, caps, ic.boolCaps, ic.extBoolCaps, ic.extBoolNames, boolSectRE), 250 | processSect(buf, caps, ic.numCaps, ic.extNumCaps, ic.extNumNames, numSectRE), 251 | processSect(buf, caps, ic.stringCaps, ic.extStringCaps, ic.extStringNames, stringSectRE), 252 | } { 253 | if err != nil { 254 | return nil, err 255 | } 256 | } 257 | return ic, nil 258 | } 259 | 260 | // regexp's used by processSect. 261 | var ( 262 | boolSectRE = regexp.MustCompile(`_bool_data\[\]\s*=\s*{`) 263 | numSectRE = regexp.MustCompile(`_number_data\[\]\s*=\s*{`) 264 | stringSectRE = regexp.MustCompile(`_string_data\[\]\s*=\s*{`) 265 | endSectRE = regexp.MustCompile(`(?m)^};$`) 266 | capValuesRE = regexp.MustCompile(`(?m)^\s+/\*\s+[0-9]+:\s+([^\s]+)\s+\*/\s+(.*),$`) 267 | numRE = regexp.MustCompile(`^[0-9]+$`) 268 | absCanRE = regexp.MustCompile(`^(ABSENT|CANCELLED)_(BOOLEAN|NUMERIC|STRING)$`) 269 | ) 270 | 271 | // processSect processes a text section in the infocmp C export. 272 | func processSect(buf []byte, caps map[string]string, xx, yy map[int]interface{}, extn map[int]string, sectRE *regexp.Regexp) error { 273 | var err error 274 | // extract section 275 | start := sectRE.FindIndex(buf) 276 | if start == nil || len(start) != 2 { 277 | return fmt.Errorf("could not find sect (%s)", sectRE) 278 | } 279 | end := endSectRE.FindIndex(buf[start[1]:]) 280 | if end == nil || len(end) != 2 { 281 | return fmt.Errorf("could not find end of section (%s)", sectRE) 282 | } 283 | buf = buf[start[1] : start[1]+end[0]] 284 | // load caps 285 | m := capValuesRE.FindAllStringSubmatch(string(buf), -1) 286 | var extc int 287 | for i, s := range m { 288 | var skip bool 289 | // determine target 290 | target := xx 291 | k, ok := shortCapNameMap[s[1]] 292 | if !ok { 293 | target, k, extn[extc] = yy, extc, s[1] 294 | extc++ 295 | } 296 | // get cap value 297 | var v interface{} 298 | switch { 299 | case s[2] == "TRUE" || s[2] == "FALSE": 300 | v = s[2] == "TRUE" 301 | case numRE.MatchString(s[2]): 302 | var j int64 303 | j, err = strconv.ParseInt(s[2], 10, 32) 304 | if err != nil { 305 | return fmt.Errorf("line %d could not parse: %v", i, err) 306 | } 307 | v = int(j) 308 | case absCanRE.MatchString(s[2]): 309 | if !ok { // absent/canceled extended cap 310 | if strings.HasSuffix(s[2], "NUMERIC") { 311 | v = -1 312 | } else { 313 | skip = true 314 | } 315 | } 316 | default: 317 | v, ok = caps[s[2]] 318 | if !ok { 319 | return fmt.Errorf("cap '%s' not defined in cap table", s[2]) 320 | } 321 | } 322 | if !skip { 323 | target[k] = v 324 | } 325 | } 326 | return nil 327 | } 328 | 329 | var termNameCache = struct { 330 | names map[string]string 331 | sync.Mutex 332 | }{} 333 | 334 | var fileRE = regexp.MustCompile("^([0-9]+|[a-zA-Z])/") 335 | 336 | func terms(t *testing.T) map[string]string { 337 | termNameCache.Lock() 338 | defer termNameCache.Unlock() 339 | if termNameCache.names == nil { 340 | termNameCache.names = make(map[string]string) 341 | for _, dir := range termDirs() { 342 | err := filepath.Walk(dir, func(n string, fi os.FileInfo, err error) error { 343 | switch { 344 | case err != nil: 345 | return err 346 | case fi.IsDir(), dir == n, !fileRE.MatchString(n[len(dir)+1:]), fi.Mode()&os.ModeSymlink != 0: 347 | return nil 348 | } 349 | term := filepath.Base(n) 350 | if term == "xterm-old" { 351 | return nil 352 | } 353 | termNameCache.names[term] = n 354 | return nil 355 | }) 356 | if err != nil { 357 | t.Fatalf("could not walk directory, got: %v", err) 358 | } 359 | } 360 | } 361 | return termNameCache.names 362 | } 363 | 364 | func termDirs() []string { 365 | var dirs []string 366 | for _, dir := range []string{"/lib/terminfo", "/usr/share/terminfo"} { 367 | fi, err := os.Stat(dir) 368 | if err != nil && os.IsNotExist(err) { 369 | continue 370 | } else if err != nil { 371 | panic(err) 372 | } 373 | if fi.IsDir() { 374 | dirs = append(dirs, dir) 375 | } 376 | } 377 | return dirs 378 | } 379 | 380 | func TestCapSizes(t *testing.T) { 381 | if CapCountBool*2 != len(boolCapNames) { 382 | t.Fatalf("boolCapNames should have same length as twice CapCountBool") 383 | } 384 | if CapCountNum*2 != len(numCapNames) { 385 | t.Fatalf("numCapNames should have same length as twice CapCountNum") 386 | } 387 | if CapCountString*2 != len(stringCapNames) { 388 | t.Fatalf("stringCapNames should have same length as twice CapCountString") 389 | } 390 | } 391 | 392 | func TestCapNames(t *testing.T) { 393 | for i := 0; i < CapCountBool; i++ { 394 | n, s := BoolCapName(i), BoolCapNameShort(i) 395 | if n == "" { 396 | t.Errorf("Bool cap %d should have name", i) 397 | } 398 | if s == "" { 399 | t.Errorf("Bool cap %d should have short name", i) 400 | } 401 | if n == s { 402 | t.Errorf("Bool cap %d name and short name should not equal (%s==%s)", i, n, s) 403 | } 404 | } 405 | for i := 0; i < CapCountNum; i++ { 406 | n, s := NumCapName(i), NumCapNameShort(i) 407 | if n == "" { 408 | t.Errorf("Num cap %d should have name", i) 409 | } 410 | if s == "" { 411 | t.Errorf("Num cap %d should have short name", i) 412 | } 413 | if n == s && n != "lines" { 414 | t.Errorf("Num cap %d name and short name should not equal (%s==%s)", i, n, s) 415 | } 416 | } 417 | for i := 0; i < CapCountString; i++ { 418 | n, s := StringCapName(i), StringCapNameShort(i) 419 | if n == "" { 420 | t.Errorf("String cap %d should have name", i) 421 | } 422 | if s == "" { 423 | t.Errorf("String cap %d should have short name", i) 424 | } 425 | if n == s && n != "tone" && n != "pulse" { 426 | t.Errorf("String cap %d name and short name should not equal (%s==%s)", i, n, s) 427 | } 428 | } 429 | } 430 | --------------------------------------------------------------------------------