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_upper_case_globals)]
13 //! ncurses-compatible compiled terminfo format parsing (term(5))
15 use std::collections::HashMap;
16 use std::io::prelude::*;
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] = &["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] = &["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] = &[ "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] = &[ "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] = &[ "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] = &[ "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 Read, 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!(read_le_u16(file));
188 return Err(format!("invalid magic number: expected {:x}, found {:x}",
189 0x011A_usize, magic as usize));
192 let names_bytes = try!(read_le_u16(file)) as isize;
193 let bools_bytes = try!(read_le_u16(file)) as isize;
194 let numbers_count = try!(read_le_u16(file)) as isize;
195 let string_offsets_count = try!(read_le_u16(file)) as isize;
196 let string_table_bytes = try!(read_le_u16(file)) as isize;
198 assert!(names_bytes > 0);
200 if (bools_bytes as usize) > boolnames.len() {
201 return Err("incompatible file: more booleans than \
202 expected".to_string());
205 if (numbers_count as usize) > numnames.len() {
206 return Err("incompatible file: more numbers than \
207 expected".to_string());
210 if (string_offsets_count as usize) > stringnames.len() {
211 return Err("incompatible file: more string offsets than \
212 expected".to_string());
216 let bytes = try!(read_exact(file, names_bytes as usize - 1));
217 let names_str = match String::from_utf8(bytes) {
219 Err(_) => return Err("input not utf-8".to_string()),
222 let term_names: Vec<String> = names_str.split('|')
223 .map(|s| s.to_string())
226 try!(read_byte(file)); // consume NUL
228 let mut bools_map = HashMap::new();
229 if bools_bytes != 0 {
230 for i in 0..bools_bytes {
231 let b = try!(read_byte(file));
233 bools_map.insert(bnames[i as usize].to_string(), true);
238 if (bools_bytes + names_bytes) % 2 == 1 {
239 try!(read_byte(file)); // compensate for padding
242 let mut numbers_map = HashMap::new();
243 if numbers_count != 0 {
244 for i in 0..numbers_count {
245 let n = try!(read_le_u16(file));
247 numbers_map.insert(nnames[i as usize].to_string(), n);
252 let mut string_map = HashMap::new();
254 if string_offsets_count != 0 {
255 let mut string_offsets = Vec::with_capacity(10);
256 for _ in 0..string_offsets_count {
257 string_offsets.push(try!(read_le_u16(file)));
260 let string_table = try!(read_exact(file, string_table_bytes as usize));
262 if string_table.len() != string_table_bytes as usize {
263 return Err("error: hit EOF before end of string \
267 for (i, v) in string_offsets.iter().enumerate() {
269 if offset == 0xFFFF { // non-entry
273 let name = if snames[i] == "_" {
279 if offset == 0xFFFE {
280 // undocumented: FFFE indicates cap@, which means the capability is not present
281 // unsure if the handling for this is correct
282 string_map.insert(name.to_string(), Vec::new());
287 // Find the offset of the NUL we want to go to
288 let nulpos = string_table[offset as usize .. string_table_bytes as usize]
289 .iter().position(|&b| b == 0);
292 string_map.insert(name.to_string(),
293 string_table[offset as usize ..
294 (offset as usize + len)].to_vec())
297 return Err("invalid file: missing NUL in \
298 string_table".to_string());
304 // And that's all there is to it
308 numbers: numbers_map,
313 fn read_le_u16<R: Read + ?Sized>(r: &mut R) -> io::Result<u16> {
315 assert_eq!(try!(r.read(&mut b)), 2);
316 Ok((b[0] as u16) | ((b[1] as u16) << 8))
319 fn read_byte<R: Read + ?Sized>(r: &mut R) -> io::Result<u8> {
321 assert_eq!(try!(r.read(&mut b)), 1);
325 fn read_exact<R: Read + ?Sized>(r: &mut R, sz: usize) -> io::Result<Vec<u8>> {
326 let mut v = Vec::with_capacity(sz);
327 try!(r.take(sz as u64).read_to_end(&mut v));
328 assert_eq!(v.len(), sz);
332 /// Create a dummy TermInfo struct for msys terminals
333 pub fn msys_terminfo() -> Box<TermInfo> {
334 let mut strings = HashMap::new();
335 strings.insert("sgr0".to_string(), b"\x1B[0m".to_vec());
336 strings.insert("bold".to_string(), b"\x1B[1m".to_vec());
337 strings.insert("setaf".to_string(), b"\x1B[3%p1%dm".to_vec());
338 strings.insert("setab".to_string(), b"\x1B[4%p1%dm".to_vec());
340 names: vec!("cygwin".to_string()), // msys is a fork of an older cygwin version
341 bools: HashMap::new(),
342 numbers: HashMap::new(),
350 use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
354 assert_eq!(boolfnames.len(), boolnames.len());
355 assert_eq!(numfnames.len(), numnames.len());
356 assert_eq!(stringfnames.len(), stringnames.len());