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, longnames: bool)
163 -> Result<Box<TermInfo>, StrBuf> {
164 macro_rules! try( ($e:expr) => (
167 Err(e) => return Err(format_strbuf!("{}", e))
177 snames = stringfnames;
181 snames = stringnames;
185 // Check magic number
186 let magic = try!(file.read_le_u16());
188 return Err(format_strbuf!("invalid magic number: expected {:x} but \
194 let names_bytes = try!(file.read_le_i16()) as int;
195 let bools_bytes = try!(file.read_le_i16()) as int;
196 let numbers_count = try!(file.read_le_i16()) as int;
197 let string_offsets_count = try!(file.read_le_i16()) as int;
198 let string_table_bytes = try!(file.read_le_i16()) as int;
200 assert!(names_bytes > 0);
202 if (bools_bytes as uint) > boolnames.len() {
203 return Err("incompatible file: more booleans than \
204 expected".to_strbuf());
207 if (numbers_count as uint) > numnames.len() {
208 return Err("incompatible file: more numbers than \
209 expected".to_strbuf());
212 if (string_offsets_count as uint) > stringnames.len() {
213 return Err("incompatible file: more string offsets than \
214 expected".to_strbuf());
218 let bytes = try!(file.read_exact(names_bytes as uint - 1));
219 let names_str = match str::from_utf8(bytes.as_slice()) {
220 Some(s) => s.to_owned(),
221 None => return Err("input not utf-8".to_strbuf()),
224 let term_names: Vec<StrBuf> = names_str.as_slice()
226 .map(|s| s.to_strbuf())
229 try!(file.read_byte()); // consume NUL
231 let mut bools_map = HashMap::new();
232 if bools_bytes != 0 {
233 for i in range(0, bools_bytes) {
234 let b = try!(file.read_byte());
236 bools_map.insert(bnames[i as uint].to_strbuf(), true);
241 if (bools_bytes + names_bytes) % 2 == 1 {
242 try!(file.read_byte()); // compensate for padding
245 let mut numbers_map = HashMap::new();
246 if numbers_count != 0 {
247 for i in range(0, numbers_count) {
248 let n = try!(file.read_le_u16());
250 numbers_map.insert(nnames[i as uint].to_strbuf(), n);
255 let mut string_map = HashMap::new();
257 if string_offsets_count != 0 {
258 let mut string_offsets = Vec::with_capacity(10);
259 for _ in range(0, string_offsets_count) {
260 string_offsets.push(try!(file.read_le_u16()));
263 let string_table = try!(file.read_exact(string_table_bytes as uint));
265 if string_table.len() != string_table_bytes as uint {
266 return Err("error: hit EOF before end of string \
270 for (i, v) in string_offsets.iter().enumerate() {
272 if offset == 0xFFFF { // non-entry
276 let name = if snames[i] == "_" {
282 if offset == 0xFFFE {
283 // undocumented: FFFE indicates cap@, which means the capability is not present
284 // unsure if the handling for this is correct
285 string_map.insert(name.to_strbuf(), Vec::new());
290 // Find the offset of the NUL we want to go to
291 let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
292 .iter().position(|&b| b == 0);
295 string_map.insert(name.to_strbuf(),
297 string_table.slice(offset as uint,
298 offset as uint + len)))
301 return Err("invalid file: missing NUL in \
302 string_table".to_strbuf());
308 // And that's all there is to it
312 numbers: numbers_map,
317 /// Create a dummy TermInfo struct for msys terminals
318 pub fn msys_terminfo() -> Box<TermInfo> {
319 let mut strings = HashMap::new();
320 strings.insert("sgr0".to_strbuf(), Vec::from_slice(bytes!("\x1b[0m")));
321 strings.insert("bold".to_strbuf(), Vec::from_slice(bytes!("\x1b[1m")));
322 strings.insert("setaf".to_strbuf(), Vec::from_slice(bytes!("\x1b[3%p1%dm")));
323 strings.insert("setab".to_strbuf(), Vec::from_slice(bytes!("\x1b[4%p1%dm")));
325 names: vec!("cygwin".to_strbuf()), // msys is a fork of an older cygwin version
326 bools: HashMap::new(),
327 numbers: HashMap::new(),
335 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
339 assert_eq!(boolfnames.len(), boolnames.len());
340 assert_eq!(numfnames.len(), numnames.len());
341 assert_eq!(stringfnames.len(), stringnames.len());
345 #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
347 // FIXME #6870: Distribute a compiled file in src/tests and test there
348 // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);