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