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