]> git.lizzy.rs Git - rust.git/blob - src/libterm/terminfo/parser/compiled.rs
auto merge of #13967 : richo/rust/features/ICE-fails, r=alexcrichton
[rust.git] / src / libterm / terminfo / parser / compiled.rs
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.
4 //
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.
10
11 #![allow(non_uppercase_statics)]
12
13 //! ncurses-compatible compiled terminfo format parsing (term(5))
14
15 use collections::HashMap;
16 use std::io;
17 use std::str;
18 use super::super::TermInfo;
19
20 // These are the orders ncurses uses in its compiled format (as of 5.9). Not sure if portable.
21
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"];
33
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"];
38
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"];
48
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"];
53
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"];
126
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",
159     "box1"];
160
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>, ~str> {
164     macro_rules! try( ($e:expr) => (
165         match $e { Ok(e) => e, Err(e) => return Err(format!("{}", e)) }
166     ) )
167
168     let bnames;
169     let snames;
170     let nnames;
171
172     if longnames {
173         bnames = boolfnames;
174         snames = stringfnames;
175         nnames = numfnames;
176     } else {
177         bnames = boolnames;
178         snames = stringnames;
179         nnames = numnames;
180     }
181
182     // Check magic number
183     let magic = try!(file.read_le_u16());
184     if magic != 0x011A {
185         return Err(format!("invalid magic number: expected {:x} but found {:x}",
186                            0x011A, magic as uint));
187     }
188
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;
194
195     assert!(names_bytes          > 0);
196
197     if (bools_bytes as uint) > boolnames.len() {
198         return Err("incompatible file: more booleans than expected".to_owned());
199     }
200
201     if (numbers_count as uint) > numnames.len() {
202         return Err("incompatible file: more numbers than expected".to_owned());
203     }
204
205     if (string_offsets_count as uint) > stringnames.len() {
206         return Err("incompatible file: more string offsets than expected".to_owned());
207     }
208
209     // don't read NUL
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()),
213     };
214
215     let term_names: Vec<~str> = names_str.split('|').map(|s| s.to_owned()).collect();
216
217     try!(file.read_byte()); // consume NUL
218
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());
223             if b == 1 {
224                 bools_map.insert(bnames[i as uint].to_owned(), true);
225             }
226         }
227     }
228
229     if (bools_bytes + names_bytes) % 2 == 1 {
230         try!(file.read_byte()); // compensate for padding
231     }
232
233     let mut numbers_map = HashMap::new();
234     if numbers_count != 0 {
235         for i in range(0, numbers_count) {
236             let n = try!(file.read_le_u16());
237             if n != 0xFFFF {
238                 numbers_map.insert(nnames[i as uint].to_owned(), n);
239             }
240         }
241     }
242
243     let mut string_map = HashMap::new();
244
245     if string_offsets_count != 0 {
246         let mut string_offsets = Vec::with_capacity(10);
247         for _ in range(0, string_offsets_count) {
248             string_offsets.push(try!(file.read_le_u16()));
249         }
250
251         let string_table = try!(file.read_exact(string_table_bytes as uint));
252
253         if string_table.len() != string_table_bytes as uint {
254             return Err("error: hit EOF before end of string table".to_owned());
255         }
256
257         for (i, v) in string_offsets.iter().enumerate() {
258             let offset = *v;
259             if offset == 0xFFFF { // non-entry
260                 continue;
261             }
262
263             let name = if snames[i] == "_" {
264                 stringfnames[i]
265             } else {
266                 snames[i]
267             };
268
269             if offset == 0xFFFE {
270                 // undocumented: FFFE indicates cap@, which means the capability is not present
271                 // unsure if the handling for this is correct
272                 string_map.insert(name.to_owned(), Vec::new());
273                 continue;
274             }
275
276
277             // Find the offset of the NUL we want to go to
278             let nulpos = string_table.slice(offset as uint, string_table_bytes as uint)
279                 .iter().position(|&b| b == 0);
280             match nulpos {
281                 Some(len) => {
282                     string_map.insert(name.to_owned(),
283                                       Vec::from_slice(
284                                           string_table.slice(offset as uint,
285                                           offset as uint + len)))
286                 },
287                 None => {
288                     return Err("invalid file: missing NUL in string_table".to_owned());
289                 }
290             };
291         }
292     }
293
294     // And that's all there is to it
295     Ok(box TermInfo {
296         names: term_names,
297         bools: bools_map,
298         numbers: numbers_map,
299         strings: string_map
300     })
301 }
302
303 /// Create a dummy TermInfo struct for msys terminals
304 pub fn msys_terminfo() -> Box<TermInfo> {
305     let mut strings = HashMap::new();
306     strings.insert("sgr0".to_owned(), Vec::from_slice(bytes!("\x1b[0m")));
307     strings.insert("bold".to_owned(), Vec::from_slice(bytes!("\x1b[1m")));
308     strings.insert("setaf".to_owned(), Vec::from_slice(bytes!("\x1b[3%p1%dm")));
309     strings.insert("setab".to_owned(), Vec::from_slice(bytes!("\x1b[4%p1%dm")));
310     box TermInfo {
311         names: vec!("cygwin".to_owned()), // msys is a fork of an older cygwin version
312         bools: HashMap::new(),
313         numbers: HashMap::new(),
314         strings: strings
315     }
316 }
317
318 #[cfg(test)]
319 mod test {
320
321     use super::{boolnames, boolfnames, numnames, numfnames, stringnames, stringfnames};
322
323     #[test]
324     fn test_veclens() {
325         assert_eq!(boolfnames.len(), boolnames.len());
326         assert_eq!(numfnames.len(), numnames.len());
327         assert_eq!(stringfnames.len(), stringnames.len());
328     }
329
330     #[test]
331     #[ignore(reason = "no ncurses on buildbots, needs a bundled terminfo file to test against")]
332     fn test_parse() {
333         // FIXME #6870: Distribute a compiled file in src/tests and test there
334         // parse(io::fs_reader(&p("/usr/share/terminfo/r/rxvt-256color")).unwrap(), false);
335     }
336 }