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))
17 use std::hashmap::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> {
171 snames = stringfnames;
175 snames = stringnames;
179 // Check magic number
180 let magic = file.read_le_u16();
182 return Err(format!("invalid magic number: expected {:x} but found {:x}",
183 0x011A, magic as uint));
186 let names_bytes = file.read_le_i16() as int;
187 let bools_bytes = file.read_le_i16() as int;
188 let numbers_count = file.read_le_i16() as int;
189 let string_offsets_count = file.read_le_i16() as int;
190 let string_table_bytes = file.read_le_i16() as int;
192 assert!(names_bytes > 0);
194 debug!("names_bytes = {}", names_bytes);
195 debug!("bools_bytes = {}", bools_bytes);
196 debug!("numbers_count = {}", numbers_count);
197 debug!("string_offsets_count = {}", string_offsets_count);
198 debug!("string_table_bytes = {}", string_table_bytes);
200 if (bools_bytes as uint) > boolnames.len() {
201 error!("expected bools_bytes to be less than {} but found {}", boolnames.len(),
203 return Err(~"incompatible file: more booleans than expected");
206 if (numbers_count as uint) > numnames.len() {
207 error!("expected numbers_count to be less than {} but found {}", numnames.len(),
209 return Err(~"incompatible file: more numbers than expected");
212 if (string_offsets_count as uint) > stringnames.len() {
213 error!("expected string_offsets_count to be less than {} but found {}", stringnames.len(),
214 string_offsets_count);
215 return Err(~"incompatible file: more string offsets than expected");
219 let names_str = str::from_utf8_owned(file.read_bytes(names_bytes as uint - 1)).unwrap();
221 let term_names: ~[~str] = names_str.split('|').map(|s| s.to_owned()).collect();
223 file.read_byte(); // consume NUL
225 debug!("term names: {:?}", term_names);
227 let mut bools_map = HashMap::new();
228 if bools_bytes != 0 {
229 for i in range(0, bools_bytes) {
230 let b = file.read_byte().unwrap();
232 error!("EOF reading bools after {} entries", i);
233 return Err(~"error: expected more bools but hit EOF");
235 debug!("{} set", bnames[i]);
236 bools_map.insert(bnames[i].to_owned(), true);
241 debug!("bools: {:?}", bools_map);
243 if (bools_bytes + names_bytes) % 2 == 1 {
244 debug!("adjusting for padding between bools and numbers");
245 file.read_byte(); // compensate for padding
248 let mut numbers_map = HashMap::new();
249 if numbers_count != 0 {
250 for i in range(0, numbers_count) {
251 let n = file.read_le_u16();
253 debug!("{}\\#{}", nnames[i], n);
254 numbers_map.insert(nnames[i].to_owned(), n);
259 debug!("numbers: {:?}", numbers_map);
261 let mut string_map = HashMap::new();
263 if string_offsets_count != 0 {
264 let mut string_offsets = vec::with_capacity(10);
265 for _ in range(0, string_offsets_count) {
266 string_offsets.push(file.read_le_u16());
269 debug!("offsets: {:?}", string_offsets);
271 let string_table = file.read_bytes(string_table_bytes as uint);
273 if string_table.len() != string_table_bytes as uint {
274 error!("EOF reading string table after {} bytes, wanted {}", string_table.len(),
276 return Err(~"error: hit EOF before end of string table");
279 for (i, v) in string_offsets.iter().enumerate() {
281 if offset == 0xFFFF { // non-entry
285 let name = if snames[i] == "_" {
291 if offset == 0xFFFE {
292 // undocumented: FFFE indicates cap@, which means the capability is not present
293 // unsure if the handling for this is correct
294 string_map.insert(name.to_owned(), ~[]);
299 // Find the offset of the NUL we want to go to
300 let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
301 .iter().position(|&b| b == 0);
304 string_map.insert(name.to_owned(),
305 string_table.slice(offset as uint,
306 offset as uint + len).to_owned())
309 return Err(~"invalid file: missing NUL in string_table");
315 // And that's all there is to it
316 Ok(~TermInfo {names: term_names, bools: bools_map, numbers: numbers_map, strings: string_map })
319 /// Create a dummy TermInfo struct for msys terminals
320 pub fn msys_terminfo() -> ~TermInfo {
321 let mut strings = HashMap::new();
322 strings.insert(~"sgr0", bytes!("\x1b[0m").to_owned());
323 strings.insert(~"bold", bytes!("\x1b[1m").to_owned());
324 strings.insert(~"setaf", bytes!("\x1b[3%p1%dm").to_owned());
325 strings.insert(~"setab", bytes!("\x1b[4%p1%dm").to_owned());
327 names: ~[~"cygwin"], // msys is a fork of an older cygwin version
328 bools: HashMap::new(),
329 numbers: HashMap::new(),
337 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
341 assert_eq!(boolfnames.len(), boolnames.len());
342 assert_eq!(numfnames.len(), numnames.len());
343 assert_eq!(stringfnames.len(), stringnames.len());
347 #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
349 // FIXME #6870: Distribute a compiled file in src/tests and test there
350 // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);