1#![allow(non_upper_case_globals, missing_docs)]
2
3use std::collections::HashMap;
6use std::io;
7use std::io::prelude::*;
8
9use super::super::TermInfo;
10
11#[cfg(test)]
12mod tests;
13
14#[rustfmt::skip]
17pub(crate) static boolfnames: &[&str] = &["auto_left_margin", "auto_right_margin",
18 "no_esc_ctlc", "ceol_standout_glitch", "eat_newline_glitch", "erase_overstrike", "generic_type",
19 "hard_copy", "has_meta_key", "has_status_line", "insert_null_glitch", "memory_above",
20 "memory_below", "move_insert_mode", "move_standout_mode", "over_strike", "status_line_esc_ok",
21 "dest_tabs_magic_smso", "tilde_glitch", "transparent_underline", "xon_xoff", "needs_xon_xoff",
22 "prtr_silent", "hard_cursor", "non_rev_rmcup", "no_pad_char", "non_dest_scroll_region",
23 "can_change", "back_color_erase", "hue_lightness_saturation", "col_addr_glitch",
24 "cr_cancels_micro_mode", "has_print_wheel", "row_addr_glitch", "semi_auto_right_margin",
25 "cpi_changes_res", "lpi_changes_res", "backspaces_with_bs", "crt_no_scrolling",
26 "no_correctly_working_cr", "gnu_has_meta_key", "linefeed_is_newline", "has_hardware_tabs",
27 "return_does_clr_eol"];
28
29#[rustfmt::skip]
30pub(crate) static boolnames: &[&str] = &["bw", "am", "xsb", "xhp", "xenl", "eo",
31 "gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon",
32 "nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy",
33 "xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
34
35#[rustfmt::skip]
36pub(crate) static numfnames: &[&str] = &[ "columns", "init_tabs", "lines",
37 "lines_of_memory", "magic_cookie_glitch", "padding_baud_rate", "virtual_terminal",
38 "width_status_line", "num_labels", "label_height", "label_width", "max_attributes",
39 "maximum_windows", "max_colors", "max_pairs", "no_color_video", "buffer_capacity",
40 "dot_vert_spacing", "dot_horz_spacing", "max_micro_address", "max_micro_jump", "micro_col_size",
41 "micro_line_size", "number_of_pins", "output_res_char", "output_res_line",
42 "output_res_horz_inch", "output_res_vert_inch", "print_rate", "wide_char_size", "buttons",
43 "bit_image_entwining", "bit_image_type", "magic_cookie_glitch_ul", "carriage_return_delay",
44 "new_line_delay", "backspace_delay", "horizontal_tab_delay", "number_of_function_keys"];
45
46#[rustfmt::skip]
47pub(crate) static numnames: &[&str] = &[ "cols", "it", "lines", "lm", "xmc", "pb",
48 "vt", "wsl", "nlab", "lh", "lw", "ma", "wnum", "colors", "pairs", "ncv", "bufsz", "spinv",
49 "spinh", "maddr", "mjump", "mcs", "mls", "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs",
50 "btns", "bitwin", "bitype", "UTug", "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
51
52#[rustfmt::skip]
53pub(crate) static stringfnames: &[&str] = &[ "back_tab", "bell", "carriage_return",
54 "change_scroll_region", "clear_all_tabs", "clear_screen", "clr_eol", "clr_eos",
55 "column_address", "command_character", "cursor_address", "cursor_down", "cursor_home",
56 "cursor_invisible", "cursor_left", "cursor_mem_address", "cursor_normal", "cursor_right",
57 "cursor_to_ll", "cursor_up", "cursor_visible", "delete_character", "delete_line",
58 "dis_status_line", "down_half_line", "enter_alt_charset_mode", "enter_blink_mode",
59 "enter_bold_mode", "enter_ca_mode", "enter_delete_mode", "enter_dim_mode", "enter_insert_mode",
60 "enter_secure_mode", "enter_protected_mode", "enter_reverse_mode", "enter_standout_mode",
61 "enter_underline_mode", "erase_chars", "exit_alt_charset_mode", "exit_attribute_mode",
62 "exit_ca_mode", "exit_delete_mode", "exit_insert_mode", "exit_standout_mode",
63 "exit_underline_mode", "flash_screen", "form_feed", "from_status_line", "init_1string",
64 "init_2string", "init_3string", "init_file", "insert_character", "insert_line",
65 "insert_padding", "key_backspace", "key_catab", "key_clear", "key_ctab", "key_dc", "key_dl",
66 "key_down", "key_eic", "key_eol", "key_eos", "key_f0", "key_f1", "key_f10", "key_f2", "key_f3",
67 "key_f4", "key_f5", "key_f6", "key_f7", "key_f8", "key_f9", "key_home", "key_ic", "key_il",
68 "key_left", "key_ll", "key_npage", "key_ppage", "key_right", "key_sf", "key_sr", "key_stab",
69 "key_up", "keypad_local", "keypad_xmit", "lab_f0", "lab_f1", "lab_f10", "lab_f2", "lab_f3",
70 "lab_f4", "lab_f5", "lab_f6", "lab_f7", "lab_f8", "lab_f9", "meta_off", "meta_on", "newline",
71 "pad_char", "parm_dch", "parm_delete_line", "parm_down_cursor", "parm_ich", "parm_index",
72 "parm_insert_line", "parm_left_cursor", "parm_right_cursor", "parm_rindex", "parm_up_cursor",
73 "pkey_key", "pkey_local", "pkey_xmit", "print_screen", "prtr_off", "prtr_on", "repeat_char",
74 "reset_1string", "reset_2string", "reset_3string", "reset_file", "restore_cursor",
75 "row_address", "save_cursor", "scroll_forward", "scroll_reverse", "set_attributes", "set_tab",
76 "set_window", "tab", "to_status_line", "underline_char", "up_half_line", "init_prog", "key_a1",
77 "key_a3", "key_b2", "key_c1", "key_c3", "prtr_non", "char_padding", "acs_chars", "plab_norm",
78 "key_btab", "enter_xon_mode", "exit_xon_mode", "enter_am_mode", "exit_am_mode", "xon_character",
79 "xoff_character", "ena_acs", "label_on", "label_off", "key_beg", "key_cancel", "key_close",
80 "key_command", "key_copy", "key_create", "key_end", "key_enter", "key_exit", "key_find",
81 "key_help", "key_mark", "key_message", "key_move", "key_next", "key_open", "key_options",
82 "key_previous", "key_print", "key_redo", "key_reference", "key_refresh", "key_replace",
83 "key_restart", "key_resume", "key_save", "key_suspend", "key_undo", "key_sbeg", "key_scancel",
84 "key_scommand", "key_scopy", "key_screate", "key_sdc", "key_sdl", "key_select", "key_send",
85 "key_seol", "key_sexit", "key_sfind", "key_shelp", "key_shome", "key_sic", "key_sleft",
86 "key_smessage", "key_smove", "key_snext", "key_soptions", "key_sprevious", "key_sprint",
87 "key_sredo", "key_sreplace", "key_sright", "key_srsume", "key_ssave", "key_ssuspend",
88 "key_sundo", "req_for_input", "key_f11", "key_f12", "key_f13", "key_f14", "key_f15", "key_f16",
89 "key_f17", "key_f18", "key_f19", "key_f20", "key_f21", "key_f22", "key_f23", "key_f24",
90 "key_f25", "key_f26", "key_f27", "key_f28", "key_f29", "key_f30", "key_f31", "key_f32",
91 "key_f33", "key_f34", "key_f35", "key_f36", "key_f37", "key_f38", "key_f39", "key_f40",
92 "key_f41", "key_f42", "key_f43", "key_f44", "key_f45", "key_f46", "key_f47", "key_f48",
93 "key_f49", "key_f50", "key_f51", "key_f52", "key_f53", "key_f54", "key_f55", "key_f56",
94 "key_f57", "key_f58", "key_f59", "key_f60", "key_f61", "key_f62", "key_f63", "clr_bol",
95 "clear_margins", "set_left_margin", "set_right_margin", "label_format", "set_clock",
96 "display_clock", "remove_clock", "create_window", "goto_window", "hangup", "dial_phone",
97 "quick_dial", "tone", "pulse", "flash_hook", "fixed_pause", "wait_tone", "user0", "user1",
98 "user2", "user3", "user4", "user5", "user6", "user7", "user8", "user9", "orig_pair",
99 "orig_colors", "initialize_color", "initialize_pair", "set_color_pair", "set_foreground",
100 "set_background", "change_char_pitch", "change_line_pitch", "change_res_horz",
101 "change_res_vert", "define_char", "enter_doublewide_mode", "enter_draft_quality",
102 "enter_italics_mode", "enter_leftward_mode", "enter_micro_mode", "enter_near_letter_quality",
103 "enter_normal_quality", "enter_shadow_mode", "enter_subscript_mode", "enter_superscript_mode",
104 "enter_upward_mode", "exit_doublewide_mode", "exit_italics_mode", "exit_leftward_mode",
105 "exit_micro_mode", "exit_shadow_mode", "exit_subscript_mode", "exit_superscript_mode",
106 "exit_upward_mode", "micro_column_address", "micro_down", "micro_left", "micro_right",
107 "micro_row_address", "micro_up", "order_of_pins", "parm_down_micro", "parm_left_micro",
108 "parm_right_micro", "parm_up_micro", "select_char_set", "set_bottom_margin",
109 "set_bottom_margin_parm", "set_left_margin_parm", "set_right_margin_parm", "set_top_margin",
110 "set_top_margin_parm", "start_bit_image", "start_char_set_def", "stop_bit_image",
111 "stop_char_set_def", "subscript_characters", "superscript_characters", "these_cause_cr",
112 "zero_motion", "char_set_names", "key_mouse", "mouse_info", "req_mouse_pos", "get_mouse",
113 "set_a_foreground", "set_a_background", "pkey_plab", "device_type", "code_set_init",
114 "set0_des_seq", "set1_des_seq", "set2_des_seq", "set3_des_seq", "set_lr_margin",
115 "set_tb_margin", "bit_image_repeat", "bit_image_newline", "bit_image_carriage_return",
116 "color_names", "define_bit_image_region", "end_bit_image_region", "set_color_band",
117 "set_page_length", "display_pc_char", "enter_pc_charset_mode", "exit_pc_charset_mode",
118 "enter_scancode_mode", "exit_scancode_mode", "pc_term_options", "scancode_escape",
119 "alt_scancode_esc", "enter_horizontal_hl_mode", "enter_left_hl_mode", "enter_low_hl_mode",
120 "enter_right_hl_mode", "enter_top_hl_mode", "enter_vertical_hl_mode", "set_a_attributes",
121 "set_pglen_inch", "termcap_init2", "termcap_reset", "linefeed_if_not_lf", "backspace_if_not_bs",
122 "other_non_function_keys", "arrow_key_map", "acs_ulcorner", "acs_llcorner", "acs_urcorner",
123 "acs_lrcorner", "acs_ltee", "acs_rtee", "acs_btee", "acs_ttee", "acs_hline", "acs_vline",
124 "acs_plus", "memory_lock", "memory_unlock", "box_chars_1"];
125
126#[rustfmt::skip]
127pub(crate) static stringnames: &[&str] = &[ "cbt", "_", "cr", "csr", "tbc", "clear",
128 "_", "_", "hpa", "cmdch", "cup", "cud1", "home", "civis", "cub1", "mrcup", "cnorm", "cuf1",
129 "ll", "cuu1", "cvvis", "dch1", "dl1", "dsl", "hd", "smacs", "blink", "bold", "smcup", "smdc",
130 "dim", "smir", "invis", "prot", "rev", "smso", "smul", "ech", "rmacs", "sgr0", "rmcup", "rmdc",
131 "rmir", "rmso", "rmul", "flash", "ff", "fsl", "is1", "is2", "is3", "if", "ich1", "il1", "ip",
132 "kbs", "ktbc", "kclr", "kctab", "_", "_", "kcud1", "_", "_", "_", "_", "_", "_", "_", "_", "_",
133 "_", "_", "_", "_", "_", "khome", "_", "_", "kcub1", "_", "knp", "kpp", "kcuf1", "_", "_",
134 "khts", "_", "rmkx", "smkx", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "rmm", "_",
135 "_", "pad", "dch", "dl", "cud", "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey",
136 "pfloc", "pfx", "mc0", "mc4", "_", "rep", "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind",
137 "ri", "sgr", "_", "wind", "_", "tsl", "uc", "hu", "iprog", "_", "_", "_", "_", "_", "mc5p",
138 "rmp", "acsc", "pln", "kcbt", "smxon", "rmxon", "smam", "rmam", "xonc", "xoffc", "_", "smln",
139 "rmln", "_", "kcan", "kclo", "kcmd", "kcpy", "kcrt", "_", "kent", "kext", "kfnd", "khlp",
140 "kmrk", "kmsg", "kmov", "knxt", "kopn", "kopt", "kprv", "kprt", "krdo", "kref", "krfr", "krpl",
141 "krst", "kres", "ksav", "kspd", "kund", "kBEG", "kCAN", "kCMD", "kCPY", "kCRT", "_", "_",
142 "kslt", "kEND", "kEOL", "kEXT", "kFND", "kHLP", "kHOM", "_", "kLFT", "kMSG", "kMOV", "kNXT",
143 "kOPT", "kPRV", "kPRT", "kRDO", "kRPL", "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "_", "_",
144 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
145 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
146 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
147 "dclk", "rmclk", "cwin", "wingo", "_", "dial", "qdial", "_", "_", "hook", "pause", "wait", "_",
148 "_", "_", "_", "_", "_", "_", "_", "_", "_", "op", "oc", "initc", "initp", "scp", "setf",
149 "setb", "cpi", "lpi", "chr", "cvr", "defc", "swidm", "sdrfq", "sitm", "slm", "smicm", "snlq",
150 "snrmq", "sshm", "ssubm", "ssupm", "sum", "rwidm", "ritm", "rlm", "rmicm", "rshm", "rsubm",
151 "rsupm", "rum", "mhpa", "mcud1", "mcub1", "mcuf1", "mvpa", "mcuu1", "porder", "mcud", "mcub",
152 "mcuf", "mcuu", "scs", "smgb", "smgbp", "smglp", "smgrp", "smgt", "smgtp", "sbim", "scsd",
153 "rbim", "rcsd", "subcs", "supcs", "docr", "zerom", "csnm", "kmous", "minfo", "reqmp", "getm",
154 "setaf", "setab", "pfxl", "devt", "csin", "s0ds", "s1ds", "s2ds", "s3ds", "smglr", "smgtb",
155 "birep", "binel", "bicr", "colornm", "defbi", "endbi", "setcolor", "slines", "dispc", "smpch",
156 "rmpch", "smsc", "rmsc", "pctrm", "scesc", "scesa", "ehhlm", "elhlm", "elohlm", "erhlm",
157 "ethlm", "evhlm", "sgr1", "slength", "OTi2", "OTrs", "OTnl", "OTbs", "OTko", "OTma", "OTG2",
158 "OTG3", "OTG1", "OTG4", "OTGR", "OTGL", "OTGU", "OTGD", "OTGH", "OTGV", "OTGC", "meml", "memu",
159 "box1"];
160
161fn read_le_u16(r: &mut dyn io::Read) -> io::Result<u16> {
162 let mut b = [0; 2];
163 r.read_exact(&mut b)?;
164 Ok((b[0] as u16) | ((b[1] as u16) << 8))
165}
166
167fn read_le_u32(r: &mut dyn io::Read) -> io::Result<u32> {
168 let mut b = [0; 4];
169 r.read_exact(&mut b)?;
170 Ok((b[0] as u32) | ((b[1] as u32) << 8) | ((b[2] as u32) << 16) | ((b[3] as u32) << 24))
171}
172
173fn read_byte(r: &mut dyn io::Read) -> io::Result<u8> {
174 match r.bytes().next() {
175 Some(s) => s,
176 None => Err(io::const_error!(io::ErrorKind::Other, "end of file")),
177 }
178}
179
180pub(crate) fn parse(file: &mut dyn io::Read, longnames: bool) -> Result<TermInfo, String> {
183 macro_rules! t( ($e:expr) => (
184 match $e {
185 Ok(e) => e,
186 Err(e) => return Err(e.to_string())
187 }
188 ) );
189
190 let (bnames, snames, nnames) = if longnames {
191 (boolfnames, stringfnames, numfnames)
192 } else {
193 (boolnames, stringnames, numnames)
194 };
195
196 let magic = t!(read_le_u16(file));
198
199 let extended = match magic {
200 0o0432 => false,
201 0o01036 => true,
202 _ => return Err(format!("invalid magic number, found {magic:o}")),
203 };
204
205 macro_rules! read_nonneg {
208 () => {{
209 match t!(read_le_u16(file)) as i16 {
210 n if n >= 0 => n as usize,
211 -1 => 0,
212 _ => return Err("incompatible file: length fields must be >= -1".to_string()),
213 }
214 }};
215 }
216
217 let names_bytes = read_nonneg!();
218 let bools_bytes = read_nonneg!();
219 let numbers_count = read_nonneg!();
220 let string_offsets_count = read_nonneg!();
221 let string_table_bytes = read_nonneg!();
222
223 if names_bytes == 0 {
224 return Err("incompatible file: names field must be at least 1 byte wide".to_string());
225 }
226
227 if bools_bytes > boolnames.len() {
228 return Err("incompatible file: more booleans than expected".to_string());
229 }
230
231 if numbers_count > numnames.len() {
232 return Err("incompatible file: more numbers than expected".to_string());
233 }
234
235 if string_offsets_count > stringnames.len() {
236 return Err("incompatible file: more string offsets than expected".to_string());
237 }
238
239 let mut bytes = Vec::new();
241 t!(file.take((names_bytes - 1) as u64).read_to_end(&mut bytes));
242 let names_str = match String::from_utf8(bytes) {
243 Ok(s) => s,
244 Err(_) => return Err("input not utf-8".to_string()),
245 };
246
247 let term_names: Vec<String> = names_str.split('|').map(|s| s.to_string()).collect();
248 if t!(read_byte(file)) != b'\0' {
250 return Err("incompatible file: missing null terminator for names section".to_string());
251 }
252
253 let bools_map: HashMap<String, bool> = t! {
254 (0..bools_bytes).filter_map(|i| match read_byte(file) {
255 Err(e) => Some(Err(e)),
256 Ok(1) => Some(Ok((bnames[i].to_string(), true))),
257 Ok(_) => None
258 }).collect()
259 };
260
261 if (bools_bytes + names_bytes) % 2 == 1 {
262 t!(read_byte(file)); }
264
265 let numbers_map: HashMap<String, u32> = t! {
266 (0..numbers_count).filter_map(|i| {
267 let number = if extended { read_le_u32(file) } else { read_le_u16(file).map(Into::into) };
268
269 match number {
270 Ok(0xFFFF) => None,
271 Ok(n) => Some(Ok((nnames[i].to_string(), n))),
272 Err(e) => Some(Err(e))
273 }
274 }).collect()
275 };
276
277 let string_map: HashMap<String, Vec<u8>> = if string_offsets_count > 0 {
278 let string_offsets: Vec<u16> =
279 t!((0..string_offsets_count).map(|_| read_le_u16(file)).collect());
280
281 let mut string_table = Vec::new();
282 t!(file.take(string_table_bytes as u64).read_to_end(&mut string_table));
283
284 t!(string_offsets
285 .into_iter()
286 .enumerate()
287 .filter(|&(_, offset)| {
288 offset != 0xFFFF
290 })
291 .map(|(i, offset)| {
292 let offset = offset as usize;
293
294 let name = if snames[i] == "_" { stringfnames[i] } else { snames[i] };
295
296 if offset == 0xFFFE {
297 return Ok((name.to_string(), Vec::new()));
300 }
301
302 let nulpos = string_table[offset..string_table_bytes].iter().position(|&b| b == 0);
304 match nulpos {
305 Some(len) => {
306 Ok((name.to_string(), string_table[offset..offset + len].to_vec()))
307 }
308 None => Err("invalid file: missing NUL in string_table".to_string()),
309 }
310 })
311 .collect())
312 } else {
313 HashMap::new()
314 };
315
316 Ok(TermInfo { names: term_names, bools: bools_map, numbers: numbers_map, strings: string_map })
318}
319
320pub(crate) fn msys_terminfo() -> TermInfo {
322 let mut strings = HashMap::new();
323 strings.insert("sgr0".to_string(), b"\x1B[0m".to_vec());
324 strings.insert("bold".to_string(), b"\x1B[1m".to_vec());
325 strings.insert("setaf".to_string(), b"\x1B[3%p1%dm".to_vec());
326 strings.insert("setab".to_string(), b"\x1B[4%p1%dm".to_vec());
327
328 let mut numbers = HashMap::new();
329 numbers.insert("colors".to_string(), 8);
330
331 TermInfo {
332 names: vec!["cygwin".to_string()], bools: HashMap::new(),
334 numbers,
335 strings,
336 }
337}