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