1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4// Built-in fallback terminfo entries.
5
6#include <stdbool.h>
7#include <string.h>
8
9#include <unibilium.h>
10
11#include "nvim/log.h"
12#include "nvim/globals.h"
13#include "nvim/memory.h"
14#include "nvim/message.h"
15#include "nvim/option.h"
16#include "nvim/os/os.h"
17#include "nvim/tui/terminfo.h"
18#include "nvim/tui/terminfo_defs.h"
19
20#ifdef INCLUDE_GENERATED_DECLARATIONS
21# include "tui/terminfo.c.generated.h"
22#endif
23
24bool terminfo_is_term_family(const char *term, const char *family)
25{
26 if (!term) {
27 return false;
28 }
29 size_t tlen = strlen(term);
30 size_t flen = strlen(family);
31 return tlen >= flen
32 && 0 == memcmp(term, family, flen)
33 // Per commentary in terminfo, minus is the only valid suffix separator.
34 && ('\0' == term[flen] || '-' == term[flen]);
35}
36
37bool terminfo_is_bsd_console(const char *term)
38{
39#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \
40 || defined(__DragonFly__)
41 if (strequal(term, "vt220") // OpenBSD
42 || strequal(term, "vt100")) { // NetBSD
43 return true;
44 }
45# if defined(__FreeBSD__)
46 // FreeBSD console sets TERM=xterm, but it does not support xterm features
47 // like cursor-shaping. Assume that TERM=xterm is degraded. #8644
48 return strequal(term, "xterm") && !!os_getenv("XTERM_VERSION");
49# endif
50#else
51 return false;
52#endif
53}
54
55/// Loads a built-in terminfo db when we (unibilium) failed to load a terminfo
56/// record from the environment (termcap systems, unrecognized $TERM, …).
57/// We do not attempt to detect xterm pretenders here.
58///
59/// @param term $TERM value
60/// @param[out,allocated] termname decided builtin 'term' name
61/// @return [allocated] terminfo structure
62static unibi_term *terminfo_builtin(const char *term, char **termname)
63{
64 if (terminfo_is_term_family(term, "xterm")) {
65 *termname = xstrdup("builtin_xterm");
66 return unibi_from_mem((const char *)xterm_256colour_terminfo,
67 sizeof xterm_256colour_terminfo);
68 } else if (terminfo_is_term_family(term, "screen")) {
69 *termname = xstrdup("builtin_screen");
70 return unibi_from_mem((const char *)screen_256colour_terminfo,
71 sizeof screen_256colour_terminfo);
72 } else if (terminfo_is_term_family(term, "tmux")) {
73 *termname = xstrdup("builtin_tmux");
74 return unibi_from_mem((const char *)tmux_256colour_terminfo,
75 sizeof tmux_256colour_terminfo);
76 } else if (terminfo_is_term_family(term, "rxvt")) {
77 *termname = xstrdup("builtin_rxvt");
78 return unibi_from_mem((const char *)rxvt_256colour_terminfo,
79 sizeof rxvt_256colour_terminfo);
80 } else if (terminfo_is_term_family(term, "putty")) {
81 *termname = xstrdup("builtin_putty");
82 return unibi_from_mem((const char *)putty_256colour_terminfo,
83 sizeof putty_256colour_terminfo);
84 } else if (terminfo_is_term_family(term, "linux")) {
85 *termname = xstrdup("builtin_linux");
86 return unibi_from_mem((const char *)linux_16colour_terminfo,
87 sizeof linux_16colour_terminfo);
88 } else if (terminfo_is_term_family(term, "interix")) {
89 *termname = xstrdup("builtin_interix");
90 return unibi_from_mem((const char *)interix_8colour_terminfo,
91 sizeof interix_8colour_terminfo);
92 } else if (terminfo_is_term_family(term, "iterm")
93 || terminfo_is_term_family(term, "iterm2")
94 || terminfo_is_term_family(term, "iTerm.app")
95 || terminfo_is_term_family(term, "iTerm2.app")) {
96 *termname = xstrdup("builtin_iterm");
97 return unibi_from_mem((const char *)iterm_256colour_terminfo,
98 sizeof iterm_256colour_terminfo);
99 } else if (terminfo_is_term_family(term, "st")) {
100 *termname = xstrdup("builtin_st");
101 return unibi_from_mem((const char *)st_256colour_terminfo,
102 sizeof st_256colour_terminfo);
103 } else if (terminfo_is_term_family(term, "gnome")
104 || terminfo_is_term_family(term, "vte")) {
105 *termname = xstrdup("builtin_vte");
106 return unibi_from_mem((const char *)vte_256colour_terminfo,
107 sizeof vte_256colour_terminfo);
108 } else if (terminfo_is_term_family(term, "cygwin")) {
109 *termname = xstrdup("builtin_cygwin");
110 return unibi_from_mem((const char *)cygwin_terminfo,
111 sizeof cygwin_terminfo);
112 } else if (terminfo_is_term_family(term, "win32con")) {
113 *termname = xstrdup("builtin_win32con");
114 return unibi_from_mem((const char *)win32con_terminfo,
115 sizeof win32con_terminfo);
116 } else if (terminfo_is_term_family(term, "conemu")) {
117 *termname = xstrdup("builtin_conemu");
118 return unibi_from_mem((const char *)conemu_terminfo,
119 sizeof conemu_terminfo);
120 } else if (terminfo_is_term_family(term, "vtpcon")) {
121 *termname = xstrdup("builtin_vtpcon");
122 return unibi_from_mem((const char *)vtpcon_terminfo,
123 sizeof vtpcon_terminfo);
124 } else {
125 *termname = xstrdup("builtin_ansi");
126 return unibi_from_mem((const char *)ansi_terminfo,
127 sizeof ansi_terminfo);
128 }
129}
130
131/// @param term $TERM value
132/// @param[out,allocated] termname decided builtin 'term' name
133/// @return [allocated] terminfo structure
134unibi_term *terminfo_from_builtin(const char *term, char **termname)
135{
136 unibi_term *ut = terminfo_builtin(term, termname);
137 if (*termname == NULL) {
138 *termname = xstrdup("builtin_?");
139 }
140 return ut;
141}
142
143/// Dumps termcap info to the messages area.
144/// Serves a similar purpose as Vim `:set termcap` (removed in Nvim).
145///
146/// @note adapted from unibilium unibi-dump.c
147void terminfo_info_msg(const unibi_term *const ut)
148{
149 if (exiting) {
150 return;
151 }
152 msg_puts_title("\n\n--- Terminal info --- {{{\n");
153
154 char *term;
155 get_tty_option("term", &term);
156 msg_printf_attr(0, "&term: %s\n", term);
157 msg_printf_attr(0, "Description: %s\n", unibi_get_name(ut));
158 const char **a = unibi_get_aliases(ut);
159 if (*a) {
160 msg_puts("Aliases: ");
161 do {
162 msg_printf_attr(0, "%s%s\n", *a, a[1] ? " | " : "");
163 a++;
164 } while (*a);
165 }
166
167 msg_puts("Boolean capabilities:\n");
168 for (enum unibi_boolean i = unibi_boolean_begin_ + 1;
169 i < unibi_boolean_end_; i++) {
170 msg_printf_attr(0, " %-25s %-10s = %s\n", unibi_name_bool(i),
171 unibi_short_name_bool(i),
172 unibi_get_bool(ut, i) ? "true" : "false");
173 }
174
175 msg_puts("Numeric capabilities:\n");
176 for (enum unibi_numeric i = unibi_numeric_begin_ + 1;
177 i < unibi_numeric_end_; i++) {
178 int n = unibi_get_num(ut, i); // -1 means "empty"
179 msg_printf_attr(0, " %-25s %-10s = %d\n", unibi_name_num(i),
180 unibi_short_name_num(i), n);
181 }
182
183 msg_puts("String capabilities:\n");
184 for (enum unibi_string i = unibi_string_begin_ + 1;
185 i < unibi_string_end_; i++) {
186 const char *s = unibi_get_str(ut, i);
187 if (s) {
188 msg_printf_attr(0, " %-25s %-10s = ", unibi_name_str(i),
189 unibi_short_name_str(i));
190 // Most of these strings will contain escape sequences.
191 msg_outtrans_special((char_u *)s, false);
192 msg_putchar('\n');
193 }
194 }
195
196 if (unibi_count_ext_bool(ut)) {
197 msg_puts("Extended boolean capabilities:\n");
198 for (size_t i = 0; i < unibi_count_ext_bool(ut); i++) {
199 msg_printf_attr(0, " %-25s = %s\n",
200 unibi_get_ext_bool_name(ut, i),
201 unibi_get_ext_bool(ut, i) ? "true" : "false");
202 }
203 }
204
205 if (unibi_count_ext_num(ut)) {
206 msg_puts("Extended numeric capabilities:\n");
207 for (size_t i = 0; i < unibi_count_ext_num(ut); i++) {
208 msg_printf_attr(0, " %-25s = %d\n",
209 unibi_get_ext_num_name(ut, i),
210 unibi_get_ext_num(ut, i));
211 }
212 }
213
214 if (unibi_count_ext_str(ut)) {
215 msg_puts("Extended string capabilities:\n");
216 for (size_t i = 0; i < unibi_count_ext_str(ut); i++) {
217 msg_printf_attr(0, " %-25s = ", unibi_get_ext_str_name(ut, i));
218 msg_outtrans_special((char_u *)unibi_get_ext_str(ut, i), false);
219 msg_putchar('\n');
220 }
221 }
222
223 msg_puts("}}}\n");
224 xfree(term);
225}
226