1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![allow(non_upper_case_globals)]
13 //! ncurses-compatible compiled terminfo format parsing (term(5))
15 use std::collections::HashMap;
17 use super::super::TermInfo;
19 // These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
21 pub static boolfnames: &'static[&'static str] = &["auto_left_margin", "auto_right_margin",
22 "no_esc_ctlc", "ceol_standout_glitch", "eat_newline_glitch", "erase_overstrike", "generic_type",
23 "hard_copy", "has_meta_key", "has_status_line", "insert_null_glitch", "memory_above",
24 "memory_below", "move_insert_mode", "move_standout_mode", "over_strike", "status_line_esc_ok",
25 "dest_tabs_magic_smso", "tilde_glitch", "transparent_underline", "xon_xoff", "needs_xon_xoff",
26 "prtr_silent", "hard_cursor", "non_rev_rmcup", "no_pad_char", "non_dest_scroll_region",
27 "can_change", "back_color_erase", "hue_lightness_saturation", "col_addr_glitch",
28 "cr_cancels_micro_mode", "has_print_wheel", "row_addr_glitch", "semi_auto_right_margin",
29 "cpi_changes_res", "lpi_changes_res", "backspaces_with_bs", "crt_no_scrolling",
30 "no_correctly_working_cr", "gnu_has_meta_key", "linefeed_is_newline", "has_hardware_tabs",
31 "return_does_clr_eol"];
33 pub static boolnames: &'static[&'static str] = &["bw", "am", "xsb", "xhp", "xenl", "eo",
34 "gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon",
35 "nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy",
36 "xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
38 pub static numfnames: &'static[&'static str] = &[ "columns", "init_tabs", "lines",
39 "lines_of_memory", "magic_cookie_glitch", "padding_baud_rate", "virtual_terminal",
40 "width_status_line", "num_labels", "label_height", "label_width", "max_attributes",
41 "maximum_windows", "max_colors", "max_pairs", "no_color_video", "buffer_capacity",
42 "dot_vert_spacing", "dot_horz_spacing", "max_micro_address", "max_micro_jump", "micro_col_size",
43 "micro_line_size", "number_of_pins", "output_res_char", "output_res_line",
44 "output_res_horz_inch", "output_res_vert_inch", "print_rate", "wide_char_size", "buttons",
45 "bit_image_entwining", "bit_image_type", "magic_cookie_glitch_ul", "carriage_return_delay",
46 "new_line_delay", "backspace_delay", "horizontal_tab_delay", "number_of_function_keys"];
48 pub static numnames: &'static[&'static str] = &[ "cols", "it", "lines", "lm", "xmc", "pb",
49 "vt", "wsl", "nlab", "lh", "lw", "ma", "wnum", "colors", "pairs", "ncv", "bufsz", "spinv",
50 "spinh", "maddr", "mjump", "mcs", "mls", "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs",
51 "btns", "bitwin", "bitype", "UTug", "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
53 pub static stringfnames: &'static[&'static 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"];
126 pub static stringnames: &'static[&'static str] = &[ "cbt", "_", "cr", "csr", "tbc", "clear",
127 "_", "_", "hpa", "cmdch", "cup", "cud1", "home", "civis", "cub1", "mrcup", "cnorm", "cuf1",
128 "ll", "cuu1", "cvvis", "dch1", "dl1", "dsl", "hd", "smacs", "blink", "bold", "smcup", "smdc",
129 "dim", "smir", "invis", "prot", "rev", "smso", "smul", "ech", "rmacs", "sgr0", "rmcup", "rmdc",
130 "rmir", "rmso", "rmul", "flash", "ff", "fsl", "is1", "is2", "is3", "if", "ich1", "il1", "ip",
131 "kbs", "ktbc", "kclr", "kctab", "_", "_", "kcud1", "_", "_", "_", "_", "_", "_", "_", "_", "_",
132 "_", "_", "_", "_", "_", "khome", "_", "_", "kcub1", "_", "knp", "kpp", "kcuf1", "_", "_",
133 "khts", "_", "rmkx", "smkx", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "rmm", "_",
134 "_", "pad", "dch", "dl", "cud", "ich", "indn", "il", "cub", "cuf", "rin", "cuu", "pfkey",
135 "pfloc", "pfx", "mc0", "mc4", "_", "rep", "rs1", "rs2", "rs3", "rf", "rc", "vpa", "sc", "ind",
136 "ri", "sgr", "_", "wind", "_", "tsl", "uc", "hu", "iprog", "_", "_", "_", "_", "_", "mc5p",
137 "rmp", "acsc", "pln", "kcbt", "smxon", "rmxon", "smam", "rmam", "xonc", "xoffc", "_", "smln",
138 "rmln", "_", "kcan", "kclo", "kcmd", "kcpy", "kcrt", "_", "kent", "kext", "kfnd", "khlp",
139 "kmrk", "kmsg", "kmov", "knxt", "kopn", "kopt", "kprv", "kprt", "krdo", "kref", "krfr", "krpl",
140 "krst", "kres", "ksav", "kspd", "kund", "kBEG", "kCAN", "kCMD", "kCPY", "kCRT", "_", "_",
141 "kslt", "kEND", "kEOL", "kEXT", "kFND", "kHLP", "kHOM", "_", "kLFT", "kMSG", "kMOV", "kNXT",
142 "kOPT", "kPRV", "kPRT", "kRDO", "kRPL", "kRIT", "kRES", "kSAV", "kSPD", "kUND", "rfi", "_", "_",
143 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
144 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
145 "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_", "_",
146 "dclk", "rmclk", "cwin", "wingo", "_", "dial", "qdial", "_", "_", "hook", "pause", "wait", "_",
147 "_", "_", "_", "_", "_", "_", "_", "_", "_", "op", "oc", "initc", "initp", "scp", "setf",
148 "setb", "cpi", "lpi", "chr", "cvr", "defc", "swidm", "sdrfq", "sitm", "slm", "smicm", "snlq",
149 "snrmq", "sshm", "ssubm", "ssupm", "sum", "rwidm", "ritm", "rlm", "rmicm", "rshm", "rsubm",
150 "rsupm", "rum", "mhpa", "mcud1", "mcub1", "mcuf1", "mvpa", "mcuu1", "porder", "mcud", "mcub",
151 "mcuf", "mcuu", "scs", "smgb", "smgbp", "smglp", "smgrp", "smgt", "smgtp", "sbim", "scsd",
152 "rbim", "rcsd", "subcs", "supcs", "docr", "zerom", "csnm", "kmous", "minfo", "reqmp", "getm",
153 "setaf", "setab", "pfxl", "devt", "csin", "s0ds", "s1ds", "s2ds", "s3ds", "smglr", "smgtb",
154 "birep", "binel", "bicr", "colornm", "defbi", "endbi", "setcolor", "slines", "dispc", "smpch",
155 "rmpch", "smsc", "rmsc", "pctrm", "scesc", "scesa", "ehhlm", "elhlm", "elohlm", "erhlm",
156 "ethlm", "evhlm", "sgr1", "slength", "OTi2", "OTrs", "OTnl", "OTbs", "OTko", "OTma", "OTG2",
157 "OTG3", "OTG1", "OTG4", "OTGR", "OTGL", "OTGU", "OTGD", "OTGH", "OTGV", "OTGC", "meml", "memu",
160 /// Parse a compiled terminfo entry, using long capability names if `longnames` is true
161 pub fn parse(file: &mut io::Reader, longnames: bool)
162 -> Result<Box<TermInfo>, String> {
163 macro_rules! try( ($e:expr) => (
166 Err(e) => return Err(format!("{}", e))
176 snames = stringfnames;
180 snames = stringnames;
184 // Check magic number
185 let magic = try!(file.read_le_u16());
187 return Err(format!("invalid magic number: expected {:x}, found {:x}",
188 0x011Au, magic as uint));
191 let names_bytes = try!(file.read_le_i16()) as int;
192 let bools_bytes = try!(file.read_le_i16()) as int;
193 let numbers_count = try!(file.read_le_i16()) as int;
194 let string_offsets_count = try!(file.read_le_i16()) as int;
195 let string_table_bytes = try!(file.read_le_i16()) as int;
197 assert!(names_bytes > 0);
199 if (bools_bytes as uint) > boolnames.len() {
200 return Err("incompatible file: more booleans than \
201 expected".to_string());
204 if (numbers_count as uint) > numnames.len() {
205 return Err("incompatible file: more numbers than \
206 expected".to_string());
209 if (string_offsets_count as uint) > stringnames.len() {
210 return Err("incompatible file: more string offsets than \
211 expected".to_string());
215 let bytes = try!(file.read_exact(names_bytes as uint - 1));
216 let names_str = match String::from_utf8(bytes) {
218 Err(_) => return Err("input not utf-8".to_string()),
221 let term_names: Vec<String> = names_str.split('|')
222 .map(|s| s.to_string())
225 try!(file.read_byte()); // consume NUL
227 let mut bools_map = HashMap::new();
228 if bools_bytes != 0 {
229 for i in range(0, bools_bytes) {
230 let b = try!(file.read_byte());
232 bools_map.insert(bnames[i as uint].to_string(), true);
237 if (bools_bytes + names_bytes) % 2 == 1 {
238 try!(file.read_byte()); // compensate for padding
241 let mut numbers_map = HashMap::new();
242 if numbers_count != 0 {
243 for i in range(0, numbers_count) {
244 let n = try!(file.read_le_u16());
246 numbers_map.insert(nnames[i as uint].to_string(), n);
251 let mut string_map = HashMap::new();
253 if string_offsets_count != 0 {
254 let mut string_offsets = Vec::with_capacity(10);
255 for _ in range(0, string_offsets_count) {
256 string_offsets.push(try!(file.read_le_u16()));
259 let string_table = try!(file.read_exact(string_table_bytes as uint));
261 if string_table.len() != string_table_bytes as uint {
262 return Err("error: hit EOF before end of string \
266 for (i, v) in string_offsets.iter().enumerate() {
268 if offset == 0xFFFF { // non-entry
272 let name = if snames[i] == "_" {
278 if offset == 0xFFFE {
279 // undocumented: FFFE indicates cap@, which means the capability is not present
280 // unsure if the handling for this is correct
281 string_map.insert(name.to_string(), Vec::new());
286 // Find the offset of the NUL we want to go to
287 let nulpos = string_table[offset as uint .. string_table_bytes as uint]
288 .iter().position(|&b| b == 0);
291 string_map.insert(name.to_string(),
292 string_table[offset as uint ..
293 offset as uint + len].to_vec())
296 return Err("invalid file: missing NUL in \
297 string_table".to_string());
303 // And that's all there is to it
307 numbers: numbers_map,
312 /// Create a dummy TermInfo struct for msys terminals
313 pub fn msys_terminfo() -> Box<TermInfo> {
314 let mut strings = HashMap::new();
315 strings.insert("sgr0".to_string(), b"\x1B[0m".to_vec());
316 strings.insert("bold".to_string(), b"\x1B[1m".to_vec());
317 strings.insert("setaf".to_string(), b"\x1B[3%p1%dm".to_vec());
318 strings.insert("setab".to_string(), b"\x1B[4%p1%dm".to_vec());
320 names: vec!("cygwin".to_string()), // msys is a fork of an older cygwin version
321 bools: HashMap::new(),
322 numbers: HashMap::new(),
330 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
334 assert_eq!(boolfnames.len(), boolnames.len());
335 assert_eq!(numfnames.len(), numnames.len());
336 assert_eq!(stringfnames.len(), stringnames.len());
340 #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
342 // FIXME #6870: Distribute a compiled file in src/tests and test there
343 // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);