1// +build ignore 2 3// Copyright 2018 The TCell Authors 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use file except in compliance with the License. 7// You may obtain a copy of the license at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17// This command is used to generate suitable configuration files in either 18// go syntax or in JSON. It defaults to JSON output on stdout. If no 19// term values are specified on the command line, then $TERM is used. 20// 21// Usage is like this: 22// 23// mkinfo [-init] [-go file.go] [-json file.json] [-quiet] [-nofatal] [<term>...] 24// 25// -all scan terminfo to determine database entries to use 26// -db generate database entries (database/*), implied for -all 27// -gzip specifies output should be compressed (json only) 28// -go specifies Go output into the named file. Use - for stdout. 29// -json specifies JSON output in the named file. Use - for stdout 30// -nofatal indicates that errors loading definitions should not be fatal 31// 32 33package main 34 35import ( 36 "bufio" 37 "bytes" 38 "compress/gzip" 39 "crypto/sha1" 40 "encoding/json" 41 "errors" 42 "flag" 43 "fmt" 44 "io" 45 "os" 46 "os/exec" 47 "path" 48 "regexp" 49 "strconv" 50 "strings" 51 52 "github.com/gdamore/tcell/terminfo" 53) 54 55type termcap struct { 56 name string 57 desc string 58 aliases []string 59 bools map[string]bool 60 nums map[string]int 61 strs map[string]string 62} 63 64func (tc *termcap) getnum(s string) int { 65 return (tc.nums[s]) 66} 67 68func (tc *termcap) getflag(s string) bool { 69 return (tc.bools[s]) 70} 71 72func (tc *termcap) getstr(s string) string { 73 return (tc.strs[s]) 74} 75 76const ( 77 NONE = iota 78 CTRL 79 ESC 80) 81 82var notaddressable = errors.New("terminal not cursor addressable") 83 84func unescape(s string) string { 85 // Various escapes are in \x format. Control codes are 86 // encoded as ^M (carat followed by ASCII equivalent). 87 // Escapes are: \e, \E - escape 88 // \0 NULL, \n \l \r \t \b \f \s for equivalent C escape. 89 buf := &bytes.Buffer{} 90 esc := NONE 91 92 for i := 0; i < len(s); i++ { 93 c := s[i] 94 switch esc { 95 case NONE: 96 switch c { 97 case '\\': 98 esc = ESC 99 case '^': 100 esc = CTRL 101 default: 102 buf.WriteByte(c) 103 } 104 case CTRL: 105 buf.WriteByte(c - 0x40) 106 esc = NONE 107 case ESC: 108 switch c { 109 case 'E', 'e': 110 buf.WriteByte(0x1b) 111 case '0', '1', '2', '3', '4', '5', '6', '7': 112 if i+2 < len(s) && s[i+1] >= '0' && s[i+1] <= '7' && s[i+2] >= '0' && s[i+2] <= '7' { 113 buf.WriteByte(((c - '0') * 64) + ((s[i+1] - '0') * 8) + (s[i+2] - '0')) 114 i = i + 2 115 } else if c == '0' { 116 buf.WriteByte(0) 117 } 118 case 'n': 119 buf.WriteByte('\n') 120 case 'r': 121 buf.WriteByte('\r') 122 case 't': 123 buf.WriteByte('\t') 124 case 'b': 125 buf.WriteByte('\b') 126 case 'f': 127 buf.WriteByte('\f') 128 case 's': 129 buf.WriteByte(' ') 130 case 'l': 131 panic("WTF: weird format: " + s) 132 default: 133 buf.WriteByte(c) 134 } 135 esc = NONE 136 } 137 } 138 return (buf.String()) 139} 140 141func getallterms() ([]string, error) { 142 out := []string{} 143 cmd := exec.Command("toe", "-a") 144 output := &bytes.Buffer{} 145 cmd.Stdout = output 146 err := cmd.Run() 147 if err != nil { 148 return nil, err 149 } 150 lines := strings.Split(output.String(), "\n") 151 for _, l := range lines { 152 fields := strings.Fields(l) 153 if len(fields) > 0 { 154 out = append(out, fields[0]) 155 } 156 } 157 return out, nil 158} 159 160func (tc *termcap) setupterm(name string) error { 161 cmd := exec.Command("infocmp", "-1", name) 162 output := &bytes.Buffer{} 163 cmd.Stdout = output 164 165 tc.strs = make(map[string]string) 166 tc.bools = make(map[string]bool) 167 tc.nums = make(map[string]int) 168 169 err := cmd.Run() 170 if err != nil { 171 return err 172 } 173 174 // Now parse the output. 175 // We get comment lines (starting with "#"), followed by 176 // a header line that looks like "<name>|<alias>|...|<desc>" 177 // then capabilities, one per line, starting with a tab and ending 178 // with a comma and newline. 179 lines := strings.Split(output.String(), "\n") 180 for len(lines) > 0 && strings.HasPrefix(lines[0], "#") { 181 lines = lines[1:] 182 } 183 184 // Ditch trailing empty last line 185 if lines[len(lines)-1] == "" { 186 lines = lines[:len(lines)-1] 187 } 188 header := lines[0] 189 if strings.HasSuffix(header, ",") { 190 header = header[:len(header)-1] 191 } 192 names := strings.Split(header, "|") 193 tc.name = names[0] 194 names = names[1:] 195 if len(names) > 0 { 196 tc.desc = names[len(names)-1] 197 names = names[:len(names)-1] 198 } 199 tc.aliases = names 200 for _, val := range lines[1:] { 201 if (!strings.HasPrefix(val, "\t")) || 202 (!strings.HasSuffix(val, ",")) { 203 return (errors.New("malformed infocmp: " + val)) 204 } 205 206 val = val[1:] 207 val = val[:len(val)-1] 208 209 if k := strings.SplitN(val, "=", 2); len(k) == 2 { 210 tc.strs[k[0]] = unescape(k[1]) 211 } else if k := strings.SplitN(val, "#", 2); len(k) == 2 { 212 if u, err := strconv.ParseUint(k[1], 0, 0); err != nil { 213 return (err) 214 } else { 215 tc.nums[k[0]] = int(u) 216 } 217 } else { 218 tc.bools[val] = true 219 } 220 } 221 return nil 222} 223 224// This program is used to collect data from the system's terminfo library, 225// and write it into Go source code. That is, we maintain our terminfo 226// capabilities encoded in the program. It should never need to be run by 227// an end user, but developers can use this to add codes for additional 228// terminal types. 229// 230// If a terminal name ending with -truecolor is given, and we cannot find 231// one, we will try to fabricate one from either the -256color (if present) 232// or the unadorned base name, adding the XTerm specific 24-bit color 233// escapes. We believe that all 24-bit capable terminals use the same 234// escape sequences, and terminfo has yet to evolve to support this. 235func getinfo(name string) (*terminfo.Terminfo, string, error) { 236 var tc termcap 237 addTrueColor := false 238 if err := tc.setupterm(name); err != nil { 239 if strings.HasSuffix(name, "-truecolor") { 240 base := name[:len(name)-len("-truecolor")] 241 // Probably -256color is closest to what we want 242 if err = tc.setupterm(base + "-256color"); err != nil { 243 err = tc.setupterm(base) 244 } 245 if err == nil { 246 addTrueColor = true 247 } 248 tc.name = name 249 } 250 if err != nil { 251 return nil, "", err 252 } 253 } 254 t := &terminfo.Terminfo{} 255 // If this is an alias record, then just emit the alias 256 t.Name = tc.name 257 if t.Name != name { 258 return t, "", nil 259 } 260 t.Aliases = tc.aliases 261 t.Colors = tc.getnum("colors") 262 t.Columns = tc.getnum("cols") 263 t.Lines = tc.getnum("lines") 264 t.Bell = tc.getstr("bel") 265 t.Clear = tc.getstr("clear") 266 t.EnterCA = tc.getstr("smcup") 267 t.ExitCA = tc.getstr("rmcup") 268 t.ShowCursor = tc.getstr("cnorm") 269 t.HideCursor = tc.getstr("civis") 270 t.AttrOff = tc.getstr("sgr0") 271 t.Underline = tc.getstr("smul") 272 t.Bold = tc.getstr("bold") 273 t.Blink = tc.getstr("blink") 274 t.Dim = tc.getstr("dim") 275 t.Reverse = tc.getstr("rev") 276 t.EnterKeypad = tc.getstr("smkx") 277 t.ExitKeypad = tc.getstr("rmkx") 278 t.SetFg = tc.getstr("setaf") 279 t.SetBg = tc.getstr("setab") 280 t.SetCursor = tc.getstr("cup") 281 t.CursorBack1 = tc.getstr("cub1") 282 t.CursorUp1 = tc.getstr("cuu1") 283 t.KeyF1 = tc.getstr("kf1") 284 t.KeyF2 = tc.getstr("kf2") 285 t.KeyF3 = tc.getstr("kf3") 286 t.KeyF4 = tc.getstr("kf4") 287 t.KeyF5 = tc.getstr("kf5") 288 t.KeyF6 = tc.getstr("kf6") 289 t.KeyF7 = tc.getstr("kf7") 290 t.KeyF8 = tc.getstr("kf8") 291 t.KeyF9 = tc.getstr("kf9") 292 t.KeyF10 = tc.getstr("kf10") 293 t.KeyF11 = tc.getstr("kf11") 294 t.KeyF12 = tc.getstr("kf12") 295 t.KeyF13 = tc.getstr("kf13") 296 t.KeyF14 = tc.getstr("kf14") 297 t.KeyF15 = tc.getstr("kf15") 298 t.KeyF16 = tc.getstr("kf16") 299 t.KeyF17 = tc.getstr("kf17") 300 t.KeyF18 = tc.getstr("kf18") 301 t.KeyF19 = tc.getstr("kf19") 302 t.KeyF20 = tc.getstr("kf20") 303 t.KeyF21 = tc.getstr("kf21") 304 t.KeyF22 = tc.getstr("kf22") 305 t.KeyF23 = tc.getstr("kf23") 306 t.KeyF24 = tc.getstr("kf24") 307 t.KeyF25 = tc.getstr("kf25") 308 t.KeyF26 = tc.getstr("kf26") 309 t.KeyF27 = tc.getstr("kf27") 310 t.KeyF28 = tc.getstr("kf28") 311 t.KeyF29 = tc.getstr("kf29") 312 t.KeyF30 = tc.getstr("kf30") 313 t.KeyF31 = tc.getstr("kf31") 314 t.KeyF32 = tc.getstr("kf32") 315 t.KeyF33 = tc.getstr("kf33") 316 t.KeyF34 = tc.getstr("kf34") 317 t.KeyF35 = tc.getstr("kf35") 318 t.KeyF36 = tc.getstr("kf36") 319 t.KeyF37 = tc.getstr("kf37") 320 t.KeyF38 = tc.getstr("kf38") 321 t.KeyF39 = tc.getstr("kf39") 322 t.KeyF40 = tc.getstr("kf40") 323 t.KeyF41 = tc.getstr("kf41") 324 t.KeyF42 = tc.getstr("kf42") 325 t.KeyF43 = tc.getstr("kf43") 326 t.KeyF44 = tc.getstr("kf44") 327 t.KeyF45 = tc.getstr("kf45") 328 t.KeyF46 = tc.getstr("kf46") 329 t.KeyF47 = tc.getstr("kf47") 330 t.KeyF48 = tc.getstr("kf48") 331 t.KeyF49 = tc.getstr("kf49") 332 t.KeyF50 = tc.getstr("kf50") 333 t.KeyF51 = tc.getstr("kf51") 334 t.KeyF52 = tc.getstr("kf52") 335 t.KeyF53 = tc.getstr("kf53") 336 t.KeyF54 = tc.getstr("kf54") 337 t.KeyF55 = tc.getstr("kf55") 338 t.KeyF56 = tc.getstr("kf56") 339 t.KeyF57 = tc.getstr("kf57") 340 t.KeyF58 = tc.getstr("kf58") 341 t.KeyF59 = tc.getstr("kf59") 342 t.KeyF60 = tc.getstr("kf60") 343 t.KeyF61 = tc.getstr("kf61") 344 t.KeyF62 = tc.getstr("kf62") 345 t.KeyF63 = tc.getstr("kf63") 346 t.KeyF64 = tc.getstr("kf64") 347 t.KeyInsert = tc.getstr("kich1") 348 t.KeyDelete = tc.getstr("kdch1") 349 t.KeyBackspace = tc.getstr("kbs") 350 t.KeyHome = tc.getstr("khome") 351 t.KeyEnd = tc.getstr("kend") 352 t.KeyUp = tc.getstr("kcuu1") 353 t.KeyDown = tc.getstr("kcud1") 354 t.KeyRight = tc.getstr("kcuf1") 355 t.KeyLeft = tc.getstr("kcub1") 356 t.KeyPgDn = tc.getstr("knp") 357 t.KeyPgUp = tc.getstr("kpp") 358 t.KeyBacktab = tc.getstr("kcbt") 359 t.KeyExit = tc.getstr("kext") 360 t.KeyCancel = tc.getstr("kcan") 361 t.KeyPrint = tc.getstr("kprt") 362 t.KeyHelp = tc.getstr("khlp") 363 t.KeyClear = tc.getstr("kclr") 364 t.AltChars = tc.getstr("acsc") 365 t.EnterAcs = tc.getstr("smacs") 366 t.ExitAcs = tc.getstr("rmacs") 367 t.EnableAcs = tc.getstr("enacs") 368 t.Mouse = tc.getstr("kmous") 369 t.KeyShfRight = tc.getstr("kRIT") 370 t.KeyShfLeft = tc.getstr("kLFT") 371 t.KeyShfHome = tc.getstr("kHOM") 372 t.KeyShfEnd = tc.getstr("kEND") 373 374 // Terminfo lacks descriptions for a bunch of modified keys, 375 // but modern XTerm and emulators often have them. Let's add them, 376 // if the shifted right and left arrows are defined. 377 if t.KeyShfRight == "\x1b[1;2C" && t.KeyShfLeft == "\x1b[1;2D" { 378 t.KeyShfUp = "\x1b[1;2A" 379 t.KeyShfDown = "\x1b[1;2B" 380 t.KeyMetaUp = "\x1b[1;9A" 381 t.KeyMetaDown = "\x1b[1;9B" 382 t.KeyMetaRight = "\x1b[1;9C" 383 t.KeyMetaLeft = "\x1b[1;9D" 384 t.KeyAltUp = "\x1b[1;3A" 385 t.KeyAltDown = "\x1b[1;3B" 386 t.KeyAltRight = "\x1b[1;3C" 387 t.KeyAltLeft = "\x1b[1;3D" 388 t.KeyCtrlUp = "\x1b[1;5A" 389 t.KeyCtrlDown = "\x1b[1;5B" 390 t.KeyCtrlRight = "\x1b[1;5C" 391 t.KeyCtrlLeft = "\x1b[1;5D" 392 t.KeyAltShfUp = "\x1b[1;4A" 393 t.KeyAltShfDown = "\x1b[1;4B" 394 t.KeyAltShfRight = "\x1b[1;4C" 395 t.KeyAltShfLeft = "\x1b[1;4D" 396 397 t.KeyMetaShfUp = "\x1b[1;10A" 398 t.KeyMetaShfDown = "\x1b[1;10B" 399 t.KeyMetaShfRight = "\x1b[1;10C" 400 t.KeyMetaShfLeft = "\x1b[1;10D" 401 402 t.KeyCtrlShfUp = "\x1b[1;6A" 403 t.KeyCtrlShfDown = "\x1b[1;6B" 404 t.KeyCtrlShfRight = "\x1b[1;6C" 405 t.KeyCtrlShfLeft = "\x1b[1;6D" 406 } 407 // And also for Home and End 408 if t.KeyShfHome == "\x1b[1;2H" && t.KeyShfEnd == "\x1b[1;2F" { 409 t.KeyCtrlHome = "\x1b[1;5H" 410 t.KeyCtrlEnd = "\x1b[1;5F" 411 t.KeyAltHome = "\x1b[1;9H" 412 t.KeyAltEnd = "\x1b[1;9F" 413 t.KeyCtrlShfHome = "\x1b[1;6H" 414 t.KeyCtrlShfEnd = "\x1b[1;6F" 415 t.KeyAltShfHome = "\x1b[1;4H" 416 t.KeyAltShfEnd = "\x1b[1;4F" 417 t.KeyMetaShfHome = "\x1b[1;10H" 418 t.KeyMetaShfEnd = "\x1b[1;10F" 419 } 420 421 // And the same thing for rxvt and workalikes (Eterm, aterm, etc.) 422 // It seems that urxvt at least send ESC as ALT prefix for these, 423 // although some places seem to indicate a separate ALT key sesquence. 424 if t.KeyShfRight == "\x1b[c" && t.KeyShfLeft == "\x1b[d" { 425 t.KeyShfUp = "\x1b[a" 426 t.KeyShfDown = "\x1b[b" 427 t.KeyCtrlUp = "\x1b[Oa" 428 t.KeyCtrlDown = "\x1b[Ob" 429 t.KeyCtrlRight = "\x1b[Oc" 430 t.KeyCtrlLeft = "\x1b[Od" 431 } 432 if t.KeyShfHome == "\x1b[7$" && t.KeyShfEnd == "\x1b[8$" { 433 t.KeyCtrlHome = "\x1b[7^" 434 t.KeyCtrlEnd = "\x1b[8^" 435 } 436 437 // If the kmous entry is present, then we need to record the 438 // the codes to enter and exit mouse mode. Sadly, this is not 439 // part of the terminfo databases anywhere that I've found, but 440 // is an extension. The escape codes are documented in the XTerm 441 // manual, and all terminals that have kmous are expected to 442 // use these same codes, unless explicitly configured otherwise 443 // vi XM. Note that in any event, we only known how to parse either 444 // x11 or SGR mouse events -- if your terminal doesn't support one 445 // of these two forms, you maybe out of luck. 446 t.MouseMode = tc.getstr("XM") 447 if t.Mouse != "" && t.MouseMode == "" { 448 // we anticipate that all xterm mouse tracking compatible 449 // terminals understand mouse tracking (1000), but we hope 450 // that those that don't understand any-event tracking (1003) 451 // will at least ignore it. Likewise we hope that terminals 452 // that don't understand SGR reporting (1006) just ignore it. 453 t.MouseMode = "%?%p1%{1}%=%t%'h'%Pa%e%'l'%Pa%;" + 454 "\x1b[?1000%ga%c\x1b[?1002%ga%c\x1b[?1003%ga%c\x1b[?1006%ga%c" 455 } 456 457 // We only support colors in ANSI 8 or 256 color mode. 458 if t.Colors < 8 || t.SetFg == "" { 459 t.Colors = 0 460 } 461 if t.SetCursor == "" { 462 return nil, "", notaddressable 463 } 464 465 // For padding, we lookup the pad char. If that isn't present, 466 // and npc is *not* set, then we assume a null byte. 467 t.PadChar = tc.getstr("pad") 468 if t.PadChar == "" { 469 if !tc.getflag("npc") { 470 t.PadChar = "\u0000" 471 } 472 } 473 474 // For some terminals we fabricate a -truecolor entry, that may 475 // not exist in terminfo. 476 if addTrueColor { 477 t.SetFgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%dm" 478 t.SetBgRGB = "\x1b[48;2;%p1%d;%p2%d;%p3%dm" 479 t.SetFgBgRGB = "\x1b[38;2;%p1%d;%p2%d;%p3%d;" + 480 "48;2;%p4%d;%p5%d;%p6%dm" 481 } 482 483 // For terminals that use "standard" SGR sequences, lets combine the 484 // foreground and background together. 485 if strings.HasPrefix(t.SetFg, "\x1b[") && 486 strings.HasPrefix(t.SetBg, "\x1b[") && 487 strings.HasSuffix(t.SetFg, "m") && 488 strings.HasSuffix(t.SetBg, "m") { 489 fg := t.SetFg[:len(t.SetFg)-1] 490 r := regexp.MustCompile("%p1") 491 bg := r.ReplaceAllString(t.SetBg[2:], "%p2") 492 t.SetFgBg = fg + ";" + bg 493 } 494 495 return t, tc.desc, nil 496} 497 498func dotGoAddInt(w io.Writer, n string, i int) { 499 if i == 0 { 500 // initialized to 0, ignore 501 return 502 } 503 fmt.Fprintf(w, "\t\t%-13s %d,\n", n+":", i) 504} 505func dotGoAddStr(w io.Writer, n string, s string) { 506 if s == "" { 507 return 508 } 509 fmt.Fprintf(w, "\t\t%-13s %q,\n", n+":", s) 510} 511 512func dotGoAddArr(w io.Writer, n string, a []string) { 513 if len(a) == 0 { 514 return 515 } 516 fmt.Fprintf(w, "\t\t%-13s []string{", n+":") 517 did := false 518 for _, b := range a { 519 if did { 520 fmt.Fprint(w, ", ") 521 } 522 did = true 523 fmt.Fprintf(w, "%q", b) 524 } 525 fmt.Fprintln(w, "},") 526} 527 528func dotGoHeader(w io.Writer, packname string) { 529 fmt.Fprintln(w, "// Generated automatically. DO NOT HAND-EDIT.") 530 fmt.Fprintln(w, "") 531 fmt.Fprintf(w, "package %s\n", packname) 532 fmt.Fprintln(w, "") 533} 534 535func dotGoTrailer(w io.Writer) { 536} 537 538func dotGoInfo(w io.Writer, t *terminfo.Terminfo, desc string) { 539 540 fmt.Fprintln(w, "") 541 fmt.Fprintln(w, "func init() {") 542 fmt.Fprintf(w, "\t// %s\n", desc) 543 fmt.Fprintln(w, "\tAddTerminfo(&Terminfo{") 544 dotGoAddStr(w, "Name", t.Name) 545 dotGoAddArr(w, "Aliases", t.Aliases) 546 dotGoAddInt(w, "Columns", t.Columns) 547 dotGoAddInt(w, "Lines", t.Lines) 548 dotGoAddInt(w, "Colors", t.Colors) 549 dotGoAddStr(w, "Bell", t.Bell) 550 dotGoAddStr(w, "Clear", t.Clear) 551 dotGoAddStr(w, "EnterCA", t.EnterCA) 552 dotGoAddStr(w, "ExitCA", t.ExitCA) 553 dotGoAddStr(w, "ShowCursor", t.ShowCursor) 554 dotGoAddStr(w, "HideCursor", t.HideCursor) 555 dotGoAddStr(w, "AttrOff", t.AttrOff) 556 dotGoAddStr(w, "Underline", t.Underline) 557 dotGoAddStr(w, "Bold", t.Bold) 558 dotGoAddStr(w, "Dim", t.Dim) 559 dotGoAddStr(w, "Blink", t.Blink) 560 dotGoAddStr(w, "Reverse", t.Reverse) 561 dotGoAddStr(w, "EnterKeypad", t.EnterKeypad) 562 dotGoAddStr(w, "ExitKeypad", t.ExitKeypad) 563 dotGoAddStr(w, "SetFg", t.SetFg) 564 dotGoAddStr(w, "SetBg", t.SetBg) 565 dotGoAddStr(w, "SetFgBg", t.SetFgBg) 566 dotGoAddStr(w, "PadChar", t.PadChar) 567 dotGoAddStr(w, "AltChars", t.AltChars) 568 dotGoAddStr(w, "EnterAcs", t.EnterAcs) 569 dotGoAddStr(w, "ExitAcs", t.ExitAcs) 570 dotGoAddStr(w, "EnableAcs", t.EnableAcs) 571 dotGoAddStr(w, "SetFgRGB", t.SetFgRGB) 572 dotGoAddStr(w, "SetBgRGB", t.SetBgRGB) 573 dotGoAddStr(w, "SetFgBgRGB", t.SetFgBgRGB) 574 dotGoAddStr(w, "Mouse", t.Mouse) 575 dotGoAddStr(w, "MouseMode", t.MouseMode) 576 dotGoAddStr(w, "SetCursor", t.SetCursor) 577 dotGoAddStr(w, "CursorBack1", t.CursorBack1) 578 dotGoAddStr(w, "CursorUp1", t.CursorUp1) 579 dotGoAddStr(w, "KeyUp", t.KeyUp) 580 dotGoAddStr(w, "KeyDown", t.KeyDown) 581 dotGoAddStr(w, "KeyRight", t.KeyRight) 582 dotGoAddStr(w, "KeyLeft", t.KeyLeft) 583 dotGoAddStr(w, "KeyInsert", t.KeyInsert) 584 dotGoAddStr(w, "KeyDelete", t.KeyDelete) 585 dotGoAddStr(w, "KeyBackspace", t.KeyBackspace) 586 dotGoAddStr(w, "KeyHome", t.KeyHome) 587 dotGoAddStr(w, "KeyEnd", t.KeyEnd) 588 dotGoAddStr(w, "KeyPgUp", t.KeyPgUp) 589 dotGoAddStr(w, "KeyPgDn", t.KeyPgDn) 590 dotGoAddStr(w, "KeyF1", t.KeyF1) 591 dotGoAddStr(w, "KeyF2", t.KeyF2) 592 dotGoAddStr(w, "KeyF3", t.KeyF3) 593 dotGoAddStr(w, "KeyF4", t.KeyF4) 594 dotGoAddStr(w, "KeyF5", t.KeyF5) 595 dotGoAddStr(w, "KeyF6", t.KeyF6) 596 dotGoAddStr(w, "KeyF7", t.KeyF7) 597 dotGoAddStr(w, "KeyF8", t.KeyF8) 598 dotGoAddStr(w, "KeyF9", t.KeyF9) 599 dotGoAddStr(w, "KeyF10", t.KeyF10) 600 dotGoAddStr(w, "KeyF11", t.KeyF11) 601 dotGoAddStr(w, "KeyF12", t.KeyF12) 602 dotGoAddStr(w, "KeyF13", t.KeyF13) 603 dotGoAddStr(w, "KeyF14", t.KeyF14) 604 dotGoAddStr(w, "KeyF15", t.KeyF15) 605 dotGoAddStr(w, "KeyF16", t.KeyF16) 606 dotGoAddStr(w, "KeyF17", t.KeyF17) 607 dotGoAddStr(w, "KeyF18", t.KeyF18) 608 dotGoAddStr(w, "KeyF19", t.KeyF19) 609 dotGoAddStr(w, "KeyF20", t.KeyF20) 610 dotGoAddStr(w, "KeyF21", t.KeyF21) 611 dotGoAddStr(w, "KeyF22", t.KeyF22) 612 dotGoAddStr(w, "KeyF23", t.KeyF23) 613 dotGoAddStr(w, "KeyF24", t.KeyF24) 614 dotGoAddStr(w, "KeyF25", t.KeyF25) 615 dotGoAddStr(w, "KeyF26", t.KeyF26) 616 dotGoAddStr(w, "KeyF27", t.KeyF27) 617 dotGoAddStr(w, "KeyF28", t.KeyF28) 618 dotGoAddStr(w, "KeyF29", t.KeyF29) 619 dotGoAddStr(w, "KeyF30", t.KeyF30) 620 dotGoAddStr(w, "KeyF31", t.KeyF31) 621 dotGoAddStr(w, "KeyF32", t.KeyF32) 622 dotGoAddStr(w, "KeyF33", t.KeyF33) 623 dotGoAddStr(w, "KeyF34", t.KeyF34) 624 dotGoAddStr(w, "KeyF35", t.KeyF35) 625 dotGoAddStr(w, "KeyF36", t.KeyF36) 626 dotGoAddStr(w, "KeyF37", t.KeyF37) 627 dotGoAddStr(w, "KeyF38", t.KeyF38) 628 dotGoAddStr(w, "KeyF39", t.KeyF39) 629 dotGoAddStr(w, "KeyF40", t.KeyF40) 630 dotGoAddStr(w, "KeyF41", t.KeyF41) 631 dotGoAddStr(w, "KeyF42", t.KeyF42) 632 dotGoAddStr(w, "KeyF43", t.KeyF43) 633 dotGoAddStr(w, "KeyF44", t.KeyF44) 634 dotGoAddStr(w, "KeyF45", t.KeyF45) 635 dotGoAddStr(w, "KeyF46", t.KeyF46) 636 dotGoAddStr(w, "KeyF47", t.KeyF47) 637 dotGoAddStr(w, "KeyF48", t.KeyF48) 638 dotGoAddStr(w, "KeyF49", t.KeyF49) 639 dotGoAddStr(w, "KeyF50", t.KeyF50) 640 dotGoAddStr(w, "KeyF51", t.KeyF51) 641 dotGoAddStr(w, "KeyF52", t.KeyF52) 642 dotGoAddStr(w, "KeyF53", t.KeyF53) 643 dotGoAddStr(w, "KeyF54", t.KeyF54) 644 dotGoAddStr(w, "KeyF55", t.KeyF55) 645 dotGoAddStr(w, "KeyF56", t.KeyF56) 646 dotGoAddStr(w, "KeyF57", t.KeyF57) 647 dotGoAddStr(w, "KeyF58", t.KeyF58) 648 dotGoAddStr(w, "KeyF59", t.KeyF59) 649 dotGoAddStr(w, "KeyF60", t.KeyF60) 650 dotGoAddStr(w, "KeyF61", t.KeyF61) 651 dotGoAddStr(w, "KeyF62", t.KeyF62) 652 dotGoAddStr(w, "KeyF63", t.KeyF63) 653 dotGoAddStr(w, "KeyF64", t.KeyF64) 654 dotGoAddStr(w, "KeyCancel", t.KeyCancel) 655 dotGoAddStr(w, "KeyPrint", t.KeyPrint) 656 dotGoAddStr(w, "KeyExit", t.KeyExit) 657 dotGoAddStr(w, "KeyHelp", t.KeyHelp) 658 dotGoAddStr(w, "KeyClear", t.KeyClear) 659 dotGoAddStr(w, "KeyBacktab", t.KeyBacktab) 660 dotGoAddStr(w, "KeyShfLeft", t.KeyShfLeft) 661 dotGoAddStr(w, "KeyShfRight", t.KeyShfRight) 662 dotGoAddStr(w, "KeyShfUp", t.KeyShfUp) 663 dotGoAddStr(w, "KeyShfDown", t.KeyShfDown) 664 dotGoAddStr(w, "KeyCtrlLeft", t.KeyCtrlLeft) 665 dotGoAddStr(w, "KeyCtrlRight", t.KeyCtrlRight) 666 dotGoAddStr(w, "KeyCtrlUp", t.KeyCtrlUp) 667 dotGoAddStr(w, "KeyCtrlDown", t.KeyCtrlDown) 668 dotGoAddStr(w, "KeyMetaLeft", t.KeyMetaLeft) 669 dotGoAddStr(w, "KeyMetaRight", t.KeyMetaRight) 670 dotGoAddStr(w, "KeyMetaUp", t.KeyMetaUp) 671 dotGoAddStr(w, "KeyMetaDown", t.KeyMetaDown) 672 dotGoAddStr(w, "KeyAltLeft", t.KeyAltLeft) 673 dotGoAddStr(w, "KeyAltRight", t.KeyAltRight) 674 dotGoAddStr(w, "KeyAltUp", t.KeyAltUp) 675 dotGoAddStr(w, "KeyAltDown", t.KeyAltDown) 676 dotGoAddStr(w, "KeyAltShfLeft", t.KeyAltShfLeft) 677 dotGoAddStr(w, "KeyAltShfRight", t.KeyAltShfRight) 678 dotGoAddStr(w, "KeyAltShfUp", t.KeyAltShfUp) 679 dotGoAddStr(w, "KeyAltShfDown", t.KeyAltShfDown) 680 dotGoAddStr(w, "KeyMetaShfLeft", t.KeyMetaShfLeft) 681 dotGoAddStr(w, "KeyMetaShfRight", t.KeyMetaShfRight) 682 dotGoAddStr(w, "KeyMetaShfUp", t.KeyMetaShfUp) 683 dotGoAddStr(w, "KeyMetaShfDown", t.KeyMetaShfDown) 684 dotGoAddStr(w, "KeyCtrlShfLeft", t.KeyCtrlShfLeft) 685 dotGoAddStr(w, "KeyCtrlShfRight", t.KeyCtrlShfRight) 686 dotGoAddStr(w, "KeyCtrlShfUp", t.KeyCtrlShfUp) 687 dotGoAddStr(w, "KeyCtrlShfDown", t.KeyCtrlShfDown) 688 dotGoAddStr(w, "KeyShfHome", t.KeyShfHome) 689 dotGoAddStr(w, "KeyShfEnd", t.KeyShfEnd) 690 dotGoAddStr(w, "KeyCtrlHome", t.KeyCtrlHome) 691 dotGoAddStr(w, "KeyCtrlEnd", t.KeyCtrlEnd) 692 dotGoAddStr(w, "KeyMetaHome", t.KeyMetaHome) 693 dotGoAddStr(w, "KeyMetaEnd", t.KeyMetaEnd) 694 dotGoAddStr(w, "KeyAltHome", t.KeyAltHome) 695 dotGoAddStr(w, "KeyAltEnd", t.KeyAltEnd) 696 dotGoAddStr(w, "KeyCtrlShfHome", t.KeyCtrlShfHome) 697 dotGoAddStr(w, "KeyCtrlShfEnd", t.KeyCtrlShfEnd) 698 dotGoAddStr(w, "KeyMetaShfHome", t.KeyMetaShfHome) 699 dotGoAddStr(w, "KeyMetaShfEnd", t.KeyMetaShfEnd) 700 dotGoAddStr(w, "KeyAltShfHome", t.KeyAltShfHome) 701 dotGoAddStr(w, "KeyAltShfEnd", t.KeyAltShfEnd) 702 fmt.Fprintln(w, "\t})") 703 fmt.Fprintln(w, "}") 704} 705 706var packname = "terminfo" 707 708func dotGoFile(fname string, term *terminfo.Terminfo, desc string, makeDir bool) error { 709 w := os.Stdout 710 var e error 711 if fname != "-" && fname != "" { 712 if makeDir { 713 dname := path.Dir(fname) 714 _ = os.Mkdir(dname, 0777) 715 } 716 if w, e = os.Create(fname); e != nil { 717 return e 718 } 719 } 720 dotGoHeader(w, packname) 721 dotGoInfo(w, term, desc) 722 dotGoTrailer(w) 723 if w != os.Stdout { 724 w.Close() 725 } 726 cmd := exec.Command("go", "fmt", fname) 727 cmd.Run() 728 return nil 729} 730 731func dotGzFile(fname string, term *terminfo.Terminfo, makeDir bool) error { 732 733 var w io.WriteCloser = os.Stdout 734 var e error 735 if fname != "-" && fname != "" { 736 if makeDir { 737 dname := path.Dir(fname) 738 _ = os.Mkdir(dname, 0777) 739 } 740 if w, e = os.Create(fname); e != nil { 741 return e 742 } 743 } 744 745 w = gzip.NewWriter(w) 746 747 js, e := json.Marshal(term) 748 fmt.Fprintln(w, string(js)) 749 750 if w != os.Stdout { 751 w.Close() 752 } 753 return nil 754} 755 756func jsonFile(fname string, term *terminfo.Terminfo, makeDir bool) error { 757 w := os.Stdout 758 var e error 759 if fname != "-" && fname != "" { 760 if makeDir { 761 dname := path.Dir(fname) 762 _ = os.Mkdir(dname, 0777) 763 } 764 if w, e = os.Create(fname); e != nil { 765 return e 766 } 767 } 768 769 js, e := json.Marshal(term) 770 fmt.Fprintln(w, string(js)) 771 772 if w != os.Stdout { 773 w.Close() 774 } 775 return nil 776} 777 778func dumpDatabase(terms map[string]*terminfo.Terminfo, descs map[string]string) { 779 780 // Load models .text 781 mfile, e := os.Open("models.txt") 782 models := make(map[string]bool) 783 if e != nil { 784 fmt.Fprintf(os.Stderr, "Failed reading models.txt: %v", e) 785 } 786 scanner := bufio.NewScanner(mfile) 787 for scanner.Scan() { 788 models[scanner.Text()] = true 789 } 790 791 for name, t := range terms { 792 793 // If this is one of our builtin models, generate the GO file 794 if models[name] { 795 desc := descs[name] 796 safename := strings.Replace(name, "-", "_", -1) 797 goname := fmt.Sprintf("term_%s.go", safename) 798 e = dotGoFile(goname, t, desc, true) 799 if e != nil { 800 fmt.Fprintf(os.Stderr, "Failed creating %s: %v", goname, e) 801 os.Exit(1) 802 } 803 continue 804 } 805 806 hash := fmt.Sprintf("%x", sha1.Sum([]byte(name))) 807 fname := fmt.Sprintf("%s.gz", hash[0:8]) 808 fname = path.Join("database", hash[0:2], fname) 809 e = dotGzFile(fname, t, true) 810 if e != nil { 811 fmt.Fprintf(os.Stderr, "Failed creating %s: %v", fname, e) 812 os.Exit(1) 813 } 814 815 for _, a := range t.Aliases { 816 hash = fmt.Sprintf("%x", sha1.Sum([]byte(a))) 817 fname = path.Join("database", hash[0:2], hash[0:8]) 818 e = jsonFile(fname, &terminfo.Terminfo{Name: t.Name}, true) 819 if e != nil { 820 fmt.Fprintf(os.Stderr, "Failed creating %s: %v", fname, e) 821 os.Exit(1) 822 } 823 } 824 } 825} 826 827func main() { 828 gofile := "" 829 jsonfile := "" 830 nofatal := false 831 quiet := false 832 dogzip := false 833 all := false 834 db := false 835 836 flag.StringVar(&gofile, "go", "", "generate go source in named file") 837 flag.StringVar(&jsonfile, "json", "", "generate json in named file") 838 flag.StringVar(&packname, "P", packname, "package name (go source)") 839 flag.BoolVar(&nofatal, "nofatal", false, "errors are not fatal") 840 flag.BoolVar(&quiet, "quiet", false, "suppress error messages") 841 flag.BoolVar(&dogzip, "gzip", false, "compress json output") 842 flag.BoolVar(&all, "all", false, "load all terminals from terminfo") 843 flag.BoolVar(&db, "db", false, "generate json db file in place") 844 flag.Parse() 845 var e error 846 847 args := flag.Args() 848 if all { 849 db = true // implied 850 allterms, e := getallterms() 851 if e != nil { 852 fmt.Fprintf(os.Stderr, "Failed: %v", e) 853 os.Exit(1) 854 } 855 args = append(args, allterms...) 856 } 857 if len(args) == 0 { 858 args = []string{os.Getenv("TERM")} 859 } 860 861 tdata := make(map[string]*terminfo.Terminfo) 862 descs := make(map[string]string) 863 864 for _, term := range args { 865 if t, desc, e := getinfo(term); e != nil { 866 if all && e == notaddressable { 867 continue 868 } 869 if !quiet { 870 fmt.Fprintf(os.Stderr, 871 "Failed loading %s: %v\n", term, e) 872 } 873 if !nofatal { 874 os.Exit(1) 875 } 876 } else { 877 tdata[term] = t 878 descs[term] = desc 879 } 880 } 881 882 if len(tdata) == 0 { 883 // No data. 884 os.Exit(0) 885 } 886 887 if db { 888 dumpDatabase(tdata, descs) 889 } else if gofile != "" { 890 for term, t := range tdata { 891 if t.Name == term { 892 e = dotGoFile(gofile, t, descs[term], false) 893 if e != nil { 894 fmt.Fprintf(os.Stderr, "Failed %s: %v", gofile, e) 895 os.Exit(1) 896 } 897 } 898 } 899 900 } else { 901 for _, t := range tdata { 902 if dogzip { 903 if e = dotGzFile(jsonfile, t, false); e != nil { 904 fmt.Fprintf(os.Stderr, "Failed %s: %v", gofile, e) 905 os.Exit(1) 906 } 907 } else { 908 if e = jsonFile(jsonfile, t, false); e != nil { 909 fmt.Fprintf(os.Stderr, "Failed %s: %v", gofile, e) 910 os.Exit(1) 911 } 912 } 913 } 914 } 915} 916