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 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,
163 longnames: bool) -> Result<~TermInfo, ~str> {
164 macro_rules! try( ($e:expr) => (
165 match $e { Ok(e) => e, Err(e) => return Err(format!("{}", e)) }
174 snames = stringfnames;
178 snames = stringnames;
182 // Check magic number
183 let magic = try!(file.read_le_u16());
185 return Err(format!("invalid magic number: expected {:x} but found {:x}",
186 0x011A, magic as uint));
189 let names_bytes = try!(file.read_le_i16()) as int;
190 let bools_bytes = try!(file.read_le_i16()) as int;
191 let numbers_count = try!(file.read_le_i16()) as int;
192 let string_offsets_count = try!(file.read_le_i16()) as int;
193 let string_table_bytes = try!(file.read_le_i16()) as int;
195 assert!(names_bytes > 0);
197 if (bools_bytes as uint) > boolnames.len() {
198 return Err("incompatible file: more booleans than expected".to_owned());
201 if (numbers_count as uint) > numnames.len() {
202 return Err("incompatible file: more numbers than expected".to_owned());
205 if (string_offsets_count as uint) > stringnames.len() {
206 return Err("incompatible file: more string offsets than expected".to_owned());
210 let bytes = try!(file.read_exact(names_bytes as uint - 1));
211 let names_str = match str::from_utf8(bytes.as_slice()) {
212 Some(s) => s.to_owned(), None => return Err("input not utf-8".to_owned()),
215 let term_names: Vec<~str> = names_str.split('|').map(|s| s.to_owned()).collect();
217 try!(file.read_byte()); // consume NUL
219 let mut bools_map = HashMap::new();
220 if bools_bytes != 0 {
221 for i in range(0, bools_bytes) {
222 let b = try!(file.read_byte());
224 return Err("error: expected more bools but hit EOF".to_owned());
226 bools_map.insert(bnames[i as uint].to_owned(), true);
231 if (bools_bytes + names_bytes) % 2 == 1 {
232 try!(file.read_byte()); // compensate for padding
235 let mut numbers_map = HashMap::new();
236 if numbers_count != 0 {
237 for i in range(0, numbers_count) {
238 let n = try!(file.read_le_u16());
240 numbers_map.insert(nnames[i as uint].to_owned(), n);
245 let mut string_map = HashMap::new();
247 if string_offsets_count != 0 {
248 let mut string_offsets = Vec::with_capacity(10);
249 for _ in range(0, string_offsets_count) {
250 string_offsets.push(try!(file.read_le_u16()));
253 let string_table = try!(file.read_exact(string_table_bytes as uint));
255 if string_table.len() != string_table_bytes as uint {
256 return Err("error: hit EOF before end of string table".to_owned());
259 for (i, v) in string_offsets.iter().enumerate() {
261 if offset == 0xFFFF { // non-entry
265 let name = if snames[i] == "_" {
271 if offset == 0xFFFE {
272 // undocumented: FFFE indicates cap@, which means the capability is not present
273 // unsure if the handling for this is correct
274 string_map.insert(name.to_owned(), Vec::new());
279 // Find the offset of the NUL we want to go to
280 let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
281 .iter().position(|&b| b == 0);
284 string_map.insert(name.to_owned(),
286 string_table.slice(offset as uint,
287 offset as uint + len)))
290 return Err("invalid file: missing NUL in string_table".to_owned());
296 // And that's all there is to it
300 numbers: numbers_map,
305 /// Create a dummy TermInfo struct for msys terminals
306 pub fn msys_terminfo() -> ~TermInfo {
307 let mut strings = HashMap::new();
308 strings.insert("sgr0".to_owned(), Vec::from_slice(bytes!("\x1b[0m")));
309 strings.insert("bold".to_owned(), Vec::from_slice(bytes!("\x1b[1m")));
310 strings.insert("setaf".to_owned(), Vec::from_slice(bytes!("\x1b[3%p1%dm")));
311 strings.insert("setab".to_owned(), Vec::from_slice(bytes!("\x1b[4%p1%dm")));
313 names: vec!("cygwin".to_owned()), // msys is a fork of an older cygwin version
314 bools: HashMap::new(),
315 numbers: HashMap::new(),
323 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
327 assert_eq!(boolfnames.len(), boolnames.len());
328 assert_eq!(numfnames.len(), numnames.len());
329 assert_eq!(stringfnames.len(), stringnames.len());
333 #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
335 // FIXME #6870: Distribute a compiled file in src/tests and test there
336 // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);