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_uppercase_statics)]
13 //! ncurses-compatible compiled terminfo format parsing (term(5))
15 use std::collections::HashMap;
18 use super::super::TermInfo;
20 // These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
22 pub static boolfnames: &'static[&'static str] = &'static["auto_left_margin", "auto_right_margin",
23 "no_esc_ctlc", "ceol_standout_glitch", "eat_newline_glitch", "erase_overstrike", "generic_type",
24 "hard_copy", "has_meta_key", "has_status_line", "insert_null_glitch", "memory_above",
25 "memory_below", "move_insert_mode", "move_standout_mode", "over_strike", "status_line_esc_ok",
26 "dest_tabs_magic_smso", "tilde_glitch", "transparent_underline", "xon_xoff", "needs_xon_xoff",
27 "prtr_silent", "hard_cursor", "non_rev_rmcup", "no_pad_char", "non_dest_scroll_region",
28 "can_change", "back_color_erase", "hue_lightness_saturation", "col_addr_glitch",
29 "cr_cancels_micro_mode", "has_print_wheel", "row_addr_glitch", "semi_auto_right_margin",
30 "cpi_changes_res", "lpi_changes_res", "backspaces_with_bs", "crt_no_scrolling",
31 "no_correctly_working_cr", "gnu_has_meta_key", "linefeed_is_newline", "has_hardware_tabs",
32 "return_does_clr_eol"];
34 pub static boolnames: &'static[&'static str] = &'static["bw", "am", "xsb", "xhp", "xenl", "eo",
35 "gn", "hc", "km", "hs", "in", "db", "da", "mir", "msgr", "os", "eslok", "xt", "hz", "ul", "xon",
36 "nxon", "mc5i", "chts", "nrrmc", "npc", "ndscr", "ccc", "bce", "hls", "xhpa", "crxm", "daisy",
37 "xvpa", "sam", "cpix", "lpix", "OTbs", "OTns", "OTnc", "OTMT", "OTNL", "OTpt", "OTxr"];
39 pub static numfnames: &'static[&'static str] = &'static[ "columns", "init_tabs", "lines",
40 "lines_of_memory", "magic_cookie_glitch", "padding_baud_rate", "virtual_terminal",
41 "width_status_line", "num_labels", "label_height", "label_width", "max_attributes",
42 "maximum_windows", "max_colors", "max_pairs", "no_color_video", "buffer_capacity",
43 "dot_vert_spacing", "dot_horz_spacing", "max_micro_address", "max_micro_jump", "micro_col_size",
44 "micro_line_size", "number_of_pins", "output_res_char", "output_res_line",
45 "output_res_horz_inch", "output_res_vert_inch", "print_rate", "wide_char_size", "buttons",
46 "bit_image_entwining", "bit_image_type", "magic_cookie_glitch_ul", "carriage_return_delay",
47 "new_line_delay", "backspace_delay", "horizontal_tab_delay", "number_of_function_keys"];
49 pub static numnames: &'static[&'static str] = &'static[ "cols", "it", "lines", "lm", "xmc", "pb",
50 "vt", "wsl", "nlab", "lh", "lw", "ma", "wnum", "colors", "pairs", "ncv", "bufsz", "spinv",
51 "spinh", "maddr", "mjump", "mcs", "mls", "npins", "orc", "orl", "orhi", "orvi", "cps", "widcs",
52 "btns", "bitwin", "bitype", "UTug", "OTdC", "OTdN", "OTdB", "OTdT", "OTkn"];
54 pub static stringfnames: &'static[&'static str] = &'static[ "back_tab", "bell", "carriage_return",
55 "change_scroll_region", "clear_all_tabs", "clear_screen", "clr_eol", "clr_eos",
56 "column_address", "command_character", "cursor_address", "cursor_down", "cursor_home",
57 "cursor_invisible", "cursor_left", "cursor_mem_address", "cursor_normal", "cursor_right",
58 "cursor_to_ll", "cursor_up", "cursor_visible", "delete_character", "delete_line",
59 "dis_status_line", "down_half_line", "enter_alt_charset_mode", "enter_blink_mode",
60 "enter_bold_mode", "enter_ca_mode", "enter_delete_mode", "enter_dim_mode", "enter_insert_mode",
61 "enter_secure_mode", "enter_protected_mode", "enter_reverse_mode", "enter_standout_mode",
62 "enter_underline_mode", "erase_chars", "exit_alt_charset_mode", "exit_attribute_mode",
63 "exit_ca_mode", "exit_delete_mode", "exit_insert_mode", "exit_standout_mode",
64 "exit_underline_mode", "flash_screen", "form_feed", "from_status_line", "init_1string",
65 "init_2string", "init_3string", "init_file", "insert_character", "insert_line",
66 "insert_padding", "key_backspace", "key_catab", "key_clear", "key_ctab", "key_dc", "key_dl",
67 "key_down", "key_eic", "key_eol", "key_eos", "key_f0", "key_f1", "key_f10", "key_f2", "key_f3",
68 "key_f4", "key_f5", "key_f6", "key_f7", "key_f8", "key_f9", "key_home", "key_ic", "key_il",
69 "key_left", "key_ll", "key_npage", "key_ppage", "key_right", "key_sf", "key_sr", "key_stab",
70 "key_up", "keypad_local", "keypad_xmit", "lab_f0", "lab_f1", "lab_f10", "lab_f2", "lab_f3",
71 "lab_f4", "lab_f5", "lab_f6", "lab_f7", "lab_f8", "lab_f9", "meta_off", "meta_on", "newline",
72 "pad_char", "parm_dch", "parm_delete_line", "parm_down_cursor", "parm_ich", "parm_index",
73 "parm_insert_line", "parm_left_cursor", "parm_right_cursor", "parm_rindex", "parm_up_cursor",
74 "pkey_key", "pkey_local", "pkey_xmit", "print_screen", "prtr_off", "prtr_on", "repeat_char",
75 "reset_1string", "reset_2string", "reset_3string", "reset_file", "restore_cursor",
76 "row_address", "save_cursor", "scroll_forward", "scroll_reverse", "set_attributes", "set_tab",
77 "set_window", "tab", "to_status_line", "underline_char", "up_half_line", "init_prog", "key_a1",
78 "key_a3", "key_b2", "key_c1", "key_c3", "prtr_non", "char_padding", "acs_chars", "plab_norm",
79 "key_btab", "enter_xon_mode", "exit_xon_mode", "enter_am_mode", "exit_am_mode", "xon_character",
80 "xoff_character", "ena_acs", "label_on", "label_off", "key_beg", "key_cancel", "key_close",
81 "key_command", "key_copy", "key_create", "key_end", "key_enter", "key_exit", "key_find",
82 "key_help", "key_mark", "key_message", "key_move", "key_next", "key_open", "key_options",
83 "key_previous", "key_print", "key_redo", "key_reference", "key_refresh", "key_replace",
84 "key_restart", "key_resume", "key_save", "key_suspend", "key_undo", "key_sbeg", "key_scancel",
85 "key_scommand", "key_scopy", "key_screate", "key_sdc", "key_sdl", "key_select", "key_send",
86 "key_seol", "key_sexit", "key_sfind", "key_shelp", "key_shome", "key_sic", "key_sleft",
87 "key_smessage", "key_smove", "key_snext", "key_soptions", "key_sprevious", "key_sprint",
88 "key_sredo", "key_sreplace", "key_sright", "key_srsume", "key_ssave", "key_ssuspend",
89 "key_sundo", "req_for_input", "key_f11", "key_f12", "key_f13", "key_f14", "key_f15", "key_f16",
90 "key_f17", "key_f18", "key_f19", "key_f20", "key_f21", "key_f22", "key_f23", "key_f24",
91 "key_f25", "key_f26", "key_f27", "key_f28", "key_f29", "key_f30", "key_f31", "key_f32",
92 "key_f33", "key_f34", "key_f35", "key_f36", "key_f37", "key_f38", "key_f39", "key_f40",
93 "key_f41", "key_f42", "key_f43", "key_f44", "key_f45", "key_f46", "key_f47", "key_f48",
94 "key_f49", "key_f50", "key_f51", "key_f52", "key_f53", "key_f54", "key_f55", "key_f56",
95 "key_f57", "key_f58", "key_f59", "key_f60", "key_f61", "key_f62", "key_f63", "clr_bol",
96 "clear_margins", "set_left_margin", "set_right_margin", "label_format", "set_clock",
97 "display_clock", "remove_clock", "create_window", "goto_window", "hangup", "dial_phone",
98 "quick_dial", "tone", "pulse", "flash_hook", "fixed_pause", "wait_tone", "user0", "user1",
99 "user2", "user3", "user4", "user5", "user6", "user7", "user8", "user9", "orig_pair",
100 "orig_colors", "initialize_color", "initialize_pair", "set_color_pair", "set_foreground",
101 "set_background", "change_char_pitch", "change_line_pitch", "change_res_horz",
102 "change_res_vert", "define_char", "enter_doublewide_mode", "enter_draft_quality",
103 "enter_italics_mode", "enter_leftward_mode", "enter_micro_mode", "enter_near_letter_quality",
104 "enter_normal_quality", "enter_shadow_mode", "enter_subscript_mode", "enter_superscript_mode",
105 "enter_upward_mode", "exit_doublewide_mode", "exit_italics_mode", "exit_leftward_mode",
106 "exit_micro_mode", "exit_shadow_mode", "exit_subscript_mode", "exit_superscript_mode",
107 "exit_upward_mode", "micro_column_address", "micro_down", "micro_left", "micro_right",
108 "micro_row_address", "micro_up", "order_of_pins", "parm_down_micro", "parm_left_micro",
109 "parm_right_micro", "parm_up_micro", "select_char_set", "set_bottom_margin",
110 "set_bottom_margin_parm", "set_left_margin_parm", "set_right_margin_parm", "set_top_margin",
111 "set_top_margin_parm", "start_bit_image", "start_char_set_def", "stop_bit_image",
112 "stop_char_set_def", "subscript_characters", "superscript_characters", "these_cause_cr",
113 "zero_motion", "char_set_names", "key_mouse", "mouse_info", "req_mouse_pos", "get_mouse",
114 "set_a_foreground", "set_a_background", "pkey_plab", "device_type", "code_set_init",
115 "set0_des_seq", "set1_des_seq", "set2_des_seq", "set3_des_seq", "set_lr_margin",
116 "set_tb_margin", "bit_image_repeat", "bit_image_newline", "bit_image_carriage_return",
117 "color_names", "define_bit_image_region", "end_bit_image_region", "set_color_band",
118 "set_page_length", "display_pc_char", "enter_pc_charset_mode", "exit_pc_charset_mode",
119 "enter_scancode_mode", "exit_scancode_mode", "pc_term_options", "scancode_escape",
120 "alt_scancode_esc", "enter_horizontal_hl_mode", "enter_left_hl_mode", "enter_low_hl_mode",
121 "enter_right_hl_mode", "enter_top_hl_mode", "enter_vertical_hl_mode", "set_a_attributes",
122 "set_pglen_inch", "termcap_init2", "termcap_reset", "linefeed_if_not_lf", "backspace_if_not_bs",
123 "other_non_function_keys", "arrow_key_map", "acs_ulcorner", "acs_llcorner", "acs_urcorner",
124 "acs_lrcorner", "acs_ltee", "acs_rtee", "acs_btee", "acs_ttee", "acs_hline", "acs_vline",
125 "acs_plus", "memory_lock", "memory_unlock", "box_chars_1"];
127 pub static stringnames: &'static[&'static str] = &'static[ "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",
161 /// Parse a compiled terminfo entry, using long capability names if `longnames` is true
162 pub fn parse(file: &mut io::Reader, longnames: bool)
163 -> Result<Box<TermInfo>, String> {
164 macro_rules! try( ($e:expr) => (
167 Err(e) => return Err(format!("{}", e))
177 snames = stringfnames;
181 snames = stringnames;
185 // Check magic number
186 let magic = try!(file.read_le_u16());
188 return Err(format!("invalid magic number: expected {:x} but found {:x}",
189 0x011A, magic as uint));
192 let names_bytes = try!(file.read_le_i16()) as int;
193 let bools_bytes = try!(file.read_le_i16()) as int;
194 let numbers_count = try!(file.read_le_i16()) as int;
195 let string_offsets_count = try!(file.read_le_i16()) as int;
196 let string_table_bytes = try!(file.read_le_i16()) as int;
198 assert!(names_bytes > 0);
200 if (bools_bytes as uint) > boolnames.len() {
201 return Err("incompatible file: more booleans than \
202 expected".to_string());
205 if (numbers_count as uint) > numnames.len() {
206 return Err("incompatible file: more numbers than \
207 expected".to_string());
210 if (string_offsets_count as uint) > stringnames.len() {
211 return Err("incompatible file: more string offsets than \
212 expected".to_string());
216 let bytes = try!(file.read_exact(names_bytes as uint - 1));
217 let names_str = match str::from_utf8(bytes.as_slice()) {
218 Some(s) => s.to_string(),
219 None => return Err("input not utf-8".to_string()),
222 let term_names: Vec<String> = names_str.as_slice()
224 .map(|s| s.to_string())
227 try!(file.read_byte()); // consume NUL
229 let mut bools_map = HashMap::new();
230 if bools_bytes != 0 {
231 for i in range(0, bools_bytes) {
232 let b = try!(file.read_byte());
234 bools_map.insert(bnames[i as uint].to_string(), true);
239 if (bools_bytes + names_bytes) % 2 == 1 {
240 try!(file.read_byte()); // compensate for padding
243 let mut numbers_map = HashMap::new();
244 if numbers_count != 0 {
245 for i in range(0, numbers_count) {
246 let n = try!(file.read_le_u16());
248 numbers_map.insert(nnames[i as uint].to_string(), n);
253 let mut string_map = HashMap::new();
255 if string_offsets_count != 0 {
256 let mut string_offsets = Vec::with_capacity(10);
257 for _ in range(0, string_offsets_count) {
258 string_offsets.push(try!(file.read_le_u16()));
261 let string_table = try!(file.read_exact(string_table_bytes as uint));
263 if string_table.len() != string_table_bytes as uint {
264 return Err("error: hit EOF before end of string \
268 for (i, v) in string_offsets.iter().enumerate() {
270 if offset == 0xFFFF { // non-entry
274 let name = if snames[i] == "_" {
280 if offset == 0xFFFE {
281 // undocumented: FFFE indicates cap@, which means the capability is not present
282 // unsure if the handling for this is correct
283 string_map.insert(name.to_string(), Vec::new());
288 // Find the offset of the NUL we want to go to
289 let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
290 .iter().position(|&b| b == 0);
293 string_map.insert(name.to_string(),
295 string_table.slice(offset as uint,
296 offset as uint + len)))
299 return Err("invalid file: missing NUL in \
300 string_table".to_string());
306 // And that's all there is to it
310 numbers: numbers_map,
315 /// Create a dummy TermInfo struct for msys terminals
316 pub fn msys_terminfo() -> Box<TermInfo> {
317 let mut strings = HashMap::new();
318 strings.insert("sgr0".to_string(), Vec::from_slice(b"\x1B[0m"));
319 strings.insert("bold".to_string(), Vec::from_slice(b"\x1B[1m"));
320 strings.insert("setaf".to_string(), Vec::from_slice(b"\x1B[3%p1%dm"));
321 strings.insert("setab".to_string(), Vec::from_slice(b"\x1B[4%p1%dm"));
323 names: vec!("cygwin".to_string()), // msys is a fork of an older cygwin version
324 bools: HashMap::new(),
325 numbers: HashMap::new(),
333 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
337 assert_eq!(boolfnames.len(), boolnames.len());
338 assert_eq!(numfnames.len(), numnames.len());
339 assert_eq!(stringfnames.len(), stringnames.len());
343 #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
345 // FIXME #6870: Distribute a compiled file in src/tests and test there
346 // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);